sbox-mcp-server 1.2.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/LICENSE +21 -0
- package/README.md +90 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +155 -0
- package/dist/tools/assets.d.ts +8 -0
- package/dist/tools/assets.js +80 -0
- package/dist/tools/audio.d.ts +8 -0
- package/dist/tools/audio.js +101 -0
- package/dist/tools/components.d.ts +11 -0
- package/dist/tools/components.js +78 -0
- package/dist/tools/console.d.ts +8 -0
- package/dist/tools/console.js +59 -0
- package/dist/tools/discovery.d.ts +13 -0
- package/dist/tools/discovery.js +58 -0
- package/dist/tools/gameobjects.d.ts +4 -0
- package/dist/tools/gameobjects.js +197 -0
- package/dist/tools/materials.d.ts +8 -0
- package/dist/tools/materials.js +82 -0
- package/dist/tools/networking.d.ts +11 -0
- package/dist/tools/networking.js +227 -0
- package/dist/tools/physics.d.ts +8 -0
- package/dist/tools/physics.js +130 -0
- package/dist/tools/playmode.d.ts +11 -0
- package/dist/tools/playmode.js +140 -0
- package/dist/tools/prefabs.d.ts +8 -0
- package/dist/tools/prefabs.js +94 -0
- package/dist/tools/project.d.ts +12 -0
- package/dist/tools/project.js +90 -0
- package/dist/tools/publishing.d.ts +11 -0
- package/dist/tools/publishing.js +168 -0
- package/dist/tools/scenes.d.ts +8 -0
- package/dist/tools/scenes.js +75 -0
- package/dist/tools/scripts.d.ts +9 -0
- package/dist/tools/scripts.js +132 -0
- package/dist/tools/status.d.ts +8 -0
- package/dist/tools/status.js +49 -0
- package/dist/tools/templates.d.ts +11 -0
- package/dist/tools/templates.js +135 -0
- package/dist/tools/ui.d.ts +8 -0
- package/dist/tools/ui.js +116 -0
- package/dist/tools/world.d.ts +20 -0
- package/dist/tools/world.js +272 -0
- package/dist/transport/bridge-client.d.ts +60 -0
- package/dist/transport/bridge-client.js +239 -0
- package/package.json +54 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Physics tools: add_physics, add_collider, add_joint, raycast.
|
|
4
|
+
* Manages rigidbodies, colliders, physics constraints, and ray tracing.
|
|
5
|
+
*/
|
|
6
|
+
export function registerPhysicsTools(server, bridge) {
|
|
7
|
+
// ── add_physics ───────────────────────────────────────────────────
|
|
8
|
+
server.tool("add_physics", "Add a Rigidbody and collider to a GameObject, making it a dynamic physics object. Auto-selects BoxCollider if no collider type specified", {
|
|
9
|
+
id: z.string().describe("GUID of the GameObject"),
|
|
10
|
+
collider: z
|
|
11
|
+
.enum(["box", "sphere", "capsule", "mesh"])
|
|
12
|
+
.optional()
|
|
13
|
+
.describe("Collider type to add. Defaults to 'box'"),
|
|
14
|
+
mass: z
|
|
15
|
+
.number()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("Mass of the physics body in kg"),
|
|
18
|
+
gravity: z
|
|
19
|
+
.boolean()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("Whether gravity affects this object. Defaults to true"),
|
|
22
|
+
}, async (params) => {
|
|
23
|
+
const res = await bridge.send("add_physics", params);
|
|
24
|
+
if (!res.success) {
|
|
25
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
// ── add_collider ──────────────────────────────────────────────────
|
|
32
|
+
server.tool("add_collider", "Add a specific collider component to a GameObject. Supports box, sphere, capsule, mesh, and hull types. Can be configured as trigger", {
|
|
33
|
+
id: z.string().describe("GUID of the GameObject"),
|
|
34
|
+
type: z
|
|
35
|
+
.enum(["box", "sphere", "capsule", "mesh", "hull"])
|
|
36
|
+
.describe("Type of collider to add"),
|
|
37
|
+
isTrigger: z
|
|
38
|
+
.boolean()
|
|
39
|
+
.optional()
|
|
40
|
+
.describe("If true, the collider acts as a trigger (no physics collision). Defaults to false"),
|
|
41
|
+
size: z
|
|
42
|
+
.object({ x: z.number(), y: z.number(), z: z.number() })
|
|
43
|
+
.optional()
|
|
44
|
+
.describe("Size for BoxCollider (x, y, z dimensions)"),
|
|
45
|
+
radius: z
|
|
46
|
+
.number()
|
|
47
|
+
.optional()
|
|
48
|
+
.describe("Radius for SphereCollider or CapsuleCollider"),
|
|
49
|
+
height: z
|
|
50
|
+
.number()
|
|
51
|
+
.optional()
|
|
52
|
+
.describe("Height/length for CapsuleCollider"),
|
|
53
|
+
}, async (params) => {
|
|
54
|
+
const res = await bridge.send("add_collider", params);
|
|
55
|
+
if (!res.success) {
|
|
56
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
// ── add_joint ─────────────────────────────────────────────────────
|
|
63
|
+
server.tool("add_joint", "Add a physics joint/constraint between two GameObjects. Supports fixed, spring, and slider joint types", {
|
|
64
|
+
id: z
|
|
65
|
+
.string()
|
|
66
|
+
.describe("GUID of the GameObject to add the joint to"),
|
|
67
|
+
type: z
|
|
68
|
+
.enum(["fixed", "spring", "slider"])
|
|
69
|
+
.describe("Type of joint to create"),
|
|
70
|
+
targetId: z
|
|
71
|
+
.string()
|
|
72
|
+
.optional()
|
|
73
|
+
.describe("GUID of the target GameObject to connect to"),
|
|
74
|
+
frequency: z
|
|
75
|
+
.number()
|
|
76
|
+
.optional()
|
|
77
|
+
.describe("Spring frequency (spring joints only)"),
|
|
78
|
+
damping: z
|
|
79
|
+
.number()
|
|
80
|
+
.optional()
|
|
81
|
+
.describe("Damping ratio (spring joints only). 0 = no damping, 1 = critical"),
|
|
82
|
+
}, async (params) => {
|
|
83
|
+
const res = await bridge.send("add_joint", params);
|
|
84
|
+
if (!res.success) {
|
|
85
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
// ── raycast ───────────────────────────────────────────────────────
|
|
92
|
+
server.tool("raycast", "Perform a physics raycast (Scene.Trace.Ray) and return hit results. Useful for line-of-sight checks, object placement, and collision detection", {
|
|
93
|
+
start: z
|
|
94
|
+
.object({ x: z.number(), y: z.number(), z: z.number() })
|
|
95
|
+
.describe("Ray start position (world space)"),
|
|
96
|
+
end: z
|
|
97
|
+
.object({ x: z.number(), y: z.number(), z: z.number() })
|
|
98
|
+
.optional()
|
|
99
|
+
.describe("Ray end position. Use either end or direction+maxDistance"),
|
|
100
|
+
direction: z
|
|
101
|
+
.object({ x: z.number(), y: z.number(), z: z.number() })
|
|
102
|
+
.optional()
|
|
103
|
+
.describe("Ray direction (normalized). Used with maxDistance instead of end"),
|
|
104
|
+
maxDistance: z
|
|
105
|
+
.number()
|
|
106
|
+
.optional()
|
|
107
|
+
.describe("Maximum ray distance when using direction. Defaults to 10000"),
|
|
108
|
+
radius: z
|
|
109
|
+
.number()
|
|
110
|
+
.optional()
|
|
111
|
+
.describe("Sphere/box trace radius. 0 = thin ray (default)"),
|
|
112
|
+
ignoreIds: z
|
|
113
|
+
.array(z.string())
|
|
114
|
+
.optional()
|
|
115
|
+
.describe("GUIDs of GameObjects to ignore"),
|
|
116
|
+
all: z
|
|
117
|
+
.boolean()
|
|
118
|
+
.optional()
|
|
119
|
+
.describe("If true, returns all hits along the ray. Defaults to false (first hit only)"),
|
|
120
|
+
}, async (params) => {
|
|
121
|
+
const res = await bridge.send("raycast", params);
|
|
122
|
+
if (!res.success) {
|
|
123
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=physics.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { BridgeClient } from "../transport/bridge-client.js";
|
|
3
|
+
/**
|
|
4
|
+
* Play mode control and related editor tools.
|
|
5
|
+
*
|
|
6
|
+
* Beyond play/pause/resume/stop, this file also registers: set_property
|
|
7
|
+
* (editor-mode property writes), get/set_runtime_property (live tweaking
|
|
8
|
+
* during play mode), take_screenshot, and undo/redo.
|
|
9
|
+
*/
|
|
10
|
+
export declare function registerPlayModeTools(server: McpServer, bridge: BridgeClient): void;
|
|
11
|
+
//# sourceMappingURL=playmode.d.ts.map
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Play mode control and related editor tools.
|
|
4
|
+
*
|
|
5
|
+
* Beyond play/pause/resume/stop, this file also registers: set_property
|
|
6
|
+
* (editor-mode property writes), get/set_runtime_property (live tweaking
|
|
7
|
+
* during play mode), take_screenshot, and undo/redo.
|
|
8
|
+
*/
|
|
9
|
+
export function registerPlayModeTools(server, bridge) {
|
|
10
|
+
// ── start_play ───────────────────────────────────────────────────
|
|
11
|
+
server.tool("start_play", "Enter play mode — starts running the game in the editor. Scripts execute, physics simulate, everything goes live", {}, async () => {
|
|
12
|
+
const res = await bridge.send("start_play");
|
|
13
|
+
if (!res.success) {
|
|
14
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
// ── stop_play ────────────────────────────────────────────────────
|
|
21
|
+
server.tool("stop_play", "Exit play mode — stops the game and returns to editor. All runtime changes are discarded", {}, async () => {
|
|
22
|
+
const res = await bridge.send("stop_play");
|
|
23
|
+
if (!res.success) {
|
|
24
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
// ── pause_play ───────────────────────────────────────────────────
|
|
31
|
+
server.tool("pause_play", "Pause the running game — freezes simulation but stays in play mode. Use resume_play to continue", {}, async () => {
|
|
32
|
+
const res = await bridge.send("pause_play");
|
|
33
|
+
if (!res.success) {
|
|
34
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
// ── resume_play ──────────────────────────────────────────────────
|
|
41
|
+
server.tool("resume_play", "Resume a paused game — unfreezes simulation", {}, async () => {
|
|
42
|
+
const res = await bridge.send("resume_play");
|
|
43
|
+
if (!res.success) {
|
|
44
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
// ── is_playing ───────────────────────────────────────────────────
|
|
51
|
+
server.tool("is_playing", "Check current play state — returns 'playing', 'paused', or 'stopped'", {}, async () => {
|
|
52
|
+
const res = await bridge.send("is_playing");
|
|
53
|
+
if (!res.success) {
|
|
54
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
// ── take_screenshot ──────────────────────────────────────────────
|
|
61
|
+
server.tool("take_screenshot", "Capture the current editor viewport as a PNG screenshot", {
|
|
62
|
+
path: z
|
|
63
|
+
.string()
|
|
64
|
+
.optional()
|
|
65
|
+
.describe("Output path (e.g. 'screenshots/test.png'). Defaults to screenshots/screenshot_{timestamp}.png"),
|
|
66
|
+
}, async (params) => {
|
|
67
|
+
const res = await bridge.send("take_screenshot", params);
|
|
68
|
+
if (!res.success) {
|
|
69
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
// ── undo ─────────────────────────────────────────────────────────
|
|
76
|
+
server.tool("undo", "Undo the last editor action. Safety net for when a change goes wrong", {}, async () => {
|
|
77
|
+
const res = await bridge.send("undo");
|
|
78
|
+
if (!res.success) {
|
|
79
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
// ── redo ─────────────────────────────────────────────────────────
|
|
86
|
+
server.tool("redo", "Redo the last undone editor action", {}, async () => {
|
|
87
|
+
const res = await bridge.send("redo");
|
|
88
|
+
if (!res.success) {
|
|
89
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
// ── set_property ─────────────────────────────────────────────────
|
|
96
|
+
server.tool("set_property", "Set a property value on a component (editor mode). Reads the value back to confirm", {
|
|
97
|
+
id: z.string().describe("GUID of the GameObject"),
|
|
98
|
+
component: z.string().describe("Component type name"),
|
|
99
|
+
property: z.string().describe("Property name to set"),
|
|
100
|
+
value: z.unknown().describe("New value — auto-converted to the correct type"),
|
|
101
|
+
}, async (params) => {
|
|
102
|
+
const res = await bridge.send("set_property", params);
|
|
103
|
+
if (!res.success) {
|
|
104
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
// ── get_runtime_property ─────────────────────────────────────────
|
|
111
|
+
server.tool("get_runtime_property", "Read a component property value during play mode. Must call start_play first", {
|
|
112
|
+
id: z.string().describe("GUID of the GameObject"),
|
|
113
|
+
component: z.string().describe("Component type name"),
|
|
114
|
+
property: z.string().describe("Property name to read"),
|
|
115
|
+
}, async (params) => {
|
|
116
|
+
const res = await bridge.send("get_runtime_property", params);
|
|
117
|
+
if (!res.success) {
|
|
118
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
// ── set_runtime_property ─────────────────────────────────────────
|
|
125
|
+
server.tool("set_runtime_property", "Set a component property value during play mode — tweak values live while the game runs", {
|
|
126
|
+
id: z.string().describe("GUID of the GameObject"),
|
|
127
|
+
component: z.string().describe("Component type name"),
|
|
128
|
+
property: z.string().describe("Property name to set"),
|
|
129
|
+
value: z.unknown().describe("New value"),
|
|
130
|
+
}, async (params) => {
|
|
131
|
+
const res = await bridge.send("set_runtime_property", params);
|
|
132
|
+
if (!res.success) {
|
|
133
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=playmode.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { BridgeClient } from "../transport/bridge-client.js";
|
|
3
|
+
/**
|
|
4
|
+
* Prefab tools: create_prefab, instantiate_prefab, list_prefabs, get_prefab_info.
|
|
5
|
+
* Manages .prefab files — saving GameObjects as reusable templates and spawning instances.
|
|
6
|
+
*/
|
|
7
|
+
export declare function registerPrefabTools(server: McpServer, bridge: BridgeClient): void;
|
|
8
|
+
//# sourceMappingURL=prefabs.d.ts.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Prefab tools: create_prefab, instantiate_prefab, list_prefabs, get_prefab_info.
|
|
4
|
+
* Manages .prefab files — saving GameObjects as reusable templates and spawning instances.
|
|
5
|
+
*/
|
|
6
|
+
export function registerPrefabTools(server, bridge) {
|
|
7
|
+
// ── create_prefab ─────────────────────────────────────────────────
|
|
8
|
+
server.tool("create_prefab", "Save an existing GameObject as a reusable .prefab file. The prefab can be instantiated later", {
|
|
9
|
+
id: z.string().describe("GUID of the GameObject to save as prefab"),
|
|
10
|
+
path: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe("Path for the prefab file relative to project root (e.g. 'prefabs/enemies/grunt.prefab')"),
|
|
13
|
+
}, async (params) => {
|
|
14
|
+
const res = await bridge.send("create_prefab", params);
|
|
15
|
+
if (!res.success) {
|
|
16
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
// ── instantiate_prefab ────────────────────────────────────────────
|
|
23
|
+
server.tool("instantiate_prefab", "Spawn a prefab instance into the active scene at an optional position and rotation", {
|
|
24
|
+
path: z
|
|
25
|
+
.string()
|
|
26
|
+
.describe("Path to the .prefab file (e.g. 'prefabs/enemies/grunt.prefab')"),
|
|
27
|
+
position: z
|
|
28
|
+
.object({
|
|
29
|
+
x: z.number(),
|
|
30
|
+
y: z.number(),
|
|
31
|
+
z: z.number(),
|
|
32
|
+
})
|
|
33
|
+
.optional()
|
|
34
|
+
.describe("World position to spawn at. Defaults to origin"),
|
|
35
|
+
rotation: z
|
|
36
|
+
.object({
|
|
37
|
+
pitch: z.number(),
|
|
38
|
+
yaw: z.number(),
|
|
39
|
+
roll: z.number(),
|
|
40
|
+
})
|
|
41
|
+
.optional()
|
|
42
|
+
.describe("Rotation as euler angles. Defaults to identity"),
|
|
43
|
+
scale: z
|
|
44
|
+
.number()
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("Uniform scale multiplier. Defaults to 1.0"),
|
|
47
|
+
parent: z
|
|
48
|
+
.string()
|
|
49
|
+
.optional()
|
|
50
|
+
.describe("GUID of parent GameObject to attach to"),
|
|
51
|
+
}, async (params) => {
|
|
52
|
+
const res = await bridge.send("instantiate_prefab", params);
|
|
53
|
+
if (!res.success) {
|
|
54
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
// ── list_prefabs ──────────────────────────────────────────────────
|
|
61
|
+
server.tool("list_prefabs", "List all .prefab files in the project. Filter by name or path", {
|
|
62
|
+
filter: z
|
|
63
|
+
.string()
|
|
64
|
+
.optional()
|
|
65
|
+
.describe("Search filter for prefab name or path"),
|
|
66
|
+
maxResults: z
|
|
67
|
+
.number()
|
|
68
|
+
.optional()
|
|
69
|
+
.describe("Maximum results to return. Defaults to 100"),
|
|
70
|
+
}, async (params) => {
|
|
71
|
+
const res = await bridge.send("list_prefabs", params);
|
|
72
|
+
if (!res.success) {
|
|
73
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
// ── get_prefab_info ───────────────────────────────────────────────
|
|
80
|
+
server.tool("get_prefab_info", "Get detailed information about a prefab file including its JSON contents and metadata", {
|
|
81
|
+
path: z
|
|
82
|
+
.string()
|
|
83
|
+
.describe("Path to the .prefab file (e.g. 'prefabs/enemies/grunt.prefab')"),
|
|
84
|
+
}, async (params) => {
|
|
85
|
+
const res = await bridge.send("get_prefab_info", params);
|
|
86
|
+
if (!res.success) {
|
|
87
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=prefabs.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { BridgeClient } from "../transport/bridge-client.js";
|
|
3
|
+
/**
|
|
4
|
+
* Project and file management tools: get_project_info, list_project_files,
|
|
5
|
+
* read_file, write_file.
|
|
6
|
+
*
|
|
7
|
+
* This is the simplest tool file and serves as a good template for contributors.
|
|
8
|
+
* Every tool follows the same pattern: define with server.tool(), send the
|
|
9
|
+
* command to the Bridge, and return the result (or error) as text content.
|
|
10
|
+
*/
|
|
11
|
+
export declare function registerProjectTools(server: McpServer, bridge: BridgeClient): void;
|
|
12
|
+
//# sourceMappingURL=project.d.ts.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Project and file management tools: get_project_info, list_project_files,
|
|
4
|
+
* read_file, write_file.
|
|
5
|
+
*
|
|
6
|
+
* This is the simplest tool file and serves as a good template for contributors.
|
|
7
|
+
* Every tool follows the same pattern: define with server.tool(), send the
|
|
8
|
+
* command to the Bridge, and return the result (or error) as text content.
|
|
9
|
+
*/
|
|
10
|
+
export function registerProjectTools(server, bridge) {
|
|
11
|
+
// ── get_project_info ─────────────────────────────────────────────
|
|
12
|
+
server.tool("get_project_info", "Get information about the current s&box project — path, name, game type, dependencies, and configuration", {}, async () => {
|
|
13
|
+
const res = await bridge.send("get_project_info");
|
|
14
|
+
if (!res.success) {
|
|
15
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
content: [
|
|
19
|
+
{ type: "text", text: JSON.stringify(res.data, null, 2) },
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
// ── list_project_files ───────────────────────────────────────────
|
|
24
|
+
server.tool("list_project_files", "Browse the project file tree. Optionally filter by directory path and/or file extension (e.g. '.cs', '.scene')", {
|
|
25
|
+
path: z
|
|
26
|
+
.string()
|
|
27
|
+
.optional()
|
|
28
|
+
.describe("Relative directory path to list (e.g. 'code/Components'). Defaults to project root"),
|
|
29
|
+
extension: z
|
|
30
|
+
.string()
|
|
31
|
+
.optional()
|
|
32
|
+
.describe("Filter by file extension, including the dot (e.g. '.cs', '.scene')"),
|
|
33
|
+
recursive: z
|
|
34
|
+
.boolean()
|
|
35
|
+
.optional()
|
|
36
|
+
.describe("Whether to list files recursively. Defaults to true"),
|
|
37
|
+
}, async (params) => {
|
|
38
|
+
const res = await bridge.send("list_project_files", params);
|
|
39
|
+
if (!res.success) {
|
|
40
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
content: [
|
|
44
|
+
{ type: "text", text: JSON.stringify(res.data, null, 2) },
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
// ── read_file ────────────────────────────────────────────────────
|
|
49
|
+
server.tool("read_file", "Read the contents of a file in the s&box project (scripts, scenes, configs, etc.)", {
|
|
50
|
+
path: z
|
|
51
|
+
.string()
|
|
52
|
+
.describe("Relative path to the file within the project (e.g. 'code/PlayerController.cs')"),
|
|
53
|
+
}, async (params) => {
|
|
54
|
+
const res = await bridge.send("read_file", params);
|
|
55
|
+
if (!res.success) {
|
|
56
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
content: [
|
|
60
|
+
{
|
|
61
|
+
type: "text",
|
|
62
|
+
text: typeof res.data === "string"
|
|
63
|
+
? res.data
|
|
64
|
+
: JSON.stringify(res.data, null, 2),
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
// ── write_file ───────────────────────────────────────────────────
|
|
70
|
+
server.tool("write_file", "Write or overwrite a file in the s&box project. Creates parent directories as needed", {
|
|
71
|
+
path: z
|
|
72
|
+
.string()
|
|
73
|
+
.describe("Relative path for the file (e.g. 'code/Components/Health.cs')"),
|
|
74
|
+
content: z.string().describe("The full file content to write"),
|
|
75
|
+
}, async (params) => {
|
|
76
|
+
const res = await bridge.send("write_file", params);
|
|
77
|
+
if (!res.success) {
|
|
78
|
+
return { content: [{ type: "text", text: `Error: ${res.error}` }] };
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
content: [
|
|
82
|
+
{
|
|
83
|
+
type: "text",
|
|
84
|
+
text: `File written successfully: ${params.path}`,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=project.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { BridgeClient } from "../transport/bridge-client.js";
|
|
3
|
+
/**
|
|
4
|
+
* Publishing tools: get_project_config, set_project_config, validate_project,
|
|
5
|
+
* build_project, get_build_status, clean_build, export_project,
|
|
6
|
+
* set_project_thumbnail, get_package_details, prepare_publish.
|
|
7
|
+
*
|
|
8
|
+
* Manages project configuration, building, exporting, and publishing preparation.
|
|
9
|
+
*/
|
|
10
|
+
export declare function registerPublishingTools(server: McpServer, bridge: BridgeClient): void;
|
|
11
|
+
//# sourceMappingURL=publishing.d.ts.map
|