@kolbo/mcp 1.53.0 → 1.53.1

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.53.0",
3
+ "version": "1.53.1",
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": {
@@ -31,9 +31,20 @@ el('kolbo-link').onclick = function (e) { e.preventDefault(); window.kolbo.openL
31
31
  var state = null;
32
32
 
33
33
  function boot(sc) {
34
- if (!sc || !sc.groups) return;
34
+ if (!sc || !sc.groups || !sc.groups.length) return false;
35
35
  state = sc;
36
36
  el('title').textContent = sc.title || 'AI Models';
37
+ // Internal lookup (Claude checking caps mid-task): a full catalog card would
38
+ // be chat spam, an empty card would look broken. One expandable row instead.
39
+ if (sc.compact) {
40
+ el('stage').innerHTML = '<div class="k-audio-row" id="expand" style="cursor:pointer;padding:8px">' +
41
+ '<div class="k-audio-meta"><div class="k-audio-title" style="font-size:12px">Kolbo AI models</div>' +
42
+ '<div class="k-audio-sub" style="font-size:10.5px">Tap to browse what you can generate</div></div>' +
43
+ '<span class="k-chip" style="flex:none;padding:2px 7px;font-size:10px">Browse</span></div>';
44
+ el('expand').onclick = function () { sc.compact = false; boot(sc); };
45
+ window.kolbo.notifySize();
46
+ return true;
47
+ }
37
48
  var shown = sc.groups.reduce(function (n, g) { return n + g.models.length; }, 0);
38
49
  el('count-chip').style.display = '';
39
50
  el('count-chip').textContent = sc.total_available && sc.total_available > shown
@@ -60,13 +71,13 @@ function boot(sc) {
60
71
  };
61
72
  });
62
73
  window.kolbo.notifySize();
74
+ return true;
63
75
  }
64
76
 
65
77
  window.kolbo.onToolResult(function (result) {
66
- var sc = result.structuredContent || structured(result);
67
- if (sc && sc.groups) return boot(sc);
68
- // Internal lookup (Claude checking constraints mid-task) no catalog data
69
- // was attached on purpose. Collapse instead of showing a dead card.
78
+ if (boot(result.structuredContent || structured(result))) return;
79
+ // No catalog data reached the iframe (host/version mismatch, or a filter
80
+ // that matched nothing) collapse instead of showing a dead card.
70
81
  var card = document.querySelector('.k-card');
71
82
  if (card) card.style.display = 'none';
72
83
  window.kolbo.notifySize();
@@ -49,7 +49,7 @@ function modelChips(m) {
49
49
  // deliberately EXCLUDED — we always want a specific model chosen (server
50
50
  // instruction #9): auto-routing hides the model choice and the generation
51
51
  // metadata used to read just "Auto".
52
- function buildCatalogStructured(models, type) {
52
+ function buildCatalogStructured(models, type, compact) {
53
53
  const groups = [];
54
54
  const byName = new Map();
55
55
  const isAuto = (m) => /^auto$|smart.select/i.test(String(m.name || '')) || /smart-select|k_auto/i.test(String(m.identifier || ''));
@@ -78,6 +78,7 @@ function buildCatalogStructured(models, type) {
78
78
  widget: 'catalog',
79
79
  title: 'Kolbo AI Models' + (type ? ' — ' + type : ''),
80
80
  total_available: models.length,
81
+ compact: compact === true,
81
82
  groups,
82
83
  };
83
84
  }
@@ -91,11 +92,16 @@ function registerModelTools(server, client, options = {}) {
91
92
  {
92
93
  type: z.string().optional().describe('Filter by DB type name: "text_to_img", "image_editing", "text_to_video", "img_to_video", "draw_to_video", "video_to_video", "elements", "firstlastgenerations", "lipsync-image", "lipsync-video", "music_gen", "text_to_speech", "text_to_sound", "stt", "text". Legacy aliases also accepted: "image", "image_edit", "video", "video_from_image", "video_from_video", "music", "speech", "sound", "chat", "lipsync" (both lipsync types), "three_d" (all 3D types), "first_last_frame", "transcription". Omit for all models.'),
93
94
  format: z.enum(['text', 'json']).optional().describe('Output format. "text" (default) returns a human-readable summary with the most-used caps. "json" returns the raw model documents from the API — use this when you need to programmatically verify caps (max_reference_images, max_visual_dna, max_video_duration, supported_aspect_ratios, etc.) before passing an array/value that might exceed a model-specific limit. The JSON form is the source of truth; the text form is a convenience preview.'),
94
- display_catalog: z.boolean().optional().describe('Set true ONLY when the USER explicitly asked to see/browse the available models — it renders a visual model catalog to them. Leave unset for internal lookups (verifying a model name, checking caps before a generation): those should stay invisible to the user.')
95
+ display_catalog: z.boolean().optional().describe('Set true when the USER explicitly asked to see/browse the available models — the visual catalog opens expanded. Leave unset for internal lookups (verifying a model name, checking caps before a generation): the catalog stays collapsed to a single row the user can tap to browse.')
95
96
  },
96
97
  async ({ type, format, display_catalog }) => {
97
- // Widget only when the user asked to browse internal validation
98
- // lookups were spamming the chat with a full catalog card.
98
+ // The tool DECLARATION always carries widget meta, so hosts that mount
99
+ // from tools/list (Claude Code desktop) prepare an iframe on EVERY call.
100
+ // Returning plain text for internal lookups left that iframe with no
101
+ // data — a dead, empty "Widget from Kolbo list_models" shell. Always
102
+ // ship structuredContent; `compact` tells the widget to render a single
103
+ // "Browse models" row (expandable) instead of the full catalog, which is
104
+ // what display_catalog was really asking for.
99
105
  const showCatalog = display_catalog === true;
100
106
  const path = type ? `/v1/models?type=${encodeURIComponent(type)}` : '/v1/models';
101
107
  const result = await client.get(path);
@@ -106,7 +112,7 @@ function registerModelTools(server, client, options = {}) {
106
112
  // resolution multipliers, supports_* flags, prompt-length limits, etc.).
107
113
  if (format === 'json') {
108
114
  const text = JSON.stringify({ count: result.count, models: result.models }, null, 2);
109
- if (ui() && showCatalog) return uiResult(UI.catalog, text, buildCatalogStructured(result.models, type));
115
+ if (ui()) return uiResult(UI.catalog, text, buildCatalogStructured(result.models, type, !showCatalog));
110
116
  return { content: [{ type: 'text', text }] };
111
117
  }
112
118
 
@@ -277,7 +283,7 @@ function registerModelTools(server, client, options = {}) {
277
283
  }
278
284
 
279
285
  const text = `Available models (${result.count}):\n\n${sections.join('\n\n')}\n\nUse the "identifier" value as the "model" parameter in generate tools. For programmatic cap validation, re-call with format: "json".`;
280
- if (ui() && showCatalog) return uiResult(UI.catalog, text, buildCatalogStructured(result.models, type));
286
+ if (ui()) return uiResult(UI.catalog, text, buildCatalogStructured(result.models, type, !showCatalog));
281
287
  return { content: [{ type: 'text', text }] };
282
288
  }
283
289
  );