summer-engine 3.5.0 → 3.5.1

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.
@@ -11,7 +11,7 @@
11
11
  {
12
12
  "name": "summer",
13
13
  "source": "./",
14
- "version": "3.5.0",
14
+ "version": "3.5.1",
15
15
  "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 105-tool MCP bridge to the local desktop app.",
16
16
  "author": {
17
17
  "name": "Summer Engine",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "_generated": "GENERATED from integrations/claude — do not edit; npm run generate:registry",
4
4
  "name": "summer",
5
- "version": "3.5.0",
5
+ "version": "3.5.1",
6
6
  "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 105-tool MCP bridge to the local desktop app.",
7
7
  "author": {
8
8
  "name": "Summer Engine",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "_generated": "GENERATED from integrations/codex — do not edit; npm run generate:registry",
3
3
  "name": "summer",
4
- "version": "3.5.0",
4
+ "version": "3.5.1",
5
5
  "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 105-tool MCP bridge. Build games by talking.",
6
6
  "author": {
7
7
  "name": "Summer Engine",
@@ -3,7 +3,7 @@
3
3
  "_generated": "GENERATED from integrations/cursor — do not edit; npm run generate:registry",
4
4
  "name": "summer",
5
5
  "displayName": "Summer",
6
- "version": "3.5.0",
6
+ "version": "3.5.1",
7
7
  "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 105-tool MCP bridge to the local desktop app.",
8
8
  "author": {
9
9
  "name": "Summer Engine",
@@ -2,7 +2,7 @@
2
2
  "_generated": "GENERATED from integrations/factory — do not edit; npm run generate:registry",
3
3
  "name": "summer",
4
4
  "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 105-tool MCP bridge to the local desktop app.",
5
- "version": "3.5.0",
5
+ "version": "3.5.1",
6
6
  "author": {
7
7
  "name": "Summer Engine",
8
8
  "email": "founders@summerengine.com",
package/CHANGELOG.md CHANGED
@@ -4,6 +4,14 @@ All notable changes to summer-engine will be documented here. Following [Keep a
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [3.5.1] - 2026-10-10
8
+
9
+ Summer Games now connects from almost every agent setup supports, and large store uploads finish.
10
+
11
+ - **Summer Games in more agents.** `summer setup` now adds `summer-games` (`https://mcp.summer.games/mcp`: friends, parties and messages as you) for Cursor, GitHub Copilot in VS Code, the Copilot CLI and Copilot in JetBrains, Zed, Cline and Cline CLI, Kilo Code, OpenCode, Devin Desktop (Windsurf), Antigravity, Kiro, Trae, Warp, Goose, Hermes, Crush, Amp, Factory Droid, Junie, Qwen Code, Kimi Code, Grok Build and Mistral Vibe, as well as Claude Code and Codex. Each gets the entry in its own format, and setup prints the one sign-in step for that agent. As in 3.5.0, setup adds no second entry when the same address is already listed under another name, and `--no-games` skips it.
12
+ - Not included, because they cannot sign in to it from their config file: Claude Desktop (add Summer Games as a custom connector in its settings), Roo Code and Rovo Dev (no sign-in for remote servers), and Visual Studio, Qoder and LM Studio (sign-in not yet checked).
13
+ - `summer_publish_build` and `summer_publish_download` no longer fail an upload of 3 or more parts with `store_sign_failed: The store did not sign part N` when the store signed every part. Each sign now asks for every part still to upload, a part counts as unsigned only when the store was asked for it and left it out, a signing error is retried like a failed part upload, and the first failure stops the other parts' uploads.
14
+
7
15
  ## [3.5.0] - 2026-10-10
8
16
 
9
17
  Setup now connects Summer Games too, and agents that publish a game get further on their own. 3.4.2 was never published; its changes ship here.
package/README.md CHANGED
@@ -57,7 +57,7 @@ The first run will:
57
57
  4. Open your browser for one-click sign-in.
58
58
  5. Scaffold a starter project and open the engine.
59
59
 
60
- In Claude Code and Codex, setup also connects Summer Games (`summer-games`), so the agent can see your friends, start parties and send messages as you. You sign in once: Claude Code `/mcp`, choose `summer-games`; Codex `/mcp login summer-games`. A summer.games page opens in your browser; sign in and choose Allow.
60
+ In most agents (Claude Code, Codex, Cursor, GitHub Copilot (VS Code, CLI, JetBrains), Zed, Cline, Kilo Code, OpenCode, Devin Desktop, Antigravity, Kiro, Trae, Warp, Goose, Hermes, Crush, Amp, Factory, Junie, Qwen Code, Kimi Code, Grok Build and Mistral Vibe), setup also connects Summer Games (`summer-games`), so the agent can see your friends, start parties and send messages as you. You sign in once; setup prints the step for your agent (Claude Code: `/mcp`, choose `summer-games`; Codex: `/mcp login summer-games`; Cursor: Connect next to `summer-games` in its MCP settings). A summer.games page opens in your browser; sign in and choose Allow.
61
61
 
62
62
  Then ask the agent to build something.
63
63
 
@@ -125,7 +125,7 @@ npx -y summer-engine@latest setup claude-code --yes --force
125
125
 
126
126
  Replace `claude-code` with the user's actual agent: `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `cline-cli`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `warp`, `rovo-dev`, `qoder`, `grok-build`, `mistral-vibe`, `lm-studio`. Use `github-copilot` for Copilot CLI, `vscode-copilot` for GitHub Copilot Chat/Agent in VS Code, `visual-studio` for Copilot in Visual Studio, and `copilot-jetbrains` for Copilot in JetBrains IDEs. Legacy targets `gemini` and `roo-code` still work; for Google's agent use `antigravity`. This installs **every skill in the library** to `~/.claude/skills/<slug>/SKILL.md` (or the agent's own skill directory), writes the MCP server config, runs doctor, and prints what it installed and where. Preview skills are included and labelled; `--stable-only` skips them. `--force` wipes stale skill copies first. Don't use `--recommended` from this playbook; it installs only a subset.
127
127
 
128
- **Summer Games:** for `claude-code` and `codex`, setup also adds the remote `summer-games` MCP (`https://mcp.summer.games/mcp`) and prints a `Sign in once:` line. Tell the user that line once, in plain words: in Claude Code, run `/mcp`, choose `summer-games` and sign in; in Codex, run `/mcp login summer-games`. Their browser opens summer.games; they sign in and choose Allow. Do not wait for it; continue the playbook. If the user already added the same URL under another name (for example `summer`), setup keeps theirs and adds no second entry.
128
+ **Summer Games:** for every agent whose sign-in summer.games accepts (all targets except `claude-desktop`, `visual-studio`, `qoder`, `rovo-dev`, `lm-studio` and the legacy ones), setup also adds the remote `summer-games` MCP (`https://mcp.summer.games/mcp`) and prints a `Sign in once:` line. Tell the user that line once, in plain words. Their browser opens summer.games; they sign in and choose Allow. Do not wait for it; continue the playbook. If the user already added the same URL under another name (for example `summer`), setup keeps theirs and adds no second entry.
129
129
 
130
130
  **First-install detection:** if `~/.claude/skills/` didn't exist before this command, Claude Code wasn't watching it and won't auto-detect the new files this session. Tell the user **once**: "Skills installed - restart your agent so they load." On subsequent installs (directory already existed), skills auto-detect mid-session and no restart is needed. **You can detect first-install vs upgrade by checking if `~/.claude/skills/` existed before Step 1; record the result before running setup.**
131
131
 
@@ -374,7 +374,7 @@ matchmaking are not promised by this package.
374
374
  | `summer tool <name> --args '<json>'` | Run any Summer tool from the shell — the same implementation the MCP tool uses. `summer tool --list` prints them all. |
375
375
  | `summer mcp [--project <path> \| --instance <id>]` | Start MCP; normally auto-binds from the agent's project directory. |
376
376
  | `summer mcp setup <agent>` | Deprecated alias of `summer setup <agent>`. |
377
- | `summer setup <agent> [--yes] [--force] [--recommended] [--stable-only] [--no-games]` | One shot: MCP config + all skills, preview included (`--recommended` for the subset, `--stable-only` to skip preview) + doctor. Idempotent. For Claude Code and Codex it also adds `summer-games` (`https://mcp.summer.games/mcp`: friends, parties and messages as you) unless the same URL is already listed; you sign in once through summer.games. `--no-games` skips it. |
377
+ | `summer setup <agent> [--yes] [--force] [--recommended] [--stable-only] [--no-games]` | One shot: MCP config + all skills, preview included (`--recommended` for the subset, `--stable-only` to skip preview) + doctor. Idempotent. For most agents (not Claude Desktop, Visual Studio, Qoder, Rovo Dev, LM Studio or the legacy targets) it also adds `summer-games` (`https://mcp.summer.games/mcp`: friends, parties and messages as you) unless the same URL is already listed; you sign in once through summer.games. `--no-games` skips it. |
378
378
 
379
379
  Agents: `claude-code`, `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `cline-cli`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `warp`, `rovo-dev`, `qoder`, `grok-build`, `mistral-vibe`, `lm-studio`. Legacy (still accepted, warn on use): `gemini`, `roo-code`. Aliases: `devin`/`devin-desktop` → `windsurf`, `agy` → `antigravity`, `droid` → `factory`, `copilot` → `github-copilot`, `vscode` → `vscode-copilot`, `kilo` → `kilo-code`, `qwen` → `qwen-code`, `kimi` → `kimi-code`. Scopes: `--scope user` (default), `--scope project`.
380
380
 
@@ -7,7 +7,7 @@ import { TOOLKIT_VERSION as cliVersion } from "../../core/version.js";
7
7
  import { resolveDefaultChannel } from "../../installer/version-check.js";
8
8
  import { agentLabel } from "../../installer/agent-table.js";
9
9
  export const setupCommand = new Command("setup")
10
- .description("Configure Summer Engine for an AI agent and run diagnostics. For Claude Code and Codex it also adds Summer Games (summer-games).")
10
+ .description("Configure Summer Engine for an AI agent and run diagnostics. For agents that can sign in to it, it also adds Summer Games (summer-games).")
11
11
  .argument("[agent]", `Agent to configure: ${supportedAgents.join(", ")}`)
12
12
  .option("--agent <agent>", `Agent to configure: ${supportedAgents.join(", ")}`)
13
13
  .option("--scope <scope>", "Configuration scope: user or project", "user")
@@ -16,7 +16,7 @@ export const setupCommand = new Command("setup")
16
16
  .option("--local-dev", "Point the agent at this checkout's built CLI (node <repo>/dist/bin/summer.js mcp) instead of npx summer-engine@latest — for testing unpublished builds. SUMMER_DEV=1 does the same.")
17
17
  .option("--channel <dist-tag>", "npm dist-tag the agent's MCP entry runs: npx -y summer-engine@<dist-tag> mcp (default latest; use next while a release soaks on the next tag). SUMMER_CHANNEL does the same.")
18
18
  .option("--yes", "Apply practical setup steps without prompting")
19
- .option("--no-games", "Do not add the Summer Games MCP (summer-games: friends, parties and messages as you, signed in through summer.games; Claude Code and Codex)")
19
+ .option("--no-games", "Do not add the Summer Games MCP (summer-games: friends, parties and messages as you, signed in through summer.games)")
20
20
  .option("--json", "Print setup result as JSON")
21
21
  .option("--force", "Overwrite existing skill content (passes --force through to skills install)")
22
22
  .option("--recommended", "Install only the recommended skill subset instead of the whole library")
@@ -51,6 +51,10 @@ export declare function readGrants(body: Record<string, any>): Grant[];
51
51
  * Upload the given parts: signed in batches, PUT from disk, re-signed when
52
52
  * refused or about to expire. `sign` asks the store for URLs (build
53
53
  * publications bind each to its part SHA-256; store versions to its length).
54
+ * Each sign asks for every part not yet uploaded, including the parts other
55
+ * workers hold, so one sign usually covers them all. A part fails to sign only
56
+ * when the store was asked for it and did not sign it; the first fatal error
57
+ * stops the other workers and aborts their uploads.
54
58
  */
55
59
  export declare function uploadParts(sign: (partNumbers: number[]) => Promise<Grant[]>, file: string, sizeBytes: number, plan: {
56
60
  partSizeBytes: number;
@@ -185,19 +185,28 @@ async function readPart(path, partNumber, partSize, sizeBytes) {
185
185
  * Upload the given parts: signed in batches, PUT from disk, re-signed when
186
186
  * refused or about to expire. `sign` asks the store for URLs (build
187
187
  * publications bind each to its part SHA-256; store versions to its length).
188
+ * Each sign asks for every part not yet uploaded, including the parts other
189
+ * workers hold, so one sign usually covers them all. A part fails to sign only
190
+ * when the store was asked for it and did not sign it; the first fatal error
191
+ * stops the other workers and aborts their uploads.
188
192
  */
189
193
  export async function uploadParts(sign, file, sizeBytes, plan, deps, parts = Array.from({ length: plan.partCount }, (_, index) => index + 1)) {
190
194
  const queue = [...parts];
195
+ const unfinished = new Set(parts);
191
196
  const grants = new Map();
197
+ const stop = new AbortController();
198
+ let failure;
192
199
  let signing = null;
193
200
  const fresh = (grant) => grant !== undefined && grant.expiresAt - deps.now() > 60_000;
194
201
  const grantFor = async (partNumber) => {
195
- for (let attempt = 0; attempt < 3; attempt++) {
196
- if (fresh(grants.get(partNumber)))
197
- return grants.get(partNumber);
202
+ for (;;) {
203
+ const grant = grants.get(partNumber);
204
+ if (fresh(grant))
205
+ return grant;
198
206
  if (!signing) {
199
- const batch = [...new Set([partNumber, ...queue.filter((n) => !fresh(grants.get(n)))])].slice(0, MAX_SIGNED_PARTS);
200
- signing = sign(batch)
207
+ const others = [...unfinished].filter((n) => n !== partNumber && !fresh(grants.get(n)));
208
+ const batch = [partNumber, ...others].slice(0, MAX_SIGNED_PARTS);
209
+ const done = sign(batch)
201
210
  .then((signed) => {
202
211
  for (const grant of signed)
203
212
  grants.set(grant.partNumber, grant);
@@ -205,43 +214,67 @@ export async function uploadParts(sign, file, sizeBytes, plan, deps, parts = Arr
205
214
  .finally(() => {
206
215
  signing = null;
207
216
  });
217
+ signing = { batch, done };
218
+ }
219
+ const { batch, done } = signing;
220
+ await done;
221
+ // The store was asked for this part and did not sign it (or signed it already expiring).
222
+ if (batch.includes(partNumber) && !fresh(grants.get(partNumber))) {
223
+ throw new BuildToolError("store_sign_failed", `The store did not sign part ${partNumber}.`, "Recovery: retry; a retry continues the same upload.");
208
224
  }
209
- await signing;
210
225
  }
211
- throw new BuildToolError("store_sign_failed", `The store did not sign part ${partNumber}.`, "Recovery: retry; a retry continues the same upload.");
212
226
  };
213
- const worker = async () => {
214
- for (let partNumber = queue.shift(); partNumber !== undefined; partNumber = queue.shift()) {
215
- const body = await readPart(file, partNumber, plan.partSizeBytes, sizeBytes);
216
- for (let attempt = 0;; attempt++) {
227
+ const upload = async (partNumber) => {
228
+ const body = await readPart(file, partNumber, plan.partSizeBytes, sizeBytes);
229
+ for (let attempt = 0;; attempt++) {
230
+ let status = null;
231
+ let error = null;
232
+ try {
217
233
  const grant = await grantFor(partNumber);
218
- let status = null;
219
- try {
220
- const response = await deps.fetch(grant.url, {
221
- method: "PUT",
222
- headers: grant.headers,
223
- body,
224
- redirect: "error",
225
- signal: AbortSignal.timeout(600_000),
226
- });
227
- if (response.ok)
228
- break;
229
- status = response.status;
230
- }
231
- catch {
232
- status = null;
233
- }
234
- // An expired or refused signature gets a new URL on the next try.
235
- if (status === 400 || status === 403)
236
- grants.delete(partNumber);
237
- if (attempt >= PART_RETRIES) {
238
- throw new BuildToolError("store_part_upload_failed", `Part ${partNumber} of ${plan.partCount} did not upload${status ? ` (${status})` : ""}.`, "Recovery: retry with the same file and clientVersion; the upload continues where it stopped.");
239
- }
240
- await deps.sleep(500 * 2 ** attempt);
234
+ const response = await deps.fetch(grant.url, {
235
+ method: "PUT",
236
+ headers: grant.headers,
237
+ body,
238
+ redirect: "error",
239
+ signal: AbortSignal.any([stop.signal, AbortSignal.timeout(600_000)]),
240
+ });
241
+ if (response.ok)
242
+ return;
243
+ status = response.status;
244
+ }
245
+ catch (caught) {
246
+ error = caught;
247
+ }
248
+ if (stop.signal.aborted)
249
+ return;
250
+ // An expired or refused signature gets a new URL on the next try.
251
+ if (status === 400 || status === 403)
252
+ grants.delete(partNumber);
253
+ if (attempt >= PART_RETRIES) {
254
+ if (error instanceof BuildToolError)
255
+ throw error;
256
+ throw new BuildToolError("store_part_upload_failed", `Part ${partNumber} of ${plan.partCount} did not upload${status ? ` (${status})` : ""}.`, "Recovery: retry with the same file and clientVersion; the upload continues where it stopped.");
257
+ }
258
+ await deps.sleep(500 * 2 ** attempt);
259
+ if (stop.signal.aborted)
260
+ return;
261
+ }
262
+ };
263
+ const worker = async () => {
264
+ for (let partNumber = queue.shift(); partNumber !== undefined && !stop.signal.aborted; partNumber = queue.shift()) {
265
+ try {
266
+ await upload(partNumber);
267
+ unfinished.delete(partNumber);
268
+ }
269
+ catch (error) {
270
+ failure ??= error;
271
+ stop.abort();
241
272
  }
242
273
  }
243
274
  };
244
275
  await Promise.all(Array.from({ length: Math.min(CONCURRENCY, queue.length) }, worker));
276
+ if (stop.signal.aborted)
277
+ throw failure;
245
278
  }
246
279
  export function normalizeClientVersion(value) {
247
280
  const raw = value?.trim() ?? "";
@@ -186,8 +186,8 @@ export declare const runtimeAnimateArgsSchema: z.ZodObject<{
186
186
  value?: string | number | boolean | undefined;
187
187
  instance?: string | undefined;
188
188
  name?: string | undefined;
189
- cmd?: "state" | "pause" | "play" | "stop" | "seek" | "speed" | "travel" | "start" | "set_param" | "get_param" | undefined;
190
189
  space?: "local" | "global" | "both" | undefined;
190
+ cmd?: "state" | "pause" | "play" | "stop" | "seek" | "speed" | "travel" | "start" | "set_param" | "get_param" | undefined;
191
191
  blend?: number | undefined;
192
192
  position?: number | undefined;
193
193
  speed?: number | undefined;
@@ -205,8 +205,8 @@ export declare const runtimeAnimateArgsSchema: z.ZodObject<{
205
205
  value?: string | number | boolean | undefined;
206
206
  instance?: string | undefined;
207
207
  name?: string | undefined;
208
- cmd?: "state" | "pause" | "play" | "stop" | "seek" | "speed" | "travel" | "start" | "set_param" | "get_param" | undefined;
209
208
  space?: "local" | "global" | "both" | undefined;
209
+ cmd?: "state" | "pause" | "play" | "stop" | "seek" | "speed" | "travel" | "start" | "set_param" | "get_param" | undefined;
210
210
  blend?: number | undefined;
211
211
  position?: number | undefined;
212
212
  speed?: number | undefined;
@@ -64,10 +64,10 @@ export interface RemoteMcpResult {
64
64
  signIn: string;
65
65
  }
66
66
  /**
67
- * Adds the Summer Games MCP next to summer-engine in the same config file.
68
- * An entry that already points at the same URL, under any name, counts as
69
- * added and is left as it is. Returns null for agents without a remote-server
70
- * entry in agent-table.ts.
67
+ * Adds the Summer Games MCP next to summer-engine in the same config file, in
68
+ * the agent's own entry shape (remoteMcp in agent-table.ts). An entry that
69
+ * already points at the same URL, under any name, counts as added and is left
70
+ * as it is. Returns null for agents without remoteMcp.
71
71
  */
72
72
  export declare function configureSummerGamesMcp(options: Pick<AgentConfigOptions, "agent" | "scope" | "dryRun" | "cwd" | "env">): Promise<RemoteMcpResult | null>;
73
73
  export declare const DEFAULT_CHANNEL = "latest";
@@ -85,4 +85,4 @@ export declare function mcpEntry(format: McpFormat, server: StdioMcpServerConfig
85
85
  export declare function resolvePackageRoot(): string;
86
86
  /** Directory name Gemini expects the extension under; the manifest `name` must match it. */
87
87
  export declare const GEMINI_EXTENSION_DIR_NAME = "summer-engine";
88
- export declare function upsertTomlArrayBlock(content: string, block: string): string;
88
+ export declare function upsertTomlArrayBlock(content: string, block: string, name?: string): string;
@@ -79,10 +79,10 @@ export async function configureAgentMcp(options) {
79
79
  export const SUMMER_GAMES_MCP_SERVER_NAME = "summer-games";
80
80
  export const SUMMER_GAMES_MCP_URL = "https://mcp.summer.games/mcp";
81
81
  /**
82
- * Adds the Summer Games MCP next to summer-engine in the same config file.
83
- * An entry that already points at the same URL, under any name, counts as
84
- * added and is left as it is. Returns null for agents without a remote-server
85
- * entry in agent-table.ts.
82
+ * Adds the Summer Games MCP next to summer-engine in the same config file, in
83
+ * the agent's own entry shape (remoteMcp in agent-table.ts). An entry that
84
+ * already points at the same URL, under any name, counts as added and is left
85
+ * as it is. Returns null for agents without remoteMcp.
86
86
  */
87
87
  export async function configureSummerGamesMcp(options) {
88
88
  const spec = agentSpec(options.agent);
@@ -92,39 +92,68 @@ export async function configureSummerGamesMcp(options) {
92
92
  const target = resolveConfigTarget(spec, options.scope, resolve(options.cwd ?? process.cwd()), env);
93
93
  const write = !options.dryRun;
94
94
  const url = SUMMER_GAMES_MCP_URL;
95
+ const name = SUMMER_GAMES_MCP_SERVER_NAME;
96
+ const entry = spec.remoteMcp.entry(url);
97
+ const key = topLevelKey(spec.format);
95
98
  let existing;
96
99
  let changed = false;
97
- if (spec.remoteMcp.format === "toml-url") {
98
- const current = await readTextFileIfExists(target.path);
99
- existing = findTomlServerByUrl(current, url);
100
- if (!existing) {
101
- const name = SUMMER_GAMES_MCP_SERVER_NAME;
102
- const next = upsertTomlTable(current, `[mcp_servers.${name}]\nurl = ${tomlString(url)}\n`, name);
100
+ switch (spec.format) {
101
+ case "toml":
102
+ case "toml-array": {
103
+ const current = await readTextFileIfExists(target.path);
104
+ existing = spec.format === "toml" ? findTomlServerByUrl(current, url) : findTomlArrayServerByUrl(current, url);
105
+ if (existing)
106
+ break;
107
+ const next = spec.format === "toml"
108
+ ? upsertTomlTable(current, renderTomlTable(`[mcp_servers.${name}]`, entry), name)
109
+ : upsertTomlArrayBlock(current, renderTomlTable("[[mcp_servers]]", entry), name);
103
110
  changed = current !== next;
104
111
  if (write && changed)
105
112
  await writeTextFile(target.path, next);
113
+ break;
106
114
  }
107
- }
108
- else {
109
- const current = await readJsonConfig(target.path);
110
- existing = findJsonServerByUrl(current, url);
111
- if (!existing) {
115
+ case "yaml-goose":
116
+ case "yaml-hermes": {
117
+ const current = await readTextFileIfExists(target.path);
118
+ const parsed = current.trim() === "" ? {} : parseDocument(current).toJS();
119
+ existing = isJsonObject(parsed) ? findServerByUrl([parsed[key]], url) : undefined;
120
+ if (existing)
121
+ break;
122
+ ({ changed } = await upsertYamlConfig(target.path, [key, name], entry, write));
123
+ break;
124
+ }
125
+ case "json-gemini":
126
+ throw new Error("The Gemini extension manifest has no Summer Games entry.");
127
+ default: {
128
+ const current = await readJsonConfig(target.path);
129
+ const maps = [current[key]];
130
+ // Claude Code's ~/.claude.json also keeps per-project servers (`claude
131
+ // mcp add` without --scope user) under projects.<dir>.mcpServers.
132
+ if (isJsonObject(current.projects)) {
133
+ for (const project of Object.values(current.projects)) {
134
+ if (isJsonObject(project))
135
+ maps.push(project.mcpServers);
136
+ }
137
+ }
138
+ existing = findServerByUrl(maps, url);
139
+ if (existing)
140
+ break;
112
141
  const next = copyJsonObject(current);
113
- next.mcpServers = mergeNamedObject(next.mcpServers, SUMMER_GAMES_MCP_SERVER_NAME, { type: "http", url }, "mcpServers");
142
+ next[key] = mergeNamedObject(next[key], name, entry, key);
114
143
  const nextRendered = renderJsonFile(next);
115
144
  changed = renderJsonFile(current) !== nextRendered;
116
145
  if (write && changed)
117
146
  await writeTextFile(target.path, nextRendered);
118
147
  }
119
148
  }
120
- const serverName = existing ?? SUMMER_GAMES_MCP_SERVER_NAME;
149
+ const serverName = existing ?? name;
121
150
  return {
122
151
  serverName,
123
152
  url,
124
153
  path: target.path,
125
154
  changed,
126
155
  wrote: write && changed,
127
- ...(existing && existing !== SUMMER_GAMES_MCP_SERVER_NAME ? { existingName: existing } : {}),
156
+ ...(existing && existing !== name ? { existingName: existing } : {}),
128
157
  signIn: spec.remoteMcp.signIn(serverName),
129
158
  };
130
159
  }
@@ -142,27 +171,62 @@ function sameMcpUrl(a, b) {
142
171
  };
143
172
  return normalize(a) === normalize(b);
144
173
  }
145
- /** The name of a server with this URL in mcpServers. Claude Code's
146
- * ~/.claude.json also keeps per-project servers (`claude mcp add` without
147
- * --scope user) under projects.<dir>.mcpServers; those count too. */
148
- function findJsonServerByUrl(config, url) {
149
- const maps = [config.mcpServers];
150
- if (isJsonObject(config.projects)) {
151
- for (const project of Object.values(config.projects)) {
152
- if (isJsonObject(project))
153
- maps.push(project.mcpServers);
154
- }
155
- }
174
+ /** Agents name a remote server's address url, serverUrl (Windsurf,
175
+ * Antigravity), httpUrl (Qwen Code) or uri (Goose). */
176
+ const REMOTE_URL_FIELDS = ["url", "serverUrl", "httpUrl", "uri"];
177
+ /** The name of a server with this URL in any of these name -> entry maps. */
178
+ function findServerByUrl(maps, url) {
156
179
  for (const map of maps) {
157
180
  if (!isJsonObject(map))
158
181
  continue;
159
182
  for (const [name, entry] of Object.entries(map)) {
160
- if (isJsonObject(entry) && sameMcpUrl(entry.url, url))
183
+ if (isJsonObject(entry) && REMOTE_URL_FIELDS.some((field) => sameMcpUrl(entry[field], url)))
161
184
  return name;
162
185
  }
163
186
  }
164
187
  return undefined;
165
188
  }
189
+ /** The name of a [[mcp_servers]] block (Mistral Vibe) whose url is this URL. */
190
+ function findTomlArrayServerByUrl(content, url) {
191
+ let block;
192
+ const done = () => (block?.matches ? block.name : undefined);
193
+ for (const line of content.split(/\r?\n/)) {
194
+ if (/^\s*\[/.test(line)) {
195
+ if (done())
196
+ return done();
197
+ block = /^\s*\[\[mcp_servers\]\]\s*(?:#.*)?$/.test(line) ? { matches: false } : undefined;
198
+ continue;
199
+ }
200
+ if (!block)
201
+ continue;
202
+ const field = /^\s*(name|url)\s*=\s*(?:"([^"]*)"|'([^']*)')/.exec(line);
203
+ if (!field)
204
+ continue;
205
+ const value = field[2] ?? field[3];
206
+ if (field[1] === "name")
207
+ block.name = value;
208
+ else if (sameMcpUrl(value, url))
209
+ block.matches = true;
210
+ }
211
+ return done();
212
+ }
213
+ /** A TOML table: the header line, then key = value for each entry field. */
214
+ function renderTomlTable(header, entry) {
215
+ return `${[header, ...Object.entries(entry).map(([field, value]) => `${field} = ${tomlValue(value)}`)].join("\n")}\n`;
216
+ }
217
+ function tomlValue(value) {
218
+ if (typeof value === "string")
219
+ return tomlString(value);
220
+ if (typeof value === "number" || typeof value === "boolean")
221
+ return String(value);
222
+ if (Array.isArray(value))
223
+ return `[${value.map(tomlValue).join(", ")}]`;
224
+ if (isJsonObject(value)) {
225
+ const fields = Object.entries(value).map(([field, inner]) => `${field} = ${tomlValue(inner)}`);
226
+ return fields.length === 0 ? "{}" : `{ ${fields.join(", ")} }`;
227
+ }
228
+ throw new Error(`Cannot write ${JSON.stringify(value)} as TOML.`);
229
+ }
166
230
  /** The name of a [mcp_servers.<name>] table whose url is this URL. */
167
231
  function findTomlServerByUrl(content, url) {
168
232
  let table;
@@ -430,10 +494,10 @@ async function upsertTomlArrayConfig(path, server, write) {
430
494
  }
431
495
  return { changed };
432
496
  }
433
- export function upsertTomlArrayBlock(content, block) {
497
+ export function upsertTomlArrayBlock(content, block, name = SUMMER_MCP_SERVER_NAME) {
434
498
  const lines = content.split(/\r?\n/);
435
499
  const header = /^\s*\[\[mcp_servers\]\]\s*(?:#.*)?$/;
436
- const nameLine = new RegExp(`^\\s*name\\s*=\\s*"${SUMMER_MCP_SERVER_NAME}"\\s*(?:#.*)?$`);
500
+ const nameLine = new RegExp(`^\\s*name\\s*=\\s*"${name}"\\s*(?:#.*)?$`);
437
501
  let start = -1;
438
502
  let end = lines.length;
439
503
  for (let index = 0; index < lines.length; index += 1) {
@@ -22,7 +22,6 @@ export type ConfigScope = "user" | "project";
22
22
  * json-gemini the Gemini extension manifest (legacy)
23
23
  */
24
24
  export type McpFormat = "json" | "json-stdio" | "json-vscode" | "json-copilot" | "json-opencode" | "json-servers" | "json-zed" | "json-amp" | "json-crush" | "json-transport" | "toml" | "toml-array" | "yaml-goose" | "yaml-hermes" | "json-gemini";
25
- export type RemoteMcpFormat = "json-http" | "toml-url";
26
25
  export type SkillLocationKind = "skill-dir" | "cursor-rule-dir" | "windsurf-rule-file" | "cline-rule-dir" | "opencode-skill-dir";
27
26
  export interface PathContext {
28
27
  home: string;
@@ -72,14 +71,16 @@ export interface AgentSpec {
72
71
  envOverride: string;
73
72
  format: McpFormat;
74
73
  /**
75
- * How this agent lists a remote MCP server that signs in with OAuth on its
76
- * own (the Summer Games player MCP). Absent: setup links the engine only.
77
- * json-http { mcpServers: { <name>: { type: "http", url } } } (Claude Code)
78
- * toml-url [mcp_servers.<name>] url (Codex)
74
+ * The Summer Games player MCP (a remote server that signs in with OAuth on
75
+ * its own) in this agent's file, under the same key as summer-engine.
76
+ * entry is the server's value; for TOML it is written as key = value lines.
79
77
  * signIn tells the person how to sign in once, for the entry's name.
78
+ * Absent: setup links the engine only (no OAuth sign-in for remote servers,
79
+ * no file entry for them, or a sign-in api.summer.games has not been checked
80
+ * to accept).
80
81
  */
81
82
  remoteMcp?: {
82
- format: RemoteMcpFormat;
83
+ entry: (url: string) => Record<string, unknown>;
83
84
  signIn: (name: string) => string;
84
85
  };
85
86
  mcp: {
@@ -108,7 +108,10 @@ const SPECS = [
108
108
  status: "active",
109
109
  envOverride: "SUMMER_CLAUDE_CONFIG_FILE",
110
110
  format: "json-stdio",
111
- remoteMcp: { format: "json-http", signIn: (name) => `In Claude Code, run /mcp, choose ${name} and sign in.` },
111
+ remoteMcp: {
112
+ entry: (url) => ({ type: "http", url }),
113
+ signIn: (name) => `In Claude Code, run /mcp, choose ${name} and sign in.`,
114
+ },
112
115
  mcp: { user: home(".claude.json"), project: project(".mcp.json") },
113
116
  restart: "Restart Claude Code or run /mcp in a new session.",
114
117
  skills: skillDirs(home(".claude", "skills"), project(".claude", "skills")),
@@ -120,6 +123,7 @@ const SPECS = [
120
123
  status: "active",
121
124
  envOverride: "SUMMER_CLAUDE_DESKTOP_CONFIG_FILE",
122
125
  format: "json",
126
+ // No Summer Games entry: Claude Desktop's file takes local servers only; remote ones are added as Connectors in its settings.
123
127
  mcp: {
124
128
  user: (ctx) => join(appConfigRoot(ctx), "Claude", "claude_desktop_config.json"),
125
129
  project: null,
@@ -136,7 +140,7 @@ const SPECS = [
136
140
  envOverride: "SUMMER_CODEX_CONFIG_FILE",
137
141
  format: "toml",
138
142
  remoteMcp: {
139
- format: "toml-url",
143
+ entry: (url) => ({ url }),
140
144
  signIn: (name) => `In Codex, run /mcp login ${name} (or \`codex mcp login ${name}\` in a terminal) and sign in.`,
141
145
  },
142
146
  mcp: {
@@ -154,10 +158,10 @@ const SPECS = [
154
158
  status: "active",
155
159
  envOverride: "SUMMER_CURSOR_MCP_CONFIG_FILE",
156
160
  format: "json-stdio",
157
- // No remoteMcp (Summer Games) yet: Cursor's fallback OAuth flow registers
158
- // cursor://anysphere.cursor-mcp/oauth/callback, which api.summer.games
159
- // refuses (summer-platform#1681 allows it). Add { format: "json-url" }
160
- // ({ mcpServers: { <name>: { url } } }) once that is live.
161
+ remoteMcp: {
162
+ entry: (url) => ({ url }),
163
+ signIn: (name) => `In Cursor Settings, open MCP and click Connect (or Authenticate) next to ${name}.`,
164
+ },
161
165
  mcp: { user: home(".cursor", "mcp.json"), project: project(".cursor", "mcp.json") },
162
166
  restart: "Restart Cursor and enable the summer-engine MCP server if prompted.",
163
167
  skills: sharedSkills({
@@ -173,6 +177,10 @@ const SPECS = [
173
177
  status: "active",
174
178
  envOverride: "SUMMER_WINDSURF_MCP_CONFIG_FILE",
175
179
  format: "json",
180
+ remoteMcp: {
181
+ entry: (url) => ({ serverUrl: url }),
182
+ signIn: (name) => `Devin Desktop opens summer.games in your browser the first time ${name} connects; sign in there.`,
183
+ },
176
184
  mcp: {
177
185
  user: home(".codeium", "windsurf", "mcp_config.json"),
178
186
  project: null,
@@ -187,6 +195,10 @@ const SPECS = [
187
195
  status: "active",
188
196
  envOverride: "SUMMER_ANTIGRAVITY_CONFIG_FILE",
189
197
  format: "json",
198
+ remoteMcp: {
199
+ entry: (url) => ({ serverUrl: url }),
200
+ signIn: (name) => `In Antigravity, open Settings > Customizations, choose Authenticate next to ${name}, sign in on summer.games, then paste the code back and choose Submit.`,
201
+ },
190
202
  mcp: {
191
203
  user: home(".gemini", "config", "mcp_config.json"),
192
204
  project: project(".agents", "mcp_config.json"),
@@ -202,6 +214,7 @@ const SPECS = [
202
214
  legacyNote: "Gemini CLI was retired for individual accounts on 2026-06-18 and replaced by Antigravity. Use `summer setup antigravity` unless you are on a Workspace/enterprise Gemini CLI.",
203
215
  envOverride: "SUMMER_GEMINI_CONFIG_FILE",
204
216
  format: "json-gemini",
217
+ // No Summer Games entry: legacy target; use antigravity.
205
218
  mcp: {
206
219
  user: home(".gemini", "extensions", "summer-engine", "gemini-extension.json"),
207
220
  project: null,
@@ -216,6 +229,10 @@ const SPECS = [
216
229
  status: "active",
217
230
  envOverride: "SUMMER_CLINE_CONFIG_FILE",
218
231
  format: "json",
232
+ remoteMcp: {
233
+ entry: (url) => ({ type: "streamableHttp", url }),
234
+ signIn: (name) => `In Cline, open MCP Servers and click Authenticate next to ${name}.`,
235
+ },
219
236
  mcp: {
220
237
  user: (ctx) => vsCodeGlobalStorage(ctx, "saoudrizwan.claude-dev", "cline_mcp_settings.json"),
221
238
  project: null,
@@ -230,6 +247,10 @@ const SPECS = [
230
247
  status: "active",
231
248
  envOverride: "SUMMER_CLINE_CLI_CONFIG_FILE",
232
249
  format: "json",
250
+ remoteMcp: {
251
+ entry: (url) => ({ type: "streamableHttp", url }),
252
+ signIn: (name) => `Run \`cline mcp\`, pick ${name} and choose Authorize OAuth.`,
253
+ },
233
254
  mcp: { user: home(".cline", "data", "settings", "cline_mcp_settings.json"), project: null },
234
255
  restart: "Restart the Cline CLI so it reloads its MCP settings.",
235
256
  skills: skillDirs(home(".cline", "skills"), project(".cline", "skills")),
@@ -242,6 +263,7 @@ const SPECS = [
242
263
  legacyNote: "Roo Code shut down on 2026-05-15. The extension may still run, but it is no longer maintained; consider Cline or Kilo Code.",
243
264
  envOverride: "SUMMER_ROO_CODE_CONFIG_FILE",
244
265
  format: "json",
266
+ // No Summer Games entry: Roo Code has no OAuth sign-in for remote MCP servers.
245
267
  mcp: {
246
268
  user: (ctx) => vsCodeGlobalStorage(ctx, "rooveterinaryinc.roo-cline", "cline_mcp_settings.json"),
247
269
  project: null,
@@ -261,6 +283,10 @@ const SPECS = [
261
283
  status: "active",
262
284
  envOverride: "SUMMER_KILO_CODE_CONFIG_FILE",
263
285
  format: "json-opencode",
286
+ remoteMcp: {
287
+ entry: (url) => ({ type: "remote", url }),
288
+ signIn: (name) => `In Kilo, click the sign-in notice for ${name} (Kilo CLI: \`kilo mcp auth ${name}\`).`,
289
+ },
264
290
  mcp: {
265
291
  user: (ctx) => join(xdgOrAppData(ctx), "kilo", "kilo.json"),
266
292
  project: project("kilo.json"),
@@ -275,6 +301,10 @@ const SPECS = [
275
301
  status: "active",
276
302
  envOverride: "SUMMER_GITHUB_COPILOT_CONFIG_FILE",
277
303
  format: "json-copilot",
304
+ remoteMcp: {
305
+ entry: (url) => ({ type: "http", url, tools: ["*"] }),
306
+ signIn: (name) => `Copilot CLI opens summer.games to sign in when ${name} first connects; /mcp signs in again.`,
307
+ },
278
308
  mcp: { user: home(".copilot", "mcp-config.json"), project: project(".mcp.json") },
279
309
  restart: "Restart Copilot CLI, or run /mcp reload and /skills reload in the active session.",
280
310
  skills: skillDirs(home(".copilot", "skills"), project(".github", "skills")),
@@ -286,6 +316,10 @@ const SPECS = [
286
316
  status: "active",
287
317
  envOverride: "SUMMER_VSCODE_COPILOT_CONFIG_FILE",
288
318
  format: "json-vscode",
319
+ remoteMcp: {
320
+ entry: (url) => ({ type: "http", url }),
321
+ signIn: (name) => `In VS Code, click Allow when ${name} asks to authenticate, then sign in on summer.games.`,
322
+ },
289
323
  mcp: { user: (ctx) => join(vsCodeUserDir(ctx), "mcp.json"), project: project(".vscode", "mcp.json") },
290
324
  restart: "Restart VS Code or run MCP: List Servers, then start summer-engine in Copilot Agent mode.",
291
325
  skills: sharedSkills({ legacyDirs: { user: home(".copilot", "skills"), project: project(".github", "skills") } }),
@@ -297,6 +331,7 @@ const SPECS = [
297
331
  status: "active",
298
332
  envOverride: "SUMMER_VISUAL_STUDIO_CONFIG_FILE",
299
333
  format: "json-vscode",
334
+ // No Summer Games entry: Visual Studio's OAuth redirect for MCP is not documented; not checked against api.summer.games.
300
335
  mcp: { user: home(".mcp.json"), project: project(".mcp.json") },
301
336
  restart: "Restart Visual Studio, open Copilot Chat in Agent mode, and enable summer-engine in the tools picker.",
302
337
  skills: null,
@@ -309,6 +344,10 @@ const SPECS = [
309
344
  status: "active",
310
345
  envOverride: "SUMMER_COPILOT_JETBRAINS_CONFIG_FILE",
311
346
  format: "json-servers",
347
+ remoteMcp: {
348
+ entry: (url) => ({ url }),
349
+ signIn: (name) => `In your JetBrains IDE, click OK when ${name} asks to authenticate, then sign in on summer.games.`,
350
+ },
312
351
  mcp: {
313
352
  user: (ctx) => join(xdgOrAppData(ctx), "github-copilot", "intellij", "mcp.json"),
314
353
  project: null,
@@ -324,6 +363,10 @@ const SPECS = [
324
363
  status: "active",
325
364
  envOverride: "SUMMER_OPENCODE_CONFIG_FILE",
326
365
  format: "json-opencode",
366
+ remoteMcp: {
367
+ entry: (url) => ({ type: "remote", url }),
368
+ signIn: (name) => `Run \`opencode mcp auth ${name}\`; it opens summer.games to sign in.`,
369
+ },
327
370
  mcp: {
328
371
  user: (ctx) => join(xdgOrAppData(ctx), "opencode", "opencode.json"),
329
372
  project: project("opencode.json"),
@@ -342,6 +385,10 @@ const SPECS = [
342
385
  status: "active",
343
386
  envOverride: "SUMMER_ZED_CONFIG_FILE",
344
387
  format: "json-zed",
388
+ remoteMcp: {
389
+ entry: (url) => ({ url }),
390
+ signIn: (name) => `In Zed, open the agent settings and click Authenticate next to ${name}.`,
391
+ },
345
392
  mcp: {
346
393
  user: (ctx) => join(xdgOrAppData(ctx), "zed", "settings.json"),
347
394
  project: null,
@@ -356,6 +403,10 @@ const SPECS = [
356
403
  status: "active",
357
404
  envOverride: "SUMMER_KIRO_CONFIG_FILE",
358
405
  format: "json",
406
+ remoteMcp: {
407
+ entry: (url) => ({ url }),
408
+ signIn: (name) => `Kiro opens summer.games to sign in when ${name} connects (Kiro CLI: /mcp auth).`,
409
+ },
359
410
  mcp: { user: home(".kiro", "settings", "mcp.json"), project: project(".kiro", "settings", "mcp.json") },
360
411
  restart: "Kiro reloads MCP config on save; open the MCP Servers view to confirm summer-engine is connected.",
361
412
  skills: skillDirs(home(".kiro", "skills"), project(".kiro", "skills")),
@@ -367,6 +418,19 @@ const SPECS = [
367
418
  status: "active",
368
419
  envOverride: "SUMMER_GOOSE_CONFIG_FILE",
369
420
  format: "yaml-goose",
421
+ remoteMcp: {
422
+ entry: (url) => ({
423
+ type: "streamable_http",
424
+ name: "summer-games",
425
+ description: "Summer Games: friends, parties and messages as you",
426
+ uri: url,
427
+ enabled: true,
428
+ timeout: 300,
429
+ env_keys: [],
430
+ envs: {},
431
+ }),
432
+ signIn: (name) => `Goose opens summer.games to sign in when the ${name} extension starts.`,
433
+ },
370
434
  mcp: {
371
435
  user: (ctx) => ctx.os === "win32"
372
436
  ? join(appData(ctx), "Block", "goose", "config", "config.yaml")
@@ -383,6 +447,10 @@ const SPECS = [
383
447
  status: "active",
384
448
  envOverride: "SUMMER_HERMES_CONFIG_FILE",
385
449
  format: "yaml-hermes",
450
+ remoteMcp: {
451
+ entry: (url) => ({ url, auth: "oauth" }),
452
+ signIn: (name) => `Run \`hermes mcp login ${name}\`, or sign in on summer.games when Hermes first connects.`,
453
+ },
386
454
  mcp: { user: home(".hermes", "config.yaml"), project: null },
387
455
  restart: "Run /reload-mcp in Hermes Agent (or restart it).",
388
456
  skills: skillDirs(home(".hermes", "skills"), SHARED_SKILLS_PROJECT, {
@@ -397,6 +465,10 @@ const SPECS = [
397
465
  status: "active",
398
466
  envOverride: "SUMMER_TRAE_CONFIG_FILE",
399
467
  format: "json",
468
+ remoteMcp: {
469
+ entry: (url) => ({ url }),
470
+ signIn: (name) => `Trae opens summer.games to sign in when ${name} connects.`,
471
+ },
400
472
  mcp: {
401
473
  user: null,
402
474
  project: project(".trae", "mcp.json"),
@@ -413,6 +485,10 @@ const SPECS = [
413
485
  status: "active",
414
486
  envOverride: "SUMMER_QWEN_CODE_CONFIG_FILE",
415
487
  format: "json",
488
+ remoteMcp: {
489
+ entry: (url) => ({ httpUrl: url }),
490
+ signIn: (name) => `In Qwen Code, run /mcp auth ${name} and sign in.`,
491
+ },
416
492
  mcp: { user: home(".qwen", "settings.json"), project: project(".qwen", "settings.json") },
417
493
  restart: "Restart Qwen Code or run /mcp in a new session.",
418
494
  skills: skillDirs(home(".qwen", "skills"), project(".qwen", "skills")),
@@ -424,6 +500,10 @@ const SPECS = [
424
500
  status: "active",
425
501
  envOverride: "SUMMER_KIMI_CODE_CONFIG_FILE",
426
502
  format: "json",
503
+ remoteMcp: {
504
+ entry: (url) => ({ url, auth: "oauth" }),
505
+ signIn: (name) => `Run \`kimi mcp auth ${name}\`; it opens summer.games to sign in.`,
506
+ },
427
507
  mcp: { user: home(".kimi-code", "mcp.json"), project: project(".kimi-code", "mcp.json") },
428
508
  restart: "Restart Kimi Code CLI so it reconnects its MCP servers.",
429
509
  skills: sharedSkills({ legacyDirs: { user: home(".kimi-code", "skills"), project: project(".kimi-code", "skills") } }),
@@ -435,6 +515,10 @@ const SPECS = [
435
515
  status: "active",
436
516
  envOverride: "SUMMER_CRUSH_CONFIG_FILE",
437
517
  format: "json-crush",
518
+ remoteMcp: {
519
+ entry: (url) => ({ type: "http", url, oauth: true }),
520
+ signIn: (name) => `Crush opens summer.games to sign in when ${name} connects.`,
521
+ },
438
522
  mcp: {
439
523
  user: home(".config", "crush", "crushrc"),
440
524
  project: project(".crushrc"),
@@ -449,6 +533,10 @@ const SPECS = [
449
533
  status: "active",
450
534
  envOverride: "SUMMER_AMP_CONFIG_FILE",
451
535
  format: "json-amp",
536
+ remoteMcp: {
537
+ entry: (url) => ({ url }),
538
+ signIn: (name) => `Amp opens summer.games to sign in for ${name} when it starts.`,
539
+ },
452
540
  mcp: { user: (ctx) => join(xdgOrAppData(ctx), "amp", "settings.json"), project: null },
453
541
  restart: "Restart Amp so it reloads settings.json.",
454
542
  skills: sharedSkills({ legacyDirs: { user: (ctx) => join(xdgOrAppData(ctx), "amp", "skills"), project: null } }),
@@ -460,6 +548,10 @@ const SPECS = [
460
548
  status: "active",
461
549
  envOverride: "SUMMER_FACTORY_CONFIG_FILE",
462
550
  format: "json-stdio",
551
+ remoteMcp: {
552
+ entry: (url) => ({ type: "http", url }),
553
+ signIn: (name) => `In Droid, run /mcp, choose ${name} and Authenticate.`,
554
+ },
463
555
  mcp: { user: home(".factory", "mcp.json"), project: project(".factory", "mcp.json") },
464
556
  restart: "Restart droid or run /mcp in the active session.",
465
557
  skills: sharedSkills({ legacyDirs: { user: home(".factory", "skills"), project: project(".factory", "skills") } }),
@@ -471,6 +563,10 @@ const SPECS = [
471
563
  status: "active",
472
564
  envOverride: "SUMMER_JUNIE_CONFIG_FILE",
473
565
  format: "json",
566
+ remoteMcp: {
567
+ entry: (url) => ({ url }),
568
+ signIn: (name) => `In Junie, run /mcp, open ${name} and choose Authorize.`,
569
+ },
474
570
  mcp: { user: home(".junie", "mcp", "mcp.json"), project: project(".junie", "mcp", "mcp.json") },
475
571
  restart: "Restart the JetBrains IDE so Junie reloads its MCP config.",
476
572
  skills: null,
@@ -483,6 +579,10 @@ const SPECS = [
483
579
  status: "active",
484
580
  envOverride: "SUMMER_WARP_CONFIG_FILE",
485
581
  format: "json",
582
+ remoteMcp: {
583
+ entry: (url) => ({ url }),
584
+ signIn: (name) => `Warp opens summer.games to sign in when ${name} starts.`,
585
+ },
486
586
  mcp: { user: home(".warp", ".mcp.json"), project: project(".warp", ".mcp.json") },
487
587
  restart: "Warp detects the file and spawns the server; check Settings > AI > MCP servers.",
488
588
  skills: sharedSkills({ legacyDirs: { user: home(".warp", "skills"), project: project(".warp", "skills") } }),
@@ -494,6 +594,7 @@ const SPECS = [
494
594
  status: "active",
495
595
  envOverride: "SUMMER_ROVO_DEV_CONFIG_FILE",
496
596
  format: "json-transport",
597
+ // No Summer Games entry: Rovo Dev CLI has no OAuth sign-in for remote MCP servers (headers only).
497
598
  mcp: { user: home(".rovodev", "mcp.json"), project: null },
498
599
  restart: "Restart Rovo Dev CLI so it reconnects its MCP servers.",
499
600
  skills: sharedSkills({ legacyDirs: { user: home(".rovodev", "skills"), project: project(".rovodev", "skills") } }),
@@ -505,6 +606,7 @@ const SPECS = [
505
606
  status: "active",
506
607
  envOverride: "SUMMER_QODER_CONFIG_FILE",
507
608
  format: "json",
609
+ // No Summer Games entry: Qoder's remote-server fields in settings.json are not documented.
508
610
  mcp: { user: home(".qoder", "settings.json"), project: project(".mcp.json") },
509
611
  restart: "Restart Qoder CLI so it reloads its MCP settings (the Qoder IDE manages servers in its UI).",
510
612
  skills: skillDirs(home(".qoder", "skills"), project(".qoder", "skills")),
@@ -516,6 +618,10 @@ const SPECS = [
516
618
  status: "active",
517
619
  envOverride: "SUMMER_GROK_BUILD_CONFIG_FILE",
518
620
  format: "toml",
621
+ remoteMcp: {
622
+ entry: (url) => ({ url }),
623
+ signIn: (name) => `In Grok Build, run /mcps, select ${name} and press i to sign in.`,
624
+ },
519
625
  mcp: { user: home(".grok", "config.toml"), project: project(".grok", "config.toml") },
520
626
  restart: "Restart Grok Build so it reloads config.toml (it also reads ~/.claude.json and .cursor/mcp.json).",
521
627
  skills: sharedSkills({ legacyDirs: { user: home(".grok", "skills"), project: project(".grok", "skills") } }),
@@ -527,6 +633,10 @@ const SPECS = [
527
633
  status: "active",
528
634
  envOverride: "SUMMER_MISTRAL_VIBE_CONFIG_FILE",
529
635
  format: "toml-array",
636
+ remoteMcp: {
637
+ entry: (url) => ({ name: "summer-games", transport: "streamable-http", url, auth: { type: "oauth", scopes: [] } }),
638
+ signIn: (name) => `In Vibe, run /mcp login ${name}.`,
639
+ },
530
640
  mcp: { user: home(".vibe", "config.toml"), project: project(".vibe", "config.toml") },
531
641
  restart: "Restart Vibe so it reloads config.toml.",
532
642
  skills: skillDirs(home(".vibe", "skills"), SHARED_SKILLS_PROJECT, { legacyDirs: { user: home(".vibe", "skills"), project: project(".vibe", "skills") } }),
@@ -538,6 +648,7 @@ const SPECS = [
538
648
  status: "active",
539
649
  envOverride: "SUMMER_LM_STUDIO_CONFIG_FILE",
540
650
  format: "json",
651
+ // No Summer Games entry: LM Studio's MCP client registration is closed and unverified.
541
652
  mcp: { user: home(".lmstudio", "mcp.json"), project: null },
542
653
  restart: "Open LM Studio, toggle on the summer-engine MCP server in the Program tab, and raise the loaded model's context length to 32k or higher.",
543
654
  skills: null,
@@ -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.5.0",
4
+ "version": "3.5.1",
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": {
@@ -24,6 +24,12 @@ never sits beside project-scope skills. `--scope project` moves both;
24
24
  `--recommended` installs only the recommended subset. Clients whose MCP config
25
25
  is user-only fall back to user scope with a warning.
26
26
 
27
+ For every client with `remoteMcp` in `agent-table.ts`, setup also writes the
28
+ remote `summer-games` server (`https://mcp.summer.games/mcp`) into the same
29
+ file, in that client's entry shape, and prints its one sign-in step. A client
30
+ gets `remoteMcp` only after its OAuth redirect is checked against what
31
+ api.summer.games accepts; the table comments name why the rest have none.
32
+
27
33
  Generated root files (`.claude-plugin/plugin.json`, `gemini-extension.json`,
28
34
  `.mcp.json`, …) are build artifacts of `integrations/<agent>` + `library/`.
29
35
  Their `_generated` banner says so. CI `--check` fails on any drift between
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "summer-engine",
3
- "version": "3.5.0",
3
+ "version": "3.5.1",
4
4
  "description": "Local Summer CLI and MCP server for Summer Engine. Install and run the engine, connect Claude Code, Cursor, Codex, Gemini, and other agents, and build real games with bundled skills, hooks, and plugins.",
5
5
  "keywords": [
6
6
  "summer-engine",
@@ -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.5.0",
4
+ "version": "3.5.1",
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": {
@@ -11,7 +11,7 @@
11
11
  {
12
12
  "name": "summer",
13
13
  "source": "./",
14
- "version": "3.5.0",
14
+ "version": "3.5.1",
15
15
  "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 105-tool MCP bridge to the local desktop app.",
16
16
  "author": {
17
17
  "name": "Summer Engine",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "_generated": "GENERATED from integrations/claude — do not edit; npm run generate:registry",
4
4
  "name": "summer",
5
- "version": "3.5.0",
5
+ "version": "3.5.1",
6
6
  "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 105-tool MCP bridge to the local desktop app.",
7
7
  "author": {
8
8
  "name": "Summer Engine",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "_generated": "GENERATED from integrations/codex — do not edit; npm run generate:registry",
3
3
  "name": "summer",
4
- "version": "3.5.0",
4
+ "version": "3.5.1",
5
5
  "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 105-tool MCP bridge. Build games by talking.",
6
6
  "author": {
7
7
  "name": "Summer Engine",
@@ -3,7 +3,7 @@
3
3
  "_generated": "GENERATED from integrations/cursor — do not edit; npm run generate:registry",
4
4
  "name": "summer",
5
5
  "displayName": "Summer",
6
- "version": "3.5.0",
6
+ "version": "3.5.1",
7
7
  "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 105-tool MCP bridge to the local desktop app.",
8
8
  "author": {
9
9
  "name": "Summer Engine",
@@ -2,7 +2,7 @@
2
2
  "_generated": "GENERATED from integrations/factory — do not edit; npm run generate:registry",
3
3
  "name": "summer",
4
4
  "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 105-tool MCP bridge to the local desktop app.",
5
- "version": "3.5.0",
5
+ "version": "3.5.1",
6
6
  "author": {
7
7
  "name": "Summer Engine",
8
8
  "email": "founders@summerengine.com",