summer-engine 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -24
- package/dist/bin/summer.js +4 -0
- package/dist/commands/doctor.d.ts +26 -0
- package/dist/commands/doctor.js +248 -0
- package/dist/commands/doctor.test.d.ts +1 -0
- package/dist/commands/doctor.test.js +29 -0
- package/dist/commands/mcp.js +49 -0
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +128 -0
- package/dist/commands/skills.js +246 -107
- package/dist/lib/agent-config.d.ts +39 -0
- package/dist/lib/agent-config.js +284 -0
- package/dist/lib/agent-config.test.d.ts +1 -0
- package/dist/lib/agent-config.test.js +80 -0
- package/dist/lib/api-client.d.ts +19 -2
- package/dist/lib/api-client.js +129 -14
- package/dist/lib/skills-registry.d.ts +69 -0
- package/dist/lib/skills-registry.js +118 -0
- package/dist/mcp/server.js +2 -0
- package/dist/mcp/tools/asset-tools.js +14 -5
- package/dist/mcp/tools/generate-tools.d.ts +2 -0
- package/dist/mcp/tools/generate-tools.js +422 -0
- package/dist/mcp/tools/project-tools.js +100 -23
- package/dist/mcp/tools/with-engine.js +3 -53
- package/package.json +5 -2
- package/skills/asset-strategy/SKILL.md +235 -0
- package/skills/gdscript-patterns/SKILL.md +2 -2
- package/skills/make-game/SKILL.md +441 -0
- package/skills/scene-composition/SKILL.md +1 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
export const AGENT_CLIENTS = [
|
|
2
|
+
"summer",
|
|
3
|
+
"codex",
|
|
4
|
+
"claude-code",
|
|
5
|
+
"cursor",
|
|
6
|
+
"windsurf",
|
|
7
|
+
];
|
|
8
|
+
const ALL_CLIENTS = AGENT_CLIENTS;
|
|
9
|
+
export const SKILL_REGISTRY = [
|
|
10
|
+
{
|
|
11
|
+
name: "gdscript-patterns",
|
|
12
|
+
public: true,
|
|
13
|
+
category: "scripting",
|
|
14
|
+
clients: ALL_CLIENTS,
|
|
15
|
+
recommended: true,
|
|
16
|
+
requiresMcpTools: [
|
|
17
|
+
"summer_set_prop",
|
|
18
|
+
"summer_connect_signal",
|
|
19
|
+
"summer_input_map_bind",
|
|
20
|
+
"summer_get_script_errors",
|
|
21
|
+
],
|
|
22
|
+
testScenario: "Create a typed GDScript with a signal, attach it to a node, connect the signal, and verify script errors.",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: "scene-composition",
|
|
26
|
+
public: true,
|
|
27
|
+
category: "scene",
|
|
28
|
+
clients: ALL_CLIENTS,
|
|
29
|
+
recommended: true,
|
|
30
|
+
requiresMcpTools: [
|
|
31
|
+
"summer_create_scene",
|
|
32
|
+
"summer_open_scene",
|
|
33
|
+
"summer_get_scene_tree",
|
|
34
|
+
"summer_add_node",
|
|
35
|
+
"summer_instantiate_scene",
|
|
36
|
+
"summer_save_scene",
|
|
37
|
+
],
|
|
38
|
+
testScenario: "Build a small reusable player scene, instantiate it in a main scene, and save both scenes through Summer tools.",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "fps-controller",
|
|
42
|
+
public: true,
|
|
43
|
+
category: "gameplay",
|
|
44
|
+
clients: ALL_CLIENTS,
|
|
45
|
+
recommended: true,
|
|
46
|
+
requiresMcpTools: [
|
|
47
|
+
"summer_add_node",
|
|
48
|
+
"summer_set_prop",
|
|
49
|
+
"summer_set_resource_property",
|
|
50
|
+
"summer_input_map_bind",
|
|
51
|
+
],
|
|
52
|
+
testScenario: "Create a CharacterBody3D player with collision, camera, input actions, and a host-edited controller script.",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "3d-lighting",
|
|
56
|
+
public: true,
|
|
57
|
+
category: "lighting",
|
|
58
|
+
clients: ALL_CLIENTS,
|
|
59
|
+
recommended: true,
|
|
60
|
+
requiresMcpTools: [
|
|
61
|
+
"summer_add_node",
|
|
62
|
+
"summer_set_prop",
|
|
63
|
+
"summer_set_resource_property",
|
|
64
|
+
],
|
|
65
|
+
testScenario: "Add sun, fill lighting, WorldEnvironment, and shadow settings to a simple 3D scene.",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "ui-basics",
|
|
69
|
+
public: true,
|
|
70
|
+
category: "ui",
|
|
71
|
+
clients: ALL_CLIENTS,
|
|
72
|
+
recommended: true,
|
|
73
|
+
requiresMcpTools: [
|
|
74
|
+
"summer_add_node",
|
|
75
|
+
"summer_set_prop",
|
|
76
|
+
"summer_connect_signal",
|
|
77
|
+
],
|
|
78
|
+
testScenario: "Create a HUD CanvasLayer with labels, a ProgressBar, and a connected Button signal.",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: "asset-strategy",
|
|
82
|
+
public: true,
|
|
83
|
+
category: "assets",
|
|
84
|
+
clients: ALL_CLIENTS,
|
|
85
|
+
recommended: true,
|
|
86
|
+
requiresMcpTools: [
|
|
87
|
+
"summer_search_assets",
|
|
88
|
+
"summer_import_asset",
|
|
89
|
+
"summer_import_from_url",
|
|
90
|
+
"summer_generate_image",
|
|
91
|
+
"summer_generate_3d",
|
|
92
|
+
"summer_generate_audio",
|
|
93
|
+
],
|
|
94
|
+
testScenario: "Choose library, generated, or primitive assets for a small level and import one asset into the project.",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "make-game",
|
|
98
|
+
public: true,
|
|
99
|
+
category: "workflow",
|
|
100
|
+
clients: ALL_CLIENTS,
|
|
101
|
+
recommended: false,
|
|
102
|
+
requiresMcpTools: [
|
|
103
|
+
"summer_get_agent_playbook",
|
|
104
|
+
"summer_get_project_context",
|
|
105
|
+
"summer_create_scene",
|
|
106
|
+
"summer_open_scene",
|
|
107
|
+
"summer_save_scene",
|
|
108
|
+
"summer_add_node",
|
|
109
|
+
"summer_set_prop",
|
|
110
|
+
"summer_set_resource_property",
|
|
111
|
+
"summer_instantiate_scene",
|
|
112
|
+
"summer_play",
|
|
113
|
+
"summer_stop",
|
|
114
|
+
"summer_get_diagnostics",
|
|
115
|
+
],
|
|
116
|
+
testScenario: "Build one narrow playable prototype from requirements through scene setup, host-edited scripts, and diagnostics.",
|
|
117
|
+
},
|
|
118
|
+
];
|
package/dist/mcp/server.js
CHANGED
|
@@ -6,6 +6,7 @@ import { registerSceneTools } from "./tools/scene-tools.js";
|
|
|
6
6
|
import { registerDebugTools } from "./tools/debug-tools.js";
|
|
7
7
|
import { registerProjectTools } from "./tools/project-tools.js";
|
|
8
8
|
import { registerAssetTools } from "./tools/asset-tools.js";
|
|
9
|
+
import { registerGenerateTools } from "./tools/generate-tools.js";
|
|
9
10
|
const require = createRequire(import.meta.url);
|
|
10
11
|
const { version } = require("../../package.json");
|
|
11
12
|
let cachedClient = null;
|
|
@@ -34,6 +35,7 @@ export async function startMcpServer() {
|
|
|
34
35
|
registerDebugTools(server);
|
|
35
36
|
registerProjectTools(server);
|
|
36
37
|
registerAssetTools(server);
|
|
38
|
+
registerGenerateTools(server);
|
|
37
39
|
const transport = new StdioServerTransport();
|
|
38
40
|
await server.connect(transport);
|
|
39
41
|
process.stderr.write(`[summer-mcp] MCP server running v${version}.\n`);
|
|
@@ -56,6 +56,9 @@ async function searchAssetsApi(params) {
|
|
|
56
56
|
if (params.limit) {
|
|
57
57
|
searchParams.set("limit", String(params.limit));
|
|
58
58
|
}
|
|
59
|
+
if (params.source) {
|
|
60
|
+
searchParams.set("source", params.source);
|
|
61
|
+
}
|
|
59
62
|
const res = await fetch(`${GATEWAY_URL}/api/mcp/assets?${searchParams}`, {
|
|
60
63
|
headers: { Authorization: `Bearer ${token}` },
|
|
61
64
|
signal: AbortSignal.timeout(15000),
|
|
@@ -85,17 +88,23 @@ async function searchAssetsApi(params) {
|
|
|
85
88
|
return data;
|
|
86
89
|
}
|
|
87
90
|
export function registerAssetTools(server) {
|
|
88
|
-
server.tool("summer_search_assets", `Search
|
|
91
|
+
server.tool("summer_search_assets", `Search for game assets in the Summer Engine ecosystem.
|
|
92
|
+
|
|
93
|
+
Sources:
|
|
94
|
+
- "library" (default) — Public asset library (25k+ community assets). Requires Pro plan.
|
|
95
|
+
- "my_assets" — Your own generated/uploaded assets. Free for all users. Query is optional.
|
|
96
|
+
- "all" — Search both library and your assets.
|
|
89
97
|
|
|
90
98
|
Uses hybrid search: keywords + semantic similarity. Finds assets by name AND by meaning.
|
|
91
99
|
Returns asset names, types, preview URLs, and import-ready file URLs.
|
|
92
100
|
|
|
93
|
-
Requires authentication
|
|
94
|
-
query: z.string().describe("Natural language search, e.g. 'low-poly tree', 'sci-fi weapon',
|
|
101
|
+
Requires authentication: run 'npx summer-engine login' first.`, {
|
|
102
|
+
query: z.string().describe("Natural language search, e.g. 'low-poly tree', 'sci-fi weapon'. For my_assets, can be empty to list recent."),
|
|
95
103
|
assetType: z.enum(["2d_image", "animation", "3d_model", "audio", "music", "all"]).default("all").describe("Filter by asset type"),
|
|
96
104
|
limit: z.number().default(10).describe("Max results (1-20)"),
|
|
97
|
-
|
|
98
|
-
|
|
105
|
+
source: z.enum(["library", "my_assets", "all"]).default("library").describe("Where to search: library (public 25k+), my_assets (your generated assets), all"),
|
|
106
|
+
}, async ({ query, assetType, limit, source }) => {
|
|
107
|
+
const result = await searchAssetsApi({ query, assetType, limit: Math.min(limit, 20), source });
|
|
99
108
|
if (result.error) {
|
|
100
109
|
return {
|
|
101
110
|
content: [
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { writeFile, mkdir } from "fs/promises";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { getAuthToken } from "../../lib/auth.js";
|
|
6
|
+
const GATEWAY_URL = process.env.SUMMER_GATEWAY_URL || "https://www.summerengine.com";
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Shared helpers
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
async function mcpGenerate(endpoint, body, timeoutMs = 120_000) {
|
|
11
|
+
const token = await getAuthToken();
|
|
12
|
+
if (!token) {
|
|
13
|
+
return {
|
|
14
|
+
error: "Not signed in. Run in your terminal:\n npx summer-engine login\nOr open: https://www.summerengine.com/login",
|
|
15
|
+
status: 401,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const res = await fetch(`${GATEWAY_URL}${endpoint}`, {
|
|
19
|
+
method: "POST",
|
|
20
|
+
headers: {
|
|
21
|
+
Authorization: `Bearer ${token}`,
|
|
22
|
+
"Content-Type": "application/json",
|
|
23
|
+
},
|
|
24
|
+
body: JSON.stringify(body),
|
|
25
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
26
|
+
});
|
|
27
|
+
const data = await res.json();
|
|
28
|
+
if (!res.ok) {
|
|
29
|
+
let message = data.message || data.error || "Request failed";
|
|
30
|
+
if (res.status === 402) {
|
|
31
|
+
message =
|
|
32
|
+
(data.message || "Insufficient credits.") +
|
|
33
|
+
"\nTop up at: https://www.summerengine.com/dashboard\nOr upgrade your plan at: https://www.summerengine.com/pricing";
|
|
34
|
+
}
|
|
35
|
+
if (res.status === 401) {
|
|
36
|
+
message =
|
|
37
|
+
(data.message || "Auth token expired.") +
|
|
38
|
+
"\nRe-authenticate:\n npx summer-engine login";
|
|
39
|
+
}
|
|
40
|
+
return { error: message, data, status: res.status };
|
|
41
|
+
}
|
|
42
|
+
return { data, status: res.status };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Download an image URL to a temp file so the AI can Read it and show the user.
|
|
46
|
+
*/
|
|
47
|
+
async function downloadToTemp(url, prefix, ext = "png") {
|
|
48
|
+
try {
|
|
49
|
+
const dir = join(tmpdir(), "summer-gen");
|
|
50
|
+
await mkdir(dir, { recursive: true });
|
|
51
|
+
const filename = `${prefix}-${Date.now()}.${ext}`;
|
|
52
|
+
const filepath = join(dir, filename);
|
|
53
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(30_000) });
|
|
54
|
+
if (!res.ok)
|
|
55
|
+
return null;
|
|
56
|
+
const buffer = Buffer.from(await res.arrayBuffer());
|
|
57
|
+
await writeFile(filepath, buffer);
|
|
58
|
+
return filepath;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Poll a job until completed, failed, or timeout.
|
|
66
|
+
* Returns the final job status object.
|
|
67
|
+
*/
|
|
68
|
+
async function pollJob(jobId, maxWaitMs = 300_000, intervalMs = 5_000) {
|
|
69
|
+
const token = await getAuthToken();
|
|
70
|
+
if (!token)
|
|
71
|
+
return { error: "Not signed in" };
|
|
72
|
+
const deadline = Date.now() + maxWaitMs;
|
|
73
|
+
while (Date.now() < deadline) {
|
|
74
|
+
try {
|
|
75
|
+
const res = await fetch(`${GATEWAY_URL}/api/mcp/jobs/${encodeURIComponent(jobId)}`, {
|
|
76
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
77
|
+
signal: AbortSignal.timeout(15_000),
|
|
78
|
+
});
|
|
79
|
+
const data = await res.json();
|
|
80
|
+
if (!res.ok)
|
|
81
|
+
return { error: data.message || "Poll failed", data };
|
|
82
|
+
if (data.status === "completed")
|
|
83
|
+
return { data };
|
|
84
|
+
if (data.status === "failed")
|
|
85
|
+
return { error: data.error || "Job failed", data };
|
|
86
|
+
// Still running — wait and retry
|
|
87
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
88
|
+
// Back off slightly after 30s
|
|
89
|
+
if (Date.now() - (deadline - maxWaitMs) > 30_000) {
|
|
90
|
+
intervalMs = Math.min(intervalMs * 1.2, 15_000);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
95
|
+
return { error: `Poll error: ${msg}` };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { error: `Timed out after ${maxWaitMs / 1000}s. Use summer_check_job({ jobId: "${jobId}" }) to check later.` };
|
|
99
|
+
}
|
|
100
|
+
function errorResult(message, extra) {
|
|
101
|
+
return {
|
|
102
|
+
content: [
|
|
103
|
+
{
|
|
104
|
+
type: "text",
|
|
105
|
+
text: JSON.stringify({ error: true, message, ...extra }, null, 2),
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
isError: true,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function successResult(data) {
|
|
112
|
+
return {
|
|
113
|
+
content: [
|
|
114
|
+
{
|
|
115
|
+
type: "text",
|
|
116
|
+
text: JSON.stringify(data, null, 2),
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Registration
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
export function registerGenerateTools(server) {
|
|
125
|
+
// ========================================================================
|
|
126
|
+
// summer_generate_image
|
|
127
|
+
// ========================================================================
|
|
128
|
+
server.tool("summer_generate_image", `Generate an image using AI models via Summer Engine Studio.
|
|
129
|
+
|
|
130
|
+
Known models:
|
|
131
|
+
- "nano-banana-2" (default) — High quality, supports txt2img and img2img
|
|
132
|
+
- "gemini-flash" — Google Gemini 2.5 Flash, fast
|
|
133
|
+
- "flux-2" — FLUX.2, good for specific styles
|
|
134
|
+
- Any fal-ai model ID works as a passthrough
|
|
135
|
+
|
|
136
|
+
Two modes:
|
|
137
|
+
- txt2img (default): just pass prompt
|
|
138
|
+
- img2img (edit): pass prompt + referenceImageUrl to edit/transform an existing image
|
|
139
|
+
|
|
140
|
+
Style presets: "realistic" (default), "cartoon", "anime", "none"
|
|
141
|
+
|
|
142
|
+
The 'options' object is passed directly to the AI provider for full control.
|
|
143
|
+
|
|
144
|
+
Returns the asset with fileUrl (hosted) and localPath (temp file on disk).
|
|
145
|
+
Use the Read tool on localPath to show the image to the user for approval.
|
|
146
|
+
|
|
147
|
+
Requires authentication: run 'npx summer-engine login' first.`, {
|
|
148
|
+
prompt: z.string().describe("Description of the image to generate"),
|
|
149
|
+
model: z
|
|
150
|
+
.string()
|
|
151
|
+
.default("nano-banana-2")
|
|
152
|
+
.describe("Model name or full provider ID"),
|
|
153
|
+
style: z
|
|
154
|
+
.string()
|
|
155
|
+
.default("realistic")
|
|
156
|
+
.describe("Style preset: realistic, cartoon, anime, or 'none' to skip"),
|
|
157
|
+
referenceImageUrl: z
|
|
158
|
+
.string()
|
|
159
|
+
.optional()
|
|
160
|
+
.describe("Source image URL for img2img / edit mode. The prompt describes how to transform this image."),
|
|
161
|
+
options: z
|
|
162
|
+
.record(z.any())
|
|
163
|
+
.optional()
|
|
164
|
+
.describe("Provider-specific params (guidance_scale, seed, image_size, negative_prompt, etc.)"),
|
|
165
|
+
}, async ({ prompt, model, style, referenceImageUrl, options }) => {
|
|
166
|
+
const result = await mcpGenerate("/api/mcp/generate/image", {
|
|
167
|
+
prompt,
|
|
168
|
+
model,
|
|
169
|
+
style,
|
|
170
|
+
referenceImageUrl,
|
|
171
|
+
options,
|
|
172
|
+
});
|
|
173
|
+
if (result.error) {
|
|
174
|
+
return errorResult(result.error, result.data);
|
|
175
|
+
}
|
|
176
|
+
// Download to temp so the AI can Read and show the image
|
|
177
|
+
const imageUrl = result.data?.asset?.fileUrl || result.data?.asset?.thumbnailUrl;
|
|
178
|
+
let localPath = null;
|
|
179
|
+
if (imageUrl) {
|
|
180
|
+
localPath = await downloadToTemp(imageUrl, "img");
|
|
181
|
+
}
|
|
182
|
+
return successResult({
|
|
183
|
+
...result.data,
|
|
184
|
+
localPath,
|
|
185
|
+
hint: localPath
|
|
186
|
+
? `Image saved to ${localPath}. Use the Read tool on this path to show it to the user.`
|
|
187
|
+
: undefined,
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
// ========================================================================
|
|
191
|
+
// summer_generate_audio
|
|
192
|
+
// ========================================================================
|
|
193
|
+
server.tool("summer_generate_audio", `Generate audio using AI providers via Summer Engine Studio.
|
|
194
|
+
|
|
195
|
+
Capabilities:
|
|
196
|
+
- "text_to_speech" — Convert text to spoken audio. Requires 'text'. Optional 'voiceId'.
|
|
197
|
+
- "sound_effects" — Generate sound effects from description. Requires 'text'. Optional 'durationSeconds'.
|
|
198
|
+
- "music" — Generate background music. Requires 'prompt'. Optional 'durationSeconds'.
|
|
199
|
+
- "text_to_dialogue" — Multi-voice dialogue. Requires 'inputs' array of {text, voiceId} objects.
|
|
200
|
+
|
|
201
|
+
The 'options' object is passed directly to ElevenLabs, so you can set any
|
|
202
|
+
provider-specific parameter: stability, similarity_boost, style, speed, etc.
|
|
203
|
+
|
|
204
|
+
Returns the generated audio URL and asset metadata.
|
|
205
|
+
Requires authentication: run 'npx summer-engine login' first.`, {
|
|
206
|
+
capability: z
|
|
207
|
+
.string()
|
|
208
|
+
.describe("Type of audio: text_to_speech, sound_effects, music, text_to_dialogue"),
|
|
209
|
+
text: z
|
|
210
|
+
.string()
|
|
211
|
+
.optional()
|
|
212
|
+
.describe("Text for TTS, or description for sound effects"),
|
|
213
|
+
prompt: z
|
|
214
|
+
.string()
|
|
215
|
+
.optional()
|
|
216
|
+
.describe("Music description (for 'music' capability)"),
|
|
217
|
+
voiceId: z
|
|
218
|
+
.string()
|
|
219
|
+
.optional()
|
|
220
|
+
.describe("ElevenLabs voice ID for TTS"),
|
|
221
|
+
modelId: z
|
|
222
|
+
.string()
|
|
223
|
+
.optional()
|
|
224
|
+
.describe("ElevenLabs model ID (e.g. 'eleven_multilingual_v2')"),
|
|
225
|
+
durationSeconds: z
|
|
226
|
+
.number()
|
|
227
|
+
.optional()
|
|
228
|
+
.describe("Duration in seconds for SFX or music"),
|
|
229
|
+
inputs: z
|
|
230
|
+
.array(z.object({ text: z.string(), voiceId: z.string() }))
|
|
231
|
+
.optional()
|
|
232
|
+
.describe("Dialogue lines for text_to_dialogue"),
|
|
233
|
+
options: z
|
|
234
|
+
.record(z.any())
|
|
235
|
+
.optional()
|
|
236
|
+
.describe("Provider-specific params (stability, similarity_boost, style, speed, etc.)"),
|
|
237
|
+
}, async ({ capability, text, prompt, voiceId, modelId, durationSeconds, inputs, options }) => {
|
|
238
|
+
const body = { capability };
|
|
239
|
+
if (text)
|
|
240
|
+
body.text = text;
|
|
241
|
+
if (prompt)
|
|
242
|
+
body.prompt = prompt;
|
|
243
|
+
if (voiceId)
|
|
244
|
+
body.voiceId = voiceId;
|
|
245
|
+
if (modelId)
|
|
246
|
+
body.modelId = modelId;
|
|
247
|
+
if (durationSeconds)
|
|
248
|
+
body.durationSeconds = durationSeconds;
|
|
249
|
+
if (inputs)
|
|
250
|
+
body.inputs = inputs;
|
|
251
|
+
if (options)
|
|
252
|
+
body.options = options;
|
|
253
|
+
const result = await mcpGenerate("/api/mcp/generate/audio", body);
|
|
254
|
+
if (result.error) {
|
|
255
|
+
return errorResult(result.error, result.data);
|
|
256
|
+
}
|
|
257
|
+
return successResult(result.data);
|
|
258
|
+
});
|
|
259
|
+
// ========================================================================
|
|
260
|
+
// summer_generate_3d
|
|
261
|
+
// ========================================================================
|
|
262
|
+
server.tool("summer_generate_3d", `Generate a 3D model via Summer Engine Studio.
|
|
263
|
+
|
|
264
|
+
Available models:
|
|
265
|
+
- "hunyuan" (default) — Hunyuan 3D v3.1 Pro, high quality
|
|
266
|
+
- "trellis" — Trellis 2, fast and detailed
|
|
267
|
+
- "meshy" — Meshy, legacy option
|
|
268
|
+
- Any fal-ai model ID works as a passthrough
|
|
269
|
+
|
|
270
|
+
Available kinds:
|
|
271
|
+
- "text-to-3d" (default) — From text description. Requires 'prompt'.
|
|
272
|
+
Internally generates a 3D-optimized reference image first, then converts to 3D.
|
|
273
|
+
- "image-to-3d" — From your own image. Requires 'imageUrl'.
|
|
274
|
+
- "texture" — Generate textures for a model. Requires 'imageUrl'.
|
|
275
|
+
|
|
276
|
+
By default, waits for completion (up to 5 min) and returns the result directly.
|
|
277
|
+
Set wait=false to get the jobId immediately and poll manually with summer_check_job.
|
|
278
|
+
|
|
279
|
+
Requires authentication: run 'npx summer-engine login' first.`, {
|
|
280
|
+
prompt: z
|
|
281
|
+
.string()
|
|
282
|
+
.optional()
|
|
283
|
+
.describe("Description of the 3D model, e.g. 'a low-poly treasure chest'"),
|
|
284
|
+
kind: z
|
|
285
|
+
.string()
|
|
286
|
+
.default("text-to-3d")
|
|
287
|
+
.describe("Generation type: text-to-3d, image-to-3d, texture"),
|
|
288
|
+
model: z
|
|
289
|
+
.string()
|
|
290
|
+
.default("hunyuan")
|
|
291
|
+
.describe("Model: hunyuan (default), trellis, meshy, or any fal-ai model ID"),
|
|
292
|
+
imageUrl: z
|
|
293
|
+
.string()
|
|
294
|
+
.optional()
|
|
295
|
+
.describe("Source image URL for image-to-3d or texture"),
|
|
296
|
+
wait: z
|
|
297
|
+
.boolean()
|
|
298
|
+
.default(true)
|
|
299
|
+
.describe("Wait for completion (default true, up to 5 min). Set false to get jobId immediately."),
|
|
300
|
+
options: z
|
|
301
|
+
.record(z.any())
|
|
302
|
+
.optional()
|
|
303
|
+
.describe("Provider-specific params (target_polycount, topology, art_style, etc.)"),
|
|
304
|
+
}, async ({ prompt, kind, model, imageUrl, wait, options }) => {
|
|
305
|
+
const result = await mcpGenerate("/api/mcp/generate/3d", {
|
|
306
|
+
prompt,
|
|
307
|
+
kind,
|
|
308
|
+
model,
|
|
309
|
+
imageUrl,
|
|
310
|
+
options,
|
|
311
|
+
});
|
|
312
|
+
if (result.error) {
|
|
313
|
+
return errorResult(result.error, result.data);
|
|
314
|
+
}
|
|
315
|
+
const jobId = result.data?.jobId;
|
|
316
|
+
// If not waiting or no jobId, return immediately
|
|
317
|
+
if (!wait || !jobId) {
|
|
318
|
+
return successResult(result.data);
|
|
319
|
+
}
|
|
320
|
+
// Auto-poll until done
|
|
321
|
+
const pollResult = await pollJob(jobId);
|
|
322
|
+
if (pollResult.error) {
|
|
323
|
+
return errorResult(pollResult.error, { ...pollResult.data, jobId });
|
|
324
|
+
}
|
|
325
|
+
return successResult({
|
|
326
|
+
...pollResult.data,
|
|
327
|
+
jobId,
|
|
328
|
+
message: "3D generation complete.",
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
// ========================================================================
|
|
332
|
+
// summer_generate_video
|
|
333
|
+
// ========================================================================
|
|
334
|
+
server.tool("summer_generate_video", `Generate a video using AI models via Summer Engine Studio.
|
|
335
|
+
|
|
336
|
+
Known models (text-to-video):
|
|
337
|
+
- "ltx" (default) — LTX Video, fast (~$0.10)
|
|
338
|
+
- "kling" — Kling 2 Master, high quality (~$0.50)
|
|
339
|
+
- "kling-turbo" — Kling 2.5 Turbo Pro (~$0.30)
|
|
340
|
+
- "minimax" — MiniMax (~$0.25)
|
|
341
|
+
- "veo3" — Google Veo 3, premium (~$1.00)
|
|
342
|
+
|
|
343
|
+
Known models (image-to-video, when imageUrl provided):
|
|
344
|
+
- "ltx" (default), "kling", "minimax"
|
|
345
|
+
|
|
346
|
+
If imageUrl is provided, switches to image-to-video mode.
|
|
347
|
+
Pass provider-specific params in 'options' (negative_prompt, num_frames, etc.).
|
|
348
|
+
|
|
349
|
+
Returns the generated video URL and asset metadata.
|
|
350
|
+
Requires authentication: run 'npx summer-engine login' first.`, {
|
|
351
|
+
prompt: z
|
|
352
|
+
.string()
|
|
353
|
+
.describe("Description of the video content"),
|
|
354
|
+
model: z
|
|
355
|
+
.string()
|
|
356
|
+
.default("ltx")
|
|
357
|
+
.describe("Model name: ltx, kling, kling-turbo, minimax, veo3 (or any new model)"),
|
|
358
|
+
imageUrl: z
|
|
359
|
+
.string()
|
|
360
|
+
.optional()
|
|
361
|
+
.describe("Source image URL — if provided, uses image-to-video mode"),
|
|
362
|
+
duration: z
|
|
363
|
+
.number()
|
|
364
|
+
.default(5)
|
|
365
|
+
.describe("Duration in seconds (5 or 10)"),
|
|
366
|
+
aspectRatio: z
|
|
367
|
+
.string()
|
|
368
|
+
.default("16:9")
|
|
369
|
+
.describe("Aspect ratio: 16:9, 9:16, 1:1, 4:3"),
|
|
370
|
+
options: z
|
|
371
|
+
.record(z.any())
|
|
372
|
+
.optional()
|
|
373
|
+
.describe("Provider-specific params (negative_prompt, num_frames, guidance_scale, etc.)"),
|
|
374
|
+
}, async ({ prompt, model, imageUrl, duration, aspectRatio, options }) => {
|
|
375
|
+
const result = await mcpGenerate("/api/mcp/generate/video", {
|
|
376
|
+
prompt,
|
|
377
|
+
model,
|
|
378
|
+
imageUrl,
|
|
379
|
+
duration,
|
|
380
|
+
aspectRatio,
|
|
381
|
+
options,
|
|
382
|
+
});
|
|
383
|
+
if (result.error) {
|
|
384
|
+
return errorResult(result.error, result.data);
|
|
385
|
+
}
|
|
386
|
+
return successResult(result.data);
|
|
387
|
+
});
|
|
388
|
+
// ========================================================================
|
|
389
|
+
// summer_check_job
|
|
390
|
+
// ========================================================================
|
|
391
|
+
server.tool("summer_check_job", `Check the status of an async generation job.
|
|
392
|
+
|
|
393
|
+
Use this when you called summer_generate_3d with wait=false, or need to re-check
|
|
394
|
+
a job that timed out. Most of the time you won't need this — summer_generate_3d
|
|
395
|
+
waits automatically.
|
|
396
|
+
|
|
397
|
+
Status values: waiting, active, completed, failed, delayed, unknown.
|
|
398
|
+
|
|
399
|
+
Requires authentication: run 'npx summer-engine login' first.`, {
|
|
400
|
+
jobId: z.string().describe("The job ID returned by an async generation tool"),
|
|
401
|
+
}, async ({ jobId }) => {
|
|
402
|
+
const token = await getAuthToken();
|
|
403
|
+
if (!token) {
|
|
404
|
+
return errorResult("Not signed in. Run: npx summer-engine login");
|
|
405
|
+
}
|
|
406
|
+
try {
|
|
407
|
+
const res = await fetch(`${GATEWAY_URL}/api/mcp/jobs/${encodeURIComponent(jobId)}`, {
|
|
408
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
409
|
+
signal: AbortSignal.timeout(15_000),
|
|
410
|
+
});
|
|
411
|
+
const data = await res.json();
|
|
412
|
+
if (!res.ok) {
|
|
413
|
+
return errorResult(data.message || data.error || "Failed to check job status", data);
|
|
414
|
+
}
|
|
415
|
+
return successResult(data);
|
|
416
|
+
}
|
|
417
|
+
catch (err) {
|
|
418
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
419
|
+
return errorResult(`Job status check failed: ${msg}`);
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
}
|