@kolbo/mcp 1.120.2 → 1.121.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 CHANGED
@@ -321,6 +321,7 @@ Requires DaVinci Resolve Studio with the Kolbo plugin open (Workspace → Workfl
321
321
  | `generate_character_sheet` | Generate a multi-angle character sheet from reference images (credits) → pass URL to create_visual_dna or update_visual_dna |
322
322
  | `list_visual_dna_folders` / `create_visual_dna_folder` / `update_visual_dna_folder` / `delete_visual_dna_folder` / `move_visual_dna_to_folder` | Organize Visual DNA characters into user folders (create/rename/recolor/delete + move DNAs in/out) |
323
323
  | `create_project` / `update_project` / `archive_project` / `unarchive_project` | Project lifecycle (create/rename/describe/archive; deletion stays in-app) |
324
+ | `publish_project` / `get_project_publication` / `unpublish_project` / `submit_project_to_catalog` / `remove_project_hero_video` | Publish a project as a public showcase link, with write-up and final video as hero |
324
325
  | `transfer_visual_dna` / `list_visual_dna_transfers` / `respond_visual_dna_transfer` | Send Visual DNAs you own to another Kolbo account (they accept a copy with new ids; optionally move instead of copy) |
325
326
  | `transfer_project` / `list_project_transfers` / `respond_project_transfer` | Hand a project you own to another Kolbo account (they accept; same ids; cast assets copied; payer history unchanged) |
326
327
  | `list_skills` / `create_skill` / `update_skill` / `delete_skill` | Skills — reusable named personas for the chat tool; `description` is the system instruction |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolbo/mcp",
3
- "version": "1.120.2",
3
+ "version": "1.121.0",
4
4
  "description": "Kolbo AI MCP Server - Generate images, videos, music, speech, and sound effects from Claude Code",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -798,14 +798,16 @@ function schedulePoll(sc) {
798
798
  if (cancelRequested) return;
799
799
  if (!sc.generation_id && !(sc.generation_ids && sc.generation_ids.length) && !sc.status_args) return;
800
800
  if (!seen) { whenSeenFns.push(function () { schedulePoll(sc); }); return; }
801
- // The call itself long-waits server-side, for one transport-safe window
802
- // (~45s over the remote connector — see WAIT_WINDOW_MS in tools/generate.js).
803
- // This short pause only separates successive wait windows — the FIRST call
804
- // goes out immediately, so a card revealed by scrolling resolves at once.
805
- // After failed calls, back off exponentially (capped at 30s): a host that
806
- // rejects tools/call for a while (busy, rate-limited, reconnecting) used to
807
- // burn all MAX_POLL_ERRORS retries in ~45s and strand finished generations.
808
- var delay = pollStart ? Math.min(1500 * Math.pow(2, pollErrors), 30000) : 0;
801
+ // Each poll is a QUICK status read (wait:false). Long server-side waits held
802
+ // one host slot per card for up to 45s, and a chat with a dozen-plus live
803
+ // cards (17 fired in one minute on 2026-10-22) left cards queued behind each
804
+ // other on Generating while their images were done. Quick reads cost the
805
+ // host nothing between polls whatever its concurrency. Cadence: 3s for the
806
+ // first minute, 6s to five minutes, then 12s, plus jitter so many cards do
807
+ // not fire in lockstep. Failures back off exponentially (capped at 30s).
808
+ var age = pollStart ? Date.now() - pollStart : 0;
809
+ var base = age < 60000 ? 3000 : age < 300000 ? 6000 : 12000;
810
+ var delay = pollStart ? Math.min(base * Math.pow(2, pollErrors), 30000) + Math.floor(Math.random() * 1000) : 0;
809
811
  if (!pollStart) pollStart = Date.now();
810
812
  clearTimeout(pollTimer);
811
813
  pollTimer = setTimeout(function () { poll(sc); }, delay);
@@ -815,10 +817,8 @@ function poll(sc) {
815
817
  if (pollStart && (Date.now() - pollStart) > MAX_POLL_MS) {
816
818
  return renderTrackingIssue('This is still running longer than the tracking window. Do not retry it — any completed result will appear in your Kolbo library.');
817
819
  }
818
- var args = sc.status_args || { generation_id: sc.generation_id, wait: true };
819
- // Host-bridged calls may not reset their timeout on progress: keep each
820
- // wait window inside the common 60s request timeout.
821
- if (args.wait) args = Object.assign({}, args, { wait_seconds: 45 });
820
+ var args = Object.assign({}, sc.status_args || { generation_id: sc.generation_id }, { wait: false });
821
+ delete args.wait_seconds;
822
822
  pollInFlight = true;
823
823
  window.kolbo.callTool(sc.poll_tool || 'get_generation_status', args).then(function (res) {
824
824
  pollInFlight = false;
@@ -93,9 +93,9 @@ function poll() {
93
93
  if ((Date.now() - pollStart) > MAX_POLL_MS) {
94
94
  return trackingIssue('This transcription is still running longer than the tracking window. Do not retry it — any completed result will appear in your Kolbo library.');
95
95
  }
96
- var args = state.status_args || { generation_id: state.generation_id, wait: true };
97
- // Keep each host-bridged wait inside the common 60s request timeout.
98
- if (args.wait) args = Object.assign({}, args, { wait_seconds: 45 });
96
+ // Quick status read; see schedulePoll in the generation widget for why.
97
+ var args = Object.assign({}, state.status_args || { generation_id: state.generation_id }, { wait: false });
98
+ delete args.wait_seconds;
99
99
  window.kolbo.callTool(state.poll_tool || 'get_generation_status', args)
100
100
  .then(function (res) {
101
101
  var st = structured(res) || {};
@@ -118,10 +118,9 @@ function poll() {
118
118
  }
119
119
 
120
120
  function pollDelay() {
121
- // The call itself long-waits server-side; only briefly back off between
122
- // successive wait windows; after failed calls, back off exponentially
123
- // (capped at 30s) so a briefly unavailable host can't exhaust the retries.
124
- return Math.min(1500 * Math.pow(2, pollErrors), 30000);
121
+ // Quick reads every 5s (transcripts run minutes); failures back off
122
+ // exponentially, capped at 30s, plus jitter so cards don't fire in lockstep.
123
+ return Math.min(5000 * Math.pow(2, pollErrors), 30000) + Math.floor(Math.random() * 1000);
125
124
  }
126
125
 
127
126
  function trackingIssue(msg) {
@@ -8,15 +8,15 @@
8
8
  * or non-boolean entries whenever the registered tool surface changes.
9
9
  */
10
10
 
11
- const READ_ONLY = [
12
- 'get_video_inspection',
13
- 'get_flow_schema', 'list_flow_sessions', 'get_flow_session', 'validate_flow_session',
14
- 'estimate_flow_run', 'get_flow_run', 'list_flow_runs',
15
- 'list_morphious_styles',
16
- 'list_trends', 'get_trend', 'estimate_trend_run', 'get_trend_run', 'list_trend_runs',
11
+ const READ_ONLY = [
12
+ 'get_video_inspection',
13
+ 'get_flow_schema', 'list_flow_sessions', 'get_flow_session', 'validate_flow_session',
14
+ 'estimate_flow_run', 'get_flow_run', 'list_flow_runs',
15
+ 'list_morphious_styles',
16
+ 'list_trends', 'get_trend', 'estimate_trend_run', 'get_trend_run', 'list_trend_runs',
17
17
  'get_download_status',
18
- 'get_video_editor_schema', 'list_video_editor_sessions', 'get_video_editor_session',
19
- 'list_fonts', 'get_font', 'get_font_upload_status',
18
+ 'get_video_editor_schema', 'list_video_editor_sessions', 'get_video_editor_session',
19
+ 'list_fonts', 'get_font', 'get_font_upload_status',
20
20
  'get_creative_director_status', 'get_generation_status', 'list_models',
21
21
  'check_credits', 'show_plans', 'get_session_usage', 'list_voices',
22
22
  'chat_list_conversations', 'chat_get_messages',
@@ -25,7 +25,7 @@ const READ_ONLY = [
25
25
  'list_color_palettes', 'analyze_color_palette',
26
26
  'list_media', 'list_media_folders', 'get_media', 'get_media_stats',
27
27
  'list_presets', 'list_cinematic_presets',
28
- 'list_projects', 'get_project', 'list_sessions', 'list_project_context', 'get_project_profile',
28
+ 'list_projects', 'get_project', 'get_project_publication', 'list_sessions', 'list_project_context', 'get_project_profile',
29
29
  'list_project_assets',
30
30
  'list_project_transfers', 'list_visual_dna_transfers',
31
31
  'list_session_generations',
@@ -41,18 +41,18 @@ const READ_ONLY = [
41
41
  'create_music_composition_plan',
42
42
  ];
43
43
 
44
- const OPEN_WORLD_READ_ONLY = [
45
- // Reads public source media into private ephemeral scratch evidence, no library writes.
46
- 'prepare_video_inspection', 'inspect_video',
44
+ const OPEN_WORLD_READ_ONLY = [
45
+ // Reads public source media into private ephemeral scratch evidence, no library writes.
46
+ 'prepare_video_inspection', 'inspect_video',
47
47
  'blender_list_sessions', 'blender_get_scene', 'blender_search_docs',
48
48
  'blender_get_command_status',
49
49
  'adobe_list_sessions', 'adobe_get_project', 'adobe_get_timeline', 'adobe_get_command_status',
50
50
  'resolve_list_sessions', 'resolve_get_project', 'resolve_get_timeline', 'resolve_get_command_status',
51
51
  ];
52
52
 
53
- const PRIVATE_WRITE = [
54
- 'create_flow_session', 'duplicate_flow_session', 'move_flow_session', 'restore_flow_session',
55
- 'upload_font', 'rename_font', 'create_font_upload_ticket', 'font_upload_widget',
53
+ const PRIVATE_WRITE = [
54
+ 'create_flow_session', 'duplicate_flow_session', 'move_flow_session', 'restore_flow_session',
55
+ 'upload_font', 'rename_font', 'create_font_upload_ticket', 'font_upload_widget',
56
56
  'create_video_editor_session',
57
57
  'media_upload_widget', 'create_upload_ticket', 'upload_media',
58
58
  'favorite_media', 'unfavorite_media', 'dislike_media', 'undislike_media', 'rate_media', 'clear_media_rating',
@@ -70,7 +70,7 @@ const PRIVATE_WRITE = [
70
70
  'split_session', 'undo_session_organization',
71
71
  'rename_session', 'restore_session',
72
72
  'create_project', 'duplicate_project', 'update_project',
73
- 'archive_project', 'unarchive_project', 'add_project_context',
73
+ 'archive_project', 'unarchive_project', 'add_project_context', 'remove_project_hero_video',
74
74
  'link_project_asset', 'unlink_project_asset',
75
75
  'create_agent', 'create_skill',
76
76
  'create_doc',
@@ -81,14 +81,14 @@ const PRIVATE_WRITE = [
81
81
  'import_stock_asset',
82
82
  ];
83
83
 
84
- const DESTRUCTIVE_WRITE = [
85
- 'update_flow_session', 'undo_flow_edit', 'trash_flow_session',
86
- 'run_flow_session', 'cancel_flow_run', 'retry_flow_run',
87
- 'run_trend', 'cancel_trend_run',
84
+ const DESTRUCTIVE_WRITE = [
85
+ 'update_flow_session', 'undo_flow_edit', 'trash_flow_session',
86
+ 'run_flow_session', 'cancel_flow_run', 'retry_flow_run',
87
+ 'run_trend', 'cancel_trend_run',
88
88
  'cancel_download',
89
- 'extend_music', 'cover_music',
90
- 'update_video_editor_session',
91
- 'delete_font',
89
+ 'extend_music', 'cover_music',
90
+ 'update_video_editor_session',
91
+ 'delete_font',
92
92
  'export_video_editor_session',
93
93
  // These actions spend credits, enqueue irreversible work, or cancel it.
94
94
  'generate_image', 'generate_image_edit', 'generate_creative_director',
@@ -103,8 +103,8 @@ const DESTRUCTIVE_WRITE = [
103
103
  'chat_send_message', 'generate_character_sheet',
104
104
  'acquire_clean_music_track', 'import_music_track_to_library',
105
105
  'separate_audio_stems', 'clean_dialogue_leftovers', 'separate_ambience',
106
- 'analyze_video',
107
- 'analyze_video_evidence', 'transcribe_video_evidence', 'cancel_video_inspection',
106
+ 'analyze_video',
107
+ 'analyze_video_evidence', 'transcribe_video_evidence', 'cancel_video_inspection',
108
108
 
109
109
  // Deletes and whole-value replacement updates are conservatively destructive.
110
110
  'delete_voice', 'update_visual_dna', 'delete_visual_dna', 'delete_visual_dna_folder',
@@ -125,10 +125,12 @@ const DESTRUCTIVE_WRITE = [
125
125
  'edit_review_comment', 'delete_review_comment',
126
126
  ];
127
127
 
128
- const OPEN_WORLD_WRITE = [
128
+ const OPEN_WORLD_WRITE = [
129
129
  'download_media_from_url',
130
- 'import_music_audio',
130
+ 'import_music_audio',
131
131
  'publish_html_artifact', 'create_review_share_link', 'blender_capture_viewport',
132
+ // Public showcase link for a project / catalog submission.
133
+ 'publish_project', 'submit_project_to_catalog',
132
134
  // Adobe edits add bins, clips, sequences or caption tracks; none delete or overwrite.
133
135
  'adobe_import_media', 'adobe_place_on_timeline', 'adobe_create_sequence', 'adobe_import_captions',
134
136
  'adobe_capture_frame',
@@ -137,7 +139,7 @@ const OPEN_WORLD_WRITE = [
137
139
  ];
138
140
 
139
141
  const OPEN_WORLD_DESTRUCTIVE = [
140
- 'share_doc', 'revoke_review_share_link',
142
+ 'share_doc', 'revoke_review_share_link', 'unpublish_project',
141
143
  'blender_apply_operations', 'blender_import_media', 'blender_render',
142
144
  'blender_undo', 'blender_file_operation', 'blender_execute_python',
143
145
  // Can delete layers; approved once per batch in the Kolbo panel.
@@ -315,6 +315,61 @@ function registerProjectTools(server, client) {
315
315
  }
316
316
  );
317
317
 
318
+ // ─── publish a project (public showcase link) ──────────────
319
+ const pubText = (r, hint) => ({ content: [{ type: 'text', text: JSON.stringify({ ...r, success: undefined, ...(hint ? { _hint: hint } : {}) }, null, 2) }] });
320
+
321
+ server.tool(
322
+ 'publish_project',
323
+ 'PUBLISH one of YOUR OWN projects as a public showcase page with a shareable link (the same as the app\'s Publish tab) and return the public `url`. Safe to call again: it never changes the URL, it only updates what you pass. In one call you can also set `allow_fork` (let viewers copy the project into their account), the public write-up `showcase_about`, and the project\'s FINAL VIDEO as the page hero. HERO VIDEO: the video must already be in THIS project — a generated video made with `project_id`, or a file you put there with `upload_media` (pass `project_id`; a local file is uploaded from disk). Then pass its media id as `hero_video_media_id` (or its Kolbo URL as `hero_video_url`). Anyone with the link can see the project\'s media, so ALWAYS confirm with the user before publishing, and consider `duplicate_project` first to publish a clean copy. Undo with `unpublish_project`.',
324
+ {
325
+ project_id: z.string().describe('ObjectId of a project you OWN (from list_projects).'),
326
+ allow_fork: z.boolean().optional().describe('Let viewers copy the project into their own account. Default true on first publish; omit to keep the current setting.'),
327
+ showcase_about: z.string().optional().describe('Public "About" write-up shown on the page (max 10000 chars, plain text). Omit on first publish and Kolbo drafts one for you; pass "" to clear.'),
328
+ hero_video_media_id: z.string().optional().describe('Media id (from upload_media / list_media) of a VIDEO already in this project, shown as the full-bleed hero of the public page.'),
329
+ hero_video_url: z.string().optional().describe('Alternative to hero_video_media_id: the Kolbo CDN URL of a video already in this project.')
330
+ },
331
+ async ({ project_id, allow_fork, showcase_about, hero_video_media_id, hero_video_url }) => {
332
+ const body = {};
333
+ if (allow_fork !== undefined) body.allow_fork = allow_fork;
334
+ if (showcase_about !== undefined) body.showcase_about = showcase_about;
335
+ if (hero_video_media_id) body.hero_video_media_id = hero_video_media_id;
336
+ if (hero_video_url) body.hero_video_url = hero_video_url;
337
+ const result = await client.post(`/v1/projects/${encodeURIComponent(project_id)}/publish`, body);
338
+ return pubText(result, 'Share `url` with the user — it is live now. To list it in the public catalog call `submit_project_to_catalog` (review is manual).');
339
+ }
340
+ );
341
+
342
+ server.tool(
343
+ 'get_project_publication',
344
+ 'Check whether a project is published and read its public `url`, allow_fork, showcase_about, hero video, catalog listing status (none / pending / approved / rejected) and fork count. Returns `{ published: false }` when it is not published.',
345
+ { project_id: z.string().describe('ObjectId of a project you OWN.') },
346
+ async ({ project_id }) => pubText(await client.get(`/v1/projects/${encodeURIComponent(project_id)}/publish`))
347
+ );
348
+
349
+ server.tool(
350
+ 'unpublish_project',
351
+ 'Take a published project OFFLINE: the public link stops working immediately. Copies other people already made stay theirs. Re-publishing with `publish_project` creates a new link. Confirm with the user first.',
352
+ { project_id: z.string().describe('ObjectId of a project you OWN.') },
353
+ async ({ project_id }) => pubText(await client.delete(`/v1/projects/${encodeURIComponent(project_id)}/publish`))
354
+ );
355
+
356
+ server.tool(
357
+ 'submit_project_to_catalog',
358
+ 'Submit a PUBLISHED project to Kolbo\'s public catalog (status becomes `pending`; a Kolbo admin approves it — you cannot approve your own). Pass `submit: false` to withdraw. Fails with NOT_PUBLISHED if `publish_project` has not been called.',
359
+ {
360
+ project_id: z.string().describe('ObjectId of a project you OWN.'),
361
+ submit: z.boolean().optional().describe('true (default) = submit for review, false = withdraw.')
362
+ },
363
+ async ({ project_id, submit }) => pubText(await client.post(`/v1/projects/${encodeURIComponent(project_id)}/publish/listing`, { submit: submit !== false }))
364
+ );
365
+
366
+ server.tool(
367
+ 'remove_project_hero_video',
368
+ 'Clear the hero video of a published project (the page falls back to a mosaic of the project\'s stills). The video file itself stays in the project\'s media.',
369
+ { project_id: z.string().describe('ObjectId of a project you OWN.') },
370
+ async ({ project_id }) => pubText(await client.delete(`/v1/projects/${encodeURIComponent(project_id)}/publish/hero-video`))
371
+ );
372
+
318
373
  // ─── project transfer (hand a project to another account) ──
319
374
  server.tool(
320
375
  'transfer_project',