summer-engine 3.2.1 → 3.3.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.
Files changed (61) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/.codex-plugin/plugin.json +3 -3
  4. package/.cursor-plugin/plugin.json +2 -2
  5. package/.factory-plugin/plugin.json +2 -2
  6. package/CHANGELOG.md +25 -0
  7. package/README.md +5 -3
  8. package/dist/cli/commands/login.js +12 -0
  9. package/dist/cli/commands/publish.js +2 -1
  10. package/dist/core/auth.js +3 -0
  11. package/dist/core/capabilities/audit/args.d.ts +14 -14
  12. package/dist/core/capabilities/engine-run.d.ts +8 -0
  13. package/dist/core/capabilities/engine-run.js +33 -0
  14. package/dist/core/capabilities/export-game.d.ts +99 -0
  15. package/dist/core/capabilities/export-game.js +369 -0
  16. package/dist/core/capabilities/export-presets.d.ts +50 -0
  17. package/dist/core/capabilities/export-presets.js +177 -0
  18. package/dist/core/capabilities/export-templates.d.ts +118 -0
  19. package/dist/core/capabilities/export-templates.js +291 -0
  20. package/dist/core/capabilities/publish-build.d.ts +64 -0
  21. package/dist/core/capabilities/publish-build.js +448 -0
  22. package/dist/core/capabilities/publish-download.d.ts +37 -0
  23. package/dist/core/capabilities/publish-download.js +168 -0
  24. package/dist/core/capabilities/runtime-control.d.ts +10 -10
  25. package/dist/core/capabilities/summer-bundle.d.ts +49 -0
  26. package/dist/core/capabilities/summer-bundle.js +156 -0
  27. package/dist/core/capabilities/tool-dispatch.js +33 -1
  28. package/dist/core/capabilities/ui-control.d.ts +2 -2
  29. package/dist/core/library-read.d.ts +2 -2
  30. package/dist/core/oauth.d.ts +78 -0
  31. package/dist/core/oauth.js +309 -0
  32. package/dist/core/util/zip-write.d.ts +18 -0
  33. package/dist/core/util/zip-write.js +112 -0
  34. package/dist/core/util/zip.d.ts +22 -0
  35. package/dist/core/util/zip.js +140 -0
  36. package/dist/mcp/hosted-mount.d.ts +96 -0
  37. package/dist/mcp/hosted-mount.js +206 -0
  38. package/dist/mcp/server.d.ts +2 -1
  39. package/dist/mcp/server.js +13 -6
  40. package/dist/mcp/tools/creator-tools.js +54 -1
  41. package/gemini-extension.json +1 -1
  42. package/library/references/mcp-tools-reference/mcp-tools-reference.md +6 -3
  43. package/library/skills/export-and-ship/SKILL.md +2 -0
  44. package/library/skills/multiplayer-publish/SKILL.md +5 -1
  45. package/library/skills/navigate-summer/SKILL.md +1 -1
  46. package/library/tools/creator-publish/resource.yaml +4 -3
  47. package/library/tools/creator-releases/resource.yaml +1 -1
  48. package/library/tools/export-game/resource.yaml +72 -0
  49. package/library/tools/export-templates/resource.yaml +63 -0
  50. package/library/tools/open/resource.yaml +1 -1
  51. package/library/tools/publish-build/resource.yaml +78 -0
  52. package/package.json +1 -1
  53. package/registry/generated/aliases.json +3 -0
  54. package/registry/generated/counts.json +2 -2
  55. package/registry/generated/gemini-extension.json +1 -1
  56. package/registry/generated/index.json +119 -10
  57. package/registry/generated/marketplace.claude.json +2 -2
  58. package/registry/generated/plugin.claude.json +2 -2
  59. package/registry/generated/plugin.codex.json +3 -3
  60. package/registry/generated/plugin.cursor.json +2 -2
  61. package/registry/generated/plugin.factory.json +2 -2
@@ -0,0 +1,96 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { type Tool } from "@modelcontextprotocol/sdk/types.js";
3
+ /**
4
+ * One Summer Engine MCP. The cloud tools a creator's agent needs to publish
5
+ * (projects, store page, build upload, submit for the owner's approval, the
6
+ * board, Grow) live on the hosted Summer Engine MCP at mcp.summerengine.com,
7
+ * the same server ChatGPT and Claude connect to. This module mounts them into
8
+ * the local MCP so a local agent sees one server: each hosted tool, prompt
9
+ * and resource is registered here under its own name and forwarded with the
10
+ * person's store sign-in ("summer login --store"; the token's audience is
11
+ * that server). A name the local MCP already has stays local (the engine and
12
+ * generation tools keep their CLI-token path).
13
+ *
14
+ * Mounting is lazy: the server answers with the engine tools at once, and the
15
+ * hosted tools appear when the mount finishes (tools/list_changed). Without a
16
+ * store sign-in one tool, summer_store_tools, says how to sign in and mounts
17
+ * on the next call.
18
+ */
19
+ export declare const STORE_TOOLS_STATUS = "summer_store_tools";
20
+ /** The part of an MCP client the mount uses (a fake in tests). */
21
+ export interface HostedClient {
22
+ listTools(params?: {
23
+ cursor?: string;
24
+ }): Promise<{
25
+ tools: Tool[];
26
+ nextCursor?: string;
27
+ }>;
28
+ callTool(params: {
29
+ name: string;
30
+ arguments?: Record<string, unknown>;
31
+ }): Promise<Record<string, unknown>>;
32
+ listPrompts(params?: {
33
+ cursor?: string;
34
+ }): Promise<{
35
+ prompts: Array<{
36
+ name: string;
37
+ title?: string;
38
+ description?: string;
39
+ arguments?: Array<{
40
+ name: string;
41
+ description?: string;
42
+ required?: boolean;
43
+ }>;
44
+ }>;
45
+ nextCursor?: string;
46
+ }>;
47
+ getPrompt(params: {
48
+ name: string;
49
+ arguments?: Record<string, string>;
50
+ }): Promise<Record<string, unknown>>;
51
+ listResources(params?: {
52
+ cursor?: string;
53
+ }): Promise<{
54
+ resources: Array<{
55
+ uri: string;
56
+ name: string;
57
+ title?: string;
58
+ description?: string;
59
+ mimeType?: string;
60
+ }>;
61
+ nextCursor?: string;
62
+ }>;
63
+ readResource(params: {
64
+ uri: string;
65
+ }): Promise<Record<string, unknown>>;
66
+ }
67
+ export interface HostedMountDependencies {
68
+ /** The hosted Summer Engine MCP URL (the store sign-in's resource). */
69
+ url: () => string;
70
+ token: () => Promise<string>;
71
+ connect: (url: string, token: () => Promise<string>) => Promise<HostedClient>;
72
+ }
73
+ export declare const defaultHostedMountDependencies: HostedMountDependencies;
74
+ export interface HostedMountState {
75
+ status: "idle" | "mounting" | "mounted" | "unavailable";
76
+ tools: string[];
77
+ prompts: string[];
78
+ resources: string[];
79
+ /** Hosted names the local MCP already defines (kept local). */
80
+ collisions: string[];
81
+ error?: {
82
+ code: string;
83
+ message: string;
84
+ recovery: string;
85
+ };
86
+ }
87
+ /**
88
+ * Get the server ready for hosted tools before the transport connects (the
89
+ * SDK fixes capabilities at connect): resource handlers exist, and tools/list
90
+ * reports each mounted tool's own JSON Schema rather than the passthrough one.
91
+ * Returns mount(), which callers run after connect without awaiting it.
92
+ */
93
+ export declare function prepareHostedMount(server: McpServer, deps?: HostedMountDependencies): {
94
+ mount: () => Promise<HostedMountState>;
95
+ state: HostedMountState;
96
+ };
@@ -0,0 +1,206 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
3
+ import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
4
+ import { z } from "zod";
5
+ import { getStoreAccessToken, OAuthError, resolveMcpResourceUrl } from "../core/oauth.js";
6
+ import { appendMcpLogEvent } from "../core/mcp-log.js";
7
+ import { TOOLKIT_VERSION } from "../core/version.js";
8
+ /**
9
+ * One Summer Engine MCP. The cloud tools a creator's agent needs to publish
10
+ * (projects, store page, build upload, submit for the owner's approval, the
11
+ * board, Grow) live on the hosted Summer Engine MCP at mcp.summerengine.com,
12
+ * the same server ChatGPT and Claude connect to. This module mounts them into
13
+ * the local MCP so a local agent sees one server: each hosted tool, prompt
14
+ * and resource is registered here under its own name and forwarded with the
15
+ * person's store sign-in ("summer login --store"; the token's audience is
16
+ * that server). A name the local MCP already has stays local (the engine and
17
+ * generation tools keep their CLI-token path).
18
+ *
19
+ * Mounting is lazy: the server answers with the engine tools at once, and the
20
+ * hosted tools appear when the mount finishes (tools/list_changed). Without a
21
+ * store sign-in one tool, summer_store_tools, says how to sign in and mounts
22
+ * on the next call.
23
+ */
24
+ export const STORE_TOOLS_STATUS = "summer_store_tools";
25
+ const CALL_TIMEOUT_MS = 120_000;
26
+ const passthroughArgs = z.object({}).passthrough();
27
+ export const defaultHostedMountDependencies = {
28
+ url: () => resolveMcpResourceUrl(),
29
+ token: () => getStoreAccessToken(),
30
+ connect: async (url, token) => {
31
+ const transport = new StreamableHTTPClientTransport(new URL(url), {
32
+ // A fresh (refreshed when near expiry) token on every request.
33
+ fetch: async (input, init) => {
34
+ const headers = new Headers(init?.headers);
35
+ headers.set("authorization", `Bearer ${await token()}`);
36
+ return fetch(input, { ...init, headers });
37
+ },
38
+ });
39
+ const client = new Client({ name: "summer-engine-local", version: TOOLKIT_VERSION });
40
+ await client.connect(transport);
41
+ const timeout = { timeout: CALL_TIMEOUT_MS };
42
+ return {
43
+ listTools: (params) => client.listTools(params),
44
+ callTool: (params) => client.callTool(params, undefined, timeout),
45
+ listPrompts: (params) => client.listPrompts(params),
46
+ getPrompt: (params) => client.getPrompt(params),
47
+ listResources: (params) => client.listResources(params),
48
+ readResource: (params) => client.readResource(params),
49
+ };
50
+ },
51
+ };
52
+ async function listAll(page, key) {
53
+ const out = [];
54
+ let cursor;
55
+ for (let pages = 0; pages < 20; pages++) {
56
+ const result = await page(cursor);
57
+ out.push(...(result[key] ?? []));
58
+ if (!result.nextCursor)
59
+ break;
60
+ cursor = result.nextCursor;
61
+ }
62
+ return out;
63
+ }
64
+ function errorText(error) {
65
+ return { content: [{ type: "text", text: JSON.stringify({ ok: false, ...error }) }], isError: true };
66
+ }
67
+ /**
68
+ * Get the server ready for hosted tools before the transport connects (the
69
+ * SDK fixes capabilities at connect): resource handlers exist, and tools/list
70
+ * reports each mounted tool's own JSON Schema rather than the passthrough one.
71
+ * Returns mount(), which callers run after connect without awaiting it.
72
+ */
73
+ export function prepareHostedMount(server, deps = defaultHostedMountDependencies) {
74
+ const state = { status: "idle", tools: [], prompts: [], resources: [], collisions: [] };
75
+ const schemas = new Map();
76
+ const registries = server;
77
+ // Declare the resources capability now: registering one and removing it
78
+ // keeps the handlers (and capability) without listing anything yet.
79
+ server.registerResource("summer-hosted-placeholder", "summer://hosted/placeholder", {}, async () => ({ contents: [] })).remove();
80
+ // tools/list as the SDK builds it, with mounted tools carrying their hosted schema.
81
+ const handlers = server.server._requestHandlers;
82
+ const listTools = handlers.get("tools/list");
83
+ if (listTools) {
84
+ server.server.setRequestHandler(ListToolsRequestSchema, async (request, extra) => {
85
+ const result = await listTools(request, extra);
86
+ return {
87
+ ...result,
88
+ tools: result.tools.map((tool) => {
89
+ const hosted = schemas.get(tool.name);
90
+ return hosted ? { ...tool, inputSchema: hosted.inputSchema, ...(hosted.annotations ? { annotations: hosted.annotations } : {}), ...(hosted.title ? { title: hosted.title } : {}) } : tool;
91
+ }),
92
+ };
93
+ });
94
+ }
95
+ let statusTool = null;
96
+ let running = null;
97
+ const showStatusTool = () => {
98
+ if (statusTool || STORE_TOOLS_STATUS in registries._registeredTools)
99
+ return;
100
+ statusTool = server.registerTool(STORE_TOOLS_STATUS, {
101
+ description: "The Summer store, publishing, board and Grow tools come from the hosted Summer Engine MCP and are not loaded. Call this to see why and how to fix it (usually: run \"summer login --store\" in a terminal); it loads them when the sign-in works.",
102
+ inputSchema: passthroughArgs,
103
+ }, async () => {
104
+ const after = await mount();
105
+ if (after.status === "mounted") {
106
+ return { content: [{ type: "text", text: JSON.stringify({ ok: true, loaded: after.tools.length, tools: after.tools }) }] };
107
+ }
108
+ return errorText(after.error ?? { code: "store_tools_unavailable", message: "The hosted tools are not loaded.", recovery: 'Recovery: run "summer login --store", then call this again.' });
109
+ });
110
+ };
111
+ async function attempt() {
112
+ let client;
113
+ try {
114
+ const url = deps.url();
115
+ await deps.token();
116
+ client = await deps.connect(url, deps.token);
117
+ }
118
+ catch (error) {
119
+ state.status = "unavailable";
120
+ state.error =
121
+ error instanceof OAuthError
122
+ ? { code: error.code, message: error.message.replace(` ${error.recovery}`, ""), recovery: error.recovery }
123
+ : {
124
+ code: "store_tools_unreachable",
125
+ message: `The hosted Summer Engine MCP did not answer: ${error instanceof Error ? error.message : String(error)}.`,
126
+ recovery: 'Recovery: check the network; if it says unauthorized, run "summer login --store --force". Then call summer_store_tools.',
127
+ };
128
+ appendMcpLogEvent("mcp:hosted_mount_failed", { code: state.error.code });
129
+ showStatusTool();
130
+ return state;
131
+ }
132
+ const [tools, prompts, resources] = await Promise.all([
133
+ listAll((cursor) => client.listTools(cursor ? { cursor } : undefined), "tools"),
134
+ listAll((cursor) => client.listPrompts(cursor ? { cursor } : undefined), "prompts").catch(() => []),
135
+ listAll((cursor) => client.listResources(cursor ? { cursor } : undefined), "resources").catch(() => []),
136
+ ]);
137
+ for (const tool of tools) {
138
+ if (tool.name in registries._registeredTools && !schemas.has(tool.name)) {
139
+ if (tool.name !== STORE_TOOLS_STATUS)
140
+ state.collisions.push(tool.name);
141
+ continue;
142
+ }
143
+ if (schemas.has(tool.name))
144
+ continue;
145
+ schemas.set(tool.name, tool);
146
+ server.registerTool(tool.name, { description: tool.description ?? "", inputSchema: passthroughArgs, ...(tool.annotations ? { annotations: tool.annotations } : {}) }, async (args) => {
147
+ try {
148
+ return (await client.callTool({ name: tool.name, arguments: args }));
149
+ }
150
+ catch (error) {
151
+ return errorText({
152
+ code: "store_tool_failed",
153
+ message: `The hosted Summer Engine MCP did not complete ${tool.name}: ${error instanceof Error ? error.message : String(error)}.`,
154
+ recovery: 'Recovery: retry; if it says unauthorized, run "summer login --store --force".',
155
+ });
156
+ }
157
+ });
158
+ state.tools.push(tool.name);
159
+ }
160
+ for (const prompt of prompts) {
161
+ if (prompt.name in registries._registeredPrompts) {
162
+ if (!state.prompts.includes(prompt.name))
163
+ state.collisions.push(`prompt:${prompt.name}`);
164
+ continue;
165
+ }
166
+ const shape = Object.fromEntries((prompt.arguments ?? []).map((arg) => [arg.name, arg.required ? z.string().describe(arg.description ?? "") : z.string().optional().describe(arg.description ?? "")]));
167
+ server.registerPrompt(prompt.name, { ...(prompt.title ? { title: prompt.title } : {}), ...(prompt.description ? { description: prompt.description } : {}), ...(prompt.arguments?.length ? { argsSchema: shape } : {}) }, (async (args = {}) => client.getPrompt({ name: prompt.name, arguments: Object.fromEntries(Object.entries(args).filter((entry) => typeof entry[1] === "string")) })));
168
+ state.prompts.push(prompt.name);
169
+ }
170
+ for (const resource of resources) {
171
+ if (resource.uri in registries._registeredResources) {
172
+ if (!state.resources.includes(resource.uri))
173
+ state.collisions.push(`resource:${resource.uri}`);
174
+ continue;
175
+ }
176
+ server.registerResource(resource.name, resource.uri, { ...(resource.title ? { title: resource.title } : {}), ...(resource.description ? { description: resource.description } : {}), ...(resource.mimeType ? { mimeType: resource.mimeType } : {}) }, async (uri) => (await client.readResource({ uri: uri.href })));
177
+ state.resources.push(resource.uri);
178
+ }
179
+ statusTool?.remove();
180
+ statusTool = null;
181
+ state.status = "mounted";
182
+ delete state.error;
183
+ appendMcpLogEvent("mcp:hosted_mounted", { tools: state.tools.length, prompts: state.prompts.length, resources: state.resources.length, collisions: state.collisions.length });
184
+ return state;
185
+ }
186
+ /** Mount once; a failed mount runs again on the next call. */
187
+ function mount() {
188
+ if (state.status === "mounted")
189
+ return Promise.resolve(state);
190
+ if (!running) {
191
+ state.status = "mounting";
192
+ running = attempt()
193
+ .catch((error) => {
194
+ state.status = "unavailable";
195
+ state.error = { code: "store_tools_unreachable", message: error instanceof Error ? error.message : String(error), recovery: "Recovery: call summer_store_tools to retry." };
196
+ showStatusTool();
197
+ return state;
198
+ })
199
+ .finally(() => {
200
+ running = null;
201
+ });
202
+ }
203
+ return running;
204
+ }
205
+ return { mount, state };
206
+ }
@@ -10,7 +10,8 @@ export declare const getCachedBootDriftNotice: typeof getCachedNotice;
10
10
  * (docs/design/archive/E2E-2026-09-03.md, F-17) showed an agent needs on the first
11
11
  * turn and would otherwise learn the hard way. Kept under 600 characters so
12
12
  * it costs a few hundred tokens once; the full operating guide stays in the
13
- * summer_agent_playbook prompt / summer_get_agent_playbook tool.
13
+ * summer_agent_playbook prompt / summer_get_agent_playbook tool. The last
14
+ * sentence names the publishing path and the owner-approval rule.
14
15
  */
15
16
  export declare const SUMMER_MCP_INSTRUCTIONS: string;
16
17
  export declare function configureMcpEngineSelection(selection?: EngineSelection): void;
@@ -26,6 +26,7 @@ import { registerNavigationTools } from "./tools/navigation-tools.js";
26
26
  import { registerLibraryTools } from "./tools/library-tools.js";
27
27
  import { getCachedBootDriftNotice as getCachedNotice, setCachedBootDriftNotice, } from "./boot-notice.js";
28
28
  import { appendMcpLogEvent } from "../core/mcp-log.js";
29
+ import { prepareHostedMount } from "./hosted-mount.js";
29
30
  import { buildBootDriftNotice, fetchLatestRegistryVersion, } from "../installer/version-check.js";
30
31
  import { TOOLKIT_VERSION as version } from "../core/version.js";
31
32
  let processDiagnosticsInstalled = false;
@@ -37,13 +38,15 @@ export const getCachedBootDriftNotice = getCachedNotice;
37
38
  * (docs/design/archive/E2E-2026-09-03.md, F-17) showed an agent needs on the first
38
39
  * turn and would otherwise learn the hard way. Kept under 600 characters so
39
40
  * it costs a few hundred tokens once; the full operating guide stays in the
40
- * summer_agent_playbook prompt / summer_get_agent_playbook tool.
41
+ * summer_agent_playbook prompt / summer_get_agent_playbook tool. The last
42
+ * sentence names the publishing path and the owner-approval rule.
41
43
  */
42
- export const SUMMER_MCP_INSTRUCTIONS = "Summer Engine MCP. Call summer_get_project_context first: it binds this session to the open project and returns mainScene, projectMemory (GameSoul, template pin) and any capabilitySkewWarning, compactly. " +
43
- "Before building or fixing, summer_search_library then summer_read_library the hit: skills, examples, templates live there. " +
44
- "After a playthrough read summer_get_diagnostics, not summer_get_console alone (runtime errors live in the debugger). " +
45
- "An all-black screenshot means the viewport had not redrawn — recapture before concluding. " +
46
- "Report entries you verified in-engine via summer_library_feedback.";
44
+ export const SUMMER_MCP_INSTRUCTIONS = "Summer Engine MCP. Call summer_get_project_context first: it binds the open project and returns mainScene, projectMemory and any capabilitySkewWarning. " +
45
+ "Before building or fixing, summer_search_library then summer_read_library the hit. " +
46
+ "After a playthrough read summer_get_diagnostics, not summer_get_console alone. " +
47
+ "An all-black screenshot means the viewport had not redrawn: recapture. " +
48
+ "Report verified entries via summer_library_feedback. " +
49
+ "Publishing: summer_export_game, summer_publish_build, summer_store_*; an agent never publishes alone: summer_store_submit returns the owner's approval link.";
47
50
  /**
48
51
  * Fire-and-forget probe of the npm registry on MCP boot. Caches the result for
49
52
  * the session. We never await this in startup so a slow / offline registry
@@ -352,8 +355,12 @@ export async function startMcpServer(options = {}) {
352
355
  setCachedBootDriftNotice(null);
353
356
  appendMcpLogEvent("mcp:boot_drift_probe_failed", errorDetails(error));
354
357
  });
358
+ // Hosted Summer Engine MCP tools (store, publishing, board, Grow): ready
359
+ // before connect, mounted after it without blocking the engine tools.
360
+ const hosted = prepareHostedMount(server);
355
361
  const transport = new StdioServerTransport();
356
362
  await server.connect(transport);
363
+ void hosted.mount();
357
364
  appendMcpLogEvent("mcp:ready", {
358
365
  version,
359
366
  pid: process.pid,
@@ -1,5 +1,10 @@
1
1
  import { z } from "zod";
2
2
  import { CreatorOperationError, listCreatorReleases, publishCreator, } from "../../core/capabilities/creator.js";
3
+ import { exportGame } from "../../core/capabilities/export-game.js";
4
+ import { STORE_TARGETS } from "../../core/capabilities/export-presets.js";
5
+ import { exportTemplates, TEMPLATE_PLATFORMS } from "../../core/capabilities/export-templates.js";
6
+ import { publishBuild } from "../../core/capabilities/publish-build.js";
7
+ import { BuildToolError } from "../../core/capabilities/summer-bundle.js";
3
8
  import { CONFIG_KEYS, getConfigValue, isConfigKey, readSummerConfig, setConfigValue, unsetConfigValue, } from "../../core/config.js";
4
9
  import { textJson } from "./text-json.js";
5
10
  async function creatorResult(operation) {
@@ -16,6 +21,16 @@ async function creatorResult(operation) {
16
21
  recovery: error.recovery,
17
22
  }, true);
18
23
  }
24
+ if (error instanceof BuildToolError) {
25
+ return textJson({
26
+ ok: false,
27
+ code: error.code,
28
+ message: error.message.replace(` ${error.recovery}`, ""),
29
+ recovery: error.recovery,
30
+ ...(error.status ? { status: error.status } : {}),
31
+ ...error.detail,
32
+ }, true);
33
+ }
19
34
  return textJson({
20
35
  ok: false,
21
36
  code: "creator_request_invalid",
@@ -24,7 +39,45 @@ async function creatorResult(operation) {
24
39
  }
25
40
  }
26
41
  export function registerCreatorTools(server) {
27
- server.tool("summer_creator_publish", "Publish an exact exported Summer .pck through the versioned Summer Platform 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.", {
42
+ server.tool("summer_export_game", "Export the game in the exact file the Summer Games store takes, with the installed Summer Engine headless (no window). format \"bundle\" (default): the summer.games .zip the Summer apps run on iPhone, Android, macOS and Windows; targets picks the platforms (a game without a server: ios and android only); upload it with summer_publish_build. format \"download\" with one target: web (an HTML5 .zip on Summer's WebGPU Forward+ web template, played on summer.games), macos (.app zip, macos-universal) or windows (.exe zip, windows-x64); these need the export template from summer_export_templates and are uploaded as store versions. Works without the editor running. Returns path, sha256, size, the manifest or store platform, and warnings.", {
43
+ project: z.string().optional().describe("Project folder with project.godot. Defaults to the MCP's bound project, then the working directory."),
44
+ out: z.string().optional().describe("Output .zip path. Defaults to <project>/.summer/exports/."),
45
+ targets: z
46
+ .array(z.enum(STORE_TARGETS))
47
+ .optional()
48
+ .describe("Store platforms. Bundle: any of macos, windows, ios, android (omit to use every platform the project declares). Download: exactly one of web, macos, windows."),
49
+ format: z
50
+ .enum(["bundle", "download"])
51
+ .optional()
52
+ .describe("bundle (default): the summer.games store build. download: a web build or native download; the default when targets is [\"web\"]."),
53
+ debug: z.boolean().default(false).describe("Export a debug build (--export-debug)."),
54
+ timeoutSeconds: z.number().int().min(10).max(7200).optional().describe("Stop the engine after this many seconds (default 900; a first export imports every asset)."),
55
+ }, async ({ project, out, targets, format, debug, timeoutSeconds }) => creatorResult(() => exportGame({ project, out, targets, format, debug, ...(timeoutSeconds ? { timeoutMs: timeoutSeconds * 1000 } : {}) })));
56
+ server.tool("summer_export_templates", "List, download and install Summer Engine export templates from Summer's CDN into the folder the installed engine reads, checking each file's sha256. Needed only for summer_export_game format \"download\": web (Summer's WebGPU Forward+ template), macos, windows. Store bundles (format \"bundle\") need no template. action list shows what is installed and published; action install fetches the platforms asked (default web).", {
57
+ action: z.enum(["list", "install"]).describe("list: installed and published templates. install: download and install."),
58
+ platforms: z
59
+ .array(z.enum(TEMPLATE_PLATFORMS))
60
+ .optional()
61
+ .describe("Template platforms to install (default [\"web\"])."),
62
+ includeDebug: z.boolean().default(false).describe("Also install the debug templates (for exports with debug true)."),
63
+ summerVersion: z
64
+ .string()
65
+ .optional()
66
+ .describe("The installed Summer Engine version (Help > About, e.g. 0.7.1). Read automatically on macOS; needed on Windows and Linux."),
67
+ }, async ({ action, platforms, includeDebug, summerVersion }) => creatorResult(() => exportTemplates({ action, platforms, includeDebug, summerVersion })));
68
+ 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 with publish=true approve it. 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 and show the user the returned target; set confirm only after they approve. Needs \"summer login --store\". A retry with the same file and clientVersion continues the same upload. Nothing here makes a game live: the owner approves publishing (summer_store_submit).", {
69
+ gameId: z.string().optional().describe("The store game id (Studio store page URL). Omit to get the list of your games."),
70
+ file: z.string().optional().describe("Exported .zip. Defaults to the last summer_export_game result."),
71
+ clientVersion: z.string().describe("Build version, vMAJOR.MINOR.PATCH (e.g. v1.0.0); a new upload needs a new version."),
72
+ publish: z.boolean().default(false).describe("Bundles only: also approve the Build for players. A game that has not passed review keeps it as a preview; an agent needs the owner's approval."),
73
+ platform: z
74
+ .enum(["web", "macos-universal", "windows-x64", "linux-x64"])
75
+ .optional()
76
+ .describe("Web builds and native downloads: the store platform. Defaults to the last export's platform; omit for a store bundle."),
77
+ confirm: z.boolean().default(false).describe("Set true only after the user approves the exact upload."),
78
+ 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."),
79
+ }, async (args) => creatorResult(() => publishBuild({ ...args, face: "mcp" })));
80
+ server.tool("summer_creator_publish", "Deprecated: use summer_export_game, then summer_publish_build, which upload the summer.games .zip to the game's store listing. This old path publishes a .pck to the legacy Summercraft creator API with a separate sc_ token and will be removed. 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.", {
28
81
  project: z.string().optional().describe("Project root. Defaults to the current working directory."),
29
82
  artifact: z.string().describe("Exact path to the exported Summer .pck artifact."),
30
83
  version: z.string().describe("Immutable release version."),
@@ -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": "3.2.1",
4
+ "version": "3.3.0",
5
5
  "description": "Agent tooling for Summer Engine: MCP bridge, context primer, and game-dev skills. Use with the npm CLI for skill files on disk.",
6
6
  "contextFileName": "GEMINI.md",
7
7
  "mcpServers": {
@@ -17,7 +17,7 @@
17
17
 
18
18
  **Rule of thumb:** project reads/writes go through Summer; live hierarchy/inspector changes use scene tools; process-level work remains with the host.
19
19
 
20
- ## Tool surface (101 tools)
20
+ ## Tool surface (104 tools)
21
21
 
22
22
  ### Project files (4)
23
23
 
@@ -237,11 +237,14 @@ Preview. One fast, read-only call that walks every node of a 3D scene and lists
237
237
  | `summer_get_project_context` | Project, scene, and `.summer` memory summary — call at start of session. Binds the session to the open project and surfaces a `capabilitySkewWarning` when the engine build and CLI have drifted; tools whose op the engine provably lacks return a structured `engine_lacks_op` result instead of running. Compact by default (a few KB: `sceneSummary`, scalar `health` with capability counts); `include:['scene_tree','capabilities','settings']` adds the heavy blocks, `omitted` says how. |
238
238
  | `summer_get_agent_playbook` | Daily operating contract (observe-first loop, content routing, invariants, verification ritual) — call at start of session. Also served natively as the `summer_agent_playbook` MCP prompt. |
239
239
 
240
- ### Creator platform (3)
240
+ ### Creator platform (5)
241
241
 
242
242
  | Tool | Use |
243
243
  |---|---|
244
- | `summer_creator_publish` | Compute the exact `.pck` digest and size, require user confirmation, then run versioned prepare → write-once upload → finalize. The server independently verifies `publish` scope, ownership, bytes, and review state. |
244
+ | `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. |
245
+ | `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. |
246
+ | `summer_publish_build` | 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, name its client pack; `publish:true` also approves it. Needs `summer login --store`; a retry with the same file and version continues the same upload. |
247
+ | `summer_creator_publish` | **Deprecated** (use the two tools above). Compute the exact `.pck` digest and size, require user confirmation, then run versioned prepare → write-once upload → finalize. The server independently verifies `publish` scope, ownership, bytes, and review state. |
245
248
  | `summer_creator_releases` | List real creator-owned releases from `summer.creator.v1`, with opaque cursor pagination. |
246
249
  | `summer_creator_config` | Read or confirm updates to the shared non-secret `~/.summer/config.json`. It never accepts or returns tokens. |
247
250
 
@@ -291,6 +291,8 @@ For each build:
291
291
 
292
292
  Per platform, point the user at the correct upload path:
293
293
 
294
+ - **summer.games:** `summer_export_game` writes the summer.games `.zip`; `summer_publish_build` uploads it to the game's store listing after the user confirms the exact game, file and version (needs `summer login --store`). If it answers `store_auth_refused`, the user uploads the `.zip` in Studio instead.
295
+ - Per platform, the store takes: iPhone, Android, macOS and Windows in the Summer apps = one summer.games bundle (`summer_export_game` `targets`, format `bundle`; no template, no signing; a game without a server targets `ios`/`android` only). Web = an HTML5 `.zip` on Summer's WebGPU Forward+ template (`targets:["web"]`; the project must render with Forward+ or Mobile). A macOS or Windows download for the store page = `targets:["macos"]` or `["windows"]` with format `download`. Download exports need the template: `summer_export_templates` `install` fetches it from Summer's CDN.
294
296
  - **Steam:** `steamcmd` + Steam Pipe (`builder_app_xxx.vdf`). Set up depots first in Steamworks.
295
297
  - **itch.io:** `butler push <path> <user>/<game>:<channel>` (e.g. `butler push build/win <user>/mygame:windows`).
296
298
  - **Web (itch hosted):** zip + drop into itch upload UI as "playable in browser".
@@ -197,7 +197,11 @@ writes `rejection.json`; its codes are in [export-errors.md](export-errors.md).
197
197
  `gameId` and section `builds`, or summerengine.com → Studio → your game →
198
198
  Builds.
199
199
  3. Upload the `.zip` as a new Build. Summer checks it again: unknown fields,
200
- queue rules, both packs, and the composition match.
200
+ queue rules, both packs, and the composition match. From an agent:
201
+ `summer_export_game`, then `summer_publish_build` with `gameId` and
202
+ `clientVersion`; it asks for confirmation first and needs
203
+ `summer login --store`. If it answers `store_auth_refused`, upload in
204
+ Studio.
201
205
  4. Release the Build to an environment. Its queues go live there, and
202
206
  clients join them with `Summer.client.join(SummerJoinTarget.queue(...))`.
203
207
  The code is the same as in Local Play.
@@ -22,7 +22,7 @@ Rigid skill: follow the rules below exactly.
22
22
  | The user wants to… | Do |
23
23
  |---|---|
24
24
  | **see / check / decide** — plan and billing (money), their published games and releases, usage, pricing, the MCP setup guide for their agent, docs, the scene or node you just built ("show me") | **Open the surface** with `summer_open` |
25
- | **get a result** — add a node, set a property, import an asset, run the game, publish | **Act through Summer tools** (mutation tools, `summer_play`, `summer_creator_publish`); do not open the editor to do by hand what a tool does |
25
+ | **get a result** — add a node, set a property, import an asset, run the game, publish | **Act through Summer tools** (mutation tools, `summer_play`, `summer_publish_build`); do not open the editor to do by hand what a tool does |
26
26
  | **both** ("add a camera and show me") | act first, then open the scene / select the node so the user sees the result |
27
27
 
28
28
  Never open a browser or switch the editor's tab as a side effect of building. Opening is a user-visible action — do it because the user asked to look, and say what will open before it opens.
@@ -1,12 +1,13 @@
1
1
  id: tool/creator-publish
2
2
  kind: tool
3
- version: 1.0.2
4
- summary: Publish an exported Summer .pck through the versioned Summercraft creator API, with an explicit confirm step before upload.
3
+ version: 1.1.0
4
+ summary: "Deprecated: publishes a .pck to the legacy Summercraft API. Use summer_export_game, then summer_publish_build, to upload to summer.games."
5
5
  use_when:
6
6
  - releasing a user-approved build to a Summercraft channel
7
7
  - '"ship this exported build to the live channel"'
8
8
  - '"release version 1.2.0 of my game"'
9
9
  do_not_use_when:
10
+ - uploading to summer.games — summer_export_game, then summer_publish_build
10
11
  - the user has not approved the exact version, digest, and channel
11
12
  - the game is not exported yet — export the .pck first (skill/export-and-ship)
12
13
  facets:
@@ -19,7 +20,7 @@ compatibility:
19
20
  engine: ">=4.6"
20
21
  source: official
21
22
  license: MIT
22
- status: stable
23
+ status: deprecated
23
24
  aliases:
24
25
  - summer_creator_publish
25
26
  implementation:
@@ -7,7 +7,7 @@ use_when:
7
7
  - '"what version is live right now?"'
8
8
  - '"did the publish go through?" — confirming a release landed'
9
9
  do_not_use_when:
10
- - publishing a new release — summer_creator_publish
10
+ - publishing a new release — summer_publish_build
11
11
  facets:
12
12
  lifecycle:
13
13
  - launch
@@ -0,0 +1,72 @@
1
+ id: tool/export-game
2
+ kind: tool
3
+ version: 1.0.0
4
+ summary: Export the game in the exact file the Summer Games store takes per platform (iPhone, Android, macOS, Windows bundle; web build; desktop download), headless.
5
+ use_when:
6
+ - '"export my game for summer.games"'
7
+ - '"make a build I can upload to my store page"'
8
+ - '"export my game for iPhone and Android on Summer Games"'
9
+ - '"make a web build of my game for summer.games"'
10
+ - '"export a Mac or Windows download for my store page"'
11
+ - preparing the bundle that summer_publish_build uploads
12
+ do_not_use_when:
13
+ - exporting for Steam, itch.io, the App Store, Google Play or other stores — skill/export-and-ship
14
+ - the game is still being built; export when the user wants a Build
15
+ facets:
16
+ lifecycle:
17
+ - launch
18
+ domains:
19
+ - creator
20
+ - deployment
21
+ - headless
22
+ compatibility:
23
+ engine: ">=4.6"
24
+ source: official
25
+ license: MIT
26
+ status: preview
27
+ aliases:
28
+ - summer_export_game
29
+ implementation:
30
+ module: src/core/capabilities/export-game.ts
31
+ export: exportGame
32
+ surfaces:
33
+ mcp:
34
+ tool_name: summer_export_game
35
+ remote: true
36
+ input_schema:
37
+ type: object
38
+ properties:
39
+ project:
40
+ type: string
41
+ description: Project folder with project.godot. Defaults to the MCP's bound project, then the working directory.
42
+ out:
43
+ type: string
44
+ description: Output .zip path. Defaults to <project>/.summer/exports/.
45
+ targets:
46
+ type: array
47
+ items:
48
+ type: string
49
+ enum: [macos, windows, ios, android, web]
50
+ description: "Store platforms. Bundle: any of macos, windows, ios, android (omit to use every platform the project declares). Download: exactly one of web, macos, windows."
51
+ format:
52
+ type: string
53
+ enum: [bundle, download]
54
+ description: 'bundle (default): the summer.games store build. download: a web build or native download; the default when targets is ["web"].'
55
+ debug:
56
+ type: boolean
57
+ default: false
58
+ description: Export a debug build (--export-debug).
59
+ timeoutSeconds:
60
+ type: integer
61
+ minimum: 10
62
+ maximum: 7200
63
+ description: Stop the engine after this many seconds (default 900; a first export imports every asset).
64
+ additionalProperties: false
65
+ authority:
66
+ filesystem: true
67
+ editor_mutation: false
68
+ network: false
69
+ credentials: false
70
+ publish: false
71
+ evidence_checks:
72
+ - none