@kolbo/mcp 1.79.7 → 1.80.0
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/skill/GENERATED.md +1 -1
- package/src/apps/bridge.js +2 -0
- package/src/apps/html.js +90 -1
- package/src/apps/index.js +153 -5
- package/src/apps/theme.js +42 -4
- package/src/apps/widgets/generation.js +198 -84
- package/src/apps/widgets/list.js +4 -1
- package/src/apps/widgets/mediaGrid.js +10 -3
- package/src/apps/widgets/transcript.js +4 -1
- package/src/tools/_shared.js +157 -22
- package/src/tools/generate.js +45 -21
- package/src/tools/moodboards.js +1 -0
- package/src/tools/presets.js +37 -36
- package/src/tools/visual_dna.js +12 -4
package/src/tools/presets.js
CHANGED
|
@@ -12,55 +12,56 @@ function registerPresetTools(server, client, options = {}) {
|
|
|
12
12
|
// ─── list_presets ──────────────────────────────────────────
|
|
13
13
|
server.tool(
|
|
14
14
|
'list_presets',
|
|
15
|
-
'
|
|
15
|
+
'Resolve a generation preset and pass its exact id as preset_id. ALWAYS pass `search` when you already know the name (headless, bible, character sheet, a user preset) — that is a silent id lookup, not a catalog to show the user. Omit search only when they asked to browse. Custom instructions live on the preset, so prefer generate_image + preset_id over generate_character_sheet. Never invent an id.',
|
|
16
16
|
{
|
|
17
|
-
type: z.string().optional().describe('Filter by catalog: "image" | "image_edit" | "video" | "music" | "text_to_video". Omit for all.')
|
|
17
|
+
type: z.string().optional().describe('Filter by catalog: "image" | "image_edit" | "video" | "music" | "text_to_video". Omit for all.'),
|
|
18
|
+
search: z.string().optional().describe('Name lookup, e.g. "headless", "bible", "character sheet". Required when resolving a named sheet/style — do not list the whole catalog.')
|
|
18
19
|
},
|
|
19
|
-
async ({ type }) => {
|
|
20
|
+
async ({ type, search }) => {
|
|
20
21
|
const params = new URLSearchParams();
|
|
21
22
|
if (type) params.set('type', type);
|
|
23
|
+
if (search) params.set('search', search);
|
|
22
24
|
const qs = params.toString();
|
|
23
25
|
const result = await client.get(`/v1/presets${qs ? '?' + qs : ''}`);
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
let presets = result.presets || [];
|
|
28
|
+
if (search && !result.search) {
|
|
29
|
+
const q = search.toLowerCase();
|
|
30
|
+
presets = presets.filter((p) => `${p.name || ''} ${p.description || ''} ${p.category || ''}`.toLowerCase().includes(q));
|
|
31
|
+
}
|
|
32
|
+
const lookup = Boolean(search);
|
|
33
|
+
// Full catalog measured 632,919 chars. A named lookup should return a handful of rows.
|
|
28
34
|
const text = compactList(presets, {
|
|
29
35
|
fields: ['id', 'name', 'category', 'type', 'description'],
|
|
30
|
-
cap:
|
|
36
|
+
cap: lookup ? 8 : 12,
|
|
31
37
|
total: result.count || presets.length,
|
|
32
|
-
extra:
|
|
33
|
-
|
|
38
|
+
extra: {
|
|
39
|
+
...(result.warning ? { warning: result.warning } : {}),
|
|
40
|
+
...(lookup ? { _lookup: true } : {}),
|
|
41
|
+
},
|
|
42
|
+
note: lookup
|
|
43
|
+
? 'Pick the closest id and pass it as preset_id. Do not show this list to the user.'
|
|
44
|
+
: 'Pass search next time if you already know the name. Pass the chosen exact id as preset_id.',
|
|
34
45
|
});
|
|
35
46
|
|
|
36
|
-
|
|
37
|
-
// renders widgets without advertising MCP Apps (Kolbo Code) with text-only rows
|
|
38
|
-
// that carry no thumbnail field at all — and its BY_TOOL map still force-mounts
|
|
39
|
-
// the media grid on them, so the card rendered one broken-file glyph per cell.
|
|
40
|
-
// media.js and listResult() have always done it this way; these five lagged.
|
|
41
|
-
{
|
|
42
|
-
return uiResult(UI.mediaGrid, text, {
|
|
43
|
-
widget: 'media-grid',
|
|
44
|
-
title: 'Presets' + (type ? ' — ' + type : ''),
|
|
45
|
-
items: presets.slice(0, 24).map(p => ({
|
|
46
|
-
id: p.id,
|
|
47
|
-
title: p.name,
|
|
48
|
-
subtitle: p.category,
|
|
49
|
-
// API returns thumbnail_url / audio_url (see sdk listPresets) — NOT
|
|
50
|
-
// thumbnail / audio_preview_url. Reading the wrong key rendered every
|
|
51
|
-
// preset as a blank tile and dropped music previews entirely.
|
|
52
|
-
thumbnail: p.thumbnail_url || p.thumbnail,
|
|
53
|
-
media_type: p.audio_url ? 'audio' : 'image',
|
|
54
|
-
preview_audio: p.audio_url,
|
|
55
|
-
url: p.thumbnail_url || p.thumbnail,
|
|
56
|
-
use_hint: 'Use preset "{TITLE}" (preset_id: {ID}) for my next generation — ask me for the prompt.'
|
|
57
|
-
})),
|
|
58
|
-
total: result.count || presets.length,
|
|
59
|
-
has_more: presets.length > 24
|
|
60
|
-
});
|
|
61
|
-
}
|
|
47
|
+
if (lookup) return { content: [{ type: 'text', text }] };
|
|
62
48
|
|
|
63
|
-
return
|
|
49
|
+
return uiResult(UI.list, text, {
|
|
50
|
+
widget: 'list',
|
|
51
|
+
title: 'Presets' + (type ? ' — ' + type : ''),
|
|
52
|
+
items: presets.slice(0, 8).map(p => ({
|
|
53
|
+
id: p.id,
|
|
54
|
+
title: p.name,
|
|
55
|
+
subtitle: p.category,
|
|
56
|
+
thumbnail: p.thumbnail_url || p.thumbnail,
|
|
57
|
+
media_type: p.audio_url ? 'audio' : 'image',
|
|
58
|
+
preview_audio: p.audio_url,
|
|
59
|
+
url: p.thumbnail_url || p.thumbnail,
|
|
60
|
+
use_hint: 'Use preset "{TITLE}" (preset_id: {ID}) for my next generation — ask me for the prompt.'
|
|
61
|
+
})),
|
|
62
|
+
total: result.count || presets.length,
|
|
63
|
+
has_more: presets.length > 8
|
|
64
|
+
});
|
|
64
65
|
}
|
|
65
66
|
);
|
|
66
67
|
|
package/src/tools/visual_dna.js
CHANGED
|
@@ -153,7 +153,8 @@ function registerVisualDnaTools(server, client, options = {}) {
|
|
|
153
153
|
id: d.id,
|
|
154
154
|
title: d.name,
|
|
155
155
|
subtitle: (d.dna_type || '') + (Array.isArray(d.tags) && d.tags.length ? ' · ' + d.tags.slice(0, 3).join(', ') : ''),
|
|
156
|
-
thumbnail: d.thumbnail_url || d.thumbnail,
|
|
156
|
+
thumbnail: d.sheet_url || d.thumbnail_url || d.thumbnail,
|
|
157
|
+
url: d.sheet_url || d.thumbnail_url || d.thumbnail,
|
|
157
158
|
media_type: 'image',
|
|
158
159
|
use_hint: 'Use Visual DNA "{TITLE}" (id: {ID}) in my next generation for character/style consistency.'
|
|
159
160
|
})),
|
|
@@ -210,15 +211,22 @@ function registerVisualDnaTools(server, client, options = {}) {
|
|
|
210
211
|
'STANDARD FIRST STEP OF THE ASSET PASS for any film/ad/scene: inventory the characters, locations and props the script needs, generate a sheet for each, create its Visual DNA from that sheet, confirm the whole set with the user, and only THEN generate video. Sheets for cinematic environments and invented characters run well on `mirage-film-2` (3cr); use `nano-banana-2` (10cr) or `gpt-image-2` (12cr) when reference fidelity or legible text matters. Generate a reference sheet for a Visual DNA from 1+ reference image URLs — the same step the in-app Visual DNA wizard offers, for EVERY DNA type via `sheet_type`: character = multi-angle turnaround, product = angles + branding/material/construction close-ups, environment = location angles + one signature detail, style = a style board (the same look applied to six varied subjects). The sheet is the single strongest consistency booster for a DNA, and it always preserves the reference\'s original art style (2D stays 2D, photo stays photo). CHARGES CREDITS, so when the user is about to create a DNA, OFFER this first ("want me to generate a reference sheet for stronger consistency? it costs a few credits") and only run it on a yes. Returns `character_sheet_url` — pass it as `character_sheet_url` to `create_visual_dna` with the matching `dna_type`.',
|
|
211
212
|
{
|
|
212
213
|
image_urls: z.array(z.string()).min(1).describe('Reference image URLs of the subject (for characters: front/side/varied angles work best). Use generated-image URLs or upload_media output.'),
|
|
213
|
-
sheet_type: z.enum(['character', 'character_headless', 'character_bible', 'product', 'environment', 'style']).optional().describe('Sheet layout. character = front/back/face turnaround. character_headless = wardrobe/body refs with a headless front panel (use when clothing must change without fighting the face sheet). character_bible = denser production model-sheet (turnaround + faces + wardrobe + color swatches). product / environment / style = matching DNA types. Defaults to character.')
|
|
214
|
+
sheet_type: z.enum(['character', 'character_headless', 'character_bible', 'product', 'environment', 'style']).optional().describe('Sheet layout. character = front/back/face turnaround. character_headless = wardrobe/body refs with a headless front panel (use when clothing must change without fighting the face sheet). character_bible = denser production model-sheet (turnaround + faces + wardrobe + color swatches). product / environment / style = matching DNA types. Defaults to character. This IS the Character Sheet / Headless / Bible preset — do not call list_presets for those names.'),
|
|
215
|
+
model: z.string().optional().describe('Image model for the sheet. Default nano-banana-2. Pass gpt-image-2 when the user names it.'),
|
|
216
|
+
resolution: z.enum(['2K', '4K']).optional().describe('Sheet resolution. 2K or 4K only — never 1K. Default 2K. Use 4K for character_bible, high-detail leads, or when the user asks.')
|
|
214
217
|
},
|
|
215
|
-
async ({ image_urls, sheet_type }) => {
|
|
218
|
+
async ({ image_urls, sheet_type, model, resolution }) => {
|
|
216
219
|
// The endpoint is blocking and a 2K multi-panel sheet routinely runs past the
|
|
217
220
|
// 120s default: the MCP aborted while kolbo-api kept going, finished, and
|
|
218
221
|
// billed — the user saw "Failed" for a sheet they had already paid for.
|
|
219
222
|
const result = await client.post(
|
|
220
223
|
'/v1/visual-dna/character-sheet',
|
|
221
|
-
{
|
|
224
|
+
{
|
|
225
|
+
image_urls,
|
|
226
|
+
...(sheet_type ? { sheet_type } : {}),
|
|
227
|
+
...(model ? { model } : {}),
|
|
228
|
+
...(resolution ? { resolution } : {}),
|
|
229
|
+
},
|
|
222
230
|
{ timeoutMs: CHARACTER_SHEET_TIMEOUT_MS },
|
|
223
231
|
);
|
|
224
232
|
// `urls` is NOT redundant with character_sheet_url. Every generation-card
|