@kolbo/mcp 1.106.4 → 1.107.1
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/html.js +1 -1
- package/src/apps/index.js +1 -0
- package/src/apps/theme.js +15 -9
- package/src/apps/widgets/generation.js +8 -2
- package/src/index.js +2 -0
- package/src/toolAnnotations.js +3 -2
- package/src/tools/generate.js +9 -5
- package/src/tools/morphious_styles.js +59 -0
package/package.json
CHANGED
package/src/apps/html.js
CHANGED
|
@@ -17,7 +17,7 @@ function widgetPage({ title, body, script }) {
|
|
|
17
17
|
<title>${title}</title>
|
|
18
18
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
19
19
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
20
|
-
<link href="https://fonts.googleapis.com/css2?family=
|
|
20
|
+
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet" media="print" onload="this.media='all'">
|
|
21
21
|
<style>${KOLBO_CSS}</style>
|
|
22
22
|
</head>
|
|
23
23
|
<body>
|
package/src/apps/index.js
CHANGED
|
@@ -779,6 +779,7 @@ const TOOL_WIDGETS = {
|
|
|
779
779
|
list_voices: UI.mediaGrid,
|
|
780
780
|
list_visual_dnas: UI.mediaGrid,
|
|
781
781
|
list_moodboards: UI.mediaGrid,
|
|
782
|
+
list_morphious_styles: UI.mediaGrid,
|
|
782
783
|
// NOTE: list_color_palettes' handler has always called uiResult(UI.mediaGrid, ...)
|
|
783
784
|
// (see color_palettes.js) but was missing here — hosts that prepare the widget
|
|
784
785
|
// iframe from the tool DECLARATION (claude.ai reads tools/list, not the result)
|
package/src/apps/theme.js
CHANGED
|
@@ -34,7 +34,13 @@ const KOLBO_CSS = `
|
|
|
34
34
|
--spring: cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
35
35
|
--smooth: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
36
36
|
--specular: inset 0 1px 0 rgba(255, 255, 255, 0.18);
|
|
37
|
-
|
|
37
|
+
/* "light dark" on the ROOT, never a fixed scheme. When an iframe's root
|
|
38
|
+
scheme differs from the host frame's, the browser paints an OPAQUE canvas
|
|
39
|
+
behind the page: a fixed "dark" here drew a dark square around every card
|
|
40
|
+
in hosts whose frame is light/normal (Codex). "light dark" adopts the host
|
|
41
|
+
frame's scheme, so the canvas stays transparent everywhere. The card
|
|
42
|
+
carries the real theme for its native controls below. */
|
|
43
|
+
color-scheme: light dark;
|
|
38
44
|
}
|
|
39
45
|
[data-theme="light"] {
|
|
40
46
|
--bg: #f5f5f2;
|
|
@@ -50,12 +56,13 @@ const KOLBO_CSS = `
|
|
|
50
56
|
--text-faint: rgba(10, 10, 12, 0.40);
|
|
51
57
|
--brand-soft: rgba(59, 130, 246, 0.10);
|
|
52
58
|
--specular: inset 0 1px 0 rgba(255, 255, 255, 0.65);
|
|
53
|
-
color-scheme: light;
|
|
54
59
|
}
|
|
55
60
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
56
61
|
html, body { background: transparent; }
|
|
62
|
+
.k-card { color-scheme: dark; }
|
|
63
|
+
[data-theme="light"] .k-card { color-scheme: light; }
|
|
57
64
|
body {
|
|
58
|
-
font-family: '
|
|
65
|
+
font-family: 'Poppins', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Arial, sans-serif;
|
|
59
66
|
color: var(--text);
|
|
60
67
|
font-size: 14px;
|
|
61
68
|
line-height: 1.5;
|
|
@@ -114,7 +121,6 @@ body {
|
|
|
114
121
|
.k-mention {
|
|
115
122
|
display: inline; padding: 1px 5px; border-radius: 5px;
|
|
116
123
|
background: var(--brand-soft); color: var(--brand);
|
|
117
|
-
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
|
118
124
|
font-size: 0.94em; font-weight: 500;
|
|
119
125
|
}
|
|
120
126
|
/* Single-line media caption (scene / batch prompt under the viewer) */
|
|
@@ -143,7 +149,7 @@ body {
|
|
|
143
149
|
.k-chip .k-mono-icon {
|
|
144
150
|
width: 14px; height: 14px; border-radius: 4px; background: var(--brand);
|
|
145
151
|
color: #fff; font-size: 9px; font-weight: 700; display: inline-flex;
|
|
146
|
-
align-items: center; justify-content: center; font-family:
|
|
152
|
+
align-items: center; justify-content: center; font-family: inherit;
|
|
147
153
|
}
|
|
148
154
|
.k-chip img.k-voice-thumb { width: 18px; height: 18px; border-radius: 999px; margin-left: -3px; }
|
|
149
155
|
.k-ref-thumb { width: 26px; height: 26px; border-radius: 6px; object-fit: cover; border: 1px solid var(--border-strong); }
|
|
@@ -161,7 +167,7 @@ html.k-peek-open .k-card { min-height: 460px; }
|
|
|
161
167
|
.k-peek-bar { flex: none; display: flex; align-items: center; gap: 8px; padding: 8px 8px 8px 14px; min-height: 44px; }
|
|
162
168
|
.k-peek-cap { flex: 1; min-width: 0; font-size: 12.5px; color: rgba(255,255,255,0.78);
|
|
163
169
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
164
|
-
.k-peek-count { flex: none; font-size: 11.5px; color: rgba(255,255,255,0.55); font-
|
|
170
|
+
.k-peek-count { flex: none; font-size: 11.5px; color: rgba(255,255,255,0.55); font-variant-numeric: tabular-nums; }
|
|
165
171
|
.k-peek-btn {
|
|
166
172
|
flex: none; width: 32px; height: 32px; border: 1px solid rgba(255,255,255,0.14); border-radius: 999px;
|
|
167
173
|
background: rgba(255,255,255,0.08); color: #fff; cursor: pointer; font-size: 15px;
|
|
@@ -303,7 +309,7 @@ video::-webkit-media-controls-fullscreen-button { display: none !important; }
|
|
|
303
309
|
animating k-sweep skeleton forced per-frame backdrop re-sampling on phones;
|
|
304
310
|
0.65 black over the shimmer reads identically without it */
|
|
305
311
|
border: 1px solid rgba(255, 255, 255, 0.14);
|
|
306
|
-
font-size: 11px; font-weight: 600; color: #fff;
|
|
312
|
+
font-size: 11px; font-weight: 600; color: #fff; text-transform: capitalize;
|
|
307
313
|
}
|
|
308
314
|
.k-spin {
|
|
309
315
|
width: 12px; height: 12px; border-radius: 50%;
|
|
@@ -463,7 +469,7 @@ video::-webkit-media-controls-fullscreen-button { display: none !important; }
|
|
|
463
469
|
.k-dot.on { width: 22px; opacity: 0.95; cursor: default; }
|
|
464
470
|
.k-dot.ghost { cursor: default; opacity: 0.14; }
|
|
465
471
|
.k-pager-label { font-size: 11px; color: var(--text-faint);
|
|
466
|
-
font-
|
|
472
|
+
font-variant-numeric: tabular-nums; min-width: 58px; text-align: center; }
|
|
467
473
|
|
|
468
474
|
/* ---- Audio rows ---- */
|
|
469
475
|
.k-audio-row { display: flex; align-items: center; gap: 10px; padding: 9px 10px;
|
|
@@ -533,7 +539,7 @@ video::-webkit-media-controls-fullscreen-button { display: none !important; }
|
|
|
533
539
|
.k-footer:not(:has(.k-credits:not(:empty))) { display: none; }
|
|
534
540
|
.k-footer a { color: var(--text-faint); text-decoration: none; }
|
|
535
541
|
.k-footer a:hover { color: var(--brand); }
|
|
536
|
-
.k-credits { margin-left: auto; font-
|
|
542
|
+
.k-credits { margin-left: auto; font-variant-numeric: tabular-nums; }
|
|
537
543
|
`;
|
|
538
544
|
|
|
539
545
|
/**
|
|
@@ -453,11 +453,17 @@ function moodboardChipsHTML(sc, s) {
|
|
|
453
453
|
}
|
|
454
454
|
return h;
|
|
455
455
|
}
|
|
456
|
-
|
|
456
|
+
// Labels read as words: "Image", "Enhanced", "Web search" — the first letter
|
|
457
|
+
// of the visible text (after any leading icon markup) is uppercased. Digits
|
|
458
|
+
// first ("1080p", "16:9", "7 images") are left alone.
|
|
459
|
+
function capLabel(inner) {
|
|
460
|
+
return String(inner).replace(/(^|>)(\\s*)([a-z])/, function (m, gt, sp, ch) { return gt + sp + ch.toUpperCase(); });
|
|
461
|
+
}
|
|
462
|
+
function chip(inner) { return '<span class="k-chip">' + capLabel(inner) + '</span>'; }
|
|
457
463
|
// Same chip with a hover title — used to surface the asset id behind a
|
|
458
464
|
// "2 Visual DNA" / "preset" label without spending chip width on it.
|
|
459
465
|
function chipT(inner, title) {
|
|
460
|
-
return '<span class="k-chip" title="' + esc(title) + '">' + inner + '</span>';
|
|
466
|
+
return '<span class="k-chip" title="' + esc(title) + '">' + capLabel(inner) + '</span>';
|
|
461
467
|
}
|
|
462
468
|
function iconFor(kind) {
|
|
463
469
|
switch (kind) {
|
package/src/index.js
CHANGED
|
@@ -70,6 +70,7 @@ const { registerFontTools } = require('./tools/fonts');
|
|
|
70
70
|
const { registerEditorTools } = require('./tools/editor');
|
|
71
71
|
const { registerFlowTools } = require('./tools/flow');
|
|
72
72
|
const { registerTrendTools } = require('./tools/trends');
|
|
73
|
+
const { registerMorphiousStyleTools } = require('./tools/morphious_styles');
|
|
73
74
|
const { registerMediaTools } = require('./tools/media');
|
|
74
75
|
const { registerPresetTools } = require('./tools/presets');
|
|
75
76
|
const { registerArtifactTools } = require('./tools/artifacts');
|
|
@@ -196,6 +197,7 @@ function createServer(opts = {}) {
|
|
|
196
197
|
registerEditorTools(server, client);
|
|
197
198
|
registerFlowTools(server, client);
|
|
198
199
|
registerTrendTools(server, client);
|
|
200
|
+
registerMorphiousStyleTools(server, client);
|
|
199
201
|
registerAnalyzeTools(server, client, toolOptions);
|
|
200
202
|
registerMediaTools(server, client, toolOptions);
|
|
201
203
|
registerPresetTools(server, client, toolOptions);
|
package/src/toolAnnotations.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
const READ_ONLY = [
|
|
12
12
|
'get_flow_schema', 'list_flow_sessions', 'get_flow_session', 'validate_flow_session',
|
|
13
13
|
'estimate_flow_run', 'get_flow_run', 'list_flow_runs',
|
|
14
|
+
'list_morphious_styles',
|
|
14
15
|
'list_trends', 'get_trend', 'estimate_trend_run', 'get_trend_run', 'list_trend_runs',
|
|
15
16
|
'get_download_status',
|
|
16
17
|
'get_video_editor_schema', 'list_video_editor_sessions', 'get_video_editor_session',
|
|
@@ -59,7 +60,7 @@ const PRIVATE_WRITE = [
|
|
|
59
60
|
'import_elevenlabs_voice',
|
|
60
61
|
'create_visual_dna', 'create_visual_dna_folder', 'update_visual_dna_folder',
|
|
61
62
|
'move_visual_dna_to_folder',
|
|
62
|
-
'create_moodboard',
|
|
63
|
+
'create_moodboard', 'create_morphious_style',
|
|
63
64
|
'create_color_palette', 'activate_color_palette', 'deactivate_color_palette',
|
|
64
65
|
'move_session', 'bulk_move_sessions', 'move_generations_to_session',
|
|
65
66
|
'split_session', 'undo_session_organization',
|
|
@@ -102,7 +103,7 @@ const DESTRUCTIVE_WRITE = [
|
|
|
102
103
|
|
|
103
104
|
// Deletes and whole-value replacement updates are conservatively destructive.
|
|
104
105
|
'delete_voice', 'update_visual_dna', 'delete_visual_dna', 'delete_visual_dna_folder',
|
|
105
|
-
'update_moodboard', 'delete_moodboard',
|
|
106
|
+
'update_moodboard', 'delete_moodboard', 'delete_morphious_style',
|
|
106
107
|
'update_color_palette', 'delete_color_palette',
|
|
107
108
|
'delete_media_folder', 'delete_media', 'permanently_delete_media',
|
|
108
109
|
'bulk_delete_media', 'bulk_permanently_delete_media',
|
package/src/tools/generate.js
CHANGED
|
@@ -240,7 +240,8 @@ const MORPHIOUS_GUIDE = ' MORPHIOUS / GENJUTSU (exactly ONE source video, omit d
|
|
|
240
240
|
+ 'kolbo-morphious-motion 1-30 images, 4-30s; kolbo-morphious-swap 0-30 images, 4-30s; both 480p/720p/1080p, output keeps the source shape. '
|
|
241
241
|
+ 'Lite (kolbo-morphious-lite-motion 1-10 images / kolbo-morphious-lite-swap 0-10 images) caps at 15s. '
|
|
242
242
|
+ 'kolbo-morphious-reframe: NO images/DNA, 1-300s, aspect_ratio REQUIRED (16:9, 9:16, 1:1, 4:3, 3:4, 21:9), 540p or 720p. '
|
|
243
|
-
+ 'higgsfield-genjutsu-motion-transfer / -object-swap: 1-8 images or Visual DNA, 4-30s, 480p/720p/1080p.'
|
|
243
|
+
+ 'higgsfield-genjutsu-motion-transfer / -object-swap: 1-8 images or Visual DNA, 4-30s, 480p/720p/1080p. '
|
|
244
|
+
+ 'STYLES: to restyle a whole video into an animated look (2D toon, painterly 3D, gouache, comic, clay, felt...), pick an id from list_morphious_styles and pass it as style_id with model kolbo-morphious-motion; no reference image or prompt is needed.';
|
|
244
245
|
|
|
245
246
|
function registerGenerateTools(server, client, options = {}) {
|
|
246
247
|
// Every JSON POST from these tools rehosts local file paths into the media
|
|
@@ -1579,6 +1580,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1579
1580
|
preset_id: z.string().optional().describe('Preset ID from list_presets type="video" (optional)'),
|
|
1580
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.'),
|
|
1581
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 / -lite-motion): 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.'),
|
|
1582
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.'),
|
|
1583
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".'),
|
|
1584
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.'),
|
|
@@ -1592,7 +1594,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1592
1594
|
project_id: projectIdField,
|
|
1593
1595
|
session_id: sessionIdField
|
|
1594
1596
|
},
|
|
1595
|
-
async ({ prompt = '', model, reference_images, reference_videos, reference_audio_urls, audio_url, files, duration, aspect_ratio, motion, preset_id, enhance_prompt = false, visual_dna_ids, resolution, draft, sound_enabled, keyframes, multi_shots, multi_shot_count, session_name, project_id, session_id }) => {
|
|
1597
|
+
async ({ prompt = '', model, reference_images, reference_videos, reference_audio_urls, audio_url, files, duration, aspect_ratio, motion, preset_id, enhance_prompt = false, visual_dna_ids, style_id, resolution, draft, sound_enabled, keyframes, multi_shots, multi_shot_count, session_name, project_id, session_id }) => {
|
|
1596
1598
|
validateVideoPrompt({ prompt, duration, aspect_ratio, multi_shots, multi_shot_count });
|
|
1597
1599
|
if (draft === true) resolution = '480p-draft';
|
|
1598
1600
|
else if (draft === false && resolution?.endsWith('-draft')) resolution = resolution.slice(0, -6);
|
|
@@ -1638,7 +1640,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1638
1640
|
reference_images: some(urlsOf('image')),
|
|
1639
1641
|
reference_videos: some(urlsOf('video')),
|
|
1640
1642
|
reference_audio_urls: some(urlsOf('audio')),
|
|
1641
|
-
duration, aspect_ratio, motion, preset_id, enhance_prompt, visual_dna_ids,
|
|
1643
|
+
duration, aspect_ratio, motion, preset_id, enhance_prompt, visual_dna_ids, style_id,
|
|
1642
1644
|
resolution, draft, sound_enabled, keyframes, multi_shots, multi_shot_count,
|
|
1643
1645
|
session_name, project_id, session_id
|
|
1644
1646
|
};
|
|
@@ -1994,6 +1996,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1994
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'),
|
|
1995
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.'),
|
|
1996
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 / -lite-motion): 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.'),
|
|
1997
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.'),
|
|
1998
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".'),
|
|
1999
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.'),
|
|
@@ -2029,7 +2032,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
2029
2032
|
project_id: projectIdField,
|
|
2030
2033
|
session_id: sessionIdField
|
|
2031
2034
|
},
|
|
2032
|
-
async ({ source_video, prompt, model, aspect_ratio, duration, enhance_prompt = false, visual_dna_ids, resolution, draft, sound_enabled, reference_images, reference_videos, elements, preset, source_language, translation_language, srt_content, srt_file_url, vocabulary, customization, enhancement_model, target_fps, slowdown_factor, output_format, project_id, session_id }) => {
|
|
2035
|
+
async ({ source_video, prompt, model, aspect_ratio, duration, enhance_prompt = false, visual_dna_ids, style_id, resolution, draft, sound_enabled, reference_images, reference_videos, elements, preset, source_language, translation_language, srt_content, srt_file_url, vocabulary, customization, enhancement_model, target_fps, slowdown_factor, output_format, project_id, session_id }) => {
|
|
2033
2036
|
if (draft === true) resolution = '480p-draft';
|
|
2034
2037
|
else if (draft === false && resolution?.endsWith('-draft')) resolution = resolution.slice(0, -6);
|
|
2035
2038
|
model = await canonicalModelId(client, model, 'video_to_video'); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
@@ -2040,7 +2043,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
2040
2043
|
let startResponse;
|
|
2041
2044
|
if (isUrl) {
|
|
2042
2045
|
startResponse = await client.post('/v1/generate/video-from-video', {
|
|
2043
|
-
video_url: source_video, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, resolution, draft, sound_enabled,
|
|
2046
|
+
video_url: source_video, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, style_id, resolution, draft, sound_enabled,
|
|
2044
2047
|
reference_images, reference_videos, elements, preset, source_language, translation_language,
|
|
2045
2048
|
srt_content, srt_file_url, vocabulary, customization,
|
|
2046
2049
|
enhancement_model, target_fps, slowdown_factor, output_format,
|
|
@@ -2063,6 +2066,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
2063
2066
|
if (duration !== undefined) form.append('duration', String(duration));
|
|
2064
2067
|
if (enhance_prompt !== undefined) form.append('enhance_prompt', String(enhance_prompt));
|
|
2065
2068
|
if (visual_dna_ids) form.append('visual_dna_ids', JSON.stringify(visual_dna_ids));
|
|
2069
|
+
if (style_id) form.append('style_id', style_id);
|
|
2066
2070
|
if (resolution) form.append('resolution', resolution);
|
|
2067
2071
|
if (draft !== undefined) form.append('draft', String(draft));
|
|
2068
2072
|
if (sound_enabled !== undefined) form.append('sound_enabled', String(sound_enabled));
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/* ⛔ BACKWARD COMPATIBILITY: Tool names and arg names below are a PUBLIC
|
|
2
|
+
* CONTRACT. Never rename, remove, or break an existing tool/arg — old cached
|
|
3
|
+
* `npx @kolbo/mcp` installs in the wild will break silently. Add new tools or
|
|
4
|
+
* new OPTIONAL args only. Full rules: ../index.js top-of-file and CLAUDE.md. */
|
|
5
|
+
|
|
6
|
+
// Morphious Styles: restyle a whole video into a look (Kolbo catalog or the user's own custom style).
|
|
7
|
+
// Apply one by passing its id as `style_id` to generate_video_from_video / generate_elements with
|
|
8
|
+
// model kolbo-morphious-motion. Contract: kolbo-api src/modules/morphiousStyles.
|
|
9
|
+
|
|
10
|
+
const { z } = require('zod');
|
|
11
|
+
const { UI, uiResult } = require('../apps');
|
|
12
|
+
|
|
13
|
+
const unwrap = r => (r && r.status === true && r.data !== undefined ? r.data : r);
|
|
14
|
+
const text = value => ({ content: [{ type: 'text', text: JSON.stringify(value, null, 2) }] });
|
|
15
|
+
|
|
16
|
+
function registerMorphiousStyleTools(server, client) {
|
|
17
|
+
server.tool(
|
|
18
|
+
'list_morphious_styles',
|
|
19
|
+
'List Morphious Styles: looks that restyle a WHOLE video (2D toon, painterly 3D, gouache, comic, clay, felt, yarn...). Returns the Kolbo catalog first, then the user\'s own custom styles (custom: true). To apply one, pass its id as style_id to generate_video_from_video with model "kolbo-morphious-motion" and the source video; no reference image or prompt is needed.',
|
|
20
|
+
{},
|
|
21
|
+
async () => {
|
|
22
|
+
const styles = unwrap(await client.get('/v1/morphious-styles')).styles || [];
|
|
23
|
+
return uiResult(UI.mediaGrid, JSON.stringify({ styles, count: styles.length }, null, 2), {
|
|
24
|
+
widget: 'media-grid',
|
|
25
|
+
title: 'Morphious Styles',
|
|
26
|
+
items: styles.map(s => ({
|
|
27
|
+
id: s.id,
|
|
28
|
+
title: s.name,
|
|
29
|
+
subtitle: s.custom ? 'Custom' : undefined,
|
|
30
|
+
thumbnail: s.thumb || s.cover,
|
|
31
|
+
url: s.preview || s.motion || s.cover,
|
|
32
|
+
media_type: s.preview ? 'video' : 'image',
|
|
33
|
+
use_hint: 'Restyle my video in the "{TITLE}" style (style_id: {ID}).',
|
|
34
|
+
})),
|
|
35
|
+
total: styles.length,
|
|
36
|
+
has_more: false,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
server.tool(
|
|
42
|
+
'create_morphious_style',
|
|
43
|
+
'Create a custom Morphious Style from 1-8 image URLs that show the look (an illustration style, a painting technique, a 3D render style). The images define the LOOK only, never the subject. Returns the style id to pass as style_id. Upload local files first with upload_media / create_upload_ticket.',
|
|
44
|
+
{
|
|
45
|
+
name: z.string().min(1).max(60).describe('Style name shown to the user, e.g. "Ink Wash".'),
|
|
46
|
+
image_urls: z.array(z.string().url()).min(1).max(8).describe('1-8 https image URLs that show the look.'),
|
|
47
|
+
},
|
|
48
|
+
async ({ name, image_urls }) => text(unwrap(await client.post('/v1/morphious-styles/custom', { name, image_urls })))
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
server.tool(
|
|
52
|
+
'delete_morphious_style',
|
|
53
|
+
'Delete one of the user\'s own custom Morphious Styles. Catalog styles cannot be deleted. Past generations keep their result.',
|
|
54
|
+
{ style_id: z.string().min(1).max(64).describe('Custom style id from list_morphious_styles (custom: true).') },
|
|
55
|
+
async ({ style_id }) => text(unwrap(await client.delete(`/v1/morphious-styles/custom/${encodeURIComponent(style_id)}`)))
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = { registerMorphiousStyleTools };
|