summer-engine 2.7.0 → 2.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.
- package/.claude-plugin/plugin.json +2 -1
- package/.codex-plugin/plugin.json +3 -2
- package/.cursor-plugin/plugin.json +2 -1
- package/.opencode/INSTALL.md +1 -1
- package/.opencode/plugins/summer.js +1 -1
- package/AGENTS.md +4 -4
- package/CHANGELOG.md +10 -0
- package/GEMINI.md +5 -4
- package/README.md +58 -19
- package/assets/autopilot/README.md +51 -0
- package/assets/autopilot/autopilot.gd +222 -0
- package/assets/autopilot/probe_base.gd +262 -0
- package/assets/autopilot/run.sh +78 -0
- package/commands/gameskill.md +11 -2
- package/dist/bin/summer.js +8 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +53 -0
- package/dist/commands/create.d.ts +1 -0
- package/dist/commands/create.js +38 -6
- package/dist/commands/list.js +1 -1
- package/dist/commands/login.d.ts +19 -0
- package/dist/commands/login.js +123 -36
- package/dist/commands/logout.js +3 -16
- package/dist/commands/logs.d.ts +2 -0
- package/dist/commands/logs.js +19 -0
- package/dist/commands/mcp.js +7 -2
- package/dist/commands/open.js +1 -1
- package/dist/commands/publish.d.ts +2 -0
- package/dist/commands/publish.js +20 -0
- package/dist/commands/releases.d.ts +2 -0
- package/dist/commands/releases.js +20 -0
- package/dist/commands/run.js +1 -1
- package/dist/lib/api-client.d.ts +4 -2
- package/dist/lib/api-client.js +28 -4
- package/dist/lib/auth.d.ts +40 -12
- package/dist/lib/auth.js +173 -37
- package/dist/lib/cloud/engine-bridge.d.ts +17 -4
- package/dist/lib/cloud/engine-bridge.js +32 -6
- package/dist/lib/config.d.ts +20 -0
- package/dist/lib/config.js +142 -0
- package/dist/lib/creator.d.ts +93 -0
- package/dist/lib/creator.js +393 -0
- package/dist/lib/engine.d.ts +35 -2
- package/dist/lib/engine.js +240 -6
- package/dist/lib/public-surface-paths.d.ts +13 -0
- package/dist/lib/public-surface-paths.js +42 -0
- package/dist/lib/skills-registry.d.ts +8 -0
- package/dist/lib/skills-registry.js +9 -0
- package/dist/lib/store.d.ts +14 -0
- package/dist/lib/store.js +145 -0
- package/dist/lib/summer-compatibility.d.ts +6 -0
- package/dist/lib/summer-compatibility.js +22 -0
- package/dist/mcp/server.d.ts +8 -1
- package/dist/mcp/server.js +25 -4
- package/dist/mcp/tools/creator-tools.d.ts +2 -0
- package/dist/mcp/tools/creator-tools.js +105 -0
- package/dist/mcp/tools/generate-tools.js +16 -6
- package/dist/mcp/tools/project-tools.js +12 -7
- package/dist/mcp/tools/scene-tools.js +6 -6
- package/dist/mcp/tools/visual-tools.js +4 -3
- package/hooks/pre-commit-doctor.sh +0 -0
- package/hooks/run-hook.cmd +0 -0
- package/hooks/session-start.sh +5 -5
- package/package.json +3 -1
- package/references/collaborative-protocol.md +50 -0
- package/references/gd-style.md +121 -0
- package/references/godot-version.md +51 -0
- package/references/mcp-tools-reference.md +199 -0
- package/references/public-surface-language.md +30 -0
- package/references/summer-folder.md +90 -0
- package/references/template-registry.md +73 -0
- package/skills/2d-assets/character-portrait/SKILL.md +17 -12
- package/skills/2d-assets/concept-art/SKILL.md +10 -9
- package/skills/2d-assets/pixel-art/SKILL.md +36 -20
- package/skills/2d-assets/skybox-panorama/SKILL.md +32 -37
- package/skills/2d-assets/sprite-sheet/SKILL.md +40 -35
- package/skills/2d-assets/tileable-texture/SKILL.md +28 -22
- package/skills/2d-assets/ui-graphics/SKILL.md +46 -32
- package/skills/2d-assets/use-widget-asset/SKILL.md +8 -6
- package/skills/3d-assets/character-model/SKILL.md +34 -19
- package/skills/3d-assets/environment-kit/SKILL.md +11 -5
- package/skills/3d-assets/organic-model/SKILL.md +8 -5
- package/skills/3d-assets/prop-model/SKILL.md +9 -3
- package/skills/3d-assets/vehicle-model/SKILL.md +32 -19
- package/skills/README.md +1 -1
- package/skills/ai-and-npcs/design-npc/SKILL.md +31 -13
- package/skills/animation/animation-tree/SKILL.md +39 -15
- package/skills/animation/facial-and-lipsync/SKILL.md +25 -8
- package/skills/animation/generate-motion/SKILL.md +47 -15
- package/skills/animation/procedural-animation/SKILL.md +31 -13
- package/skills/animation/retarget/SKILL.md +127 -79
- package/skills/audio/adaptive-music/SKILL.md +24 -20
- package/skills/audio/ambient-bed/SKILL.md +22 -16
- package/skills/audio/audio-direction/SKILL.md +15 -10
- package/skills/audio/music-track/SKILL.md +18 -12
- package/skills/audio/sound-effect/SKILL.md +17 -12
- package/skills/audio/voice-line/SKILL.md +42 -42
- package/skills/character-controllers/fps-controller/SKILL.md +26 -16
- package/skills/debugging/debug/SKILL.md +52 -20
- package/skills/deployment/export-and-ship/SKILL.md +105 -38
- package/skills/deployment/remote-deploy/SKILL.md +56 -23
- package/skills/gameplay-mechanics/auto-fire-targeting/SKILL.md +26 -1
- package/skills/gameplay-mechanics/design-mechanic/SKILL.md +23 -15
- package/skills/level-design/design-level/SKILL.md +22 -8
- package/skills/level-design/scene-to-level/SKILL.md +13 -7
- package/skills/multiplayer-and-networking/host-authoritative-state/SKILL.md +8 -8
- package/skills/multiplayer-and-networking/peer-to-peer-multiplayer/SKILL.md +2 -7
- package/skills/multiplayer-and-networking/setup-multiplayer/SKILL.md +26 -18
- package/skills/performance/tune-performance/SKILL.md +51 -32
- package/skills/rendering-and-lighting/3d-lighting/SKILL.md +34 -5
- package/skills/rendering-and-lighting/art-direction/SKILL.md +6 -8
- package/skills/scene-and-project/brainstorm-game/SKILL.md +3 -5
- package/skills/scene-and-project/make-game/SKILL.md +2 -1
- package/skills/scene-and-project/play/SKILL.md +10 -4
- package/skills/scene-and-project/scene-composition/SKILL.md +21 -8
- package/skills/scene-and-project/summer-cloud/SKILL.md +1 -1
- package/skills/ui-and-ux/ui-basics/SKILL.md +16 -7
- package/skills/video/animated-loop/SKILL.md +63 -39
- package/skills/video/cinematic-cutscene/SKILL.md +66 -30
- package/skills/video/trailer-shot/SKILL.md +30 -21
- package/skills/visual-effects/_building-blocks/additive-billboard-particles.md +25 -16
- package/skills/visual-effects/_building-blocks/trauma-shake-snippet.md +5 -3
- package/skills/visual-effects/game-feel/SKILL.md +51 -33
- package/skills/visual-effects/recipes/dissolve/SKILL.md +51 -9
- package/skills/visual-effects/recipes/fire/SKILL.md +136 -49
- package/skills/visual-effects/recipes/hit-spark/SKILL.md +45 -8
- package/skills/visual-effects/recipes/lightning/SKILL.md +59 -27
- package/skills/visual-effects/recipes/magic-glow/SKILL.md +59 -26
- package/skills/visual-effects/recipes/muzzle-flash/SKILL.md +68 -25
- package/skills/visual-effects/recipes/smoke/SKILL.md +89 -28
- package/skills/visual-effects/recipes/water-ripple/SKILL.md +83 -23
- package/skills/workflow/brainstorming/SKILL.md +1 -1
- package/skills/workflow/brainstorming/scripts/start-server.sh +0 -0
- package/skills/workflow/brainstorming/scripts/stop-server.sh +0 -0
- package/skills/workflow/debugging-game-feel/SKILL.md +29 -9
- package/skills/workflow/diagnosing-perf-regressions/SKILL.md +46 -21
- package/skills/workflow/gameskill/SKILL.md +11 -2
- package/skills/workflow/headless-scripting/SKILL.md +407 -0
- package/skills/workflow/investigating-bugs/SKILL.md +4 -2
- package/skills/workflow/investigating-bugs/find-polluter.sh +0 -0
- package/skills/workflow/mcpupdate/SKILL.md +3 -3
- package/skills/workflow/playtesting-a-feature/SKILL.md +94 -21
- package/skills/workflow/skill-create/SKILL.md +7 -7
- package/skills/workflow/skill-test/SKILL.md +3 -3
- package/skills/workflow/using-summer/SKILL.md +12 -2
- package/skills/workflow/verification-before-completion/SKILL.md +2 -0
- package/skills/workflow/writing-plans/SKILL.md +7 -7
- package/skills/workflow/writing-skills/SKILL.md +7 -5
- package/skills/workflow/writing-skills/render-graphs.js +0 -0
- package/skills/workflow/writing-skills/testing-skills-with-subagents.md +1 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { CreatorOperationError, listCreatorReleases, publishCreator, readCreatorLogs, } from "../../lib/creator.js";
|
|
3
|
+
import { CONFIG_KEYS, getConfigValue, isConfigKey, readSummerConfig, setConfigValue, unsetConfigValue, } from "../../lib/config.js";
|
|
4
|
+
function textJson(value, isError = false) {
|
|
5
|
+
return {
|
|
6
|
+
content: [
|
|
7
|
+
{
|
|
8
|
+
type: "text",
|
|
9
|
+
text: JSON.stringify(value, null, 2),
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
...(isError ? { isError: true } : {}),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
async function creatorResult(operation) {
|
|
16
|
+
try {
|
|
17
|
+
return textJson(await operation());
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
if (error instanceof CreatorOperationError) {
|
|
21
|
+
return textJson({
|
|
22
|
+
ok: false,
|
|
23
|
+
code: error.code,
|
|
24
|
+
operation: error.operation,
|
|
25
|
+
message: error.message,
|
|
26
|
+
recovery: error.recovery,
|
|
27
|
+
}, true);
|
|
28
|
+
}
|
|
29
|
+
return textJson({
|
|
30
|
+
ok: false,
|
|
31
|
+
code: "creator_request_invalid",
|
|
32
|
+
message: error instanceof Error ? error.message : String(error),
|
|
33
|
+
}, true);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function registerCreatorTools(server) {
|
|
37
|
+
server.tool("summer_creator_publish", "Publish an exact exported Summer .pck through the versioned Summercraft creator API. First call with confirm=false and present the returned project, version, digest, size, artifact path, channel, and notes; set confirm only after the user approves that exact target. The server independently verifies token scope, ownership, bytes, and review state.", {
|
|
38
|
+
project: z.string().optional().describe("Project root. Defaults to the current working directory."),
|
|
39
|
+
artifact: z.string().describe("Exact path to the exported Summer .pck artifact."),
|
|
40
|
+
version: z.string().describe("Immutable release version."),
|
|
41
|
+
manifest: z.string().optional().describe("Optional path to a JSON release manifest."),
|
|
42
|
+
projectId: z.string().optional().describe("Creator project ID. Defaults to ~/.summer/config.json."),
|
|
43
|
+
channel: z.string().optional().describe("Release channel. Defaults to the configured channel or production."),
|
|
44
|
+
notes: z.string().optional().describe("Release notes shown during confirmation and recorded in the local audit."),
|
|
45
|
+
confirm: z.boolean().default(false).describe("Set true only after the user approves the exact release target."),
|
|
46
|
+
}, async (args) => creatorResult(() => publishCreator({
|
|
47
|
+
...args,
|
|
48
|
+
face: "mcp",
|
|
49
|
+
})));
|
|
50
|
+
server.tool("summer_creator_releases", "List real creator-owned releases from the versioned Summercraft creator API. The server independently verifies the exact publish scope and project ownership.", {
|
|
51
|
+
projectId: z.string().optional().describe("Creator project ID. Defaults to ~/.summer/config.json."),
|
|
52
|
+
limit: z.number().int().min(1).max(100).default(20),
|
|
53
|
+
cursor: z.string().optional().describe("Opaque nextCursor from the previous page."),
|
|
54
|
+
}, async (args) => creatorResult(() => listCreatorReleases({
|
|
55
|
+
...args,
|
|
56
|
+
face: "mcp",
|
|
57
|
+
})));
|
|
58
|
+
server.tool("summer_creator_logs", "Read creator runtime logs for a project or release. If the required platform route or token scope is unavailable, returns the exact recovery action instead of placeholder logs.", {
|
|
59
|
+
projectId: z.string().optional().describe("Creator project ID. Defaults to ~/.summer/config.json."),
|
|
60
|
+
releaseId: z.string().optional().describe("Optional release ID."),
|
|
61
|
+
limit: z.number().int().min(1).max(1000).default(100),
|
|
62
|
+
}, async (args) => creatorResult(() => readCreatorLogs({
|
|
63
|
+
...args,
|
|
64
|
+
face: "mcp",
|
|
65
|
+
})));
|
|
66
|
+
server.tool("summer_creator_config", "Read or update the shared non-secret ~/.summer configuration used by the CLI and this MCP. Mutations require confirm=true. Tokens are never returned or accepted by this tool.", {
|
|
67
|
+
action: z.enum(["list", "get", "set", "unset"]),
|
|
68
|
+
key: z.enum(CONFIG_KEYS).optional(),
|
|
69
|
+
value: z.string().optional(),
|
|
70
|
+
confirm: z.boolean().default(false),
|
|
71
|
+
}, async ({ action, key, value, confirm }) => creatorResult(async () => {
|
|
72
|
+
if (action === "list") {
|
|
73
|
+
const config = await readSummerConfig();
|
|
74
|
+
return {
|
|
75
|
+
ok: true,
|
|
76
|
+
values: Object.fromEntries(CONFIG_KEYS.map((name) => [
|
|
77
|
+
name,
|
|
78
|
+
getConfigValue(config, name) ?? null,
|
|
79
|
+
])),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (!key || !isConfigKey(key)) {
|
|
83
|
+
throw new Error(`A valid key is required. Recovery: use one of ${CONFIG_KEYS.join(", ")}.`);
|
|
84
|
+
}
|
|
85
|
+
if (action === "get") {
|
|
86
|
+
return {
|
|
87
|
+
ok: true,
|
|
88
|
+
key,
|
|
89
|
+
value: getConfigValue(await readSummerConfig(), key) ?? null,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (!confirm) {
|
|
93
|
+
throw new Error(`Changing ${key} requires confirmation. Recovery: show the exact change to the user, then retry with confirm=true.`);
|
|
94
|
+
}
|
|
95
|
+
if (action === "set") {
|
|
96
|
+
if (value === undefined) {
|
|
97
|
+
throw new Error(`A value is required for ${key}. Recovery: provide value and retry with confirm=true.`);
|
|
98
|
+
}
|
|
99
|
+
const config = await setConfigValue(key, value);
|
|
100
|
+
return { ok: true, key, value: getConfigValue(config, key) };
|
|
101
|
+
}
|
|
102
|
+
const config = await unsetConfigValue(key);
|
|
103
|
+
return { ok: true, key, value: getConfigValue(config, key) ?? null };
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
@@ -274,7 +274,9 @@ Known models:
|
|
|
274
274
|
- "nano-banana-2" (default) — High quality, supports txt2img and img2img
|
|
275
275
|
- "gemini-flash" — Google Gemini 2.5 Flash, fast
|
|
276
276
|
- "flux-2" — FLUX.2, good for specific styles
|
|
277
|
-
|
|
277
|
+
|
|
278
|
+
Model ids are an ALLOWLIST, not a passthrough. An unrecognised id is rejected
|
|
279
|
+
with 400 and the response lists every accepted id — it is not silently swapped.
|
|
278
280
|
|
|
279
281
|
Two modes:
|
|
280
282
|
- txt2img (default): just pass prompt
|
|
@@ -371,8 +373,12 @@ Capabilities:
|
|
|
371
373
|
- "music" — Generate background music. Requires 'prompt'. Optional 'durationSeconds'.
|
|
372
374
|
- "text_to_dialogue" — Multi-voice dialogue. Requires 'inputs' array of {text, voiceId} objects.
|
|
373
375
|
|
|
374
|
-
The 'options' object is
|
|
375
|
-
|
|
376
|
+
The 'options' object is spread into the ElevenLabs SDK call at the TOP level, so
|
|
377
|
+
only keys the SDK accepts there have any effect. Voice tuning is NOT top-level and
|
|
378
|
+
is NOT snake_case: pass it as options.voiceSettings with camelCase keys —
|
|
379
|
+
{ voiceSettings: { stability, similarityBoost, style, speed, useSpeakerBoost } }.
|
|
380
|
+
Flat snake_case keys are accepted by the schema, silently dropped by the SDK, and
|
|
381
|
+
return a normal 200 with the setting ignored.
|
|
376
382
|
|
|
377
383
|
Returns the generated audio URL and asset metadata.
|
|
378
384
|
Cloud tool — runs on Summer's servers and works WITHOUT the Summer Engine app open.
|
|
@@ -407,7 +413,9 @@ Requires authentication: run 'npx summer-engine login' first.`, {
|
|
|
407
413
|
options: z
|
|
408
414
|
.record(z.any())
|
|
409
415
|
.optional()
|
|
410
|
-
.describe("Provider
|
|
416
|
+
.describe("Provider params, spread at the SDK's top level. Voice tuning goes in " +
|
|
417
|
+
"options.voiceSettings with camelCase keys (stability, similarityBoost, " +
|
|
418
|
+
"style, speed, useSpeakerBoost); flat snake_case is silently ignored."),
|
|
411
419
|
}, async ({ capability, text, prompt, voiceId, modelId, durationSeconds, inputs, options }) => {
|
|
412
420
|
const body = { capability };
|
|
413
421
|
if (text)
|
|
@@ -439,7 +447,9 @@ Available models:
|
|
|
439
447
|
- "hunyuan" (default) — Hunyuan 3D v3.1 Pro, high quality
|
|
440
448
|
- "trellis" — Trellis 2, fast and detailed
|
|
441
449
|
- "meshy" — Meshy, legacy option
|
|
442
|
-
|
|
450
|
+
|
|
451
|
+
Model ids are an ALLOWLIST, not a passthrough. An unrecognised id is rejected
|
|
452
|
+
with 400 listing the accepted ids.
|
|
443
453
|
|
|
444
454
|
Available kinds:
|
|
445
455
|
- "text-to-3d" (default) — From text description. Requires 'prompt'.
|
|
@@ -699,7 +709,7 @@ Cost: ~$0.10 per clip. Confirm with user before spending.
|
|
|
699
709
|
|
|
700
710
|
Custom prompt-driven motion is on the roadmap; not yet shipped. For one-off
|
|
701
711
|
signature moves not on the curated list, fall back to hand-authoring in the
|
|
702
|
-
|
|
712
|
+
Summer Engine or importing from Mixamo.
|
|
703
713
|
|
|
704
714
|
Cloud tool — runs on Summer's servers and works WITHOUT the Summer Engine app open.
|
|
705
715
|
Requires authentication: run 'npx summer-engine login' first.`, {
|
|
@@ -180,10 +180,12 @@ anti-patterns, and recovery steps.`, {}, async () => ({
|
|
|
180
180
|
"summer_stop -> stop when runtime verification is finished; editor scene mutations are not categorically blocked by a running game, but an existing game instance may need a restart to observe them",
|
|
181
181
|
],
|
|
182
182
|
"4_interactive": [
|
|
183
|
-
"To prove input-driven behavior (does jump/move/shoot actually work),
|
|
184
|
-
"
|
|
185
|
-
"
|
|
186
|
-
"
|
|
183
|
+
"To prove input-driven behavior (does jump/move/shoot actually work), use RunVerification. It is the ONLY interactive route available to MCP, and it is a real one — do NOT hand this rung to the user.",
|
|
184
|
+
"RunVerification: spawn a hidden, disposable game instance that runs a GDScript probe (press inputs, read state, save frames) and dies — never touches the user's editor. Returns results.json + frames. Send it as a raw op through summer_batch (see rawOpsViaBatch).",
|
|
185
|
+
"Unlike the editor's own --headless mode, the verify instance renders REAL PIXELS, so save_frame() writes a real image and Performance.TIME_FPS reports a real number.",
|
|
186
|
+
"press()/key() are COROUTINES — 'await press(\"move_right\", 500)' or the hold never elapses and the input does nothing.",
|
|
187
|
+
"Assert on physics-frame-derived state (positions after N 'await get_tree().physics_frame'), which is reproducible. press(hold_ms) waits on wall clock, so distance-travelled jitters run to run — assert 'moved more than X', never an exact value.",
|
|
188
|
+
"SimulateInput is NOT reachable from MCP — see rawOpsViaBatch. Do not attempt it as a fallback.",
|
|
187
189
|
],
|
|
188
190
|
},
|
|
189
191
|
// HONESTY — mirror the in-product agent's vision rules. A capture is
|
|
@@ -217,8 +219,10 @@ anti-patterns, and recovery steps.`, {}, async () => ({
|
|
|
217
219
|
// engine ops that have no dedicated tool are still reachable.
|
|
218
220
|
rawOpsViaBatch: [
|
|
219
221
|
"summer_batch runs an array of raw engine ops in one undo group; each op is passed through untouched, so newer engine ops with no dedicated tool are still callable.",
|
|
220
|
-
"
|
|
221
|
-
"
|
|
222
|
+
"RunVerification (hidden probe instance): summer_batch ops:[{op:'RunVerification', probe_source:'<gdscript>', max_seconds:20}]. probe_source extends SummerProbeBase and uses report()/save_frame()/press()/key()/finish(); returns {ok, results, frames, out_dir} or {ok:false, failure_reason: spawn_failed|timeout|bad_args|no_project|probe_not_node}.",
|
|
223
|
+
"SimulateInput is NOT reachable this way, on any engine build. It needs the in-editor chat bridge's async reply channel; every queued caller (MCP, CLI) is answered with {ok:false, failure_reason:'unsupported_transport'}. Use RunVerification's press()/key() instead.",
|
|
224
|
+
"WriteFile and ReplaceText are rejected here by design — use summer_write_file / summer_replace_text so project identity, content guards and same-file ordering are enforced.",
|
|
225
|
+
"You do not need an engine op to run a shell command: your own host already has a shell. The engine binary that runs project scripts is at OS.get_executable_path() (on macOS, /Applications/Summer.app/Contents/MacOS/Summer); see the summer-cli headless-scripting skill.",
|
|
222
226
|
"These are runtime ops, not scene mutations — the batch undo group is a harmless no-op for them.",
|
|
223
227
|
],
|
|
224
228
|
recovery: [
|
|
@@ -228,7 +232,8 @@ anti-patterns, and recovery steps.`, {}, async () => ({
|
|
|
228
232
|
"If save fails: use the returned scenePath/error to repair the exact cause. A running game alone is not a generic scene-mutation blocker.",
|
|
229
233
|
"If a mutation is rejected with identity_mismatch: the engine switched projects — call summer_get_project_context to rebind (only if you meant to follow it), then retry.",
|
|
230
234
|
"If a guarded file mutation is rejected with content mismatch: call summer_read_file again, review the new content, and retry with its new sha256 only if the edit is still valid.",
|
|
231
|
-
"If a run op
|
|
235
|
+
"If a run op returns 'unsupported' / an unknown-op error: this engine build predates it — fall back to summer_play + summer_get_debugger_errors.",
|
|
236
|
+
"If SimulateInput returns 'unsupported_transport': that is permanent for MCP, not a build gap. Rewrite the check as a RunVerification probe.",
|
|
232
237
|
],
|
|
233
238
|
debugging: [
|
|
234
239
|
"Set SUMMER_MCP_DEBUG=1 in the MCP server's environment to log a structured stderr line per tool call (tool, ok, durationMs, terminalState, errorClass, failureReason, retried, boundProjectIdHash). With the flag OFF, only failures are logged. Use it to see exactly which op failed and why.",
|
|
@@ -120,7 +120,7 @@ Common node types:
|
|
|
120
120
|
The parent path uses "./" prefix for relative paths from scene root. E.g., "./World" means the "World" child of the root node.`, {
|
|
121
121
|
scenePath: z.string().describe("Target scene path, e.g. 'res://main.tscn'"),
|
|
122
122
|
parent: z.string().describe("Parent node path, e.g. './World' or './World/Enemies'"),
|
|
123
|
-
type: z.string().describe("
|
|
123
|
+
type: z.string().describe("Summer Engine node type, e.g. 'MeshInstance3D', 'CharacterBody3D'"),
|
|
124
124
|
name: z.string().describe("Name for the new node, e.g. 'Player', 'MainCamera'"),
|
|
125
125
|
}, async ({ scenePath, parent, type, name }) => withEngine(async (client) => executeSceneMutation(client, scenePath, [{ op: "AddNode", parent, type, name }])));
|
|
126
126
|
server.tool("summer_set_prop", `Set a property on a node. This is the primary way to configure nodes after adding them.
|
|
@@ -147,7 +147,7 @@ COMMON PROPERTIES:
|
|
|
147
147
|
scenePath: z.string().describe("Target scene path, e.g. 'res://main.tscn'"),
|
|
148
148
|
path: z.string().describe("Node path, e.g. './World/Player'"),
|
|
149
149
|
key: z.string().describe("Property name, e.g. 'position', 'mesh', 'visible'"),
|
|
150
|
-
value: z.union([z.string(), z.number(), z.boolean()]).describe("Value in
|
|
150
|
+
value: z.union([z.string(), z.number(), z.boolean()]).describe("Value in Summer Engine variant-string format for complex types, native JSON for primitives"),
|
|
151
151
|
}, async ({ scenePath, path, key, value }) => withEngine(async (client) => executeSceneMutation(client, scenePath, [{ op: "SetProp", path, key, value }])));
|
|
152
152
|
server.tool("summer_set_resource_property", `Set a nested property on a resource attached to a node.
|
|
153
153
|
|
|
@@ -159,7 +159,7 @@ Use when you need to modify a sub-property of a resource, like:
|
|
|
159
159
|
nodePath: z.string().describe("Node path, e.g. './Player/CollisionShape3D'"),
|
|
160
160
|
resourceProperty: z.string().describe("Resource property on the node, e.g. 'shape', 'mesh', 'material_override'"),
|
|
161
161
|
subProperty: z.string().describe("Property on the resource, e.g. 'size', 'radius', 'albedo_color'"),
|
|
162
|
-
value: z.union([z.string(), z.number(), z.boolean()]).describe("Value in
|
|
162
|
+
value: z.union([z.string(), z.number(), z.boolean()]).describe("Value in Summer Engine variant-string format"),
|
|
163
163
|
}, async ({ scenePath, nodePath, resourceProperty, subProperty, value }) => withEngine(async (client) => executeSceneMutation(client, scenePath, [
|
|
164
164
|
{ op: "SetResourceProperty", nodePath, resourceProperty, subProperty, value },
|
|
165
165
|
])));
|
|
@@ -260,9 +260,9 @@ Each op in the array uses the same format as the individual tools:
|
|
|
260
260
|
- {"op": "SetProp", "path": "Floor", "key": "mesh", "value": "PlaneMesh"}
|
|
261
261
|
- {"op": "SetResourceProperty", "nodePath": "Floor", "resourceProperty": "mesh", "subProperty": "size", "value": "Vector2(20, 20)"}
|
|
262
262
|
|
|
263
|
-
RAW RUNTIME OPS (interactive verification —
|
|
264
|
-
-
|
|
265
|
-
-
|
|
263
|
+
RAW RUNTIME OPS (interactive verification — structured failure_reason passes through verbatim):
|
|
264
|
+
- RunVerification — spawn a hidden, disposable game instance that runs a GDScript probe and dies (never touches the editor): {"op": "RunVerification", "probe_source": "extends SummerProbeBase\\nfunc _ready(): await super._ready(); report('ok', true); finish()", "max_seconds": 20}. Returns {ok, results, frames, out_dir}. Probe API: report()/save_frame()/press()/key()/finish(). This is the ONLY way to drive input from MCP, and unlike the editor it renders real pixels, so save_frame() produces a real image.
|
|
265
|
+
- SimulateInput is NOT reachable from MCP or the CLI. It requires the in-editor chat bridge's async reply channel; every queued caller gets {"ok": false, "failure_reason": "unsupported_transport"} on every engine build. Do not send it and do not treat that reply as a version problem — use RunVerification's press()/key() instead.
|
|
266
266
|
|
|
267
267
|
Do not mix OpenScene with scene mutations in one batch. OpenScene is a UI action;
|
|
268
268
|
send it separately. scenePath selects every mutation target. The tool appends one
|
|
@@ -10,14 +10,15 @@ import { withEngine } from "./with-engine.js";
|
|
|
10
10
|
export function registerVisualTools(server) {
|
|
11
11
|
server.tool("summer_screenshot", `Capture a frame from Summer Engine and return it as an image you can look at directly.
|
|
12
12
|
|
|
13
|
-
Use this to visually verify your work: scene layout, asset placement, scale, framing,
|
|
13
|
+
Use this to visually verify your work: scene layout, asset placement, scale, framing, missing/untextured assets, or runtime gameplay state. You see the actual pixels — no description layer in between. Lighting and materials are only truthfully shown by the "viewport" and "game" targets — see the note on "scene" below.
|
|
14
14
|
|
|
15
15
|
target:
|
|
16
16
|
"viewport" (default) — the editor's CURRENT view (whatever scene/tab is open). No game boot. Use for edit-time checks of how the scene looks right now.
|
|
17
|
-
"scene" — an OFFSCREEN render of a scene file (no game boot; physics/particles/animations are static at t=0). Optionally pass scenePath/framing/size/nodePath. Use for
|
|
17
|
+
"scene" — an OFFSCREEN render of a scene file (no game boot; physics/particles/animations are static at t=0). Optionally pass scenePath/framing/size/nodePath. Use for COMPOSITION, SCALE and FRAMING without touching the editor's open tab.
|
|
18
|
+
It does NOT use the scene's environment/sky, and it injects a synthetic camera and light when the scene has none. The scene's WorldEnvironment — sky, fog, tonemap, glow, SSAO, ambient — is replaced by a flat preview environment. So this target CANNOT verify lighting, mood, or any material property that depends on the environment: change them and the frame comes back identical. For those, boot the game or run a RunVerification probe, whose instance renders the real environment.
|
|
18
19
|
"game" — a frame from the RUNNING game (real runtime state). Start the game first (summer_play). Not available over a plain local connection — needs the Summer desktop app bridge.
|
|
19
20
|
|
|
20
|
-
Static frame only — one moment, not motion.
|
|
21
|
+
Static frame only — one moment, not motion. For a SEQUENCE of frames over time, or for anything lighting-dependent when the desktop bridge is unavailable, use a RunVerification probe's save_frame() — its instance has a real renderer.`, {
|
|
21
22
|
target: z
|
|
22
23
|
.enum(["viewport", "scene", "game"])
|
|
23
24
|
.optional()
|
|
File without changes
|
package/hooks/run-hook.cmd
CHANGED
|
File without changes
|
package/hooks/session-start.sh
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
#
|
|
4
4
|
# Event: SessionStart (matcher: startup|clear|compact)
|
|
5
5
|
# Purpose: Orient Claude on every fresh Summer-enabled session.
|
|
6
|
-
# Detect
|
|
6
|
+
# Detect Summer project + GameSoul + doctor health, surface a one-line
|
|
7
7
|
# Bob-style greeting as additional context.
|
|
8
8
|
#
|
|
9
9
|
# Input: JSON on stdin with { session_id, source, model, cwd, ... }
|
|
@@ -36,7 +36,7 @@ else
|
|
|
36
36
|
LINES+=("Summer ready.")
|
|
37
37
|
fi
|
|
38
38
|
|
|
39
|
-
# Line 2:
|
|
39
|
+
# Line 2: Summer project + GameSoul one-liner (only if project.godot exists)
|
|
40
40
|
if [ -f "${PROJECT_DIR}/project.godot" ]; then
|
|
41
41
|
PROJECT_NAME=$(grep -E '^config/name=' "${PROJECT_DIR}/project.godot" 2>/dev/null \
|
|
42
42
|
| head -1 | sed 's/^config\/name=//;s/^"//;s/"$//')
|
|
@@ -48,12 +48,12 @@ if [ -f "${PROJECT_DIR}/project.godot" ]; then
|
|
|
48
48
|
SOUL_LINE=$(awk 'BEGIN{p=""} /^[^#[:space:]]/ && NF>0 {p=$0; exit} END{print p}' \
|
|
49
49
|
"$SOUL_FILE" 2>/dev/null | cut -c1-120)
|
|
50
50
|
if [ -n "$SOUL_LINE" ]; then
|
|
51
|
-
LINES+=("
|
|
51
|
+
LINES+=("Summer project: ${PROJECT_NAME}. GameSoul: ${SOUL_LINE}")
|
|
52
52
|
else
|
|
53
|
-
LINES+=("
|
|
53
|
+
LINES+=("Summer project: ${PROJECT_NAME}.")
|
|
54
54
|
fi
|
|
55
55
|
else
|
|
56
|
-
LINES+=("
|
|
56
|
+
LINES+=("Summer project: ${PROJECT_NAME}.")
|
|
57
57
|
fi
|
|
58
58
|
fi
|
|
59
59
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "summer-engine",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"description": "Local Summer CLI and MCP server for Summer Engine. Install and run the engine, connect Claude Code, Cursor, Codex, Gemini, and other agents, and build real games with bundled skills, hooks, and plugins.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"summer-engine",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"files": [
|
|
42
42
|
"dist/",
|
|
43
43
|
"skills/",
|
|
44
|
+
"references/",
|
|
44
45
|
"commands/",
|
|
45
46
|
"hooks/",
|
|
46
47
|
"assets/",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"dev": "tsc --watch",
|
|
66
67
|
"summer": "node dist/bin/summer.js",
|
|
67
68
|
"test": "vitest run",
|
|
69
|
+
"test:public-language": "vitest run src/lib/public-surface-language.test.ts",
|
|
68
70
|
"test:watch": "vitest",
|
|
69
71
|
"prepublishOnly": "npm run build && npm test"
|
|
70
72
|
},
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Collaborative Protocol — "May I write?"
|
|
2
|
+
|
|
3
|
+
> Every Summer skill must follow this. The agent works *with* the user, not around them.
|
|
4
|
+
|
|
5
|
+
## The rule
|
|
6
|
+
|
|
7
|
+
**Before any user-visible write step, the agent must explicitly state what it is about to do and ask permission to proceed.**
|
|
8
|
+
|
|
9
|
+
This applies to:
|
|
10
|
+
- Creating files (`.gd`, `.tscn`, `.tres`, JSON, configs)
|
|
11
|
+
- Modifying existing files (especially `.gd` and `.tscn`)
|
|
12
|
+
- Running irreversible operations (deletion, reset, force-overwrite)
|
|
13
|
+
- Setting up project-wide settings (`project.godot`)
|
|
14
|
+
- Installing tools, libraries, or templates
|
|
15
|
+
|
|
16
|
+
Reading is unconditional. Inspection, scene-tree walks, diagnostics — never need permission.
|
|
17
|
+
|
|
18
|
+
## Phrasing
|
|
19
|
+
|
|
20
|
+
Plain, short, action-first. Examples:
|
|
21
|
+
|
|
22
|
+
> May I create `scenes/player.tscn` and add a CharacterBody3D + Camera3D?
|
|
23
|
+
|
|
24
|
+
> I'm about to overwrite `levels/level_01.tscn`. The current version has 14 nodes — I'll preserve them and add the new lighting setup. Continue?
|
|
25
|
+
|
|
26
|
+
> May I attach `scripts/player_controller.gd` to `./World/Player`?
|
|
27
|
+
|
|
28
|
+
> I'm going to bind `move_forward`, `move_back`, `move_left`, `move_right`, `jump`. OK?
|
|
29
|
+
|
|
30
|
+
## What this prevents
|
|
31
|
+
|
|
32
|
+
1. **Silent overwrites.** The agent stomps the user's hand-edited script.
|
|
33
|
+
2. **Cascading scope creep.** A "small fix" turns into a 30-file refactor.
|
|
34
|
+
3. **Unclear state.** The user has no idea what changed when something breaks.
|
|
35
|
+
4. **Trust loss.** One unauthorized edit and the user stops trusting the agent.
|
|
36
|
+
|
|
37
|
+
## What this is NOT
|
|
38
|
+
|
|
39
|
+
- Not a confirmation dialog for every property change in a single skill execution. Group related writes into one ask: "I'm about to add the player root, attach the controller script, and bind 5 input actions. OK?"
|
|
40
|
+
- Not a request for design input. The skill should know what to do; the ask is for permission, not direction.
|
|
41
|
+
- Not a stop on simple read-only ops or single-property tweaks the user clearly directed.
|
|
42
|
+
|
|
43
|
+
## Linter check
|
|
44
|
+
|
|
45
|
+
The `workflow/skill-test/SKILL.md` static linter looks for the phrases:
|
|
46
|
+
- "May I" / "May I write" / "May I create" / "May I attach"
|
|
47
|
+
- "I'm about to"
|
|
48
|
+
- "Continue?" / "OK?" / "Proceed?"
|
|
49
|
+
|
|
50
|
+
A skill that mutates files but never says any of these fails the linter.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# GDScript Style — Summer Conventions
|
|
2
|
+
|
|
3
|
+
> The bar for GDScript in Summer. Skills should produce code that matches this. Where Godot's official style guide is silent, this is the tiebreaker.
|
|
4
|
+
|
|
5
|
+
## File structure
|
|
6
|
+
|
|
7
|
+
```gdscript
|
|
8
|
+
class_name PlayerController
|
|
9
|
+
extends CharacterBody3D
|
|
10
|
+
|
|
11
|
+
# 1. Constants
|
|
12
|
+
const SPEED := 5.0
|
|
13
|
+
const JUMP_VELOCITY := 4.5
|
|
14
|
+
const GRAVITY := 9.8
|
|
15
|
+
|
|
16
|
+
# 2. Exports (designer-tweakable in inspector)
|
|
17
|
+
@export var move_speed: float = 5.0
|
|
18
|
+
@export var jump_height: float = 1.0
|
|
19
|
+
@export_range(0.1, 10.0) var look_sensitivity: float = 1.0
|
|
20
|
+
|
|
21
|
+
# 3. Signals (events this node emits)
|
|
22
|
+
signal died
|
|
23
|
+
signal health_changed(new_value: int)
|
|
24
|
+
|
|
25
|
+
# 4. Onready references (cached node lookups)
|
|
26
|
+
@onready var camera: Camera3D = $Camera3D
|
|
27
|
+
@onready var collision: CollisionShape3D = $CollisionShape3D
|
|
28
|
+
|
|
29
|
+
# 5. Private state (snake_case, leading underscore optional)
|
|
30
|
+
var _is_grounded: bool = false
|
|
31
|
+
var _velocity := Vector3.ZERO
|
|
32
|
+
|
|
33
|
+
# 6. Lifecycle (in canonical order)
|
|
34
|
+
func _ready() -> void:
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
func _process(delta: float) -> void:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
func _physics_process(delta: float) -> void:
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
func _input(event: InputEvent) -> void:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
# 7. Public API (PascalCase or snake_case methods, snake_case for funcs)
|
|
47
|
+
func take_damage(amount: int) -> void:
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
# 8. Private helpers (leading underscore)
|
|
51
|
+
func _calculate_movement(delta: float) -> Vector3:
|
|
52
|
+
return Vector3.ZERO
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Type hints
|
|
56
|
+
|
|
57
|
+
**Always type-hint.** No bare `var x = 5`. Use `:=` for inferred typing or `:` for explicit.
|
|
58
|
+
|
|
59
|
+
```gdscript
|
|
60
|
+
# RIGHT
|
|
61
|
+
var speed: float = 5.0
|
|
62
|
+
var name := "Player"
|
|
63
|
+
func attack(target: Node3D, amount: int) -> bool:
|
|
64
|
+
return true
|
|
65
|
+
|
|
66
|
+
# WRONG
|
|
67
|
+
var speed = 5.0
|
|
68
|
+
func attack(target, amount):
|
|
69
|
+
return true
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Naming
|
|
73
|
+
|
|
74
|
+
| Thing | Convention | Example |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| Class | PascalCase | `class_name PlayerController` |
|
|
77
|
+
| File | snake_case | `player_controller.gd` |
|
|
78
|
+
| Variable, function | snake_case | `move_speed`, `take_damage()` |
|
|
79
|
+
| Constant | SCREAMING_SNAKE_CASE | `const MAX_HEALTH := 100` |
|
|
80
|
+
| Signal | snake_case (past tense for events) | `died`, `health_changed` |
|
|
81
|
+
| Private | leading underscore | `_is_grounded`, `_calculate_movement` |
|
|
82
|
+
| Node in scene | PascalCase | `Player`, `Camera3D`, `CollisionShape3D` |
|
|
83
|
+
|
|
84
|
+
## Signals
|
|
85
|
+
|
|
86
|
+
Define with typed args. Connect in `_ready()` or in scene `.tscn` directly.
|
|
87
|
+
|
|
88
|
+
```gdscript
|
|
89
|
+
signal health_changed(new_value: int, max_value: int)
|
|
90
|
+
|
|
91
|
+
func _ready() -> void:
|
|
92
|
+
health_changed.connect(_on_health_changed)
|
|
93
|
+
|
|
94
|
+
func _on_health_changed(new_value: int, max_value: int) -> void:
|
|
95
|
+
pass
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Tweens
|
|
99
|
+
|
|
100
|
+
Use `create_tween()` for animation. Don't manually lerp in `_process` unless you need fine control.
|
|
101
|
+
|
|
102
|
+
```gdscript
|
|
103
|
+
var tween := create_tween()
|
|
104
|
+
tween.tween_property(self, "modulate", Color.WHITE, 0.3)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## What to avoid
|
|
108
|
+
|
|
109
|
+
- Bare `var x = something` (no type)
|
|
110
|
+
- `Object` typed parameters (use specific class)
|
|
111
|
+
- `match` for two-branch logic (use `if`)
|
|
112
|
+
- Recreating node lookups in `_process` (cache via `@onready`)
|
|
113
|
+
- Hard-coded strings for node paths in code (use `@onready` + scene-relative paths)
|
|
114
|
+
- Setting `position` in `_process` for physics objects (use `_physics_process` and `velocity`)
|
|
115
|
+
|
|
116
|
+
## See also
|
|
117
|
+
|
|
118
|
+
- [Official Godot GDScript style guide](https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_styleguide.html)
|
|
119
|
+
- `scripting-patterns/gdscript-patterns/SKILL.md` — patterns and idioms
|
|
120
|
+
- `scripting-patterns/signal-patterns/SKILL.md` — signal usage
|
|
121
|
+
- `scripting-patterns/state-machine-patterns/SKILL.md` — state machine patterns
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Summer Engine Compatibility Reference
|
|
2
|
+
|
|
3
|
+
> Read this before writing version-sensitive code, especially shaders,
|
|
4
|
+
> rendering code, extensions, or export configuration.
|
|
5
|
+
|
|
6
|
+
## Current technical base
|
|
7
|
+
|
|
8
|
+
Summer Engine is the product. Its current upstream technical base is **4.6.1**
|
|
9
|
+
(stable, Mono), the approved next target is **4.7.1**, and Summer follows
|
|
10
|
+
upstream continuously. These are compatibility and lineage facts. They are not
|
|
11
|
+
the Summer product name or a permanent SDK version.
|
|
12
|
+
|
|
13
|
+
The repository source of truth is
|
|
14
|
+
`compatibility/summer-engine.json`. The running engine also reports its measured
|
|
15
|
+
technical version:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
<engineBinaryPath> --version # measured: 4.6.1.stable.mono.custom_build.b708b1182
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Get `engineBinaryPath` from `summer_get_project_context`. Prefer live
|
|
22
|
+
inspection to a copied version string. Creator onboarding and headlines should
|
|
23
|
+
say Summer Engine, Summer game, Summer SDK, and GDScript. Use the upstream name
|
|
24
|
+
only where technical compatibility, migration, extension APIs, contribution
|
|
25
|
+
routing, attribution, or licensing requires it.
|
|
26
|
+
|
|
27
|
+
## LLM training cutoff versus upstream releases
|
|
28
|
+
|
|
29
|
+
| Domain | Godot 4.x churn | Action |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| Scripting (GDScript core) | low | Trust your training. Spot-check signatures only when something feels off. |
|
|
32
|
+
| Scene / node tree APIs | low | Trust your training. |
|
|
33
|
+
| Renderer (`RenderingServer`, `Compositor`) | high in 4.4 / 4.5 | Verify before writing. Check `MovieMaker`, `Compositor` effects, `RenderSceneBuffers` — these renamed/added recently. |
|
|
34
|
+
| Shaders (`canvas_item`, `spatial`, `compute`) | medium | Built-in functions stable, but `hint_*` flags shifted. Verify `hint_screen_texture` / `hint_depth_texture` syntax. |
|
|
35
|
+
| Animation (`AnimationTree`, `AnimationMixer`) | high (4.0 → 4.3 rename) | Use `AnimationMixer` for new code, not deprecated `AnimationPlayer`-as-mixer. |
|
|
36
|
+
| Multiplayer (`MultiplayerAPI`) | medium | High-level API stable. `SceneReplicationConfig` settled in 4.0+. |
|
|
37
|
+
| Editor / EditorPlugin | medium | Spot-check. |
|
|
38
|
+
| `Tween` / `SceneTreeTween` | low | Stable since 4.0. |
|
|
39
|
+
|
|
40
|
+
## Summer-specific deltas from the upstream engine
|
|
41
|
+
|
|
42
|
+
- Local API server runs on `localhost:6550` (set per machine via `~/.summer/api-token`).
|
|
43
|
+
- Engine ships a webview module in `modules/1summer_engine/`. Do not assume the
|
|
44
|
+
unmodified upstream editor for UI patches.
|
|
45
|
+
- Project root contains `.summer/` for project memory; do not delete it.
|
|
46
|
+
|
|
47
|
+
## When in doubt
|
|
48
|
+
|
|
49
|
+
1. Use Summer MCP tools (`summer_inspect_node`, `summer_inspect_resource`) to read live editor state instead of guessing.
|
|
50
|
+
2. Use `summer_get_diagnostics` after every scene change.
|
|
51
|
+
3. Check `references/mcp-tools-reference.md` for the canonical tool list.
|