@kolbo/mcp 1.106.0 → 1.106.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.106.0",
3
+ "version": "1.106.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": {
package/src/apps/index.js CHANGED
@@ -20,25 +20,25 @@ const { generationWidgetHtml } = require('./widgets/generation');
20
20
  const { mediaGridWidgetHtml } = require('./widgets/mediaGrid');
21
21
  const { catalogWidgetHtml } = require('./widgets/catalog');
22
22
  const { transcriptWidgetHtml } = require('./widgets/transcript');
23
- const { uploadWidgetHtml } = require('./widgets/upload');
24
- const { fontUploadWidgetHtml } = require('./widgets/fontUpload');
23
+ const { uploadWidgetHtml } = require('./widgets/upload');
24
+ const { fontUploadWidgetHtml } = require('./widgets/fontUpload');
25
25
  const { listWidgetHtml } = require('./widgets/list');
26
26
  const { HOST_MAP } = require('../cdn');
27
- const { plansWidgetHtml } = require('./widgets/plans');
28
- const { creditsWidgetHtml } = require('./widgets/credits');
29
- const { ResourceTemplate } = require('@modelcontextprotocol/sdk/server/mcp.js');
30
- const { McpError, ErrorCode } = require('@modelcontextprotocol/sdk/types.js');
27
+ const { plansWidgetHtml } = require('./widgets/plans');
28
+ const { creditsWidgetHtml } = require('./widgets/credits');
29
+ const { ResourceTemplate } = require('@modelcontextprotocol/sdk/server/mcp.js');
30
+ const { McpError, ErrorCode } = require('@modelcontextprotocol/sdk/types.js');
31
31
 
32
32
  const UI = {
33
33
  generation: 'ui://kolbo/generation.html',
34
34
  mediaGrid: 'ui://kolbo/media-grid.html',
35
35
  catalog: 'ui://kolbo/catalog.html',
36
36
  transcript: 'ui://kolbo/transcript.html',
37
- upload: 'ui://kolbo/upload.html',
38
- fontUpload: 'ui://kolbo/font-upload.html',
37
+ upload: 'ui://kolbo/upload.html',
38
+ fontUpload: 'ui://kolbo/font-upload.html',
39
39
  list: 'ui://kolbo/list.html',
40
- plans: 'ui://kolbo/plans.html',
41
- credits: 'ui://kolbo/credits.html',
40
+ plans: 'ui://kolbo/plans.html',
41
+ credits: 'ui://kolbo/credits.html',
42
42
  };
43
43
 
44
44
  const WIDGET_BUILDERS = {
@@ -46,11 +46,11 @@ const WIDGET_BUILDERS = {
46
46
  [UI.mediaGrid]: mediaGridWidgetHtml,
47
47
  [UI.catalog]: catalogWidgetHtml,
48
48
  [UI.transcript]: transcriptWidgetHtml,
49
- [UI.upload]: uploadWidgetHtml,
50
- [UI.fontUpload]: fontUploadWidgetHtml,
49
+ [UI.upload]: uploadWidgetHtml,
50
+ [UI.fontUpload]: fontUploadWidgetHtml,
51
51
  [UI.list]: listWidgetHtml,
52
- [UI.plans]: plansWidgetHtml,
53
- [UI.credits]: creditsWidgetHtml,
52
+ [UI.plans]: plansWidgetHtml,
53
+ [UI.credits]: creditsWidgetHtml,
54
54
  };
55
55
 
56
56
  // Widgets are pure functions of source — build once per process.
@@ -169,11 +169,11 @@ function registerApps(server) {
169
169
  [UI.mediaGrid, 'Kolbo Library Widget'],
170
170
  [UI.catalog, 'Kolbo Model Catalog Widget'],
171
171
  [UI.transcript, 'Kolbo Transcription Widget'],
172
- [UI.upload, 'Kolbo Upload Widget'],
173
- [UI.fontUpload, 'Kolbo Font Upload Widget'],
172
+ [UI.upload, 'Kolbo Upload Widget'],
173
+ [UI.fontUpload, 'Kolbo Font Upload Widget'],
174
174
  [UI.list, 'Kolbo List Widget'],
175
- [UI.plans, 'Kolbo Plans Widget'],
176
- [UI.credits, 'Kolbo Credit Balance Widget'],
175
+ [UI.plans, 'Kolbo Plans Widget'],
176
+ [UI.credits, 'Kolbo Credit Balance Widget'],
177
177
  ]) {
178
178
  for (const u of [versionedUri(uri), uri]) {
179
179
  registerAppResource(
@@ -185,30 +185,30 @@ function registerApps(server) {
185
185
  _meta: { csp: WIDGET_CSP, ui: { csp: WIDGET_CSP } },
186
186
  }],
187
187
  })
188
- );
189
- }
190
- // Published tool definitions and old conversations can retain any previous
191
- // content hash. Keep those addresses readable after deploying new HTML.
192
- // Match only this registered widget and the version format we publish.
193
- registerAppResource(
194
- server, name + ' (previous versions)',
195
- new ResourceTemplate(uri + '{?v}', { list: undefined }),
196
- { mimeType: RESOURCE_MIME_TYPE, _meta: { csp: WIDGET_CSP, ui: { csp: WIDGET_CSP } } },
197
- async (requestedUri) => {
198
- const requested = requestedUri.href;
199
- if (!new RegExp('^' + uri.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\?v=[a-f0-9]{10}$').test(requested)) {
200
- throw new McpError(ErrorCode.InvalidParams, 'Unknown widget version URI');
201
- }
202
- return {
203
- contents: [{
204
- uri: requested, mimeType: RESOURCE_MIME_TYPE, text: widgetHtml(uri),
205
- _meta: { csp: WIDGET_CSP, ui: { csp: WIDGET_CSP } },
206
- }],
207
- };
208
- }
209
- );
210
- }
211
- }
188
+ );
189
+ }
190
+ // Published tool definitions and old conversations can retain any previous
191
+ // content hash. Keep those addresses readable after deploying new HTML.
192
+ // Match only this registered widget and the version format we publish.
193
+ registerAppResource(
194
+ server, name + ' (previous versions)',
195
+ new ResourceTemplate(uri + '{?v}', { list: undefined }),
196
+ { mimeType: RESOURCE_MIME_TYPE, _meta: { csp: WIDGET_CSP, ui: { csp: WIDGET_CSP } } },
197
+ async (requestedUri) => {
198
+ const requested = requestedUri.href;
199
+ if (!new RegExp('^' + uri.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\?v=[a-f0-9]{10}$').test(requested)) {
200
+ throw new McpError(ErrorCode.InvalidParams, 'Unknown widget version URI');
201
+ }
202
+ return {
203
+ contents: [{
204
+ uri: requested, mimeType: RESOURCE_MIME_TYPE, text: widgetHtml(uri),
205
+ _meta: { csp: WIDGET_CSP, ui: { csp: WIDGET_CSP } },
206
+ }],
207
+ };
208
+ }
209
+ );
210
+ }
211
+ }
212
212
 
213
213
  /** `_meta` for a tool RESULT (and optionally for tool registration). */
214
214
  function uiMeta(uri) {
@@ -371,9 +371,25 @@ async function modelInfo(client, modelName) {
371
371
  const rows = catalog.all || [];
372
372
  return rows.find((row) => normId(row.id) === want || normId(row.name) === want)
373
373
  || rows.find((row) => normId(row.id).startsWith(want) || normId(row.name).startsWith(want))
374
+ || variantBase(rows, modelName)
374
375
  || { icon: null, eta: null, name: null };
375
376
  }
376
377
 
378
+ // A finished generation can report an internal variant of a catalog model —
379
+ // "seedance-2-5-draft-enhance" (draft tier + prompt enhance) is not itself in
380
+ // /v1/models, so the chip showed a letter monogram and the raw id. Resolve it
381
+ // to the LONGEST catalog identifier it extends at a "-" boundary
382
+ // ("seedance-2-5", not "seedance-2").
383
+ function variantBase(rows, modelName) {
384
+ const raw = String(modelName).toLowerCase();
385
+ let best = null;
386
+ for (const row of rows) {
387
+ const id = row.id && String(row.id).toLowerCase();
388
+ if (id && raw.startsWith(id + '-') && (!best || id.length > best.id.length)) best = row;
389
+ }
390
+ return best;
391
+ }
392
+
377
393
  /**
378
394
  * Snap a requested aspect ratio onto the catalog enum for that model.
379
395
  * Every image/video generate_* tool must call this after canonicalModelId —
@@ -621,8 +637,8 @@ async function canonicalModelId(client, input, type) {
621
637
  if (!input || typeof input !== 'string') return input;
622
638
  const key = input.toLowerCase().trim();
623
639
  const want = normId(key);
624
- if (!want || AUTO_ALIASES.has(want)) return input;
625
- // Exact native-workflow ids pass through: the near-miss check below would reject an
640
+ if (!want || AUTO_ALIASES.has(want)) return input;
641
+ // Exact native-workflow ids pass through: the near-miss check below would reject an
626
642
  // unpublished or cached-out 'kolbo-*' id as unknown.
627
643
  if (NATIVE_WORKFLOW_MODEL_IDS.has(key)) return input;
628
644
 
@@ -769,8 +785,8 @@ const TOOL_WIDGETS = {
769
785
  // never saw it as widget-carrying. Result-level _meta alone isn't enough.
770
786
  list_color_palettes: UI.mediaGrid,
771
787
  // upload widget
772
- media_upload_widget: UI.upload,
773
- font_upload_widget: UI.fontUpload,
788
+ media_upload_widget: UI.upload,
789
+ font_upload_widget: UI.fontUpload,
774
790
  // generic list widget — flat record lists with no natural thumbnail
775
791
  list_projects: UI.list,
776
792
  // Must stay list.html — mapping this to generation.html mounts "Kolbo Generation /
@@ -782,8 +798,8 @@ const TOOL_WIDGETS = {
782
798
  list_docs: UI.list,
783
799
  list_media_folders: UI.list,
784
800
  list_visual_dna_folders: UI.list,
785
- show_plans: UI.plans,
786
- check_credits: UI.credits,
801
+ show_plans: UI.plans,
802
+ check_credits: UI.credits,
787
803
  list_project_assets: UI.list,
788
804
  };
789
805
 
package/src/apps/theme.js CHANGED
@@ -335,8 +335,6 @@ video::-webkit-media-controls-fullscreen-button { display: none !important; }
335
335
  hover-only Download / Attach buttons were simply invisible. */
336
336
  @media (hover: none) { .k-dl { opacity: 1; } }
337
337
  .k-dl:hover { background: var(--brand); border-color: var(--brand); }
338
- /* Attach-to-prompt sits immediately left of Download (30px button + 8px gap). */
339
- .k-attach { right: 46px; }
340
338
  .k-viewer { position: relative; }
341
339
 
342
340
  /* Keep the whole completed card under the host's iframe height cap (~800px):
@@ -1040,12 +1040,10 @@ function renderLinks(urls) {
1040
1040
  // Small hover download button attached to a media cell (per-item downloads —
1041
1041
  // batch grids and CD scenes have no single "current" url for the action row).
1042
1042
  function dlBtnHTML(u) {
1043
- // Attach sits beside Download on the same hover overlay. It is the reliable
1044
- // route into the composer — see window.kolbo.attachMedia for why dragging the
1045
- // media out of the iframe cannot be made to work.
1046
- return '<button class="k-dl k-attach" data-attach="' + esc(u) + '" title="Attach to prompt" aria-label="Attach to prompt">'
1047
- + ICONS.upload + '</button>'
1048
- + '<button class="k-dl" data-dl="' + esc(u) + '" title="Download" aria-label="Download">' + ICONS.download + '</button>';
1043
+ // Download only. The "attach to prompt" button that sat beside it was
1044
+ // removed: no host (claude.ai, ChatGPT, Claude Code, Kolbo Code) reliably
1045
+ // put the media into the composer, so it read as a dead upload button.
1046
+ return '<button class="k-dl" data-dl="' + esc(u) + '" title="Download" aria-label="Download">' + ICONS.download + '</button>';
1049
1047
  }
1050
1048
  function wireDlButtons(root) {
1051
1049
  Array.prototype.forEach.call((root || document).querySelectorAll('.k-dl[data-dl]'), function (b) {
@@ -1054,12 +1052,6 @@ function wireDlButtons(root) {
1054
1052
  window.kolbo.openLink(downloadUrl(b.getAttribute('data-dl')));
1055
1053
  };
1056
1054
  });
1057
- Array.prototype.forEach.call((root || document).querySelectorAll('.k-attach[data-attach]'), function (b) {
1058
- b.onclick = function (e) {
1059
- e.stopPropagation();
1060
- window.kolbo.attachMedia(b.getAttribute('data-attach'));
1061
- };
1062
- });
1063
1055
  }
1064
1056
 
1065
1057
  // CD scenes render as the SAME viewer+thumbnail carousel as image batches —