@kolbo/mcp 1.119.0 → 1.119.2

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
+ | `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) |
324
325
  | `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) |
325
326
  | `list_skills` / `create_skill` / `update_skill` / `delete_skill` | Skills — reusable named personas for the chat tool; `description` is the system instruction |
326
327
  | `list_agents` / `create_agent` / `update_agent` / `delete_agent` | Same four tools under their original names. Still supported and unchanged; new integrations should use the `*_skill` names |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolbo/mcp",
3
- "version": "1.119.0",
3
+ "version": "1.119.2",
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": {
@@ -55,10 +55,32 @@ const BRIDGE_JS = `
55
55
 
56
56
  function post(msg) { window.parent.postMessage(msg, '*'); }
57
57
 
58
+ // Longer than the 180s stdio status wait plus transport overhead.
59
+ // This bounds tracking only; it never cancels or resubmits a paid job.
60
+ var REQUEST_TIMEOUT_MS = 240000;
61
+ function timeoutError() {
62
+ var error = new Error('The host did not answer. Tracking paused; the job may still be running.');
63
+ error.code = 'KOLBO_HOST_TIMEOUT';
64
+ return error;
65
+ }
66
+ function boundedCall(run) {
67
+ return new Promise(function (resolve, reject) {
68
+ var timer = setTimeout(function () { reject(timeoutError()); }, REQUEST_TIMEOUT_MS);
69
+ Promise.resolve().then(run).then(function (value) {
70
+ clearTimeout(timer); resolve(value);
71
+ }, function (error) { clearTimeout(timer); reject(error); });
72
+ });
73
+ }
74
+
58
75
  function request(method, params) {
59
76
  return new Promise(function (resolve, reject) {
60
77
  var id = nextId++;
61
- pending[id] = { resolve: resolve, reject: reject };
78
+ var timer = setTimeout(function () {
79
+ delete pending[id];
80
+ queue = queue.filter(function (item) { return item.id !== id; });
81
+ reject(timeoutError());
82
+ }, REQUEST_TIMEOUT_MS);
83
+ pending[id] = { resolve: resolve, reject: reject, timer: timer };
62
84
  var msg = { jsonrpc: '2.0', id: id, method: method, params: params || {} };
63
85
  if (initialized || method === 'ui/initialize') post(msg);
64
86
  else queue.push(msg);
@@ -78,6 +100,7 @@ const BRIDGE_JS = `
78
100
  var p = pending[m.id];
79
101
  if (!p) return;
80
102
  delete pending[m.id];
103
+ clearTimeout(p.timer);
81
104
  if (m.error) p.reject(new Error(m.error.message || 'host error'));
82
105
  else p.resolve(m.result);
83
106
  return;
@@ -282,7 +305,7 @@ const BRIDGE_JS = `
282
305
  var oaCall = function (method) { return typeof oa[method] === 'function'; };
283
306
  window.kolbo.callTool = function (name, args) {
284
307
  if (!oaCall('callTool')) return Promise.reject(new Error('host cannot call tools'));
285
- return Promise.resolve(oa.callTool(name, args || {})).then(function (r) {
308
+ return boundedCall(function () { return oa.callTool(name, args || {}); }).then(function (r) {
286
309
  // The SDK resolves with the CallToolResult (content + structuredContent);
287
310
  // some builds unwrap to the structured payload — normalise either.
288
311
  if (r && (r.structuredContent || r.content)) return r;
@@ -802,7 +802,10 @@ function schedulePoll(sc) {
802
802
  // (~45s over the remote connector — see WAIT_WINDOW_MS in tools/generate.js).
803
803
  // This short pause only separates successive wait windows — the FIRST call
804
804
  // goes out immediately, so a card revealed by scrolling resolves at once.
805
- var delay = pollStart ? 1500 : 0;
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;
806
809
  if (!pollStart) pollStart = Date.now();
807
810
  clearTimeout(pollTimer);
808
811
  pollTimer = setTimeout(function () { poll(sc); }, delay);
@@ -870,7 +873,7 @@ function poll(sc) {
870
873
  pollErrors = 0; // a valid in-progress response resets the failure streak
871
874
  schedulePoll(sc);
872
875
  }
873
- }).catch(function () {
876
+ }).catch(function (error) {
874
877
  pollInFlight = false;
875
878
  if (cancelRequested || (state && (state.phase === 'completed' || state.phase === 'failed'))) return;
876
879
  if (++pollErrors >= MAX_POLL_ERRORS) return renderTrackingIssue('Tracking paused after repeated connection errors. The generation may still be running.');
@@ -1270,7 +1273,7 @@ function renderError(msg) {
1270
1273
 
1271
1274
  function renderTrackingIssue(msg) {
1272
1275
  clearTimeout(pollTimer);
1273
- setPhaseChip('Still working', false);
1276
+ setPhaseChip('Tracking paused', false);
1274
1277
  el('stage').innerHTML = '<div class="k-error">' + ICONS.clock + ' ' + esc(msg) + '</div>';
1275
1278
  el('actions').innerHTML = '<button class="k-btn primary" id="status-btn">' + ICONS.clock + ' Check status</button>' +
1276
1279
  '<button class="k-btn ghost" id="tracking-open">Open in Kolbo ' + ICONS.open + '</button>';
@@ -40,6 +40,7 @@ var state = null, pollTimer = null, originArgs = {};
40
40
 
41
41
  function boot(sc) {
42
42
  if (!sc) return;
43
+ clearTimeout(pollTimer);
43
44
  state = sc;
44
45
  el('credits').textContent = sc.credits_used != null ? fmtCredits(sc.credits_used) : '';
45
46
  if (sc.phase === 'generating') {
@@ -107,7 +108,8 @@ function poll() {
107
108
  } else if (s === 'failed' || s === 'cancelled') {
108
109
  boot(Object.assign({}, state, { phase: 'failed', error: st.error }));
109
110
  } else { pollErrors = 0; pollTimer = setTimeout(poll, pollDelay()); }
110
- }).catch(function () {
111
+ }).catch(function (error) {
112
+ if (state && state.phase !== 'generating') return;
111
113
  if (++pollErrors >= MAX_POLL_ERRORS) return trackingIssue('Tracking paused after repeated connection errors. The transcription may still be running.');
112
114
  pollTimer = setTimeout(poll, pollDelay());
113
115
  });
@@ -115,14 +117,15 @@ function poll() {
115
117
 
116
118
  function pollDelay() {
117
119
  // The call itself long-waits server-side; only briefly back off between
118
- // successive wait windows.
119
- return 1500;
120
+ // successive wait windows; after failed calls, back off exponentially
121
+ // (capped at 30s) so a briefly unavailable host can't exhaust the retries.
122
+ return Math.min(1500 * Math.pow(2, pollErrors), 30000);
120
123
  }
121
124
 
122
125
  function trackingIssue(msg) {
123
126
  clearTimeout(pollTimer);
124
127
  el('phase-chip').style.display = '';
125
- el('phase-chip').textContent = 'Still working';
128
+ el('phase-chip').textContent = 'Tracking paused';
126
129
  el('stage').classList.remove('k-empty');
127
130
  el('stage').innerHTML = '<div class="k-error">' + ICONS.clock + ' ' + esc(msg) + '</div>';
128
131
  el('actions').innerHTML = '<button class="k-btn primary" id="transcript-status">' + ICONS.clock + ' Check status</button>';
@@ -327,7 +327,7 @@ function guessContentType(filename) {
327
327
  '.mp4': 'video/mp4', '.mov': 'video/quicktime', '.webm': 'video/webm',
328
328
  '.mkv': 'video/x-matroska', '.avi': 'video/x-msvideo',
329
329
  '.mp3': 'audio/mpeg', '.wav': 'audio/wav', '.ogg': 'audio/ogg',
330
- '.m4a': 'audio/mp4', '.flac': 'audio/flac', '.aac': 'audio/aac'
330
+ '.m4a': 'audio/mp4', '.flac': 'audio/flac', '.aac': 'audio/aac', '.opus': 'audio/opus'
331
331
  };
332
332
  return map[ext] || 'application/octet-stream';
333
333
  }
@@ -212,7 +212,7 @@ function registerMediaTools(server, client, options = {}) {
212
212
  const MIME = {
213
213
  jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', webp: 'image/webp', gif: 'image/gif', heic: 'image/heic', avif: 'image/avif',
214
214
  mp4: 'video/mp4', mov: 'video/quicktime', webm: 'video/webm', m4v: 'video/x-m4v', mkv: 'video/x-matroska',
215
- mp3: 'audio/mpeg', wav: 'audio/wav', m4a: 'audio/mp4', aac: 'audio/aac', ogg: 'audio/ogg', flac: 'audio/flac',
215
+ mp3: 'audio/mpeg', wav: 'audio/wav', m4a: 'audio/mp4', aac: 'audio/aac', ogg: 'audio/ogg', opus: 'audio/opus', flac: 'audio/flac',
216
216
  pdf: 'application/pdf', txt: 'text/plain', md: 'text/markdown', csv: 'text/csv', json: 'application/json',
217
217
  docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
218
218
  xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',