@kolbo/mcp 1.14.1 → 1.15.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/package.json +1 -1
- package/src/tools/generate.js +39 -23
package/package.json
CHANGED
package/src/tools/generate.js
CHANGED
|
@@ -74,13 +74,16 @@ function registerGenerateTools(server, client) {
|
|
|
74
74
|
visual_dna_ids, moodboard_id, enable_web_search, resolution
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
77
|
+
// Flux/Seedance/DNA-anchored edits routinely run 3-5 min. Old 120s
|
|
78
|
+
// timeout forced every call into the timeout-and-recover path via
|
|
79
|
+
// get_generation_status — and when the model fired multiple parallel
|
|
80
|
+
// edits, some recovered, some got abandoned with their URLs lost
|
|
81
|
+
// (the user's "lost generations" bug). 360s/480s covers the realistic
|
|
82
|
+
// p99 single-call duration without making the model poll forever.
|
|
80
83
|
const heavy = (source_images && source_images.length > 1) || (visual_dna_ids && visual_dna_ids.length > 0);
|
|
81
84
|
const result = await pollUntilDone(client, gen.generation_id, {
|
|
82
85
|
interval: (gen.poll_interval_hint || 3) * 1000,
|
|
83
|
-
timeout: heavy ?
|
|
86
|
+
timeout: heavy ? 480000 : 360000
|
|
84
87
|
});
|
|
85
88
|
|
|
86
89
|
return {
|
|
@@ -109,6 +112,7 @@ function registerGenerateTools(server, client) {
|
|
|
109
112
|
aspect_ratio: z.string().optional().describe('Aspect ratio applied to every scene (e.g., "1:1", "16:9", "9:16"). Must be in the chosen model\'s `supported_aspect_ratios` from list_models. Default: "1:1"'),
|
|
110
113
|
workflow_type: z.string().optional().describe('"image" (default) or "video"'),
|
|
111
114
|
duration: z.number().optional().describe('Duration in seconds per scene (video mode only). Must be a value in `supported_durations` from list_models, OR within `min_output_duration`-`max_output_duration`. E.g., 5 or 10.'),
|
|
115
|
+
sound_enabled: z.boolean().optional().describe('Video mode only. Enable (`true`) or disable (`false`) AI-generated synced audio on every scene. Only honored by models with `sound_generation_type: "native"` from list_models (Veo 3.1, Kling V3/2.6/O3, PixVerse V6). Omit to use each model\'s `sound_enabled_by_default`. Pass `false` when the user says no sound / silent / mute / without audio.'),
|
|
112
116
|
enhance_prompt: z.boolean().optional().describe('Enhance prompts per scene. Default: true'),
|
|
113
117
|
reference_images: z.array(z.string()).optional().describe('Array of reference image URLs to guide style/composition of every scene. **Cap: pass at most `max_reference_images` URLs from list_models for the chosen model.**'),
|
|
114
118
|
visual_dna_ids: z.array(z.string()).optional().describe('Array of Visual DNA profile IDs to apply consistently across every scene. **Cap: pass at most `max_visual_dna` IDs from list_models for the chosen model.** This is the ideal way to keep a character or product looking the same in all scenes of a campaign.'),
|
|
@@ -116,9 +120,9 @@ function registerGenerateTools(server, client) {
|
|
|
116
120
|
moodboard_ids: z.array(z.string()).optional().describe('Multiple moodboard IDs when blending styles. Prefer `moodboard_id` for single moodboards.'),
|
|
117
121
|
resolution: z.string().optional().describe('Resolution tier applied to every scene. Images: "1K" / "2K" / "3K" / "4K". Videos: "720p" / "1080p" / "1440p" / "2160p". Values are model-dependent — call list_models and read supported_resolutions on the target model. Multiplied across every scene.')
|
|
118
122
|
},
|
|
119
|
-
async ({ prompt, scene_count, model, aspect_ratio, workflow_type, duration, enhance_prompt, reference_images, visual_dna_ids, moodboard_id, moodboard_ids, resolution }) => {
|
|
123
|
+
async ({ prompt, scene_count, model, aspect_ratio, workflow_type, duration, sound_enabled, enhance_prompt, reference_images, visual_dna_ids, moodboard_id, moodboard_ids, resolution }) => {
|
|
120
124
|
const gen = await client.post('/v1/generate/creative-director', {
|
|
121
|
-
prompt, scene_count, model, aspect_ratio, workflow_type, duration,
|
|
125
|
+
prompt, scene_count, model, aspect_ratio, workflow_type, duration, sound_enabled,
|
|
122
126
|
enhance_prompt, reference_images, visual_dna_ids, moodboard_id, moodboard_ids, resolution
|
|
123
127
|
});
|
|
124
128
|
|
|
@@ -168,11 +172,12 @@ function registerGenerateTools(server, client) {
|
|
|
168
172
|
enhance_prompt: z.boolean().optional().describe('Enhance the prompt. Default: true'),
|
|
169
173
|
reference_images: z.array(z.string()).optional().describe('Array of image URLs used as visual references (style / composition / subject). **Cap: pass at most `max_reference_images` URLs from list_models for the chosen model — exceeding it is a deterministic 400.**'),
|
|
170
174
|
resolution: z.string().optional().describe('Video resolution tier (vertical pixels): "720p" / "1080p" / "1440p" / "2160p". Some models use labels like "512P"/"1024P"/"768P"/"1080P". Model-dependent — call list_models and read supported_resolutions. Read resolution_multipliers to predict cost.'),
|
|
175
|
+
sound_enabled: z.boolean().optional().describe('Enable (`true`) or disable (`false`) AI-generated synced audio on the output video. Only honored by models with `sound_generation_type: "native"` from list_models (e.g. Veo 3.1, Kling V3/2.6, PixVerse V6). On `sound_generation_type: "none"` models the flag has no effect. Omit to use the model\'s `sound_enabled_by_default`. Pass `false` when the user says no sound / silent / mute / without audio. Enabling sound may apply `sound_credit_multiplier` to cost.'),
|
|
171
176
|
preset_id: z.string().optional().describe('Preset ID from list_presets type="video" to apply a saved motion/style preset to this generation.')
|
|
172
177
|
},
|
|
173
|
-
async ({ prompt, model, aspect_ratio, duration, enhance_prompt, reference_images, resolution, preset_id }) => {
|
|
178
|
+
async ({ prompt, model, aspect_ratio, duration, enhance_prompt, reference_images, resolution, sound_enabled, preset_id }) => {
|
|
174
179
|
const gen = await client.post('/v1/generate/video', {
|
|
175
|
-
prompt, model, aspect_ratio, duration, enhance_prompt, reference_images, resolution, preset_id
|
|
180
|
+
prompt, model, aspect_ratio, duration, enhance_prompt, reference_images, resolution, sound_enabled, preset_id
|
|
176
181
|
});
|
|
177
182
|
|
|
178
183
|
const result = await pollUntilDone(client, gen.generation_id, {
|
|
@@ -209,11 +214,12 @@ function registerGenerateTools(server, client) {
|
|
|
209
214
|
duration: z.number().optional().describe('Duration in seconds. Must be in `supported_durations` from list_models, OR within `min_output_duration`-`max_output_duration`. Default: 5'),
|
|
210
215
|
enhance_prompt: z.boolean().optional().describe('Enhance the motion prompt. Default: true'),
|
|
211
216
|
visual_dna_ids: z.array(z.string()).optional().describe('Array of Visual DNA profile IDs to maintain consistency with prior characters / styles. **Cap: pass at most `max_visual_dna` IDs from list_models for the chosen model; if `supports_visual_dna: false` the model ignores DNA entirely.**'),
|
|
212
|
-
resolution: z.string().optional().describe('Video resolution tier (vertical pixels): "720p" / "1080p" / "1440p" / "2160p". Some models use labels like "512P"/"1024P"/"768P"/"1080P". Model-dependent — call list_models and read supported_resolutions. Read resolution_multipliers to predict cost.')
|
|
217
|
+
resolution: z.string().optional().describe('Video resolution tier (vertical pixels): "720p" / "1080p" / "1440p" / "2160p". Some models use labels like "512P"/"1024P"/"768P"/"1080P". Model-dependent — call list_models and read supported_resolutions. Read resolution_multipliers to predict cost.'),
|
|
218
|
+
sound_enabled: z.boolean().optional().describe('Enable (`true`) or disable (`false`) AI-generated synced audio on the output video. Only honored by models with `sound_generation_type: "native"` from list_models (e.g. Veo 3.1 Lite, Kling V3 4K, PixVerse V6, Kling 2.6/v3). On `sound_generation_type: "none"` models the flag has no effect. Omit to use the model\'s `sound_enabled_by_default`. Pass `false` when the user says no sound / silent / mute / without audio. Enabling sound may apply `sound_credit_multiplier` to cost.')
|
|
213
219
|
},
|
|
214
|
-
async ({ image_url, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, resolution }) => {
|
|
220
|
+
async ({ image_url, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, resolution, sound_enabled }) => {
|
|
215
221
|
const gen = await client.post('/v1/generate/video/from-image', {
|
|
216
|
-
image_url, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, resolution
|
|
222
|
+
image_url, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, resolution, sound_enabled
|
|
217
223
|
});
|
|
218
224
|
|
|
219
225
|
const result = await pollUntilDone(client, gen.generation_id, {
|
|
@@ -383,13 +389,17 @@ function registerGenerateTools(server, client) {
|
|
|
383
389
|
// ─── get_generation_status ─────────────────────────────────
|
|
384
390
|
server.tool(
|
|
385
391
|
'get_generation_status',
|
|
386
|
-
'Resume polling a generation after a timeout. Pass the generation_id from a prior generation tool that timed out. This call BLOCKS server-side, polling internally — you do NOT need to call it again in a loop. Defaults to a 10-minute internal poll which covers most image edits and short videos; pass `wait_seconds` up to 1700 (~28 min) for long video / 3D / batch generations. If
|
|
392
|
+
'Resume polling a generation after a timeout. Pass the generation_id from a prior generation tool that timed out. This call BLOCKS server-side, polling internally — you do NOT need to call it again in a loop. Defaults to a 10-minute internal poll which covers most image edits and short videos; pass `wait_seconds` up to 1700 (~28 min) for long video / 3D / batch generations. **If you fired multiple generations in parallel and they all timed out, you MUST call get_generation_status for EACH generation_id in this same turn — do not give up on any of them, or their URLs will be lost forever.** If a single call returns `still_pending: true`, that specific generation is genuinely slow — STOP calling THAT one, tell the user it is still running, and resume on the next user turn. Never call this tool more than ONCE per generation_id consecutively in the same turn.',
|
|
387
393
|
{
|
|
388
394
|
generation_id: z.string().describe('The generation ID to check'),
|
|
389
|
-
wait_seconds: z.number().int().min(
|
|
395
|
+
wait_seconds: z.number().int().min(60).max(1700).optional().describe('How long to block-poll internally before giving up (60–1700 seconds, default 600). Values below 300 are clamped up to 300 server-side because shorter waits cause the model to abandon still-running generations. Use higher values for video / 3D / large batches that can legitimately take 15+ minutes.'),
|
|
390
396
|
},
|
|
391
397
|
async ({ generation_id, wait_seconds }) => {
|
|
392
|
-
|
|
398
|
+
// Clamp the floor: shorter waits caused models to abandon generations
|
|
399
|
+
// that completed seconds later (URLs lost forever, paid-for compute
|
|
400
|
+
// wasted). 300s is the floor that survived real-world cases.
|
|
401
|
+
const requested = wait_seconds ?? 600;
|
|
402
|
+
const timeoutMs = Math.max(requested, 300) * 1000;
|
|
393
403
|
let result;
|
|
394
404
|
try {
|
|
395
405
|
result = await pollUntilDone(client, generation_id, {
|
|
@@ -445,9 +455,10 @@ function registerGenerateTools(server, client) {
|
|
|
445
455
|
preset_id: z.string().optional().describe('Preset ID from list_presets type="video" (optional)'),
|
|
446
456
|
enhance_prompt: z.boolean().optional().describe('Enhance the prompt. Default: true'),
|
|
447
457
|
visual_dna_ids: z.array(z.string()).optional().describe('Array of Visual DNA profile IDs to apply for character/style consistency across outputs. **Cap: pass at most `max_visual_dna` IDs from list_models for the chosen model.**'),
|
|
448
|
-
resolution: z.string().optional().describe('Video resolution tier (vertical pixels): "720p" / "1080p" / "1440p" / "2160p". Model-dependent — call list_models and read supported_resolutions.')
|
|
458
|
+
resolution: z.string().optional().describe('Video resolution tier (vertical pixels): "720p" / "1080p" / "1440p" / "2160p". Model-dependent — call list_models and read supported_resolutions.'),
|
|
459
|
+
sound_enabled: z.boolean().optional().describe('Enable (`true`) or disable (`false`) AI-generated synced audio on the output. Only honored by models with `sound_generation_type: "native"` from list_models (e.g. Kling O3 4K, Kling O3 via KIE). On other models the flag has no effect. Omit to use the model\'s `sound_enabled_by_default`. Pass `false` when the user says no sound / silent / mute / without audio. Enabling sound may apply `sound_credit_multiplier` to cost.')
|
|
449
460
|
},
|
|
450
|
-
async ({ prompt, model, reference_images, reference_videos, audio_url, files, duration, aspect_ratio, motion, preset_id, enhance_prompt, visual_dna_ids, resolution }) => {
|
|
461
|
+
async ({ prompt, model, reference_images, reference_videos, audio_url, files, duration, aspect_ratio, motion, preset_id, enhance_prompt, visual_dna_ids, resolution, sound_enabled }) => {
|
|
451
462
|
if (!prompt) throw new Error('prompt is required');
|
|
452
463
|
|
|
453
464
|
let startResponse;
|
|
@@ -467,6 +478,7 @@ function registerGenerateTools(server, client) {
|
|
|
467
478
|
if (reference_videos) form.append('reference_videos', JSON.stringify(reference_videos));
|
|
468
479
|
if (audio_url) form.append('audio_url', audio_url);
|
|
469
480
|
if (resolution) form.append('resolution', resolution);
|
|
481
|
+
if (sound_enabled !== undefined) form.append('sound_enabled', String(sound_enabled));
|
|
470
482
|
for (const f of resolved) {
|
|
471
483
|
form.append('files', f.buffer, { filename: f.filename, contentType: f.contentType });
|
|
472
484
|
}
|
|
@@ -474,7 +486,7 @@ function registerGenerateTools(server, client) {
|
|
|
474
486
|
} else {
|
|
475
487
|
// URL-only mode: plain JSON.
|
|
476
488
|
startResponse = await client.post('/v1/generate/elements', {
|
|
477
|
-
prompt, model, reference_images, reference_videos, audio_url, duration, aspect_ratio, motion, preset_id, enhance_prompt, visual_dna_ids, resolution
|
|
489
|
+
prompt, model, reference_images, reference_videos, audio_url, duration, aspect_ratio, motion, preset_id, enhance_prompt, visual_dna_ids, resolution, sound_enabled
|
|
478
490
|
});
|
|
479
491
|
}
|
|
480
492
|
|
|
@@ -513,9 +525,10 @@ function registerGenerateTools(server, client) {
|
|
|
513
525
|
aspect_ratio: z.string().optional().describe('Aspect ratio (auto-detected from first frame if not provided). Must be in `supported_aspect_ratios` from list_models when set. Default: "16:9"'),
|
|
514
526
|
enhance_prompt: z.boolean().optional().describe('Enhance the prompt. Default: true'),
|
|
515
527
|
visual_dna_ids: z.array(z.string()).optional().describe('Array of Visual DNA profile IDs to apply. **Cap: pass at most `max_visual_dna` IDs from list_models for the chosen model; if `supports_visual_dna: false`, DNA is silently ignored.**'),
|
|
516
|
-
resolution: z.string().optional().describe('Video resolution tier (vertical pixels): "720p" / "1080p" / "1440p" / "2160p". Model-dependent — call list_models and read supported_resolutions.')
|
|
528
|
+
resolution: z.string().optional().describe('Video resolution tier (vertical pixels): "720p" / "1080p" / "1440p" / "2160p". Model-dependent — call list_models and read supported_resolutions.'),
|
|
529
|
+
sound_enabled: z.boolean().optional().describe('Enable (`true`) or disable (`false`) AI-generated synced audio on the output video. Only honored by models with `sound_generation_type: "native"` from list_models (e.g. Veo 3.1 Lite, Kling V3 4K, PixVerse V6). On other models the flag has no effect. Omit to use the model\'s `sound_enabled_by_default`. Pass `false` when the user says no sound / silent / mute / without audio.')
|
|
517
530
|
},
|
|
518
|
-
async ({ first_frame_url, last_frame_url, first_frame, last_frame, prompt, model, duration, aspect_ratio, enhance_prompt, visual_dna_ids, resolution }) => {
|
|
531
|
+
async ({ first_frame_url, last_frame_url, first_frame, last_frame, prompt, model, duration, aspect_ratio, enhance_prompt, visual_dna_ids, resolution, sound_enabled }) => {
|
|
519
532
|
const urlMode = first_frame_url && last_frame_url;
|
|
520
533
|
const fileMode = first_frame && last_frame;
|
|
521
534
|
if (!urlMode && !fileMode) {
|
|
@@ -541,10 +554,11 @@ function registerGenerateTools(server, client) {
|
|
|
541
554
|
if (enhance_prompt !== undefined) form.append('enhance_prompt', String(enhance_prompt));
|
|
542
555
|
if (visual_dna_ids) form.append('visual_dna_ids', JSON.stringify(visual_dna_ids));
|
|
543
556
|
if (resolution) form.append('resolution', resolution);
|
|
557
|
+
if (sound_enabled !== undefined) form.append('sound_enabled', String(sound_enabled));
|
|
544
558
|
startResponse = await client.postMultipart('/v1/generate/first-last-frame', form);
|
|
545
559
|
} else {
|
|
546
560
|
startResponse = await client.post('/v1/generate/first-last-frame', {
|
|
547
|
-
first_frame_url, last_frame_url, prompt, model, duration, aspect_ratio, enhance_prompt, visual_dna_ids, resolution
|
|
561
|
+
first_frame_url, last_frame_url, prompt, model, duration, aspect_ratio, enhance_prompt, visual_dna_ids, resolution, sound_enabled
|
|
548
562
|
});
|
|
549
563
|
}
|
|
550
564
|
|
|
@@ -654,9 +668,10 @@ function registerGenerateTools(server, client) {
|
|
|
654
668
|
resolution: z.string().optional().describe('Video resolution tier (vertical pixels): "720p" / "1080p" / "1440p" / "2160p". Model-dependent — call list_models and read supported_resolutions.'),
|
|
655
669
|
reference_images: z.array(z.string()).optional().describe('Array of reference image URLs for models that support additional image inputs. **Cap: pass at most `max_images` URLs from list_models — if `max_images === 0` the model does not accept image refs.** Examples: character reference images for Kling O1/O3, style reference for Aleph/gen4_aleph, character image for WAN VACE video-edit.'),
|
|
656
670
|
reference_videos: z.array(z.string()).optional().describe('Array of additional reference video URLs for models that support multiple video inputs. **Cap: pass at most `max_videos` URLs from list_models — if `max_videos <= 1` only the source_video is accepted.** Example: WAN 2.6 reference-to-video accepts 1–3 reference videos.'),
|
|
657
|
-
elements: z.array(z.string()).optional().describe('Array of element image URLs. **Cap: pass at most `max_elements` URLs from list_models — if `max_elements === 0` the model does not accept elements.** Elements are style or character reference assets alongside the main video.')
|
|
671
|
+
elements: z.array(z.string()).optional().describe('Array of element image URLs. **Cap: pass at most `max_elements` URLs from list_models — if `max_elements === 0` the model does not accept elements.** Elements are style or character reference assets alongside the main video.'),
|
|
672
|
+
sound_enabled: z.boolean().optional().describe('Enable (`true`) or disable (`false`) AI-generated synced audio on the output video. Only honored by models with `sound_generation_type: "native"` from list_models (e.g. Kling v3 via KIE). On other models the flag has no effect. Omit to use the model\'s `sound_enabled_by_default`. Pass `false` when the user says no sound / silent / mute / without audio.')
|
|
658
673
|
},
|
|
659
|
-
async ({ source_video, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, resolution, reference_images, reference_videos, elements }) => {
|
|
674
|
+
async ({ source_video, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, resolution, reference_images, reference_videos, elements, sound_enabled }) => {
|
|
660
675
|
if (!source_video) throw new Error('source_video is required');
|
|
661
676
|
if (!prompt) throw new Error('prompt is required');
|
|
662
677
|
|
|
@@ -665,7 +680,7 @@ function registerGenerateTools(server, client) {
|
|
|
665
680
|
if (isUrl) {
|
|
666
681
|
startResponse = await client.post('/v1/generate/video-from-video', {
|
|
667
682
|
video_url: source_video, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, resolution,
|
|
668
|
-
reference_images, reference_videos, elements
|
|
683
|
+
reference_images, reference_videos, elements, sound_enabled
|
|
669
684
|
});
|
|
670
685
|
} else {
|
|
671
686
|
const resolved = await resolveToBuffer(source_video, 'video');
|
|
@@ -681,6 +696,7 @@ function registerGenerateTools(server, client) {
|
|
|
681
696
|
if (reference_images) form.append('reference_images', JSON.stringify(reference_images));
|
|
682
697
|
if (reference_videos) form.append('reference_videos', JSON.stringify(reference_videos));
|
|
683
698
|
if (elements) form.append('elements', JSON.stringify(elements));
|
|
699
|
+
if (sound_enabled !== undefined) form.append('sound_enabled', String(sound_enabled));
|
|
684
700
|
startResponse = await client.postMultipart('/v1/generate/video-from-video', form);
|
|
685
701
|
}
|
|
686
702
|
|