@kolbo/mcp 1.72.3 → 1.72.4
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/widgets/generation.js +2 -1
- package/src/tools/generate.js +13 -2
- package/src/tools/media.js +49 -45
- package/src/tools/moodboards.js +21 -21
- package/src/tools/visual_dna.js +20 -20
package/package.json
CHANGED
|
@@ -164,7 +164,8 @@ function renderChips(sc) {
|
|
|
164
164
|
var h = modelChipHTML(modelLabel(sc), sc.model_icon);
|
|
165
165
|
var s = sc.settings || {};
|
|
166
166
|
if (sc.kind) h += chip(iconFor(sc.kind) + ' ' + sc.kind);
|
|
167
|
-
if (s.duration) h += chip(ICONS.clock + ' ' + fmtDur(s.duration));
|
|
167
|
+
if (s.duration) h += chip(ICONS.clock + ' ' + fmtDur(s.duration) + (s.shots > 1 ? ' · ' + s.shots + ' shots' : ''));
|
|
168
|
+
else if (s.shots > 1) h += chip(s.shots + ' shots');
|
|
168
169
|
if (s.resolution) h += chip(esc(s.resolution));
|
|
169
170
|
if (s.aspect_ratio) h += chip(esc(s.aspect_ratio));
|
|
170
171
|
if (s.quality) h += chip(esc(s.quality) + ' quality');
|
package/src/tools/generate.js
CHANGED
|
@@ -125,7 +125,13 @@ const imageSettings = (a = {}) => ({
|
|
|
125
125
|
// Same block for the video tools, which carried only { duration, resolution,
|
|
126
126
|
// aspect_ratio } — a DNA-anchored video card showed no sign a DNA was in play.
|
|
127
127
|
const videoSettings = (a = {}) => ({
|
|
128
|
-
|
|
128
|
+
// Multi-shot elements calls usually set shot COUNT and let the model take the
|
|
129
|
+
// default per-shot length, so `duration` came through undefined and the card
|
|
130
|
+
// showed model/resolution/ratio but never how long the video actually is —
|
|
131
|
+
// the one number the user picked and is paying for. Fall back to what the API
|
|
132
|
+
// echoed back on start, then to per-shot × shots.
|
|
133
|
+
duration: a.duration ?? a.reported_duration ?? (a.shot_duration && a.shots ? a.shot_duration * a.shots : undefined),
|
|
134
|
+
...(a.shots > 1 ? { shots: a.shots } : {}),
|
|
129
135
|
resolution: a.resolution,
|
|
130
136
|
aspect_ratio: a.aspect_ratio,
|
|
131
137
|
...(a.mode ? { mode: a.mode } : {}),
|
|
@@ -1093,7 +1099,12 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1093
1099
|
|
|
1094
1100
|
if (ui()) return uiGenerating({
|
|
1095
1101
|
tool: 'generate_elements', kind: 'video', gen: startResponse, client, model, prompt,
|
|
1096
|
-
settings: videoSettings({
|
|
1102
|
+
settings: videoSettings({
|
|
1103
|
+
duration,
|
|
1104
|
+
reported_duration: startResponse?.duration ?? startResponse?.result?.duration,
|
|
1105
|
+
shots: multi_shot_count ?? (Array.isArray(multi_shots) ? multi_shots.length : undefined),
|
|
1106
|
+
resolution, aspect_ratio, enhance_prompt, visual_dna_ids, preset_id,
|
|
1107
|
+
}),
|
|
1097
1108
|
reference_images: [
|
|
1098
1109
|
...(reference_images || []),
|
|
1099
1110
|
...(keyframes || []).map((keyframe) => keyframe.image_url),
|
package/src/tools/media.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
const { z } = require('zod');
|
|
7
7
|
const FormData = require('form-data');
|
|
8
8
|
const { resolveToBuffer, DEFAULT_MAX_FILE_MB, compactList } = require('./_shared');
|
|
9
|
-
const { UI, uiResult, listResult
|
|
9
|
+
const { UI, uiResult, listResult } = require('../apps');
|
|
10
10
|
|
|
11
11
|
// How many tiles the media grid renders. A rendering limit only — the text
|
|
12
12
|
// payload always carries the full page, and `total` reports the real library
|
|
@@ -65,8 +65,6 @@ function uploadTicketPayload(ticket) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function registerMediaTools(server, client, options = {}) {
|
|
68
|
-
const ui = () => appsEnabled(server, options);
|
|
69
|
-
|
|
70
68
|
// `opts.apps` is set only by kolbo-api's per-request server (see createServer
|
|
71
69
|
// in ../index.js), which makes it a TRANSPORT signal — deliberately not
|
|
72
70
|
// `appsEnabled()`, which also returns true for stdio hosts that advertise UI.
|
|
@@ -101,24 +99,27 @@ function registerMediaTools(server, client, options = {}) {
|
|
|
101
99
|
expires_in_seconds: ticket.expires_in,
|
|
102
100
|
};
|
|
103
101
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
102
|
+
// Always ship structuredContent. Kolbo Code does NOT advertise MCP Apps, so
|
|
103
|
+
// gating the grid payload on appsEnabled() sent it text only; the host then
|
|
104
|
+
// rebuilt items from the compactList text, whose field names are
|
|
105
|
+
// `filename`/`url` — not the `title`/`thumbnail` the grid renders — so every
|
|
106
|
+
// tile came out black and unlabelled. Same reasoning as listResult().
|
|
107
|
+
// upload_ui_url: top-level page for Claude iOS/Android — in-iframe
|
|
108
|
+
// <input type=file> selections are dropped by WebKit (see upload widget).
|
|
109
|
+
const uploadUiUrl = ticket.upload_ui_url
|
|
110
|
+
|| String(ticket.upload_url || '').replace(/\/upload\/?$/, '/upload-ui');
|
|
111
|
+
return uiResult(UI.upload, JSON.stringify(info, null, 2), {
|
|
112
|
+
widget: 'upload',
|
|
113
|
+
title: purpose || 'Upload media',
|
|
114
|
+
upload_url: ticket.upload_url,
|
|
115
|
+
upload_ui_url: uploadUiUrl,
|
|
116
|
+
token: ticket.token,
|
|
117
|
+
expires_at: Date.now() + (ticket.expires_in || 900) * 1000,
|
|
118
|
+
kinds: media_types && media_types.length ? media_types : undefined,
|
|
119
|
+
max_files: Math.min(Math.max(Number(max_files) || 10, 1), 20),
|
|
120
|
+
max_mb: ticket.max_file_mb || DEFAULT_MAX_FILE_MB,
|
|
121
|
+
...(project_id ? { project_id } : {}),
|
|
122
|
+
});
|
|
122
123
|
|
|
123
124
|
// Text-only host (Claude Code, Codex CLI, Cursor): no iframe to render —
|
|
124
125
|
// but these are exactly the hosts that CAN reach a filesystem, so hand
|
|
@@ -263,30 +264,33 @@ function registerMediaTools(server, client, options = {}) {
|
|
|
263
264
|
note: 'Narrow with `type`, `category`, `project_id`, `folder_id`, or `search`; get_media returns one item in full.',
|
|
264
265
|
});
|
|
265
266
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
267
|
+
// Always ship structuredContent. Kolbo Code does NOT advertise MCP Apps, so
|
|
268
|
+
// gating the grid payload on appsEnabled() sent it text only; the host then
|
|
269
|
+
// rebuilt items from the compactList text, whose field names are
|
|
270
|
+
// `filename`/`url` — not the `title`/`thumbnail` the grid renders — so every
|
|
271
|
+
// tile came out black and unlabelled. Same reasoning as listResult().
|
|
272
|
+
// The SDK envelope reports `total_items` (see sdk/controller.js listMedia);
|
|
273
|
+
// reading `total` always came back undefined, so the grid claimed the page
|
|
274
|
+
// size was the whole library. Accept either, then fall back.
|
|
275
|
+
const totalItems = pagination
|
|
276
|
+
? (pagination.total_items != null ? pagination.total_items : pagination.total)
|
|
277
|
+
: null;
|
|
278
|
+
const items = media.slice(0, GRID_CAP).map((m) => ({
|
|
279
|
+
id: m.id,
|
|
280
|
+
title: m.filename,
|
|
281
|
+
subtitle: m.media_type + (m.size ? ' · ' + Math.round(m.size / 1024) + 'KB' : ''),
|
|
282
|
+
thumbnail: m.media_type === 'image' ? m.url : (m.thumbnail_url || null),
|
|
283
|
+
media_type: m.media_type,
|
|
284
|
+
url: m.url,
|
|
285
|
+
use_hint: 'Use this media library asset in my next step:\nURL: {URL}\n(id: {ID})'
|
|
286
|
+
}));
|
|
287
|
+
return uiResult(UI.mediaGrid, text, {
|
|
288
|
+
widget: 'media-grid',
|
|
289
|
+
title: 'Media Library',
|
|
290
|
+
items,
|
|
291
|
+
total: totalItems != null ? totalItems : media.length,
|
|
292
|
+
shown: Math.min(media.length, GRID_CAP)
|
|
293
|
+
});
|
|
290
294
|
|
|
291
295
|
return {
|
|
292
296
|
content: [{
|
package/src/tools/moodboards.js
CHANGED
|
@@ -4,11 +4,10 @@
|
|
|
4
4
|
* new OPTIONAL args only. Full rules: ../index.js top-of-file and CLAUDE.md. */
|
|
5
5
|
|
|
6
6
|
const { z } = require('zod');
|
|
7
|
-
const { UI, uiResult
|
|
7
|
+
const { UI, uiResult } = require('../apps');
|
|
8
8
|
const { projectScopeReadField } = require('./_shared');
|
|
9
9
|
|
|
10
10
|
function registerMoodboardTools(server, client, options = {}) {
|
|
11
|
-
const ui = () => appsEnabled(server, options);
|
|
12
11
|
// ─── list_moodboards ───────────────────────────────────────
|
|
13
12
|
server.tool(
|
|
14
13
|
'list_moodboards',
|
|
@@ -29,25 +28,26 @@ function registerMoodboardTools(server, client, options = {}) {
|
|
|
29
28
|
count: result.count || 0
|
|
30
29
|
}, null, 2);
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
31
|
+
// Always ship structuredContent. Kolbo Code does NOT advertise MCP Apps, so
|
|
32
|
+
// gating the grid payload on appsEnabled() sent it text only; the host then
|
|
33
|
+
// rebuilt items from the compactList text, whose field names are
|
|
34
|
+
// `filename`/`url` — not the `title`/`thumbnail` the grid renders — so every
|
|
35
|
+
// tile came out black and unlabelled. Same reasoning as listResult().
|
|
36
|
+
return uiResult(UI.mediaGrid, text, {
|
|
37
|
+
widget: 'media-grid',
|
|
38
|
+
title: 'Moodboards',
|
|
39
|
+
items: moodboards.slice(0, 24).map(mb => ({
|
|
40
|
+
id: mb.id,
|
|
41
|
+
title: mb.name,
|
|
42
|
+
// API returns thumbnail_url + images[] (sdk listMoodboards) — both
|
|
43
|
+
// previous keys were wrong, so the fallback never fired either.
|
|
44
|
+
thumbnail: mb.thumbnail_url || mb.thumbnail || (Array.isArray(mb.images) ? mb.images[0] : undefined),
|
|
45
|
+
media_type: 'image',
|
|
46
|
+
use_hint: 'Apply moodboard "{TITLE}" (moodboard_id: {ID}) to my next generation.'
|
|
47
|
+
})),
|
|
48
|
+
total: result.count || moodboards.length,
|
|
49
|
+
has_more: moodboards.length > 24
|
|
50
|
+
});
|
|
51
51
|
}
|
|
52
52
|
);
|
|
53
53
|
|
package/src/tools/visual_dna.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
const { z } = require('zod');
|
|
7
7
|
const FormData = require('form-data');
|
|
8
8
|
const { resolveToBuffer: sharedResolveToBuffer, VISUAL_DNA_MAX_BYTES, projectScopeReadField, compactList } = require('./_shared');
|
|
9
|
-
const { UI, uiResult, listResult
|
|
9
|
+
const { UI, uiResult, listResult } = require('../apps');
|
|
10
10
|
|
|
11
11
|
// Reference sheets are a blocking multi-panel render; the 120s client default
|
|
12
12
|
// aborted them mid-flight while the server finished and charged anyway.
|
|
@@ -20,7 +20,6 @@ function resolveToBuffer(source, kind) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function registerVisualDnaTools(server, client, options = {}) {
|
|
23
|
-
const ui = () => appsEnabled(server, options);
|
|
24
23
|
// ─── create_visual_dna ─────────────────────────────────────
|
|
25
24
|
server.tool(
|
|
26
25
|
'create_visual_dna',
|
|
@@ -133,24 +132,25 @@ function registerVisualDnaTools(server, client, options = {}) {
|
|
|
133
132
|
note: 'Narrow with `search`, `tags`, or `collection`, or pass `page`/`limit` for the rest; get_visual_dna returns one in full.',
|
|
134
133
|
});
|
|
135
134
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
135
|
+
// Always ship structuredContent. Kolbo Code does NOT advertise MCP Apps, so
|
|
136
|
+
// gating the grid payload on appsEnabled() sent it text only; the host then
|
|
137
|
+
// rebuilt items from the compactList text, whose field names are
|
|
138
|
+
// `filename`/`url` — not the `title`/`thumbnail` the grid renders — so every
|
|
139
|
+
// tile came out black and unlabelled. Same reasoning as listResult().
|
|
140
|
+
return uiResult(UI.mediaGrid, text, {
|
|
141
|
+
widget: 'media-grid',
|
|
142
|
+
title: 'Visual DNA Profiles',
|
|
143
|
+
items: dnas.slice(0, 24).map(d => ({
|
|
144
|
+
id: d.id,
|
|
145
|
+
title: d.name,
|
|
146
|
+
subtitle: (d.dna_type || '') + (Array.isArray(d.tags) && d.tags.length ? ' · ' + d.tags.slice(0, 3).join(', ') : ''),
|
|
147
|
+
thumbnail: d.thumbnail_url || d.thumbnail,
|
|
148
|
+
media_type: 'image',
|
|
149
|
+
use_hint: 'Use Visual DNA "{TITLE}" (id: {ID}) in my next generation for character/style consistency.'
|
|
150
|
+
})),
|
|
151
|
+
total,
|
|
152
|
+
has_more: result.has_more || dnas.length > 24
|
|
153
|
+
});
|
|
154
154
|
}
|
|
155
155
|
);
|
|
156
156
|
|