In short. Two things for building s&box games, either usable on its own.
The skill is 17 reference files that teach a coding agent the real s&box API, so it stops writing Unity code into your Source 2 project. Plain markdown, no runtime, no dependencies, so it works with any agent that can read a file. Every API in it is traceable to engine source at a named version.
The toolset adds 18 tools to the editor's MCP server. It answers the questions the editor otherwise leaves silent: does this type actually exist, has the compiler noticed my edit, why did nothing happen when I changed that config. Install it as a package reference,
fobiat.sbox_mcp_server, or drop the single C# file into yourEditor/folder.
On the two words. The asset.party listing is called s&box MCP Server & AI Skill. Strictly the second half is a toolset: s&box ships the MCP server inside the editor, on
127.0.0.1:7269, and this registers into it rather than starting anything. Both words appear below and they mean different things: the server is the editor's, the toolset is what this adds to it.
You write a component. It compiles. You press play. Nothing moves.
There is no error. There is no warning. The console is clean. Somewhere between the code you wrote and the thing you expected, a call that looked completely correct did nothing at all, and the engine did not think that was worth mentioning.
This project is thirteen thousand lines about that gap.
Every one of these compiles. Every one of these looks right in review.
| You wrote | What actually happens |
|---|---|
void Update() |
Never called. The lifecycle method is protected override void OnUpdate(), and yours is just a method nobody invokes |
NetworkSpawn() |
Ownership goes to whoever called it, not the host. A world object now belongs to one random client |
[Sync] on a scene object |
NetworkMode.Snapshot is the default and does not live-replicate. RPCs keep working perfectly, which is what makes it so hard to spot |
A client writing [Sync(SyncFlags.FromHost)] |
Discarded before it reaches the backing field. No exception, and the read-back on the next line already shows the authoritative value |
Model.Load( "typo/path.vmdl" ) |
Comes back as the engine's error model, non-null with IsError set, so the null check everyone writes is a branch that can never fire and the world ships orange with a clean console |
None of these are exotic. They are all first-week code.
s&box borrows GameObject and Component from Unity, then diverges nearly everywhere else.
Different lifecycle, different networking model, Z-up instead of Y-up, restricted .NET, Razor
for UI.
The resemblance is close enough to be dangerous. Ask any coding agent for a mover:
public class Mover : MonoBehaviour // does not exist
{
void Update() // never runs
{
transform.position += Vector3.forward * Time.deltaTime; // none of this is real
Debug.Log( "moving" ); // not a thing
}
}Four lines, four inventions, zero warnings until much later. Here it is correct:
public sealed class Mover : Component
{
[Property] public float Speed { get; set; } = 200f;
protected override void OnUpdate()
{
if ( IsProxy ) return;
WorldPosition += Vector3.Forward * Speed * Time.Delta; // Forward is (1,0,0), Z-up
}
}Install this and the second one is what you get.
Published on asset.party as
fobiat.sbox_mcp_server, or
two files into your Editor/ folder. Either way, eighteen tools
onto the editor's MCP server. It solves a different silent failure: the editor not noticing you
changed anything.
Six reasons it might not, none of which raise an error.
| What you changed | Why nothing happened |
|---|---|
.sbproj |
Read once at editor boot, never watched |
ProjectSettings/*.config |
Cached on first read, never invalidated |
A .cs file |
Compiler watchers stop firing once compilers are recreated |
| A content path | Model.Load returns the error model, not null, so your null check never fires |
| Nothing, you pulled a branch | The process serves the assembly it loaded at boot |
| Installed a package over MCP | install_package mounts for the session and writes nothing |
Each one leaves you having edited a file, seen no error, and concluding the edit was wrong when it simply never arrived.
| Tools | |
|---|---|
| Ask the live engine | project_find_type, project_type_members, project_find_member, project_enum_values, project_input_actions, project_console_commands, project_content_path, project_content_search |
| Ask the editor | project_info, project_compilers, project_source_changes, project_compile_errors, project_assembly_freshness, project_package_references |
| Change something | project_reload_config, project_reload_settings, project_rebuild, project_build |
The first group inverts this project's own rule. Instead of "if it is in none of the reference files it does not exist", you ask the running engine, and that answer cannot go out of date.
The server is the editor's, not this project's. It listens on http://127.0.0.1:7269/mcp,
loopback only, while the editor is running.
claude mcp add --transport http sbox http://127.0.0.1:7269/mcpThe port is McpServerPort in the editor's preferences. It acts on whatever project the editor
currently has open, never on your shell's working directory.
One thing that reads as a bug and is not: the toolset never appears in your client's tool
list. Listing is gated on an attribute internal to the engine, so only the editor's own seven
top-level tools are listed and everything else is reached through search_tools,
describe_toolset and call_tool. Search project_ and they come back.
Two ways, and they install the same code.
As a package, if you want updates to arrive with the editor's own package flow. Open Project
Settings, add fobiat.sbox_mcp_server to package references, restart. The
library project that builds that package lives in this repo.
As a file, if you would rather read every line of an unsandboxed Editor/ assembly before
it compiles into your project, which is a reasonable thing to want:
Copy-Item sbox-skill\editor-mcp\SboxMcpServer.cs,sbox-skill\editor-mcp\SboxMcpServer.Editor.cs your-game\Editor\Restart the editor either way, then run list_toolsets. Details in
editor-mcp/README.md.
Status: compiles clean against real engine assemblies in both nullable configurations,
enableanddisable, each with warnings as errors, verified byeditor-mcp/compilecheck. Both, because a generatedEditor/project takes that setting from the.sbprojfieldNullables, which defaults to off, and checking only the strict one is how a shipped file compiled here and failed on a drop-in.All 14 read-only tools have been called against a live editor and returned structured data. The 4 that mutate editor state have not, since they start builds in an open session. Report anything that misbehaves and it gets fixed.
Published to asset.party on 2026-08-15, which settles the one question engine source could not answer: a library whose only content is an unsandboxed
Editor/assembly is accepted, and loads into a consuming project from a package reference.
Setting up both halves together? Follow the Quickstart instead, which covers the loop they form and the problems each one fixes for the other. The in-depth guide is the long version: every tool, the six ways an edit disappears, and how to write a toolset of your own.
Clone it somewhere your agent can read:
git clone https://github.com/fobiat/sbox-skill.gitThere is no runtime and nothing to build. skills/sbox/SKILL.md is the entry point, and it
routes to references/. Any agent that can open a file can use it. Pick whichever of these
matches your setup.
Any agent, via its instructions file
Most agents read a project instructions file, whatever it is called in your tool. Point it at the router and let it do the rest:
## s&box
Before writing or changing any s&box C#, read `docs/sbox-skill/SKILL.md` and open the
reference file it routes you to. Do not write an API that is not in those files.Copy skills/sbox/ to wherever that path points, and you are done.
Agents with a skills directory
If your tool auto-loads skills from a directory, SKILL.md already carries the frontmatter for
it, so it triggers on its own with no instructions file needed:
mkdir -p your-game/.claude/skills
cp -r sbox-skill/skills/sbox your-game/.claude/skills/Swap the path for your tool's own skills directory. Use the home directory equivalent instead to make it available in every project.
Claude Code, as a plugin
The repo is its own plugin marketplace, so there is nothing to clone and nothing to copy:
/plugin marketplace add fobiat/sbox-skill
/plugin install sbox@sbox-skill
The skill triggers on its own frontmatter, and /plugin update brings new engine versions with
it. editor-mcp/SboxMcpServer*.cs comes along in the installed plugin, so the
toolset is a file copy away rather than another download.
As a submodule, so git pull brings updates with it
git submodule add https://github.com/fobiat/sbox-skill.git vendor/sbox-skill
ln -s ../vendor/sbox-skill/skills/sbox docs/sbox-skillNo agent at all
It reads fine as documentation. Start at
SKILL.md for the Unity translation table and the list of silent
failures, or go straight to whichever
reference file matches what you are building.
Check it took. Ask for something that should trip a Unity reflex:
Write me an s&box component that moves a cube forward at 200 units per second.
Working, if you see : Component, protected override void OnUpdate() and Vector3.Forward.
Not working, if you see MonoBehaviour, void Update() or Vector3.forward. In that case
check your agent can actually reach the files, and that SKILL.md still has its frontmatter.
SKILL.md is a router. It answers almost nothing itself, it works out which file holds the
answer and sends the reader there.
┌──────────────┐
"write a HUD" ──▶│ SKILL.md │
└──────┬───────┘
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
03_UI.md 04_NETWORKING.md 15_API_CORE.md
That shape is the point. A single flat document gets skimmed, and a skimmed API reference is exactly how an invented API gets through. Making the reader open a second file puts real signatures in front of them before they write any.
01_SCENE |
Object model | Scene, GameObject, Component, lifecycle, prefabs, scene events, IPressable |
02_COMPONENTS |
The library | Rendering, physics, controllers, Prop, inventory, camera, lighting, navigation, effects |
03_UI |
Interface | Razor panels, BuildHash, SCSS, flexbox, controls, world panels |
04_NETWORKING |
Multiplayer | Lobbies, ownership, [Sync], NetList, RPCs, dedicated servers |
05_INPUT_PHYSICS |
Simulation | Input actions, Scene.Trace, physics world, math types, time, gizmos |
06_EDITOR |
Tooling | EditorTool, [CustomEditor], docks, the Widget system, which is not Razor |
07_SERVICES |
Backend | Stats, leaderboards, achievements, save data, packages, mounting |
08_AVATARS |
Players | Citizen model, Clothing, ClothingContainer, body and material groups |
09_RENDERING |
Pixels | .shader anatomy, HLSL entry points, materials, render attributes, layers |
10_AUDIO |
Sound | Mixer graph, SoundHandle, processors, phrases, language files |
11_ACTIONGRAPH |
Visual scripting | Exposing C# as nodes, graph-backed callbacks |
12_VR_VOICE |
Extras | VR rig, controllers, haptics, voice capture and playback |
13_EXAMPLES |
Worked code | Eleven complete components, FPS controller to press-E vendor |
14_VERIFICATION |
Field notes | Editor MCP server, and behaviour confirmed live with dates |
15_API_CORE |
Lookup | Full signatures for the types a game touches most |
16_API_INDEX |
Lookup | Namespace index of the wider API surface |
Fifteen of those record what the API is, read out of engine source at a named version.
14_VERIFICATION records what it was observed to do, in a live editor session, with dates.
They are not the same claim, and the distance between them is where every entry in that table at the top comes from. A schema entry proves a call compiles. It says nothing about whether the call does anything.
Where a source-read fact and a live-verified one disagree, the field note wins. Those rows are the part of this project that cannot be generated. Somebody had to sit there while something misbehaved and work out why.
On a tool that auto-loads skills it triggers by itself, matching .sbproj, using Sandbox;,
PanelComponent, [Sync], Scene.Trace and the rest. Everywhere else your instructions file
does the same job. Either way you should not have to name it. What changes the output is how
you ask.
Name the subsystem and the right reference opens first.
Add a networked health component. Only the host may change health, and clients get a change callback for the HUD.
Ask for the trap, not just the code. This is where the value is.
I am about to spawn this prefab from the host and expect clients to see its
[Sync]values. What will bite me?
Make it verify before it writes.
Before you write this, confirm every API you plan to use exists in the reference files, and list anything you could not find.
Porting from Unity? There is a translation table covering about fifty constructs, including the ones with no equivalent at all, like coroutines.
Port this Unity script to s&box. Flag every construct with no equivalent instead of inventing one.
## Layout
sbox-skill/
├── skills/sbox/ the skill, this is what you install
├── editor-mcp/ the s&box MCP Server toolset, a drop-in Editor/ file
├── library/ the same toolset as an s&box library, for asset.party
├── .claude-plugin/ marketplace manifest, makes the repo installable
├── scripts/ repo tooling, never shipped to your game
└── .github/workflows/ CI, mirrors the local gate
Either half stands alone. Everything under skills/sbox/ is required at runtime, because the
router resolves to a reference for every task, and the gate fails the build if one goes missing.
Written against 26.08.05. Every file names its version and the upstream paths it was read from, so the next pass diffs against a newer engine instead of starting over.
Stale guidance is worse than none, and two cases are already on record:
| Older material says | 26.08.05 says |
|---|---|
SceneTrace.WithoutTags takes string[], not params |
params string[], at Scene.Trace.cs:637 |
CameraComponent.AddHookAfterOpaque is live |
[Obsolete] with a => null body. Compiles, returns null, renders nothing. Use AddCommandList |
Found one that drifted? The wrong-API template asks for the only thing that matters: where you confirmed the truth.
One rule outranks everything else: never write an API you have not verified.
An omission makes the reader go and look it up. A wrong signature makes them write code that fails later for no visible reason. The first is a gap. The second is a trap, and this whole project exists because traps are expensive.
python3 scripts/check_skill.pyGreen means the structure holds: routing pointers resolve, no reference is orphaned, frontmatter intact, no em dashes. It does not mean an API is correct. Only reading the source tells you that. More in CONTRIBUTING.md.
Built and maintained by fobiat (Kyle Tarff)
MIT licensed. The API surface described here derives from the s&box engine's managed C#
layer, published by Facepunch Studios at
Facepunch/sbox-public under the MIT
Licence, whose notice travels in LICENSE as required.
s&box is a trademark of Facepunch Studios Ltd. This project is not affiliated with, endorsed
by, or sponsored by Facepunch Studios.