@kolbo/mcp 1.78.0 → 1.79.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolbo/mcp",
3
- "version": "1.78.0",
3
+ "version": "1.79.1",
4
4
  "description": "Kolbo AI MCP Server - Generate images, videos, music, speech, and sound effects from Claude Code",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  # AUTO-GENERATED — do not edit
2
2
 
3
- This tree is mirrored from kolbo-code@b950100, the single source of truth.
3
+ This tree is mirrored from kolbo-code@a0342e4, the single source of truth.
4
4
  Canonical source: packages/opencode/skills/kolbo/
5
5
  Distribution: .github/workflows/sync-skill-to-plugin.yml
6
6
 
package/skill/SKILL.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- version: 0.8.4
2
+ version: 0.9.0
3
3
  name: kolbo
4
4
  description: |
5
5
  Generate, edit, analyze, and direct creative media through Kolbo AI: images,
@@ -57,6 +57,7 @@ For multi-scene / batch work this pairs with `generate_creative_director` (see b
57
57
 
58
58
  | If the user wants to… | Read first |
59
59
  |---|---|
60
+ | Make a **film / ad / scene / episode / campaign / any video with multiple or recurring characters** — read BEFORE planning a single shot | `references/workflows/production-planning.md` |
60
61
  | Direct, develop, audit, or continue a **film / episode / connected scene / complex performance** with continuity, acting, dialogue, music, blocking, or physics | `references/workflows/filmmaking.md` |
61
62
  | Generate a **Seedance 2.5** video | `references/models/seedance25.md` + Locked Intro in `references/models/seedance.md`. For narrative/continuity also load `references/workflows/filmmaking.md` — but compile the prompt as Locked Intro, NOT the SCENE CONTEXT / OPTICS / ACTION pack |
62
63
  | Generate a **Seedance 2 / 2.0** video **or Elements** (`generate_elements`) | `references/models/seedance.md` — same Locked Intro. Elements is NOT a different prompt language |
@@ -70,6 +71,7 @@ For multi-scene / batch work this pairs with `generate_creative_director` (see b
70
71
  | Build a **dashboard / data viz / interactive widget / mini-game / UI mockup** | `references/models/visual-code.md` |
71
72
  | Generate with **any other model** (Flux, Kling, Sora, Hailuo, ElevenLabs, DeepDub, …) — also covers universal prompt-engineering basics | `references/models/prompt-copilot.md` |
72
73
  | Build a **UGC ad / TV spot / branded video / unboxing / product review / virtual try-on** | `references/workflows/marketing-studio.md` |
74
+ | Write a **complex multi-element still**, an **edit that must not drift** (identity / product / scene lock), or a **reusable prompt template** | `references/workflows/prompt-structure.md` |
73
75
  | Make anything look **shot on a phone** — UGC, selfie, candid, "authentic", a product photo that must not look like an ad (image OR video) | `references/workflows/ugc-smartphone.md` |
74
76
  | Make a **YouTube / Shorts / Reels thumbnail** or video cover | `references/workflows/thumbnails.md` |
75
77
  | Compose a **DTC ad image** (brand kit + ad format + avatar + product + reference media) | `references/workflows/dtc-ads.md` |
@@ -94,14 +96,14 @@ Each `references/models/*.md` mirrors the matching skill prompt in `kolbo-api/sr
94
96
  | `generate_image` | Single image from a text prompt. Supports Visual DNA, moodboards, image presets, reference images, web-search grounding. When a preset is requested, resolve it with `list_presets({ type: "image" })` and pass its exact id as `preset_id`. |
95
97
  | `generate_image_edit` | Edit/transform an existing image. Pass `source_images` + edit prompt. Image-editing presets are supported through `preset_id` from `list_presets({ type: "image_edit" })`. |
96
98
  | `generate_creative_director` | **2–8 related images or videos as one coherent set.** Use INSTEAD of multiple `generate_image` calls for any related multi-output. |
97
- | `generate_video` | Text-to-video. Does **not** support Visual DNA — use `generate_elements` for character-consistent video. |
99
+ | `generate_video` | Text-to-video. Accepts `visual_dna_ids` and `sound_enabled`; `generate_elements` is still the primary reference-driven route for a DNA-anchored film. |
98
100
  | `generate_video_from_image` | Animate a still. Prompt describes motion, not subject. |
99
101
  | `generate_video_from_video` | Restyle/transform an existing video. Keeps original motion. |
100
102
  | `generate_elements` | Reference-driven video. **Primary route for DNA → video.** Prompt = Seedance Locked Intro (`Total` + `[GLOBAL LOOK]` / `[CAST]` / `[LOCATION]` + `SHOT N`). Every DNA in `visual_dna_ids` must also be `@Name` in that prompt. |
101
103
  | `generate_first_last_frame` | Keyframe interpolation between two frames. |
102
- | `generate_lipsync` | Lipsync audio to an image or video face. |
104
+ | `generate_lipsync` | Lipsync an existing waveform onto a face. **Not the route for dialogue in a film you are generating** — write the line in the Seedance prompt instead. |
103
105
  | `generate_music` | Music generation (Suno + variants). |
104
- | `generate_speech` | TTS. Use `list_voices` to pick a voice. |
106
+ | `generate_speech` | TTS for narration, voiceover and standalone audio. **NOT for scene dialogue** Seedance 2/2.5 performs quoted lines itself. |
105
107
  | `generate_sound` | Sound effects. |
106
108
  | `generate_3d` | 3D models from text / single image / multi-view. Returns GLB/FBX/OBJ/USDZ. |
107
109
  | `separate_audio_stems` | Split a soundtrack into Dialogue / Music / Effects / without-dialogue (M&E). The route for removing or isolating speech, instrumental beds, and stems for dubbing. 5cr, inline. See `workflows/audio-stems.md`. |
@@ -154,12 +156,20 @@ first, so the model renders *different words than the ones the user wrote*.
154
156
  user never asked for, and their own wording never reached the model.
155
157
  - The default is `false` in every generation tool. Leave the argument out.
156
158
 
159
+ ## ⚠️ Assets Before Shots (HARD RULE)
160
+
161
+ For any film / ad / scene / episode / campaign the order is **Map → Create → Confirm → Shoot**: inventory every character, location and prop the script needs, build each as a Visual DNA (sheet presets), get the user's explicit approval on the asset set, and only then generate video. A shot fired against an unapproved cast is waste, not a draft.
162
+
163
+ Scene dialogue is **never** `generate_speech` or `generate_lipsync`. Seedance 2 / 2.5 performs quoted lines written into the shot beat itself — English only. Full flow: `references/workflows/production-planning.md`.
164
+
157
165
  ## ⚠️ Seedance / Elements prompt contract (HARD RULE)
158
166
 
159
167
  `generate_elements`, Seedance 2, and Seedance 2.5 share **one** compile shape — the Locked Intro in `references/models/seedance.md`:
160
168
 
161
169
  `Total: Xs / N shots / AR` → `[GLOBAL LOOK – LOCKED, APPLIES TO EVERY SHOT]` → `[CAST – IDENTICAL IN EVERY SHOT]` (each person is `@DNAName`) → `[LOCATION]` → `SHOT N — 0:00–0:02 — …`
162
170
 
171
+ Write the beats at FULL DEPTH. The cap is 15,000 characters on Seedance 2.5 (10,000 on 2.0) — a 30s / 8+ shot compile should land around 4k–9k, and every beat carries its own camera move, a performance task for the speaker AND the listeners, prop/hand state, and the sound in that beat. A one-line shot beat is under-written; the structure alone is not the craft. Read `references/models/seedance25.md` before compiling.
172
+
163
173
  Do **not** default Elements to `SCENE CONTEXT` / `OPTICS` / `ACTION` / `ACTIVE REFERENCES` department packs (those live in filmmaking audit/contracts for other models). Do not load `seedance-2-prompting` SCENE CONTEXT as the Elements format.
164
174
 
165
175
  ## ⚠️ If the User Names a Tool, USE THAT TOOL (HARD RULE)
package/skill/VERSION CHANGED
@@ -1 +1 @@
1
- 0.8.4
1
+ 0.9.0
@@ -69,9 +69,11 @@ Keep identity, world, period, audio ownership, safety, and editorial objective f
69
69
 
70
70
  ## 4. Select the audio lane
71
71
 
72
+ The generation family from section 2 must already be known — see section 6. A model that bakes audio in decides how the lane executes, and sometimes that it is not a separate lane at all.
73
+
72
74
  | Lane | Use when | Required truth |
73
75
  |---|---|---|
74
- | Dialogue | Actors speak exact lines | speaker, verbatim text, timing, voice identity, listener behavior, silence ownership |
76
+ | Dialogue | Actors speak exact lines | speaker, verbatim text, timing, voice identity, listener behavior, silence ownership. **On Seedance 2 / 2.5 this lane executes IN-PROMPT** — quoted lines written into the shot beat, performed natively; never a TTS or lip-sync tool |
75
77
  | Exact-song lip-sync | A supplied waveform must own the mouth | source asset, performer ownership, exact lyrics/phonetics, breath seams, non-performer mouth behavior |
76
78
  | Native music performance | The model should create or perform a musical moment | musical structure, performers, vocal ownership, tempo/beat behavior, desired score/SFX relationship |
77
79
  | Ambience/SFX only | Music belongs in post or silence is dramatic | environment bed, timed effects, no-score instruction if model supports it |
@@ -93,9 +95,11 @@ Load a pack only when a relevant signal is present:
93
95
 
94
96
  ## 6. Select the adapter
95
97
 
96
- Read the target model adapter last so it can translate the creative plan into actual limits and syntax. Never let the adapter replace the story, performance, or production truth.
98
+ Identify the target model as soon as the generation family in section 2 is fixed **before** choosing the audio lane in section 4. A model that bakes audio in (Seedance 2 / 2.5) owns dialogue, ambience and score itself, so a lane chosen model-neutrally produces a plan the model cannot execute.
99
+
100
+ Read the adapter's limits and syntax last, at compile time, so it translates the creative plan rather than replacing it. Never let the adapter replace the story, performance, or production truth.
97
101
 
98
- If the model is unspecified, ask only when the answer materially changes the deliverable. Otherwise produce a model-neutral shot card and state which adapter remains unresolved.
102
+ If the model is unspecified, ask only when the answer materially changes the deliverable — whether the family bakes audio in is exactly that case. Otherwise produce a model-neutral shot card and state which adapter remains unresolved.
99
103
 
100
104
  ## 7. Select output depth
101
105
 
@@ -23,6 +23,7 @@ Load this file when the user wants a **GPT Image 2 / gpt-image-2** image (OpenAI
23
23
  - **Photorealism trigger**: include the literal word **"photorealistic"** to engage the model's photorealistic mode. Supporting phrases: "real photograph", "taken on a real camera", "professional photography", "iPhone photo", "35mm film". Camera specs (lens mm, aperture) work for high-level look but are loosely interpreted — use for vibe, not physics.
24
24
  - **Composition**: specify framing/viewpoint (close-up, wide, top-down), perspective (eye-level, low-angle), lighting/mood (soft diffuse, golden hour, high-contrast). If layout matters, call out placement ("logo top-right", "subject centered, negative space on left").
25
25
  - **People, pose, action**: describe scale, body framing, gaze, object interactions ("full body visible, feet included", "looking down at the open book, not at the camera", "hands naturally gripping the handlebar").
26
+ - **Complex scene, an edit that must not drift, or a reusable template**: use the block schema, the reference contract (`identity_lock` + an explicit `preserve` list) and named slots from `workflows/prompt-structure.md`.
26
27
  - **Constraints — what changes vs what stays**: state exclusions and invariants explicitly. For edits use **"change only X" + "keep everything else the same"**, and re-state the preserve list on every iteration to prevent drift. Common invariants: identity, geometry, layout, brand elements, camera angle, saturation, contrast, labels, surrounding objects. Always include "no watermark, no extra text, no logos/trademarks" unless the brief specifies otherwise.
27
28
  - **Text in images**: put literal text in **quotes** or **ALL CAPS**, specify typography (font style, size, color, placement). For tricky words / brand names, spell letter-by-letter. Recommend quality **high** when text is small, dense, or multi-font.
28
29
  - **Multi-image inputs**: reference each input by number with a short description ("Image 1: product photo… Image 2: style reference…") and describe the interaction ("apply Image 2's style to Image 1", "place the dog from Image 2 next to the woman in Image 1"). Use `@image1` / `@image2` tags — see `workflows/visual-dna.md`.
@@ -72,6 +72,7 @@ Layer these onto any framework to lift good → breathtaking.
72
72
  - **Natural**: "golden hour backlighting with long shadows", "overcast diffused light", "blue-hour twilight ambient".
73
73
 
74
74
  ### Camera, lens, focus (hardware = visual DNA)
75
+ - **Editing an upload?** Open with a reference contract — `identity_lock` plus a named `preserve` list — not "keep everything else the same". See `workflows/prompt-structure.md`.
75
76
  - **`iPhone` is a vibe word, not a look** — for a real phone-capture image (and for a product photo that must not read as an ad) read `workflows/ugc-smartphone.md` and write the optics/processing/light/imperfection stack.
76
77
  - **Hardware vibe**: `GoPro` for distorted action immersion · `Fujifilm` for authentic color science · `disposable camera` for raw nostalgic flash · `Hasselblad medium format` for editorial fashion · `iPhone` for everyday realism · `ARRI ALEXA` for cinematic.
77
78
  - **Lens / focus**: "low-angle shot, shallow depth of field f/1.8", "wide-angle for vast scale", "macro for intricate detail", "85mm portrait compression", "anamorphic 2.39:1 bokeh".
@@ -109,8 +109,10 @@ These elevate rich cinematic / reference-anchored sequences. For a short, tight,
109
109
 
110
110
  ## Dialogue & expression
111
111
 
112
- - Dialogue goes in quotes and may be in ANY language (Hebrew included). For silent tension, deliver it as expression, not speech: `He does not speak. His expression clearly says: "…"`.
113
- - **Seedance PERFORMS quoted dialogue natively** synced voices, lip movement, and room tone come out of the video model itself. Never route scene dialogue through TTS (`generate_speech`) or `generate_lipsync`; write each line in quotes inside its shot beat (`DANIEL says: "…"`) and generate once.
112
+ - **Dialogue is PERFORMED by the model, never by a TTS tool.** Quoted lines in the prompt come back as synced speech with lip movement and room tone, together with the SFX you name in AUDIO. Scene dialogue therefore never routes through `generate_speech` or `generate_lipsync` — write the line in quotes inside its shot beat and let Seedance act it.
113
+ - **Write dialogue in ENGLISH.** Seedance does not reliably perform other languages, and Hebrew in particular does not work it comes back as accented gibberish or English-shaped mouth movement. Never offer a user "Hebrew dialogue directly". If the delivered film has to be Hebrew, the honest routes are: (a) keep the spoken lines English, or (b) stage the beat as expression + on-screen text, or (c) generate the scene clean and dub it afterwards as an explicit, separately-priced pass. Say which one you are doing.
114
+ - `list_models` reports `sound_generation_type: "none"` for Seedance 2 / 2.5 because there is no in-app sound toggle (`sound_baked_in: true`). That field does NOT mean the model is silent. Do not read it as a reason to add TTS.
115
+ - For silent tension, deliver it as expression, not speech: `He does not speak. His expression clearly says: "…"`.
114
116
 
115
117
  ## Content tone
116
118
 
@@ -9,13 +9,17 @@ Load this file when the user wants a **Seedance 2.5** video (they said "2.5" / "
9
9
 
10
10
  **Kolbo MCP routing:** `generate_video` or `generate_elements` (refs / Visual DNA / first-last). Run `list_models({ type: "text_to_video" })` and pick the Seedance 2.5 variant by name.
11
11
 
12
- **Audio:** Seedance 2.5 still emits real synced audio. `list_models` may show `sound_generation_type: none` because there is no in-app toggle (`sound_baked_in: true`). Do not tell the user the model is silent. Quoted dialogue in the prompt is PERFORMED synced voices, lip movement, room tone so scene dialogue never goes through `generate_speech` or `generate_lipsync`; write the lines in quotes inside their shot beats.
12
+ **Audio:** Seedance 2.5 emits real synced audio. `list_models` shows `sound_generation_type: none` only because there is no in-app toggle (`sound_baked_in: true`) it does NOT mean the model is silent, and it is never a reason to reach for TTS. Quoted dialogue is PERFORMED (synced voices, lip movement, room tone) alongside the SFX named in AUDIO, so scene dialogue never goes through `generate_speech` or `generate_lipsync`; write the lines in quotes inside their shot beats.
13
+
14
+ **Dialogue language: English.** Other languages are not reliably performed, and Hebrew does not work — it returns accented gibberish or English-shaped mouth movement. Never offer a user "Hebrew dialogue directly". See `models/seedance.md` for the three honest alternatives.
15
+
16
+ **Draft at 480p.** Resolution is a credit MULTIPLIER, not a flat rate. Relative to 720p: 480p ×0.44, 1080p ×2.25. A 30s pass costs ~540cr at 480p against ~1230cr at 720p and ~2770cr at 1080p. Block the film at 480p, get the user's sign-off on staging, performance and timing, then re-run only the approved cut at delivery resolution.
13
17
 
14
18
  ## What's NEW in 2.5 (verified — never hedge)
15
19
 
16
20
  - **Duration 4–30 seconds**, whole seconds. 30s IS supported.
17
21
  - **Up to 30 shots/cuts in ONE generation.** Deliver exactly N if N ≤ 30.
18
- - **Prompt cap 30,000 characters** for the entire prompt as one string.
22
+ - **Prompt cap 15,000 characters** for the entire prompt as one string (`max_prompt_length` in the catalog; Seedance 2.0 is 10,000). Verify with `list_models` rather than trusting this number — it was documented as 30,000 for months, which is double the real limit.
19
23
  - **Up to 50 reference medias / Visual DNA mentions** (`@Name`, `@ImageN`, `#Moodboard`). Every referenced asset must be tagged in the prompt text.
20
24
  - **Multimodal refs:** images + video clips + audio can all anchor one generation.
21
25
 
@@ -37,11 +41,13 @@ UGC / phone vertical (full craft: `workflows/ugc-smartphone.md`): NEVER write "c
37
41
 
38
42
  ## Prompt length
39
43
 
40
- Simple ≤15s ~120–280 words. Locked-intro cinematic 15s typically 400–900 words. Full 30s / 15+ shots typically 700–1200 words / ~4k–9k chars. Hard cap 30,000. Never split into part 1 / part 2.
44
+ Simple ≤15s ~120–280 words. Locked-intro cinematic 15s typically 400–900 words. Full 30s / 15+ shots typically 700–1200 words / ~4k–9k chars. Hard cap 15,000 characters. Never split into part 1 / part 2.
45
+
46
+ A one-line shot beat is UNDER-WRITTEN. At 30s / 8+ shots you have ~15k characters to work with and a thin prompt wastes them: every beat carries its own camera move, performance task for BOTH the speaker and the listeners, prop/hand state, and the sound in that beat. If a 30s compile lands under ~4k characters, it is too thin — go back and direct it.
41
47
 
42
48
  ## Feature-Block (optional, UNDER the Locked Intro)
43
49
 
44
- Reach for extra department passes only when the user wants "their best possible 30 seconds" AND the 30k budget still has room after GLOBAL LOOK / CAST / LOCATION. Never replace the Locked Intro.
50
+ Reach for extra department passes only when the user wants "their best possible 30 seconds" AND the 15k budget still has room after GLOBAL LOOK / CAST / LOCATION. Never replace the Locked Intro.
45
51
 
46
52
  May add above GLOBAL LOOK: **EMOTIONAL INTENT** + **SIGNATURE MOMENT**.
47
53
  May add under the shot list: CAMERA timecode pass, SOUND timestamps, PHYSICS contract, EDITING/CONTINUITY, DIRECTORIAL NOTES.
@@ -19,6 +19,7 @@ Read [routing.md](references/filmmaking/routing.md) for the full decision rules.
19
19
 
20
20
  | Request | Mode | Read |
21
21
  |---|---|---|
22
+ | Any multi-asset or multi-scene production — film, ad, episode, campaign, recurring or multiple characters | Production planning | `references/workflows/production-planning.md` **first** — map assets, build the DNAs, confirm the set, only then shoot |
22
23
  | Premise, outline, screenplay, weak scene | Development | `scene-engine.md`, then `workflows.md` |
23
24
  | Character, location, prop, state, voice, or production preparation | Pre-production | `asset-preproduction.md`, `production-bible.md`; add `acting-direction.md` for recurring characters |
24
25
  | One generation-ready video prompt | Direction | `prompt-contracts.md`, selected craft references, then the model adapter |
@@ -0,0 +1,104 @@
1
+ <!-- PARITY: the asset-first rule and the model defaults here are mirrored in
2
+ kolbo-api/src/config/systemPrompt.js and the help widget's skillRouter.
3
+ Change all three together. -->
4
+
5
+ # Production Planning — map the assets before you shoot
6
+
7
+ Any request for a film, ad, scene, episode, campaign or "video with characters"
8
+ starts here, **before** a single video credit is spent. Most users do not know
9
+ this flow exists; they ask for a film and expect a film. Walk them through it
10
+ rather than jumping to a prompt.
11
+
12
+ Skip it only for a genuine one-off: a single clip, no recurring subject, nothing
13
+ that has to match anything else.
14
+
15
+ ## The order is not negotiable
16
+
17
+ 1. **Map** every element the script needs.
18
+ 2. **Create** each one as an approved asset (Visual DNA).
19
+ 3. **Confirm** the asset set with the user.
20
+ 4. **Only then** compile shots and generate video.
21
+
22
+ Generating video before step 3 is how a production ends up with a different face
23
+ in every shot and a re-shoot bill. A shot generated against an unapproved cast is
24
+ not a draft, it is waste.
25
+
26
+ ## 1. Map
27
+
28
+ Read the script and produce an explicit inventory. Name every element, even the
29
+ ones that feel obvious — the ones that get skipped are the ones that drift:
30
+
31
+ | Kind | DNA type | What it owns |
32
+ |---|---|---|
33
+ | Every speaking or recurring person | `character` | identity, wardrobe, physical state, performance |
34
+ | Every location, including reverse angles | `environment` | geography, landmarks, materials, light logic |
35
+ | Every hero prop, product, vehicle | `product` | identity, scale, material, damage/version state |
36
+ | The film's overall look, when it must hold across shots | `style` | visual register only |
37
+
38
+ State the inventory back to the user as a list with counts and cost before
39
+ creating anything. A 4-character, 2-location, 1-prop film is 7 assets, not "some
40
+ characters".
41
+
42
+ Separate **states** from **identities**: clean vs bloodied, day vs night, intact
43
+ vs broken are their own assets. Do not expect one DNA to carry both.
44
+
45
+ ## 2. Create
46
+
47
+ Generate the reference imagery, then register it as a Visual DNA.
48
+
49
+ **Model defaults for the asset pass** (this is an image job — never a video model):
50
+
51
+ | Asset | Model | Why |
52
+ |---|---|---|
53
+ | Cinematic environments; invented / original characters | **`mirage-film-2`** (MIRAGE FILM 2, 3cr) | cinematic look at a third the cost — the default for anything being invented from scratch |
54
+ | Assets needing reference fidelity, legible text, or editing | **`nano-banana-2`** (10cr) or **`gpt-image-2`** (12cr) | stronger reference adherence and text; GPT Image 2 when the asset carries readable words |
55
+
56
+ Read the matching prompt reference before writing an asset prompt:
57
+ `references/models/nano-banana.md` for Nano Banana, `references/models/gpt-image.md`
58
+ for GPT Image 2. There is no Mirage reference file — prompt it as a plain cinematic
59
+ still.
60
+
61
+ Use the sheet presets rather than free-form portraits — `generate_character_sheet`
62
+ with `sheet_type`:
63
+
64
+ - `character` — front/back/face turnaround, the default for a speaking role
65
+ - `character_bible` — denser model sheet (turnaround + faces + wardrobe + swatches) for a lead who appears across many shots
66
+ - `character_headless` — wardrobe/body when clothing changes but the face must not
67
+ - `environment` — location angles plus one signature detail
68
+ - `product` — angles plus material and construction close-ups
69
+ - `style` — one look applied across six varied subjects
70
+
71
+ The sheet is the single strongest consistency booster. It costs credits, so offer
72
+ it and generate on a yes.
73
+
74
+ Then `create_visual_dna` with the sheet as the reference and the matching
75
+ `dna_type`. Name each DNA in the exact form it will be tagged with later.
76
+
77
+ ## 3. Confirm
78
+
79
+ Show the user the asset set and get an explicit approval before shooting. This is
80
+ the cheapest possible place to change their mind.
81
+
82
+ ## 4. Shoot
83
+
84
+ Only now compile shots. Defaults:
85
+
86
+ - **`generate_elements` with Seedance 2.5** (`seedance-2-5`) for the film itself —
87
+ up to 30s and 30 shots in ONE generation, up to 20 Visual DNAs, dialogue and SFX
88
+ baked in. `generate_video` also accepts `visual_dna_ids` now; Elements remains
89
+ the primary reference-driven route.
90
+ - **Seedance 2.0** (`seedance-2`, cheaper, 4–15s, 9 DNAs) when the piece is short
91
+ and the cast is small. `seedance-2-fast` / `seedance-2-mini` for cheap blocking.
92
+ - Every DNA in `visual_dna_ids` must also appear as `@ExactName` in the prompt.
93
+ - Dialogue in quotes inside its shot beat — English only, never TTS or lipsync.
94
+ See `models/seedance25.md`.
95
+ - **First pass at 480p.** Resolution is a credit multiplier (480p ×0.44 vs 720p,
96
+ 1080p ×2.25). Block, approve, then re-run the approved cut at delivery
97
+ resolution.
98
+
99
+ ## What this replaces
100
+
101
+ Do not plan a film as "N separate image-to-video clips plus TTS plus lipsync".
102
+ That shape is a legacy of models that could not hold a cast or speak. It costs
103
+ more, drifts between shots, and produces dead-eyed dubbed performance. One
104
+ multi-shot Seedance generation against approved DNAs is the current answer.
@@ -31,7 +31,12 @@ function registerColorPaletteTools(server, client, options = {}) {
31
31
  const palettes = result.color_palettes || [];
32
32
  const text = JSON.stringify({ color_palettes: palettes, pagination: result.pagination }, null, 2);
33
33
 
34
- if (ui()) {
34
+ // Ship structuredContent UNCONDITIONALLY. Gating on ui() left every host that
35
+ // renders widgets without advertising MCP Apps (Kolbo Code) with text-only rows
36
+ // that carry no thumbnail field at all — and its BY_TOOL map still force-mounts
37
+ // the media grid on them, so the card rendered one broken-file glyph per cell.
38
+ // media.js and listResult() have always done it this way; these five lagged.
39
+ {
35
40
  return uiResult(UI.mediaGrid, text, {
36
41
  widget: 'media-grid',
37
42
  title: 'Color DNA Palettes',
@@ -541,7 +541,7 @@ function registerGenerateTools(server, client, options = {}) {
541
541
  // retired textToVideoGeneration path and was stale.
542
542
  server.tool(
543
543
  'generate_video',
544
- 'Generate a video from a text prompt using Kolbo AI. For SEVERAL different videos, pass all their prompts in `prompts` in ONE call (one combined widget) — never a series of separate calls. For animating an existing still image into motion, use generate_video_from_image instead. For a coordinated multi-scene video campaign, use generate_creative_director with workflow_type="video". Supports reference images (for style/composition guidance) and Visual DNA for character consistency. ROUTE BEFORE CALLING: when reference images anchor IDENTITY (specific characters, a specific product, a location that must match) — especially 2+ of them — that is generate_elements, not this tool; reference_images here are loose style/composition hints. Decide the right tool FIRST: a mis-routed call still starts a PAID generation, and switching tools afterwards without cancel_generation leaves the user paying for both. Returns the final video URL when complete.',
544
+ 'Generate a video from a text prompt using Kolbo AI. For SEVERAL different videos, pass all their prompts in `prompts` in ONE call (one combined widget) — never a series of separate calls. For animating an existing still image into motion, use generate_video_from_image instead. For a coordinated multi-scene video campaign, use generate_creative_director with workflow_type="video". Supports reference images (for style/composition guidance) and Visual DNA for character consistency. Seedance 2/2.5 PERFORM quoted dialogue natively (synced voice, lip movement, room tone) — do not route scene dialogue to generate_speech or generate_lipsync; write it in ENGLISH (other languages, Hebrew included, do not perform reliably). Resolution is a credit MULTIPLIER (vs 720p: 480p x0.44, 1080p x2.25, 4k x4.95), so draft at 480p and re-run only the approved cut at delivery resolution. ROUTE BEFORE CALLING: when reference images anchor IDENTITY (specific characters, a specific product, a location that must match) — especially 2+ of them — that is generate_elements, not this tool; reference_images here are loose style/composition hints. Decide the right tool FIRST: a mis-routed call still starts a PAID generation, and switching tools afterwards without cancel_generation leaves the user paying for both. Returns the final video URL when complete.',
545
545
  {
546
546
  prompt: z.string().optional().describe('Text description of the video to generate. Required unless `prompts` is provided.'),
547
547
  prompts: promptsField('videos'),
@@ -621,7 +621,7 @@ function registerGenerateTools(server, client, options = {}) {
621
621
  // ─── generate_video_from_image ─────────────────────────────
622
622
  server.tool(
623
623
  'generate_video_from_image',
624
- 'Animate an existing still image into a video using Kolbo AI. The image comes from `image_url`; `prompt` describes the motion (not the subject — the subject is already in the image). For generating a video from scratch, use generate_video. ANIMATING SEVERAL SHOTS OF THE SAME SEQUENCE? Pass them ALL in `items` in ONE call (one combined widget) — never a series of separate calls, which buries the chat under one widget per clip. Across calls (a sequence longer than the batch cap), make the first call without `session_id`, take the `session_id` from its result, and pass that same `session_id` on every following call — otherwise each clip becomes its own session and the user gets a stack of near-identical single-clip sessions in the Kolbo sidebar. Returns the final video URL(s) when complete.',
624
+ 'Animate an existing still image into a video using Kolbo AI. The image comes from `image_url`; `prompt` describes the motion (not the subject — the subject is already in the image). For generating a video from scratch, use generate_video. FOR A MULTI-SHOT FILM with recurring characters or locations, prefer ONE generate_elements call carrying the whole DNA-anchored sequence over a series of separate image-to-video clips. ANIMATING SEVERAL SHOTS OF THE SAME SEQUENCE? Pass them ALL in `items` in ONE call (one combined widget) — never a series of separate calls, which buries the chat under one widget per clip. Across calls (a sequence longer than the batch cap), make the first call without `session_id`, take the `session_id` from its result, and pass that same `session_id` on every following call — otherwise each clip becomes its own session and the user gets a stack of near-identical single-clip sessions in the Kolbo sidebar. Returns the final video URL(s) when complete.',
625
625
  {
626
626
  image_url: z.string().optional().describe('URL of the source image to animate. Required unless `items` is provided.'),
627
627
  prompt: z.string().optional().describe('Text description of the desired MOTION (e.g., "camera slowly pans right while the character walks forward"). Required unless `items` is provided.'),
@@ -772,7 +772,7 @@ function registerGenerateTools(server, client, options = {}) {
772
772
  // ─── generate_speech ───────────────────────────────────────
773
773
  server.tool(
774
774
  'generate_speech',
775
- 'Convert text to speech using Kolbo AI. Default provider is ElevenLabs. To pick a specific voice by language/gender, call list_voices first and pass the returned voice_id (or a voice display name — both work). Every voice belongs to a provider (ElevenLabs, DeepDub, MiniMax, Google/Gemini, OpenAI, Zonos) and each provider exposes its own expressive/style controls below — the engine ignores any control that does not apply to the chosen voice\'s provider, so it is safe to pass only what you need. Returns the final audio URL when complete.',
775
+ 'Convert text to speech using Kolbo AI. SCOPE: narration, voiceover, and standalone audio (ads, explainers, audiobooks, announcements). This is NOT the route for character dialogue in a film you are generating with Seedance 2/2.5 — those models PERFORM quoted dialogue natively (synced voices, lip movement, room tone), so sending scene dialogue here just adds a second, unsynced voice track. Default provider is ElevenLabs. To pick a specific voice by language/gender, call list_voices first and pass the returned voice_id (or a voice display name — both work). Every voice belongs to a provider (ElevenLabs, DeepDub, MiniMax, Google/Gemini, OpenAI, Zonos) and each provider exposes its own expressive/style controls below — the engine ignores any control that does not apply to the chosen voice\'s provider, so it is safe to pass only what you need. Returns the final audio URL when complete.',
776
776
  {
777
777
  text: z.string().describe('The text to convert to speech'),
778
778
  voice: z.string().optional().describe('Voice ID or display name — MUST come from a `list_voices` result, never constructed. Google/Gemini ids in particular are not validated provider-side: an id that is not in the catalog is silently mapped to another voice (or a default one) and the audio comes back in a voice nobody asked for. Do not pattern-match a locale onto an id you saw for another language. Default: "Rachel"'),
@@ -1185,7 +1185,7 @@ function registerGenerateTools(server, client, options = {}) {
1185
1185
  // ─── generate_elements ─────────────────────────────────────
1186
1186
  server.tool(
1187
1187
  'generate_elements',
1188
- 'Generate a video from reference elements (images, videos, and/or audio) + a text prompt. Use when the user wants to animate specific uploaded/referenced assets — e.g. "animate this product", "put these 3 characters into a scene". PROMPT CONTRACT (Seedance / Elements): Locked Intro only — Total line, then [GLOBAL LOOK] / [CAST] / [LOCATION] / SHOT N. Do NOT write SCENE CONTEXT / OPTICS / ACTION department packs. Every Visual DNA in visual_dna_ids MUST also appear in the prompt as @ExactDNAName (e.g. "@Zohar walks…") — never "Zohar\'s" or "the man on the left" as a substitute. IMPORTANT: different models accept different numbers of inputs — call list_models type="elements" and read elements_max_images / elements_max_videos / elements_max_audio on the chosen model before generating. For text-only → video use generate_video instead. For animating a single still image use generate_video_from_image. Returns the final video URL when complete.',
1188
+ 'Generate a video from reference elements (images, videos, and/or audio) + a text prompt. Use when the user wants to animate specific uploaded/referenced assets — e.g. "animate this product", "put these 3 characters into a scene". PRIMARY ROUTE FOR A DNA-ANCHORED MULTI-SHOT FILM: one call can carry the whole sequence — seedance-2-5 takes 4-30s, up to 30 shots and 20 Visual DNAs in a SINGLE generation (seedance-2: 4-15s, 9 DNAs) — instead of a stack of separate clips. DIALOGUE IS PERFORMED NATIVELY: quoted dialogue in the prompt comes back as synced voices with lip movement, room tone and the SFX named in the AUDIO block — never route scene dialogue to generate_speech or generate_lipsync. Write dialogue in ENGLISH; other languages (Hebrew included) do not perform reliably. COST: resolution is a MULTIPLIER, not a flat fee (vs 720p: 480p x0.44, 1080p x2.25, 4k x4.95) — run blocking/draft passes at 480p and re-run only the approved cut at delivery resolution. PROMPT CONTRACT (Seedance / Elements): Locked Intro only — Total line, then [GLOBAL LOOK] / [CAST] / [LOCATION] / SHOT N. Do NOT write SCENE CONTEXT / OPTICS / ACTION department packs. Every Visual DNA in visual_dna_ids MUST also appear in the prompt as @ExactDNAName (e.g. "@Zohar walks…") — never "Zohar\'s" or "the man on the left" as a substitute. IMPORTANT: different models accept different numbers of inputs — call list_models type="elements" and read elements_max_images / elements_max_videos / elements_max_audio on the chosen model before generating. For text-only → video use generate_video instead. For animating a single still image use generate_video_from_image. Returns the final video URL when complete.',
1189
1189
  {
1190
1190
  prompt: z.string().describe('Locked Intro prompt (Seedance/Elements): Total line, [GLOBAL LOOK], [CAST] with @ExactDNAName for every visual_dna_ids entry, [LOCATION], then SHOT N. Not SCENE CONTEXT/OPTICS/ACTION packs. Never substitute "the left man" or "Zohar\'s" for @Name.'),
1191
1191
  model: z.string().optional().describe('Model identifier. If the user already named a family (Grok / Kling / Veo / Seedance / …), pass THAT family — never default to Seedance because Elements often uses it. Use list_models type="elements" for exact ids and elements_max_* caps. Do NOT omit (omitting = Smart Select).'),
@@ -1462,7 +1462,7 @@ function registerGenerateTools(server, client, options = {}) {
1462
1462
  // ─── generate_lipsync ──────────────────────────────────────
1463
1463
  server.tool(
1464
1464
  'generate_lipsync',
1465
- 'Lipsync an audio track to a source image or video. Both `source` (image or video) and `audio` can be provided as URLs or as absolute local file paths. Pass a text_prompt only if the model supports it (some lipsync models do character performance from a prompt). **Validate before submitting: for `lipsync-video` sources, the input video duration must fall within `min_video_duration`-`max_video_duration` from list_models; audio duration must fall within `min_audio_duration`-`max_audio_duration` (and if `audio_max_follows_video_duration: true`, audio is also capped at the video duration); audio format must be in `supported_audio_formats` when specified.** Returns a lipsynced video URL.',
1465
+ 'Lipsync an audio track to a source image or video. SCOPE: it syncs an EXISTING audio waveform to a face in footage you already have (dubbing, translation, fixing a delivered take). It is NOT how you get characters to speak in a film you are generating — Seedance 2/2.5 perform quoted dialogue natively with synced lip movement, so a scene generated there needs no lipsync pass. Both `source` (image or video) and `audio` can be provided as URLs or as absolute local file paths. Pass a text_prompt only if the model supports it (some lipsync models do character performance from a prompt). **Validate before submitting: for `lipsync-video` sources, the input video duration must fall within `min_video_duration`-`max_video_duration` from list_models; audio duration must fall within `min_audio_duration`-`max_audio_duration` (and if `audio_max_follows_video_duration: true`, audio is also capped at the video duration); audio format must be in `supported_audio_formats` when specified.** Returns a lipsynced video URL.',
1466
1466
  {
1467
1467
  source: z.string().describe('URL or absolute local path to the source image or video (the face to animate). For lipsync-video: duration must fall within `min_video_duration`-`max_video_duration` from list_models.'),
1468
1468
  audio: z.string().describe('URL or absolute local path to the audio track (the voice to sync to). Duration must fall within `min_audio_duration`-`max_audio_duration` from list_models; format must be in `supported_audio_formats` (when set).'),
@@ -33,7 +33,12 @@ function registerPresetTools(server, client, options = {}) {
33
33
  note: 'Filter with `type` (image | image_edit | video | music | text_to_video) to see a focused set. Pass the chosen exact id as `preset_id` on the next generation call.',
34
34
  });
35
35
 
36
- if (ui()) {
36
+ // Ship structuredContent UNCONDITIONALLY. Gating on ui() left every host that
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
+ {
37
42
  return uiResult(UI.mediaGrid, text, {
38
43
  widget: 'media-grid',
39
44
  title: 'Presets' + (type ? ' — ' + type : ''),
@@ -78,7 +78,12 @@ function registerStockLibraryTools(server, client, options = {}) {
78
78
  : '';
79
79
  const text = `${head}\n\n${shownAssets.map(assetLine).join('\n\n')}${moreHint}\n\nUse [source:sourceId] with get_stock_asset for full variants, or import_stock_asset to copy it into the media library.`;
80
80
 
81
- if (ui()) {
81
+ // Ship structuredContent UNCONDITIONALLY. Gating on ui() left every host that
82
+ // renders widgets without advertising MCP Apps (Kolbo Code) with text-only rows
83
+ // that carry no thumbnail field at all — and its BY_TOOL map still force-mounts
84
+ // the media grid on them, so the card rendered one broken-file glyph per cell.
85
+ // media.js and listResult() have always done it this way; these five lagged.
86
+ {
82
87
  const items = assets.slice(0, 24).map((a) => {
83
88
  const mt = widgetMediaType(a.mediaType);
84
89
  return {
@@ -160,7 +165,12 @@ function registerStockLibraryTools(server, client, options = {}) {
160
165
  total: result.count,
161
166
  });
162
167
 
163
- if (ui()) {
168
+ // Ship structuredContent UNCONDITIONALLY. Gating on ui() left every host that
169
+ // renders widgets without advertising MCP Apps (Kolbo Code) with text-only rows
170
+ // that carry no thumbnail field at all — and its BY_TOOL map still force-mounts
171
+ // the media grid on them, so the card rendered one broken-file glyph per cell.
172
+ // media.js and listResult() have always done it this way; these five lagged.
173
+ {
164
174
  const collections = result.collections || [];
165
175
  const items = collections.slice(0, 24).map((c) => ({
166
176
  id: c.id,
@@ -207,7 +207,7 @@ function registerVisualDnaTools(server, client, options = {}) {
207
207
  // ─── generate_character_sheet ──────────────────────────────
208
208
  server.tool(
209
209
  'generate_character_sheet',
210
- '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`.',
210
+ '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
211
  {
212
212
  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
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.')
@@ -80,7 +80,12 @@ function registerVoiceTools(server, client, options = {}) {
80
80
  const range = `${start + 1}–${start + shownVoices.length}`;
81
81
  const text = `Available voices (showing ${range} of ${voices.length}, page ${pageNum}/${pageCount}):\n\n${lines.join('\n\n')}${narrowHint}\n\nUse the "voice_id" value in generate_speech calls.`;
82
82
 
83
- if (ui()) {
83
+ // Ship structuredContent UNCONDITIONALLY. Gating on ui() left every host that
84
+ // renders widgets without advertising MCP Apps (Kolbo Code) with text-only rows
85
+ // that carry no thumbnail field at all — and its BY_TOOL map still force-mounts
86
+ // the media grid on them, so the card rendered one broken-file glyph per cell.
87
+ // media.js and listResult() have always done it this way; these five lagged.
88
+ {
84
89
  return uiResult(UI.mediaGrid, text, {
85
90
  widget: 'media-grid',
86
91
  title: 'Voices',