
Nothing in s&box tells you when you're wrong, so I wrote 17 reference files and 18 MCP tools that do
TL;DR
Two things for s&box, both MIT, either usable alone.
- AI Skill: 17 markdown reference files that teach a coding agent the real s&box API, so it stops writing Unity code into your Source 2 project. Any agent that can read a file.
- MCP Server: 18 tools on the editor's own MCP server, for when you edit a file and the editor never notices. Install
fobiat.sbox_mcp_serverfrom Project Settings, or drop two C# files inEditor/. - Built against engine 26.08.05. Every API traceable to a line in
sbox-public. - Package: https://sbox.game/fobiat/sbox_mcp_server/
- Repo: https://github.com/fobiat/sbox-skill
- Write-up: https://fobiat.dev/blog/the-skill-came-out-of-the-game/
Long version below.
Ask any coding agent for something that moves in s&box and you get this:
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. s&box borrows GameObject and Component from Unity and then diverges nearly everywhere after that: different lifecycle, different networking model, Z-up instead of Y-up, Razor for UI, a restricted subset of .NET. The resemblance is close enough to be dangerous.
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
}
}
I got tired of correcting the first one, so I built two things. Either is usable on its own.
The five that will cost you an afternoon
Every one of these compiles. Every one looks right in review.
void Update()is never called. The lifecycle method isprotected override void OnUpdate(), and yours is just a method nobody invokes.NetworkSpawn()gives ownership to whoever called it, not the host. A world object now belongs to one random client.[Sync]on a scene object does not live-replicate, becauseNetworkMode.Snapshotis the default. RPCs keep working perfectly, which is what makes it so hard to spot.- A client writing
[Sync(SyncFlags.FromHost)]has the write 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" )does not return null. A path that resolves to nothing comes back as the engine's error model, non-null withIsErrorset, 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're all first-week code.
The AI Agent Skill
17 markdown reference files that teach an agent the real API. Plain markdown, no runtime, no dependencies, so it works with any agent that can open a file. Claude Code, Cursor, or three lines in your own instructions file.
The entry point is a router rather than one flat document, deliberately. A flat API reference 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.
Every API in it is traceable to engine source at a named version, 26.08.05. One file is a different kind of claim from the other sixteen: it records what was observed to happen in a live editor session, with dates. That isn't the same as "this compiles", and the distance between the two is where most of the pain lives.
The MCP Server
18 tools that register into the MCP server s&box already ships inside the editor, on 127.0.0.1:7269. It solves a different silent failure: the editor not noticing you changed anything.
Six ways that happens, and not one of them raises an error:
.sbprojis read once at editor boot and never watched.ProjectSettings/*.configis cached on first read and never invalidated.- A
.csfile stops reaching the compiler once the file watchers go stale, which happens when the compilers are recreated in-process. - A content path gives you the error model rather than null, so your guard never fires.
- Nothing, you pulled a branch. The process serves whatever assembly it loaded at boot.
- A package installed over MCP.
install_packagemounts it for the session and writes nothing, so it isn't there next boot.
Each one leaves you having edited a file, seen no error, and concluding the edit was wrong. It usually wasn't. It never arrived. project_source_changes splits "my code is wrong" from "my edit never reached the compiler" in a single call, and from the console those two look identical.
The other group asks the running engine directly: does this type exist, what are its real members, what input actions does the project define. That inverts the skill's own rule. Instead of "if it's in none of the reference files it doesn't exist", you ask the engine, and that answer can't go stale.
Getting it
The MCP server installs as a package reference, fobiat.sbox_mcp_server in Project Settings, or as two C# files dropped into Editor/. The file drop is kept as a first-class option on purpose. Editor/ is compiled unsandboxed, which is what lets the toolset reach engine internals at all, and reading files like that before they compile into your project is a reasonable thing to want.
The skill is a folder of markdown. Copy it, submodule it, or install the repo as a Claude Code plugin marketplace.
- Package: https://sbox.game/fobiat/sbox_mcp_server/
- Repo: https://github.com/fobiat/sbox-skill
- Write-up: https://fobiat.dev/blog/the-skill-came-out-of-the-game/
MIT, built against engine 26.08.05. If you find a reference file that has drifted from the engine, the issue template asks for the one thing that matters: where you confirmed the truth. Stale guidance is worse than none.