@kolbo/mcp 1.107.1 → 1.108.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/README.md +1 -0
- package/package.json +1 -1
- package/src/toolAnnotations.js +3 -0
- package/src/tools/generate.js +2 -2
- package/src/tools/projects.js +51 -0
- package/src/tools/trends.js +14 -7
package/README.md
CHANGED
|
@@ -317,6 +317,7 @@ Requires DaVinci Resolve Studio with the Kolbo plugin open (Workspace → Workfl
|
|
|
317
317
|
| `generate_character_sheet` | Generate a multi-angle character sheet from reference images (credits) → pass URL to create_visual_dna or update_visual_dna |
|
|
318
318
|
| `list_visual_dna_folders` / `create_visual_dna_folder` / `update_visual_dna_folder` / `delete_visual_dna_folder` / `move_visual_dna_to_folder` | Organize Visual DNA characters into user folders (create/rename/recolor/delete + move DNAs in/out) |
|
|
319
319
|
| `create_project` / `update_project` / `archive_project` / `unarchive_project` | Project lifecycle (create/rename/describe/archive; deletion stays in-app) |
|
|
320
|
+
| `transfer_project` / `list_project_transfers` / `respond_project_transfer` | Hand a project you own to another Kolbo account (they accept; same ids; cast assets copied; payer history unchanged) |
|
|
320
321
|
| `list_skills` / `create_skill` / `update_skill` / `delete_skill` | Skills — reusable named personas for the chat tool; `description` is the system instruction |
|
|
321
322
|
| `list_agents` / `create_agent` / `update_agent` / `delete_agent` | Same four tools under their original names. Still supported and unchanged; new integrations should use the `*_skill` names |
|
|
322
323
|
| `create_video_editor_session` / `export_video_editor_session` | Build an editable timeline from Kolbo-hosted clips, audio and text, then export it to MP4. No new media is generated; identical snapshots reuse the same export job |
|
package/package.json
CHANGED
package/src/toolAnnotations.js
CHANGED
|
@@ -26,6 +26,7 @@ const READ_ONLY = [
|
|
|
26
26
|
'list_presets', 'list_cinematic_presets',
|
|
27
27
|
'list_projects', 'get_project', 'list_sessions', 'list_project_context', 'get_project_profile',
|
|
28
28
|
'list_project_assets',
|
|
29
|
+
'list_project_transfers',
|
|
29
30
|
'list_session_generations',
|
|
30
31
|
'list_agents', 'list_skills', 'list_docs', 'get_doc',
|
|
31
32
|
'get_review_storage_usage', 'list_review_assets', 'get_review_asset',
|
|
@@ -111,6 +112,8 @@ const DESTRUCTIVE_WRITE = [
|
|
|
111
112
|
'delete_session',
|
|
112
113
|
'delete_project_context', 'regenerate_project_profile',
|
|
113
114
|
'update_project_asset',
|
|
115
|
+
// Ownership hand-off: the sender loses the project (and maybe all access) on accept.
|
|
116
|
+
'transfer_project', 'respond_project_transfer',
|
|
114
117
|
'update_agent', 'delete_agent', 'update_skill', 'delete_skill', 'update_doc', 'delete_doc',
|
|
115
118
|
'delete_review_asset', 'delete_review_collection',
|
|
116
119
|
'edit_review_comment', 'delete_review_comment',
|
package/src/tools/generate.js
CHANGED
|
@@ -1580,7 +1580,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1580
1580
|
preset_id: z.string().optional().describe('Preset ID from list_presets type="video" (optional)'),
|
|
1581
1581
|
enhance_prompt: z.boolean().optional().describe('Enhance the prompt. Default: false — only pass true if the user explicitly asks to enhance/improve the prompt.'),
|
|
1582
1582
|
visual_dna_ids: z.array(z.string()).optional().describe('Array of Visual DNA profile IDs to apply for character/style consistency across outputs. **Cap: pass at most `max_visual_dna` IDs from list_models for the chosen model.**'),
|
|
1583
|
-
style_id: z.string().optional().describe('Morphious Styles only (kolbo-morphious-motion
|
|
1583
|
+
style_id: z.string().optional().describe('Morphious Styles only (kolbo-morphious-motion or kolbo-morphious-swap; Standard only, the Lite modes refuse a style): id of a look from list_morphious_styles (Kolbo catalog or a custom style the user made). Restyles the WHOLE video in that look; the style counts as reference image 1, so no reference_images are needed. Prompt optional.'),
|
|
1584
1584
|
draft: z.boolean().optional().describe('Special Draft mode. True forces 480p Draft even if resolution says 1080p; false selects regular video. Only supported when list_models advertises draft capabilities. For Seedance 2.5 editing use model seedance-2-5-video-to-video with generate_video_from_video; the source duration and aspect ratio are preserved. Uses ordinary credits; finalize through draft_quote then draft_enhance.'),
|
|
1585
1585
|
resolution: z.string().optional().describe('Video resolution or named tier: read supported_resolutions from list_models. For Seedance 2.5 Draft use resolution="480p-draft" on the matching public model: seedance-2-5 for creation, seedance-2-5-video-to-video for editing. Plain "480p" generates regular video, NOT Draft. Finalize an actual draft with edit_video operation="draft_quote" then "draft_enhance".'),
|
|
1586
1586
|
sound_enabled: z.boolean().optional().describe('Enable (`true`) or disable (`false`) AI-generated synced audio on the output video. Honored by `sound_generation_type: "native"` models (Kling O3/V3, Veo 3.1, PixVerse V6). Omit to use `sound_enabled_by_default`. Enabling sound may apply `sound_credit_multiplier` to cost.'),
|
|
@@ -1996,7 +1996,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1996
1996
|
duration: z.number().optional().describe('Output duration in seconds. Must be in `supported_durations` from list_models, OR within `min_output_duration`-`max_output_duration`. Default: matches source'),
|
|
1997
1997
|
enhance_prompt: z.boolean().optional().describe('Enhance the prompt. Default: false — only pass true if the user explicitly asks to enhance/improve the prompt.'),
|
|
1998
1998
|
visual_dna_ids: z.array(z.string()).optional().describe('Array of Visual DNA profile IDs to apply for character/style consistency. **Cap: pass at most `max_visual_dna` IDs from list_models for the chosen model; if `supports_visual_dna: false`, DNA is silently ignored.**'),
|
|
1999
|
-
style_id: z.string().optional().describe('Morphious Styles only (kolbo-morphious-motion
|
|
1999
|
+
style_id: z.string().optional().describe('Morphious Styles only (kolbo-morphious-motion or kolbo-morphious-swap; Standard only, the Lite modes refuse a style): id of a look from list_morphious_styles (Kolbo catalog or a custom style the user made). Restyles the WHOLE video in that look; the style counts as reference image 1, so no reference_images are needed. Prompt optional.'),
|
|
2000
2000
|
draft: z.boolean().optional().describe('Special Draft mode. True forces 480p Draft even if resolution says 1080p; false selects regular video. Only supported when list_models advertises draft capabilities. For Seedance 2.5 editing use model seedance-2-5-video-to-video with generate_video_from_video; the source duration and aspect ratio are preserved. Uses ordinary credits; finalize through draft_quote then draft_enhance.'),
|
|
2001
2001
|
resolution: z.string().optional().describe('Video resolution or named tier: read supported_resolutions from list_models. For Seedance 2.5 Draft use resolution="480p-draft" on the matching public model: seedance-2-5 for creation, seedance-2-5-video-to-video for editing. Plain "480p" generates regular video, NOT Draft. Finalize an actual draft with edit_video operation="draft_quote" then "draft_enhance".'),
|
|
2002
2002
|
reference_images: z.array(z.string()).optional().describe('Array of reference image URLs for models that support additional image inputs. **Cap: pass at most `max_images` URLs from list_models — if `max_images === 0` the model does not accept image refs.** Examples: character reference images for Kling O1/O3, style reference for Aleph/gen4_aleph, character image for WAN VACE video-edit.'),
|
package/src/tools/projects.js
CHANGED
|
@@ -315,6 +315,57 @@ function registerProjectTools(server, client) {
|
|
|
315
315
|
}
|
|
316
316
|
);
|
|
317
317
|
|
|
318
|
+
// ─── project transfer (hand a project to another account) ──
|
|
319
|
+
server.tool(
|
|
320
|
+
'transfer_project',
|
|
321
|
+
'Ask another Kolbo account (by email) to take over a project you OWN. Nothing moves until they accept (in the app, or via `respond_project_transfer`) within 7 days. On accept EVERYTHING moves to them with the same ids — sessions, generations, media, chats, docs, agents; your Visual DNA / moodboards / palette in the project cast are COPIED to them (you keep yours). Credit history keeps you as the payer. Set `keep_sender_as_editor` false to lose access entirely (default: you stay as an editor). Team/organization projects cannot be transferred. ALWAYS confirm the recipient email and the keep-editor choice with the user before calling.',
|
|
322
|
+
{
|
|
323
|
+
project_id: z.string().describe('Project ObjectId you own (from list_projects).'),
|
|
324
|
+
email: z.string().describe('Email of the EXISTING Kolbo account that should become the owner.'),
|
|
325
|
+
keep_sender_as_editor: z.boolean().optional().describe('Stay on the project as an editor after the transfer. Default: true.'),
|
|
326
|
+
},
|
|
327
|
+
async ({ project_id, email, keep_sender_as_editor }) => {
|
|
328
|
+
const result = await client.post(`/v1/projects/${encodeURIComponent(project_id)}/transfer`, {
|
|
329
|
+
email,
|
|
330
|
+
...(keep_sender_as_editor === undefined ? {} : { keep_sender_as_editor }),
|
|
331
|
+
});
|
|
332
|
+
return { content: [{ type: 'text', text: JSON.stringify(result.transfer, null, 2) }] };
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
server.tool(
|
|
336
|
+
'list_project_transfers',
|
|
337
|
+
'List project transfer requests. `direction: incoming` (default) = projects other people want to give you; `outgoing` = ones you sent. Defaults to pending only. Use the `id` with `respond_project_transfer`.',
|
|
338
|
+
{
|
|
339
|
+
direction: z.enum(['incoming', 'outgoing']).optional().describe('Default: incoming.'),
|
|
340
|
+
status: z.enum(['pending', 'accepted', 'declined', 'cancelled', 'expired', 'failed']).optional().describe('Default: pending.'),
|
|
341
|
+
},
|
|
342
|
+
async ({ direction, status }) => {
|
|
343
|
+
const params = new URLSearchParams();
|
|
344
|
+
if (direction) params.set('direction', direction);
|
|
345
|
+
if (status) params.set('status', status);
|
|
346
|
+
const qs = params.toString();
|
|
347
|
+
const result = await client.get(`/v1/project-transfers${qs ? '?' + qs : ''}`);
|
|
348
|
+
return { content: [{ type: 'text', text: JSON.stringify(result.transfers || [], null, 2) }] };
|
|
349
|
+
}
|
|
350
|
+
);
|
|
351
|
+
server.tool(
|
|
352
|
+
'respond_project_transfer',
|
|
353
|
+
'Act on a project transfer request. `accept` / `decline` = you are the recipient; `cancel` = you sent it. Accepting makes you the owner of the whole project immediately. If the project still has generations running, accept returns TRANSFER_BLOCKED_IN_FLIGHT — wait and retry. Confirm with the user before accepting.',
|
|
354
|
+
{
|
|
355
|
+
transfer_id: z.string().describe('Transfer id from list_project_transfers.'),
|
|
356
|
+
action: z.enum(['accept', 'decline', 'cancel']),
|
|
357
|
+
},
|
|
358
|
+
async ({ transfer_id, action }) => {
|
|
359
|
+
const id = encodeURIComponent(transfer_id);
|
|
360
|
+
// Explicit paths (not `/${action}`) so check-parity can see each route.
|
|
361
|
+
let result;
|
|
362
|
+
if (action === 'cancel') result = await client.delete(`/v1/project-transfers/${id}`);
|
|
363
|
+
else if (action === 'accept') result = await client.post(`/v1/project-transfers/${id}/accept`, {});
|
|
364
|
+
else result = await client.post(`/v1/project-transfers/${id}/decline`, {});
|
|
365
|
+
return { content: [{ type: 'text', text: JSON.stringify(result.transfer, null, 2) }] };
|
|
366
|
+
}
|
|
367
|
+
);
|
|
368
|
+
|
|
318
369
|
// ─── list_sessions ─────────────────────────────────────────
|
|
319
370
|
server.tool(
|
|
320
371
|
'list_sessions',
|
package/src/tools/trends.js
CHANGED
|
@@ -14,8 +14,12 @@ const { UI, uiResult } = require('../apps');
|
|
|
14
14
|
|
|
15
15
|
const slugField = z.string().min(1).max(200).regex(/^[\w-]+(\/[\w-]+)?$/)
|
|
16
16
|
.describe('Trend slug from list_trends, e.g. "vanish" or "motion/kolbo-urban-dance-glide".');
|
|
17
|
-
const inputsField = z.record(z.string(), z.string().max(2048))
|
|
18
|
-
.describe('Values for the trend\'s declared inputs, keyed by input `key` from get_trend. Media inputs take an https URL (upload local files with upload_media / create_upload_ticket first). Text inputs take plain text.');
|
|
17
|
+
const inputsField = z.record(z.string(), z.union([z.string().max(2048), z.array(z.string().max(2048)).min(1).max(3)]))
|
|
18
|
+
.describe('Values for the trend\'s declared inputs, keyed by input `key` from get_trend. Media inputs take an https URL (upload local files with upload_media / create_upload_ticket first); a photo input with max_images > 1 also takes an ARRAY of up to that many photos of the same subject (more angles = better likeness). Text inputs take plain text.');
|
|
19
|
+
const dnaField = z.record(z.string(), z.string().regex(/^[a-f0-9]{24}$/i)).optional()
|
|
20
|
+
.describe('Use a saved Visual DNA for a photo input: { input key: visual DNA id } (ids from list_visual_dnas). The DNA\'s own character sheet and photos are used for that input, so leave that key out of inputs.');
|
|
21
|
+
const previewField = z.boolean().optional()
|
|
22
|
+
.describe('Run a short preview (~4 s cut of the trend, priced at its own length) instead of the full video. Only trends whose get_trend shows `preview`.');
|
|
19
23
|
const resolutionField = z.enum(['480p', '720p', '1080p']).optional()
|
|
20
24
|
.describe('Render resolution for video trends (see resolutions in get_trend). Default: the trend\'s own. Higher costs more; quote it with estimate_trend_run.');
|
|
21
25
|
const projectField = z.string().min(1).max(128).optional()
|
|
@@ -95,7 +99,8 @@ function registerTrendTools(server, client) {
|
|
|
95
99
|
const summary = trends.map(t => ({
|
|
96
100
|
slug: t.slug, name: t.name, description: t.description, family: t.family, output_type: t.output_type,
|
|
97
101
|
estimated_credits: t.estimated_credits, free_trial: t.free_trial, badges: t.badges,
|
|
98
|
-
inputs: (t.inputs || []).map(i => ({ key: i.key, kind: i.kind, role: i.role, label: i.label, required: i.required })),
|
|
102
|
+
inputs: (t.inputs || []).map(i => ({ key: i.key, kind: i.kind, role: i.role, label: i.label, required: i.required, ...(i.max_images && { max_images: i.max_images }) })),
|
|
103
|
+
...(t.preview && { preview: t.preview }),
|
|
99
104
|
}));
|
|
100
105
|
return uiResult(UI.mediaGrid, JSON.stringify({ trends: summary, count: summary.length, free_trials_left: baseData.free_trials_left ?? null }, null, 2), {
|
|
101
106
|
widget: 'media-grid',
|
|
@@ -117,8 +122,8 @@ function registerTrendTools(server, client) {
|
|
|
117
122
|
server.tool(
|
|
118
123
|
'estimate_trend_run',
|
|
119
124
|
'Quote a trend run before spending: returns total_credits for these exact inputs (video trends are priced by the source clip length). Free — no credits spent.',
|
|
120
|
-
{ slug: slugField, inputs: inputsField, project_id: projectField, resolution: resolutionField },
|
|
121
|
-
async ({ slug, inputs, project_id, resolution }) => text(unwrap(await client.post(`/v1/trends/${slugPath(slug)}/estimate`, { inputs, ...(project_id && { project_id }), ...(resolution && { resolution }) })))
|
|
125
|
+
{ slug: slugField, inputs: inputsField, dna: dnaField, preview: previewField, project_id: projectField, resolution: resolutionField },
|
|
126
|
+
async ({ slug, inputs, dna, preview, project_id, resolution }) => text(unwrap(await client.post(`/v1/trends/${slugPath(slug)}/estimate`, { inputs, ...(dna && { dna }), ...(preview && { preview: true }), ...(project_id && { project_id }), ...(resolution && { resolution }) })))
|
|
122
127
|
);
|
|
123
128
|
|
|
124
129
|
server.tool(
|
|
@@ -127,6 +132,8 @@ function registerTrendTools(server, client) {
|
|
|
127
132
|
{
|
|
128
133
|
slug: slugField,
|
|
129
134
|
inputs: inputsField,
|
|
135
|
+
dna: dnaField,
|
|
136
|
+
preview: previewField,
|
|
130
137
|
project_id: projectField,
|
|
131
138
|
resolution: resolutionField,
|
|
132
139
|
max_credits: z.number().finite().min(0).optional().describe('Credit ceiling for this run. Default: the trend\'s own ceiling.'),
|
|
@@ -135,8 +142,8 @@ function registerTrendTools(server, client) {
|
|
|
135
142
|
wait: z.boolean().optional().describe('Wait for the result. Default true.'),
|
|
136
143
|
wait_seconds: z.number().int().min(0).max(170).optional().describe('How long to wait before returning. Default 150.'),
|
|
137
144
|
},
|
|
138
|
-
async ({ slug, inputs, project_id, resolution, max_credits, free_trial, idempotency_key, wait, wait_seconds }) => {
|
|
139
|
-
const body = { inputs, idempotency_key: idempotency_key || randomUUID(), ...(project_id && { project_id }), ...(resolution && { resolution }), ...(max_credits !== undefined && { max_credits }), ...(free_trial && { free_trial: true }) };
|
|
145
|
+
async ({ slug, inputs, dna, preview, project_id, resolution, max_credits, free_trial, idempotency_key, wait, wait_seconds }) => {
|
|
146
|
+
const body = { inputs, ...(dna && { dna }), ...(preview && { preview: true }), idempotency_key: idempotency_key || randomUUID(), ...(project_id && { project_id }), ...(resolution && { resolution }), ...(max_credits !== undefined && { max_credits }), ...(free_trial && { free_trial: true }) };
|
|
140
147
|
const started = unwrap(await client.post(`/v1/trends/${slugPath(slug)}/runs`, body));
|
|
141
148
|
started.idempotency_key = body.idempotency_key;
|
|
142
149
|
if (wait === false || TERMINAL.has(started.status)) return runResult(started);
|