sbox-mcp-server 1.18.0 → 1.19.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 CHANGED
@@ -137,6 +137,6 @@ The companion plugin's `sbox-build-feature` skill encodes this workflow plus the
137
137
 
138
138
  ## License
139
139
 
140
- **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.
140
+ **Source-available (no redistribution)** — see [LICENSE](../LICENSE) and [NOTICE](../NOTICE) for details. You may use and locally modify the bridge to build your own games, but you may not redistribute, fork, repackage, or re-host it. The "s&box Claude Bridge" / "sboxskins.gg" name and branding are trademarks and may not be reused.
141
141
 
142
142
  Copyright (c) 2026 [sboxskins.gg](https://sboxskins.gg)
package/dist/index.js CHANGED
@@ -52,6 +52,7 @@ import { registerRoundStateTools } from "./tools/roundstate.js";
52
52
  import { registerStationTools } from "./tools/stations.js";
53
53
  import { registerDirectorTools } from "./tools/director.js";
54
54
  import { registerSaveSlotsTools } from "./tools/saveslots.js";
55
+ import { registerGameFeelTools } from "./tools/gamefeel.js";
55
56
  // ── CLI flags ──────────────────────────────────────────────────────
56
57
  const args = process.argv.slice(2);
57
58
  /** Read the package version from package.json, or return "unknown" on failure. */
@@ -207,6 +208,7 @@ registerRoundStateTools(server, bridge);
207
208
  registerStationTools(server, bridge);
208
209
  registerDirectorTools(server, bridge);
209
210
  registerSaveSlotsTools(server, bridge);
211
+ registerGameFeelTools(server, bridge);
210
212
  /** Start the MCP server on stdio and attempt initial Bridge connection. */
211
213
  async function main() {
212
214
  const transport = new StdioServerTransport();
@@ -0,0 +1,14 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { BridgeClient } from "../transport/bridge-client.js";
3
+ /**
4
+ * Game Feel pack — three "juice" scaffolds (v1.19.0):
5
+ *
6
+ * - create_camera_shake trauma-based Perlin camera shake
7
+ * - add_flicker_light flicker/pulse animator for an existing light
8
+ * - create_floating_combat_text rising/fading damage-number popups
9
+ *
10
+ * All generate a clean, self-contained .cs (LOCAL/visual-only — no [Sync]);
11
+ * file/scene-mutating, refused during play mode by the bridge dispatch.
12
+ */
13
+ export declare function registerGameFeelTools(server: McpServer, bridge: BridgeClient): void;
14
+ //# sourceMappingURL=gamefeel.d.ts.map
@@ -0,0 +1,113 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Game Feel pack — three "juice" scaffolds (v1.19.0):
4
+ *
5
+ * - create_camera_shake trauma-based Perlin camera shake
6
+ * - add_flicker_light flicker/pulse animator for an existing light
7
+ * - create_floating_combat_text rising/fading damage-number popups
8
+ *
9
+ * All generate a clean, self-contained .cs (LOCAL/visual-only — no [Sync]);
10
+ * file/scene-mutating, refused during play mode by the bridge dispatch.
11
+ */
12
+ export function registerGameFeelTools(server, bridge) {
13
+ // ── create_camera_shake ───────────────────────────────────────────
14
+ server.tool("create_camera_shake", "Generate a trauma-based camera shake component (the standard game-feel model: events add Trauma 0..1, shake magnitude = Trauma², smooth Perlin offsets — not white-noise jitter — and Trauma decays every frame, so explosions slam and footsteps barely register). Attach the generated component to the CAMERA GameObject; fire from any game code via the static <Name>.Shake(0.4f). Applies in OnPreRender AFTER controllers position the camera, with an un-apply guard so it neither fights a controller-driven camera nor accumulates on a static one, and restores the camera when trauma hits zero. LOCAL-only (no [Sync]) — call Shake inside an [Rpc.Broadcast] handler if every client should feel it. Optionally attached to an existing GameObject by GUID (after a hotload).", {
15
+ name: z
16
+ .string()
17
+ .optional()
18
+ .describe("Class name for the generated component. Defaults to 'CameraShake'"),
19
+ directory: z
20
+ .string()
21
+ .optional()
22
+ .describe("Subdirectory for the generated .cs file. Defaults to 'Code'"),
23
+ maxOffset: z
24
+ .number()
25
+ .optional()
26
+ .describe("Positional shake at full trauma, in world units. Defaults to 6"),
27
+ maxAngle: z
28
+ .number()
29
+ .optional()
30
+ .describe("Rotational shake at full trauma, in degrees (applied to pitch/yaw/roll). Defaults to 4"),
31
+ frequency: z
32
+ .number()
33
+ .optional()
34
+ .describe("Noise speed — higher = violent rattle, lower = drunken sway. Defaults to 10"),
35
+ decayPerSecond: z
36
+ .number()
37
+ .optional()
38
+ .describe("How much trauma drains per second. Defaults to 1.5"),
39
+ targetId: z
40
+ .string()
41
+ .optional()
42
+ .describe("GUID of the camera GameObject to attach to (only attaches if the type is already loaded — hotload first)"),
43
+ }, async (params) => {
44
+ const res = await bridge.send("create_camera_shake", params);
45
+ if (!res.success) {
46
+ return { content: [{ type: "text", text: `Error: ${res.error}` }] };
47
+ }
48
+ return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
49
+ });
50
+ // ── add_flicker_light ─────────────────────────────────────────────
51
+ server.tool("add_flicker_light", "Generate a light-flicker animator and optionally attach it to an existing light GameObject by GUID. Five presets: Candle (soft organic sway), Fluorescent (mostly steady with random dips), Faulty (hard on/off cuts), Pulse (slow sine breathing), Lightning (dim baseline with rare bright flashes). Modulates the sibling Light component's LightColor around the color it found on enable (works on PointLight / SpotLight / DirectionalLight) and restores it exactly on disable; intensity 0..1 sets flicker depth, speed scales the whole pattern. The single biggest atmosphere win per call for horror/night scenes — pairs with apply_atmosphere. LOCAL/visual-only.", {
52
+ name: z
53
+ .string()
54
+ .optional()
55
+ .describe("Class name for the generated component. Defaults to 'FlickerLight'"),
56
+ directory: z
57
+ .string()
58
+ .optional()
59
+ .describe("Subdirectory for the generated .cs file. Defaults to 'Code'"),
60
+ style: z
61
+ .enum(["Candle", "Fluorescent", "Faulty", "Pulse", "Lightning"])
62
+ .optional()
63
+ .describe("Default flicker preset baked into the component (editable per-instance in the inspector). Defaults to 'Candle'"),
64
+ intensity: z
65
+ .number()
66
+ .optional()
67
+ .describe("Flicker depth 0..1: 0 = steady, 1 = full blackouts / double-bright flashes. Defaults to 0.5"),
68
+ speed: z
69
+ .number()
70
+ .optional()
71
+ .describe("Speed multiplier for the whole pattern. Defaults to 1"),
72
+ lightId: z
73
+ .string()
74
+ .optional()
75
+ .describe("GUID of a GameObject holding a light component to attach to (only attaches if the type is already loaded — hotload first)"),
76
+ }, async (params) => {
77
+ const res = await bridge.send("add_flicker_light", params);
78
+ if (!res.success) {
79
+ return { content: [{ type: "text", text: `Error: ${res.error}` }] };
80
+ }
81
+ return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
82
+ });
83
+ // ── create_floating_combat_text ───────────────────────────────────
84
+ server.tool("create_floating_combat_text", "Generate a floating combat text component — rising, fading, camera-billboarded world-space popups for damage numbers, '+10 gold', pickup names. TextRenderer-based: no Razor, no WorldPanel, zero UI setup. Nothing to place in the scene — the generated class carries a static factory: <Name>.Spawn(position, \"-25\", Color.Red[, sizeMultiplier]) spawns a popup that rises at RiseSpeed, fades over Lifetime, and destroys itself. Pairs with create_health_system (spawn from the damage path so every hit prints its number). LOCAL-only — spawn inside an [Rpc.Broadcast] handler if every client should see it.", {
85
+ name: z
86
+ .string()
87
+ .optional()
88
+ .describe("Class name for the generated component. Defaults to 'FloatingCombatText'"),
89
+ directory: z
90
+ .string()
91
+ .optional()
92
+ .describe("Subdirectory for the generated .cs file. Defaults to 'Code'"),
93
+ riseSpeed: z
94
+ .number()
95
+ .optional()
96
+ .describe("World units the popup rises per second. Defaults to 48"),
97
+ lifetime: z
98
+ .number()
99
+ .optional()
100
+ .describe("Seconds until the popup is fully faded and destroyed. Defaults to 1.1"),
101
+ fontSize: z
102
+ .number()
103
+ .optional()
104
+ .describe("Base font size baked into Spawn() (the optional Spawn size argument multiplies it). Defaults to 24"),
105
+ }, async (params) => {
106
+ const res = await bridge.send("create_floating_combat_text", params);
107
+ if (!res.success) {
108
+ return { content: [{ type: "text", text: `Error: ${res.error}` }] };
109
+ }
110
+ return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
111
+ });
112
+ }
113
+ //# sourceMappingURL=gamefeel.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sbox-mcp-server",
3
- "version": "1.18.0",
3
+ "version": "1.19.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": "AGPL-3.0-or-later"
54
+ "license": "SEE LICENSE IN LICENSE"
55
55
  }