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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +5 -1
- package/.codex-plugin/plugin.json +5 -1
- package/.cursor-plugin/plugin.json +5 -1
- package/.factory-plugin/plugin.json +5 -1
- package/.opencode/INSTALL.md +8 -8
- package/.opencode/plugins/summer.js +2 -2
- package/AGENTS.md +7 -7
- package/CHANGELOG.md +37 -0
- package/GEMINI.md +1 -1
- package/NOTICE +1 -1
- package/README.md +35 -35
- package/assets/autopilot/run.sh +2 -2
- package/assets/navigation/web-routes.json +1 -1
- package/commands/gameskill.md +2 -2
- package/commands/summer.md +25 -25
- package/dist/cli/commands/open.d.ts +1 -1
- package/dist/cli/commands/run.d.ts +1 -1
- package/dist/cli/commands/run.js +1 -1
- package/dist/cli/commands/setup.js +24 -4
- package/dist/cli/commands/skills.js +2 -2
- package/dist/core/api-client.d.ts +5 -6
- package/dist/core/api-client.js +12 -15
- package/dist/core/auth.js +2 -2
- package/dist/core/capabilities/asset-import.d.ts +0 -1
- package/dist/core/capabilities/camera-view.d.ts +1 -1
- package/dist/core/capabilities/camera-view.js +2 -2
- package/dist/core/capabilities/capture-gameplay.d.ts +72 -7
- package/dist/core/capabilities/capture-gameplay.js +386 -25
- package/dist/core/capabilities/connect-signal.d.ts +1 -2
- package/dist/core/capabilities/connect-signal.js +1 -2
- package/dist/core/capabilities/console-read.d.ts +2 -2
- package/dist/core/capabilities/console-read.js +2 -2
- package/dist/core/capabilities/engine-receipt.d.ts +5 -7
- package/dist/core/capabilities/engine-receipt.js +7 -9
- package/dist/core/capabilities/export-engine-version.d.ts +7 -0
- package/dist/core/capabilities/export-engine-version.js +9 -0
- package/dist/core/capabilities/export-game.d.ts +4 -0
- package/dist/core/capabilities/export-game.js +53 -10
- package/dist/core/capabilities/export-presets.d.ts +1 -1
- package/dist/core/capabilities/export-presets.js +1 -1
- package/dist/core/capabilities/fabricate-mesh.d.ts +2 -3
- package/dist/core/capabilities/fabricate-mesh.js +2 -3
- package/dist/core/capabilities/frame-quality.d.ts +4 -4
- package/dist/core/capabilities/frame-quality.js +4 -4
- package/dist/core/capabilities/grep.d.ts +1 -1
- package/dist/core/capabilities/grep.js +1 -1
- package/dist/core/capabilities/image-generation.d.ts +12 -2
- package/dist/core/capabilities/image-generation.js +31 -2
- package/dist/core/capabilities/navigation/open.d.ts +1 -1
- package/dist/core/capabilities/navigation/open.js +1 -1
- package/dist/core/capabilities/navigation/render-product-map.js +1 -1
- package/dist/core/capabilities/navigation/targets.js +2 -2
- package/dist/core/capabilities/placement-batch.d.ts +1 -1
- package/dist/core/capabilities/placement-batch.js +2 -2
- package/dist/core/capabilities/play-determinism.d.ts +1 -1
- package/dist/core/capabilities/play-determinism.js +1 -1
- package/dist/core/capabilities/publish-build.d.ts +1 -1
- package/dist/core/capabilities/publish-build.js +21 -9
- package/dist/core/capabilities/publish-download.d.ts +1 -2
- package/dist/core/capabilities/publish-download.js +2 -3
- package/dist/core/capabilities/replace-node.d.ts +1 -3
- package/dist/core/capabilities/replace-node.js +1 -3
- package/dist/core/capabilities/runtime-control.d.ts +5 -7
- package/dist/core/capabilities/runtime-control.js +7 -9
- package/dist/core/capabilities/scene-batch.d.ts +1 -1
- package/dist/core/capabilities/scene-batch.js +1 -1
- package/dist/core/capabilities/scene-kind.js +1 -1
- package/dist/core/capabilities/scene-readback.d.ts +3 -4
- package/dist/core/capabilities/scene-readback.js +3 -4
- package/dist/core/capabilities/scene-script.d.ts +2 -2
- package/dist/core/capabilities/scene-script.js +2 -2
- package/dist/core/capabilities/seeing/shot-store.d.ts +1 -1
- package/dist/core/capabilities/seeing/shot-store.js +1 -1
- package/dist/core/capabilities/summer-bundle.d.ts +11 -3
- package/dist/core/capabilities/summer-bundle.js +21 -3
- package/dist/core/capabilities/surface-snap.d.ts +1 -1
- package/dist/core/capabilities/surface-snap.js +1 -1
- package/dist/core/capabilities/tool-dispatch.js +14 -3
- package/dist/core/capabilities/ui-control.d.ts +1 -2
- package/dist/core/capabilities/ui-control.js +1 -2
- package/dist/core/capabilities/world-snapshot.d.ts +1 -1
- package/dist/core/capabilities/world-snapshot.js +1 -1
- package/dist/core/capability-skew.d.ts +2 -3
- package/dist/core/capability-skew.js +4 -5
- package/dist/core/feedback/client.js +1 -1
- package/dist/core/launch-posture.d.ts +3 -3
- package/dist/core/launch-posture.js +3 -3
- package/dist/core/library-read.d.ts +7 -0
- package/dist/core/library-read.js +60 -9
- package/dist/core/library-search.d.ts +2 -0
- package/dist/core/templates.d.ts +1 -2
- package/dist/core/templates.js +1 -2
- package/dist/installer/agent-config.d.ts +25 -0
- package/dist/installer/agent-config.js +114 -3
- package/dist/installer/agent-table.d.ts +12 -0
- package/dist/installer/agent-table.js +9 -0
- package/dist/mcp/server.d.ts +2 -2
- package/dist/mcp/server.js +2 -2
- package/dist/mcp/tools/creator-tools.js +33 -4
- package/dist/mcp/tools/debug-tools.js +1 -1
- package/dist/mcp/tools/generate-tools.js +18 -37
- package/dist/mcp/tools/library-tools.d.ts +2 -1
- package/dist/mcp/tools/library-tools.js +1 -0
- package/dist/mcp/tools/navigation-tools.d.ts +1 -1
- package/dist/mcp/tools/navigation-tools.js +1 -1
- package/dist/mcp/tools/spatial-tools.js +1 -1
- package/dist/mcp/tools/visual-tools.js +2 -2
- package/dist/mcp/tools/with-engine.d.ts +1 -2
- package/dist/mcp/tools/with-engine.js +1 -2
- package/gemini-extension.json +5 -1
- package/hooks/session-start.sh +2 -2
- package/integrations/README.md +70 -76
- package/integrations/claude/README.md +0 -3
- package/integrations/codex/README.md +1 -2
- package/integrations/copilot-jetbrains/README.md +1 -1
- package/integrations/cursor/README.md +1 -2
- package/integrations/factory/README.md +15 -11
- package/integrations/mistral-vibe/README.md +1 -1
- package/integrations/opencode/README.md +1 -1
- package/integrations/roo-code/README.md +1 -1
- package/integrations/rovo-dev/README.md +1 -1
- package/integrations/trae/README.md +1 -1
- package/integrations/windsurf/README.md +3 -3
- package/library/references/collaborative-protocol/collaborative-protocol.md +5 -5
- package/library/references/gd-style/gd-style.md +4 -6
- package/library/references/godot-version/godot-version.md +3 -3
- package/library/references/headless-routing/headless-routing.md +2 -2
- package/library/references/mcp-fresh-chat-playbook/mcp-fresh-chat-playbook.md +2 -2
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +16 -15
- package/library/references/product-map/product-map.md +1 -1
- package/library/references/public-surface-language/public-surface-language.md +1 -1
- package/library/references/summer-folder/summer-folder.md +1 -1
- package/library/skills/3d-lighting/SKILL.md +4 -4
- package/library/skills/README.md +16 -6
- package/library/skills/adaptive-music/SKILL.md +7 -7
- package/library/skills/ai-characters-in-games/SKILL.md +1 -1
- package/library/skills/ambient-bed/SKILL.md +12 -14
- package/library/skills/animated-loop/SKILL.md +42 -42
- package/library/skills/animation-tree/SKILL.md +6 -6
- package/library/skills/art-direction/SKILL.md +8 -11
- package/library/skills/asset-strategy/SKILL.md +3 -3
- package/library/skills/audio-direction/SKILL.md +8 -10
- package/library/skills/auto-fire-targeting/SKILL.md +4 -3
- package/library/skills/bounded-engine-runs/SKILL.md +6 -7
- package/library/skills/brainstorm-game/SKILL.md +5 -5
- package/library/skills/brainstorming/SKILL.md +9 -15
- package/library/skills/celeste-momentum-platforming/SKILL.md +8 -13
- package/library/skills/celeste-momentum-platforming/resource.yaml +1 -1
- package/library/skills/character-animation-wiring/SKILL.md +2 -2
- package/library/skills/character-model/SKILL.md +15 -26
- package/library/skills/character-movement/SKILL.md +1 -1
- package/library/skills/character-movement/references/movement-3d.md +1 -1
- package/library/skills/character-portrait/SKILL.md +4 -4
- package/library/skills/cinematic-cutscene/SKILL.md +38 -38
- package/library/skills/combat-basics/SKILL.md +1 -1
- package/library/skills/compatibility-renderer-traps/SKILL.md +2 -2
- package/library/skills/concept-art/SKILL.md +3 -3
- package/library/skills/create-asset-sheet/SKILL.md +12 -31
- package/library/skills/debug/SKILL.md +5 -4
- package/library/skills/debugging-game-feel/SKILL.md +1 -4
- package/library/skills/debugging-game-feel/resource.yaml +1 -1
- package/library/skills/design-level/SKILL.md +5 -5
- package/library/skills/design-mechanic/SKILL.md +6 -6
- package/library/skills/design-npc/SKILL.md +8 -15
- package/library/skills/diagnosing-perf-regressions/SKILL.md +5 -10
- package/library/skills/dispatching-parallel-agents/SKILL.md +1 -19
- package/library/skills/environment-kit/SKILL.md +5 -5
- package/library/skills/export-and-ship/SKILL.md +24 -25
- package/library/skills/export-and-ship/resource.yaml +2 -1
- package/library/skills/fabricating-assets/SKILL.md +1 -1
- package/library/skills/facial-and-lipsync/SKILL.md +5 -5
- package/library/skills/fps-controller/SKILL.md +17 -16
- package/library/skills/fps-controller/resource.yaml +0 -2
- package/library/skills/game-feel/SKILL.md +6 -7
- package/library/skills/gameskill/SKILL.md +3 -3
- package/library/skills/gdscript-patterns/SKILL.md +3 -3
- package/library/skills/generate-motion/SKILL.md +5 -22
- package/library/skills/godogen-visual-proof-game-generation/SKILL.md +72 -90
- package/library/skills/godogen-visual-proof-game-generation/resource.yaml +0 -1
- package/library/skills/grow-analytics/SKILL.md +21 -0
- package/library/skills/grow-analytics/resource.yaml +27 -0
- package/library/skills/headless-scripting/SKILL.md +8 -8
- package/library/skills/instantiate-asset-pack/SKILL.md +7 -3
- package/library/skills/investigating-bugs/SKILL.md +3 -11
- package/library/skills/investigating-bugs/condition-based-waiting.md +0 -10
- package/library/skills/investigating-bugs/root-cause-tracing.md +0 -8
- package/library/skills/lumera-single-image-scene-reconstruction/SKILL.md +15 -15
- package/library/skills/lumera-single-image-scene-reconstruction/resource.yaml +0 -1
- package/library/skills/make-game/SKILL.md +3 -5
- package/library/skills/make-game/references/2d-platformer.md +1 -1
- package/library/skills/multiplayer-project/SKILL.md +1 -1
- package/library/skills/multiplayer-testing/SKILL.md +4 -4
- package/library/skills/music-track/SKILL.md +40 -38
- package/library/skills/new-project/SKILL.md +4 -6
- package/library/skills/organic-model/SKILL.md +9 -9
- package/library/skills/pixel-art/SKILL.md +6 -6
- package/library/skills/play/SKILL.md +3 -4
- package/library/skills/playtesting-a-feature/SKILL.md +4 -6
- package/library/skills/procedural-animation/SKILL.md +10 -11
- package/library/skills/prop-model/SKILL.md +4 -4
- package/library/skills/psx-retro-rendering/SKILL.md +20 -20
- package/library/skills/psx-retro-rendering/resource.yaml +2 -3
- package/library/skills/publish-your-game/SKILL.md +21 -0
- package/library/skills/publish-your-game/resource.yaml +29 -0
- package/library/skills/realtime-wet-surfaces/SKILL.md +15 -15
- package/library/skills/realtime-wet-surfaces/resource.yaml +1 -2
- package/library/skills/remote-deploy/SKILL.md +20 -21
- package/library/skills/retarget/SKILL.md +19 -23
- package/library/skills/running-in-the-cloud/SKILL.md +5 -5
- package/library/skills/scene-composition/SKILL.md +5 -5
- package/library/skills/scene-hierarchy-design/SKILL.md +3 -3
- package/library/skills/scene-hierarchy-design/resource.yaml +0 -1
- package/library/skills/scene-scripting/SKILL.md +3 -3
- package/library/skills/scene-to-level/SKILL.md +28 -31
- package/library/skills/skill-create/SKILL.md +1 -1
- package/library/skills/skill-improve/SKILL.md +9 -9
- package/library/skills/skill-test/SKILL.md +10 -10
- package/library/skills/skintokens-auto-rigging/SKILL.md +18 -18
- package/library/skills/skintokens-auto-rigging/resource.yaml +3 -3
- package/library/skills/skybox-panorama/SKILL.md +8 -9
- package/library/skills/sound-effect/SKILL.md +19 -19
- package/library/skills/spatial-placement/SKILL.md +2 -2
- package/library/skills/spatial-placement/references/kit-placement-tools.md +12 -22
- package/library/skills/sprite-sheet/SKILL.md +10 -10
- package/library/skills/store-art/SKILL.md +23 -0
- package/library/skills/store-art/resource.yaml +30 -0
- package/library/skills/store-listing/SKILL.md +21 -0
- package/library/skills/store-listing/resource.yaml +25 -0
- package/library/skills/summer-analytics/SKILL.md +3 -3
- package/library/skills/summer-analytics/resource.yaml +1 -0
- package/library/skills/summer-friends/SKILL.md +10 -10
- package/library/skills/summer-leaderboards/SKILL.md +6 -6
- package/library/skills/summer-match-results/SKILL.md +13 -13
- package/library/skills/summer-matchmaking/SKILL.md +11 -11
- package/library/skills/summer-parties/SKILL.md +9 -9
- package/library/skills/summer-player-data/SKILL.md +12 -12
- package/library/skills/summer-store/SKILL.md +6 -6
- package/library/skills/summer-world-chat/SKILL.md +9 -9
- package/library/skills/summer-world-saves/SKILL.md +7 -7
- package/library/skills/text-to-motion/rigging-for-text-motion.md +1 -1
- package/library/skills/tileable-texture/SKILL.md +1 -1
- package/library/skills/trailer-shot/SKILL.md +36 -36
- package/library/skills/tune-performance/SKILL.md +13 -13
- package/library/skills/tune-performance/resource.yaml +1 -1
- package/library/skills/ui-graphics/SKILL.md +7 -7
- package/library/skills/use-widget-asset/SKILL.md +7 -7
- package/library/skills/using-summer/SKILL.md +5 -7
- package/library/skills/vehicle-model/SKILL.md +6 -7
- package/library/skills/vehicle-model/resource.yaml +2 -2
- package/library/skills/verification-before-completion/SKILL.md +5 -6
- package/library/skills/vfx-dissolve/SKILL.md +4 -4
- package/library/skills/vfx-fire/SKILL.md +6 -6
- package/library/skills/vfx-fire/_building-blocks/additive-billboard-particles.md +3 -3
- package/library/skills/vfx-hit-spark/SKILL.md +4 -4
- package/library/skills/vfx-hit-spark/_building-blocks/additive-billboard-particles.md +3 -3
- package/library/skills/vfx-lightning/SKILL.md +5 -5
- package/library/skills/vfx-lightning/_building-blocks/additive-billboard-particles.md +3 -3
- package/library/skills/vfx-lightning/_building-blocks/trauma-shake-snippet.md +9 -3
- package/library/skills/vfx-magic-glow/SKILL.md +4 -4
- package/library/skills/vfx-magic-glow/_building-blocks/additive-billboard-particles.md +3 -3
- package/library/skills/vfx-muzzle-flash/SKILL.md +5 -5
- package/library/skills/vfx-muzzle-flash/_building-blocks/additive-billboard-particles.md +3 -3
- package/library/skills/vfx-muzzle-flash/_building-blocks/trauma-shake-snippet.md +9 -3
- package/library/skills/vfx-smoke/SKILL.md +5 -5
- package/library/skills/vfx-water-ripple/SKILL.md +2 -2
- package/library/skills/vfx-water-ripple/_building-blocks/additive-billboard-particles.md +3 -3
- package/library/skills/voice-line/SKILL.md +9 -9
- package/library/skills/world-building-3d/references/spatial-tools.md +2 -2
- package/library/skills/writing-plans/SKILL.md +4 -4
- package/library/skills/writing-skills/SKILL.md +8 -8
- package/library/tools/capture-gameplay/resource.yaml +66 -1
- package/library/tools/creator-publish/resource.yaml +4 -4
- package/library/tools/creator-releases/resource.yaml +5 -5
- package/library/tools/export-game/resource.yaml +3 -0
- package/library/tools/generate-image/resource.yaml +4 -2
- package/library/tools/publish-build/resource.yaml +5 -1
- package/package.json +1 -1
- package/registry/generated/counts.json +2 -2
- package/registry/generated/gemini-extension.json +5 -1
- package/registry/generated/index.json +280 -137
- package/registry/generated/marketplace.claude.json +1 -1
- package/registry/generated/plugin.claude.json +5 -1
- package/registry/generated/plugin.codex.json +5 -1
- package/registry/generated/plugin.cursor.json +5 -1
- package/registry/generated/plugin.factory.json +5 -1
- package/registry/generated/skills-index.md +16 -6
- package/registry/generated/skills-registry.json +57 -5
- package/registry/schemas/domains.json +1 -1
- 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),
|
|
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 (
|
|
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
|
-
|
|
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 {
|
package/dist/core/templates.d.ts
CHANGED
|
@@ -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
|
package/dist/core/templates.js
CHANGED
|
@@ -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
|
|
476
|
-
const
|
|
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({
|
package/dist/mcp/server.d.ts
CHANGED
|
@@ -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
|
|
10
|
-
*
|
|
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
|
package/dist/mcp/server.js
CHANGED
|
@@ -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
|
|
38
|
-
*
|
|
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
|
-
|
|
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", "
|
|
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;
|
|
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"
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
597
|
-
- "kling" — Kling 2 Master, high quality
|
|
598
|
-
- "kling-turbo" — Kling 2.5 Turbo Pro
|
|
599
|
-
- "minimax" — MiniMax
|
|
600
|
-
- "veo3" — Google Veo 3, premium
|
|
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),
|
|
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
|