@kolbo/mcp 1.48.0 → 1.50.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/README.md +12 -10
- package/package.json +2 -2
- package/skill/SKILL.md +2 -1
- package/skill/references/models/music.md +1 -1
- package/src/apps/bridge.js +1 -1
- package/src/apps/index.js +14 -2
- package/src/apps/theme.js +18 -0
- package/src/apps/widgets/generation.js +109 -20
- package/src/apps/widgets/transcript.js +38 -9
- package/src/index.js +1 -1
- package/src/tools/_shared.js +59 -2
- package/src/tools/chat.js +4 -3
- package/src/tools/generate.js +96 -38
- package/src/tools/music_library.js +141 -174
- package/src/tools/projects.js +6 -3
- package/src/tools/stock_library.js +1 -1
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, buildOpenUrl, uiGenerating,
|
|
9
|
+
const { resolveToBuffer, pollOrTimedOut, creditFields, projectIdField, inlineImageBlocks, buildOpenUrl, uiGenerating, appsEnabled } = require('./_shared');
|
|
10
10
|
const { UI, uiResult, canonicalModelId } = require('../apps');
|
|
11
11
|
|
|
12
12
|
// ─── Cinematic Dimensions schema (shared by generate_image + generate_image_edit) ───
|
|
@@ -43,8 +43,8 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
43
43
|
// tool output is unchanged: a text block with the image URL.
|
|
44
44
|
const inlineImages = !!options.inlineImages;
|
|
45
45
|
// MCP Apps hosts (claude.ai remote connector, Claude Desktop) get an instant
|
|
46
|
-
// "submitted" response + a live ui://kolbo/generation.html widget that
|
|
47
|
-
//
|
|
46
|
+
// "submitted" response + a live ui://kolbo/generation.html widget that keeps
|
|
47
|
+
// one wait=true status call in flight. Text-only hosts never take this branch.
|
|
48
48
|
const ui = () => appsEnabled(server, options);
|
|
49
49
|
// ─── generate_image ────────────────────────────────────────
|
|
50
50
|
server.tool(
|
|
@@ -79,10 +79,12 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
79
79
|
reference_image: reference_images?.[0]
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
-
const
|
|
82
|
+
const poll = await pollOrTimedOut(client, gen.generation_id, {
|
|
83
83
|
interval: (gen.poll_interval_hint || 3) * 1000,
|
|
84
84
|
timeout: 120000
|
|
85
85
|
});
|
|
86
|
+
if (poll.timedOut) return poll.timedOut;
|
|
87
|
+
const result = poll.result;
|
|
86
88
|
|
|
87
89
|
const images = await inlineImageBlocks(result.result.urls, { enabled: inlineImages });
|
|
88
90
|
return {
|
|
@@ -135,10 +137,12 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
135
137
|
// server-side. Extend the polling window in those cases to avoid forcing
|
|
136
138
|
// every call into the timeout-and-recover path via get_generation_status.
|
|
137
139
|
const heavy = (source_images && source_images.length > 1) || (visual_dna_ids && visual_dna_ids.length > 0);
|
|
138
|
-
const
|
|
140
|
+
const poll = await pollOrTimedOut(client, gen.generation_id, {
|
|
139
141
|
interval: (gen.poll_interval_hint || 3) * 1000,
|
|
140
142
|
timeout: heavy ? 240000 : 120000
|
|
141
143
|
});
|
|
144
|
+
if (poll.timedOut) return poll.timedOut;
|
|
145
|
+
const result = poll.result;
|
|
142
146
|
|
|
143
147
|
const images = await inlineImageBlocks(result.result.urls, { enabled: inlineImages });
|
|
144
148
|
return {
|
|
@@ -183,6 +187,15 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
183
187
|
});
|
|
184
188
|
|
|
185
189
|
const cdStatusUrl = `/v1/generate/creative-director/${gen.generation_id}/status`;
|
|
190
|
+
if (ui()) return uiGenerating({
|
|
191
|
+
tool: 'generate_creative_director', kind: 'scenes', gen, client, model, prompt,
|
|
192
|
+
count: scene_count || 4,
|
|
193
|
+
settings: { duration, resolution, aspect_ratio, mode: workflow_type || 'image' },
|
|
194
|
+
reference_image: reference_images?.[0],
|
|
195
|
+
poll_tool: 'get_creative_director_status',
|
|
196
|
+
status_args: { generation_id: gen.generation_id, wait: true }
|
|
197
|
+
});
|
|
198
|
+
|
|
186
199
|
// Video mode runs N scenes as parallel video generations, each of which
|
|
187
200
|
// can take several minutes — a whole batch routinely exceeds the 10-min
|
|
188
201
|
// image window. Give video batches 30 min (the server watchdog finalizes
|
|
@@ -237,15 +250,6 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
237
250
|
_followup_hint: 'Each scene is a separate asset. If the user asks to edit one scene, find that scene by scene_number/title and pass its image_urls[0] (or video_urls[0]) to generate_image_edit / edit_image / edit_video / generate_video_from_video. Do NOT re-run generate_creative_director unless the user explicitly wants a brand-new set.'
|
|
238
251
|
}, null, 2);
|
|
239
252
|
|
|
240
|
-
// Creative Director polls a dedicated status route the widget can't reach
|
|
241
|
-
// through get_generation_status, so it stays blocking on UI hosts too and
|
|
242
|
-
// renders the completed scene gallery.
|
|
243
|
-
if (ui()) return uiCompleted({
|
|
244
|
-
tool: 'generate_creative_director', kind: 'scenes', gen, client, model, prompt,
|
|
245
|
-
settings: { duration, resolution, mode: workflow_type }, scenes,
|
|
246
|
-
credits_used: creditFields(result).credits_used
|
|
247
|
-
}, cdText);
|
|
248
|
-
|
|
249
253
|
return { content: [{ type: 'text', text: cdText }] };
|
|
250
254
|
}
|
|
251
255
|
);
|
|
@@ -258,12 +262,30 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
258
262
|
// blocking poll window) needs this tool to be re-checked until done.
|
|
259
263
|
server.tool(
|
|
260
264
|
'get_creative_director_status',
|
|
261
|
-
'Check the status of a Creative Director batch (from generate_creative_director) by its generation_id. Returns overall state ("processing" until EVERY scene is terminal, then "completed"/"failed") plus each scene\'s number, title, per-scene status, and image_urls/video_urls.
|
|
265
|
+
'Check the status of a Creative Director batch (from generate_creative_director) by its generation_id. Returns overall state ("processing" until EVERY scene is terminal, then "completed"/"failed") plus each scene\'s number, title, per-scene status, and image_urls/video_urls. Set wait=true to block for up to ~3 minutes instead of repeatedly calling this tool. Do NOT use the generic get_generation_status for Creative Director ids; it points at the wrong endpoint.',
|
|
262
266
|
{
|
|
263
|
-
generation_id: z.string().describe('The Creative Director generation_id returned by generate_creative_director.')
|
|
267
|
+
generation_id: z.string().describe('The Creative Director generation_id returned by generate_creative_director.'),
|
|
268
|
+
wait: z.boolean().optional().describe('If true, block until the batch is terminal, up to ~3 minutes. Use this instead of repeatedly checking in a loop.')
|
|
264
269
|
},
|
|
265
|
-
async ({ generation_id }) => {
|
|
266
|
-
const
|
|
270
|
+
async ({ generation_id, wait }) => {
|
|
271
|
+
const statusUrl = `/v1/generate/creative-director/${encodeURIComponent(generation_id)}/status`;
|
|
272
|
+
let status;
|
|
273
|
+
if (wait) {
|
|
274
|
+
try {
|
|
275
|
+
status = await pollUntilDone(client, generation_id, {
|
|
276
|
+
interval: 15000,
|
|
277
|
+
timeout: 180000,
|
|
278
|
+
statusUrl
|
|
279
|
+
});
|
|
280
|
+
} catch (err) {
|
|
281
|
+
// A tracking timeout is not a failed paid generation. Return the
|
|
282
|
+
// latest batch state so the widget can start one new long wait.
|
|
283
|
+
if (!err?.timedOut) throw err;
|
|
284
|
+
status = await client.get(statusUrl);
|
|
285
|
+
}
|
|
286
|
+
} else {
|
|
287
|
+
status = await client.get(statusUrl);
|
|
288
|
+
}
|
|
267
289
|
const scenes = (status.scenes || []).map(s => ({
|
|
268
290
|
scene_number: s.scene_number,
|
|
269
291
|
status: s.status,
|
|
@@ -273,6 +295,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
273
295
|
}));
|
|
274
296
|
const completed = scenes.filter(s => s.status === 'completed').length;
|
|
275
297
|
return { content: [{ type: 'text', text: JSON.stringify({
|
|
298
|
+
...creditFields(status),
|
|
276
299
|
state: status.state,
|
|
277
300
|
generation_id,
|
|
278
301
|
progress: status.progress,
|
|
@@ -281,7 +304,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
281
304
|
completed_scenes: completed,
|
|
282
305
|
_hint: status.state === 'completed'
|
|
283
306
|
? 'All scenes terminal. Every completed scene\'s image_urls/video_urls are final.'
|
|
284
|
-
: 'Still running
|
|
307
|
+
: 'Still running. Call get_creative_director_status once with wait=true; do not poll it in a loop.'
|
|
285
308
|
}, null, 2) }] };
|
|
286
309
|
}
|
|
287
310
|
);
|
|
@@ -318,10 +341,15 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
318
341
|
reference_image: reference_images?.[0]
|
|
319
342
|
});
|
|
320
343
|
|
|
321
|
-
|
|
344
|
+
// 15 min — Kling O3 4K, Veo 3.1 at higher durations/resolutions, Hailuo 2.3,
|
|
345
|
+
// and Seedance 2 routinely run 5-12 min under provider queue load; the old
|
|
346
|
+
// 5-min window forced routine calls into the timeout-and-recover path.
|
|
347
|
+
const poll = await pollOrTimedOut(client, gen.generation_id, {
|
|
322
348
|
interval: (gen.poll_interval_hint || 8) * 1000,
|
|
323
|
-
timeout:
|
|
349
|
+
timeout: 900000
|
|
324
350
|
});
|
|
351
|
+
if (poll.timedOut) return poll.timedOut;
|
|
352
|
+
const result = poll.result;
|
|
325
353
|
|
|
326
354
|
return {
|
|
327
355
|
content: [{
|
|
@@ -368,10 +396,14 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
368
396
|
reference_image: image_url
|
|
369
397
|
});
|
|
370
398
|
|
|
371
|
-
|
|
399
|
+
// 15 min — same real-world generation times as generate_video (Kling O3 4K,
|
|
400
|
+
// Veo 3.1, Hailuo 2.3, Seedance 2 at higher durations/resolutions).
|
|
401
|
+
const poll = await pollOrTimedOut(client, gen.generation_id, {
|
|
372
402
|
interval: (gen.poll_interval_hint || 8) * 1000,
|
|
373
|
-
timeout:
|
|
403
|
+
timeout: 900000
|
|
374
404
|
});
|
|
405
|
+
if (poll.timedOut) return poll.timedOut;
|
|
406
|
+
const result = poll.result;
|
|
375
407
|
|
|
376
408
|
return {
|
|
377
409
|
content: [{
|
|
@@ -429,10 +461,12 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
429
461
|
settings: { mode: instrumental ? 'instrumental' : (style || undefined) },
|
|
430
462
|
});
|
|
431
463
|
|
|
432
|
-
const
|
|
464
|
+
const poll = await pollOrTimedOut(client, gen.generation_id, {
|
|
433
465
|
interval: (gen.poll_interval_hint || 8) * 1000,
|
|
434
466
|
timeout: 300000
|
|
435
467
|
});
|
|
468
|
+
if (poll.timedOut) return poll.timedOut;
|
|
469
|
+
const result = poll.result;
|
|
436
470
|
|
|
437
471
|
return {
|
|
438
472
|
content: [{
|
|
@@ -501,10 +535,12 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
501
535
|
settings: { voice: voice || 'Rachel', style: selected_style || emotion || style_instructions }
|
|
502
536
|
});
|
|
503
537
|
|
|
504
|
-
const
|
|
538
|
+
const poll = await pollOrTimedOut(client, gen.generation_id, {
|
|
505
539
|
interval: (gen.poll_interval_hint || 5) * 1000,
|
|
506
540
|
timeout: 120000
|
|
507
541
|
});
|
|
542
|
+
if (poll.timedOut) return poll.timedOut;
|
|
543
|
+
const result = poll.result;
|
|
508
544
|
|
|
509
545
|
return {
|
|
510
546
|
content: [{
|
|
@@ -558,10 +594,12 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
558
594
|
settings: { duration }
|
|
559
595
|
});
|
|
560
596
|
|
|
561
|
-
const
|
|
597
|
+
const poll = await pollOrTimedOut(client, gen.generation_id, {
|
|
562
598
|
interval: (gen.poll_interval_hint || 5) * 1000,
|
|
563
599
|
timeout: 120000
|
|
564
600
|
});
|
|
601
|
+
if (poll.timedOut) return poll.timedOut;
|
|
602
|
+
const result = poll.result;
|
|
565
603
|
|
|
566
604
|
return {
|
|
567
605
|
content: [{
|
|
@@ -596,14 +634,17 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
596
634
|
const checkOne = async (id) => {
|
|
597
635
|
try {
|
|
598
636
|
if (wait) {
|
|
599
|
-
|
|
637
|
+
// Widgets use this long-wait path. A 15s API check cadence keeps
|
|
638
|
+
// completion responsive without multiplying backend traffic for
|
|
639
|
+
// every card left open in a host conversation.
|
|
640
|
+
const result = await pollUntilDone(client, id, { interval: 15000, timeout: 180000 });
|
|
600
641
|
return { generation_id: id, ...result };
|
|
601
642
|
}
|
|
602
643
|
const result = await client.get(`/v1/generate/${encodeURIComponent(id)}/status`);
|
|
603
644
|
return { generation_id: id, ...result };
|
|
604
645
|
} catch (err) {
|
|
605
646
|
if (err.timedOut) {
|
|
606
|
-
return { generation_id: id, state: 'processing', note: 'Still running after 3 min of waiting — call get_generation_status again with wait=true.' };
|
|
647
|
+
return { generation_id: id, state: 'processing', _timed_out: true, note: 'Still running after 3 min of waiting — call get_generation_status again with wait=true.' };
|
|
607
648
|
}
|
|
608
649
|
if (err.name === 'GenerationFailedError') {
|
|
609
650
|
return { generation_id: id, state: 'failed', error: err.message };
|
|
@@ -621,8 +662,8 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
621
662
|
: 'Some generations are still processing. Do NOT re-call this tool in a loop — call it ONCE with wait=true (and all pending generation_ids) to block until they finish.';
|
|
622
663
|
|
|
623
664
|
// Single-id calls keep the original flat shape — the generation widget
|
|
624
|
-
//
|
|
625
|
-
// top level.
|
|
665
|
+
// waits on this tool with { generation_id, wait:true } and reads
|
|
666
|
+
// state/result at the top level.
|
|
626
667
|
if (!generation_ids || generation_ids.length === 0) {
|
|
627
668
|
const single = results[0];
|
|
628
669
|
single._hint = pending.length === 0
|
|
@@ -712,10 +753,12 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
712
753
|
reference_image: reference_images?.[0]
|
|
713
754
|
});
|
|
714
755
|
|
|
715
|
-
const
|
|
756
|
+
const poll = await pollOrTimedOut(client, startResponse.generation_id, {
|
|
716
757
|
interval: (startResponse.poll_interval_hint || 8) * 1000,
|
|
717
758
|
timeout: 600000
|
|
718
759
|
});
|
|
760
|
+
if (poll.timedOut) return poll.timedOut;
|
|
761
|
+
const result = poll.result;
|
|
719
762
|
|
|
720
763
|
return {
|
|
721
764
|
content: [{
|
|
@@ -791,10 +834,12 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
791
834
|
reference_image: first_frame_url || undefined
|
|
792
835
|
});
|
|
793
836
|
|
|
794
|
-
const
|
|
837
|
+
const poll = await pollOrTimedOut(client, startResponse.generation_id, {
|
|
795
838
|
interval: (startResponse.poll_interval_hint || 8) * 1000,
|
|
796
839
|
timeout: 300000
|
|
797
840
|
});
|
|
841
|
+
if (poll.timedOut) return poll.timedOut;
|
|
842
|
+
const result = poll.result;
|
|
798
843
|
|
|
799
844
|
return {
|
|
800
845
|
content: [{
|
|
@@ -901,10 +946,12 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
901
946
|
reference_image: sourceIsUrl && !/\.(mp4|mov|webm|mkv|avi|m4v)(\?|$)/i.test(source) ? source : undefined,
|
|
902
947
|
});
|
|
903
948
|
|
|
904
|
-
const
|
|
949
|
+
const poll = await pollOrTimedOut(client, startResponse.generation_id, {
|
|
905
950
|
interval: (startResponse.poll_interval_hint || 8) * 1000,
|
|
906
951
|
timeout: 600000
|
|
907
952
|
});
|
|
953
|
+
if (poll.timedOut) return poll.timedOut;
|
|
954
|
+
const result = poll.result;
|
|
908
955
|
|
|
909
956
|
return {
|
|
910
957
|
content: [{
|
|
@@ -1001,10 +1048,12 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1001
1048
|
reference_image: reference_images?.[0]
|
|
1002
1049
|
});
|
|
1003
1050
|
|
|
1004
|
-
const
|
|
1051
|
+
const poll = await pollOrTimedOut(client, startResponse.generation_id, {
|
|
1005
1052
|
interval: (startResponse.poll_interval_hint || 8) * 1000,
|
|
1006
1053
|
timeout: 600000
|
|
1007
1054
|
});
|
|
1055
|
+
if (poll.timedOut) return poll.timedOut;
|
|
1056
|
+
const result = poll.result;
|
|
1008
1057
|
|
|
1009
1058
|
return {
|
|
1010
1059
|
content: [{
|
|
@@ -1069,15 +1118,18 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1069
1118
|
widget: 'transcript', phase: 'generating',
|
|
1070
1119
|
generation_id: startResponse.generation_id,
|
|
1071
1120
|
poll_tool: 'get_generation_status',
|
|
1121
|
+
status_args: { generation_id: startResponse.generation_id, wait: true },
|
|
1072
1122
|
audio_url: isUrl ? source : undefined,
|
|
1073
1123
|
open_url: buildOpenUrl('transcribe_audio', startResponse),
|
|
1074
1124
|
});
|
|
1075
1125
|
}
|
|
1076
1126
|
|
|
1077
|
-
const
|
|
1127
|
+
const poll = await pollOrTimedOut(client, startResponse.generation_id, {
|
|
1078
1128
|
interval: (startResponse.poll_interval_hint || 5) * 1000,
|
|
1079
1129
|
timeout: 1800000 // 30 minutes — long podcasts are a thing
|
|
1080
1130
|
});
|
|
1131
|
+
if (poll.timedOut) return poll.timedOut;
|
|
1132
|
+
const result = poll.result;
|
|
1081
1133
|
|
|
1082
1134
|
return {
|
|
1083
1135
|
content: [{
|
|
@@ -1136,10 +1188,12 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1136
1188
|
reference_image: reference_images?.[0]
|
|
1137
1189
|
});
|
|
1138
1190
|
|
|
1139
|
-
const
|
|
1191
|
+
const poll = await pollOrTimedOut(client, startResponse.generation_id, {
|
|
1140
1192
|
interval: (startResponse.poll_interval_hint || 8) * 1000,
|
|
1141
1193
|
timeout: 900000 // 15 minutes — 3D generation is slow
|
|
1142
1194
|
});
|
|
1195
|
+
if (poll.timedOut) return poll.timedOut;
|
|
1196
|
+
const result = poll.result;
|
|
1143
1197
|
|
|
1144
1198
|
return {
|
|
1145
1199
|
content: [{
|
|
@@ -1258,10 +1312,12 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1258
1312
|
reference_image: image_url
|
|
1259
1313
|
});
|
|
1260
1314
|
|
|
1261
|
-
const
|
|
1315
|
+
const poll = await pollOrTimedOut(client, gen.generation_id, {
|
|
1262
1316
|
interval: (gen.poll_interval_hint || 5) * 1000,
|
|
1263
1317
|
timeout: 300000 // 5 min — split_upscale and multi_shot can take longer
|
|
1264
1318
|
});
|
|
1319
|
+
if (poll.timedOut) return poll.timedOut;
|
|
1320
|
+
const result = poll.result;
|
|
1265
1321
|
|
|
1266
1322
|
return {
|
|
1267
1323
|
content: [{
|
|
@@ -1416,10 +1472,12 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1416
1472
|
reference_image: image_url
|
|
1417
1473
|
});
|
|
1418
1474
|
|
|
1419
|
-
const
|
|
1475
|
+
const poll = await pollOrTimedOut(client, gen.generation_id, {
|
|
1420
1476
|
interval: (gen.poll_interval_hint || 8) * 1000,
|
|
1421
1477
|
timeout: 600000
|
|
1422
1478
|
});
|
|
1479
|
+
if (poll.timedOut) return poll.timedOut;
|
|
1480
|
+
const result = poll.result;
|
|
1423
1481
|
|
|
1424
1482
|
return {
|
|
1425
1483
|
content: [{
|