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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +2 -2
- package/.codex-plugin/plugin.json +3 -3
- package/.cursor-plugin/plugin.json +2 -2
- package/.factory-plugin/plugin.json +2 -2
- package/CHANGELOG.md +25 -0
- package/README.md +5 -3
- package/dist/cli/commands/login.js +12 -0
- package/dist/cli/commands/publish.js +2 -1
- package/dist/core/auth.js +3 -0
- package/dist/core/capabilities/audit/args.d.ts +14 -14
- package/dist/core/capabilities/engine-run.d.ts +8 -0
- package/dist/core/capabilities/engine-run.js +33 -0
- package/dist/core/capabilities/export-game.d.ts +99 -0
- package/dist/core/capabilities/export-game.js +369 -0
- package/dist/core/capabilities/export-presets.d.ts +50 -0
- package/dist/core/capabilities/export-presets.js +177 -0
- package/dist/core/capabilities/export-templates.d.ts +118 -0
- package/dist/core/capabilities/export-templates.js +291 -0
- package/dist/core/capabilities/publish-build.d.ts +64 -0
- package/dist/core/capabilities/publish-build.js +448 -0
- package/dist/core/capabilities/publish-download.d.ts +37 -0
- package/dist/core/capabilities/publish-download.js +168 -0
- package/dist/core/capabilities/runtime-control.d.ts +10 -10
- package/dist/core/capabilities/summer-bundle.d.ts +49 -0
- package/dist/core/capabilities/summer-bundle.js +156 -0
- package/dist/core/capabilities/tool-dispatch.js +33 -1
- package/dist/core/capabilities/ui-control.d.ts +2 -2
- package/dist/core/library-read.d.ts +2 -2
- package/dist/core/oauth.d.ts +78 -0
- package/dist/core/oauth.js +309 -0
- package/dist/core/util/zip-write.d.ts +18 -0
- package/dist/core/util/zip-write.js +112 -0
- package/dist/core/util/zip.d.ts +22 -0
- package/dist/core/util/zip.js +140 -0
- package/dist/mcp/hosted-mount.d.ts +96 -0
- package/dist/mcp/hosted-mount.js +206 -0
- package/dist/mcp/server.d.ts +2 -1
- package/dist/mcp/server.js +13 -6
- package/dist/mcp/tools/creator-tools.js +54 -1
- package/gemini-extension.json +1 -1
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +6 -3
- package/library/skills/export-and-ship/SKILL.md +2 -0
- package/library/skills/multiplayer-publish/SKILL.md +5 -1
- package/library/skills/navigate-summer/SKILL.md +1 -1
- package/library/tools/creator-publish/resource.yaml +4 -3
- package/library/tools/creator-releases/resource.yaml +1 -1
- package/library/tools/export-game/resource.yaml +72 -0
- package/library/tools/export-templates/resource.yaml +63 -0
- package/library/tools/open/resource.yaml +1 -1
- package/library/tools/publish-build/resource.yaml +78 -0
- package/package.json +1 -1
- package/registry/generated/aliases.json +3 -0
- package/registry/generated/counts.json +2 -2
- package/registry/generated/gemini-extension.json +1 -1
- package/registry/generated/index.json +119 -10
- package/registry/generated/marketplace.claude.json +2 -2
- package/registry/generated/plugin.claude.json +2 -2
- package/registry/generated/plugin.codex.json +3 -3
- package/registry/generated/plugin.cursor.json +2 -2
- 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
|
+
}
|
package/dist/mcp/server.d.ts
CHANGED
|
@@ -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;
|
package/dist/mcp/server.js
CHANGED
|
@@ -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
|
|
43
|
-
"Before building or fixing, summer_search_library then summer_read_library the hit
|
|
44
|
-
"After a playthrough read summer_get_diagnostics, not summer_get_console alone
|
|
45
|
-
"An all-black screenshot means the viewport had not redrawn
|
|
46
|
-
"Report
|
|
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("
|
|
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."),
|
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": "3.
|
|
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 (
|
|
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 (
|
|
240
|
+
### Creator platform (5)
|
|
241
241
|
|
|
242
242
|
| Tool | Use |
|
|
243
243
|
|---|---|
|
|
244
|
-
| `
|
|
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`, `
|
|
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
|
|
4
|
-
summary:
|
|
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:
|
|
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 —
|
|
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
|