@kolbo/mcp 1.76.0 → 1.76.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolbo/mcp",
3
- "version": "1.76.0",
3
+ "version": "1.76.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": {
@@ -639,7 +639,11 @@ async function uiCompleted(p, textPayload, extraContent) {
639
639
  ...chip,
640
640
  prompt: p.prompt,
641
641
  count: p.count || 1,
642
- settings: p.settings || {},
642
+ // Omitted entirely when the caller has none. A live generation card merges
643
+ // an incoming status payload over its own state, so an empty-but-present
644
+ // `settings` wiped the resolution / aspect / DNA chips off the finished
645
+ // card. Every reader already does `sc.settings || {}`.
646
+ ...(p.settings ? { settings: p.settings } : {}),
643
647
  visual_dnas: await resolveVisualDnas(p.client, (p.settings || {}).visual_dna_ids),
644
648
  reference_images: Array.isArray(p.reference_images)
645
649
  ? p.reference_images.filter(Boolean)
@@ -655,6 +659,13 @@ async function uiCompleted(p, textPayload, extraContent) {
655
659
  // above which assume everything finished together. Only set when the
656
660
  // caller actually has this shape; every existing caller is unaffected.
657
661
  ...(Array.isArray(p.items) ? { items: p.items } : {}),
662
+ // The RAW generation state, when the caller has one. `phase` above is
663
+ // hardcoded 'completed' — it means "this tool call finished", not "the
664
+ // generation finished" — so a status check on a still-running job looked
665
+ // done to every reader. A live generation card polls get_generation_status
666
+ // from its own iframe and reads `state` FIRST; without it, a processing job
667
+ // resolved as completed-with-no-media and painted a red failure card.
668
+ ...(p.state ? { state: p.state } : {}),
658
669
  credits_used: p.credits_used,
659
670
  open_url: buildOpenUrl(p.tool, p.gen),
660
671
  };
@@ -108,6 +108,17 @@ async function pollBatch(client, batch, { interval, timeout }, toolName) {
108
108
  }, text);
109
109
  }
110
110
 
111
+ // Same extension sniff the generation widget already does (kindFromTool in
112
+ // apps/widgets/generation.js) — a status check knows a url came back, not what
113
+ // kind of media it is, and the card needs the media kind to pick a renderer.
114
+ function mediaKind(url) {
115
+ const u = String(url || '').split('?')[0].toLowerCase();
116
+ if (/\.(mp4|mov|webm|mkv)$/.test(u)) return 'video';
117
+ if (/\.(mp3|wav|m4a|aac|ogg|flac)$/.test(u)) return 'audio';
118
+ if (/\.(glb|gltf|fbx|obj|usdz)$/.test(u)) return '3d';
119
+ return 'image';
120
+ }
121
+
111
122
  // ─── Widget settings block ──────────────────────────────────────────────────
112
123
  // What the CALLER actually asked for, for the generation card AND for the model
113
124
  // reading the tool result. Undefined/false keys are dropped by JSON.stringify, so
@@ -1016,10 +1027,28 @@ function registerGenerateTools(server, client, options = {}) {
1016
1027
  : pendingHint;
1017
1028
  const singleText = JSON.stringify(single, null, 2);
1018
1029
  const res = single.result || {};
1030
+ // A live generation card polls THIS branch from inside its own iframe,
1031
+ // and it reads structuredContent — never the text (apps/html.js
1032
+ // structured() returns structuredContent first and only falls back to
1033
+ // parsing content[].text). So preserving the flat TEXT shape, as the
1034
+ // comment above intended, protected nothing: the card does
1035
+ // `r = st.result || st` then `r.urls`, found only `items`, and painted
1036
+ // "No output received / Failed" over a generation that had completed
1037
+ // and billed. Every card rendered before v1.74 keeps that old iframe JS
1038
+ // forever, so the flat fields have to live in structuredContent too.
1039
+ const urls = Array.isArray(res.urls) ? res.urls : [];
1040
+ const done = single.state === 'completed' && urls.length > 0;
1019
1041
  return uiCompleted({
1020
- tool: 'get_generation_status', kind: 'status', client,
1021
- model: 'Generations', gen: { generation_id: single.generation_id },
1022
- settings: {},
1042
+ tool: 'get_generation_status', kind: done ? mediaKind(urls[0]) : 'status', client,
1043
+ // The model that ACTUALLY ran. A single-id check resolves one
1044
+ // generation, so 'Generations' here overwrote the real model name on
1045
+ // the finished card the moment the live widget merged this payload in.
1046
+ model: res.model || 'Generations', gen: { generation_id: single.generation_id },
1047
+ state: single.state,
1048
+ urls: done ? urls : undefined,
1049
+ thumbnail_url: res.thumbnail_url,
1050
+ prompt: res.prompt_used || res.prompt || undefined,
1051
+ credits_used: creditFields(single).credits_used,
1023
1052
  items: [{
1024
1053
  id: single.generation_id,
1025
1054
  state: single.state,