@kolbo/mcp 1.31.5 → 1.31.7
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/package.json +1 -1
- package/skill/GENERATED.md +1 -1
- package/skill/SKILL.md +5 -2
- package/skill/VERSION +1 -1
- package/skill/references/models/music.md +4 -1
- package/src/apps/bridge.js +12 -0
- package/src/apps/theme.js +16 -8
- package/src/apps/widgets/generation.js +36 -19
- package/src/apps/widgets/transcript.js +3 -2
- package/src/tools/_shared.js +53 -0
- package/src/tools/generate.js +3 -2
package/package.json
CHANGED
package/skill/GENERATED.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# AUTO-GENERATED — do not edit
|
|
2
2
|
|
|
3
3
|
This skill/ tree is mirrored from kolbo-code (the single source of truth)
|
|
4
|
-
by .github/workflows/sync-skill-to-plugin.yml — synced from kolbo-code@
|
|
4
|
+
by .github/workflows/sync-skill-to-plugin.yml — synced from kolbo-code@53720a8.
|
|
5
5
|
|
|
6
6
|
It is the skill that 'npx @kolbo/mcp install' deploys into the user's agent.
|
|
7
7
|
To change it, edit packages/opencode/skills/kolbo/ in kolbo-code and push;
|
package/skill/SKILL.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
version: 0.5.
|
|
2
|
+
version: 0.5.1
|
|
3
3
|
name: kolbo
|
|
4
4
|
description: |
|
|
5
5
|
Generate, edit, or analyze creative media via the Kolbo AI MCP server:
|
|
@@ -113,6 +113,7 @@ Each `references/models/*.md` mirrors the matching skill prompt in `kolbo-api/sr
|
|
|
113
113
|
| `upload_media` / `list_media` / `get_media` / `get_media_stats` / `favorite_media` / `unfavorite_media` / `delete_media` / `restore_media` / `permanently_delete_media` / `move_media` / `bulk_*_media` / `*_media_folder` | Media library — see `workflows/media-library.md` |
|
|
114
114
|
| `create_visual_dna` / `list_visual_dnas` / `get_visual_dna` / `delete_visual_dna` | Visual DNA — see `workflows/visual-dna.md` |
|
|
115
115
|
| `list_moodboards` / `get_moodboard` / `list_presets` | Style overlays |
|
|
116
|
+
| `search_stock_media` / `get_stock_sources` / `get_stock_categories` / `get_stock_collections` / `get_stock_asset` / `analyze_script_for_stock` / `import_stock_asset` | Stock library (free, no credits) — EXISTING photos / videos / 3D / SFX / music. For stock **music** use `search_stock_media` with `mediaType: "music"` (semantic vibe query, e.g. "uplifting corporate background") → `get_stock_asset` for downloads. The older `*_music_library` tools are deprecated adapters over this — prefer the stock tools. |
|
|
116
117
|
| `chat_send_message` / `chat_list_conversations` / `chat_get_messages` | Kolbo chat with optional `media_urls` (up to 10 per call) |
|
|
117
118
|
| `app_builder_*` (9 tools) | Full React app generation — see `workflows/app-builder.md` |
|
|
118
119
|
| `publish_html_artifact` | Publish HTML / SVG / Mermaid to `sites.kolbo.ai`. Server dedupes by content hash. Strict CSP. |
|
|
@@ -149,7 +150,7 @@ A user-named tool — in any language — overrides every other rule. Recognized
|
|
|
149
150
|
1. **Check credits** ONCE per conversation (Step 0). Skip if already checked.
|
|
150
151
|
2. **Discover models** with `list_models` using a `type` filter — but **skip when the user names a specific model**.
|
|
151
152
|
3. **Pick the model**:
|
|
152
|
-
- User named one → use it.
|
|
153
|
+
- User named one → use it. Model identifiers resolve leniently — shorthand like `"z-image"` or `"nano banana 2"` auto-resolves to the exact identifier, so don't over-engineer exact-id lookups (`list_models` is still authoritative for constraints, caps, and pricing).
|
|
153
154
|
- Auto-select → only from "Auto-selectable" section (models with a `summary`). Cheapest fit. Prefer `[RECOMMENDED]` when cost is similar.
|
|
154
155
|
- Never auto-select from "Named-only" section.
|
|
155
156
|
4. **Validate inputs** against model caps — see `references/workflows/cost-and-validation.md`.
|
|
@@ -203,6 +204,8 @@ Existing video → modify → **single `generate_video_from_video` call** with s
|
|
|
203
204
|
|
|
204
205
|
**Use a TRUE video-to-video model.** Image-to-video models reject with `WRONG_MODEL_TYPE`. Valid: `wan/2-7-videoedit`, `happyhorse/video-edit`, `kling-video/o3-video-to-video`, or any model whose DB `type` includes `video_to_video` (use `list_models({ type: "video_to_video" })`).
|
|
205
206
|
|
|
207
|
+
**Motion-control / animate-move models invert the inputs**: `reference_images[0]` = the CHARACTER IMAGE to animate, `source_video` = the driving/reference video whose motion is transferred. Omitting the character image returns a `MOTION_CONTROL_INPUTS` error.
|
|
208
|
+
|
|
206
209
|
**Do NOT** decompose into frames. **Do NOT** re-fire if the first call returned URLs.
|
|
207
210
|
|
|
208
211
|
## ⚠️ Character-Driven Video — Frames First, Then Animate (CRITICAL)
|
package/skill/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.5.
|
|
1
|
+
0.5.1
|
|
@@ -8,9 +8,12 @@ Load this file when the user wants AI-generated **music** — full songs, lyrics
|
|
|
8
8
|
|
|
9
9
|
**Kolbo MCP routing:** call `generate_music`. Suno is a model option — use `list_models({ type: "music_gen" })` to see versions. Pass `instrumental` and `duration` as separate params; pass the Style/Description text as `style` and the Lyrics as `lyrics`.
|
|
10
10
|
|
|
11
|
+
**Wants an EXISTING track, not a new song?** ("background music", "stock music", "royalty-free track") → don't generate. Use `search_stock_media` with `mediaType: "music"` (semantic vibe query — "tense cinematic pulse", "uplifting corporate background") → `get_stock_asset` for download URLs. Free, no credits. The older `*_music_library` tools are deprecated adapters over the stock library — prefer the stock tools.
|
|
12
|
+
|
|
11
13
|
## CRITICAL Kolbo Platform Rules
|
|
12
14
|
|
|
13
15
|
- **Model version, duration, and instrumental toggle are MCP-tool params.** Don't write `v4.5`, `30 seconds`, or `instrumental: true` inside the prompt fields themselves.
|
|
16
|
+
- **Exact track length = `duration_seconds`** (clamped 5–300s). Only length-controllable models honor it (e.g. ElevenLabs Music, `music-v1`) — without it those models default to a ~10s track, so ALWAYS pass it for jingles/beds on those models. Suno ignores it and picks its own length.
|
|
14
17
|
- Suno generations have **two separate input fields**: a **Style / Description** field (`style` param) and a **Lyrics** field (`lyrics` param). Output your prompt as **TWO separate fenced code blocks** so the user (and the tool call) know exactly what goes where.
|
|
15
18
|
- Tell the user to run the prompt multiple times — Suno output varies significantly between generations, that's a feature. Use `num_generations` if the tool supports it, or fire 2–4 parallel `generate_music` calls.
|
|
16
19
|
|
|
@@ -85,7 +88,7 @@ Use Suno's section tags to control structure. Each tag goes on its own line, con
|
|
|
85
88
|
### Jingle / ad music (15–30s)
|
|
86
89
|
- `style`: short, punchy descriptor (`upbeat retail pop jingle, female vocal, claps, glossy production, summer energy`)
|
|
87
90
|
- `lyrics`: 2–4 short lines max, often just chorus
|
|
88
|
-
- Pass the shortest `duration` the tool supports.
|
|
91
|
+
- Pass the shortest `duration` the tool supports — or, on a length-controllable model (ElevenLabs Music), pass the exact `duration_seconds` (e.g. `15` or `30`).
|
|
89
92
|
|
|
90
93
|
### Cinematic trailer / score
|
|
91
94
|
- `style`: `cinematic orchestral trailer, swelling strings, taiko drums, hybrid choir, dramatic build, modern hybrid score`
|
package/src/apps/bridge.js
CHANGED
|
@@ -94,12 +94,18 @@ const BRIDGE_JS = `
|
|
|
94
94
|
readyFns.forEach(function (f) { try { f(hostContext); } catch (e) {} });
|
|
95
95
|
}).catch(function () { /* host without apps support — widget stays static */ });
|
|
96
96
|
|
|
97
|
+
var fsMode = false; // fullscreen: the HOST owns layout — size reports there
|
|
98
|
+
// made the inline iframe balloon over the chat composer.
|
|
97
99
|
function notifySize() {
|
|
100
|
+
if (fsMode) return;
|
|
98
101
|
// Measure the widget card itself — documentElement.scrollHeight over-reports
|
|
99
102
|
// in some hosts and leaves a huge empty iframe below the card.
|
|
100
103
|
var card = document.querySelector('.k-card');
|
|
101
104
|
var rect = card ? card.getBoundingClientRect() : null;
|
|
102
105
|
var height = rect ? Math.ceil(rect.bottom + 8) : document.documentElement.scrollHeight;
|
|
106
|
+
// Never ask the host for more than a viewport of inline height — a card
|
|
107
|
+
// taller than the screen overlaps Claude's prompt area.
|
|
108
|
+
height = Math.min(height, Math.max(window.innerHeight || 900, 500));
|
|
103
109
|
notify('ui/notifications/size-changed', {
|
|
104
110
|
width: document.documentElement.scrollWidth, height: height
|
|
105
111
|
});
|
|
@@ -151,6 +157,12 @@ const BRIDGE_JS = `
|
|
|
151
157
|
return request('ui/request-display-mode', { mode: mode });
|
|
152
158
|
},
|
|
153
159
|
notifySize: notifySize,
|
|
160
|
+
// Toggle fullscreen mode: suppresses size reports while the host owns the
|
|
161
|
+
// layout, and re-syncs the inline size on exit.
|
|
162
|
+
setFullscreen: function (on) {
|
|
163
|
+
fsMode = !!on;
|
|
164
|
+
if (!on) queueSize(60);
|
|
165
|
+
},
|
|
154
166
|
hostContext: function () { return hostContext; }
|
|
155
167
|
};
|
|
156
168
|
})();
|
package/src/apps/theme.js
CHANGED
|
@@ -175,18 +175,26 @@ body {
|
|
|
175
175
|
header + prompt + chips + viewer + thumbs + actions + footer must all fit,
|
|
176
176
|
or claude.ai adds an inner scrollbar. Click the image to expand in-Claude. */
|
|
177
177
|
.k-viewer { margin-bottom: 10px; }
|
|
178
|
-
.k-viewer img, .k-viewer video { display: block; width: 100%;
|
|
178
|
+
.k-viewer img, .k-viewer video { display: block; width: 100%;
|
|
179
|
+
max-height: min(340px, 55vh); object-fit: contain;
|
|
179
180
|
border-radius: 12px; background: #000; border: 1px solid var(--border); cursor: zoom-in; }
|
|
180
181
|
.k-viewer video { cursor: default; }
|
|
181
182
|
|
|
182
|
-
/* ---- Fullscreen (ui/request-display-mode granted) ----
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
html.k-fullscreen .k-body {
|
|
186
|
-
html.k-fullscreen .k-
|
|
183
|
+
/* ---- Fullscreen (ui/request-display-mode granted) ----
|
|
184
|
+
position:fixed pins the card to the iframe viewport regardless of document
|
|
185
|
+
flow/host sizing quirks — the media physically cannot exceed the screen. */
|
|
186
|
+
html.k-fullscreen, html.k-fullscreen body { height: 100%; overflow: hidden; }
|
|
187
|
+
html.k-fullscreen .k-card { position: fixed; inset: 0; display: flex; flex-direction: column; border-radius: 0; }
|
|
188
|
+
html.k-fullscreen .k-body { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
|
|
189
|
+
html.k-fullscreen .k-prompt, html.k-fullscreen .k-chips { flex: none; }
|
|
190
|
+
html.k-fullscreen #stage { flex: 1; min-height: 0; display: flex; flex-direction: column; }
|
|
191
|
+
html.k-fullscreen .k-viewer { flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; margin-bottom: 6px; }
|
|
187
192
|
html.k-fullscreen .k-viewer img, html.k-fullscreen .k-viewer video {
|
|
188
|
-
max-height: 100%; max-width: 100%;
|
|
189
|
-
|
|
193
|
+
max-height: 100%; max-width: 100%; height: auto;
|
|
194
|
+
width: auto; margin: 0 auto; cursor: zoom-out; object-fit: contain; }
|
|
195
|
+
html.k-fullscreen .k-thumbs { flex: none; }
|
|
196
|
+
html.k-fullscreen .k-thumbs .k-thumb { width: 56px; height: 56px; }
|
|
197
|
+
html.k-fullscreen .k-actions { flex: none; padding-top: 8px; }
|
|
190
198
|
.k-expand-hint { display: none; }
|
|
191
199
|
.k-thumbs { display: flex; gap: 6px; margin: 10px 0 2px; }
|
|
192
200
|
.k-thumbs .k-thumb { width: 48px; height: 48px; border-radius: 8px; overflow: hidden; cursor: pointer;
|
|
@@ -258,23 +258,43 @@ function wireDlButtons(root) {
|
|
|
258
258
|
});
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
// CD scenes render as the SAME viewer+thumbnail carousel as image batches —
|
|
262
|
+
// a stacked column of full-size scenes buried the card (and the chat).
|
|
263
|
+
function sceneItems(sc) {
|
|
264
|
+
var items = [];
|
|
265
|
+
(sc.scenes || []).forEach(function (scene) {
|
|
266
|
+
var label = 'Scene ' + scene.scene_number + (scene.title ? ' — ' + scene.title : '');
|
|
267
|
+
(scene.image_urls || []).forEach(function (u) { items.push({ url: u, type: 'image', label: label }); });
|
|
268
|
+
(scene.video_urls || []).forEach(function (u) { items.push({ url: u, type: 'video', label: label }); });
|
|
269
|
+
});
|
|
270
|
+
return items;
|
|
271
|
+
}
|
|
272
|
+
|
|
261
273
|
function renderScenes(sc) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
274
|
+
var items = sceneItems(sc);
|
|
275
|
+
if (!items.length) return renderError('No completed scenes received');
|
|
276
|
+
selected = Math.min(selected, items.length - 1);
|
|
277
|
+
var it = items[selected];
|
|
278
|
+
var mediaHtml = it.type === 'video'
|
|
279
|
+
? '<video id="scene-main" src="' + esc(it.url) + '" controls playsinline></video>'
|
|
280
|
+
: '<img id="scene-main" src="' + esc(it.url) + '" alt="" style="cursor:zoom-in">';
|
|
281
|
+
var thumbs = '<div class="k-thumbs">' + items.map(function (t, i) {
|
|
282
|
+
var inner = t.type === 'video'
|
|
283
|
+
? '<span style="display:flex;align-items:center;justify-content:center;width:100%;height:100%;background:rgba(255,255,255,0.06);font-size:14px">▶</span>'
|
|
284
|
+
: '<img src="' + esc(t.url) + '" alt="" loading="lazy">';
|
|
285
|
+
return '<div class="k-thumb' + (i === selected ? ' active' : '') + '" data-i="' + i + '" title="' + esc(t.label) + '">' + inner + '</div>';
|
|
286
|
+
}).join('') + '</div>';
|
|
287
|
+
el('stage').innerHTML =
|
|
288
|
+
'<div class="k-viewer">' + mediaHtml + dlBtnHTML(it.url) + '</div>' +
|
|
289
|
+
'<div style="font-size:11px;color:var(--text-faint);margin:2px 2px 0">' + esc(it.label) + '</div>' +
|
|
290
|
+
thumbs;
|
|
272
291
|
wireDlButtons(el('stage'));
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
292
|
+
if (it.type === 'image') {
|
|
293
|
+
var main = el('scene-main');
|
|
294
|
+
if (main) main.onclick = function () { focusMedia(it.url); };
|
|
295
|
+
}
|
|
296
|
+
Array.prototype.forEach.call(el('stage').querySelectorAll('.k-thumb'), function (t) {
|
|
297
|
+
t.onclick = function () { selected = +t.getAttribute('data-i'); renderScenes(sc); window.kolbo.notifySize(); };
|
|
278
298
|
});
|
|
279
299
|
renderActions(sc);
|
|
280
300
|
}
|
|
@@ -331,6 +351,7 @@ function toggleFullscreen() {
|
|
|
331
351
|
}
|
|
332
352
|
function applyFullscreen(on, exitHandler) {
|
|
333
353
|
document.documentElement.classList.toggle('k-fullscreen', on);
|
|
354
|
+
if (window.kolbo.setFullscreen) window.kolbo.setFullscreen(on);
|
|
334
355
|
var c = el('phase-chip');
|
|
335
356
|
if (on) {
|
|
336
357
|
c.style.display = '';
|
|
@@ -366,7 +387,6 @@ function renderActions(sc) {
|
|
|
366
387
|
}
|
|
367
388
|
if (sc.kind === 'video') {
|
|
368
389
|
a.push('<button class="k-btn primary" id="btn-download">⬇ Download</button>');
|
|
369
|
-
a.push('<button class="k-btn" id="btn-analyze">📊 Analyze</button>');
|
|
370
390
|
} else if (hasSingleUrl) {
|
|
371
391
|
// Scenes (Creative Director) have no single "current" url — per-item hover
|
|
372
392
|
// download buttons cover them instead.
|
|
@@ -400,9 +420,6 @@ function renderActions(sc) {
|
|
|
400
420
|
'\\nEdit instruction: ' + text);
|
|
401
421
|
});
|
|
402
422
|
});
|
|
403
|
-
bind('btn-analyze', function () {
|
|
404
|
-
window.kolbo.sendMessage('Analyze this video and give me an engagement/virality read — hook strength, pacing, retention risks, and concrete improvement tips:\\n' + currentUrl());
|
|
405
|
-
});
|
|
406
423
|
}
|
|
407
424
|
function bind(id, fn) { var b = el(id); if (b) b.onclick = fn; }
|
|
408
425
|
|
|
@@ -8,7 +8,8 @@ const { widgetPage } = require('../html');
|
|
|
8
8
|
* structuredContent: {
|
|
9
9
|
* widget: 'transcript', phase: 'completed'|'generating'|'failed',
|
|
10
10
|
* generation_id, poll_tool, text, duration, audio_url, srt_url,
|
|
11
|
-
* word_by_word_srt_url, txt_url, credits_used, error
|
|
11
|
+
* word_by_word_srt_url, txt_url, credits_used, error,
|
|
12
|
+
* open_url // "Open in Kolbo" session deep link (optional)
|
|
12
13
|
* }
|
|
13
14
|
*/
|
|
14
15
|
|
|
@@ -34,7 +35,7 @@ const BODY = `
|
|
|
34
35
|
|
|
35
36
|
const SCRIPT = `
|
|
36
37
|
el('logo').innerHTML = KOLBO_LOGO + '<span>Kolbo</span>';
|
|
37
|
-
el('kolbo-link').onclick = function (e) { e.preventDefault(); window.kolbo.openLink('https://app.kolbo.ai'); };
|
|
38
|
+
el('kolbo-link').onclick = function (e) { e.preventDefault(); window.kolbo.openLink((state && state.open_url) || 'https://app.kolbo.ai'); };
|
|
38
39
|
var state = null, pollTimer = null;
|
|
39
40
|
|
|
40
41
|
function boot(sc) {
|
package/src/tools/_shared.js
CHANGED
|
@@ -305,6 +305,56 @@ async function inlineImageBlocks(urls, opts = {}) {
|
|
|
305
305
|
return blocks.filter(Boolean);
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
+
// ─── "Open in Kolbo" deep links ───────────────────────────────────────────────
|
|
309
|
+
// kolbo-api submit responses include `session_id` + `project_id`. Map each MCP
|
|
310
|
+
// tool to the frontend page + tool slug whose session view can RESUME that
|
|
311
|
+
// session (mirrors kolbo-map src/constants/sessionTypes.js resumeUrl map — the
|
|
312
|
+
// route must match the SESSION MODEL the SDK created, per sdkSessionManager):
|
|
313
|
+
// ImageSession → /image-tools?tool=text-to-image
|
|
314
|
+
// imgEditSession (image_edit AND edit_image/global_image_edit) → /image-tools?tool=image-editing
|
|
315
|
+
// textToVideoSession → /video-tools?tool=text-to-video
|
|
316
|
+
// imgToVideoSession (video_from_image, elements, first_last_frame) → /video-tools?tool=image-to-video
|
|
317
|
+
// videoToVideoSession → /video-tools?tool=video-to-video
|
|
318
|
+
// lipsyncSession → /video-tools?tool=lipsync
|
|
319
|
+
// MusicGeneratorSession / TextToSpeechSession / textToSoundSession /
|
|
320
|
+
// speechToTextSession → /audio-tools with the matching slug
|
|
321
|
+
// CreativeDirectorSession → /creative-director?session=... (no tool param)
|
|
322
|
+
// Intentionally ABSENT (no deep-linkable session page — widget falls back to
|
|
323
|
+
// plain https://app.kolbo.ai): edit_video (GlobalVideoEditSession has no
|
|
324
|
+
// session deep-link), generate_3d (project-scoped, no session), shorts render.
|
|
325
|
+
const APP_BASE_URL = 'https://app.kolbo.ai';
|
|
326
|
+
const OPEN_URL_ROUTES = {
|
|
327
|
+
generate_image: { path: '/image-tools', tool: 'text-to-image' },
|
|
328
|
+
generate_image_edit: { path: '/image-tools', tool: 'image-editing' },
|
|
329
|
+
edit_image: { path: '/image-tools', tool: 'image-editing' },
|
|
330
|
+
generate_video: { path: '/video-tools', tool: 'text-to-video' },
|
|
331
|
+
generate_video_from_image: { path: '/video-tools', tool: 'image-to-video' },
|
|
332
|
+
generate_elements: { path: '/video-tools', tool: 'image-to-video' },
|
|
333
|
+
generate_first_last_frame: { path: '/video-tools', tool: 'image-to-video' },
|
|
334
|
+
generate_video_from_video: { path: '/video-tools', tool: 'video-to-video' },
|
|
335
|
+
generate_lipsync: { path: '/video-tools', tool: 'lipsync' },
|
|
336
|
+
generate_music: { path: '/audio-tools', tool: 'music-generator' },
|
|
337
|
+
generate_speech: { path: '/audio-tools', tool: 'text-to-speech' },
|
|
338
|
+
generate_sound: { path: '/audio-tools', tool: 'text-to-sound' },
|
|
339
|
+
transcribe_audio: { path: '/audio-tools', tool: 'speech-to-text' },
|
|
340
|
+
generate_creative_director: { path: '/creative-director' },
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Build the "Open in Kolbo" deep link for a generation's actual session.
|
|
345
|
+
* Returns undefined (widget falls back to app.kolbo.ai) when the tool has no
|
|
346
|
+
* deep-linkable page or the submit response carried no session_id (older
|
|
347
|
+
* kolbo-api, shorts render, 3D).
|
|
348
|
+
*/
|
|
349
|
+
function buildOpenUrl(tool, gen) {
|
|
350
|
+
const route = OPEN_URL_ROUTES[tool];
|
|
351
|
+
if (!route || !gen || !gen.session_id) return undefined;
|
|
352
|
+
let url = `${APP_BASE_URL}${route.path}?session=${encodeURIComponent(gen.session_id)}`;
|
|
353
|
+
if (route.tool) url += `&tool=${route.tool}`;
|
|
354
|
+
if (gen.project_id) url += `&project=${encodeURIComponent(gen.project_id)}`;
|
|
355
|
+
return url;
|
|
356
|
+
}
|
|
357
|
+
|
|
308
358
|
// ─── MCP Apps generation widget helpers ──────────────────────────────────────
|
|
309
359
|
// When the host renders MCP Apps (claude.ai via the remote connector, Claude
|
|
310
360
|
// Desktop over stdio), generation tools return IMMEDIATELY after submit and the
|
|
@@ -341,6 +391,7 @@ async function uiGenerating(p) {
|
|
|
341
391
|
count: p.count || 1,
|
|
342
392
|
settings: p.settings || {},
|
|
343
393
|
reference_image: p.reference_image,
|
|
394
|
+
open_url: buildOpenUrl(p.tool, p.gen),
|
|
344
395
|
};
|
|
345
396
|
const text = JSON.stringify({
|
|
346
397
|
status: 'submitted',
|
|
@@ -373,6 +424,7 @@ async function uiCompleted(p, textPayload) {
|
|
|
373
424
|
duration: p.duration,
|
|
374
425
|
scenes: p.scenes,
|
|
375
426
|
credits_used: p.credits_used,
|
|
427
|
+
open_url: buildOpenUrl(p.tool, p.gen),
|
|
376
428
|
};
|
|
377
429
|
return uiResult(UI.generation, textPayload, structured);
|
|
378
430
|
}
|
|
@@ -389,6 +441,7 @@ module.exports = {
|
|
|
389
441
|
creditFields,
|
|
390
442
|
projectIdField,
|
|
391
443
|
inlineImageBlocks,
|
|
444
|
+
buildOpenUrl,
|
|
392
445
|
uiGenerating,
|
|
393
446
|
uiCompleted,
|
|
394
447
|
appsEnabled,
|
package/src/tools/generate.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
const { z } = require('zod');
|
|
7
7
|
const FormData = require('form-data');
|
|
8
8
|
const { pollUntilDone } = require('../polling');
|
|
9
|
-
const { resolveToBuffer, creditFields, projectIdField, inlineImageBlocks, uiGenerating, uiCompleted, appsEnabled } = require('./_shared');
|
|
9
|
+
const { resolveToBuffer, creditFields, projectIdField, inlineImageBlocks, buildOpenUrl, uiGenerating, uiCompleted, appsEnabled } = require('./_shared');
|
|
10
10
|
const { UI, uiResult, canonicalModelId } = require('../apps');
|
|
11
11
|
|
|
12
12
|
function registerGenerateTools(server, client, options = {}) {
|
|
@@ -178,7 +178,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
178
178
|
// through get_generation_status, so it stays blocking on UI hosts too and
|
|
179
179
|
// renders the completed scene gallery.
|
|
180
180
|
if (ui()) return uiCompleted({
|
|
181
|
-
tool: 'generate_creative_director', kind: 'scenes', client, model, prompt,
|
|
181
|
+
tool: 'generate_creative_director', kind: 'scenes', gen, client, model, prompt,
|
|
182
182
|
settings: { duration, resolution, mode: workflow_type }, scenes,
|
|
183
183
|
credits_used: creditFields(result).credits_used
|
|
184
184
|
}, cdText);
|
|
@@ -839,6 +839,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
839
839
|
generation_id: startResponse.generation_id,
|
|
840
840
|
poll_tool: 'get_generation_status',
|
|
841
841
|
audio_url: isUrl ? source : undefined,
|
|
842
|
+
open_url: buildOpenUrl('transcribe_audio', startResponse),
|
|
842
843
|
});
|
|
843
844
|
}
|
|
844
845
|
|