summer-engine 3.4.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +5 -1
  3. package/.codex-plugin/plugin.json +5 -1
  4. package/.cursor-plugin/plugin.json +5 -1
  5. package/.factory-plugin/plugin.json +5 -1
  6. package/.opencode/INSTALL.md +8 -8
  7. package/.opencode/plugins/summer.js +2 -2
  8. package/AGENTS.md +7 -7
  9. package/CHANGELOG.md +37 -0
  10. package/GEMINI.md +1 -1
  11. package/NOTICE +1 -1
  12. package/README.md +35 -35
  13. package/assets/autopilot/run.sh +2 -2
  14. package/assets/navigation/web-routes.json +1 -1
  15. package/commands/gameskill.md +2 -2
  16. package/commands/summer.md +25 -25
  17. package/dist/cli/commands/open.d.ts +1 -1
  18. package/dist/cli/commands/run.d.ts +1 -1
  19. package/dist/cli/commands/run.js +1 -1
  20. package/dist/cli/commands/setup.js +24 -4
  21. package/dist/cli/commands/skills.js +2 -2
  22. package/dist/core/api-client.d.ts +5 -6
  23. package/dist/core/api-client.js +12 -15
  24. package/dist/core/auth.js +2 -2
  25. package/dist/core/capabilities/asset-import.d.ts +0 -1
  26. package/dist/core/capabilities/camera-view.d.ts +1 -1
  27. package/dist/core/capabilities/camera-view.js +2 -2
  28. package/dist/core/capabilities/capture-gameplay.d.ts +72 -7
  29. package/dist/core/capabilities/capture-gameplay.js +386 -25
  30. package/dist/core/capabilities/connect-signal.d.ts +1 -2
  31. package/dist/core/capabilities/connect-signal.js +1 -2
  32. package/dist/core/capabilities/console-read.d.ts +2 -2
  33. package/dist/core/capabilities/console-read.js +2 -2
  34. package/dist/core/capabilities/engine-receipt.d.ts +5 -7
  35. package/dist/core/capabilities/engine-receipt.js +7 -9
  36. package/dist/core/capabilities/export-engine-version.d.ts +7 -0
  37. package/dist/core/capabilities/export-engine-version.js +9 -0
  38. package/dist/core/capabilities/export-game.d.ts +4 -0
  39. package/dist/core/capabilities/export-game.js +53 -10
  40. package/dist/core/capabilities/export-presets.d.ts +1 -1
  41. package/dist/core/capabilities/export-presets.js +1 -1
  42. package/dist/core/capabilities/fabricate-mesh.d.ts +2 -3
  43. package/dist/core/capabilities/fabricate-mesh.js +2 -3
  44. package/dist/core/capabilities/frame-quality.d.ts +4 -4
  45. package/dist/core/capabilities/frame-quality.js +4 -4
  46. package/dist/core/capabilities/grep.d.ts +1 -1
  47. package/dist/core/capabilities/grep.js +1 -1
  48. package/dist/core/capabilities/image-generation.d.ts +12 -2
  49. package/dist/core/capabilities/image-generation.js +31 -2
  50. package/dist/core/capabilities/navigation/open.d.ts +1 -1
  51. package/dist/core/capabilities/navigation/open.js +1 -1
  52. package/dist/core/capabilities/navigation/render-product-map.js +1 -1
  53. package/dist/core/capabilities/navigation/targets.js +2 -2
  54. package/dist/core/capabilities/placement-batch.d.ts +1 -1
  55. package/dist/core/capabilities/placement-batch.js +2 -2
  56. package/dist/core/capabilities/play-determinism.d.ts +1 -1
  57. package/dist/core/capabilities/play-determinism.js +1 -1
  58. package/dist/core/capabilities/publish-build.d.ts +1 -1
  59. package/dist/core/capabilities/publish-build.js +21 -9
  60. package/dist/core/capabilities/publish-download.d.ts +1 -2
  61. package/dist/core/capabilities/publish-download.js +2 -3
  62. package/dist/core/capabilities/replace-node.d.ts +1 -3
  63. package/dist/core/capabilities/replace-node.js +1 -3
  64. package/dist/core/capabilities/runtime-control.d.ts +5 -7
  65. package/dist/core/capabilities/runtime-control.js +7 -9
  66. package/dist/core/capabilities/scene-batch.d.ts +1 -1
  67. package/dist/core/capabilities/scene-batch.js +1 -1
  68. package/dist/core/capabilities/scene-kind.js +1 -1
  69. package/dist/core/capabilities/scene-readback.d.ts +3 -4
  70. package/dist/core/capabilities/scene-readback.js +3 -4
  71. package/dist/core/capabilities/scene-script.d.ts +2 -2
  72. package/dist/core/capabilities/scene-script.js +2 -2
  73. package/dist/core/capabilities/seeing/shot-store.d.ts +1 -1
  74. package/dist/core/capabilities/seeing/shot-store.js +1 -1
  75. package/dist/core/capabilities/summer-bundle.d.ts +11 -3
  76. package/dist/core/capabilities/summer-bundle.js +21 -3
  77. package/dist/core/capabilities/surface-snap.d.ts +1 -1
  78. package/dist/core/capabilities/surface-snap.js +1 -1
  79. package/dist/core/capabilities/tool-dispatch.js +14 -3
  80. package/dist/core/capabilities/ui-control.d.ts +1 -2
  81. package/dist/core/capabilities/ui-control.js +1 -2
  82. package/dist/core/capabilities/world-snapshot.d.ts +1 -1
  83. package/dist/core/capabilities/world-snapshot.js +1 -1
  84. package/dist/core/capability-skew.d.ts +2 -3
  85. package/dist/core/capability-skew.js +4 -5
  86. package/dist/core/feedback/client.js +1 -1
  87. package/dist/core/launch-posture.d.ts +3 -3
  88. package/dist/core/launch-posture.js +3 -3
  89. package/dist/core/library-read.d.ts +7 -0
  90. package/dist/core/library-read.js +60 -9
  91. package/dist/core/library-search.d.ts +2 -0
  92. package/dist/core/templates.d.ts +1 -2
  93. package/dist/core/templates.js +1 -2
  94. package/dist/installer/agent-config.d.ts +25 -0
  95. package/dist/installer/agent-config.js +114 -3
  96. package/dist/installer/agent-table.d.ts +12 -0
  97. package/dist/installer/agent-table.js +9 -0
  98. package/dist/mcp/server.d.ts +2 -2
  99. package/dist/mcp/server.js +2 -2
  100. package/dist/mcp/tools/creator-tools.js +33 -4
  101. package/dist/mcp/tools/debug-tools.js +1 -1
  102. package/dist/mcp/tools/generate-tools.js +18 -37
  103. package/dist/mcp/tools/library-tools.d.ts +2 -1
  104. package/dist/mcp/tools/library-tools.js +1 -0
  105. package/dist/mcp/tools/navigation-tools.d.ts +1 -1
  106. package/dist/mcp/tools/navigation-tools.js +1 -1
  107. package/dist/mcp/tools/spatial-tools.js +1 -1
  108. package/dist/mcp/tools/visual-tools.js +2 -2
  109. package/dist/mcp/tools/with-engine.d.ts +1 -2
  110. package/dist/mcp/tools/with-engine.js +1 -2
  111. package/gemini-extension.json +5 -1
  112. package/hooks/session-start.sh +2 -2
  113. package/integrations/README.md +70 -76
  114. package/integrations/claude/README.md +0 -3
  115. package/integrations/codex/README.md +1 -2
  116. package/integrations/copilot-jetbrains/README.md +1 -1
  117. package/integrations/cursor/README.md +1 -2
  118. package/integrations/factory/README.md +15 -11
  119. package/integrations/mistral-vibe/README.md +1 -1
  120. package/integrations/opencode/README.md +1 -1
  121. package/integrations/roo-code/README.md +1 -1
  122. package/integrations/rovo-dev/README.md +1 -1
  123. package/integrations/trae/README.md +1 -1
  124. package/integrations/windsurf/README.md +3 -3
  125. package/library/references/collaborative-protocol/collaborative-protocol.md +5 -5
  126. package/library/references/gd-style/gd-style.md +4 -6
  127. package/library/references/godot-version/godot-version.md +3 -3
  128. package/library/references/headless-routing/headless-routing.md +2 -2
  129. package/library/references/mcp-fresh-chat-playbook/mcp-fresh-chat-playbook.md +2 -2
  130. package/library/references/mcp-tools-reference/mcp-tools-reference.md +16 -15
  131. package/library/references/product-map/product-map.md +1 -1
  132. package/library/references/public-surface-language/public-surface-language.md +1 -1
  133. package/library/references/summer-folder/summer-folder.md +1 -1
  134. package/library/skills/3d-lighting/SKILL.md +4 -4
  135. package/library/skills/README.md +16 -6
  136. package/library/skills/adaptive-music/SKILL.md +7 -7
  137. package/library/skills/ai-characters-in-games/SKILL.md +1 -1
  138. package/library/skills/ambient-bed/SKILL.md +12 -14
  139. package/library/skills/animated-loop/SKILL.md +42 -42
  140. package/library/skills/animation-tree/SKILL.md +6 -6
  141. package/library/skills/art-direction/SKILL.md +8 -11
  142. package/library/skills/asset-strategy/SKILL.md +3 -3
  143. package/library/skills/audio-direction/SKILL.md +8 -10
  144. package/library/skills/auto-fire-targeting/SKILL.md +4 -3
  145. package/library/skills/bounded-engine-runs/SKILL.md +6 -7
  146. package/library/skills/brainstorm-game/SKILL.md +5 -5
  147. package/library/skills/brainstorming/SKILL.md +9 -15
  148. package/library/skills/celeste-momentum-platforming/SKILL.md +8 -13
  149. package/library/skills/celeste-momentum-platforming/resource.yaml +1 -1
  150. package/library/skills/character-animation-wiring/SKILL.md +2 -2
  151. package/library/skills/character-model/SKILL.md +15 -26
  152. package/library/skills/character-movement/SKILL.md +1 -1
  153. package/library/skills/character-movement/references/movement-3d.md +1 -1
  154. package/library/skills/character-portrait/SKILL.md +4 -4
  155. package/library/skills/cinematic-cutscene/SKILL.md +38 -38
  156. package/library/skills/combat-basics/SKILL.md +1 -1
  157. package/library/skills/compatibility-renderer-traps/SKILL.md +2 -2
  158. package/library/skills/concept-art/SKILL.md +3 -3
  159. package/library/skills/create-asset-sheet/SKILL.md +12 -31
  160. package/library/skills/debug/SKILL.md +5 -4
  161. package/library/skills/debugging-game-feel/SKILL.md +1 -4
  162. package/library/skills/debugging-game-feel/resource.yaml +1 -1
  163. package/library/skills/design-level/SKILL.md +5 -5
  164. package/library/skills/design-mechanic/SKILL.md +6 -6
  165. package/library/skills/design-npc/SKILL.md +8 -15
  166. package/library/skills/diagnosing-perf-regressions/SKILL.md +5 -10
  167. package/library/skills/dispatching-parallel-agents/SKILL.md +1 -19
  168. package/library/skills/environment-kit/SKILL.md +5 -5
  169. package/library/skills/export-and-ship/SKILL.md +24 -25
  170. package/library/skills/export-and-ship/resource.yaml +2 -1
  171. package/library/skills/fabricating-assets/SKILL.md +1 -1
  172. package/library/skills/facial-and-lipsync/SKILL.md +5 -5
  173. package/library/skills/fps-controller/SKILL.md +17 -16
  174. package/library/skills/fps-controller/resource.yaml +0 -2
  175. package/library/skills/game-feel/SKILL.md +6 -7
  176. package/library/skills/gameskill/SKILL.md +3 -3
  177. package/library/skills/gdscript-patterns/SKILL.md +3 -3
  178. package/library/skills/generate-motion/SKILL.md +5 -22
  179. package/library/skills/godogen-visual-proof-game-generation/SKILL.md +72 -90
  180. package/library/skills/godogen-visual-proof-game-generation/resource.yaml +0 -1
  181. package/library/skills/grow-analytics/SKILL.md +21 -0
  182. package/library/skills/grow-analytics/resource.yaml +27 -0
  183. package/library/skills/headless-scripting/SKILL.md +8 -8
  184. package/library/skills/instantiate-asset-pack/SKILL.md +7 -3
  185. package/library/skills/investigating-bugs/SKILL.md +3 -11
  186. package/library/skills/investigating-bugs/condition-based-waiting.md +0 -10
  187. package/library/skills/investigating-bugs/root-cause-tracing.md +0 -8
  188. package/library/skills/lumera-single-image-scene-reconstruction/SKILL.md +15 -15
  189. package/library/skills/lumera-single-image-scene-reconstruction/resource.yaml +0 -1
  190. package/library/skills/make-game/SKILL.md +3 -5
  191. package/library/skills/make-game/references/2d-platformer.md +1 -1
  192. package/library/skills/multiplayer-project/SKILL.md +1 -1
  193. package/library/skills/multiplayer-testing/SKILL.md +4 -4
  194. package/library/skills/music-track/SKILL.md +40 -38
  195. package/library/skills/new-project/SKILL.md +4 -6
  196. package/library/skills/organic-model/SKILL.md +9 -9
  197. package/library/skills/pixel-art/SKILL.md +6 -6
  198. package/library/skills/play/SKILL.md +3 -4
  199. package/library/skills/playtesting-a-feature/SKILL.md +4 -6
  200. package/library/skills/procedural-animation/SKILL.md +10 -11
  201. package/library/skills/prop-model/SKILL.md +4 -4
  202. package/library/skills/psx-retro-rendering/SKILL.md +20 -20
  203. package/library/skills/psx-retro-rendering/resource.yaml +2 -3
  204. package/library/skills/publish-your-game/SKILL.md +21 -0
  205. package/library/skills/publish-your-game/resource.yaml +29 -0
  206. package/library/skills/realtime-wet-surfaces/SKILL.md +15 -15
  207. package/library/skills/realtime-wet-surfaces/resource.yaml +1 -2
  208. package/library/skills/remote-deploy/SKILL.md +20 -21
  209. package/library/skills/retarget/SKILL.md +19 -23
  210. package/library/skills/running-in-the-cloud/SKILL.md +5 -5
  211. package/library/skills/scene-composition/SKILL.md +5 -5
  212. package/library/skills/scene-hierarchy-design/SKILL.md +3 -3
  213. package/library/skills/scene-hierarchy-design/resource.yaml +0 -1
  214. package/library/skills/scene-scripting/SKILL.md +3 -3
  215. package/library/skills/scene-to-level/SKILL.md +28 -31
  216. package/library/skills/skill-create/SKILL.md +1 -1
  217. package/library/skills/skill-improve/SKILL.md +9 -9
  218. package/library/skills/skill-test/SKILL.md +10 -10
  219. package/library/skills/skintokens-auto-rigging/SKILL.md +18 -18
  220. package/library/skills/skintokens-auto-rigging/resource.yaml +3 -3
  221. package/library/skills/skybox-panorama/SKILL.md +8 -9
  222. package/library/skills/sound-effect/SKILL.md +19 -19
  223. package/library/skills/spatial-placement/SKILL.md +2 -2
  224. package/library/skills/spatial-placement/references/kit-placement-tools.md +12 -22
  225. package/library/skills/sprite-sheet/SKILL.md +10 -10
  226. package/library/skills/store-art/SKILL.md +23 -0
  227. package/library/skills/store-art/resource.yaml +30 -0
  228. package/library/skills/store-listing/SKILL.md +21 -0
  229. package/library/skills/store-listing/resource.yaml +25 -0
  230. package/library/skills/summer-analytics/SKILL.md +3 -3
  231. package/library/skills/summer-analytics/resource.yaml +1 -0
  232. package/library/skills/summer-friends/SKILL.md +10 -10
  233. package/library/skills/summer-leaderboards/SKILL.md +6 -6
  234. package/library/skills/summer-match-results/SKILL.md +13 -13
  235. package/library/skills/summer-matchmaking/SKILL.md +11 -11
  236. package/library/skills/summer-parties/SKILL.md +9 -9
  237. package/library/skills/summer-player-data/SKILL.md +12 -12
  238. package/library/skills/summer-store/SKILL.md +6 -6
  239. package/library/skills/summer-world-chat/SKILL.md +9 -9
  240. package/library/skills/summer-world-saves/SKILL.md +7 -7
  241. package/library/skills/text-to-motion/rigging-for-text-motion.md +1 -1
  242. package/library/skills/tileable-texture/SKILL.md +1 -1
  243. package/library/skills/trailer-shot/SKILL.md +36 -36
  244. package/library/skills/tune-performance/SKILL.md +13 -13
  245. package/library/skills/tune-performance/resource.yaml +1 -1
  246. package/library/skills/ui-graphics/SKILL.md +7 -7
  247. package/library/skills/use-widget-asset/SKILL.md +7 -7
  248. package/library/skills/using-summer/SKILL.md +5 -7
  249. package/library/skills/vehicle-model/SKILL.md +6 -7
  250. package/library/skills/vehicle-model/resource.yaml +2 -2
  251. package/library/skills/verification-before-completion/SKILL.md +5 -6
  252. package/library/skills/vfx-dissolve/SKILL.md +4 -4
  253. package/library/skills/vfx-fire/SKILL.md +6 -6
  254. package/library/skills/vfx-fire/_building-blocks/additive-billboard-particles.md +3 -3
  255. package/library/skills/vfx-hit-spark/SKILL.md +4 -4
  256. package/library/skills/vfx-hit-spark/_building-blocks/additive-billboard-particles.md +3 -3
  257. package/library/skills/vfx-lightning/SKILL.md +5 -5
  258. package/library/skills/vfx-lightning/_building-blocks/additive-billboard-particles.md +3 -3
  259. package/library/skills/vfx-lightning/_building-blocks/trauma-shake-snippet.md +9 -3
  260. package/library/skills/vfx-magic-glow/SKILL.md +4 -4
  261. package/library/skills/vfx-magic-glow/_building-blocks/additive-billboard-particles.md +3 -3
  262. package/library/skills/vfx-muzzle-flash/SKILL.md +5 -5
  263. package/library/skills/vfx-muzzle-flash/_building-blocks/additive-billboard-particles.md +3 -3
  264. package/library/skills/vfx-muzzle-flash/_building-blocks/trauma-shake-snippet.md +9 -3
  265. package/library/skills/vfx-smoke/SKILL.md +5 -5
  266. package/library/skills/vfx-water-ripple/SKILL.md +2 -2
  267. package/library/skills/vfx-water-ripple/_building-blocks/additive-billboard-particles.md +3 -3
  268. package/library/skills/voice-line/SKILL.md +9 -9
  269. package/library/skills/world-building-3d/references/spatial-tools.md +2 -2
  270. package/library/skills/writing-plans/SKILL.md +4 -4
  271. package/library/skills/writing-skills/SKILL.md +8 -8
  272. package/library/tools/capture-gameplay/resource.yaml +66 -1
  273. package/library/tools/creator-publish/resource.yaml +4 -4
  274. package/library/tools/creator-releases/resource.yaml +5 -5
  275. package/library/tools/export-game/resource.yaml +3 -0
  276. package/library/tools/generate-image/resource.yaml +4 -2
  277. package/library/tools/publish-build/resource.yaml +5 -1
  278. package/package.json +1 -1
  279. package/registry/generated/counts.json +2 -2
  280. package/registry/generated/gemini-extension.json +5 -1
  281. package/registry/generated/index.json +280 -137
  282. package/registry/generated/marketplace.claude.json +1 -1
  283. package/registry/generated/plugin.claude.json +5 -1
  284. package/registry/generated/plugin.codex.json +5 -1
  285. package/registry/generated/plugin.cursor.json +5 -1
  286. package/registry/generated/plugin.factory.json +5 -1
  287. package/registry/generated/skills-index.md +16 -6
  288. package/registry/generated/skills-registry.json +57 -5
  289. package/registry/schemas/domains.json +1 -1
  290. package/registry/schemas/skill.schema.json +5 -0
@@ -13,8 +13,7 @@
13
13
  * reference the markdown body
14
14
  * example / collection README.md / collection.yaml when present
15
15
  *
16
- * The LAST line of every load is the feedback footer
17
- * (SELF_IMPROVING_LIBRARY.md §3.1 "trigger placement"):
16
+ * The LAST line of every load is the feedback footer:
18
17
  * — entry_id: <id>@<content_hash first 12>. If this entry is wrong, stale,
19
18
  * or you deviate from it, report via summer_library_feedback.
20
19
  * The agent copies that entry_id verbatim into summer_library_feedback, so
@@ -32,11 +31,18 @@
32
31
  * ships it; and `reference/<slug>` that is no entry falls back to the one
33
32
  * `references/<slug>.md` an entry ships. A body render ends with its links and
34
33
  * the id that loads each. A linked file's footer names its entry.
34
+ *
35
+ * Hosted skills: a skill whose resource.yaml names a `hosted_resource`
36
+ * (summer://skills/<slug>) keeps its current text on the hosted Summer Engine
37
+ * MCP, the text the Studio agent reads. The body is read from there with the
38
+ * store sign-in; without it the local SKILL.md renders with the reason and how
39
+ * to load the full text. The entry also resolves by that resource URI.
35
40
  */
36
41
  import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
37
42
  import { join, posix, sep } from "node:path";
38
43
  import { z } from "zod";
39
44
  import { LIBRARY_KIND_DIRS, loadLibraryIndex, searchLibrary, } from "./library-search.js";
45
+ import { withHostedMcp } from "./hosted-mcp.js";
40
46
  import { PACKAGE_ROOT } from "./package-root.js";
41
47
  import { getTemplateRegistry } from "./templates.js";
42
48
  export const READ_PARTS = ["skill", "resource", "all"];
@@ -61,6 +67,8 @@ export const LINKED_FILE_EXTENSIONS = new Set([
61
67
  ]);
62
68
  /** A linked file larger than this is refused rather than dumped. */
63
69
  export const LINKED_FILE_MAX_BYTES = 256 * 1024;
70
+ /** How long a hosted skill read waits for the hosted Summer Engine MCP. */
71
+ export const HOSTED_READ_TIMEOUT_MS = 8_000;
64
72
  export const readLibraryInputShape = {
65
73
  id: z
66
74
  .string()
@@ -82,13 +90,13 @@ export function entryIdWithHash(id, contentHash) {
82
90
  return hash ? `${id}@${hash}` : id;
83
91
  }
84
92
  // ── Resolution ─────────────────────────────────────────────────────────────
85
- /** Exact id, an id with an @hash suffix (as the footer prints it), or a bare
86
- * slug that names exactly one entry. */
93
+ /** Exact id, an id with an @hash suffix (as the footer prints it), a hosted
94
+ * skill's resource URI, or a bare slug that names exactly one entry. */
87
95
  function resolveEntry(requested, entries) {
88
96
  const bare = requested.trim().replace(/@[a-f0-9]+$/i, "");
89
97
  if (!bare)
90
98
  return null;
91
- const exact = entries.find((entry) => entry.id === bare);
99
+ const exact = entries.find((entry) => entry.id === bare || entry.hosted_resource === bare);
92
100
  if (exact)
93
101
  return exact;
94
102
  if (!bare.includes("/")) {
@@ -325,7 +333,7 @@ function relatedMap(entry) {
325
333
  }
326
334
  return out;
327
335
  }
328
- function header(entry, slug, related) {
336
+ function header(entry, slug, related, hostedLoaded) {
329
337
  const lines = [];
330
338
  lines.push(`${entry.id} — ${entry.kind} v${entry.version ?? "?"} (${entry.status ?? "stable"})`);
331
339
  if (entry.summary)
@@ -339,7 +347,10 @@ function header(entry, slug, related) {
339
347
  const relatedIds = Object.values(related).flat();
340
348
  if (relatedIds.length > 0)
341
349
  lines.push(`related: ${relatedIds.join(", ")}`);
342
- if (entry.kind === "skill") {
350
+ if (hostedLoaded) {
351
+ lines.push(`The body is the current text from the hosted Summer Engine MCP (also the \`${slug}\` MCP prompt and the resource ${entry.hosted_resource}). Follow it, do not paraphrase it.`);
352
+ }
353
+ else if (entry.kind === "skill") {
343
354
  lines.push(`Invoke: the \`${slug}\` skill in your host (Claude Code: /${slug}); installed under its bare slug by \`summer setup\`. The body follows — follow it, do not paraphrase it.`);
344
355
  }
345
356
  return lines.join("\n");
@@ -452,7 +463,21 @@ export async function readLibraryEntry(requestedId, part = "all", deps = {}) {
452
463
  let bodyTitle;
453
464
  let body;
454
465
  let links = [];
455
- if (kind === "tool") {
466
+ let hostedText = null;
467
+ let hostedError;
468
+ if (kind === "skill" && entry.hosted_resource && part !== "resource") {
469
+ try {
470
+ hostedText = (await (deps.readHostedResource ?? readHostedSkill)(entry.hosted_resource)).replace(/\s+$/, "");
471
+ }
472
+ catch (error) {
473
+ hostedError = error instanceof Error ? error.message : String(error);
474
+ }
475
+ }
476
+ if (hostedText !== null) {
477
+ bodyTitle = `${entry.hosted_resource} (hosted Summer Engine MCP)`;
478
+ body = hostedText;
479
+ }
480
+ else if (kind === "tool") {
456
481
  bodyTitle = "how to call";
457
482
  body = toolBody(entry, slug);
458
483
  }
@@ -475,11 +500,14 @@ export async function readLibraryEntry(requestedId, part = "all", deps = {}) {
475
500
  body = `(no body file shipped for this ${kind}; the descriptor below is all there is)`;
476
501
  }
477
502
  }
478
- const sections = [header(entry, slug, related)];
503
+ const sections = [header(entry, slug, related, hostedText !== null)];
479
504
  if (part === "skill" || part === "all") {
480
505
  sections.push(`--- ${bodyTitle} ---\n${body}`);
481
506
  if (links.length > 0)
482
507
  sections.push(linksSection(links));
508
+ if (hostedError !== undefined) {
509
+ sections.push(`--- hosted text not loaded ---\nThe full current text of this skill is the resource ${entry.hosted_resource} on the hosted Summer Engine MCP, and it could not be read: ${hostedError}\nThe body above is only the local summary: load the full text before you act (here after the fix, or as the \`${slug}\` MCP prompt or resource in a host connected to the hosted Summer Engine MCP).`);
510
+ }
483
511
  }
484
512
  if (part === "resource" || part === "all")
485
513
  sections.push(`--- ${relPath}/resource.yaml ---\n${resourceYaml}`);
@@ -487,6 +515,10 @@ export async function readLibraryEntry(requestedId, part = "all", deps = {}) {
487
515
  const result = { ...base, text: sections.join("\n\n") };
488
516
  if (bodyFile)
489
517
  result.body_file = bodyFile;
518
+ if (entry.hosted_resource)
519
+ result.hosted_resource = entry.hosted_resource;
520
+ if (hostedError !== undefined)
521
+ result.hosted_error = hostedError;
490
522
  if (links.length > 0 && part !== "resource")
491
523
  result.links = links;
492
524
  if (kind === "tool") {
@@ -500,6 +532,25 @@ export async function readLibraryEntry(requestedId, part = "all", deps = {}) {
500
532
  }
501
533
  return result;
502
534
  }
535
+ /** A hosted skill's text, read from the hosted Summer Engine MCP with the store sign-in. */
536
+ async function readHostedSkill(uri) {
537
+ let timer;
538
+ const timeout = new Promise((_, reject) => {
539
+ timer = setTimeout(() => reject(new Error(`the hosted Summer Engine MCP did not answer within ${HOSTED_READ_TIMEOUT_MS / 1000} s.`)), HOSTED_READ_TIMEOUT_MS);
540
+ timer.unref?.();
541
+ });
542
+ try {
543
+ const result = await Promise.race([withHostedMcp((client) => client.readResource({ uri })), timeout]);
544
+ const contents = Array.isArray(result.contents) ? result.contents : [];
545
+ const text = contents.map((content) => (typeof content.text === "string" ? content.text : "")).join("\n").trim();
546
+ if (!text)
547
+ throw new Error("the hosted Summer Engine MCP returned no text for it.");
548
+ return text;
549
+ }
550
+ finally {
551
+ clearTimeout(timer);
552
+ }
553
+ }
503
554
  function safeTemplates() {
504
555
  try {
505
556
  return getTemplateRegistry();
@@ -49,6 +49,8 @@ export interface LibraryIndexEntry {
49
49
  cli_command?: string;
50
50
  authority?: Record<string, boolean>;
51
51
  recommended?: boolean;
52
+ /** The hosted Summer Engine MCP resource holding the skill's current text. */
53
+ hosted_resource?: string;
52
54
  }
53
55
  export type MatchedBy = "lexical" | "semantic";
54
56
  export interface LibrarySearchHit {
@@ -53,8 +53,7 @@ export declare function getTemplateRegistry(): readonly TemplateEntry[];
53
53
  */
54
54
  export declare function resolveTemplate(query: string, entries: readonly TemplateEntry[]): TemplateResolution;
55
55
  /** Seconds a network git call (`fetch`) may take before it is killed.
56
- * `summer create` once hung 42 minutes inside a fetch
57
- * (docs/design/archive/TK-VS-FOLD-2026-09-07.md, gap 4). */
56
+ * `summer create` once hung 42 minutes inside a fetch. */
58
57
  export declare const FETCH_TIMEOUT_ENV = "SUMMER_FETCH_TIMEOUT_S";
59
58
  export declare const DEFAULT_FETCH_TIMEOUT_S = 120;
60
59
  /** The bound in force: FETCH_TIMEOUT_ENV when it is a positive number of
@@ -109,8 +109,7 @@ export function resolveTemplate(query, entries) {
109
109
  }
110
110
  // ---------- git ----------
111
111
  /** Seconds a network git call (`fetch`) may take before it is killed.
112
- * `summer create` once hung 42 minutes inside a fetch
113
- * (docs/design/archive/TK-VS-FOLD-2026-09-07.md, gap 4). */
112
+ * `summer create` once hung 42 minutes inside a fetch. */
114
113
  export const FETCH_TIMEOUT_ENV = "SUMMER_FETCH_TIMEOUT_S";
115
114
  export const DEFAULT_FETCH_TIMEOUT_S = 120;
116
115
  /** The bound in force: FETCH_TIMEOUT_ENV when it is a positive number of
@@ -45,6 +45,31 @@ export declare function resolveAgentSelection(agentArg: string | undefined, agen
45
45
  /** `--scope` with a user-facing error; absent means "user". */
46
46
  export declare function resolveConfigScope(scopeOpt: string | undefined): ConfigScope;
47
47
  export declare function configureAgentMcp(options: AgentConfigOptions): Promise<AgentConfigResult>;
48
+ /** The Summer Games player MCP: friends, parties, messages and the player's
49
+ * inbox, as the signed-in player. A remote server; the agent signs in to it
50
+ * with OAuth in the browser, so setup writes no credential. */
51
+ export declare const SUMMER_GAMES_MCP_SERVER_NAME = "summer-games";
52
+ export declare const SUMMER_GAMES_MCP_URL = "https://mcp.summer.games/mcp";
53
+ export interface RemoteMcpResult {
54
+ /** The entry's name: summer-games, or the name the person already gave it. */
55
+ serverName: string;
56
+ url: string;
57
+ path: string;
58
+ changed: boolean;
59
+ wrote: boolean;
60
+ /** Set when the server was already listed under another name (e.g. a
61
+ * hand-added "summer"); setup then leaves the file alone. */
62
+ existingName?: string;
63
+ /** What the person does once to sign in (the agent opens summer.games). */
64
+ signIn: string;
65
+ }
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.
71
+ */
72
+ export declare function configureSummerGamesMcp(options: Pick<AgentConfigOptions, "agent" | "scope" | "dryRun" | "cwd" | "env">): Promise<RemoteMcpResult | null>;
48
73
  export declare const DEFAULT_CHANNEL = "latest";
49
74
  /** npm dist-tags are lowercase words like `latest`, `next`, `beta`, `v3-preview`;
50
75
  * anything that could be read as a version or a path is refused (a bare
@@ -73,6 +73,116 @@ export async function configureAgentMcp(options) {
73
73
  nextSteps: [`Updated ${target.path}.`, spec.restart],
74
74
  };
75
75
  }
76
+ /** The Summer Games player MCP: friends, parties, messages and the player's
77
+ * inbox, as the signed-in player. A remote server; the agent signs in to it
78
+ * with OAuth in the browser, so setup writes no credential. */
79
+ export const SUMMER_GAMES_MCP_SERVER_NAME = "summer-games";
80
+ export const SUMMER_GAMES_MCP_URL = "https://mcp.summer.games/mcp";
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.
86
+ */
87
+ export async function configureSummerGamesMcp(options) {
88
+ const spec = agentSpec(options.agent);
89
+ if (!spec.remoteMcp)
90
+ return null;
91
+ const env = options.env ?? process.env;
92
+ const target = resolveConfigTarget(spec, options.scope, resolve(options.cwd ?? process.cwd()), env);
93
+ const write = !options.dryRun;
94
+ const url = SUMMER_GAMES_MCP_URL;
95
+ let existing;
96
+ 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);
103
+ changed = current !== next;
104
+ if (write && changed)
105
+ await writeTextFile(target.path, next);
106
+ }
107
+ }
108
+ else {
109
+ const current = await readJsonConfig(target.path);
110
+ existing = findJsonServerByUrl(current, url);
111
+ if (!existing) {
112
+ const next = copyJsonObject(current);
113
+ next.mcpServers = mergeNamedObject(next.mcpServers, SUMMER_GAMES_MCP_SERVER_NAME, { type: "http", url }, "mcpServers");
114
+ const nextRendered = renderJsonFile(next);
115
+ changed = renderJsonFile(current) !== nextRendered;
116
+ if (write && changed)
117
+ await writeTextFile(target.path, nextRendered);
118
+ }
119
+ }
120
+ const serverName = existing ?? SUMMER_GAMES_MCP_SERVER_NAME;
121
+ return {
122
+ serverName,
123
+ url,
124
+ path: target.path,
125
+ changed,
126
+ wrote: write && changed,
127
+ ...(existing && existing !== SUMMER_GAMES_MCP_SERVER_NAME ? { existingName: existing } : {}),
128
+ signIn: spec.remoteMcp.signIn(serverName),
129
+ };
130
+ }
131
+ function sameMcpUrl(a, b) {
132
+ if (typeof a !== "string")
133
+ return false;
134
+ const normalize = (value) => {
135
+ try {
136
+ const parsed = new URL(value.trim());
137
+ return `${parsed.protocol}//${parsed.host}${parsed.pathname.replace(/\/+$/, "")}`.toLowerCase();
138
+ }
139
+ catch {
140
+ return value.trim().toLowerCase();
141
+ }
142
+ };
143
+ return normalize(a) === normalize(b);
144
+ }
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
+ }
156
+ for (const map of maps) {
157
+ if (!isJsonObject(map))
158
+ continue;
159
+ for (const [name, entry] of Object.entries(map)) {
160
+ if (isJsonObject(entry) && sameMcpUrl(entry.url, url))
161
+ return name;
162
+ }
163
+ }
164
+ return undefined;
165
+ }
166
+ /** The name of a [mcp_servers.<name>] table whose url is this URL. */
167
+ function findTomlServerByUrl(content, url) {
168
+ let table;
169
+ for (const line of content.split(/\r?\n/)) {
170
+ const header = /^\s*\[([^\[\]]+)\]\s*(?:#.*)?$/.exec(line);
171
+ if (header) {
172
+ const server = /^mcp_servers\.(?:"([^"]+)"|([A-Za-z0-9_-]+))$/.exec(header[1].trim());
173
+ table = server ? (server[1] ?? server[2]) : undefined;
174
+ continue;
175
+ }
176
+ if (/^\s*\[/.test(line)) {
177
+ table = undefined;
178
+ continue;
179
+ }
180
+ const value = /^\s*url\s*=\s*(?:"([^"]*)"|'([^']*)')/.exec(line);
181
+ if (table && value && sameMcpUrl(value[1] ?? value[2], url))
182
+ return table;
183
+ }
184
+ return undefined;
185
+ }
76
186
  async function upsertConfig(format, path, server, write) {
77
187
  switch (format) {
78
188
  case "toml":
@@ -470,10 +580,11 @@ async function readTextFileIfExists(path) {
470
580
  throw new Error(`Could not read ${path}: ${formatError(error)}`);
471
581
  }
472
582
  }
473
- function upsertTomlTable(content, table) {
583
+ function upsertTomlTable(content, table, name = SUMMER_MCP_SERVER_NAME) {
474
584
  const lines = content.split(/\r?\n/);
475
- const serverHeader = /^\s*\[mcp_servers\.(?:"summer-engine"|summer-engine)(?:\.|\])/;
476
- const start = lines.findIndex((line) => /^\s*\[mcp_servers\.(?:"summer-engine"|summer-engine)\]\s*(?:#.*)?$/.test(line));
585
+ const key = name.replace(/[.*+?^${}()|[\]\\-]/g, "\\$&");
586
+ const serverHeader = new RegExp(`^\\s*\\[mcp_servers\\.(?:"${key}"|${key})(?:\\.|\\])`);
587
+ const start = lines.findIndex((line) => new RegExp(`^\\s*\\[mcp_servers\\.(?:"${key}"|${key})\\]\\s*(?:#.*)?$`).test(line));
477
588
  if (start === -1) {
478
589
  const trimmed = content.endsWith("\n") || content === "" ? content : `${content}\n`;
479
590
  return `${trimmed}${trimmed === "" ? "" : "\n"}${table}`;
@@ -22,6 +22,7 @@ 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";
25
26
  export type SkillLocationKind = "skill-dir" | "cursor-rule-dir" | "windsurf-rule-file" | "cline-rule-dir" | "opencode-skill-dir";
26
27
  export interface PathContext {
27
28
  home: string;
@@ -70,6 +71,17 @@ export interface AgentSpec {
70
71
  /** Env var that overrides the MCP config path (tests, unusual installs). */
71
72
  envOverride: string;
72
73
  format: McpFormat;
74
+ /**
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)
79
+ * signIn tells the person how to sign in once, for the entry's name.
80
+ */
81
+ remoteMcp?: {
82
+ format: RemoteMcpFormat;
83
+ signIn: (name: string) => string;
84
+ };
73
85
  mcp: {
74
86
  /** null: user-level servers are managed in the app's UI; a user request writes the project file with a warning. */
75
87
  user: ((ctx: PathContext) => string) | null;
@@ -108,6 +108,7 @@ 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
112
  mcp: { user: home(".claude.json"), project: project(".mcp.json") },
112
113
  restart: "Restart Claude Code or run /mcp in a new session.",
113
114
  skills: skillDirs(home(".claude", "skills"), project(".claude", "skills")),
@@ -134,6 +135,10 @@ const SPECS = [
134
135
  status: "active",
135
136
  envOverride: "SUMMER_CODEX_CONFIG_FILE",
136
137
  format: "toml",
138
+ remoteMcp: {
139
+ format: "toml-url",
140
+ signIn: (name) => `In Codex, run /mcp login ${name} (or \`codex mcp login ${name}\` in a terminal) and sign in.`,
141
+ },
137
142
  mcp: {
138
143
  user: home(".codex", "config.toml"),
139
144
  project: project(".codex", "config.toml"),
@@ -149,6 +154,10 @@ const SPECS = [
149
154
  status: "active",
150
155
  envOverride: "SUMMER_CURSOR_MCP_CONFIG_FILE",
151
156
  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.
152
161
  mcp: { user: home(".cursor", "mcp.json"), project: project(".cursor", "mcp.json") },
153
162
  restart: "Restart Cursor and enable the summer-engine MCP server if prompted.",
154
163
  skills: sharedSkills({
@@ -6,8 +6,8 @@ export declare const getCachedBootDriftNotice: typeof getCachedNotice;
6
6
  /**
7
7
  * Server `instructions` in the MCP initialize response (SDK ServerOptions,
8
8
  * @modelcontextprotocol/sdk >= 1.x). Hosts hand this to the model once per
9
- * session, before any tool call — the place for the five habits the E2E run
10
- * (docs/design/archive/E2E-2026-09-03.md, F-17) showed an agent needs on the first
9
+ * session, before any tool call — the place for the five habits an end-to-end run
10
+ * showed an agent needs on the first
11
11
  * turn and would otherwise learn the hard way. Kept under 600 characters so
12
12
  * it costs a few hundred tokens once; the full operating guide stays in the
13
13
  * summer_agent_playbook prompt / summer_get_agent_playbook tool. The last
@@ -34,8 +34,8 @@ export const getCachedBootDriftNotice = getCachedNotice;
34
34
  /**
35
35
  * Server `instructions` in the MCP initialize response (SDK ServerOptions,
36
36
  * @modelcontextprotocol/sdk >= 1.x). Hosts hand this to the model once per
37
- * session, before any tool call — the place for the five habits the E2E run
38
- * (docs/design/archive/E2E-2026-09-03.md, F-17) showed an agent needs on the first
37
+ * session, before any tool call — the place for the five habits an end-to-end run
38
+ * showed an agent needs on the first
39
39
  * turn and would otherwise learn the hard way. Kept under 600 characters so
40
40
  * it costs a few hundred tokens once; the full operating guide stays in the
41
41
  * summer_agent_playbook prompt / summer_get_agent_playbook tool. The last
@@ -40,7 +40,7 @@ async function creatorResult(operation) {
40
40
  }
41
41
  }
42
42
  export function registerCreatorTools(server) {
43
- server.tool("summer_export_game", "Export the game in the exact file the Summer Games store takes, with the installed Summer Engine headless (no window). format \"bundle\" (default): the summer.games .zip the Summer apps run on iPhone, Android, macOS and Windows; targets picks the platforms (a game without a server: ios and android only); upload it with summer_publish_build. format \"download\" with one target: web (an HTML5 .zip on Summer's WebGPU Forward+ web template, played on summer.games), macos (.app zip, macos-universal) or windows (.exe zip, windows-x64); these need the export template from summer_export_templates and are uploaded as store versions. Needs Summer Engine 0.7.0 or newer: an older engine is refused before anything is written (code engine_too_old). Works without the editor running. Returns path, sha256, size, the manifest or store platform, and warnings.", {
43
+ server.tool("summer_export_game", "Export the game in the exact file the Summer Games store takes, with the installed Summer Engine headless (no window). format \"bundle\" (default): the summer.games .zip the Summer apps run on iPhone, Android, macOS and Windows; targets picks the platforms (a game without a server: ios and android only); upload it with summer_publish_build. format \"download\" with one target: web (an HTML5 .zip on Summer's WebGPU Forward+ web template, played on summer.games), macos (.app zip, macos-universal) or windows (.exe zip, windows-x64); these need the export template from summer_export_templates and are uploaded as store versions. Needs Summer Engine 0.7.0 or newer: an older engine is refused before anything is written (code engine_too_old). A target the installed engine cannot build (android needs 0.7.1) is left out: the other targets export, and skippedTargets plus a warning name it. A game with a server declares its platforms in summer.build.json; when they differ from the exported targets a warning says so (summer_publish_build would be refused), and alignDeclaration:true aligns them. Works without the editor running. Returns path, sha256, size, the manifest or store platform, and warnings.", {
44
44
  project: z.string().optional().describe("Project folder with project.godot. Defaults to the MCP's bound project, then the working directory."),
45
45
  out: z.string().optional().describe("Output .zip path. Defaults to <project>/.summer/exports/."),
46
46
  targets: z
@@ -53,7 +53,11 @@ export function registerCreatorTools(server) {
53
53
  .describe("bundle (default): the summer.games store build. download: a web build or native download; the default when targets is [\"web\"]."),
54
54
  debug: z.boolean().default(false).describe("Export a debug build (--export-debug)."),
55
55
  timeoutSeconds: z.number().int().min(10).max(7200).optional().describe("Stop the engine after this many seconds (default 900; a first export imports every asset)."),
56
- }, async ({ project, out, targets, format, debug, timeoutSeconds }) => creatorResult(() => exportGame({ project, out, targets, format, debug, ...(timeoutSeconds ? { timeoutMs: timeoutSeconds * 1000 } : {}) })));
56
+ alignDeclaration: z
57
+ .boolean()
58
+ .optional()
59
+ .describe("A game with a server: set summer.build.json targetPlatforms to targets before exporting. The store refuses an upload whose declaration differs from the exported platforms (declaration_mismatch) and that spends the version."),
60
+ }, async ({ project, out, targets, format, debug, timeoutSeconds, alignDeclaration }) => creatorResult(() => exportGame({ project, out, targets, format, debug, alignDeclaration, ...(timeoutSeconds ? { timeoutMs: timeoutSeconds * 1000 } : {}) })));
57
61
  server.tool("summer_export_templates", "List, download and install Summer Engine export templates from Summer's CDN into the folder the installed engine reads, checking each file's sha256. Needed only for summer_export_game format \"download\": web (Summer's WebGPU Forward+ template), macos, windows. Store bundles (format \"bundle\") need no template. action list shows what is installed and published; action install fetches the platforms asked (default web).", {
58
62
  action: z.enum(["list", "install"]).describe("list: installed and published templates. install: download and install."),
59
63
  platforms: z
@@ -66,7 +70,7 @@ export function registerCreatorTools(server) {
66
70
  .optional()
67
71
  .describe("The installed Summer Engine version (Help > About, e.g. 0.7.1). Read automatically on macOS; needed on Windows and Linux."),
68
72
  }, async ({ action, platforms, includeDebug, summerVersion }) => creatorResult(() => exportTemplates({ action, platforms, includeDebug, summerVersion })));
69
- server.tool("summer_capture_gameplay", "Capture real gameplay frames without a running editor: starts the game in Summer Engine's offscreen verify instance (a real renderer, a window parked offscreen with no focus, never on screen), lets it run waitSeconds, and saves PNG frames of what the game draws, HUD included. Use for store screenshots (1920x1080 landscape or 1080x1920 portrait). With an editor open, summer_screenshot target game also works. Returns each frame's path, width and height; look at them before using them.", {
73
+ server.tool("summer_capture_gameplay", "Capture real gameplay frames without a running editor: starts the game in Summer Engine's offscreen verify instance (--summer-offscreen: a real renderer, a window parked offscreen with no focus, never on screen), lets it run waitSeconds, runs steps, and saves PNG frames of what the game draws, HUD included. Use for store screenshots (1920x1080 landscape or 1080x1920 portrait). The main scene is often a title menu: pass the game's own flags in args (after \"--\", e.g. [\"--autostart\"]), a gameplay scene, or steps that play like a player: [{press:\"Play\"}, {wait:3000}, {key:\"Space\"}, {action:\"jump\"}, {click:[960,540]}, {drag:{from:[400,800],to:[1400,800]}}, {shot:true}]. With a shot step, only shot steps save frames; without one, frames are saved after the steps. A step that cannot run (no such button, key or action) becomes a warning. With an editor open, summer_screenshot target game also works. Returns each frame's path, width and height; look at them before using them.", {
70
74
  project: z.string().optional().describe("Project folder with project.godot. Defaults to the MCP's bound project, then the working directory."),
71
75
  scene: z.string().optional().describe("Scene to start (res://... or uid://...). Default: the project's main scene, which is often a title menu; pass the gameplay scene for gameplay frames."),
72
76
  resolution: z.string().optional().describe("Window size WIDTHxHEIGHT (default 1920x1080; 1080x1920 for portrait)."),
@@ -74,6 +78,31 @@ export function registerCreatorTools(server) {
74
78
  waitSeconds: z.number().min(0).max(120).optional().describe("Seconds the game runs before the first frame (default 3)."),
75
79
  intervalSeconds: z.number().min(0.1).max(60).optional().describe("Seconds between frames (default 1)."),
76
80
  out: z.string().optional().describe("Output folder. Defaults to <project>/.summer/captures/<time>/ (ignored by git)."),
81
+ args: z
82
+ .array(z.string())
83
+ .max(32)
84
+ .optional()
85
+ .describe('The game\'s own command-line flags, passed after "--" (OS.get_cmdline_user_args()), e.g. ["--autostart", "--solo"] to skip the title screen.'),
86
+ steps: z
87
+ .array(z
88
+ .object({
89
+ press: z.string().optional().describe("Press the visible button with this text (or node name); waits up to timeoutSeconds (default 10) for it."),
90
+ timeoutSeconds: z.number().min(0).max(60).optional(),
91
+ key: z.string().optional().describe("Press a key by Godot name: Space, Enter, Escape, A, Up."),
92
+ action: z.string().optional().describe("Press an input action from the project's InputMap, e.g. jump."),
93
+ holdMs: z.number().min(0).max(10000).optional().describe("How long key or action is held (default 100)."),
94
+ click: z.tuple([z.number(), z.number()]).optional().describe("Left-click at [x, y] in frame pixels."),
95
+ drag: z
96
+ .object({ from: z.tuple([z.number(), z.number()]), to: z.tuple([z.number(), z.number()]), ms: z.number().min(0).max(10000).optional() })
97
+ .optional()
98
+ .describe("Drag with the left button from [x, y] to [x, y] over ms (default 300)."),
99
+ wait: z.number().min(0).max(60000).optional().describe("Wait this many milliseconds."),
100
+ shot: z.boolean().optional().describe("true: save a frame now."),
101
+ })
102
+ .strict())
103
+ .max(50)
104
+ .optional()
105
+ .describe("What to do after waitSeconds, in order; exactly one of press, key, action, click, drag, wait, shot per step."),
77
106
  }, async (args) => creatorResult(() => captureGameplay(args)));
78
107
  server.tool("summer_publish_build", "Upload an export from summer_export_game to the creator's game on summer.games, through the same store upload Studio uses, straight from disk. A store bundle (format bundle): declare, upload parts, seal, wait until Summer makes the Build, name its client pack, and with publish=true approve it. A web build or native download (format download): upload it as the game's store version for its platform (web, macos-universal, windows-x64, linux-x64; read from the last export) and wait until Summer has checked it. First call with confirm=false: it checks that the game exists for this account and warns when its store page does not list the export's platforms, then returns the target (game name and id, file, digest, size, version) to show the user; set confirm only after they approve. Needs \"summer login --store\". A retry with the same file and clientVersion continues the same upload. Nothing here makes a game live: the owner approves publishing (summer_store_submit).", {
79
108
  gameId: z.string().optional().describe("The store game id (from summer_store_list_games, or the Studio store page URL). Without it the call fails with game_required and lists your store games, or says why the store did not list them."),
@@ -100,7 +129,7 @@ export function registerCreatorTools(server) {
100
129
  ...args,
101
130
  face: "mcp",
102
131
  })));
103
- server.tool("summer_creator_releases", "List real creator-owned releases from the versioned Summer Platform creator API. The server independently verifies the exact publish scope and project ownership.", {
132
+ server.tool("summer_creator_releases", "Deprecated: lists releases on the legacy Summercraft creator API (separate sc_ token), not summer.games. For what players get on summer.games, and which upload can be released, use summer_store_releases.", {
104
133
  projectId: z.string().optional().describe("Creator project ID. Defaults to ~/.summer/config.json."),
105
134
  limit: z.number().int().min(1).max(100).default(20),
106
135
  cursor: z.string().optional().describe("Opaque nextCursor from the previous page."),
@@ -178,7 +178,7 @@ Use this when summer_get_diagnostics shows a non-zero \`debugger.warnings\` coun
178
178
  }, async ({ max_warnings, include_stack, raw }) => withEngine(async (client) => {
179
179
  // Reuses the existing GetDebuggerErrors op with type: "warning" filter.
180
180
  // Engine-side change: ScriptEditorDebugger::get_errors_data() supports
181
- // include_warnings; debug_ops::get_debugger_errors honours `type` to
181
+ // include_warnings; the engine's GetDebuggerErrors honours `type` to
182
182
  // return warnings only. Diagnostics also exposes `warnings_data`.
183
183
  const op = {
184
184
  op: "GetDebuggerErrors",
@@ -1,10 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { BOARD_ENDPOINT, boardArgsSchema, boardContent, boardQuery } from "../../core/capabilities/board.js";
3
- import { imageGenerationArgsSchema } from "../../core/capabilities/image-generation.js";
3
+ import { IMAGE_GENERATION_TIMEOUT_MS, imageGenerationArgsSchema, saveGeneratedImage } from "../../core/capabilities/image-generation.js";
4
4
  import { buildMotionRequestBody, motionErrorHint, motionGenerationArgsSchema, motionJobFailureHint, } from "../../core/capabilities/motion-generation.js";
5
- import { writeFile, mkdir } from "fs/promises";
6
- import { tmpdir } from "os";
7
- import { join } from "path";
8
5
  import { getAuthToken } from "../../core/auth.js";
9
6
  import { resolveGatewayUrl } from "../../core/config.js";
10
7
  import { readJsonResponse } from "../../core/util/http.js";
@@ -151,26 +148,6 @@ async function mcpGet(endpoint, searchParams, timeoutMs = 30_000) {
151
148
  }
152
149
  return { data, status: res.status };
153
150
  }
154
- /**
155
- * Download an image URL to a temp file so the AI can Read it and show the user.
156
- */
157
- async function downloadToTemp(url, prefix, ext = "png") {
158
- try {
159
- const dir = join(tmpdir(), "summer-gen");
160
- await mkdir(dir, { recursive: true });
161
- const filename = `${prefix}-${Date.now()}.${ext}`;
162
- const filepath = join(dir, filename);
163
- const res = await fetch(url, { signal: AbortSignal.timeout(30_000) });
164
- if (!res.ok)
165
- return null;
166
- const buffer = Buffer.from(await res.arrayBuffer());
167
- await writeFile(filepath, buffer);
168
- return filepath;
169
- }
170
- catch {
171
- return null;
172
- }
173
- }
174
151
  /**
175
152
  * Poll a job until completed, failed, or timeout.
176
153
  * Returns the final job status object.
@@ -281,7 +258,8 @@ Requires authentication: run 'npx -y summer-engine@latest login' first.`, boardA
281
258
  server.tool("summer_generate_image", `Generate an image using AI models via Summer Engine Studio.
282
259
 
283
260
  Known models:
284
- - "nano-banana-2" (default) — High quality, supports txt2img and img2img
261
+ - "nano-banana-2" — High quality, supports txt2img and img2img (used when no
262
+ model and no size is asked)
285
263
  - "gemini-flash" — Google Gemini 2.5 Flash, fast
286
264
  - "flux-2" — FLUX.2, good for specific styles
287
265
 
@@ -300,17 +278,20 @@ guarantee transparency; background removal runs server-side after generation.
300
278
  Size: aspectRatio (1:1, 16:9, 9:16, 4:3, 3:4, ...) or width + height (the
301
279
  smallest size you need). For store art ask width 1920 height 1080 (key art)
302
280
  or width 1080 height 1920 (tall cover): the server picks a model that reaches
303
- that size, or refuses a named model that cannot, before anything is spent.
281
+ that size when model is omitted, or refuses a named model that cannot, before
282
+ anything is spent. Larger models can take over a minute; the call waits up to
283
+ 3 minutes.
304
284
  Each result reports the real width and height and the model that ran.
305
285
 
306
286
  'options' carries extra model params (seed, negative_prompt, ...); the result
307
287
  lists any the server did not use in ignoredOptions.
308
288
 
309
- Returns the asset with fileUrl (hosted) and localPath (temp file on disk).
289
+ Returns the asset with fileUrl (hosted) and localPath: the file on disk, in
290
+ 'out' (a folder, or a .png/.jpg/.webp path) or else <TMPDIR>/summer-gen/.
310
291
  Use the Read tool on localPath to show the image to the user for approval.
311
292
 
312
293
  Cloud tool — runs on Summer's servers and works WITHOUT the Summer Engine app open.
313
- Requires authentication: run 'npx -y summer-engine@latest login' first.`, imageGenerationArgsSchema.shape, async ({ prompt, model, style, referenceImageUrl, removeBackground, aspectRatio, width, height, options }) => {
294
+ Requires authentication: run 'npx -y summer-engine@latest login' first.`, imageGenerationArgsSchema.shape, async ({ prompt, model, style, referenceImageUrl, removeBackground, aspectRatio, width, height, options, out }) => {
314
295
  const result = await mcpGenerate("/api/mcp/generate/image", {
315
296
  prompt,
316
297
  model,
@@ -321,7 +302,7 @@ Requires authentication: run 'npx -y summer-engine@latest login' first.`, imageG
321
302
  width,
322
303
  height,
323
304
  options,
324
- });
305
+ }, IMAGE_GENERATION_TIMEOUT_MS);
325
306
  if (result.error) {
326
307
  return errorResult(result.error, result.data);
327
308
  }
@@ -329,7 +310,7 @@ Requires authentication: run 'npx -y summer-engine@latest login' first.`, imageG
329
310
  const imageUrl = result.data?.asset?.fileUrl || result.data?.asset?.thumbnailUrl;
330
311
  let localPath = null;
331
312
  if (imageUrl) {
332
- localPath = await downloadToTemp(imageUrl, "img");
313
+ localPath = await saveGeneratedImage(imageUrl, out);
333
314
  }
334
315
  return successResult({
335
316
  ...result.data,
@@ -467,7 +448,7 @@ Optional rig pass (image-to-3d only):
467
448
  Set options.rig = true to add an auto-rig pass via Meshy v6. The result
468
449
  job (poll via summer_check_job) will include rigAssetId — the asset ID
469
450
  of the rigged glb. Use that rigAssetId with summer_generate_motion to
470
- add animation clips. Adds ~$0.30 and ~60s to the job.
451
+ add animation clips. Adds ~60s to the job.
471
452
 
472
453
  Example:
473
454
  summer_generate_3d({
@@ -593,11 +574,11 @@ Requires authentication: run 'npx -y summer-engine@latest login' first.`, {
593
574
  server.tool("summer_generate_video", `Generate a video using AI models via Summer Engine Studio.
594
575
 
595
576
  Known models (text-to-video):
596
- - "ltx" (default) — LTX Video, fast (~$0.10)
597
- - "kling" — Kling 2 Master, high quality (~$0.50)
598
- - "kling-turbo" — Kling 2.5 Turbo Pro (~$0.30)
599
- - "minimax" — MiniMax (~$0.25)
600
- - "veo3" — Google Veo 3, premium (~$1.00)
577
+ - "ltx" (default) — LTX Video, fast
578
+ - "kling" — Kling 2 Master, high quality
579
+ - "kling-turbo" — Kling 2.5 Turbo Pro
580
+ - "minimax" — MiniMax
581
+ - "veo3" — Google Veo 3, premium
601
582
 
602
583
  Known models (image-to-video, when imageUrl provided):
603
584
  - "ltx" (default), "kling", "minimax"
@@ -698,7 +679,7 @@ Requires authentication: run 'npx -y summer-engine@latest login' first.`, {
698
679
  Backends (pick one):
699
680
  - "meshy-library" (default) — a curated mocap clip by motionName on a
700
681
  Summer-rigged humanoid (rigAssetId from summer_generate_3d with
701
- options.rig=true). Fast (~30s), cheap (~$0.10), real mocap quality.
682
+ options.rig=true). Fast (~30s), real mocap quality.
702
683
  Names that always work: idle, walk, run, jump, attack; anything else must
703
684
  be an exact library name.
704
685
  - "text-to-motion" — custom 2-second clips (60 frames) from text prompts on