sbox-mcp-server 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/dist/index.js +2 -0
- package/dist/tools/inspection.d.ts +17 -0
- package/dist/tools/inspection.js +109 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# sbox-mcp-server
|
|
2
2
|
|
|
3
|
-
MCP Server for the s&box game engine. Lets Claude Code build s&box games through conversation — **
|
|
3
|
+
MCP Server for the s&box game engine. Lets Claude Code build s&box games through conversation — **170+ tools** for scenes, scripts, GameObjects, components, assets, materials, audio, physics, UI, networking, publishing, world-gen, lighting & atmosphere, characters, scene layout, navmesh & spatial queries, particles, animation, NPC brains, playable-game scaffolds, networking & scene inspection/lint, save & services queries, self-diagnosis, console/C# execution, live docs search, and type discovery.
|
|
4
4
|
|
|
5
5
|
## Fastest install — the Claude Code plugin
|
|
6
6
|
|
|
7
|
-
If you use Claude Code, the easiest install is the companion plugin. It registers this MCP server automatically, ships
|
|
7
|
+
If you use Claude Code, the easiest install is the companion plugin. It registers this MCP server automatically, ships the workflow + recipe skills (`sbox-build-feature`, `sbox-api`, `sbox-cookbook`, `sbox-scaffold-game`, `sbox-setup`), and includes the `sbox-game-dev` specialist agent.
|
|
8
8
|
|
|
9
9
|
```
|
|
10
10
|
/plugin marketplace add LouSputthole/Sbox-Claude
|
|
@@ -54,7 +54,7 @@ Claude Code → (stdio) → sbox-mcp-server → (file IPC) → bridge addon →
|
|
|
54
54
|
|
|
55
55
|
Communication uses file-based IPC through `%TEMP%/sbox-bridge-ipc/`. The MCP server writes request JSON files, the bridge addon (running inside s&box) polls and processes on the main editor thread, then writes response files back. WebSocket is not used — s&box's sandboxed C# environment blocks `System.Net`.
|
|
56
56
|
|
|
57
|
-
## Tools (v1.
|
|
57
|
+
## Tools (v1.9.0)
|
|
58
58
|
|
|
59
59
|
`get_bridge_status` reports the `handlerCount` — that's the C# handlers compiled inside the editor. Six tools run **MCP-server-side** and need no editor handler: `read_log`, `get_compile_errors`, `execute_csharp`, `search_docs`, `get_doc_page`, `list_doc_categories`. They read the log / hotload-eval / fetch docs directly, so they keep working even when the editor has crashed or stalled.
|
|
60
60
|
|
|
@@ -99,6 +99,7 @@ Communication uses file-based IPC through `%TEMP%/sbox-bridge-ipc/`. The MCP ser
|
|
|
99
99
|
| **Console / Exec** *(v1.5.0)* | console_run, execute_csharp *(experimental)* |
|
|
100
100
|
| **Object utilities** *(v1.5.0)* | remove_component, get_tags |
|
|
101
101
|
| **Docs search** *(v1.5.0, MCP-server-side)* | search_docs, get_doc_page, list_doc_categories — official `Facepunch/sbox-docs` |
|
|
102
|
+
| **Inspection & validation** *(v1.9.0)* | inspect_networked_object (per-object `Network.*` + every component's `[Sync]` fields/values), networking_lint (static scan for `[Sync]`/RPC footguns), scene_validate (no-camera / stray root Rigidbody / trigger-vs-trace), save_inspect (list/read/diff `FileSystem.Data` saves), services_query (`Sandbox.Services` stats + leaderboards), simulate_input (drive named input actions in play mode) |
|
|
102
103
|
|
|
103
104
|
## Working with Claude effectively
|
|
104
105
|
|
|
@@ -126,6 +127,6 @@ The companion plugin's `sbox-build-feature` skill encodes this workflow plus the
|
|
|
126
127
|
|
|
127
128
|
## License
|
|
128
129
|
|
|
129
|
-
**
|
|
130
|
+
**AGPL-3.0-or-later** — see [LICENSE](../LICENSE) and [NOTICE](../NOTICE) for details. The code is open under AGPL; the "s&box Claude Bridge" / "sboxskins.gg" name and branding may not be reused to pass a fork off as the original.
|
|
130
131
|
|
|
131
132
|
Copyright (c) 2026 [sboxskins.gg](https://sboxskins.gg)
|
package/dist/index.js
CHANGED
|
@@ -43,6 +43,7 @@ import { registerNavigationTools } from "./tools/navigation.js";
|
|
|
43
43
|
import { registerSelfTestTools } from "./tools/selftest.js";
|
|
44
44
|
import { registerGameplayTools } from "./tools/gameplay.js";
|
|
45
45
|
import { registerNpcTools } from "./tools/npc.js";
|
|
46
|
+
import { registerInspectionTools } from "./tools/inspection.js";
|
|
46
47
|
// ── CLI flags ──────────────────────────────────────────────────────
|
|
47
48
|
const args = process.argv.slice(2);
|
|
48
49
|
/** Read the package version from package.json, or return "unknown" on failure. */
|
|
@@ -189,6 +190,7 @@ registerNavigationTools(server, bridge);
|
|
|
189
190
|
registerSelfTestTools(server, bridge);
|
|
190
191
|
registerGameplayTools(server, bridge);
|
|
191
192
|
registerNpcTools(server, bridge);
|
|
193
|
+
registerInspectionTools(server, bridge);
|
|
192
194
|
/** Start the MCP server on stdio and attempt initial Bridge connection. */
|
|
193
195
|
async function main() {
|
|
194
196
|
const transport = new StdioServerTransport();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { BridgeClient } from "../transport/bridge-client.js";
|
|
3
|
+
/**
|
|
4
|
+
* Inspection & validation tools — the high-leverage gaps surfaced by mining 27 shipped
|
|
5
|
+
* s&box games. These let an assistant VERIFY the things that are otherwise invisible from
|
|
6
|
+
* a single editor instance: networking authority, save state, input-driven behavior, and
|
|
7
|
+
* scene/networking correctness.
|
|
8
|
+
*
|
|
9
|
+
* inspect_networked_object — per-object Network.Owner/IsProxy/[Sync] values (vs session-only get_network_status)
|
|
10
|
+
* networking_lint — static scan for unguarded [Sync] mutators, money-as-[Sync], List-as-[Sync], etc.
|
|
11
|
+
* scene_validate — missing camera/controller, multiple root Rigidbodies, IsTrigger-vs-trace footguns
|
|
12
|
+
* save_inspect — list/read/diff FileSystem.Data save files (21 of 27 games persist; assistant was blind)
|
|
13
|
+
* services_query — Sandbox.Services Stats / leaderboard reads
|
|
14
|
+
* simulate_input — press/release a named action + AnalogMove/Look during play (drive movement/IPressable/weapons)
|
|
15
|
+
*/
|
|
16
|
+
export declare function registerInspectionTools(server: McpServer, bridge: BridgeClient): void;
|
|
17
|
+
//# sourceMappingURL=inspection.d.ts.map
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Inspection & validation tools — the high-leverage gaps surfaced by mining 27 shipped
|
|
4
|
+
* s&box games. These let an assistant VERIFY the things that are otherwise invisible from
|
|
5
|
+
* a single editor instance: networking authority, save state, input-driven behavior, and
|
|
6
|
+
* scene/networking correctness.
|
|
7
|
+
*
|
|
8
|
+
* inspect_networked_object — per-object Network.Owner/IsProxy/[Sync] values (vs session-only get_network_status)
|
|
9
|
+
* networking_lint — static scan for unguarded [Sync] mutators, money-as-[Sync], List-as-[Sync], etc.
|
|
10
|
+
* scene_validate — missing camera/controller, multiple root Rigidbodies, IsTrigger-vs-trace footguns
|
|
11
|
+
* save_inspect — list/read/diff FileSystem.Data save files (21 of 27 games persist; assistant was blind)
|
|
12
|
+
* services_query — Sandbox.Services Stats / leaderboard reads
|
|
13
|
+
* simulate_input — press/release a named action + AnalogMove/Look during play (drive movement/IPressable/weapons)
|
|
14
|
+
*/
|
|
15
|
+
export function registerInspectionTools(server, bridge) {
|
|
16
|
+
// ── inspect_networked_object ──────────────────────────────────────
|
|
17
|
+
server.tool("inspect_networked_object", "Inspect the live networking contract of a GameObject: Network.Owner/IsProxy/IsOwner/Active, OwnerTransfer + OrphanedMode, and every component's [Sync] property names, SyncFlags, and current values (host vs proxy). Unlike get_network_status (session-only), this is per-object — the way to verify a host-authoritative or ownership change actually replicated. Works in edit or play mode.", {
|
|
18
|
+
id: z.string().describe("GUID of the GameObject to inspect"),
|
|
19
|
+
allProps: z
|
|
20
|
+
.boolean()
|
|
21
|
+
.default(false)
|
|
22
|
+
.describe("Include all component properties, not just [Sync]-marked ones"),
|
|
23
|
+
}, async (params) => {
|
|
24
|
+
const res = await bridge.send("inspect_networked_object", params);
|
|
25
|
+
if (!res.success)
|
|
26
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
27
|
+
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
|
|
28
|
+
});
|
|
29
|
+
// ── networking_lint ───────────────────────────────────────────────
|
|
30
|
+
server.tool("networking_lint", "Static-scan the project's C# for the highest-frequency networking/authority bugs: a mutator that writes a [Sync] field with no IsProxy/Networking.IsHost guard; money/health/score-shaped fields marked plain [Sync] (should be SyncFlags.FromHost); List<>/Dictionary<> marked [Sync] (should be NetList/NetDictionary); [Sync] fields typed Connection/GameObject (sync a Guid instead); [Rpc.Host] methods that mutate without re-checking Rpc.Caller; and component swaps / reflection writes missing Network.Refresh(). Returns findings with file:line + the suggested fix.", {
|
|
31
|
+
path: z
|
|
32
|
+
.string()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe("Optional sub-path under the project (e.g. 'Code/Player') to scope the scan; omit for the whole project"),
|
|
35
|
+
}, async (params) => {
|
|
36
|
+
const res = await bridge.send("networking_lint", params);
|
|
37
|
+
if (!res.success)
|
|
38
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
39
|
+
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
|
|
40
|
+
});
|
|
41
|
+
// ── scene_validate ────────────────────────────────────────────────
|
|
42
|
+
server.tool("scene_validate", "Validate the active scene for the silent setup footguns that break controllers/physics/cameras: no CameraComponent, no player controller, multiple root Rigidbodies, a Rigidbody with MotionEnabled=false fighting a kinematic root, IsTrigger colliders that Scene.Trace will ignore, child Rigidbodies breaking collider binding, and missing required child anchors. Returns each issue with the GameObject and the exact fix.", {}, async () => {
|
|
43
|
+
const res = await bridge.send("scene_validate", {});
|
|
44
|
+
if (!res.success)
|
|
45
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
46
|
+
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
|
|
47
|
+
});
|
|
48
|
+
// ── save_inspect ──────────────────────────────────────────────────
|
|
49
|
+
server.tool("save_inspect", "Inspect the game's FileSystem.Data save files — the assistant is otherwise blind to persisted state. action='list' enumerates files under a folder (with size/mtime); action='read' dumps a save file's JSON; action='diff' compares two save files key-by-key. Use to verify a save actually wrote, debug a load/migration, or confirm a sanitize/clamp ran.", {
|
|
50
|
+
action: z.enum(["list", "read", "diff"]).default("list"),
|
|
51
|
+
path: z
|
|
52
|
+
.string()
|
|
53
|
+
.optional()
|
|
54
|
+
.describe("File or folder path under FileSystem.Data (e.g. 'lumber_corp2_progress' or '<folder>/steam_123.json')"),
|
|
55
|
+
pathB: z
|
|
56
|
+
.string()
|
|
57
|
+
.optional()
|
|
58
|
+
.describe("Second file path for action='diff'"),
|
|
59
|
+
}, async (params) => {
|
|
60
|
+
const res = await bridge.send("save_inspect", params);
|
|
61
|
+
if (!res.success)
|
|
62
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
63
|
+
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
|
|
64
|
+
});
|
|
65
|
+
// ── services_query ────────────────────────────────────────────────
|
|
66
|
+
server.tool("services_query", "Read from Sandbox.Services — the cloud stats/leaderboard layer many games use as their real DB. action='stats' lists/reads the project's stat definitions and the local player's values; action='leaderboard' fetches a leaderboard board's top entries. Read-only. Use to verify a Stats.Increment/SetValue path or a leaderboard wired correctly.", {
|
|
67
|
+
action: z.enum(["stats", "leaderboard"]).default("stats"),
|
|
68
|
+
name: z
|
|
69
|
+
.string()
|
|
70
|
+
.optional()
|
|
71
|
+
.describe("Stat name (action='stats') or leaderboard/board name (action='leaderboard')"),
|
|
72
|
+
limit: z.number().int().default(10).describe("Max leaderboard entries to return"),
|
|
73
|
+
}, async (params) => {
|
|
74
|
+
const res = await bridge.send("services_query", params);
|
|
75
|
+
if (!res.success)
|
|
76
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
77
|
+
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
|
|
78
|
+
});
|
|
79
|
+
// ── simulate_input ────────────────────────────────────────────────
|
|
80
|
+
server.tool("simulate_input", "Synthesize player input during PLAY mode so behavior can be verified without a human at the keyboard: press/hold/release a named input action (e.g. 'jump','attack1','use','reload') and/or set AnalogMove / AnalogLook for a duration. Drives movement, IPressable interactions, weapon fire/cooldown/reload, vehicle controls, and HUD advance-on-click. Requires play mode.", {
|
|
81
|
+
action: z
|
|
82
|
+
.string()
|
|
83
|
+
.optional()
|
|
84
|
+
.describe("Named input action to drive (must exist in Input.config), e.g. 'jump', 'attack1', 'use'"),
|
|
85
|
+
state: z
|
|
86
|
+
.enum(["press", "hold", "release"])
|
|
87
|
+
.default("press")
|
|
88
|
+
.describe("press = one tick (Pressed); hold = held for durationMs; release = clear a held action"),
|
|
89
|
+
analogMove: z
|
|
90
|
+
.object({ x: z.number(), y: z.number(), z: z.number().default(0) })
|
|
91
|
+
.optional()
|
|
92
|
+
.describe("AnalogMove vector to apply (forward/left)"),
|
|
93
|
+
analogLook: z
|
|
94
|
+
.object({ pitch: z.number(), yaw: z.number(), roll: z.number().default(0) })
|
|
95
|
+
.optional()
|
|
96
|
+
.describe("AnalogLook angles delta to apply"),
|
|
97
|
+
durationMs: z
|
|
98
|
+
.number()
|
|
99
|
+
.int()
|
|
100
|
+
.default(100)
|
|
101
|
+
.describe("How long to hold the action / apply the analog values"),
|
|
102
|
+
}, async (params) => {
|
|
103
|
+
const res = await bridge.send("simulate_input", params);
|
|
104
|
+
if (!res.success)
|
|
105
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
106
|
+
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=inspection.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sbox-mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "MCP Server for s&box game engine — enables Claude to build games through conversation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"engines": {
|
|
52
52
|
"node": ">=18.0.0"
|
|
53
53
|
},
|
|
54
|
-
"license": "
|
|
54
|
+
"license": "AGPL-3.0-or-later"
|
|
55
55
|
}
|