sbox-mcp-server 1.7.0 → 1.8.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.
@@ -114,7 +114,7 @@ export function registerNetworkingTools(server, bridge) {
114
114
  syncFlags: z
115
115
  .string()
116
116
  .optional()
117
- .describe("Currently ignorednot yet implemented. The addon emits a plain [Sync] with no SyncFlags"),
117
+ .describe("Optional SyncFlags to emit as [Sync( SyncFlags.X )] e.g. 'Interpolate' (smooth interpolation), 'Query', 'FromHost'. Omit for a plain [Sync]"),
118
118
  defaultValue: z
119
119
  .string()
120
120
  .optional()
@@ -129,7 +129,7 @@ export function registerNetworkingTools(server, bridge) {
129
129
  };
130
130
  });
131
131
  // ── add_rpc_method ────────────────────────────────────────────────
132
- server.tool("add_rpc_method", "Generate an EMPTY, no-argument RPC method stub in a C# script for you to fill in. The addon inserts the chosen RPC attribute ([Rpc.Broadcast] all clients, [Rpc.Host] host only, [Rpc.Owner] owner only) above a parameterless method with an empty body it does NOT add parameters or generate body code", {
132
+ server.tool("add_rpc_method", "Generate an RPC method stub in a C# script. Inserts the chosen RPC attribute ([Rpc.Broadcast] all clients, [Rpc.Host] host only, [Rpc.Owner] owner only) above a method with an empty body. Pass methodParams to give it a parameter list (e.g. 'Vector3 pos, int damage'); the body is left as a TODO for you to fill in", {
133
133
  path: z
134
134
  .string()
135
135
  .describe("Relative path to the script file"),
@@ -141,7 +141,7 @@ export function registerNetworkingTools(server, bridge) {
141
141
  methodParams: z
142
142
  .string()
143
143
  .optional()
144
- .describe("Currently ignored not yet implemented. The addon emits a no-argument method; add parameters yourself afterward"),
144
+ .describe("Optional parameter list for the RPC method signature, e.g. 'Vector3 pos, int damage'. Omit for a parameterless method"),
145
145
  body: z
146
146
  .string()
147
147
  .optional()
@@ -82,7 +82,7 @@ export function registerPlayModeTools(server, bridge) {
82
82
  property: z.string().describe("Property name to set"),
83
83
  value: z
84
84
  .unknown()
85
- .describe("New value as a string. Primitive: '5', 'true'. Color/Vector3: '1,0,0,1' / '0,0,200'. Enum: the member name. Asset ref (Model/Material/...): the asset path e.g. 'models/dev/box.vmdl'. GameObject/Component ref: the target GameObject's GUID. Empty/'null' clears the property"),
85
+ .describe("New value. Primitive: '5', 'true'. Color/Vector3: a comma string ('1,0,0,1' / '0,0,200'), an array ([0,0,200]), or an object ({r,g,b,a} / {x,y,z}). Enum: the member name. Asset ref (Model/Material/...): the asset path e.g. 'models/dev/box.vmdl'. GameObject/Component ref: the target GameObject's GUID. Empty/'null' clears the property"),
86
86
  }, async (params) => {
87
87
  const res = await bridge.send("set_property", params);
88
88
  if (!res.success) {
@@ -61,30 +61,30 @@ export function registerVisualTools(server, bridge) {
61
61
  };
62
62
  });
63
63
  // ── set_fog ────────────────────────────────────────────────────────
64
- server.tool("set_fog", "Add or update distance fog in the active scene. v1 supports gradient fog (atmospheric distance haze — great for mood/horror). Re-running on the same target updates it rather than duplicating.", {
64
+ server.tool("set_fog", "Add or update fog in the active scene. Types: 'gradient' (distance haze — great for mood/horror), 'cubemap' (sky-tinted distance fog), 'volumetric' (a localized fog volume). Re-running on the same target updates it rather than duplicating.", {
65
65
  type: z
66
- .enum(["gradient"])
66
+ .enum(["gradient", "cubemap", "volumetric"])
67
67
  .optional()
68
- .describe("Fog type (default gradient; cubemap/volumetric coming later)"),
68
+ .describe("Fog type (default gradient)"),
69
69
  name: z.string().optional().describe("GameObject name when creating a new fog object"),
70
70
  targetId: z
71
71
  .string()
72
72
  .optional()
73
- .describe("GUID of an existing GameObject to host the fog (else a new 'Gradient Fog' object is created)"),
74
- color: ColorSchema.optional().describe("Fog colour"),
75
- startDistance: z
76
- .number()
77
- .optional()
78
- .describe("Distance (units) where fog begins"),
79
- endDistance: z
80
- .number()
81
- .optional()
82
- .describe("Distance (units) where fog reaches full density"),
83
- height: z.number().optional().describe("World height the fog settles around"),
84
- falloff: z
85
- .number()
73
+ .describe("GUID of an existing GameObject to host the fog (else a new fog object is created)"),
74
+ color: ColorSchema.optional().describe("Fog colour (gradient/volumetric Color, cubemap Tint)"),
75
+ startDistance: z.number().optional().describe("gradient/cubemap: distance (units) where fog begins"),
76
+ endDistance: z.number().optional().describe("gradient/cubemap: distance (units) where fog reaches full density"),
77
+ height: z.number().optional().describe("gradient: world height the fog settles around"),
78
+ falloff: z.number().optional().describe("Distance/density falloff exponent (higher = sharper onset)"),
79
+ blur: z.number().optional().describe("cubemap: sky blur amount"),
80
+ heightStart: z.number().optional().describe("cubemap: world height where height-fog starts"),
81
+ heightWidth: z.number().optional().describe("cubemap: height-fog band width"),
82
+ heightExponent: z.number().optional().describe("cubemap: height-fog falloff exponent"),
83
+ strength: z.number().optional().describe("volumetric: fog density/strength"),
84
+ size: z
85
+ .object({ x: z.number(), y: z.number(), z: z.number() })
86
86
  .optional()
87
- .describe("Distance falloff exponent (higher = sharper onset)"),
87
+ .describe("volumetric: bounds size (units) centred on the object"),
88
88
  }, async (params) => {
89
89
  const res = await bridge.send("set_fog", params);
90
90
  if (!res.success) {
@@ -17,17 +17,21 @@ import { z } from "zod";
17
17
  */
18
18
  export function registerWorldTools(server, bridge) {
19
19
  // ── invoke_button ────────────────────────────────────────────────
20
- server.tool("invoke_button", "Call a no-argument public method on a component. Matching is tried in order: (1) a [Button] attribute label, (2) the exact method NAME, (3) case-insensitive name with spaces stripped. So this calls ANY parameterless public method, not only [Button]-attributed ones (e.g. 'StartGame' works even without a [Button]). This is the general 'call a component method to drive game state' tool. LIMITATION: parameterless methods only methods that take arguments are skipped. (list_component_buttons only lists [Button] methods, so a plain method may be invokable yet not appear there.)", {
20
+ server.tool("invoke_button", "Call a public method on a component. Matching is tried in order: (1) a [Button] attribute label, (2) the exact method NAME, (3) case-insensitive name with spaces stripped. Calls ANY public method, not only [Button]-attributed ones (e.g. 'StartGame'). Pass `args` to call methods that take parameters — the arg count must match and each value is coerced to the parameter type (primitives: string/number/bool work; complex types like Vector3 may not coerce). Omit args (or []) for parameterless methods. (list_component_buttons only lists [Button] methods, so a plain method may be invokable yet not appear there.)", {
21
21
  component: z
22
22
  .string()
23
23
  .describe("Component type name (e.g. 'MapBuilder', 'SasquatchedGame')"),
24
24
  button: z
25
25
  .string()
26
- .describe("A [Button] label OR a public no-arg method name (e.g. 'Build Terrain', 'StartGame'); case- and space-insensitive"),
26
+ .describe("A [Button] label OR a public method name (e.g. 'Build Terrain', 'StartGame'); case- and space-insensitive"),
27
27
  id: z
28
28
  .string()
29
29
  .optional()
30
30
  .describe("Optional GameObject GUID — if omitted, finds first matching component in scene"),
31
+ args: z
32
+ .array(z.unknown())
33
+ .optional()
34
+ .describe("Arguments to pass (must match the method's parameter count); coerced to each parameter type"),
31
35
  }, async (params) => {
32
36
  const res = await bridge.send("invoke_button", params);
33
37
  if (!res.success)
@@ -49,6 +53,10 @@ export function registerWorldTools(server, bridge) {
49
53
  x: z.number().describe("World X coordinate"),
50
54
  y: z.number().describe("World Y coordinate"),
51
55
  id: z.string().optional().describe("Optional GameObject GUID for MapBuilder"),
56
+ component: z
57
+ .string()
58
+ .optional()
59
+ .describe("Override the terrain component type name (default MapBuilder)"),
52
60
  }, async (params) => {
53
61
  const res = await bridge.send("raycast_terrain", params);
54
62
  if (!res.success)
@@ -63,6 +71,10 @@ export function registerWorldTools(server, bridge) {
63
71
  height: z.number().default(100).describe("Peak height (negative for depression)"),
64
72
  rebuild: z.boolean().default(true).describe("Rebuild terrain after adding (set false to batch)"),
65
73
  id: z.string().optional(),
74
+ component: z
75
+ .string()
76
+ .optional()
77
+ .describe("Override the builder component type name — set this if your project's terrain/cave/forest component is named differently than the default (MapBuilder/CaveBuilder/ForestGenerator)"),
66
78
  }, async (params) => {
67
79
  const res = await bridge.send("add_terrain_hill", params);
68
80
  if (!res.success)
@@ -76,6 +88,10 @@ export function registerWorldTools(server, bridge) {
76
88
  radius: z.number().default(300),
77
89
  rebuild: z.boolean().default(true),
78
90
  id: z.string().optional(),
91
+ component: z
92
+ .string()
93
+ .optional()
94
+ .describe("Override the builder component type name — set this if your project's terrain/cave/forest component is named differently than the default (MapBuilder/CaveBuilder/ForestGenerator)"),
79
95
  }, async (params) => {
80
96
  const res = await bridge.send("add_terrain_clearing", params);
81
97
  if (!res.success)
@@ -88,6 +104,10 @@ export function registerWorldTools(server, bridge) {
88
104
  to: z.object({ x: z.number(), y: z.number() }),
89
105
  rebuild: z.boolean().default(true),
90
106
  id: z.string().optional(),
107
+ component: z
108
+ .string()
109
+ .optional()
110
+ .describe("Override the builder component type name — set this if your project's terrain/cave/forest component is named differently than the default (MapBuilder/CaveBuilder/ForestGenerator)"),
91
111
  }, async (params) => {
92
112
  const res = await bridge.send("add_terrain_trail", params);
93
113
  if (!res.success)
@@ -101,6 +121,10 @@ export function registerWorldTools(server, bridge) {
101
121
  .default("all"),
102
122
  rebuild: z.boolean().default(true),
103
123
  id: z.string().optional(),
124
+ component: z
125
+ .string()
126
+ .optional()
127
+ .describe("Override the builder component type name — set this if your project's terrain/cave/forest component is named differently than the default (MapBuilder/CaveBuilder/ForestGenerator)"),
104
128
  }, async (params) => {
105
129
  const res = await bridge.send("clear_terrain_features", params);
106
130
  if (!res.success)
@@ -119,6 +143,10 @@ export function registerWorldTools(server, bridge) {
119
143
  .describe("Optional insert position (default: append to end)"),
120
144
  rebuild: z.boolean().default(true),
121
145
  id: z.string().optional(),
146
+ component: z
147
+ .string()
148
+ .optional()
149
+ .describe("Override the builder component type name — set this if your project's terrain/cave/forest component is named differently than the default (MapBuilder/CaveBuilder/ForestGenerator)"),
122
150
  }, async (params) => {
123
151
  const res = await bridge.send("add_cave_waypoint", params);
124
152
  if (!res.success)
@@ -128,6 +156,10 @@ export function registerWorldTools(server, bridge) {
128
156
  // ── clear_cave_path ──────────────────────────────────────────────
129
157
  server.tool("clear_cave_path", "Clear all waypoints in CaveBuilder and remove the cave from the scene.", {
130
158
  id: z.string().optional(),
159
+ component: z
160
+ .string()
161
+ .optional()
162
+ .describe("Override the builder component type name — set this if your project's terrain/cave/forest component is named differently than the default (MapBuilder/CaveBuilder/ForestGenerator)"),
131
163
  }, async (params) => {
132
164
  const res = await bridge.send("clear_cave_path", params);
133
165
  if (!res.success)
@@ -149,6 +181,10 @@ export function registerWorldTools(server, bridge) {
149
181
  .default(false)
150
182
  .describe("Forest gen is slow (~1s); default false to batch"),
151
183
  id: z.string().optional(),
184
+ component: z
185
+ .string()
186
+ .optional()
187
+ .describe("Override the builder component type name — set this if your project's terrain/cave/forest component is named differently than the default (MapBuilder/CaveBuilder/ForestGenerator)"),
152
188
  }, async (params) => {
153
189
  const res = await bridge.send("add_forest_poi", params);
154
190
  if (!res.success)
@@ -161,6 +197,10 @@ export function registerWorldTools(server, bridge) {
161
197
  to_index: z.number().int(),
162
198
  rebuild: z.boolean().default(false),
163
199
  id: z.string().optional(),
200
+ component: z
201
+ .string()
202
+ .optional()
203
+ .describe("Override the builder component type name — set this if your project's terrain/cave/forest component is named differently than the default (MapBuilder/CaveBuilder/ForestGenerator)"),
164
204
  }, async (params) => {
165
205
  const res = await bridge.send("add_forest_trail", params);
166
206
  if (!res.success)
@@ -172,6 +212,10 @@ export function registerWorldTools(server, bridge) {
172
212
  seed: z.number().int().default(77),
173
213
  rebuild: z.boolean().default(true),
174
214
  id: z.string().optional(),
215
+ component: z
216
+ .string()
217
+ .optional()
218
+ .describe("Override the builder component type name — set this if your project's terrain/cave/forest component is named differently than the default (MapBuilder/CaveBuilder/ForestGenerator)"),
175
219
  }, async (params) => {
176
220
  const res = await bridge.send("set_forest_seed", params);
177
221
  if (!res.success)
@@ -181,6 +225,10 @@ export function registerWorldTools(server, bridge) {
181
225
  // ── clear_forest_pois ────────────────────────────────────────────
182
226
  server.tool("clear_forest_pois", "Wipe all POIs and trails in ForestGenerator and clear placed forest objects from the scene.", {
183
227
  id: z.string().optional(),
228
+ component: z
229
+ .string()
230
+ .optional()
231
+ .describe("Override the builder component type name — set this if your project's terrain/cave/forest component is named differently than the default (MapBuilder/CaveBuilder/ForestGenerator)"),
184
232
  }, async (params) => {
185
233
  const res = await bridge.send("clear_forest_pois", params);
186
234
  if (!res.success)
@@ -195,6 +243,10 @@ export function registerWorldTools(server, bridge) {
195
243
  strength: z.number().default(50).describe("Height delta (units) for raise/lower; ignored for flatten/smooth"),
196
244
  mode: z.enum(["raise", "lower", "flatten", "smooth"]).default("raise"),
197
245
  id: z.string().optional(),
246
+ component: z
247
+ .string()
248
+ .optional()
249
+ .describe("Override the builder component type name — set this if your project's terrain/cave/forest component is named differently than the default (MapBuilder/CaveBuilder/ForestGenerator)"),
198
250
  }, async (params) => {
199
251
  const res = await bridge.send("sculpt_terrain", params);
200
252
  if (!res.success)
@@ -209,6 +261,10 @@ export function registerWorldTools(server, bridge) {
209
261
  density: z.number().default(1).describe("Density multiplier (0=clear, 1=normal, 2=dense)"),
210
262
  rebuild: z.boolean().default(false),
211
263
  id: z.string().optional(),
264
+ component: z
265
+ .string()
266
+ .optional()
267
+ .describe("Override the builder component type name — set this if your project's terrain/cave/forest component is named differently than the default (MapBuilder/CaveBuilder/ForestGenerator)"),
212
268
  }, async (params) => {
213
269
  const res = await bridge.send("paint_forest_density", params);
214
270
  if (!res.success)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sbox-mcp-server",
3
- "version": "1.7.0",
3
+ "version": "1.8.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",