@kolbo/mcp 1.31.0 → 1.31.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
package/src/apps/bridge.js
CHANGED
|
@@ -33,7 +33,7 @@ const BRIDGE_JS = `
|
|
|
33
33
|
var initialized = false;
|
|
34
34
|
var queue = []; // deferred host-bound sends until initialized
|
|
35
35
|
var hostContext = null;
|
|
36
|
-
var readyFns = [], toolResultFns = [], themeFns = [];
|
|
36
|
+
var readyFns = [], toolResultFns = [], themeFns = [], toolInputFns = [];
|
|
37
37
|
|
|
38
38
|
function post(msg) { window.parent.postMessage(msg, '*'); }
|
|
39
39
|
|
|
@@ -66,6 +66,10 @@ const BRIDGE_JS = `
|
|
|
66
66
|
}
|
|
67
67
|
if (m.method === 'ui/notifications/tool-result') {
|
|
68
68
|
toolResultFns.forEach(function (f) { try { f(m.params || {}); } catch (e) {} });
|
|
69
|
+
} else if (m.method === 'ui/notifications/tool-input' || m.method === 'ui/notifications/tool-input-partial') {
|
|
70
|
+
// Fires while the tool is still RUNNING — lets widgets show a real
|
|
71
|
+
// "preparing" state instead of a blank card until the result lands.
|
|
72
|
+
toolInputFns.forEach(function (f) { try { f((m.params && m.params.arguments) || {}); } catch (e) {} });
|
|
69
73
|
} else if (m.method === 'ui/notifications/host-context-changed') {
|
|
70
74
|
hostContext = (m.params && m.params.hostContext) || m.params || hostContext;
|
|
71
75
|
themeFns.forEach(function (f) { try { f(hostContext); } catch (e) {} });
|
|
@@ -132,6 +136,7 @@ const BRIDGE_JS = `
|
|
|
132
136
|
window.kolbo = {
|
|
133
137
|
ready: function (f) { if (initialized) f(hostContext); else readyFns.push(f); },
|
|
134
138
|
onToolResult: function (f) { toolResultFns.push(f); },
|
|
139
|
+
onToolInput: function (f) { toolInputFns.push(f); },
|
|
135
140
|
onThemeChange: function (f) { themeFns.push(f); },
|
|
136
141
|
callTool: function (name, args) { return request('tools/call', { name: name, arguments: args || {} }); },
|
|
137
142
|
sendMessage: function (text) {
|
package/src/apps/html.js
CHANGED
|
@@ -33,6 +33,12 @@ function esc(s) {
|
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
function el(id) { return document.getElementById(id); }
|
|
36
|
+
// Force-download via the api.kolbo.ai proxy (CDN files open inline otherwise —
|
|
37
|
+
// browsers display images/videos instead of saving them).
|
|
38
|
+
function downloadUrl(u) {
|
|
39
|
+
if (!u) return u;
|
|
40
|
+
return 'https://api.kolbo.ai/mcp/download?url=' + encodeURIComponent(u);
|
|
41
|
+
}
|
|
36
42
|
function fmtCredits(n) { return (n == null) ? '' : (Math.round(n * 100) / 100) + ' cr'; }
|
|
37
43
|
function fmtDur(s) { if (s == null) return ''; s = Math.round(s); return s >= 60 ? Math.floor(s/60) + 'm ' + (s%60) + 's' : s + 's'; }
|
|
38
44
|
function applyTheme(ctx) {
|
|
@@ -64,7 +64,12 @@ function boot(sc) {
|
|
|
64
64
|
|
|
65
65
|
window.kolbo.onToolResult(function (result) {
|
|
66
66
|
var sc = result.structuredContent || structured(result);
|
|
67
|
-
if (sc) boot(sc);
|
|
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.
|
|
70
|
+
var card = document.querySelector('.k-card');
|
|
71
|
+
if (card) card.style.display = 'none';
|
|
72
|
+
window.kolbo.notifySize();
|
|
68
73
|
});
|
|
69
74
|
`;
|
|
70
75
|
|
|
@@ -224,7 +224,7 @@ function render3d(sc, urls) {
|
|
|
224
224
|
'<button class="k-btn" data-url="' + esc(u) + '">Download</button></div>';
|
|
225
225
|
}).join('');
|
|
226
226
|
Array.prototype.forEach.call(el('stage').querySelectorAll('.k-btn[data-url]'), function (b) {
|
|
227
|
-
b.onclick = function () { window.kolbo.openLink(b.getAttribute('data-url')); };
|
|
227
|
+
b.onclick = function () { window.kolbo.openLink(downloadUrl(b.getAttribute('data-url'))); };
|
|
228
228
|
});
|
|
229
229
|
}
|
|
230
230
|
|
|
@@ -259,7 +259,8 @@ function renderError(msg) {
|
|
|
259
259
|
el('stage').innerHTML = '<div class="k-error">⚠ ' + esc(msg) + '</div>';
|
|
260
260
|
el('actions').innerHTML = '<button class="k-btn" id="retry-btn">↻ Try Again</button>';
|
|
261
261
|
el('retry-btn').onclick = function () {
|
|
262
|
-
|
|
262
|
+
var what = (state && TOOL_TITLES[state.tool]) || 'generation';
|
|
263
|
+
window.kolbo.sendMessage('Please retry that ' + what.toLowerCase() + ' — it failed with: ' + msg);
|
|
263
264
|
};
|
|
264
265
|
window.kolbo.notifySize();
|
|
265
266
|
}
|
|
@@ -324,7 +325,7 @@ function renderActions(sc) {
|
|
|
324
325
|
a.push('<button class="k-btn ghost" id="btn-open">Open in Kolbo ↗</button>');
|
|
325
326
|
el('actions').innerHTML = a.join('');
|
|
326
327
|
|
|
327
|
-
bind('btn-download', function () { window.kolbo.openLink(currentUrl()); });
|
|
328
|
+
bind('btn-download', function () { window.kolbo.openLink(downloadUrl(currentUrl())); });
|
|
328
329
|
bind('btn-open', function () { window.kolbo.openLink(state.open_url || 'https://app.kolbo.ai'); });
|
|
329
330
|
bind('btn-recreate', function () {
|
|
330
331
|
window.kolbo.sendMessage('Recreate this with the same settings' +
|
|
@@ -367,17 +368,41 @@ function openPromptRow(placeholder, onSend) {
|
|
|
367
368
|
window.kolbo.notifySize();
|
|
368
369
|
}
|
|
369
370
|
|
|
371
|
+
/* ---------- pre-result "Preparing" state ----------
|
|
372
|
+
The host mounts this iframe as soon as the tool is CALLED; the result can
|
|
373
|
+
take many seconds (model resolution, file upload, submit). Show a live
|
|
374
|
+
shell immediately instead of a blank card. */
|
|
375
|
+
function bootPre(toolName, args) {
|
|
376
|
+
if (state) return; // real data already arrived
|
|
377
|
+
el('tool-title').textContent = TOOL_TITLES[toolName] || 'Generation';
|
|
378
|
+
if (args && (args.prompt || args.text)) {
|
|
379
|
+
el('prompt').textContent = args.prompt || args.text;
|
|
380
|
+
el('prompt').style.display = '';
|
|
381
|
+
}
|
|
382
|
+
setPhaseChip('Preparing', true);
|
|
383
|
+
if (!el('stage').innerHTML) {
|
|
384
|
+
el('stage').innerHTML = '<div class="k-gen-grid n1"><div class="k-skel video" style="min-height:100px;max-height:140px"></div></div>';
|
|
385
|
+
}
|
|
386
|
+
window.kolbo.notifySize();
|
|
387
|
+
}
|
|
388
|
+
|
|
370
389
|
/* ---------- wire host events ---------- */
|
|
371
390
|
window.kolbo.onToolResult(function (result) {
|
|
372
391
|
var sc = result.structuredContent || structured(result);
|
|
373
|
-
if (sc) boot(sc);
|
|
392
|
+
if (sc && (sc.phase || sc.widget)) return boot(sc);
|
|
393
|
+
// Tool errored (or returned plain text): show it instead of a dead blank card.
|
|
394
|
+
var txt = '';
|
|
395
|
+
try { txt = (result.content || []).filter(function (c) { return c.type === 'text'; }).map(function (c) { return c.text; }).join(' '); } catch (e) {}
|
|
396
|
+
if (result.isError || /error|failed/i.test(txt)) {
|
|
397
|
+
renderError((txt || 'The request failed.').slice(0, 300));
|
|
398
|
+
}
|
|
374
399
|
});
|
|
400
|
+
window.kolbo.onToolInput(function (args) { bootPre(null, args); });
|
|
375
401
|
window.kolbo.ready(function (ctx) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
if (sc) boot(sc);
|
|
402
|
+
var info = ctx && ctx.toolInfo;
|
|
403
|
+
if (!state && info) {
|
|
404
|
+
if (info.result && info.result.structuredContent) return boot(info.result.structuredContent);
|
|
405
|
+
bootPre(info.tool && info.tool.name, null);
|
|
381
406
|
}
|
|
382
407
|
});
|
|
383
408
|
`;
|
|
@@ -72,7 +72,7 @@ function boot(sc) {
|
|
|
72
72
|
a.push('<button class="k-btn ghost" id="btn-copy">Copy text</button>');
|
|
73
73
|
el('actions').innerHTML = a.join('');
|
|
74
74
|
Array.prototype.forEach.call(el('actions').querySelectorAll('[data-url]'), function (b) {
|
|
75
|
-
b.onclick = function () { window.kolbo.openLink(b.getAttribute('data-url')); };
|
|
75
|
+
b.onclick = function () { window.kolbo.openLink(downloadUrl(b.getAttribute('data-url'))); };
|
|
76
76
|
});
|
|
77
77
|
var copyBtn = el('btn-copy');
|
|
78
78
|
if (copyBtn) copyBtn.onclick = function () {
|
package/src/tools/models.js
CHANGED
|
@@ -96,9 +96,13 @@ function registerModelTools(server, client, options = {}) {
|
|
|
96
96
|
'List available AI models on Kolbo. Filter by `type` to narrow to a generation type, and pass `format: "json"` to get the raw model documents (every constraint field, useful for programmatic comparison / cap validation before submitting a generation). Default `format: "text"` returns the human-readable summary.',
|
|
97
97
|
{
|
|
98
98
|
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.'),
|
|
99
|
-
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.')
|
|
99
|
+
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.'),
|
|
100
|
+
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.')
|
|
100
101
|
},
|
|
101
|
-
async ({ type, format }) => {
|
|
102
|
+
async ({ type, format, display_catalog }) => {
|
|
103
|
+
// Widget only when the user asked to browse — internal validation
|
|
104
|
+
// lookups were spamming the chat with a full catalog card.
|
|
105
|
+
const showCatalog = display_catalog === true;
|
|
102
106
|
const path = type ? `/v1/models?type=${encodeURIComponent(type)}` : '/v1/models';
|
|
103
107
|
const result = await client.get(path);
|
|
104
108
|
|
|
@@ -108,7 +112,7 @@ function registerModelTools(server, client, options = {}) {
|
|
|
108
112
|
// resolution multipliers, supports_* flags, prompt-length limits, etc.).
|
|
109
113
|
if (format === 'json') {
|
|
110
114
|
const text = JSON.stringify({ count: result.count, models: result.models }, null, 2);
|
|
111
|
-
if (ui()) return uiResult(UI.catalog, text, buildCatalogStructured(result.models, type));
|
|
115
|
+
if (ui() && showCatalog) return uiResult(UI.catalog, text, buildCatalogStructured(result.models, type));
|
|
112
116
|
return { content: [{ type: 'text', text }] };
|
|
113
117
|
}
|
|
114
118
|
|
|
@@ -279,7 +283,7 @@ function registerModelTools(server, client, options = {}) {
|
|
|
279
283
|
}
|
|
280
284
|
|
|
281
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".`;
|
|
282
|
-
if (ui()) return uiResult(UI.catalog, text, buildCatalogStructured(result.models, type));
|
|
286
|
+
if (ui() && showCatalog) return uiResult(UI.catalog, text, buildCatalogStructured(result.models, type));
|
|
283
287
|
return { content: [{ type: 'text', text }] };
|
|
284
288
|
}
|
|
285
289
|
);
|