summer-engine 3.5.0 → 3.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -1
  3. package/.codex-plugin/plugin.json +2 -1
  4. package/.cursor-plugin/plugin.json +2 -1
  5. package/.factory-plugin/plugin.json +2 -1
  6. package/CHANGELOG.md +23 -0
  7. package/README.md +3 -3
  8. package/commands/summer.md +1 -0
  9. package/dist/cli/commands/setup.js +2 -2
  10. package/dist/core/capabilities/publish-build.d.ts +4 -0
  11. package/dist/core/capabilities/publish-build.js +67 -34
  12. package/dist/core/capabilities/runtime-control.d.ts +2 -2
  13. package/dist/installer/agent-config.d.ts +5 -5
  14. package/dist/installer/agent-config.js +96 -32
  15. package/dist/installer/agent-table.d.ts +7 -6
  16. package/dist/installer/agent-table.js +117 -6
  17. package/dist/mcp/hosted-mount.js +2 -1
  18. package/dist/mcp/provider-safe-schema.d.ts +10 -0
  19. package/dist/mcp/provider-safe-schema.js +96 -0
  20. package/dist/mcp/server.js +3 -0
  21. package/gemini-extension.json +2 -1
  22. package/integrations/README.md +6 -0
  23. package/library/skills/README.md +3 -2
  24. package/library/skills/export-and-ship/SKILL.md +2 -0
  25. package/library/skills/make-game/SKILL.md +21 -2
  26. package/library/skills/make-game/resource.yaml +1 -0
  27. package/library/skills/multiplayer/SKILL.md +3 -1
  28. package/library/skills/multiplayer/resource.yaml +1 -0
  29. package/library/skills/multiplayer-movement/SKILL.md +1 -1
  30. package/library/skills/multiplayer-movement/files.md +2 -3
  31. package/library/skills/multiplayer-project/SKILL.md +20 -4
  32. package/library/skills/multiplayer-project/files.md +2 -3
  33. package/library/skills/multiplayer-publish/SKILL.md +86 -5
  34. package/library/skills/multiplayer-publish/resource.yaml +1 -0
  35. package/library/skills/multiplayer-state/SKILL.md +1 -1
  36. package/library/skills/multiplayer-state/files.md +2 -3
  37. package/library/skills/multiplayer-testing/SKILL.md +13 -0
  38. package/library/skills/multiplayer-testing/resource.yaml +1 -0
  39. package/library/skills/publish-your-game/SKILL.md +2 -0
  40. package/library/skills/release-readiness/SKILL.md +298 -0
  41. package/library/skills/release-readiness/resource.yaml +41 -0
  42. package/library/skills/summer-parties/SKILL.md +1 -1
  43. package/package.json +1 -1
  44. package/registry/generated/counts.json +2 -2
  45. package/registry/generated/gemini-extension.json +2 -1
  46. package/registry/generated/index.json +63 -11
  47. package/registry/generated/marketplace.claude.json +1 -1
  48. package/registry/generated/plugin.claude.json +2 -1
  49. package/registry/generated/plugin.codex.json +2 -1
  50. package/registry/generated/plugin.cursor.json +2 -1
  51. package/registry/generated/plugin.factory.json +2 -1
  52. package/registry/generated/skills-index.md +3 -2
  53. package/registry/generated/skills-registry.json +14 -0
@@ -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,
@@ -3,6 +3,7 @@ import { z } from "zod";
3
3
  import { connectHostedMcp } from "../core/hosted-mcp.js";
4
4
  import { getStoreAccessToken, OAuthError, resolveMcpResourceUrl } from "../core/oauth.js";
5
5
  import { appendMcpLogEvent } from "../core/mcp-log.js";
6
+ import { providerSafeSchema } from "./provider-safe-schema.js";
6
7
  /**
7
8
  * One Summer Engine MCP. The cloud tools a creator's agent needs to publish
8
9
  * (projects, store page, build upload, submit for the owner's approval, the
@@ -73,7 +74,7 @@ export function prepareHostedMount(server, deps = defaultHostedMountDependencies
73
74
  ...result,
74
75
  tools: result.tools.map((tool) => {
75
76
  const hosted = schemas.get(tool.name);
76
- return hosted ? { ...tool, inputSchema: hosted.inputSchema, ...(hosted.annotations ? { annotations: hosted.annotations } : {}), ...(hosted.title ? { title: hosted.title } : {}) } : tool;
77
+ return hosted ? { ...tool, inputSchema: providerSafeSchema(hosted.inputSchema), ...(hosted.annotations ? { annotations: hosted.annotations } : {}), ...(hosted.title ? { title: hosted.title } : {}) } : tool;
77
78
  }),
78
79
  };
79
80
  });
@@ -0,0 +1,10 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ /** String formats OpenAI strict mode accepts; other providers accept no more. */
3
+ export declare const STRICT_PROVIDER_FORMATS: Set<string>;
4
+ export declare function providerSafeSchema<T>(schema: T): T;
5
+ /**
6
+ * Wrap the SDK's tools/list so every tool's inputSchema leaves the server in
7
+ * the provider-safe form. Call after the first registerTool (the SDK installs
8
+ * its tools/list handler then).
9
+ */
10
+ export declare function serveProviderSafeToolSchemas(server: McpServer): void;
@@ -0,0 +1,96 @@
1
+ import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
2
+ /** String formats OpenAI strict mode accepts; other providers accept no more. */
3
+ export const STRICT_PROVIDER_FORMATS = new Set(["date-time", "time", "date", "duration", "email", "hostname", "ipv4", "ipv6", "uuid"]);
4
+ // Where a schema holds subschemas: one, a list, or a name -> schema map.
5
+ const SCHEMA_KEYS = new Set(["items", "additionalItems", "additionalProperties", "contains", "not", "if", "then", "else", "propertyNames", "unevaluatedItems", "unevaluatedProperties"]);
6
+ const SCHEMA_LIST_KEYS = new Set(["anyOf", "oneOf", "allOf", "prefixItems"]);
7
+ const SCHEMA_MAP_KEYS = new Set(["properties", "patternProperties", "dependentSchemas", "$defs", "definitions"]);
8
+ const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
9
+ function resolvePointer(root, ref) {
10
+ let node = root;
11
+ for (const raw of ref.slice(2).split("/")) {
12
+ if (raw === "")
13
+ continue;
14
+ const key = raw.replace(/~1/g, "/").replace(/~0/g, "~");
15
+ if (Array.isArray(node))
16
+ node = node[Number(key)];
17
+ else if (isObject(node))
18
+ node = node[key];
19
+ else
20
+ return undefined;
21
+ }
22
+ return node;
23
+ }
24
+ function uniqueSchemas(schemas) {
25
+ const seen = new Map();
26
+ for (const schema of schemas)
27
+ seen.set(JSON.stringify(schema), schema);
28
+ return [...seen.values()];
29
+ }
30
+ function rewrite(schema, root, expanding) {
31
+ if (!isObject(schema))
32
+ return schema;
33
+ const child = (value) => rewrite(value, root, expanding);
34
+ const { $ref, ...rest } = schema;
35
+ if (typeof $ref === "string" && ($ref === "#" || $ref.startsWith("#/"))) {
36
+ // A recursive shape cannot be inlined; accept any value there and let zod validate it.
37
+ const target = expanding.includes($ref) ? {} : rewrite(resolvePointer(root, $ref), root, [...expanding, $ref]);
38
+ return { ...(isObject(target) ? target : {}), ...child(rest) };
39
+ }
40
+ const out = {};
41
+ for (const [key, value] of Object.entries(schema)) {
42
+ if (key === "additionalItems" || key === "prefixItems")
43
+ continue;
44
+ if (SCHEMA_KEYS.has(key))
45
+ out[key] = Array.isArray(value) ? value.map(child) : child(value);
46
+ else if (SCHEMA_LIST_KEYS.has(key) && Array.isArray(value))
47
+ out[key] = value.map(child);
48
+ else if (SCHEMA_MAP_KEYS.has(key) && isObject(value))
49
+ out[key] = Object.fromEntries(Object.entries(value).map(([name, sub]) => [name, child(sub)]));
50
+ else
51
+ out[key] = value;
52
+ }
53
+ if (Array.isArray(schema.type) && !("anyOf" in out)) {
54
+ delete out.type;
55
+ out.anyOf = schema.type.map((type) => ({ type }));
56
+ }
57
+ if (typeof out.format === "string" && !STRICT_PROVIDER_FORMATS.has(out.format))
58
+ delete out.format;
59
+ if (isObject(out.propertyNames) && Object.keys(out.propertyNames).length === 1 && out.propertyNames.type === "string")
60
+ delete out.propertyNames;
61
+ const tuple = Array.isArray(schema.prefixItems) ? schema.prefixItems : Array.isArray(schema.items) ? schema.items : null;
62
+ if (tuple) {
63
+ const extra = Array.isArray(schema.prefixItems) ? schema.items : schema.additionalItems;
64
+ const open = extra !== undefined && extra !== false;
65
+ const members = uniqueSchemas([...tuple, ...(isObject(extra) ? [extra] : [])].map(child));
66
+ out.items = members.length === 1 ? members[0] : members.length === 0 ? {} : { anyOf: members };
67
+ out.minItems ??= tuple.length;
68
+ if (!open)
69
+ out.maxItems ??= tuple.length;
70
+ }
71
+ return out;
72
+ }
73
+ export function providerSafeSchema(schema) {
74
+ const out = rewrite(schema, schema, []);
75
+ if (isObject(out)) {
76
+ // Every local $ref is inlined, so the definition tables are dead weight.
77
+ delete out.definitions;
78
+ delete out.$defs;
79
+ }
80
+ return out;
81
+ }
82
+ /**
83
+ * Wrap the SDK's tools/list so every tool's inputSchema leaves the server in
84
+ * the provider-safe form. Call after the first registerTool (the SDK installs
85
+ * its tools/list handler then).
86
+ */
87
+ export function serveProviderSafeToolSchemas(server) {
88
+ const handlers = server.server._requestHandlers;
89
+ const listTools = handlers.get("tools/list");
90
+ if (!listTools)
91
+ return;
92
+ server.server.setRequestHandler(ListToolsRequestSchema, async (request, extra) => {
93
+ const result = await listTools(request, extra);
94
+ return { ...result, tools: result.tools.map((tool) => ({ ...tool, inputSchema: providerSafeSchema(tool.inputSchema) })) };
95
+ });
96
+ }
@@ -27,6 +27,7 @@ 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
29
  import { prepareHostedMount } from "./hosted-mount.js";
30
+ import { serveProviderSafeToolSchemas } from "./provider-safe-schema.js";
30
31
  import { buildBootDriftNotice, fetchLatestRegistryVersion, } from "../installer/version-check.js";
31
32
  import { TOOLKIT_VERSION as version } from "../core/version.js";
32
33
  let processDiagnosticsInstalled = false;
@@ -329,6 +330,8 @@ export function createMcpServer() {
329
330
  // The playbook is also an MCP prompt so prompt-surfacing hosts get it
330
331
  // natively (same content as the summer_get_agent_playbook tool).
331
332
  registerPlaybookPrompt(server);
333
+ // Strict providers (Kimi, OpenAI strict) reject tuple and $ref schemas.
334
+ serveProviderSafeToolSchemas(server);
332
335
  return { server, getRegisteredToolCount };
333
336
  }
334
337
  export async function startMcpServer(options = {}) {
@@ -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.2",
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": {
@@ -93,6 +93,7 @@
93
93
  "./library/skills/psx-retro-rendering/",
94
94
  "./library/skills/publish-your-game/",
95
95
  "./library/skills/realtime-wet-surfaces/",
96
+ "./library/skills/release-readiness/",
96
97
  "./library/skills/remote-deploy/",
97
98
  "./library/skills/retarget/",
98
99
  "./library/skills/running-in-the-cloud/",
@@ -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
@@ -1,6 +1,6 @@
1
1
  # Summer skills
2
2
 
3
- > 122 skills, grouped by their primary domain. **Generated** by `npm run generate:registry` from each skill's `resource.yaml`; `--check` fails when this file and the library disagree. Do not edit by hand.
3
+ > 123 skills, grouped by their primary domain. **Generated** by `npm run generate:registry` from each skill's `resource.yaml`; `--check` fails when this file and the library disagree. Do not edit by hand.
4
4
 
5
5
  The folder is flat on purpose (`docs/design/DECISIONS.md`, D3): a skill usually belongs to several domains, so categories live in each skill's `facets.domains` and this index is rendered from them. The first domain listed is the primary one. `summer skills list --by-domain` prints the same grouping; `summer skills info <slug>` shows one skill.
6
6
 
@@ -18,7 +18,7 @@ Status: **stable** unless marked; *preview* = not yet exercised in-engine by the
18
18
  - [character-controller](#character-controller) (2)
19
19
  - [creator](#creator) (4)
20
20
  - [debug](#debug) (1)
21
- - [deployment](#deployment) (2)
21
+ - [deployment](#deployment) (3)
22
22
  - [editor](#editor) (1)
23
23
  - [gameplay](#gameplay) (6)
24
24
  - [level-design](#level-design) (2)
@@ -147,6 +147,7 @@ Status: **stable** unless marked; *preview* = not yet exercised in-engine by the
147
147
  | skill | when to use | also |
148
148
  |---|---|---|
149
149
  | [export-and-ship](./export-and-ship/SKILL.md) ★ | Assess and prepare a game export: inventory installed templates and targets, validate release assets and config, produce supported builds after approval. | project |
150
+ | [*release-readiness*](./release-readiness/SKILL.md) (preview) | Before releasing a Summer game version, prove players can play it: server scene, online join, saved progress, no fixed Game ID, a released Summer compiles it. | multiplayer, playtest |
150
151
  | [remote-deploy](./remote-deploy/SKILL.md) ★ | Run or test the game on a real device — phone, tablet, another computer — via the Remote Deploy button, runnable export presets, and on-device debugging. | project |
151
152
 
152
153
  ## editor
@@ -298,6 +298,8 @@ Per platform, point the user at the correct upload path:
298
298
  - **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.
299
299
  - 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.
300
300
  - Hosted games: if `summer_export_game` fails with "… is in the source graph's authority_engine domain, which the client pack leaves out", an authority root in `source-domains.json` holds a file the client loads (often the composition `.tres`). Narrow that root to the server-only files, or move the shared file under a `shared` path, then export again. Every exporter message is in the `multiplayer-publish` skill's export-errors.md; the folder rules are in `multiplayer-project`.
301
+ - Every summer.games bundle: export with the released Summer editor from the download page, never a development build, and export again after updating Summer. Otherwise its scripts can fail to compile on players' devices and the game opens to an empty window, though every upload check passes. `release-readiness` check 7 proves it.
302
+ - Hosted games, before the upload: a clean export does not prove players can join. Run `release-readiness` on the `.zip`: the bundle's `server.pck`, started with no scene, must load the authority scene (the server preset needs the `summer_authority` custom feature), and `Summer.initialize` must pass no hard-coded Game ID. Otherwise every online join fails with `world_start_timeout`.
301
303
  - Store page art: key art, covers and icons carry no text, logos or UI (the store page shows the title). Screenshots are real gameplay and may show the game's own HUD; add no captions, logos or overlays. Capture them with `summer_capture_gameplay` (offscreen, no editor needed; `resolution` `1920x1080`, or `1080x1920` for portrait; get past a title menu with the game's own flags in `args`, e.g. `["--autostart"]`, or `steps` such as `[{"press":"Play"},{"wait":3000},{"shot":true}]`), or `summer_screenshot` target `game` while the editor runs the game.
302
304
  - **Steam:** `steamcmd` + Steam Pipe (`builder_app_xxx.vdf`). Set up depots first in Steamworks.
303
305
  - **itch.io:** `butler push <path> <user>/<game>:<channel>` (e.g. `butler push build/win <user>/mygame:windows`).
@@ -47,6 +47,7 @@ These rules from the playbooks apply to every genre:
47
47
  - **2D scales to the viewport.** Set `display/window/stretch/mode = canvas_items` and `stretch/aspect = expand`; UI uses Control anchors; code-positioned nodes compute from `get_viewport_rect().size`. Never hardcode 1920x1080.
48
48
  - **Playable slice, not tech demo.** Name the core loop in one sentence; every input gets feedback (tween, sound, particle, flash, shake); progression is visible; a restart exists. The bar is "feels good for 30 seconds".
49
49
  - **Fast first draft when someone is waiting.** The first playable version uses shapes, the palette and any template art, plus at most one or two generated assets the person specifically asked for; generation takes minutes, so it never blocks the first play. Say plainly that it is a first draft to get a feel for the game, that the art is not final, and ask if this is the direction they want. Generate the rest of the art in later passes.
50
+ - **A way back to the title.** From inside play, a pause menu or a **Leave** button returns to the title screen without quitting the app.
50
51
  - **Text the default font can show.** No emoji or symbols (▲ ◀ ★ ♥ ✓) in UI text: they render as boxes with hex codes. Use words or icons; see `ui-basics`.
51
52
 
52
53
  ## The Pipeline
@@ -95,6 +96,12 @@ This phase doesn't have a dedicated skill yet — do it inline. Read `.summer/Ga
95
96
  2. **Core mechanics** — ranked, max 3 (the brief enforces this; respect it)
96
97
  3. **Cut list** — what's explicitly NOT in scope
97
98
 
99
+ If the game has online play, the plan also fixes three product defaults; each one was missed by a real release:
100
+
101
+ - **Online play is reachable by default.** A new player gets to it from the title without changing a setting. Hiding it behind a setting or a grown-ups gate is the owner's explicit decision, recorded in the plan, and it means someone joins online before every release.
102
+ - **Progress is saved by the server.** Online progress goes through authority player data (`summer-player-data`), not only the player's local files.
103
+ - **A way back to the title** from inside an online and an offline session.
104
+
98
105
  Save the plan to `.summer/build-plan.md`. Format:
99
106
 
100
107
  ```markdown
@@ -236,8 +243,10 @@ Only if `summer_get_diagnostics` flags rendering or physics hotspots. Don't opti
236
243
  1. `play` from main scene.
237
244
  2. Walk every mechanic from the plan.
238
245
  3. Hit the win condition (or lose condition).
239
- 4. `summer_get_diagnostics` clean.
240
- 5. `debug` until clean if not.
246
+ 4. Return to the title from inside play, then start again.
247
+ 5. Online games: run Local Play (`multiplayer-testing`) and reach online play from the title as a new player, with default settings.
248
+ 6. `summer_get_diagnostics` clean.
249
+ 7. `debug` until clean if not.
241
250
 
242
251
  **Don't skip this even if every mechanic worked individually.** Integration bugs hide between mechanics.
243
252
 
@@ -249,6 +258,14 @@ Skill: export-and-ship
249
258
 
250
259
  Pre-flight checklist (icon, store banners, build config) before producing release builds for the targets in the brief.
251
260
 
261
+ Before any version reaches players, run:
262
+
263
+ ```
264
+ Skill: release-readiness
265
+ ```
266
+
267
+ It checks the file you upload, not the editor: a hosted game's `server.pck` starts its authority scene, `Summer.initialize` passes no hard-coded Game ID, online play is reachable, progress is saved by the server, and players can get back to the title. Then, after publishing, the user joins online with the released version. Report each check as pass, fail, not applicable or not run. Hosted games publish through `multiplayer-publish`; store pages through `publish-your-game`.
268
+
252
269
  If the brief said "jam" or "prototype", skip this phase. If the brief said "game I want to release", run it.
253
270
 
254
271
  ## Anti-Patterns
@@ -260,6 +277,8 @@ If the brief said "jam" or "prototype", skip this phase. If the brief said "game
260
277
  | Stack art direction on top of unfinished mechanics | Art changes are reversible only if the underlying scene structure is stable. Get mechanics done first. |
261
278
  | Treat "polish" as required | If the brief says "jam in 2 days", polish is a cut item. Respect the brief. |
262
279
  | Run all phases without confirming | The user owns scope. Stop at every checkpoint. |
280
+ | Ship online play behind a setting nobody changes | Hosted join is never exercised, so it breaks without anyone noticing. Reachable by default, or the owner joins online before every release. |
281
+ | Hard-code a Game ID in `Summer.initialize` | The summer.games bundle has no `gameId`; a different ID fails on the hosted server and every online join times out. Pass no ID. |
263
282
  | Build a fully featured menu before there's a playable mechanic | Menu work is endless polish; player-loop work is the actual game. Mechanics first. |
264
283
  | Fall back to "let me try a few things" mid-pipeline | If you don't know what to do, name the phase and ask the user, don't improvise around the structure. |
265
284
 
@@ -21,6 +21,7 @@ related:
21
21
  skills:
22
22
  - skill/audio-direction
23
23
  - skill/export-and-ship
24
+ - skill/release-readiness
24
25
  - skill/design-mechanic
25
26
  - skill/tune-performance
26
27
  - skill/art-direction
@@ -84,7 +84,7 @@ the sender's verified Session. Events are fire-and-forget.
84
84
 
85
85
  | Word | Meaning |
86
86
  |---|---|
87
- | Game | Your game on Summer; its id is `gameId` in `summer.build.json` and the argument to `Summer.initialize` |
87
+ | Game | Your game on Summer; its id is `gameId` in `summer.build.json`. `Summer.initialize()` takes no ID: Summer knows which game is running |
88
88
  | World | One running match or shared place, simulated by one authority |
89
89
  | WorldDefinition | `world.json`: which scenes run the client and the authority, lifetime, persistence |
90
90
  | Queue | How players find a World; declared in `summer.build.json` |
@@ -109,6 +109,8 @@ Each skill continues the same small game, so their code fits together:
109
109
  what a passing run must show.
110
110
  5. **`multiplayer-publish`**: export the summer.games bundle and upload it as
111
111
  a Build.
112
+ 6. **`release-readiness`**: before players get a version, prove the server
113
+ starts its authority scene and a real online join works.
112
114
 
113
115
  ## Add Summer services
114
116