@kolbo/mcp 1.69.0-rc.1 → 1.69.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 CHANGED
@@ -113,13 +113,13 @@ Just ask your agent naturally:
113
113
 
114
114
  Without the optional skill, the config block alone already exposes every tool — you just describe what you want. With the skill installed, each of these is also routed to the right MCP tool with the right defaults — UGC mode picks 9:16 + sound-off + no-captions, marketplace mode enforces compliance (pure white bg, no text, no props), product photoshoot mode uses the right aspect for the platform (2:3 Pinterest, 16:9 hero banner, 1:1 IG feed), etc. The routing logic is shared with [Kolbo Code](https://github.com/Zoharvan12/kolbo-code), so the behavior is identical however you connect.
115
115
 
116
- ## Available Tools (117)
116
+ ## Available Tools
117
117
 
118
118
  **Generation**
119
119
  | Tool | Description |
120
120
  |------|-------------|
121
- | `generate_image` | Text → image |
122
- | `generate_image_edit` | Existing image(s) + prompt → edited image |
121
+ | `generate_image` | Text → image. Supports `preset_id` from `list_presets type="image"`. |
122
+ | `generate_image_edit` | Existing image(s) + prompt → edited image. Supports `preset_id` from `list_presets type="image_edit"`. |
123
123
  | `generate_video` | Text → video |
124
124
  | `generate_video_from_image` | Still image + motion prompt → video |
125
125
  | `generate_video_from_video` | Input video → restyled video, or burn in subtitles (video-to-video). `prompt` optional — prompt-less models (VEED Subtitles, Act Two, Wan Animate) use `preset` / `source_language` / `translation_language`, plus `srt_content` / `srt_file_url` / `vocabulary` / `customization` for VEED |
@@ -233,7 +233,7 @@ Every generation tool also accepts an optional `project_id` arg that routes the
233
233
  |------|-------------|
234
234
  | `list_models` | Current model catalog with costs and capabilities |
235
235
  | `list_voices` | TTS voices (presets + cloned) |
236
- | `list_presets` | Generation presets across image/video/music/text-to-video catalogs |
236
+ | `list_presets` | Generation presets across image/image-edit/video/music/text-to-video catalogs. Pass the selected exact id as `preset_id`; never claim a preset was applied without it. |
237
237
  | `list_cinematic_presets` | "Cinema mode" presets grouped by dimension (camera, lens, focal_length, aperture, angle, shot_type, color_palette, lighting) — pass ids via the `cinematic` arg on `generate_image` / `generate_image_edit`. Only when the user wants a specific cinematic look |
238
238
  | `list_projects` | List owned + shared projects (id, name, role, is_default) — call first to resolve a project name into the `project_id` you pass to generation tools |
239
239
  | `move_session` | Move a session (generation, chat, transcription…) and ALL its media to another project |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolbo/mcp",
3
- "version": "1.69.0-rc.1",
3
+ "version": "1.69.0",
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": {
package/skill/SKILL.md CHANGED
@@ -93,8 +93,8 @@ Each `references/models/*.md` mirrors the matching skill prompt in `kolbo-api/sr
93
93
  ### Generation
94
94
  | Tool | Description |
95
95
  |------|-------------|
96
- | `generate_image` | Single image from a text prompt. Supports Visual DNA, moodboards, reference images, web-search grounding. |
97
- | `generate_image_edit` | Edit/transform an existing image. Pass `source_images` + edit prompt. |
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`. |
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" })`. |
98
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. |
99
99
  | `generate_video` | Text-to-video. Does **not** support Visual DNA — use `generate_elements` for character-consistent video. |
100
100
  | `generate_video_from_image` | Animate a still. Prompt describes motion, not subject. |
@@ -113,7 +113,7 @@ Each `references/models/*.md` mirrors the matching skill prompt in `kolbo-api/sr
113
113
  | `list_models` / `list_voices` / `check_credits` / `get_generation_status` / `cancel_generation` / `get_session_usage` | Discovery + status. `list_models` with no args returns the recommended shortlist out of ~428 — pass `type` for a full category with per-model caps. `cancel_generation` stops an in-flight job and refunds what it can: use it when the user changes their mind mid-generation instead of letting it run. |
114
114
  | `upload_media` / `create_upload_ticket` / `list_media` / `get_media` / `get_media_stats` / `favorite_media` / `unfavorite_media` / `delete_media` / `restore_media` / `permanently_delete_media` / `move_media` / `bulk_*_media` / `*_media_folder` | Media library — see `workflows/media-library.md`. Getting a LOCAL file in depends on where the server runs: `upload_media` with a path only works on a local (stdio) install; over a remote connector use `create_upload_ticket` and POST the file yourself. |
115
115
  | `create_visual_dna` / `generate_character_sheet` / `list_visual_dnas` / `get_visual_dna` / `delete_visual_dna` / `*_visual_dna_folder` (5 folder tools) | Visual DNA (+ character sheet, character folders) — see `workflows/visual-dna.md` |
116
- | `list_moodboards` / `get_moodboard` / `list_presets` | Style overlays |
116
+ | `list_moodboards` / `get_moodboard` / `list_presets` | Style overlays. A preset request is binding: resolve the requested or closest matching preset in the correct catalog, then pass its exact returned `id` as `preset_id`. Never say a preset was used if the generation call omitted it. |
117
117
  | `list_color_palettes` / `analyze_color_palette` / `create_color_palette` / `update_color_palette` / `delete_color_palette` / `activate_color_palette` / `deactivate_color_palette` | **Color DNA — sticky and account-wide.** At most one palette is active at a time; while it is, it strict-grades **every** image and video generation automatically, with no per-call argument. `analyze_color_palette` pulls colors out of 1-5 image URLs for free and does NOT save. `create_color_palette` defaults `is_active: true`, which activates it and deactivates any other. Per-generation opt-out: `skip_color_palette: true` on `generate_image` / `generate_image_edit` / `generate_video` / `generate_video_from_image`. |
118
118
  | `list_agents` / `create_agent` / `update_agent` / `delete_agent` | Custom chat agents — reusable named personas for `chat_send_message`. The agent's `description` IS the system instruction. Resolve a name the user mentions ("use my SEO agent") to an id with `list_agents`, then pass `agent_id`. Global/preset agents are read-only; only the user's own can be updated or deleted. |
119
119
  | `search_stock_media` / `get_stock_sources` / `get_stock_categories` / `get_stock_collections` / `get_stock_asset` / `analyze_script_for_stock` / `import_stock_asset` | Stock library (free, no credits) — EXISTING photos / videos / 3D / SFX / music. For stock **music** use `search_stock_media` with `mediaType: "music"` (semantic vibe query, e.g. "uplifting corporate background") → `get_stock_asset` for downloads. The older `*_music_library` tools are deprecated adapters over this — prefer the stock tools, except for the licensed-catalog tools in the next row. |
@@ -158,6 +158,8 @@ A user-named tool — in any language — overrides every other rule. Recognized
158
158
 
159
159
  ## Core Workflow
160
160
 
161
+ **Preset contract:** if the user asks for a preset, names one, or says to use one of their/Kolbo presets, call `list_presets` with the matching type before generation and pass the selected exact `id` as `preset_id`. Use `image` for `generate_image` and `image_edit` for `generate_image_edit`. Never invent an id or silently continue without the requested preset.
162
+
161
163
  1. **Check credits** ONCE per conversation (Step 0). Skip if already checked.
162
164
  2. **Discover models** with `list_models` using a `type` filter — but **skip when the user names a specific model**.
163
165
  3. **Pick the model**:
package/src/apps/theme.js CHANGED
@@ -140,7 +140,7 @@ body {
140
140
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.65));
141
141
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
142
142
  .k-skel.done::after { animation: none; background: none; }
143
- .k-cell-fill { width: 100%; height: 100%; object-fit: cover; display: block; }
143
+ .k-cell-fill { width: 100%; height: 100%; object-fit: contain; display: block; background: #000; }
144
144
  .k-gen-badge {
145
145
  position: absolute; top: 10px; left: 10px; z-index: 2;
146
146
  display: inline-flex; align-items: center; gap: 6px;
@@ -170,7 +170,7 @@ body {
170
170
  .k-media { position: relative; border-radius: 10px; overflow: hidden; border: 1px solid var(--border);
171
171
  background: #000; cursor: pointer; transition: transform 300ms var(--spring), box-shadow 300ms var(--smooth); }
172
172
  .k-media:hover { transform: scale(1.015); box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45); }
173
- .k-media img, .k-media video { display: block; width: 100%; height: 100%; object-fit: cover; }
173
+ .k-media img, .k-media video { display: block; width: 100%; height: 100%; object-fit: contain; }
174
174
  .k-media.selected { outline: 2px solid var(--brand); outline-offset: 1px; }
175
175
 
176
176
  /* ---- Per-item hover download button (multi-image grids, CD scenes, viewer) ---- */
@@ -257,7 +257,7 @@ html.k-fullscreen .k-actions { flex: none; padding-top: 8px; }
257
257
  background: var(--surface); cursor: pointer; transition: transform 250ms var(--spring); }
258
258
  .k-cell:hover { transform: translateY(-2px) scale(1.01); }
259
259
  .k-cell .k-cell-media { position: relative; aspect-ratio: 1; background: #000; }
260
- .k-cell .k-cell-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
260
+ .k-cell .k-cell-media img { width: 100%; height: 100%; object-fit: contain; display: block; }
261
261
  .k-cell-play { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
262
262
  .k-cell .k-cell-label { padding: 6px 8px; font-size: 11px; color: var(--text-muted);
263
263
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@@ -112,7 +112,7 @@ function wire() {
112
112
  e.stopPropagation();
113
113
  var url = b.getAttribute('data-video-play');
114
114
  var holder = b.parentNode;
115
- holder.innerHTML = '<video src="' + esc(url) + '" controls autoplay playsinline style="width:100%;height:100%;object-fit:cover"></video>';
115
+ holder.innerHTML = '<video src="' + esc(url) + '" controls autoplay playsinline style="width:100%;height:100%;object-fit:contain;background:#000"></video>';
116
116
  holder.querySelector('video').onclick = function (ev) { ev.stopPropagation(); };
117
117
  };
118
118
  });
package/src/index.js CHANGED
@@ -127,7 +127,8 @@ function createServer(opts = {}) {
127
127
  '7. SESSION CONTINUITY — one task, one session, always: every generation tool returns a `session_id`. For ANY follow-up, refinement, retry, or next step on the SAME task, pass that session_id back — never start fresh. BATCH RULE (critical): when a single user request produces multiple parallel generations (e.g. "animate these 5 images", "generate 3 variants"), do NOT launch them all at once without a session_id. Instead: (1) run the FIRST generation without session_id to create the session, (2) capture the session_id from its response, (3) pass that session_id to ALL remaining generations in the batch. This keeps the entire batch in one session. Exception: only omit session_id and start fresh when the user explicitly starts an unrelated new task.',
128
128
  '8. LOCAL FILES / REFERENCE MEDIA — HOW TO HANDLE EVERY CASE. (A) User has a LOCAL file (audio, video, image, document) on their machine. What matters is WHERE THIS SERVER RUNS, not what your client can do — your own filesystem access is irrelevant if the server is somewhere else. On a LOCAL stdio install (server and client share a machine) → call `upload_media` with the absolute path, or pass the path straight to tools like `transcribe_audio` that accept local paths. Over a REMOTE connector the server cannot see that path no matter how capable you are, so a local path will always fail: if you can run shell commands or issue HTTP requests → call `create_upload_ticket` and POST the file to the returned upload_url yourself (fastest, no user interaction); if you cannot → call `media_upload_widget` IMMEDIATELY, the user uploads, and a `media.kolbo.ai` CDN URL comes back for any follow-up call. (B) You already have a public URL (media.kolbo.ai, any CDN, any direct link) → pass it directly; all Kolbo tools accept public URLs. NEVER search for DO Spaces keys, DigitalOcean credentials, or server-side upload credentials. NEVER ask the user to put the file on Google Drive, Dropbox, or Loom. NEVER invent or guess a URL. NEVER base64 anything but a tiny file — it costs context in proportion to file size; use the ticket or the widget instead.',
129
129
  '9. MODEL SELECTION — ROUTE BY THE STRENGTHS SUMMARY, NEVER BY THE BADGE OR THE PRICE TAG: ALWAYS pass a specific `model` on every generation tool — do NOT omit it (omitting falls back to "Smart Select" auto-routing, which hides the choice from the user; use it ONLY if the user explicitly asks you to auto-pick). To choose: call `list_models` with the matching `type` and read each model\'s STRENGTHS SUMMARY — the "— …" clause printed after the credit cost. That summary IS the routing instruction: match it against what the user actually asked for (subject, style, motion, length, quality bar, speed), then pick the CHEAPEST model whose summary covers the task. `[NEW]` and `[RECOMMENDED]` badges, a high credit number, and "flagship"/"most intelligent" wording are NOT selection signals — never pick a model because it is newest, biggest or most expensive. Escalate to a premium/frontier model only when the user explicitly asks for maximum quality, or when no cheaper summary covers the requirement. Models printed under "Named-only" (no summary) are opt-in: use them only when the user names them. TEXT/CHAT: `chat_send_message` bills PER TOKEN, so the listed credit number is not the cost — a frontier text model (Claude Fable 5, GPT-5.6 Sol, Pro-class) costs 5-30x a mid-tier one per reply. Default ordinary chat (writing, brainstorming, Q&A, summarising) to a balanced mid-tier model and reserve the frontier tier for hard reasoning or long-form code the user asked for.',
130
- '10. IMAGE EDITING: for ANY prompt-driven / content edit of an existing image — "make it night", changing scene/lighting/colors, adding/removing/replacing objects, restyling — use `generate_image_edit` (it runs on strong dedicated editing models, same as image generation). Do NOT use `edit_image` for content edits — `edit_image` is ONLY for mechanical enhancements (upscale, expand/outpaint, remove-background, skin retouch). Its `magic_edit` operation is deprecated in favor of `generate_image_edit`. EXPANDING AN IMAGE: to widen/extend/uncrop an image or fit it into a wider frame while KEEPING the existing artwork, use `edit_image` with operation="zoom_out" (outpainting — original pixels preserved; size it with `zoom_out_percentage` or the `expand_left/right/top/bottom` pixel args). The "reframe" operation is NOT this: it re-generates the whole picture at a new aspect ratio and the subject comes back re-imagined. Only pick "reframe" when the user wants the shot re-taken, never when they want their image extended.'
130
+ '10. IMAGE EDITING: for ANY prompt-driven / content edit of an existing image — "make it night", changing scene/lighting/colors, adding/removing/replacing objects, restyling — use `generate_image_edit` (it runs on strong dedicated editing models, same as image generation). Do NOT use `edit_image` for content edits — `edit_image` is ONLY for mechanical enhancements (upscale, expand/outpaint, remove-background, skin retouch). Its `magic_edit` operation is deprecated in favor of `generate_image_edit`. EXPANDING AN IMAGE: to widen/extend/uncrop an image or fit it into a wider frame while KEEPING the existing artwork, use `edit_image` with operation="zoom_out" (outpainting — original pixels preserved; size it with `zoom_out_percentage` or the `expand_left/right/top/bottom` pixel args). The "reframe" operation is NOT this: it re-generates the whole picture at a new aspect ratio and the subject comes back re-imagined. Only pick "reframe" when the user wants the shot re-taken, never when they want their image extended.',
131
+ '11. PRESET CONTRACT: if the user asks for a preset, names a preset, or says to use one of their/Kolbo presets, you MUST call `list_presets` with the matching type before generation, resolve the named or closest matching preset, and pass its exact returned `id` as `preset_id`. Use type="image" for generate_image and type="image_edit" for generate_image_edit. Never silently ignore a preset request, never invent an id, and never claim a preset was applied unless `preset_id` was present in the generation call.'
131
132
  ].join('\n')
132
133
  });
133
134
  const progress = require('./progress');
@@ -141,7 +141,7 @@ function registerGenerateTools(server, client, options = {}) {
141
141
  // ─── generate_image ────────────────────────────────────────
142
142
  server.tool(
143
143
  'generate_image',
144
- 'Generate image(s) from a text prompt using Kolbo AI. Supports Visual DNA profiles (for character/style/product consistency), moodboards (for style direction), reference images (for composition guidance), batch generation (num_images for variations of ONE prompt, `prompts` for SEVERAL different prompts in one combined widget), and web-search grounding. When the user wants multiple distinct images, pass all their prompts in `prompts` in ONE call — never a series of separate generate_image calls. For EDITING an existing image, use generate_image_edit instead. For a coordinated multi-scene set planned by AI from a single brief (storyboard, ad campaign), use generate_creative_director. Returns the final image URL(s) when complete.',
144
+ 'Generate image(s) from a text prompt using Kolbo AI. Supports Visual DNA profiles (for character/style/product consistency), moodboards (for style direction), Kolbo image presets, reference images (for composition guidance), batch generation (num_images for variations of ONE prompt, `prompts` for SEVERAL different prompts in one combined widget), and web-search grounding. PRESET CONTRACT: when the user asks to use a preset, a named preset, or "one of my/Kolbo image presets", call list_presets type="image" first, resolve the requested or best-matching preset, and pass its exact id as `preset_id`; never silently generate without it. When the user wants multiple distinct images, pass all their prompts in `prompts` in ONE call — never a series of separate generate_image calls. For EDITING an existing image, use generate_image_edit instead. For a coordinated multi-scene set planned by AI from a single brief (storyboard, ad campaign), use generate_creative_director. Returns the final image URL(s) when complete.',
145
145
  {
146
146
  prompt: z.string().optional().describe('Text description of the image to generate. Required unless `prompts` is provided.'),
147
147
  prompts: promptsField('images'),
@@ -155,7 +155,7 @@ function registerGenerateTools(server, client, options = {}) {
155
155
  enable_web_search: z.boolean().optional().describe('Enable web-search grounding for the prompt (useful for current events, brand references, real-world accuracy). Default: false'),
156
156
  resolution: z.string().optional().describe('Image resolution tier: "1K" (~1024px), "2K" (Full HD), "3K" (QHD), or "4K" (UHD). Model-dependent — call list_models and read supported_resolutions on the chosen model. Read resolution_multipliers on the same model to predict credit cost. Omit to use the model default.'),
157
157
  quality: z.string().optional().describe('Quality tier for models that support it (e.g. "low", "medium", "high", "auto"). Check list_models → supported_qualities on the chosen model. "auto" is normalised to "medium" on gpt-image-2. Omit to use the model default.'),
158
- preset_id: z.string().optional().describe('Preset ID from list_presets type="image" to apply a saved style preset to this generation.'),
158
+ preset_id: z.string().optional().describe('Exact preset ID from list_presets type="image". If the user requests any image preset, resolve it with list_presets and pass it here; do not omit it.'),
159
159
  cinematic: CINEMATIC_SCHEMA,
160
160
  skip_color_palette: z.boolean().optional().describe('Opt this single call OUT of the account\'s active Color DNA palette (see list_color_palettes / activate_color_palette). By default, if the user has an active palette it strict-grades every generation automatically — pass true only when the user explicitly wants this one image ungraded.'),
161
161
  project_id: projectIdField,
@@ -218,7 +218,7 @@ function registerGenerateTools(server, client, options = {}) {
218
218
  // ─── generate_image_edit ──────────────────────────────────
219
219
  server.tool(
220
220
  'generate_image_edit',
221
- 'THE tool for ANY prompt-driven / content edit of an existing image — changing the scene ("make it night", "change the sky to sunset"), adding/removing/replacing objects, restyling, recoloring, compositing, or any "edit this image to…" request. This is the image-editing equivalent of generate_image and runs on strong dedicated editing models (nano-banana-2, gpt-image-2). Provide the source image URL(s) in `source_images` and the instruction in `prompt`. Supports Visual DNA profiles and moodboards for style-consistent edits. Do NOT use `edit_image` for these — that tool is only for mechanical enhancements (upscale/reframe/remove-background/skin). For a brand-new image from scratch, use generate_image. Returns the edited image URL(s) when complete.',
221
+ 'THE tool for ANY prompt-driven / content edit of an existing image — changing the scene ("make it night", "change the sky to sunset"), adding/removing/replacing objects, restyling, recoloring, compositing, or any "edit this image to…" request. This is the image-editing equivalent of generate_image and runs on strong dedicated editing models (nano-banana-2, gpt-image-2). Provide the source image URL(s) in `source_images` and the instruction in `prompt`. Supports Visual DNA profiles, moodboards, and Kolbo image-editing presets. PRESET CONTRACT: if the user requests a preset, call list_presets type="image_edit" and pass its exact id as `preset_id`; never silently omit it. Do NOT use `edit_image` for these — that tool is only for mechanical enhancements (upscale/reframe/remove-background/skin). For a brand-new image from scratch, use generate_image. Returns the edited image URL(s) when complete.',
222
222
  {
223
223
  prompt: z.string().describe('Description of the edit to apply (e.g., "remove the background", "change the sky to sunset")'),
224
224
  model: z.string().optional().describe('Model identifier — REQUIRED in practice: pick a specific model, do NOT omit (omitting = Smart Select auto-pick, which we avoid). Many text-to-image ids double as editors: the server auto-routes a base id to its editing variant when source_images is present (e.g. "gpt-image-2" → gpt-image-2/edit, "nano-banana-2" → nano-banana-2/edit) — passing the bare id is fine, no need to hunt for the "/edit" suffix yourself. BUT this only works for models that actually have a registered edit variant (most flagship models do: gpt-image, nano-banana, flux-2, seedream, qwen, wan, grok-imagine, kling-image families). Models with none (Midjourney, Flux Pro/Ultra, Imagen4, Ideogram, Recraft, Higgsfield Soul, Krea, Dreamina, and others) silently ignore source_images if passed here instead of erroring — if unsure, confirm the model appears in `list_models type="image_editing"` before trusting a bare id, or just use a known-safe default: "nano-banana-pro/edit" (best general prompt editor), "gpt-image-2" (photoreal, strong text), or "flux-2/edit".'),
@@ -230,22 +230,23 @@ function registerGenerateTools(server, client, options = {}) {
230
230
  moodboard_id: z.string().optional().describe('Moodboard ID whose master_prompt and style_guide should be applied.'),
231
231
  enable_web_search: z.boolean().optional().describe('Enable web-search grounding. Default: false'),
232
232
  resolution: z.string().optional().describe('Image resolution tier: "1K" / "2K" / "3K" / "4K". Model-dependent — call list_models and read supported_resolutions. Default: "1K" for most edit models.'),
233
+ preset_id: z.string().optional().describe('Exact preset ID from list_presets type="image_edit" to apply an image-editing preset. If the user requests a preset, resolve and pass it; do not silently omit it.'),
233
234
  cinematic: CINEMATIC_SCHEMA,
234
235
  skip_color_palette: z.boolean().optional().describe('Opt this single call OUT of the account\'s active Color DNA palette (see list_color_palettes / activate_color_palette). By default, if the user has an active palette it strict-grades every generation automatically — pass true only when the user explicitly wants this one edit ungraded.'),
235
236
  project_id: projectIdField,
236
237
  session_id: sessionIdField
237
238
  },
238
- async ({ prompt, model, source_images, aspect_ratio, enhance_prompt = false, num_images, visual_dna_ids, moodboard_id, enable_web_search, resolution, cinematic, skip_color_palette, project_id, session_id }) => {
239
+ async ({ prompt, model, source_images, aspect_ratio, enhance_prompt = false, num_images, visual_dna_ids, moodboard_id, enable_web_search, resolution, preset_id, cinematic, skip_color_palette, project_id, session_id }) => {
239
240
  model = await canonicalModelId(client, model, 'image_editing'); // lenient id resolution ("z-image" → "z-image/turbo")
240
241
  const gen = await client.post('/v1/generate/image-edit', {
241
242
  prompt, model, source_images, aspect_ratio, enhance_prompt, num_images,
242
- visual_dna_ids, moodboard_id, enable_web_search, resolution, cinematic, skip_color_palette, project_id, session_id
243
+ visual_dna_ids, moodboard_id, enable_web_search, resolution, preset_id, cinematic, skip_color_palette, project_id, session_id
243
244
  });
244
245
 
245
246
  if (ui()) return uiGenerating({
246
247
  tool: 'generate_image_edit', kind: 'image', gen, client, model, prompt,
247
248
  count: num_images,
248
- settings: imageSettings({ resolution, aspect_ratio, enhance_prompt, enable_web_search, visual_dna_ids, moodboard_id, cinematic }),
249
+ settings: imageSettings({ resolution, aspect_ratio, enhance_prompt, enable_web_search, visual_dna_ids, moodboard_id, preset_id, cinematic }),
249
250
  reference_image: source_images?.[0]
250
251
  });
251
252
 
@@ -12,9 +12,9 @@ function registerPresetTools(server, client, options = {}) {
12
12
  // ─── list_presets ──────────────────────────────────────────
13
13
  server.tool(
14
14
  'list_presets',
15
- 'List generation presets across image, video, music, and text-to-video catalogs. Presets bundle a specific prompt template + style direction that can be passed to a generation tool via its `preset_id` arg for a one-shot creative direction. Filter by `type` to narrow to a specific catalog. Returns id, name, description, thumbnail, category, and (for music) audio preview URL.',
15
+ 'List generation presets across image, image-editing, video, music, and text-to-video catalogs. Use this BEFORE generating whenever the user requests a preset, names a preset, or asks to use one of their/Kolbo presets. Resolve the requested name or choose the closest match from the correct `type`, then pass the returned exact `id` as `preset_id` on the generation tool. Never claim a preset was used unless that id is passed. Returns id, name, description, thumbnail, category, and (for music) audio preview URL.',
16
16
  {
17
- type: z.string().optional().describe('Filter by catalog: "image" | "video" | "music" | "text_to_video". Omit for all.')
17
+ type: z.string().optional().describe('Filter by catalog: "image" | "image_edit" | "video" | "music" | "text_to_video". Omit for all.')
18
18
  },
19
19
  async ({ type }) => {
20
20
  const params = new URLSearchParams();
@@ -30,7 +30,7 @@ function registerPresetTools(server, client, options = {}) {
30
30
  cap: 60,
31
31
  total: result.count || presets.length,
32
32
  extra: result.warning ? { warning: result.warning } : undefined,
33
- note: 'Filter with `type` (image | video | music | text_to_video) to see a focused set.',
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
36
  if (ui()) {