@kolbo/mcp 1.30.4 → 1.30.6
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 +1 -1
- package/src/apps/index.js +56 -7
- package/src/index.js +6 -1
- package/src/tools/generate.js +16 -1
package/package.json
CHANGED
package/src/apps/index.js
CHANGED
|
@@ -48,16 +48,29 @@ function widgetHtml(uri) {
|
|
|
48
48
|
// img/script/style/font/media-src; connectDomains to connect-src.
|
|
49
49
|
const WIDGET_CSP = {
|
|
50
50
|
resourceDomains: [
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
'https
|
|
51
|
+
// Exact hosts FIRST — not every host CSP implementation honors wildcards,
|
|
52
|
+
// and a declared-but-unmatched allowlist blocks harder than no declaration.
|
|
53
|
+
'https://api.kolbo.ai', // model icons (/assets)
|
|
54
|
+
'https://app.kolbo.ai',
|
|
55
|
+
'https://media.kolbo.ai', // media CDN (prod)
|
|
56
|
+
'https://media-staging.kolbo.ai',
|
|
57
|
+
'https://media-dev.kolbo.ai',
|
|
58
|
+
'https://kolboai-production.ams3.digitaloceanspaces.com',
|
|
59
|
+
'https://kolboai-production.ams3.cdn.digitaloceanspaces.com',
|
|
60
|
+
'https://kolboai-development.ams3.digitaloceanspaces.com',
|
|
61
|
+
'https://kolboai-development.ams3.cdn.digitaloceanspaces.com',
|
|
62
|
+
'https://kolbo-general-media.fra1.cdn.digitaloceanspaces.com',
|
|
54
63
|
'https://fonts.googleapis.com', // Inter / JetBrains Mono stylesheet
|
|
55
64
|
'https://fonts.gstatic.com', // font files
|
|
56
65
|
'https://images.pexels.com', // stock thumbnails
|
|
66
|
+
// Wildcards as a second layer for hosts that do support them.
|
|
67
|
+
'https://*.kolbo.ai',
|
|
68
|
+
'https://*.digitaloceanspaces.com',
|
|
69
|
+
'https://*.cdn.digitaloceanspaces.com',
|
|
57
70
|
'https://*.pexels.com',
|
|
58
71
|
'https://*.pixabay.com',
|
|
59
72
|
'https://*.sketchfab.com',
|
|
60
|
-
'https://*.cloudfront.net',
|
|
73
|
+
'https://*.cloudfront.net',
|
|
61
74
|
],
|
|
62
75
|
connectDomains: [],
|
|
63
76
|
};
|
|
@@ -156,14 +169,24 @@ async function modelInfoMap(client) {
|
|
|
156
169
|
// Real p75 wall-clock estimate mined from production creditUsages —
|
|
157
170
|
// the same source the in-app countdowns use. No estimate → no ETA shown.
|
|
158
171
|
const eta = Number(m.estimatedDurationSeconds || m.estimated_duration_seconds) || null;
|
|
159
|
-
const info = { icon, eta };
|
|
160
|
-
|
|
172
|
+
const info = { icon, eta, id: m.identifier || null };
|
|
173
|
+
// Display names collide across variants ("Nano Banana 2" names both the
|
|
174
|
+
// t2i model and its editing sibling) — on collision keep the model with
|
|
175
|
+
// the SHORTEST identifier (the base model), deterministically.
|
|
176
|
+
const setName = (k) => {
|
|
177
|
+
const prev = byKey.get(k);
|
|
178
|
+
if (!prev || !prev.id || (info.id && info.id.length < prev.id.length)) byKey.set(k, info);
|
|
179
|
+
};
|
|
180
|
+
if (m.name) setName(String(m.name).toLowerCase());
|
|
161
181
|
if (m.identifier) byKey.set(String(m.identifier).toLowerCase(), info);
|
|
162
182
|
}
|
|
163
183
|
} catch (_) {
|
|
164
184
|
/* fail open — widgets fall back to monogram chips, no ETA */
|
|
165
185
|
}
|
|
166
|
-
|
|
186
|
+
// Never cache an empty map: the first request in a fresh worker (typical
|
|
187
|
+
// right after a deploy restart) can fail transiently, and caching that
|
|
188
|
+
// failure blanks every model icon for the TTL window.
|
|
189
|
+
if (byKey.size > 0) infoCache.set(cacheKey, { at: Date.now(), byKey });
|
|
167
190
|
return byKey;
|
|
168
191
|
}
|
|
169
192
|
|
|
@@ -179,6 +202,31 @@ async function modelIcon(client, modelName) {
|
|
|
179
202
|
return (await modelInfo(client, modelName)).icon;
|
|
180
203
|
}
|
|
181
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Lenient model-identifier resolution for LLM-supplied model args.
|
|
207
|
+
* Users say "z-image"; the real identifier is "z-image/turbo" — the backend
|
|
208
|
+
* has no fuzzy matching on generation routes and fails deep in credit
|
|
209
|
+
* reservation. Resolve here: exact name/identifier hit → its identifier;
|
|
210
|
+
* else a UNIQUE identifier prefix match ("z-image" → "z-image/turbo");
|
|
211
|
+
* ambiguous or unknown → pass through unchanged (API stays source of truth).
|
|
212
|
+
*/
|
|
213
|
+
async function canonicalModelId(client, input) {
|
|
214
|
+
if (!input || typeof input !== 'string') return input;
|
|
215
|
+
try {
|
|
216
|
+
const map = await modelInfoMap(client);
|
|
217
|
+
const key = input.toLowerCase().trim();
|
|
218
|
+
const hit = map.get(key) || map.get(key.replace(/\s+/g, '-'));
|
|
219
|
+
if (hit && hit.id) return hit.id;
|
|
220
|
+
const ids = new Set();
|
|
221
|
+
for (const info of map.values()) {
|
|
222
|
+
const id = (info.id || '').toLowerCase();
|
|
223
|
+
if (id && (id.startsWith(key + '/') || id.startsWith(key + '-'))) ids.add(info.id);
|
|
224
|
+
}
|
|
225
|
+
if (ids.size === 1) return [...ids][0];
|
|
226
|
+
} catch (_) { /* fail open */ }
|
|
227
|
+
return input;
|
|
228
|
+
}
|
|
229
|
+
|
|
182
230
|
/* ------------------------------------------------------------------ */
|
|
183
231
|
/* Declaration-level widget metadata */
|
|
184
232
|
/* ------------------------------------------------------------------ */
|
|
@@ -243,6 +291,7 @@ module.exports = {
|
|
|
243
291
|
modelIcon,
|
|
244
292
|
modelInfo,
|
|
245
293
|
modelInfoMap,
|
|
294
|
+
canonicalModelId,
|
|
246
295
|
resolveAvatarUrl,
|
|
247
296
|
widgetHtml, // exported for smoke tests
|
|
248
297
|
};
|
package/src/index.js
CHANGED
|
@@ -97,7 +97,12 @@ function createServer(opts = {}) {
|
|
|
97
97
|
|
|
98
98
|
const server = new McpServer({
|
|
99
99
|
name: 'kolbo',
|
|
100
|
-
|
|
100
|
+
title: 'Kolbo',
|
|
101
|
+
version: '1.0.0',
|
|
102
|
+
websiteUrl: 'https://kolbo.ai',
|
|
103
|
+
// Connector avatar for hosts that render server icons (claude.ai tool
|
|
104
|
+
// headers show this instead of a letter monogram).
|
|
105
|
+
icons: [{ src: 'https://api.kolbo.ai/assets/kolbo-ai.png', mimeType: 'image/png', sizes: ['512x512'] }]
|
|
101
106
|
});
|
|
102
107
|
|
|
103
108
|
// Register all tools. `inlineImages` (off by default) is opt-in: only the
|
package/src/tools/generate.js
CHANGED
|
@@ -7,7 +7,7 @@ const { z } = require('zod');
|
|
|
7
7
|
const FormData = require('form-data');
|
|
8
8
|
const { pollUntilDone } = require('../polling');
|
|
9
9
|
const { resolveToBuffer, creditFields, projectIdField, inlineImageBlocks, uiGenerating, uiCompleted, appsEnabled } = require('./_shared');
|
|
10
|
-
const { UI, uiResult } = require('../apps');
|
|
10
|
+
const { UI, uiResult, canonicalModelId } = require('../apps');
|
|
11
11
|
|
|
12
12
|
function registerGenerateTools(server, client, options = {}) {
|
|
13
13
|
// Only enabled by hosts that explicitly opt in (the remote HTTP connector).
|
|
@@ -37,6 +37,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
37
37
|
project_id: projectIdField
|
|
38
38
|
},
|
|
39
39
|
async ({ prompt, model, aspect_ratio, enhance_prompt, num_images, reference_images, visual_dna_ids, moodboard_id, enable_web_search, resolution, preset_id, project_id }) => {
|
|
40
|
+
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
40
41
|
const gen = await client.post('/v1/generate/image', {
|
|
41
42
|
prompt, model, aspect_ratio, enhance_prompt, num_images,
|
|
42
43
|
reference_images, visual_dna_ids, moodboard_id, enable_web_search, resolution, preset_id, project_id
|
|
@@ -87,6 +88,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
87
88
|
project_id: projectIdField
|
|
88
89
|
},
|
|
89
90
|
async ({ prompt, model, source_images, aspect_ratio, enhance_prompt, num_images, visual_dna_ids, moodboard_id, enable_web_search, resolution, project_id }) => {
|
|
91
|
+
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
90
92
|
const gen = await client.post('/v1/generate/image-edit', {
|
|
91
93
|
prompt, model, source_images, aspect_ratio, enhance_prompt, num_images,
|
|
92
94
|
visual_dna_ids, moodboard_id, enable_web_search, resolution, project_id
|
|
@@ -143,6 +145,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
143
145
|
project_id: projectIdField
|
|
144
146
|
},
|
|
145
147
|
async ({ prompt, scene_count, model, aspect_ratio, workflow_type, duration, enhance_prompt, reference_images, visual_dna_ids, moodboard_id, moodboard_ids, resolution, project_id }) => {
|
|
148
|
+
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
146
149
|
const gen = await client.post('/v1/generate/creative-director', {
|
|
147
150
|
prompt, scene_count, model, aspect_ratio, workflow_type, duration,
|
|
148
151
|
enhance_prompt, reference_images, visual_dna_ids, moodboard_id, moodboard_ids, resolution, project_id
|
|
@@ -204,6 +207,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
204
207
|
project_id: projectIdField
|
|
205
208
|
},
|
|
206
209
|
async ({ prompt, model, aspect_ratio, duration, enhance_prompt, reference_images, resolution, preset_id, project_id }) => {
|
|
210
|
+
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
207
211
|
const gen = await client.post('/v1/generate/video', {
|
|
208
212
|
prompt, model, aspect_ratio, duration, enhance_prompt, reference_images, resolution, preset_id, project_id
|
|
209
213
|
});
|
|
@@ -252,6 +256,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
252
256
|
project_id: projectIdField
|
|
253
257
|
},
|
|
254
258
|
async ({ image_url, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, resolution, project_id }) => {
|
|
259
|
+
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
255
260
|
const gen = await client.post('/v1/generate/video/from-image', {
|
|
256
261
|
image_url, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, resolution, project_id
|
|
257
262
|
});
|
|
@@ -299,6 +304,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
299
304
|
project_id: projectIdField
|
|
300
305
|
},
|
|
301
306
|
async ({ prompt, model, style, instrumental, lyrics, vocal_gender, enhance_prompt, preset_id, project_id }) => {
|
|
307
|
+
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
302
308
|
const gen = await client.post('/v1/generate/music', {
|
|
303
309
|
prompt, model, style, instrumental, lyrics, vocal_gender, enhance_prompt, preset_id, project_id
|
|
304
310
|
});
|
|
@@ -340,6 +346,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
340
346
|
project_id: projectIdField
|
|
341
347
|
},
|
|
342
348
|
async ({ text, voice, model, language, project_id }) => {
|
|
349
|
+
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
343
350
|
const gen = await client.post('/v1/generate/speech', {
|
|
344
351
|
text, voice, model, language, project_id
|
|
345
352
|
});
|
|
@@ -380,6 +387,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
380
387
|
project_id: projectIdField
|
|
381
388
|
},
|
|
382
389
|
async ({ prompt, model, duration, prompt_influence, project_id }) => {
|
|
390
|
+
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
383
391
|
const gen = await client.post('/v1/generate/sound', {
|
|
384
392
|
prompt, model, duration, prompt_influence, project_id
|
|
385
393
|
});
|
|
@@ -451,6 +459,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
451
459
|
project_id: projectIdField
|
|
452
460
|
},
|
|
453
461
|
async ({ prompt, model, reference_images, reference_videos, audio_url, files, duration, aspect_ratio, motion, preset_id, enhance_prompt, visual_dna_ids, resolution, project_id }) => {
|
|
462
|
+
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
454
463
|
if (!prompt) throw new Error('prompt is required');
|
|
455
464
|
|
|
456
465
|
let startResponse;
|
|
@@ -527,6 +536,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
527
536
|
project_id: projectIdField
|
|
528
537
|
},
|
|
529
538
|
async ({ first_frame_url, last_frame_url, first_frame, last_frame, prompt, model, duration, aspect_ratio, enhance_prompt, visual_dna_ids, resolution, project_id }) => {
|
|
539
|
+
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
530
540
|
const urlMode = first_frame_url && last_frame_url;
|
|
531
541
|
const fileMode = first_frame && last_frame;
|
|
532
542
|
if (!urlMode && !fileMode) {
|
|
@@ -614,6 +624,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
614
624
|
project_id: projectIdField
|
|
615
625
|
},
|
|
616
626
|
async ({ source, audio, text_prompt, model, bounding_box_target, sync_mode, model_mode, emotion, temperature, occlusion_detection_enabled, active_speaker_detection, project_id }) => {
|
|
627
|
+
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
617
628
|
if (!source) throw new Error('source is required (URL or absolute local path to image/video)');
|
|
618
629
|
if (!audio) throw new Error('audio is required (URL or absolute local path to audio file)');
|
|
619
630
|
|
|
@@ -732,6 +743,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
732
743
|
project_id: projectIdField
|
|
733
744
|
},
|
|
734
745
|
async ({ source_video, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, resolution, reference_images, reference_videos, elements, preset, source_language, translation_language, srt_content, srt_file_url, vocabulary, customization, project_id }) => {
|
|
746
|
+
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
735
747
|
if (!source_video) throw new Error('source_video is required');
|
|
736
748
|
|
|
737
749
|
const isUrl = /^https?:\/\//i.test(source_video);
|
|
@@ -868,6 +880,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
868
880
|
project_id: projectIdField
|
|
869
881
|
},
|
|
870
882
|
async ({ prompt, reference_images, mode, texture_prompt, model, topology, target_polycount, enable_tpose, enable_pbr, project_id }) => {
|
|
883
|
+
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
871
884
|
if (!prompt && !(reference_images && reference_images.length > 0)) {
|
|
872
885
|
throw new Error('Provide prompt (text mode) or reference_images (single/multi mode)');
|
|
873
886
|
}
|
|
@@ -927,6 +940,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
927
940
|
project_id: projectIdField
|
|
928
941
|
},
|
|
929
942
|
async ({ image_url, operation, model, scale, aspect_ratio, skin_strength, prompt, project_id }) => {
|
|
943
|
+
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
930
944
|
if (operation === 'magic_edit' && !prompt) throw new Error('prompt is required for magic_edit operation');
|
|
931
945
|
if (operation === 'reframe' && !aspect_ratio) throw new Error('aspect_ratio is required for reframe operation');
|
|
932
946
|
|
|
@@ -979,6 +993,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
979
993
|
project_id: projectIdField
|
|
980
994
|
},
|
|
981
995
|
async ({ video_url, operation, model, aspect_ratio, scale, prompt, image_url, audio_url, duration, mode, project_id }) => {
|
|
996
|
+
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
982
997
|
if (operation === 'magic_edit' && !prompt) throw new Error('prompt is required for magic_edit');
|
|
983
998
|
if (operation === 'generate_audio' && !prompt) throw new Error('prompt is required for generate_audio');
|
|
984
999
|
if (operation === 'reframe' && !aspect_ratio) throw new Error('aspect_ratio is required for reframe');
|