@kolbo/mcp 1.87.3 → 1.87.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolbo/mcp",
3
- "version": "1.87.3",
3
+ "version": "1.87.5",
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": {
@@ -808,10 +808,15 @@ function renderResult(sc) {
808
808
  // voice that actually ran, so the finished card must not keep the guess.
809
809
  renderChips(sc);
810
810
  setPhaseChip('', false);
811
- if (sc.kind === 'status' && Array.isArray(sc.items)) return renderStatusGrid(sc);
812
811
  if (sc.batch && sc.scenes && sc.scenes.length) return renderBatchGrid(sc);
813
812
  if (sc.kind === 'scenes' && sc.scenes && sc.scenes.length) return renderScenes(sc);
814
813
  var urls = preferKolbo(sc.urls || []);
814
+ // Key the per-item grid on items[], not on kind === 'status': a host bridge
815
+ // (Kolbo Code's shaped()) rewrites kind from the TOOL name before the payload
816
+ // reaches this iframe, so a finished prompts[] batch arrived as kind:'image'
817
+ // with eight completed items and no top-level urls — and painted
818
+ // "No output received / Failed" over generations that had completed and billed.
819
+ if (Array.isArray(sc.items) && sc.items.length && (sc.kind === 'status' || !urls.length)) return renderStatusGrid(sc);
815
820
  var kind = displayKind(Object.assign({}, sc, { urls: urls }));
816
821
  if (!urls.length) return renderError('No output received');
817
822
  if (kind === 'image') renderImages(sc, urls);
@@ -420,9 +420,38 @@ function render() {
420
420
  if (sent) return;
421
421
  sent = true;
422
422
  var lines = done.map(function (i, idx) { return (idx + 1) + '. ' + filenameFor(i) + ' (' + i.kind + '): ' + i.url; });
423
- window.kolbo.sendMessage('I uploaded ' + done.length + ' file(s) to my Kolbo media library:\\n' + lines.join('\\n') + '\\nContinue with these files.');
424
- el('actions').innerHTML = '<span style="font-size:12px;color:var(--text-muted)">' + ICONS.check + ' Sent to Claude — continuing…</span>';
425
- window.kolbo.notifySize();
423
+ var text = 'Use these uploaded file(s):\\n' + lines.join('\\n');
424
+ var status = function (h) { el('actions').innerHTML = '<span style="font-size:12px;color:var(--text-muted)">' + h + '</span>'; window.kolbo.notifySize(); };
425
+ // A host that does not implement a ui/* method never answers it: the
426
+ // promise hangs, and the old code had already printed "Sent to Claude"
427
+ // while nothing reached the composer (Claude desktop over stdio). Bound
428
+ // every attempt and fall through: insert into the prompt box first (the
429
+ // user adds the instruction and sends), then send as a message, then a
430
+ // copyable list so the URLs are never lost.
431
+ var bounded = function (p, ms) {
432
+ return Promise.race([p, new Promise(function (_, rej) { setTimeout(function () { rej(new Error('no host reply')); }, ms); })]);
433
+ };
434
+ status('Adding to your message...');
435
+ bounded(window.kolbo.insertText(text), 1500)
436
+ .then(function () { status(ICONS.check + ' Added to your message. Add your instruction and send.'); })
437
+ .catch(function () {
438
+ return bounded(window.kolbo.sendMessage('I uploaded ' + done.length + ' file(s) to my Kolbo media library:\\n' + lines.join('\\n') + '\\nContinue with these files.'), 1500)
439
+ .then(function () { status(ICONS.check + ' Sent to Claude, continuing...'); });
440
+ })
441
+ .catch(function () {
442
+ sent = false;
443
+ el('actions').innerHTML =
444
+ '<div style="font-size:12px;color:var(--text-muted);margin-bottom:6px">' + ICONS.warn + ' This host cannot fill the prompt box. Copy the URLs and paste them into your message:</div>' +
445
+ '<textarea readonly style="width:100%;min-height:64px;font:11px/1.4 monospace;background:transparent;color:inherit;border:1px solid var(--border,#444);border-radius:8px;padding:6px">' + esc(lines.join('\\n')) + '</textarea>' +
446
+ '<button class="k-btn primary" id="btn-copy" style="margin-top:6px">Copy URLs</button>';
447
+ el('btn-copy').onclick = function () {
448
+ var u = done.map(function (i) { return i.url; }).join('\\n');
449
+ var ok = function () { el('btn-copy').textContent = 'Copied'; };
450
+ if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(u).then(ok, function () { window.kolbo.copyText(u).then(ok, function () {}); });
451
+ else window.kolbo.copyText(u).then(ok, function () {});
452
+ };
453
+ window.kolbo.notifySize();
454
+ });
426
455
  };
427
456
  el('btn-more').onclick = function () {
428
457
  if (isMobileHost()) openExternalUploader();
@@ -69,7 +69,9 @@ const REMOTE_FILE_HINT =
69
69
 
70
70
  const LOCAL_FILE_HINT =
71
71
  ' LOCAL FILE? Absolute local paths work here (server and client share a filesystem). ' +
72
- 'Never reply that you cannot upload files — for a file you will reference more than once, call `upload_media` first and reuse the returned https:// URL.';
72
+ 'Never reply that you cannot upload files — for a file you will reference more than once, call `upload_media` first and reuse the returned https:// URL. ' +
73
+ 'IMAGE/VIDEO PASTED OR ATTACHED IN THIS CHAT with no path? You can see it but Kolbo cannot — NEVER substitute a text description of it for the file. ' +
74
+ 'Call `media_upload_widget` so the user hands Kolbo the file (or ask for its path), then pass the returned URL — for "make this image X" use generate_image_edit with it in `source_images`.';
73
75
  const REMOTE_TEXT_FILE_HINT =
74
76
  ' REMOTE FILE INPUT: This client cannot send local filesystem paths or render Kolbo\'s upload widget. ' +
75
77
  'Use an existing public https:// URL. If the attachment has no public URL, ask the user to upload it in the Kolbo Media Library and paste the resulting URL; never invent a URL or claim a local path is usable here.';
@@ -99,9 +99,12 @@ async function pollBatch(client, batch, { interval, timeout }, toolName) {
99
99
  generations,
100
100
  failed_submissions: batch.failed.length ? batch.failed : undefined
101
101
  }, null, 2);
102
+ // Name the model that actually ran — every id in one batch ran the same one.
103
+ const modelsRan = [...new Set(polls.filter((p) => !p.timedOut).map((p) => p.result.result && p.result.result.model).filter(Boolean))];
102
104
  return uiCompleted({
103
105
  tool: toolName, kind: 'status', client,
104
- model: 'Generations', gen: { generation_id: batch.ids[0], session_id: batch.ok[0].gen.session_id },
106
+ model: modelsRan.length === 1 ? modelsRan[0] : 'Generations',
107
+ gen: { generation_id: batch.ids[0], session_id: batch.ok[0].gen.session_id },
105
108
  settings: {},
106
109
  items: generations.map(g => ({
107
110
  id: g.generation_id,