summer-engine 4.3.0 → 4.6.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/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +3 -1
- package/.codex-plugin/plugin.json +5 -3
- package/.cursor-plugin/plugin.json +4 -2
- package/.factory-plugin/plugin.json +4 -2
- package/AGENTS.md +2 -2
- package/CHANGELOG.md +100 -0
- package/README.md +67 -27
- package/commands/start.md +19 -15
- package/commands/summer.md +1 -0
- package/dist/cli/command-usage.d.ts +3 -1
- package/dist/cli/command-usage.js +10 -4
- package/dist/cli/commands/create.d.ts +1 -25
- package/dist/cli/commands/create.js +10 -138
- package/dist/cli/commands/dev.d.ts +66 -0
- package/dist/cli/commands/dev.js +234 -0
- package/dist/cli/commands/install.d.ts +15 -58
- package/dist/cli/commands/install.js +31 -481
- package/dist/cli/commands/login.js +1 -1
- package/dist/cli/commands/ops.d.ts +17 -1
- package/dist/cli/commands/ops.js +97 -9
- package/dist/cli/commands/preview.d.ts +58 -0
- package/dist/cli/commands/preview.js +181 -0
- package/dist/cli/commands/run.d.ts +1 -17
- package/dist/cli/commands/run.js +17 -206
- package/dist/cli/commands/setup.js +175 -32
- package/dist/cli/index.js +5 -0
- package/dist/core/agent-host.d.ts +3 -0
- package/dist/core/agent-host.js +1 -1
- package/dist/core/api-client.d.ts +10 -2
- package/dist/core/api-client.js +13 -8
- package/dist/core/capabilities/agent-playbook.js +1 -0
- package/dist/core/capabilities/apps.d.ts +9 -9
- package/dist/core/capabilities/asset-import.d.ts +4 -1
- package/dist/core/capabilities/asset-import.js +15 -6
- package/dist/core/capabilities/capture-gameplay.js +1 -1
- package/dist/core/capabilities/connect-signal.d.ts +2 -2
- package/dist/core/capabilities/content-make/index.d.ts +81 -0
- package/dist/core/capabilities/content-make/index.js +706 -0
- package/dist/core/capabilities/content-make/media.d.ts +57 -0
- package/dist/core/capabilities/content-make/media.js +172 -0
- package/dist/core/capabilities/content-make/plan.d.ts +130 -0
- package/dist/core/capabilities/content-make/plan.js +433 -0
- package/dist/core/capabilities/content-make/render.d.ts +64 -0
- package/dist/core/capabilities/content-make/render.js +370 -0
- package/dist/core/capabilities/content-make/study.d.ts +13 -0
- package/dist/core/capabilities/content-make/study.js +165 -0
- package/dist/core/capabilities/content-make/types.d.ts +173 -0
- package/dist/core/capabilities/content-make/types.js +6 -0
- package/dist/core/capabilities/content-make/voice.d.ts +7 -0
- package/dist/core/capabilities/content-make/voice.js +27 -0
- package/dist/core/capabilities/events.d.ts +2 -2
- package/dist/core/capabilities/export-game.d.ts +4 -1
- package/dist/core/capabilities/export-game.js +4 -3
- package/dist/core/capabilities/export-presets.d.ts +2 -1
- package/dist/core/capabilities/export-presets.js +2 -1
- package/dist/core/capabilities/go-live.d.ts +14 -0
- package/dist/core/capabilities/go-live.js +408 -0
- package/dist/core/capabilities/image-generation.d.ts +6 -6
- package/dist/core/capabilities/item-manifest.d.ts +92 -0
- package/dist/core/capabilities/item-manifest.js +247 -0
- package/dist/core/capabilities/placement.d.ts +7 -7
- package/dist/core/capabilities/projects-local.d.ts +68 -0
- package/dist/core/capabilities/projects-local.js +172 -0
- package/dist/core/capabilities/publish-build.d.ts +13 -2
- package/dist/core/capabilities/publish-build.js +87 -21
- package/dist/core/capabilities/publish-download.js +1 -1
- package/dist/core/capabilities/runtime-control.d.ts +7 -2
- package/dist/core/capabilities/runtime-control.js +27 -1
- package/dist/core/capabilities/seeing/args.d.ts +2 -2
- package/dist/core/capabilities/store-checks.d.ts +45 -0
- package/dist/core/capabilities/store-checks.js +85 -0
- package/dist/core/capabilities/store-details.d.ts +37 -0
- package/dist/core/capabilities/store-details.js +94 -0
- package/dist/core/capabilities/summer-bundle.d.ts +21 -2
- package/dist/core/capabilities/summer-bundle.js +54 -7
- package/dist/core/capabilities/sync-items.d.ts +89 -0
- package/dist/core/capabilities/sync-items.js +352 -0
- package/dist/core/capabilities/tool-dispatch.js +66 -6
- package/dist/core/capabilities/video-ogv.d.ts +49 -0
- package/dist/core/capabilities/video-ogv.js +260 -0
- package/dist/core/capability-skew.js +2 -2
- package/dist/core/cli-usage-beacon.js +26 -2
- package/dist/core/cli-usage.d.ts +5 -2
- package/dist/core/cli-usage.js +8 -2
- package/dist/core/engine-install.d.ts +53 -0
- package/dist/core/engine-install.js +93 -0
- package/dist/core/engine-installer.d.ts +134 -0
- package/dist/core/engine-installer.js +760 -0
- package/dist/core/engine-launch.d.ts +126 -0
- package/dist/core/engine-launch.js +398 -0
- package/dist/core/engine-self-start.d.ts +63 -0
- package/dist/core/engine-self-start.js +282 -0
- package/dist/core/engine.d.ts +7 -1
- package/dist/core/engine.js +9 -2
- package/dist/core/error-codes.d.ts +4 -1
- package/dist/core/error-codes.js +7 -3
- package/dist/core/library-read.d.ts +2 -2
- package/dist/core/library-search.d.ts +2 -2
- package/dist/core/local-project.d.ts +50 -0
- package/dist/core/local-project.js +234 -0
- package/dist/core/ops/areas.js +9 -3
- package/dist/core/ops/catalog.d.ts +9 -0
- package/dist/core/ops/catalog.js +56 -4
- package/dist/core/ops/engine-answer.d.ts +4 -1
- package/dist/core/ops/engine-answer.js +16 -3
- package/dist/core/ops/engine.js +4 -4
- package/dist/core/ops/save-files.d.ts +6 -1
- package/dist/core/ops/save-files.js +67 -50
- package/dist/core/ops/search.js +9 -2
- package/dist/core/preview/input.d.ts +35 -0
- package/dist/core/preview/input.js +105 -0
- package/dist/core/preview/launch-config.d.ts +52 -0
- package/dist/core/preview/launch-config.js +137 -0
- package/dist/core/preview/page.d.ts +22 -0
- package/dist/core/preview/page.js +232 -0
- package/dist/core/preview/preview-source.d.ts +179 -0
- package/dist/core/preview/preview-source.js +350 -0
- package/dist/core/preview/server.d.ts +26 -0
- package/dist/core/preview/server.js +218 -0
- package/dist/core/telemetry.d.ts +20 -16
- package/dist/core/telemetry.js +92 -16
- package/dist/core/templates.d.ts +20 -0
- package/dist/core/templates.js +300 -3
- package/dist/core/tool-profiles.d.ts +2 -2
- package/dist/core/tool-profiles.js +1 -0
- package/dist/core/usage-fields.d.ts +41 -0
- package/dist/core/usage-fields.js +150 -0
- package/dist/core/util/sleep.d.ts +3 -0
- package/dist/core/util/sleep.js +12 -0
- package/dist/installer/agent-config.d.ts +29 -0
- package/dist/installer/agent-config.js +75 -11
- package/dist/installer/agent-detect.d.ts +44 -0
- package/dist/installer/agent-detect.js +166 -0
- package/dist/installer/agent-table.d.ts +19 -0
- package/dist/installer/agent-table.js +130 -0
- package/dist/installer/setup-error.d.ts +19 -0
- package/dist/installer/setup-error.js +34 -0
- package/dist/installer/version-check.d.ts +5 -0
- package/dist/installer/version-check.js +21 -0
- package/dist/mcp/ops-surface.d.ts +7 -0
- package/dist/mcp/ops-surface.js +40 -3
- package/dist/mcp/server.d.ts +14 -4
- package/dist/mcp/server.js +42 -8
- package/dist/mcp/tool-surface.d.ts +8 -0
- package/dist/mcp/tool-surface.js +19 -2
- package/dist/mcp/tools/creator-tools.js +92 -14
- package/dist/mcp/tools/debug-tools.js +2 -0
- package/dist/mcp/tools/local-project-tools.d.ts +6 -0
- package/dist/mcp/tools/local-project-tools.js +33 -0
- package/dist/mcp/tools/navigation-tools.js +2 -1
- package/dist/mcp/tools/project-tools.js +5 -8
- package/dist/mcp/tools/seeing-tools.js +1 -1
- package/dist/mcp/tools/visual-tools.js +1 -1
- package/dist/mcp/tools/with-engine.js +1 -1
- package/dist/project-memory/agent-instructions.js +11 -3
- package/gemini-extension.json +3 -1
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +18 -5
- package/library/skills/README.md +12 -8
- package/library/skills/animated-loop/SKILL.md +10 -16
- package/library/skills/cinematic-cutscene/SKILL.md +7 -18
- package/library/skills/headless-scripting/SKILL.md +7 -4
- package/library/skills/in-game-purchases/SKILL.md +25 -8
- package/library/skills/in-game-purchases/resource.yaml +2 -1
- package/library/skills/launch-ready/SKILL.md +351 -0
- package/library/skills/launch-ready/resource.yaml +45 -0
- package/library/skills/make-content/SKILL.md +202 -0
- package/library/skills/make-content/resource.yaml +32 -0
- package/library/skills/make-game/SKILL.md +6 -4
- package/library/skills/make-game/resource.yaml +2 -1
- package/library/skills/navigate-summer/SKILL.md +4 -2
- package/library/skills/new-project/SKILL.md +15 -22
- package/library/skills/new-project/resource.yaml +2 -1
- package/library/skills/painted-25d-world/SKILL.md +366 -0
- package/library/skills/painted-25d-world/_building-blocks/blender-route.md +33 -0
- package/library/skills/painted-25d-world/_building-blocks/blockout_builder.gd +149 -0
- package/library/skills/painted-25d-world/_building-blocks/capture_paint_view.gd +32 -0
- package/library/skills/painted-25d-world/_building-blocks/paint_projection.gdshader +23 -0
- package/library/skills/painted-25d-world/_building-blocks/paintcam_check.gd +25 -0
- package/library/skills/painted-25d-world/_building-blocks/painted_door.gd +17 -0
- package/library/skills/painted-25d-world/_building-blocks/painted_location.gd +56 -0
- package/library/skills/painted-25d-world/_building-blocks/painted_player.gd +125 -0
- package/library/skills/painted-25d-world/_building-blocks/painted_world.gd +111 -0
- package/library/skills/painted-25d-world/_building-blocks/tools/cam.py +57 -0
- package/library/skills/painted-25d-world/_building-blocks/tools/detail4k.py +81 -0
- package/library/skills/painted-25d-world/_building-blocks/tools/overlay.py +17 -0
- package/library/skills/painted-25d-world/_building-blocks/tools/preview.py +60 -0
- package/library/skills/painted-25d-world/resource.yaml +39 -0
- package/library/skills/publish-your-game/SKILL.md +13 -14
- package/library/skills/publish-your-game/resource.yaml +4 -3
- package/library/skills/release-checklist/SKILL.md +188 -0
- package/library/skills/release-checklist/resource.yaml +40 -0
- package/library/skills/release-readiness/SKILL.md +1 -1
- package/library/skills/release-readiness/resource.yaml +1 -1
- package/library/skills/store-art/SKILL.md +1 -1
- package/library/skills/store-art/resource.yaml +1 -1
- package/library/skills/store-listing/SKILL.md +1 -1
- package/library/skills/store-listing/resource.yaml +1 -1
- package/library/skills/summer-cli/SKILL.md +2 -0
- package/library/skills/summer-store/SKILL.md +295 -39
- package/library/skills/summer-store/resource.yaml +9 -3
- package/library/skills/trailer-shot/SKILL.md +3 -3
- package/library/templates/README.md +5 -0
- package/library/tools/content-make/resource.yaml +132 -0
- package/library/tools/frame-shot/GUIDE.md +1 -1
- package/library/tools/go-live/resource.yaml +67 -0
- package/library/tools/import-from-url/GUIDE.md +1 -0
- package/library/tools/import-from-url-batch/GUIDE.md +1 -1
- package/library/tools/play/GUIDE.md +2 -0
- package/library/tools/projects-create-local/resource.yaml +55 -0
- package/library/tools/projects-open-local/resource.yaml +51 -0
- package/library/tools/publish-build/resource.yaml +3 -3
- package/library/tools/screenshot/GUIDE.md +1 -1
- package/library/tools/store-set-details/resource.yaml +114 -0
- package/library/tools/sync-items/resource.yaml +72 -0
- package/package.json +1 -1
- package/registry/generated/aliases.json +6 -0
- package/registry/generated/counts.json +3 -3
- package/registry/generated/gemini-extension.json +3 -1
- package/registry/generated/index.json +463 -37
- package/registry/generated/marketplace.claude.json +1 -1
- package/registry/generated/plugin.claude-directory.json +4 -2
- package/registry/generated/plugin.claude.json +3 -1
- package/registry/generated/plugin.codex.json +5 -3
- package/registry/generated/plugin.cursor.json +4 -2
- package/registry/generated/plugin.factory.json +4 -2
- package/registry/generated/server.json +2 -2
- package/registry/generated/skills-index.md +12 -8
- package/registry/generated/skills-registry.json +62 -5
- package/registry/operations/legacy-names.json +40 -0
- package/registry/operations/public-catalog.json +10144 -387
package/dist/mcp/server.js
CHANGED
|
@@ -9,6 +9,7 @@ import { registerAuditTools } from "./tools/audit-tools.js";
|
|
|
9
9
|
import { WITH_ENGINE_META } from "./tools/with-engine.js";
|
|
10
10
|
import { registerPlaybookPrompt, registerProjectTools, } from "./tools/project-tools.js";
|
|
11
11
|
import { registerPerceptionTools } from "./tools/perception-tools.js";
|
|
12
|
+
import { registerLocalProjectTools } from "./tools/local-project-tools.js";
|
|
12
13
|
import { registerSpatialTools } from "./tools/spatial-tools.js";
|
|
13
14
|
import { registerPlacementTools } from "./tools/placement-tools.js";
|
|
14
15
|
import { registerScriptTools } from "./tools/script-tools.js";
|
|
@@ -39,10 +40,12 @@ import { refreshInstalledSkills, refreshNotice } from "../installer/skills-refre
|
|
|
39
40
|
import { OUTPUT_CAPS, PROFILE_TOOL_LIMIT, PROFILES, TOOLSETS, annotationsFor, registryAuthorities, resolveToolSelection, selectionIncludes, withMaxCharactersArg, } from "./tool-surface.js";
|
|
40
41
|
import { shapeToolResult, singleEditCounter } from "./tool-results.js";
|
|
41
42
|
import { recordLocalToolCall, recordMcpSession, toolErrorCode } from "../core/telemetry.js";
|
|
43
|
+
import { boundProject } from "../core/usage-fields.js";
|
|
42
44
|
import { currentToolContext, registerMcpClientSource, runWithToolContext } from "../core/agent-host.js";
|
|
43
45
|
import { buildBootDriftNotice, fetchLatestRegistryVersion, } from "../installer/version-check.js";
|
|
44
46
|
import { TOOLKIT_VERSION as version } from "../core/version.js";
|
|
45
47
|
import { ENGINE_FREE_NOTE, engineNotRunningFromConnectError } from "../core/error-codes.js";
|
|
48
|
+
import { selfStartEngine } from "../core/engine-self-start.js";
|
|
46
49
|
import { resolveSurface } from "../core/tool-profiles.js";
|
|
47
50
|
import { OAuthError } from "../core/oauth.js";
|
|
48
51
|
let processDiagnosticsInstalled = false;
|
|
@@ -54,10 +57,11 @@ export const getCachedBootDriftNotice = getCachedNotice;
|
|
|
54
57
|
* session, before any tool call — the place for the habits end-to-end runs
|
|
55
58
|
* showed an agent needs on the first turn and would otherwise learn the hard
|
|
56
59
|
* way, including scripting a run of scene edits instead of hundreds of single
|
|
57
|
-
* calls. Kept under
|
|
60
|
+
* calls. Kept under 800 characters so it costs a few hundred tokens once; the
|
|
58
61
|
* full operating guide stays in the summer_agent_playbook prompt /
|
|
59
|
-
* summer_get_agent_playbook tool. The
|
|
60
|
-
* the
|
|
62
|
+
* summer_get_agent_playbook tool. The publish sentence names the publishing
|
|
63
|
+
* path, the user's confirmation and that going live has no staff wait; the last
|
|
64
|
+
* says an agent may open the editor and play the game in a window.
|
|
61
65
|
*/
|
|
62
66
|
export const SUMMER_MCP_INSTRUCTIONS = "Summer Engine MCP. Call summer_get_project_context first: it binds the open project. " +
|
|
63
67
|
"Before building or fixing: summer_search_library, then summer_read_library the hit. " +
|
|
@@ -65,7 +69,8 @@ export const SUMMER_MCP_INSTRUCTIONS = "Summer Engine MCP. Call summer_get_proje
|
|
|
65
69
|
"After play: summer_get_diagnostics, not only summer_get_console. " +
|
|
66
70
|
"All-black screenshot: no redraw yet, recapture. " +
|
|
67
71
|
"Report verified entries via summer_library_feedback. " +
|
|
68
|
-
"Publish: summer_export_game, summer_publish_build,
|
|
72
|
+
"Publish: summer_export_game, summer_publish_build, skill/release-checklist, summer_go_live, each confirmed. Live once automated checks pass: never say wait for review. " +
|
|
73
|
+
"Opening the editor and playing the game in a window is allowed and expected: summer_play, then summer_screenshot or summer_frame_shot; close what you opened, one editor at a time.";
|
|
69
74
|
/** The same first-turn habits for a profile without engine tools (`--profile assets`). */
|
|
70
75
|
export const SUMMER_MCP_ASSETS_INSTRUCTIONS = "Summer MCP, assets profile: generation and the Summer asset library, no engine tools. " +
|
|
71
76
|
"Read skill/summer-assets, then the asset's skill (sound-effect, prop-model, ...): stop at the file, skip its engine steps. " +
|
|
@@ -90,7 +95,22 @@ export function configureMcpEngineSelection(selection) {
|
|
|
90
95
|
engineSelection = selection ? { ...selection } : undefined;
|
|
91
96
|
cachedClient = null;
|
|
92
97
|
}
|
|
93
|
-
|
|
98
|
+
/** The session's editor selection (projects.open_local opens its project). */
|
|
99
|
+
export function currentMcpEngineSelection() {
|
|
100
|
+
return engineSelection ? { ...engineSelection } : undefined;
|
|
101
|
+
}
|
|
102
|
+
/** Bind the session to the project projects.open_local opened, with the client it connected. */
|
|
103
|
+
export function bindMcpProject(projectRoot, client) {
|
|
104
|
+
engineSelection = { cwd: projectRoot };
|
|
105
|
+
cachedClient = client;
|
|
106
|
+
}
|
|
107
|
+
/** The step's log lines go to the MCP log and stderr, never stdout (the protocol). */
|
|
108
|
+
export function logEngineSelfStart(line) {
|
|
109
|
+
appendMcpLogEvent("mcp:engine_self_start", { line });
|
|
110
|
+
writeMcpStderr("mcp:engine_self_start", { line });
|
|
111
|
+
}
|
|
112
|
+
/** `launch: false`: connect only, never open the editor (summer_open lists and prints with no side effects). */
|
|
113
|
+
export async function getClient(options = {}) {
|
|
94
114
|
// Opt-in per-project routing (editor -> live worker -> spawned worker).
|
|
95
115
|
// OFF by default: with the flag unset this block is skipped entirely and
|
|
96
116
|
// src/core/headless/ is never even loaded (dynamic import). With the flag
|
|
@@ -129,8 +149,14 @@ export async function getClient() {
|
|
|
129
149
|
// (engine_not_running). Anything else (several editors open, …) already
|
|
130
150
|
// says how to pick one.
|
|
131
151
|
const notRunning = engineNotRunningFromConnectError(reason);
|
|
132
|
-
if (notRunning)
|
|
152
|
+
if (notRunning && options.launch === false)
|
|
133
153
|
throw notRunning;
|
|
154
|
+
if (notRunning) {
|
|
155
|
+
// Open the session's project ourselves when we can, else a coded
|
|
156
|
+
// error with the one command that gets there (core/engine-self-start.ts).
|
|
157
|
+
cachedClient = await selfStartEngine(engineSelection, notRunning, { log: logEngineSelfStart });
|
|
158
|
+
return cachedClient;
|
|
159
|
+
}
|
|
134
160
|
throw new Error(reason + "\n" +
|
|
135
161
|
"Open the intended project in Summer Engine, or run: npx -y summer-engine@latest run <project-path>\n" +
|
|
136
162
|
"Note: " + ENGINE_FREE_NOTE);
|
|
@@ -235,14 +261,21 @@ function installToolCallWrapper(server, surfaceMeta) {
|
|
|
235
261
|
// A tool another tool runs (summer_call, a v4 operation over its 3.x
|
|
236
262
|
// tool) is part of that call: only the outer call sends a usage line.
|
|
237
263
|
const nested = currentToolContext() !== undefined;
|
|
264
|
+
// What the call notes for its line (core/usage-fields.ts), shared with any tool it runs.
|
|
265
|
+
let noted = {};
|
|
238
266
|
const recordUsage = (ok, durationMs, error) => {
|
|
267
|
+
const bound = boundProject();
|
|
268
|
+
const usage = { ...(bound ? { projectIdHash: bound } : {}), ...noted };
|
|
239
269
|
if (!nested)
|
|
240
|
-
recordLocalToolCall({ tool: name, op: knownOp(opForToolCall(name, handlerArgs[0])), ...surfaceMeta, ok, durationMs, mcpClient: hostApp(), error });
|
|
270
|
+
recordLocalToolCall({ tool: name, op: knownOp(opForToolCall(name, handlerArgs[0])), ...surfaceMeta, ok, durationMs, mcpClient: hostApp(), error, usage });
|
|
241
271
|
};
|
|
242
272
|
let result;
|
|
243
273
|
try {
|
|
244
274
|
// Every Summer request the tool makes names it (core/agent-host.ts).
|
|
245
|
-
result = await runWithToolContext(name, "mcp", () =>
|
|
275
|
+
result = await runWithToolContext(name, "mcp", () => {
|
|
276
|
+
noted = currentToolContext()?.usage ?? {};
|
|
277
|
+
return handler(...handlerArgs);
|
|
278
|
+
});
|
|
246
279
|
}
|
|
247
280
|
catch (error) {
|
|
248
281
|
// A handler THROW never produced a result to classify; record it as
|
|
@@ -379,6 +412,7 @@ export function createMcpServer(selection = FULL_SELECTION, options = {}) {
|
|
|
379
412
|
registerSeeingTools(server);
|
|
380
413
|
registerAuditTools(server);
|
|
381
414
|
registerProjectTools(server, { engine });
|
|
415
|
+
registerLocalProjectTools(server);
|
|
382
416
|
registerFileTools(server);
|
|
383
417
|
registerAssetTools(server);
|
|
384
418
|
registerGenerateTools(server);
|
|
@@ -32,6 +32,14 @@ export declare function resolveToolSelection(options?: {
|
|
|
32
32
|
profile?: string;
|
|
33
33
|
toolsets?: string;
|
|
34
34
|
}, env?: NodeJS.ProcessEnv): ToolSelection;
|
|
35
|
+
/**
|
|
36
|
+
* Hosted 3.x status tools a limited profile leaves out because summer_go_live
|
|
37
|
+
* answers the same question (what players get now on each platform, and
|
|
38
|
+
* where each upload and download stands), so the local go-live tools fit
|
|
39
|
+
* under PROFILE_TOOL_LIMIT without pushing hosted store tools over it. The
|
|
40
|
+
* full profile keeps them, and store.status answers it on the v4 surface.
|
|
41
|
+
*/
|
|
42
|
+
export declare const SUPERSEDED_HOSTED_TOOLS: ReadonlySet<string>;
|
|
35
43
|
/** True when the selection keeps this tool. */
|
|
36
44
|
export declare function selectionIncludes(selection: ToolSelection, name: string, local: boolean): boolean;
|
|
37
45
|
/** Every name the refinement sets mention (a test checks each is a real tool). */
|
package/dist/mcp/tool-surface.js
CHANGED
|
@@ -36,8 +36,11 @@ const LOCAL_TOOLSETS = {
|
|
|
36
36
|
"summer_get_asset",
|
|
37
37
|
"summer_get_asset_download_url",
|
|
38
38
|
],
|
|
39
|
-
ship: ["summer_export_game", "summer_export_templates", "summer_publish_build", "summer_capture_gameplay"],
|
|
39
|
+
ship: ["summer_export_game", "summer_export_templates", "summer_publish_build", "summer_go_live", "summer_store_set_details", "summer_capture_gameplay"],
|
|
40
40
|
ui: ["summer_ui_actions", "summer_ui_activate", "summer_ui_screenshot", "summer_ui_tree"],
|
|
41
|
+
// Create and open a project on this computer with no shell (the full profile;
|
|
42
|
+
// the engine profile's clients have a shell, `summer dev`, and a tool cap).
|
|
43
|
+
projects: ["summer_projects_create_local", "summer_projects_open_local"],
|
|
41
44
|
};
|
|
42
45
|
const localToolset = new Map(Object.entries(LOCAL_TOOLSETS).flatMap(([set, names]) => names.map((name) => [name, set])));
|
|
43
46
|
/** Hosted tool names by pattern, first match wins. Unknown names land in `other` (full only). */
|
|
@@ -99,9 +102,21 @@ export function resolveToolSelection(options = {}, env = process.env) {
|
|
|
99
102
|
return { profile: "custom", toolsets: new Set(["core", ...extra]), ignored };
|
|
100
103
|
return { profile, toolsets: new Set([...PROFILES[profile], ...extra]), ignored };
|
|
101
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Hosted 3.x status tools a limited profile leaves out because summer_go_live
|
|
107
|
+
* answers the same question (what players get now on each platform, and
|
|
108
|
+
* where each upload and download stands), so the local go-live tools fit
|
|
109
|
+
* under PROFILE_TOOL_LIMIT without pushing hosted store tools over it. The
|
|
110
|
+
* full profile keeps them, and store.status answers it on the v4 surface.
|
|
111
|
+
*/
|
|
112
|
+
export const SUPERSEDED_HOSTED_TOOLS = new Set(["summer_store_releases", "summer_store_version_status"]);
|
|
102
113
|
/** True when the selection keeps this tool. */
|
|
103
114
|
export function selectionIncludes(selection, name, local) {
|
|
104
|
-
|
|
115
|
+
if (selection.toolsets === null)
|
|
116
|
+
return true;
|
|
117
|
+
if (!local && SUPERSEDED_HOSTED_TOOLS.has(name))
|
|
118
|
+
return false;
|
|
119
|
+
return selection.toolsets.has(toolsetOf(name, local));
|
|
105
120
|
}
|
|
106
121
|
// ── Annotations ──────────────────────────────────────────────────────────────
|
|
107
122
|
/**
|
|
@@ -139,6 +154,7 @@ const NETWORK_WRITES = new Set([
|
|
|
139
154
|
"summer_app_report_gap",
|
|
140
155
|
"summer_app_update",
|
|
141
156
|
"summer_slice_asset_sheet",
|
|
157
|
+
"summer_store_set_details",
|
|
142
158
|
]);
|
|
143
159
|
/** Changes that only add something new, or only touch the editor view or the running game. */
|
|
144
160
|
const ADDITIVE_OR_TRANSIENT = new Set([
|
|
@@ -181,6 +197,7 @@ const ADDITIVE_OR_TRANSIENT = new Set([
|
|
|
181
197
|
]);
|
|
182
198
|
/** Changes where a second identical call has no further effect. */
|
|
183
199
|
const IDEMPOTENT_WRITES = new Set([
|
|
200
|
+
"summer_store_set_details",
|
|
184
201
|
"summer_set_prop",
|
|
185
202
|
"summer_set_resource_property",
|
|
186
203
|
"summer_project_setting",
|
|
@@ -3,24 +3,20 @@ import { captureGameplay } from "../../core/capabilities/capture-gameplay.js";
|
|
|
3
3
|
import { exportGame } from "../../core/capabilities/export-game.js";
|
|
4
4
|
import { STORE_TARGETS } from "../../core/capabilities/export-presets.js";
|
|
5
5
|
import { exportTemplates, TEMPLATE_PLATFORMS } from "../../core/capabilities/export-templates.js";
|
|
6
|
+
import { goLive } from "../../core/capabilities/go-live.js";
|
|
6
7
|
import { publishBuild } from "../../core/capabilities/publish-build.js";
|
|
7
|
-
import {
|
|
8
|
+
import { AI_USES, STORE_FEATURES, setStoreDetails, SURVEY_LEVELS } from "../../core/capabilities/store-details.js";
|
|
9
|
+
import { BuildToolError, buildToolFailure } from "../../core/capabilities/summer-bundle.js";
|
|
10
|
+
import { syncItems } from "../../core/capabilities/sync-items.js";
|
|
11
|
+
import { contentMake, contentMakeInput } from "../../core/capabilities/content-make/index.js";
|
|
8
12
|
import { textJson } from "./text-json.js";
|
|
9
13
|
async function creatorResult(operation) {
|
|
10
14
|
try {
|
|
11
15
|
return textJson(await operation());
|
|
12
16
|
}
|
|
13
17
|
catch (error) {
|
|
14
|
-
if (error instanceof BuildToolError)
|
|
15
|
-
return textJson(
|
|
16
|
-
ok: false,
|
|
17
|
-
code: error.code,
|
|
18
|
-
message: error.message.replace(` ${error.recovery}`, ""),
|
|
19
|
-
recovery: error.recovery,
|
|
20
|
-
...(error.status ? { status: error.status } : {}),
|
|
21
|
-
...error.detail,
|
|
22
|
-
}, true);
|
|
23
|
-
}
|
|
18
|
+
if (error instanceof BuildToolError)
|
|
19
|
+
return textJson(buildToolFailure(error), true);
|
|
24
20
|
return textJson({
|
|
25
21
|
ok: false,
|
|
26
22
|
code: "creator_request_invalid",
|
|
@@ -93,11 +89,11 @@ export function registerCreatorTools(server) {
|
|
|
93
89
|
.optional()
|
|
94
90
|
.describe("What to do after waitSeconds, in order; exactly one of press, key, action, click, drag, wait, shot per step."),
|
|
95
91
|
}, async (args) => creatorResult(() => captureGameplay(args)));
|
|
96
|
-
server.tool("summer_publish_build", "Upload an export from summer_export_game to the creator's game on summer.games, through the same store upload Studio uses, straight from disk. A store bundle (format bundle): declare, upload parts, seal, wait until Summer makes the Build, name its client pack
|
|
97
|
-
gameId: z.string().optional().describe("The store game id (from
|
|
92
|
+
server.tool("summer_publish_build", "Upload an export from summer_export_game to the creator's game on summer.games, through the same store upload Studio uses, straight from disk. A store bundle (format bundle): declare, upload parts, seal, wait until Summer makes the Build, name its client pack and wait for Summer's automated checks on each platform (checks: passed, or failed with the code and the fix). A web build or native download (format download): upload it as the game's store version for its platform (web, macos-universal, windows-x64, linux-x64; read from the last export) and wait until Summer has checked it. First call with confirm=false: it checks that the game exists for this account and warns when its store page does not list the export's platforms, then returns the target (game name and id, file, digest, size, version) to show the user; set confirm only after they approve. Needs \"summer login --store\". A retry with the same file and clientVersion continues the same upload. Going live is summer_go_live.", {
|
|
93
|
+
gameId: z.string().optional().describe("The store game id (from games.list, or the Studio store page URL). Without it the call fails with game_required and lists your store games, or says why the store did not list them."),
|
|
98
94
|
file: z.string().optional().describe("Exported .zip. Defaults to the last summer_export_game result."),
|
|
99
95
|
clientVersion: z.string().describe("Build version, vMAJOR.MINOR.PATCH (e.g. v1.0.0); a new upload needs a new version."),
|
|
100
|
-
publish: z.boolean().default(false).describe("Bundles only: also
|
|
96
|
+
publish: z.boolean().default(false).describe("Bundles only: also make the Build available to players once its checks pass. summer_go_live does this, the release and the store page in one step."),
|
|
101
97
|
platform: z
|
|
102
98
|
.enum(["web", "macos-universal", "windows-x64", "linux-x64"])
|
|
103
99
|
.optional()
|
|
@@ -105,4 +101,86 @@ export function registerCreatorTools(server) {
|
|
|
105
101
|
confirm: z.boolean().default(false).describe("Set true only after the user approves the exact upload."),
|
|
106
102
|
waitSeconds: z.number().int().min(0).max(1800).optional().describe("How long to wait for Summer to make the Build (default 600). On timeout, call again to keep waiting."),
|
|
107
103
|
}, async (args) => creatorResult(() => publishBuild({ ...args, face: "mcp" })));
|
|
104
|
+
server.tool("summer_go_live", "Take a store game live for players: release its uploaded bundle Build (publicationId, or the newest upload) to every platform that passed Summer's automated checks, and publish its store page, through the same store routes Studio uses; then watch until players get it. It runs without an owner approval while the person's Agent permissions allow Publish (the default). First call with confirm=false: it reads the page, the Build and its per-platform checks and what players get now, and returns live, blocked (each failure with its fix), needs_changes (Summer's decision and feedback) or confirmation_required with the plan to show the user; set confirm only after they approve going live. With confirm=true it asks the platform and polls until live, or answers waiting with waitingOn (automated_checks, activation, or summer: a hold on Summer's side that needs nothing from the creator; never tell them to wait for a review). Call again with confirm=true to keep watching: nothing is asked twice. needs_permission: Publish is off in the person's Agent permissions. Web builds and downloads go live with the page. Needs \"summer login --store\".", {
|
|
105
|
+
gameId: z.string().optional().describe("The store game id (from games.list). Without it the call fails with game_required and lists your store games."),
|
|
106
|
+
publicationId: z.string().optional().describe("The bundle Build to release: publicationId from summer_publish_build. Default: the newest uploaded export."),
|
|
107
|
+
confirm: z.boolean().default(false).describe("Set true only after the user approves taking this exact page and Build live."),
|
|
108
|
+
waitSeconds: z.number().int().min(0).max(1800).optional().describe("How long to watch before answering (default 600). On waiting, call again to keep watching."),
|
|
109
|
+
}, async (args) => creatorResult(() => goLive({ ...args, face: "mcp" })));
|
|
110
|
+
server.tool("summer_store_set_details", "Fill the store page sections store.update_listing does not edit, on the store page draft: genres (1-5 genre tag ids from store.get_listing tags), features, credits, languageSupport, systemRequirements, contentSurvey (the age questionnaire: every answer, none included) and aiDisclosure (how AI was used; an empty uses list means no AI). Each section given replaces that section, null clears it, an omitted one keeps its value. A published page gets a new draft first. Saving never publishes: take it live with summer_go_live. Needs \"summer login --store\".", {
|
|
111
|
+
gameId: z.string().optional().describe("The store game id (from games.list)."),
|
|
112
|
+
genres: z.array(z.string()).min(1).max(5).nullable().optional().describe("Genre tag ids (category genre in store.get_listing tags), in the creator's order."),
|
|
113
|
+
features: z.array(z.enum(STORE_FEATURES)).min(1).nullable().optional().describe("What the game offers, from the store's closed list."),
|
|
114
|
+
credits: z
|
|
115
|
+
.object({ developer: z.string().min(1).max(120), publisher: z.string().min(1).max(120).optional(), franchise: z.string().min(1).max(120).optional() })
|
|
116
|
+
.nullable()
|
|
117
|
+
.optional()
|
|
118
|
+
.describe("Who made and published it; omit publisher or franchise when there is none."),
|
|
119
|
+
languageSupport: z
|
|
120
|
+
.array(z.object({ code: z.string(), interface: z.boolean(), fullAudio: z.boolean(), subtitles: z.boolean() }))
|
|
121
|
+
.min(1)
|
|
122
|
+
.max(30)
|
|
123
|
+
.nullable()
|
|
124
|
+
.optional()
|
|
125
|
+
.describe("One entry per language (BCP 47 code: en, pt-br, zh-hans), each with at least one kind of support."),
|
|
126
|
+
systemRequirements: z
|
|
127
|
+
.record(z.string(), z.unknown())
|
|
128
|
+
.nullable()
|
|
129
|
+
.optional()
|
|
130
|
+
.describe("Desktop hardware: { windows | macos | linux: { minimum: { os, processor, memory, graphics, storage, network, notes }, recommended? } }."),
|
|
131
|
+
contentSurvey: z
|
|
132
|
+
.object({
|
|
133
|
+
violence: z.enum(SURVEY_LEVELS.violence),
|
|
134
|
+
bloodGore: z.enum(SURVEY_LEVELS.bloodGore),
|
|
135
|
+
sexualContent: z.enum(SURVEY_LEVELS.sexualContent),
|
|
136
|
+
nudity: z.enum(SURVEY_LEVELS.nudity),
|
|
137
|
+
language: z.enum(SURVEY_LEVELS.language),
|
|
138
|
+
substances: z.enum(SURVEY_LEVELS.substances),
|
|
139
|
+
gambling: z.enum(SURVEY_LEVELS.gambling),
|
|
140
|
+
userGeneratedContent: z.boolean(),
|
|
141
|
+
onlineInteraction: z.boolean(),
|
|
142
|
+
inGamePurchases: z.boolean(),
|
|
143
|
+
notes: z.string().max(1000).optional(),
|
|
144
|
+
})
|
|
145
|
+
.nullable()
|
|
146
|
+
.optional()
|
|
147
|
+
.describe("The mature-content questionnaire; every answer is required, none is an answer."),
|
|
148
|
+
aiDisclosure: z
|
|
149
|
+
.object({ uses: z.array(z.enum(AI_USES)), description: z.string().max(2000).optional() })
|
|
150
|
+
.nullable()
|
|
151
|
+
.optional()
|
|
152
|
+
.describe("How AI was used to make the game; uses [] means no AI. generatedGameplay needs a description of what is generated and its guardrails."),
|
|
153
|
+
}, async (args) => creatorResult(() => setStoreDetails({ ...args, face: "mcp" })));
|
|
154
|
+
server.tool("summer_sync_items", "Bring the store game's items in line with summer.items.json in the project root, matched by each item's slug (the creator key the game's code uses, e.g. emote_ponder), so the same file gives the staging game and the production game the same items. First call with confirm=false: it checks the file, reads the game's items by slug and returns the plan (create, update with the changed fields, unchanged, blocked with the reason, and the game's items the file does not name, which are never changed) for the gateway this machine is set to; show it to the user. With confirm=true it uploads the changed images and sends the plan to the game's owner: the answer carries the approval link; nothing changes until the owner approves in Studio, then each item is created or revised, priced in Sparks and submitted for Summer's review. After Summer approves an item, the owner activates it in Studio (Review activation on the item's page); players can buy it only while the game is live. A change to a live item is a new revision, reviewed and activated again. An item with the same slug that is a Passport collectible is blocked: Passport items are not synced. One approval takes at most 50 changed items: a larger plan lists batches, confirm each with slugs. A new item needs at least one image. Never activates or releases an item. Needs \"summer login --store\". Staging or production is the gateway this machine is set to; switching takes SUMMER_GATEWAY_URL and SUMMER_MCP_RESOURCE_URL, then \"summer login --store --force\" (one store sign-in per machine; a sign-in for the other environment fails with store_login_other_environment).", {
|
|
155
|
+
gameId: z.string().optional().describe("The store game id on this gateway (from games.list). Staging and production games have different ids. Without it the call fails with game_required and lists your store games."),
|
|
156
|
+
project: z.string().optional().describe("Project folder with project.godot and summer.items.json. Defaults to the MCP's bound project, then the working directory."),
|
|
157
|
+
file: z.string().optional().describe("The items file, relative to the project. Default summer.items.json."),
|
|
158
|
+
slugs: z.array(z.string()).max(500).optional().describe("Only these items (their slugs); the rest of the file is left out of the plan. Confirm a plan of more than 50 changed items one batch (from batches) at a time."),
|
|
159
|
+
confirm: z.boolean().default(false).describe("Set true only after the user approves the plan."),
|
|
160
|
+
}, async (args) => creatorResult(() => syncItems({ ...args, face: "mcp" })));
|
|
161
|
+
server.tool("summer_content_make", "Make one finished, postable piece of content for a game and put it in the team's Review (Studio > Grow > Content), on this computer: it picks the reference from Summer's library that fits today (a format the game has not posted, what changed in the repo), studies its cuts and caption look, measures the footage, maps each reference shot to a clip window with motion, cuts to the reference's timings, draws the captions in its style, mixes at -14 LUFS, checks hook, motion per shot, safe zones, length and loudness, renders the reference side by side, uploads and saves the piece (failed checks: a draft). It never writes words itself: the first call answers needs_words with the beats and the kit's learnings; call again with workDir, lines (one per beat, in the reference's pattern, true to the game, never the reference's own words) and learningIds. A reference that speaks also needs voiceFile, the lines recorded in the team's voice (needs_voice). Same workDir continues where a run stopped. claim=true takes the oldest queued make request instead (nothing_queued when none; a revision answers needs_changes with what the team asked) and reports every stage to it, so Today shows it live. Needs ffmpeg and ImageMagick (and Whisper for a voice-over). Takes a few minutes.", {
|
|
162
|
+
contentGameId: z.string().optional().describe("The game's Content id, from content.list_games. Not needed with workDir."),
|
|
163
|
+
teamId: z.string().optional().describe("From team.list, when the person has more than one team."),
|
|
164
|
+
referenceId: z.string().optional().describe("The reference to copy, from content.list_references. Without it the best fit for today is picked."),
|
|
165
|
+
footage: z.array(z.string()).max(20).optional().describe("Folders (or files) of the game's footage on this computer: clips, stills, and the game's music. Default ./content/footage."),
|
|
166
|
+
lines: z.array(z.string()).max(80).optional().describe("Our words, one per reference beat (\"\" for a beat without words), from the needs_words answer."),
|
|
167
|
+
voiceFile: z.string().optional().describe("The lines recorded in the team's voice (wav, mp3, m4a), when the reference speaks."),
|
|
168
|
+
title: z.string().max(100).optional().describe("The piece's title in Review (also the YouTube title)."),
|
|
169
|
+
caption: z.string().max(2200).optional().describe("The post caption: one dry line and a question, no hashtags."),
|
|
170
|
+
hashtags: z.array(z.string()).max(10).optional().describe("3-5 tags, the game's tag first. Default: the kit's core tags."),
|
|
171
|
+
note: z.string().max(2000).optional().describe("What to make it about (\"the new boss\", or commit lines like \"3f2a1c9 Add waiting room\"): weighs the reference pick and becomes the why."),
|
|
172
|
+
claim: z.boolean().optional().describe("Make a queued make request (the daily scheduler's, or a Change pressed on Today) and report every stage to it: the one named by requestId, else the team's oldest."),
|
|
173
|
+
requestId: z.string().optional().describe("The make request to claim, from content.make."),
|
|
174
|
+
learningIds: z.array(z.string()).max(20).optional().describe("The kit learnings (content.get_kit learnings, listed in needs_words) the words and choices applied."),
|
|
175
|
+
workDir: z.string().optional().describe("The work folder an earlier call answered: continues that piece."),
|
|
176
|
+
out: z.string().optional().describe("Where the work goes (pieces, the reference videos and footage measurements). Default ~/.summer/content."),
|
|
177
|
+
music: z.string().optional().describe("The game's own music for a reference with a song (never the reference's audio). Default: the first song in the footage folders."),
|
|
178
|
+
font: z.string().optional().describe("The game's caption font file (.ttf/.otf)."),
|
|
179
|
+
referenceFile: z.string().optional().describe("The reference video, when it cannot be fetched (yt-dlp)."),
|
|
180
|
+
repo: z.string().optional().describe("The game's repo, for what changed today. Default the working directory."),
|
|
181
|
+
stage: z.enum(["plan", "all"]).optional().describe("plan: stop after the plan, to change shots in plan.json."),
|
|
182
|
+
upload: z.boolean().optional().describe("false: render and check only, upload nothing."),
|
|
183
|
+
replan: z.boolean().optional().describe("Throw away the plan and map the shots again."),
|
|
184
|
+
status: z.enum(["draft", "in_review"]).optional().describe("Override: in_review even with failed checks, or draft."),
|
|
185
|
+
}, async (args) => creatorResult(() => contentMake({ ...contentMakeInput(args), face: "mcp" })));
|
|
108
186
|
}
|
|
@@ -194,6 +194,8 @@ Use this when summer_get_diagnostics shows a non-zero \`debugger.warnings\` coun
|
|
|
194
194
|
}));
|
|
195
195
|
server.tool("summer_play", `Start the game. With no parameters it runs QUIETLY inside Summer Engine's viewport (the 'main' instance); scene runs one level or UI screen instead of the main scene.
|
|
196
196
|
|
|
197
|
+
Playing the game in the editor's real window is allowed and expected: then summer_screenshot target:'game' to see it, summer_stop when done. One editor at a time.
|
|
198
|
+
|
|
197
199
|
QUIET BY DEFAULT (focus:false, PlayGame agent:true): the user usually works on the same machine, so the EDITOR stays put. It does not switch to the Game tab or take keyboard focus (nor follow the game's later focus report), and skips the render-health self-check that would misread the untouched Game view as a GPU failure and flip the user's embed setting. Quiet play does NOT hide the game: it runs embedded in the Game view (visible if that tab is open) and is the running game for summer_is_running / summer_screenshot target:'game' / summer_get_diagnostics. With "Embed Game on Play" off the game gets its own OS window; engines with --summer-background (0.5.66+) open it without taking focus. The result echoes agent_quiet:true when honoured; without agent_quiet the engine predates quiet play and most likely took focus (posture_note says so). focus:true launches like the toolbar Play button (Game tab + focus): ONLY when the user is watching and asked to see the game come up.
|
|
198
200
|
|
|
199
201
|
After starting, confirm boot with summer_is_running (boot time varies; never sleep a guessed delay), then summer_get_diagnostics for runtime errors.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
/**
|
|
3
|
+
* projects.create_local and projects.open_local: what `summer dev` does, for
|
|
4
|
+
* agents with no shell (core/capabilities/projects-local.ts).
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerLocalProjectTools(server: McpServer): void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createLocalProject, LocalProjectError, localProjectFailure, openLocalProject, } from "../../core/capabilities/projects-local.js";
|
|
3
|
+
import { EngineNotRunningError } from "../../core/error-codes.js";
|
|
4
|
+
import { bindMcpProject, currentMcpEngineSelection, logEngineSelfStart } from "../server.js";
|
|
5
|
+
import { textJson } from "./text-json.js";
|
|
6
|
+
import { engineNotRunningResult } from "./with-engine.js";
|
|
7
|
+
async function localProjectResult(operation) {
|
|
8
|
+
try {
|
|
9
|
+
return textJson(await operation());
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
if (error instanceof LocalProjectError)
|
|
13
|
+
return textJson(localProjectFailure(error), true);
|
|
14
|
+
if (error instanceof EngineNotRunningError)
|
|
15
|
+
return engineNotRunningResult(error);
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* projects.create_local and projects.open_local: what `summer dev` does, for
|
|
21
|
+
* agents with no shell (core/capabilities/projects-local.ts).
|
|
22
|
+
*/
|
|
23
|
+
export function registerLocalProjectTools(server) {
|
|
24
|
+
server.tool("summer_projects_create_local", "Create a Summer project on this computer from a template, with no shell: the built-in 3d-basic (sky, sun, ground and a hero; no download) unless template names another. Writes it into dir, or into <name> next to the session's project, in the session's folder, or in ~/Documents/Summer; never inside another project. A folder that already holds project.godot is left as it is. Returns projectPath. Next: summer_projects_open_local with that path.", {
|
|
25
|
+
template: z.string().max(80).optional().describe("Template slug (docs.search finds them): 3d-basic (default), empty, 3d-third-person-controller, 2d-platformer, ..."),
|
|
26
|
+
name: z.string().max(80).optional().describe("The project's name and folder name. Default my-game (or the template's name)."),
|
|
27
|
+
dir: z.string().max(1000).optional().describe("The project folder: a new or empty folder, not inside another project; ~ is home. Default <name> next to the session's project, in the session's folder, or in ~/Documents/Summer."),
|
|
28
|
+
}, async (args) => localProjectResult(() => createLocalProject(args, currentMcpEngineSelection()?.cwd)));
|
|
29
|
+
server.tool("summer_projects_open_local", "Open a project on this computer in Summer Engine, in the background, and wait until it connects (an editor that has it open is used as it is); this session's engine tools then work on it. When Summer Engine is not installed it answers engine_not_running: ask the person, and only with their yes call again with install: true (about 660 MB, free). A first open can take a few minutes: the answer says to call again. It adds summer-preview to the session folder's .claude/launch.json (Claude Code Desktop's Browser pane), and preview.url is where the game shows. Next: summer_get_project_context.", {
|
|
30
|
+
path: z.string().max(1000).optional().describe("The project folder (holds project.godot), e.g. projectPath from summer_projects_create_local. Default this session's project."),
|
|
31
|
+
install: z.boolean().default(false).describe("Install Summer Engine first when it is missing. Set true only after the person said yes."),
|
|
32
|
+
}, async (args) => localProjectResult(() => openLocalProject(args, currentMcpEngineSelection(), { log: logEngineSelfStart }, (root, client) => bindMcpProject(root, client))));
|
|
33
|
+
}
|
|
@@ -24,7 +24,8 @@ Result: { ok, action: opened | printed | listed | ambiguous | unsupported | engi
|
|
|
24
24
|
- ambiguous: several destinations match; call again with one of matches[].id.
|
|
25
25
|
Only summerengine.com (and subdomains) are ever opened — a gateway configured elsewhere is refused (blocked_origin); res:// paths with .. or other escapes are refused; a machine with no browser gets open_failed with the url to hand over.`, openArgsShape, async (args) => {
|
|
26
26
|
const result = await runOpen(args, {
|
|
27
|
-
|
|
27
|
+
// Lists and prints must not open the editor (no side effects).
|
|
28
|
+
engine: () => getClient({ launch: false }),
|
|
28
29
|
openUrl: (url) => open(url),
|
|
29
30
|
isLoggedIn: async () => (await getAuthToken()) !== null,
|
|
30
31
|
gatewayUrl: resolveGatewayUrl,
|
|
@@ -5,6 +5,7 @@ import { buildProjectContext, projectContextInputShape, resolveMainScene, } from
|
|
|
5
5
|
import { getCachedBootDriftNotice } from "../boot-notice.js";
|
|
6
6
|
import { appendMcpLogEvent } from "../../core/mcp-log.js";
|
|
7
7
|
import { withEngine } from "./with-engine.js";
|
|
8
|
+
import { importUrls } from "../../core/capabilities/video-ogv.js";
|
|
8
9
|
import { asRecord, stringFrom } from "../../core/util/json.js";
|
|
9
10
|
// One skew log line per MCP process — the warning itself stays in every
|
|
10
11
|
// summer_get_project_context payload.
|
|
@@ -245,20 +246,16 @@ Use this for:
|
|
|
245
246
|
- 3D models (.glb, .gltf, .obj)
|
|
246
247
|
- Textures (.png, .jpg, .webp)
|
|
247
248
|
- Audio (.ogg, .wav, .mp3)
|
|
249
|
+
- Video (.mp4, .mov, .webm, .ogv): Summer Engine plays only Ogg Theora, so any other video is converted to .ogv first and lands at the path with .ogv; the answer names it.
|
|
248
250
|
|
|
249
251
|
The path is auto-inferred from the URL filename if not specified. After import, the asset is immediately usable in scenes.`, {
|
|
250
252
|
url: z.string().describe("HTTP(S) URL to download from"),
|
|
251
253
|
path: z.string().optional().describe("Target path in project, e.g. 'res://assets/player.glb'. Auto-inferred from URL if omitted."),
|
|
252
|
-
}, async ({ url, path }) => withEngine(async (client) => {
|
|
253
|
-
|
|
254
|
-
if (path)
|
|
255
|
-
op.path = path;
|
|
256
|
-
return client.executeOps([op]);
|
|
257
|
-
}));
|
|
258
|
-
server.tool("summer_import_from_url_batch", "Download multiple files from URLs in one operation. Performs a single filesystem scan after all downloads, which is faster than importing one at a time.", {
|
|
254
|
+
}, async ({ url, path }) => withEngine(async (client) => importUrls(client, [{ url, ...(path ? { path } : {}) }], { batch: false })));
|
|
255
|
+
server.tool("summer_import_from_url_batch", "Download multiple files from URLs in one operation. Performs a single filesystem scan after all downloads, which is faster than importing one at a time. A video (.mp4, .mov, .webm) is converted to Ogg Theora first and lands at its path with .ogv.", {
|
|
259
256
|
imports: z.array(z.object({
|
|
260
257
|
url: z.string().describe("URL to download"),
|
|
261
258
|
path: z.string().describe("Target path in project"),
|
|
262
259
|
})).describe("Array of {url, path} objects"),
|
|
263
|
-
}, async ({ imports }) => withEngine(async (client) => client
|
|
260
|
+
}, async ({ imports }) => withEngine(async (client) => importUrls(client, imports, { batch: true })));
|
|
264
261
|
}
|
|
@@ -54,7 +54,7 @@ Returns the grid + caption. ${COMMON}`, debugViewsShape, run(debugViews));
|
|
|
54
54
|
server.tool("summer_zoom", `High-resolution close look at part of a frame: region [x, y, w, h] (fractions of the frame) or mark N from a marks render of the same pose. The camera renders the EXACT sub-frustum of that region at full output resolution, so you see real texture detail, seams, gaps and floating pieces, not upscaled pixels. A region is honoured exactly (no pad by default, never widened to an aspect ratio: the image takes the region's own aspect, with dark bars only for extreme shapes); a mark gets pad 0.15.
|
|
55
55
|
|
|
56
56
|
Use after a sheet or debug view shows something suspicious. view picks beauty or a debug view. Returns the zoomed image + caption (the real zoom factor, the rendered region, widened_because when it is larger than asked, mark -> node path and a warning when that node is hidden). ${COMMON}`, zoomShape, run(zoom));
|
|
57
|
-
server.tool("summer_frame_shot", `Smart framing: find good camera poses for a shot type automatically, measured in-engine against the real geometry.
|
|
57
|
+
server.tool("summer_frame_shot", `Smart framing: find good camera poses for a shot type automatically, measured in-engine against the real geometry. It runs in the open editor; use it to look at the scene as a player would.
|
|
58
58
|
|
|
59
59
|
shot: establishing (wide), eye_level (from a spawn node at player eye height), low_angle (hero, near the ground looking up: the camera moves closer and widens its FOV instead of sinking into the ground), detail (close-up), corridor (down a lane or corridor found inside the subject by a free-space scan, preferring the view in from its open end, walls on both sides).
|
|
60
60
|
|
|
@@ -103,7 +103,7 @@ function teachPreviewFailure(snap) {
|
|
|
103
103
|
export function registerVisualTools(server) {
|
|
104
104
|
server.tool("summer_screenshot", `Capture a frame from Summer Engine and return it as an image you can look at directly.
|
|
105
105
|
|
|
106
|
-
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.
|
|
106
|
+
Use this to visually verify your work (it reads the real open editor or game window; that is allowed and expected): 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.
|
|
107
107
|
|
|
108
108
|
target:
|
|
109
109
|
"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.
|
|
@@ -162,7 +162,7 @@ export async function withEngine(fn, opts) {
|
|
|
162
162
|
export function engineNotRunningResult(error) {
|
|
163
163
|
return {
|
|
164
164
|
isError: true,
|
|
165
|
-
content: [{ type: "text", text: JSON.stringify(codedFailure(error.code, error.message, { hint: ENGINE_FREE_NOTE }), null, 2) }],
|
|
165
|
+
content: [{ type: "text", text: JSON.stringify(codedFailure(error.code, error.message, { hint: ENGINE_FREE_NOTE }, error.recovery), null, 2) }],
|
|
166
166
|
};
|
|
167
167
|
}
|
|
168
168
|
const PRE_APPLY_FAILURE_REASON = {
|
|
@@ -36,10 +36,18 @@ export function renderAgentsBlock(commands = projectCommands()) {
|
|
|
36
36
|
"",
|
|
37
37
|
"This is a game made with Summer Engine, the game engine built for AI. Game code is GDScript (`.gd`), scenes are `.tscn`, resources are `.tres`, and `project.godot` is the project file.",
|
|
38
38
|
"",
|
|
39
|
+
"## First game: make it look finished",
|
|
40
|
+
"",
|
|
41
|
+
"People judge Summer by the first frame of their first game, so make it look finished even when they ask for a quick prototype. Keep the features they asked for and raise the look:",
|
|
42
|
+
"- sky and sun with shadows, tonemapping, a little fog;",
|
|
43
|
+
"- a texture on every surface, and real models for the player, characters and main props (the starter project and the Summer asset library first), never capsules or boxes as final art;",
|
|
44
|
+
"- one colour palette and a styled UI font.",
|
|
45
|
+
"Before you say it is ready, play it and look at a screenshot of the running game, not the editor: the editor shows a preview sky and sun that the game does not have.",
|
|
46
|
+
"",
|
|
39
47
|
"## How to work on it",
|
|
40
48
|
"",
|
|
41
|
-
"-
|
|
42
|
-
"- With Summer's MCP server,
|
|
49
|
+
"- Write GDScript (`.gd`) files directly. Create and change scenes and import assets through Summer while Summer Engine has the project open: it imports new files, and the editor rewrites `.tscn` files when it saves, so hand edits to an open scene get lost.",
|
|
50
|
+
"- With Summer's MCP server, use the `summer_*` tools; `summer_get_project_context` tells you which project you are connected to. Without it, run the same operations as `summer` commands in the shell (the `summer-cli` skill). If `summer` is missing or older than 4.0, use `npx -y summer-engine@latest` in its place.",
|
|
43
51
|
"- Summer's skills teach each part of a game. `npx -y summer-engine@latest setup <agent> --yes` installs them for your agent (add `--cli` for no MCP server). Start with `using-summer`, and find the right one with `summer docs search --query \"<the task>\"`.",
|
|
44
52
|
"- `.summer/` is the project's memory: `GameSoul.md` says what the game is, `memory/` holds the decisions to keep. Read them before you change the game's look, sound or story.",
|
|
45
53
|
"- Check your own work: script errors, a screenshot, a play run. Ask the person to play only for feel and fun.",
|
|
@@ -50,7 +58,7 @@ export function renderAgentsBlock(commands = projectCommands()) {
|
|
|
50
58
|
"",
|
|
51
59
|
"## Publishing",
|
|
52
60
|
"",
|
|
53
|
-
"The `publish-your-game` skill has the steps; `summer ops search \"publish my game\"` finds each operation.
|
|
61
|
+
"The `publish-your-game` skill has the steps; `summer ops search \"publish my game\"` finds each operation. Going live needs the game owner's yes. A game goes live as soon as Summer's automated checks pass; Summer's staff review it after it is live, so never tell anyone to wait for a review.",
|
|
54
62
|
SUMMER_BLOCK_END,
|
|
55
63
|
].join("\n");
|
|
56
64
|
}
|
package/gemini-extension.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_generated": "GENERATED from integrations/gemini — do not edit; npm run generate:registry",
|
|
3
3
|
"name": "summer",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.6.0",
|
|
5
5
|
"description": "The game engine built for AI. Any agent makes a multiplayer game and publishes it. The Summer Engine MCP server, context primer and game-dev skills.",
|
|
6
6
|
"contextFileName": "GEMINI.md",
|
|
7
7
|
"mcpServers": {
|
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
"./library/skills/in-game-purchases/",
|
|
73
73
|
"./library/skills/instantiate-asset-pack/",
|
|
74
74
|
"./library/skills/investigating-bugs/",
|
|
75
|
+
"./library/skills/make-content/",
|
|
75
76
|
"./library/skills/make-game/",
|
|
76
77
|
"./library/skills/multiplayer/",
|
|
77
78
|
"./library/skills/multiplayer-movement/",
|
|
@@ -89,6 +90,7 @@
|
|
|
89
90
|
"./library/skills/procedural-animation/",
|
|
90
91
|
"./library/skills/prop-model/",
|
|
91
92
|
"./library/skills/publish-your-game/",
|
|
93
|
+
"./library/skills/release-checklist/",
|
|
92
94
|
"./library/skills/release-readiness/",
|
|
93
95
|
"./library/skills/remote-deploy/",
|
|
94
96
|
"./library/skills/retarget/",
|
|
@@ -19,7 +19,16 @@
|
|
|
19
19
|
|
|
20
20
|
**Rule of thumb:** project reads/writes go through Summer; live hierarchy/inspector changes use scene tools; process-level work remains with the host.
|
|
21
21
|
|
|
22
|
-
## Tool surface (
|
|
22
|
+
## Tool surface (125 tools)
|
|
23
|
+
|
|
24
|
+
### Local projects (2)
|
|
25
|
+
|
|
26
|
+
No shell needed (Claude Desktop, chat apps); the `summer dev` command does both in one step from a shell.
|
|
27
|
+
|
|
28
|
+
| Tool | Use |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `summer_projects_create_local` | Write a new project from a template to this computer (`3d-basic` by default: sky, sun, ground and a hero). `dir`, or `name` next to the session's project, in the session's folder, or in `~/Documents/Summer`; never inside another project. A folder that already holds `project.godot` is left as it is. Returns `projectPath`. |
|
|
31
|
+
| `summer_projects_open_local` | Open a project in Summer Engine in the background and wait until it connects; the session's engine tools then work on it. Engine missing: answers `engine_not_running`; ask the person, and only with their yes call again with `install: true` (about 660 MB, free). A slow first open answers "call again in 30 seconds". It registers `summer preview` in the session folder's `.claude/launch.json`; `preview.url` is where the game shows. |
|
|
23
32
|
|
|
24
33
|
### Project files (4)
|
|
25
34
|
|
|
@@ -253,13 +262,17 @@ Each spends the person's credits. `dryRun: true` returns `preview.estimate` (`cr
|
|
|
253
262
|
| `summer_call` `{"op":"summer_get_agent_playbook"}` | Daily operating contract (observe-first loop, content routing, invariants, verification ritual) — read at start of session. Also served natively as the `summer_agent_playbook` MCP prompt. |
|
|
254
263
|
| `plans.get` (3.x `summer_get_board`) | Read the person's game plan (look, characters, place, storyboard, picks): every item with its id, picture URL and revision, to compare screenshots against. `projects.get({ projectId })` gives the project's `planId` from the project id in the build brief; works without the engine. |
|
|
255
264
|
|
|
256
|
-
### Creator platform (
|
|
265
|
+
### Creator platform (8)
|
|
257
266
|
|
|
258
267
|
| Tool | Use |
|
|
259
268
|
|---|---|
|
|
260
269
|
| `summer_export_game` | Export the game in the file the Summer Games store takes, headless (no window), no running editor needed. `format:"bundle"` (default): the `summer.games` `.zip` the Summer apps run; `targets` picks `ios`, `android`, `macos`, `windows` (a game without a server: phones only) through a named `summer.games <targets>` preset in `export_presets.cfg`. `format:"download"` with one target: `web` (HTML5 `.zip` on Summer's WebGPU Forward+ template; refuses Compatibility projects), `macos` (ad hoc signed `.app` zip, `macos-universal`) or `windows` (`.exe` with embedded pack, zipped, `windows-x64`). Returns path, sha256, size, bundle manifest or store platform, warnings. |
|
|
261
270
|
| `summer_export_templates` | `list` or `install` export templates from Summer's CDN (`downloads.summer.games/engine-templates/<summerVersion>/manifest.json`) into the engine's user template folder (`<data>/Godot/export_templates/<FULL_CONFIG>/`), sha256-checked. Only download exports need them; store bundles need none. `summerVersion` is read on macOS, passed elsewhere. |
|
|
262
|
-
| `store.upload_file` (3.x `summer_publish_build`; through `summer_call`) | Upload that `.zip` to the creator's game through Studio's store upload: confirm-gated preview first, then declare, part upload from disk, seal, wait for the Build; `publish:true` also
|
|
271
|
+
| `store.upload_file` (3.x `summer_publish_build`; through `summer_call`) | Upload that `.zip` to the creator's game through Studio's store upload: confirm-gated preview first, then declare, part upload from disk, seal, wait for the Build and for Summer's automated checks on each platform (`checks`: passed, or failed with `code`, `owner` and `fix`); `publish:true` also makes the Build available to players. Needs `summer login --store`; a retry with the same file and version continues the same upload. Without a shell, `store.upload_begin`, `store.upload_sign_parts` and `store.upload_complete` take the bytes in parts. |
|
|
272
|
+
| `store.go_live` (`summer_go_live`; through `summer_call`) | Take the store game live: release the uploaded bundle Build to the platforms that passed their checks and publish the store page, through the same store routes Studio uses, then watch until players get it. Confirm-gated: the plan first (or `live`, `blocked` with each failure's fix, `needs_changes` with Summer's feedback). It needs no owner approval link while the person's Agent permissions allow Publish (the default; otherwise `needs_permission`). `waiting` names what it waits on (`automated_checks`, `activation`, or `summer`: a hold on Summer's side that needs nothing from the creator). Call again to keep watching; nothing is asked twice. Run the `release-checklist` skill first. |
|
|
273
|
+
| `store.set_details` (`summer_store_set_details`; through `summer_call`) | The store page sections `store.update_listing` does not edit, on the draft: `genres`, `features`, `credits`, `languageSupport`, `systemRequirements`, `contentSurvey` (the age questionnaire) and `aiDisclosure`. A section given replaces it, `null` clears it. Never publishes. |
|
|
274
|
+
| `economy.sync_items` (`summer_sync_items`; through `summer_call`) | Bring the store game's items in line with `summer.items.json` in the project root, matched by slug, so staging and production carry the same items. Confirm-gated: the plan first (create, update with the changed fields, unchanged, blocked), then the changed images are uploaded and the plan goes to the owner, who approves it in Studio; the items are then created or revised, priced in Sparks and submitted for review. One approval takes at most 50 changed items (a larger plan lists `batches`; confirm each with `slugs`); a new item needs at least one image. Never activates or releases an item. Needs `summer login --store`. Staging or production is the gateway setting: switching takes `SUMMER_GATEWAY_URL` and `SUMMER_MCP_RESOURCE_URL`, then `summer login --store --force` (one store sign-in per machine). |
|
|
275
|
+
| `content.make` (`summer_content_make`; through `summer_call`) | Make one finished piece of content for a game on this computer and put it in the team's Review: picks the reference that fits today (a format the game has not posted, what changed in the repo), studies its cuts and caption look, maps each reference shot to a footage window with motion, cuts to its timings, draws its captions, puts a song on the beat, mixes at -14 LUFS, checks hook, motion per shot, safe zones, length and loudness, renders the reference side by side and saves the piece (failed checks: a draft). It never writes words: `needs_words` lists the beats, call again with `workDir` and `lines`; a reference that speaks also needs `voiceFile` in the team's voice (`needs_voice`). Same `workDir` continues where it stopped. Needs ffmpeg and ImageMagick (Whisper for a voice-over). |
|
|
263
276
|
| `summer_capture_gameplay` | Real gameplay frames without a running editor: starts the game (main scene or `scene`) in the engine's offscreen verify instance (`--summer-verify`, real renderer, window parked offscreen, no focus) (`--summer-offscreen`) at `resolution` (default `1920x1080`), waits `waitSeconds`, runs `steps` (`press` a button by text, `key`, `action`, `click`, `drag`, `wait`, `shot`), saves `frames` PNGs (HUD included; with a `shot` step, one per shot) to `<project>/.summer/captures/<time>/`. `args` are the game's own flags, passed after `--` (e.g. `["--autostart"]` to skip the title screen). Imports the project first when `.godot/` is missing. Returns each path with its real width and height; warns when the stretch settings render another size. For store screenshots. |
|
|
264
277
|
|
|
265
278
|
### Hosted store, publishing and Grow tools
|
|
@@ -267,9 +280,9 @@ Each spends the person's credits. `dryRun: true` returns `preview.estimate` (`cr
|
|
|
267
280
|
These come from the hosted Summer server (the one ChatGPT and Claude connect to) and are mounted into the local MCP after `summer login`; `summer tool <name>` reaches them too.
|
|
268
281
|
|
|
269
282
|
- **Store page:** `summer_store_list_games`, `summer_store_create_game`, `summer_store_get_listing` (the page, the checklist and `availableTags`), `summer_store_update_listing` (text, tags, long description, support contact, release date, content rating), `summer_store_set_art`, `summer_store_set_trailer`, `summer_store_set_sound`, `summer_store_preview_link`, `summer_store_jams` and `summer_store_enter_jam`.
|
|
270
|
-
- **Builds and release:** `summer_build_begin_upload` / `_sign_parts` / `_complete_upload` / `_status` (the local `summer_publish_build` does these from disk), `summer_store_version_*` for web and native downloads, `summer_store_submit` (one approval link for the owner), `summer_store_cancel_submission`, `summer_store_review`, `summer_store_releases`, `summer_build_release`.
|
|
283
|
+
- **Builds and release:** `summer_build_begin_upload` / `_sign_parts` / `_complete_upload` / `_status` (the local `summer_publish_build` does these from disk), `summer_store_version_*` for web and native downloads, `summer_store_submit` (one approval link for the owner, for a person whose Agent permissions have Publish off; otherwise the local `summer_go_live` takes the game live), `summer_store_cancel_submission`, `summer_store_review`, `summer_store_releases`, `summer_build_release`. The engine profile leaves out `summer_store_releases` and `summer_store_version_status`: `summer_go_live` reports what players get and each download's state.
|
|
271
284
|
- **Grow:** `summer_grow_overview` and `summer_grow_analytics` (store page views, launches, play sessions, play time, custom events), `summer_grow_event_catalog`, `summer_grow_event_trend`, `summer_grow_event_funnel`, `summer_grow_members`, `summer_grow_ab_tests`, `summer_grow_prices`, `summer_grow_event_limit`, `summer_grow_apply` and `summer_grow_applications`.
|
|
272
|
-
- **Guides:** the `publish-your-game`, `store-listing`, `store-art` and `grow-analytics` skills (`summer_docs_read`).
|
|
285
|
+
- **Guides:** the `publish-your-game`, `release-checklist`, `store-listing`, `store-art` and `grow-analytics` skills (`summer_docs_read`).
|
|
273
286
|
|
|
274
287
|
### Library search (2)
|
|
275
288
|
|