@kolbo/mcp 1.36.0 → 1.37.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/README.md +4 -0
- package/package.json +1 -1
- package/skill/GENERATED.md +1 -1
- package/skill/SKILL.md +19 -5
- package/skill/VERSION +1 -1
- package/skill/references/workflows/app-builder.md +140 -21
- package/skill/references/workflows/visual-dna.md +7 -0
- package/src/index.js +6 -2
- package/src/tools/agents.js +78 -0
- package/src/tools/generate.js +114 -14
- package/src/tools/presets.js +29 -0
package/README.md
CHANGED
|
@@ -230,12 +230,16 @@ Every generation tool also accepts an optional `project_id` arg that routes the
|
|
|
230
230
|
| `list_models` | Current model catalog with costs and capabilities |
|
|
231
231
|
| `list_voices` | TTS voices (presets + cloned) |
|
|
232
232
|
| `list_presets` | Generation presets across image/video/music/text-to-video catalogs |
|
|
233
|
+
| `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 |
|
|
233
234
|
| `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 |
|
|
234
235
|
| `move_session` | Move a session (generation, chat, transcription…) and ALL its media to another project |
|
|
235
236
|
| `create_doc` / `list_docs` / `get_doc` / `update_doc` / `share_doc` / `delete_doc` | AI Docs (Magic Pad): author project-scoped HTML documents, edit them, get public share links |
|
|
236
237
|
| `generate_character_sheet` | Generate a multi-angle character sheet from reference images (credits) → pass URL to create_visual_dna for stronger character consistency |
|
|
237
238
|
| `list_visual_dna_folders` / `create_visual_dna_folder` / `update_visual_dna_folder` / `delete_visual_dna_folder` / `move_visual_dna_to_folder` | Organize Visual DNA characters into user folders (create/rename/recolor/delete + move DNAs in/out) |
|
|
238
239
|
| `create_project` / `update_project` / `archive_project` / `unarchive_project` | Project lifecycle (create/rename/describe/archive; deletion stays in-app) |
|
|
240
|
+
| `list_agents` / `create_agent` / `update_agent` / `delete_agent` | Custom chat agents (reusable named personas; `description` is the system instruction) |
|
|
241
|
+
| `get_creative_director_status` | Re-check a Creative Director batch by generation_id until all parallel scenes finish (use after a `_timed_out` Director run) |
|
|
242
|
+
| `app_builder_list_projects` / `app_builder_create_session` / `app_builder_generate_app` / `app_builder_edit_app` / `app_builder_get_build_status` / `app_builder_get_session` / `app_builder_list_sessions` / `app_builder_list_generations` / `app_builder_delete_session` | **App Builder (preview)** — full React app generation that auto-provisions a GitHub repo + Supabase DB + a live deployment URL. Different surface from the generation tools (4-layer mental model: project → session → app → end-users). The app embeds `@kolbo/app-sdk` so visitors can call Kolbo AI from the browser; the OWNER pays for visitor AI usage. Read the App Builder workflow doc before the first turn. |
|
|
239
243
|
| `list_sessions` | Enumerate sessions across all types, filterable by project and type |
|
|
240
244
|
| `add_project_context` / `list_project_context` / `delete_project_context` / `get_project_profile` / `regenerate_project_profile` | Project knowledge base (RAG): feed scripts/URLs/notes, read the synthesized living brief |
|
|
241
245
|
| `create_moodboard` / `update_moodboard` / `delete_moodboard` | Build/edit moodboards from image URLs (AI style analysis → master prompt) |
|
package/package.json
CHANGED
package/skill/GENERATED.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# AUTO-GENERATED — do not edit
|
|
2
2
|
|
|
3
3
|
This skill/ tree is mirrored from kolbo-code (the single source of truth)
|
|
4
|
-
by .github/workflows/sync-skill-to-plugin.yml — synced from kolbo-code@
|
|
4
|
+
by .github/workflows/sync-skill-to-plugin.yml — synced from kolbo-code@82f9072.
|
|
5
5
|
|
|
6
6
|
It is the skill that 'npx @kolbo/mcp install' deploys into the user's agent.
|
|
7
7
|
To change it, edit packages/opencode/skills/kolbo/ in kolbo-code and push;
|
package/skill/SKILL.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
version: 0.7.
|
|
2
|
+
version: 0.7.1
|
|
3
3
|
name: kolbo
|
|
4
4
|
description: |
|
|
5
5
|
Generate, edit, or analyze creative media via the Kolbo AI MCP server:
|
|
@@ -83,7 +83,7 @@ For multi-scene / batch work this pairs with `generate_creative_director` (see b
|
|
|
83
83
|
| **Transcribe** or **analyze** audio/video | `references/workflows/transcription.md` |
|
|
84
84
|
| **Scrape brand/product info** before generating + persist as `.kolbo/brand-kits/<slug>.md` | `references/workflows/research-first.md` |
|
|
85
85
|
| Browse, manage, or present existing **media library** items | `references/workflows/media-library.md` |
|
|
86
|
-
| Use the **App Builder** (React app generation) | `references/workflows/app-builder.md` |
|
|
86
|
+
| Use the **App Builder** (React app generation) — preview, ask first | `references/workflows/app-builder.md` |
|
|
87
87
|
| Confirm **cost** or validate **resolution / aspect / duration** against model caps | `references/workflows/cost-and-validation.md` |
|
|
88
88
|
| Hit an **auth / MCP / 429** issue | `references/workflows/troubleshooting.md` |
|
|
89
89
|
|
|
@@ -113,7 +113,7 @@ Each `references/models/*.md` mirrors the matching skill prompt in `kolbo-api/sr
|
|
|
113
113
|
|------|---------|
|
|
114
114
|
| `list_models` / `list_voices` / `check_credits` / `get_generation_status` / `get_session_usage` | Discovery + status |
|
|
115
115
|
| `upload_media` / `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` |
|
|
116
|
-
| `create_visual_dna` / `list_visual_dnas` / `get_visual_dna` / `delete_visual_dna` / `*_visual_dna_folder` (5 folder tools) | Visual DNA + character folders — see `workflows/visual-dna.md` |
|
|
116
|
+
| `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` |
|
|
117
117
|
| `list_moodboards` / `get_moodboard` / `list_presets` | Style overlays |
|
|
118
118
|
| `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. |
|
|
119
119
|
| `list_projects` / `move_session` | Projects: resolve a project NAME → the `project_id` you pass on generation/upload/doc calls; `move_session` relocates a whole session + its media when work landed in the wrong project. NOT the same as `app_builder_list_projects`. See "Projects — Where Work Lands" below. |
|
|
@@ -124,7 +124,7 @@ Each `references/models/*.md` mirrors the matching skill prompt in `kolbo-api/sr
|
|
|
124
124
|
| `trim_video` | Frame-accurate trim of a Kolbo-hosted video (tool waits and returns the URL). `edit_video` also gained `remove_background`. |
|
|
125
125
|
| `create_doc` / `list_docs` / `get_doc` / `update_doc` / `share_doc` / `delete_doc` | AI Docs (Magic Pad): YOU author full HTML documents (plans, briefs, scripts, research) saved into the user's project, editable in the Kolbo app. `share_doc` returns a public link. `update_doc` content replaces the WHOLE doc — `get_doc` first. |
|
|
126
126
|
| `chat_send_message` / `chat_list_conversations` / `chat_get_messages` | Kolbo chat with optional `media_urls` (up to 10 per call) |
|
|
127
|
-
| `app_builder_*` (9 tools) | Full React app generation — see `workflows/app-builder.md` |
|
|
127
|
+
| `app_builder_*` (9 tools) | Full React app generation (preview) — see `workflows/app-builder.md`. Different surface from generation tools: produces a deployed app with GitHub repo + Supabase DB + live URL. Don't confuse `session_id` types, don't confuse `app_builder_generate_app` with `generate_image`. |
|
|
128
128
|
| `publish_html_artifact` | Publish HTML / SVG / Mermaid to `sites.kolbo.ai`. Server dedupes by content hash. Strict CSP. |
|
|
129
129
|
|
|
130
130
|
## ⚠️ If the User Names a Tool, USE THAT TOOL (HARD RULE)
|
|
@@ -175,9 +175,20 @@ Everything in Kolbo — sessions, generations, media, docs — lives inside a PR
|
|
|
175
175
|
|
|
176
176
|
1. **User names a project** ("in my Acme project", "for the film") → call `list_projects` ONCE to resolve the name to an ObjectId, then pass that id as `project_id` on **EVERY** subsequent `generate_*` / `upload_media` / `create_doc` / `chat_send_message` call in the conversation. It is **per-call, NOT sticky** — any call that omits it silently lands in the default "API Generations" bucket (`is_default: true`).
|
|
177
177
|
2. **No project mentioned** → omit `project_id`; the default bucket is correct. Don't ask unless intent is ambiguous.
|
|
178
|
-
3. **`list_projects` ≠ `app_builder_list_projects`** — the
|
|
178
|
+
3. **`list_projects` ≠ `app_builder_list_projects`** — both return the SAME Kolbo projects (different endpoints, different shapes). Use `list_projects` for generation/media/chat flows; use `app_builder_list_projects` when scoping an App Builder session. Never substitute one for the other.
|
|
179
179
|
4. **Work landed in the wrong project? MOVE it, never regenerate**: `move_session` relocates a whole session + all its media (works for any session type — the `session_id` from generation responses, chats, transcriptions); `move_media` / `bulk_move_media` / `move_folder_contents` relocate individual media items.
|
|
180
180
|
|
|
181
|
+
## 🧱 App Builder ≠ regular Kolbo project (4-layer mental model)
|
|
182
|
+
|
|
183
|
+
App Builder is a separate surface that builds and runs full React apps. It shares the **Kolbo project** layer with regular generations (a single Kolbo project can hold BOTH app-builder sessions AND image/video sessions), but every layer above it is distinct. The single biggest routing mistake is treating an `app_builder_generate_app` call as a media-generation call, or passing an App Builder `session_id` to `generate_image`. Read `workflows/app-builder.md` before the first App Builder turn — the four layers are:
|
|
184
|
+
|
|
185
|
+
1. **Kolbo project** (shared) — `list_projects` / `app_builder_list_projects`
|
|
186
|
+
2. **App Builder session** — `app_builder_create_session(project_id)` → returns a `session_id` distinct from any chat/generation session id
|
|
187
|
+
3. **The app itself** — `deployment_url` + `github_repo_url` + `supabase_url` + `supabase_anon_key` (auto-provisioned on first successful build)
|
|
188
|
+
4. **App end-users** — per-app JWT, hit `/api/apps/:appId/ai/*`; the OWNER of the app pays for their AI usage (NOT surfaced as MCP tools — owned by `@kolbo/app-sdk` in the deployed bundle)
|
|
189
|
+
|
|
190
|
+
Heuristic: deliverable is a **single file** (image / video / doc / artifact) → regular tools. Deliverable is "**an app** that does X" or "my **users** can do Y" → App Builder. App Builder is **preview** — don't proactively advertise; if the user asks, load `workflows/app-builder.md` and confirm they're an opted-in owner.
|
|
191
|
+
|
|
181
192
|
## Cost Awareness — Quick Rules
|
|
182
193
|
|
|
183
194
|
Full tables + formulas in `references/workflows/cost-and-validation.md`. Quick rules:
|
|
@@ -264,6 +275,9 @@ Chat renders markdown natively. `` = inline image. `[label](url)` = l
|
|
|
264
275
|
|
|
265
276
|
Avoid bare URL dumps and HTML `<table>` grids — canvas already provides a gallery.
|
|
266
277
|
|
|
278
|
+
- **3D models (`generate_3d`)**: NEVER paste the raw GLB/FBX/OBJ/USDZ file URLs as a link list. The result renders as a card with a live 3D preview + Download buttons ("View in Canvas"). Just say the model is ready and describe it — the user grabs formats from the card.
|
|
279
|
+
- **NEVER surface a raw provider domain** (e.g. `*.fal.media`, `replicate.delivery`) in chat or `.kolbo/production.md`. All Kolbo outputs are Kolbo-hosted; if you ever see a provider URL in a result, treat it as a bug — report it, don't echo it.
|
|
280
|
+
|
|
267
281
|
**After `generate_creative_director` completes** — share results as individual URLs, one per scene. Do NOT create an HTML grid artifact.
|
|
268
282
|
|
|
269
283
|
**Always** record every URL in `.kolbo/production.md` — see `references/workflows/production-log.md`.
|
package/skill/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.7.
|
|
1
|
+
0.7.1
|
|
@@ -1,41 +1,160 @@
|
|
|
1
1
|
# App Builder
|
|
2
2
|
|
|
3
|
-
Load this file when the user wants to build
|
|
3
|
+
Load this file when the user wants to **build, edit, or iterate on a full React app** with Kolbo's App Builder — "build me a todo app", "add dark mode to my app", "give me the GitHub repo", "I want my app to use Kolbo image generation from the user's browser". Do NOT load this for single-asset generation requests (use the regular `generate_*` tools).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 🚦 Preview / not live to the public yet
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
App Builder is in **preview** as of this writing. The MCP tools (`app_builder_*`) are wired and the backend runs end-to-end — owners can create projects, generate apps, edit them, get GitHub repos + Supabase DBs + a live deployment URL. The user-facing launch (in-app promo, self-serve public onboarding) is still pending. So:
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- Fires the build in the background, polls until `build_status === "deployed"` (up to 5 min)
|
|
13
|
-
- Always surface the `deployment_url` to the user: **"Your app is live at: [url]"**
|
|
14
|
-
4. **Iterate**: `app_builder_list_generations` → get `generation_id` → `app_builder_edit_app` with natural language instruction
|
|
9
|
+
- **You CAN call these tools** for owners / opted-in users — the flow works.
|
|
10
|
+
- **Don't proactively advertise App Builder** to users who haven't asked for it (no marketing pitch about "build apps with AI").
|
|
11
|
+
- If the user pushes back ("is this ready?"), say it's a working preview and ask if they want to try it.
|
|
15
12
|
|
|
16
|
-
|
|
13
|
+
## 🧠 Mental model — 4 layers, easy to conflate
|
|
17
14
|
|
|
18
|
-
|
|
15
|
+
This is the single biggest source of confusion. App Builder is NOT a generation tool. It's a **complete app factory** with its own lifecycle, its own runtime, and its own end-user identity model. Lock this in once:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
19
|
+
│ Layer 1 — KOLBO PROJECT (Kolbo account) │
|
|
20
|
+
│ The same project you use for generations/media/chat. │
|
|
21
|
+
│ `list_projects` returns these. `app_builder_list_projects` ALSO │
|
|
22
|
+
│ returns these (same data, different endpoint shape). A project can │
|
|
23
|
+
│ hold BOTH regular sessions AND app-builder sessions at the same time. │
|
|
24
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
25
|
+
│
|
|
26
|
+
▼
|
|
27
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
28
|
+
│ Layer 2 — APP BUILDER SESSION (one session = one app being built) │
|
|
29
|
+
│ Created with `app_builder_create_session` against a Kolbo project. │
|
|
30
|
+
│ Has a `session_id` that is COMPLETELY DIFFERENT from the │
|
|
31
|
+
│ `session_id` returned by `chat_send_message` or any `generate_*` │
|
|
32
|
+
│ tool. Don't mix them. │
|
|
33
|
+
│ A session accumulates "generations" — each one is a build / edit. │
|
|
34
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
35
|
+
│
|
|
36
|
+
▼
|
|
37
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
38
|
+
│ Layer 3 — THE APP ITSELF (a deployed React app) │
|
|
39
|
+
│ After the first successful generation the session has a real app: │
|
|
40
|
+
│ • `deployment_url` — the live, hosted URL (e.g. apps.kolbo.ai/…) │
|
|
41
|
+
│ • `github_repo_url` — auto-provisioned GitHub repo (clone, hack) │
|
|
42
|
+
│ • `supabase_url` + `supabase_anon_key` — auto-provisioned DB │
|
|
43
|
+
│ The app embeds `@kolbo/app-sdk` so it can call Kolbo AI directly │
|
|
44
|
+
│ from the user's browser — see Layer 4. │
|
|
45
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
46
|
+
│
|
|
47
|
+
▼
|
|
48
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
49
|
+
│ Layer 4 — APP END-USERS (real humans visiting the deployed app) │
|
|
50
|
+
│ Each visitor authenticates with a PER-APP JWT — NOT a Kolbo user │
|
|
51
|
+
│ account, NOT an API key. They hit `/api/apps/:appId/ai/*` endpoints │
|
|
52
|
+
│ that proxy Kolbo AI. The OWNER of the app (the person who built it) │
|
|
53
|
+
│ is the one whose credits get billed for every generation. This is a │
|
|
54
|
+
│ completely different surface from the MCP — there are no MCP tools │
|
|
55
|
+
│ for app end-users; the SDK lives in the generated app's bundle. │
|
|
56
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**The five easy confusions** — re-read this before every App Builder turn:
|
|
60
|
+
|
|
61
|
+
1. `list_projects` and `app_builder_list_projects` return the **same Kolbo projects** — different endpoints, different shapes, but the same data. Use whichever the current flow needs.
|
|
62
|
+
2. `session_id` from `app_builder_create_session` is NOT the same as `session_id` from `chat_send_message`. Don't pass an App Builder session id to a `generate_*` tool or vice versa.
|
|
63
|
+
3. `app_builder_generate_app` is NOT a media-generation tool. It kicks off a full multi-minute app build that produces code + infra + a deployment URL. Never confuse it with `generate_image` / `generate_video`.
|
|
64
|
+
4. The deployed app is its OWN product. Visiting it is not "using Kolbo"; the visitor's identity is per-app, not Kolbo.
|
|
65
|
+
5. The OWNER pays. App end-users consume credits from the owner's account. Confirm the owner understands this before they share their app publicly.
|
|
66
|
+
|
|
67
|
+
## When to route here
|
|
68
|
+
|
|
69
|
+
The user wants to **build or modify a full app**, not generate a single asset:
|
|
70
|
+
|
|
71
|
+
- "Build me a [todo / landing page / dashboard / form / marketplace / internal tool / mobile-friendly site] app"
|
|
72
|
+
- "Make me a [SaaS / site / app] that does X"
|
|
73
|
+
- "I want an app where my users can [Y]" (the visitor-runtime question — usually means App Builder + Layer 4)
|
|
74
|
+
- "Add [dark mode / a contact form / a chart / auth / a settings page] to my app"
|
|
75
|
+
- "Show me the GitHub repo" / "I want to clone it" / "Connect my own Supabase"
|
|
76
|
+
- "Is my app deployed?" / "My build failed" / "Why is the deployment URL blank?"
|
|
77
|
+
- "What apps have I built?" / "Show me my App Builder sessions"
|
|
78
|
+
- "Delete this app" (always confirm — see Rules)
|
|
79
|
+
|
|
80
|
+
## When NOT to route here
|
|
81
|
+
|
|
82
|
+
The user wants a single asset or a Kolbo-internal workflow. Stay on the regular `generate_*` / media / doc / chat tools:
|
|
83
|
+
|
|
84
|
+
- "Generate an image / video / song / voice / 3D model" → regular tools (the output lives in Kolbo)
|
|
85
|
+
- "Edit this image" → `generate_image_edit`
|
|
86
|
+
- "Write me a [plan / brief / script]" → `create_doc` (AI Docs)
|
|
87
|
+
- "Build a presentation / landing page artifact" → `publish_html_artifact`
|
|
88
|
+
- "I want a campaign with 4 product shots" → `generate_creative_director`
|
|
89
|
+
- "Make me a Kolbo character consistent across images" → Visual DNA tools
|
|
90
|
+
|
|
91
|
+
**Heuristic:** if the deliverable is a single file (image / video / doc / page artifact), it's NOT App Builder. If the deliverable is "an app that does X" or "my users can do Y in it", it IS App Builder.
|
|
92
|
+
|
|
93
|
+
## Standard workflow (build + iterate)
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
1. app_builder_list_projects → pick a Kolbo project (or note "default bucket")
|
|
97
|
+
2. app_builder_create_session(project_id) → returns session_id
|
|
98
|
+
3. app_builder_generate_app(session_id,
|
|
99
|
+
prompt="...") → blocks until build_status="deployed"
|
|
100
|
+
(up to ~5 min — surface the deployment_url!)
|
|
101
|
+
4. (later) app_builder_list_generations(session_id)
|
|
102
|
+
→ app_builder_edit_app(session_id,
|
|
103
|
+
generation_id=<latest>,
|
|
104
|
+
edit_prompt="add dark mode")
|
|
105
|
+
→ blocks until redeployed
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Surface the deployment URL on success** — always show the user: "Your app is live at: `<deployment_url>`". That's the whole payoff. If `deployment_url` is null after a successful build, treat it as a build failure and call `app_builder_get_build_status` to investigate.
|
|
109
|
+
|
|
110
|
+
**No manual polling needed.** `generate_app` and `edit_app` block until the build completes (or throws). If a build times out, the user can resume by calling `app_builder_get_build_status(session_id)` — DO NOT auto-retry; wait for the user's call.
|
|
111
|
+
|
|
112
|
+
## Local dev + handoff
|
|
19
113
|
|
|
20
114
|
If the user wants to run the app locally or connect to the database directly:
|
|
115
|
+
|
|
21
116
|
```
|
|
22
117
|
app_builder_get_session(session_id) → returns:
|
|
23
|
-
github_repo_url
|
|
24
|
-
supabase_url
|
|
25
|
-
supabase_anon_key
|
|
118
|
+
github_repo_url → git clone <url> && npm install && npm run dev
|
|
119
|
+
supabase_url → paste into .env as NEXT_PUBLIC_SUPABASE_URL
|
|
120
|
+
supabase_anon_key → paste into .env as NEXT_PUBLIC_SUPABASE_ANON_KEY
|
|
121
|
+
deployment_url → the currently deployed URL
|
|
26
122
|
```
|
|
27
123
|
|
|
28
|
-
|
|
124
|
+
The repo is a standard Next.js + Tailwind + shadcn/ui + Supabase app. The bundled `@kolbo/app-sdk` (`kolbo.auth`, `kolbo.data`, `kolbo.storage`, `kolbo.ai`) is what lets the app call Kolbo AI without exposing a full-access API key.
|
|
125
|
+
|
|
126
|
+
## End-user runtime — when the user asks "can my visitors do X in the app?"
|
|
29
127
|
|
|
30
|
-
|
|
31
|
-
- **On build timeout** (rare): use `app_builder_get_build_status` to check manually, then continue or report.
|
|
128
|
+
This is Layer 4 — the deployed app's AI proxy. The MCP doesn't have direct tools for it, but you should KNOW it exists so you can answer:
|
|
32
129
|
|
|
33
|
-
|
|
130
|
+
- **What end-users see:** the deployed React app at `deployment_url`. They authenticate with a per-app JWT (auto-issued; the SDK handles it).
|
|
131
|
+
- **What they can do:** anything the app is coded to do, including calling Kolbo AI (image / video / chat / etc.) via `@kolbo/app-sdk`. The SDK hits `/api/apps/:appId/ai/*` proxy endpoints server-side; the OWNER's account is billed for those calls.
|
|
132
|
+
- **Spend controls:** the owner can set a per-app spend policy and per-user rate limits on the AI proxy (see `appAI/spendPolicy.js`). If the user says "my app is burning credits" or "someone is abusing my app", route them to that dashboard.
|
|
133
|
+
- **What you CAN'T do from the MCP:** inspect an end-user's session inside the app, view their chat history, or read their app-scoped data. Those live in the Kolbo dashboard under the App's settings.
|
|
34
134
|
|
|
35
135
|
## Routing examples
|
|
36
136
|
|
|
37
137
|
| User says | Sequence |
|
|
38
138
|
|---|---|
|
|
39
|
-
| "Build me a todo app" / "Make a landing page with waitlist" | `app_builder_list_projects` → `app_builder_create_session` → `app_builder_generate_app` → show `deployment_url` |
|
|
40
|
-
| "Add dark mode to my app" / "Add a contact form" | `app_builder_list_generations` → `app_builder_edit_app` |
|
|
41
|
-
| "Give me the GitHub repo" / "
|
|
139
|
+
| "Build me a todo app" / "Make me a SaaS landing page with waitlist" | `app_builder_list_projects` → `app_builder_create_session` → `app_builder_generate_app` → show `deployment_url` |
|
|
140
|
+
| "Add dark mode to my app" / "Add a contact form / chart / auth" | `app_builder_list_generations` → grab latest `generation_id` → `app_builder_edit_app` → show `deployment_url` |
|
|
141
|
+
| "Give me the GitHub repo" / "I want to clone it and run it locally" | `app_builder_get_session` → return `github_repo_url` (+ `supabase_url` + `supabase_anon_key` if they ask) |
|
|
142
|
+
| "Show me my apps" / "What apps have I built?" | `app_builder_list_projects` → for each, `app_builder_list_sessions` → surface session list |
|
|
143
|
+
| "Is my build still running?" / "My build timed out" | `app_builder_get_build_status(session_id)` → resume |
|
|
144
|
+
| "Delete this app" | **ALWAYS CONFIRM FIRST** — see Rules. Then `app_builder_delete_session` |
|
|
145
|
+
| "My app visitors are burning my credits" | Not an MCP action — point them at the App → Settings → Spend Policy dashboard |
|
|
146
|
+
| "I want my app users to be able to generate images inside the app" | Tell them the `@kolbo/app-sdk` `kolbo.ai.generateImage` namespace handles it server-side via `/api/apps/:appId/ai/generate/image` — the OWNER pays |
|
|
147
|
+
|
|
148
|
+
## Hard rules
|
|
149
|
+
|
|
150
|
+
- **ALWAYS confirm before `app_builder_delete_session`** — permanently deletes the GitHub repo, Supabase DB (unless user-connected their own), deployed files, generation history, messages, and form submissions. IRREVERSIBLE.
|
|
151
|
+
- **On build timeout / failure:** use `app_builder_get_build_status(session_id)` and report the actual status to the user. DO NOT auto-retry the build.
|
|
152
|
+
- **Never invent a `deployment_url` or `github_repo_url`** — they come back from the tool. If null, the build didn't finish.
|
|
153
|
+
- **The owner pays for every AI call made by an app visitor.** Make sure the user understands this before they share the app publicly. Direct them to spend-policy settings if they need a cap.
|
|
154
|
+
- **Whitelabel works automatically** — the MCP client routes App Builder calls through the whitelabel API endpoints when configured.
|
|
155
|
+
- **Don't conflate `project_id` types.** A Kolbo project holds App Builder sessions; the App Builder session is a CHILD of that project. The deployed app has its OWN `:appId` (visible in `app_builder_get_session`) which is what the app-runtime uses to route AI calls — that's a different id namespace from the Kolbo `project_id`.
|
|
156
|
+
|
|
157
|
+
## Versioning note (MCP)
|
|
158
|
+
|
|
159
|
+
- The App Builder MCP surface is **stable as of `@kolbo/mcp@1.37.0`**. No renames or removals planned. New optional args ship as `npm version minor` per the standard rules.
|
|
160
|
+
- The kolbo-code skill mirrors this doc (canonical source: `kolbo-code/packages/opencode/skills/kolbo/references/workflows/app-builder.md`); a `chore(skill)` bot keeps them in sync.
|
|
@@ -242,3 +242,10 @@ Tools: `list_visual_dna_folders`, `create_visual_dna_folder` (`name`, optional h
|
|
|
242
242
|
- **Deleting a folder never deletes DNAs** — contents move back to root (`items_moved_to_root` in the response). Mention this instead of asking for confirmation on non-empty folders.
|
|
243
243
|
- **Creating many characters for one production?** Create the folder FIRST, then `move_visual_dna_to_folder` each DNA right after `create_visual_dna` — don't leave a big cast unsorted at root.
|
|
244
244
|
- To list a folder's contents: `list_visual_dnas` and filter by each profile's `folder_id` (there is no server-side folder filter).
|
|
245
|
+
|
|
246
|
+
## Character sheet — offer it for character DNAs
|
|
247
|
+
|
|
248
|
+
`generate_character_sheet` builds a multi-angle turnaround from reference image URLs — the strongest consistency booster for a character DNA. It CHARGES CREDITS, so:
|
|
249
|
+
- When the user is about to create a **character** DNA, proactively OFFER it: "want me to generate a character sheet first? It makes the character far more consistent and costs a few credits." Run it only on a yes.
|
|
250
|
+
- Flow: `generate_character_sheet {image_urls}` → show the sheet → `create_visual_dna {name, images, character_sheet_url: <url>}`.
|
|
251
|
+
- For non-character DNAs (style/product/environment), skip it.
|
package/src/index.js
CHANGED
|
@@ -69,6 +69,7 @@ const { registerPresetTools } = require('./tools/presets');
|
|
|
69
69
|
const { registerAppBuilderTools } = require('./tools/app_builder');
|
|
70
70
|
const { registerArtifactTools } = require('./tools/artifacts');
|
|
71
71
|
const { registerProjectTools } = require('./tools/projects');
|
|
72
|
+
const { registerAgentTools } = require('./tools/agents');
|
|
72
73
|
const { registerDocTools } = require('./tools/docs');
|
|
73
74
|
const { registerVoiceTools } = require('./tools/voices');
|
|
74
75
|
const { registerMusicLibraryTools } = require('./tools/music_library');
|
|
@@ -112,10 +113,12 @@ function createServer(opts = {}) {
|
|
|
112
113
|
'PROJECT CONTRACT (read this before generating anything):',
|
|
113
114
|
'Everything in Kolbo lives inside a PROJECT — sessions, generations, and media are all project-scoped.',
|
|
114
115
|
'1. When the user names a project ("in my Acme project", "for the summer campaign"), call `list_projects` ONCE to resolve the name to an id, then pass that id as `project_id` on EVERY subsequent generate_* / chat_send_message / upload_media call in the conversation. The target project is per-call, NOT sticky — any call that omits `project_id` silently lands in the default "API Generations" bucket (flagged is_default:true), which users experience as their work going to the wrong project.',
|
|
115
|
-
'2. `list_projects` lists the user\'s platform projects (for generations/media/chat). `app_builder_list_projects` is a DIFFERENT tool that scopes App Builder coding sessions only — never use one where the other is meant.',
|
|
116
|
+
'2. `list_projects` lists the user\'s platform projects (for generations/media/chat). `app_builder_list_projects` is a DIFFERENT tool that scopes App Builder coding sessions only — never use one where the other is meant (both return the same projects but via different endpoints, different shapes). The App Builder surface (project → session → deployed app with GitHub+Supabase+live URL → end-users who hit `/api/apps/:appId/ai/*`) is a SEPARATE world from the generation tools — `app_builder_generate_app` is NOT a media-generation call, and an App Builder `session_id` is NOT interchangeable with a chat/generation `session_id`. Read `skill/references/workflows/app-builder.md` before the first App Builder turn; App Builder is preview-only, do not proactively advertise it.',
|
|
116
117
|
'3. Misplaced work is fixable: `move_media` / `bulk_move_media` / `move_folder_contents` move media items between projects; `move_session` moves a whole session (plus its media) to another project. If the user says a generation landed in the wrong project, move it rather than regenerating.',
|
|
117
118
|
'4. If the user has not mentioned any project, omit `project_id` — the default bucket is correct in that case. Do not ask which project to use unless the user\'s intent is ambiguous.',
|
|
118
|
-
'5. Written deliverables (plans, briefs, scripts, research summaries) can live in Kolbo too: author them as AI Docs with `create_doc` (project-scoped, editable in the app, shareable via `share_doc`).'
|
|
119
|
+
'5. Written deliverables (plans, briefs, scripts, research summaries) can live in Kolbo too: author them as AI Docs with `create_doc` (project-scoped, editable in the app, shareable via `share_doc`).',
|
|
120
|
+
'6. DIRECTOR / BATCH JOBS: generate_creative_director runs its scenes (image OR video) in parallel and only reports state="completed" once EVERY scene is terminal. Video batches can take many minutes. If the tool returns `_timed_out:true`, the batch is STILL RUNNING on the server — call `get_creative_director_status` with the returned generation_id and keep checking until state="completed" to collect all scene outputs. NEVER conclude a Director run failed and fall back to plain generate_image/generate_video without first checking status — doing so wastes the user\'s credits by paying twice. If scenes already carry image_urls/video_urls, they are done; do not regenerate.',
|
|
121
|
+
'7. SESSION CONTINUITY: keep one workflow in ONE session. chat_send_message and the generation tools return a `session_id` — for follow-ups, refinements, retries, or additional steps on the SAME task/theme, pass that same `session_id` back on the next call instead of starting fresh. Only OMIT session_id (start a new session) when the user genuinely switches to an unrelated task. Do not open a new conversation/session for every message of the same workflow — it fragments the user\'s history and loses context.'
|
|
119
122
|
].join('\n')
|
|
120
123
|
});
|
|
121
124
|
|
|
@@ -135,6 +138,7 @@ function createServer(opts = {}) {
|
|
|
135
138
|
registerAppBuilderTools(server, client, toolOptions);
|
|
136
139
|
registerArtifactTools(server, client, toolOptions);
|
|
137
140
|
registerProjectTools(server, client, toolOptions);
|
|
141
|
+
registerAgentTools(server, client, toolOptions);
|
|
138
142
|
registerDocTools(server, client, toolOptions);
|
|
139
143
|
registerMusicLibraryTools(server, client, toolOptions);
|
|
140
144
|
registerStockLibraryTools(server, client, toolOptions);
|
|
@@ -0,0 +1,78 @@
|
|
|
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
|
+
const { z } = require('zod');
|
|
7
|
+
|
|
8
|
+
function registerAgentTools(server, client) {
|
|
9
|
+
// ─── list_agents ───────────────────────────────────────────
|
|
10
|
+
server.tool(
|
|
11
|
+
'list_agents',
|
|
12
|
+
'List the user\'s custom chat agents (personal + any global/org preset agents visible to them). A custom agent is a reusable, named persona for the chat tool — its `description` is the system instruction the model adopts. Use this to resolve an agent NAME the user mentioned into its id, or to show what agents exist. Returns id, name, description, emoji, is_global. Personal agents (is_global:false) are editable/deletable; global presets are not.',
|
|
13
|
+
{ search: z.string().optional().describe('Optional case-insensitive name filter.') },
|
|
14
|
+
async ({ search }) => {
|
|
15
|
+
const qs = search ? `?search=${encodeURIComponent(search)}` : '';
|
|
16
|
+
const result = await client.get(`/v1/agents${qs}`);
|
|
17
|
+
return { content: [{ type: 'text', text: JSON.stringify({
|
|
18
|
+
agents: result.agents || [],
|
|
19
|
+
count: result.count || (result.agents || []).length
|
|
20
|
+
}, null, 2) }] };
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
// ─── create_agent ──────────────────────────────────────────
|
|
25
|
+
server.tool(
|
|
26
|
+
'create_agent',
|
|
27
|
+
'Create a reusable custom chat agent (a named persona for the chat tool). The `description` IS the agent\'s system instruction — write it as the persona + behavior you want ("You are a senior creative director. Turn any brief into a structured shot list…"). Use when the user wants a persistent, reusable assistant ("make me a creative-director agent", "set up a support-triage bot"). For a ONE-OFF persona on a single conversation, pass `system_prompt` to chat_send_message instead — no need to create an agent. Plan limits apply (server rejects when the custom-agent cap is reached).',
|
|
28
|
+
{
|
|
29
|
+
name: z.string().optional().describe('Agent name. If omitted, a name is generated from the description.'),
|
|
30
|
+
description: z.string().describe('The agent persona + instructions (max 2000 chars). This becomes the system prompt the model adopts in every conversation that uses the agent.'),
|
|
31
|
+
emoji: z.string().optional().describe('Optional emoji avatar (auto-picked if omitted).'),
|
|
32
|
+
thumbnail: z.string().optional().describe('Optional thumbnail image URL.')
|
|
33
|
+
},
|
|
34
|
+
async ({ name, description, emoji, thumbnail }) => {
|
|
35
|
+
const body = { description };
|
|
36
|
+
if (name !== undefined) body.name = name;
|
|
37
|
+
if (emoji !== undefined) body.emoji = emoji;
|
|
38
|
+
if (thumbnail !== undefined) body.thumbnail = thumbnail;
|
|
39
|
+
const result = await client.post('/v1/agents', body);
|
|
40
|
+
return { content: [{ type: 'text', text: JSON.stringify({ agent: result.agent, _hint: 'Reuse this agent by selecting it in the chat tool. Its description is the system instruction applied to every conversation using it.' }, null, 2) }] };
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// ─── update_agent ──────────────────────────────────────────
|
|
45
|
+
server.tool(
|
|
46
|
+
'update_agent',
|
|
47
|
+
'Update a custom chat agent\'s name, description (persona/instructions), or emoji/thumbnail. Only personal agents you own can be edited — global preset agents are protected. Resolve the agent id with list_agents first.',
|
|
48
|
+
{
|
|
49
|
+
agent_id: z.string().describe('Agent id (from list_agents).'),
|
|
50
|
+
name: z.string().optional().describe('New name.'),
|
|
51
|
+
description: z.string().optional().describe('New persona/instructions (replaces the old description; max 2000 chars).'),
|
|
52
|
+
emoji: z.string().optional().describe('New emoji avatar.'),
|
|
53
|
+
thumbnail: z.string().optional().describe('New thumbnail image URL.')
|
|
54
|
+
},
|
|
55
|
+
async ({ agent_id, name, description, emoji, thumbnail }) => {
|
|
56
|
+
const body = {};
|
|
57
|
+
if (name !== undefined) body.name = name;
|
|
58
|
+
if (description !== undefined) body.description = description;
|
|
59
|
+
if (emoji !== undefined) body.emoji = emoji;
|
|
60
|
+
if (thumbnail !== undefined) body.thumbnail = thumbnail;
|
|
61
|
+
const result = await client.put(`/v1/agents/${encodeURIComponent(agent_id)}`, body);
|
|
62
|
+
return { content: [{ type: 'text', text: JSON.stringify({ agent: result.agent }, null, 2) }] };
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// ─── delete_agent ──────────────────────────────────────────
|
|
67
|
+
server.tool(
|
|
68
|
+
'delete_agent',
|
|
69
|
+
'Delete a custom chat agent you own. Global preset agents cannot be deleted. This removes the agent config only — it does not touch any conversations that used it.',
|
|
70
|
+
{ agent_id: z.string().describe('Agent id (from list_agents).') },
|
|
71
|
+
async ({ agent_id }) => {
|
|
72
|
+
const result = await client.delete(`/v1/agents/${encodeURIComponent(agent_id)}`);
|
|
73
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = { registerAgentTools };
|
package/src/tools/generate.js
CHANGED
|
@@ -9,6 +9,34 @@ const { pollUntilDone } = require('../polling');
|
|
|
9
9
|
const { resolveToBuffer, creditFields, projectIdField, inlineImageBlocks, buildOpenUrl, uiGenerating, uiCompleted, appsEnabled } = require('./_shared');
|
|
10
10
|
const { UI, uiResult, canonicalModelId } = require('../apps');
|
|
11
11
|
|
|
12
|
+
// ─── Cinematic Dimensions schema (shared by generate_image + generate_image_edit) ───
|
|
13
|
+
// Kolbo's "Cinema mode": eight independent photographic dimensions, each an OPTIONAL
|
|
14
|
+
// preset id fetched from list_cinematic_presets. Any dimension left unset = "Auto" (the
|
|
15
|
+
// enhancer decides). Applies ONLY when the user explicitly wants a deliberate cinematic
|
|
16
|
+
// look — omit the whole object for a normal generation. Ids are validated server-side
|
|
17
|
+
// against their dimension (passing a lighting id in the camera slot is a 400). The
|
|
18
|
+
// selected fragments are woven into the prompt before enhancement.
|
|
19
|
+
// NOTE: dimensions are data-driven — always call list_cinematic_presets for the live set
|
|
20
|
+
// and valid ids; the keys below mirror the current catalog (Genre was removed).
|
|
21
|
+
const cinematicDim = (label) => z.string().nullable().optional()
|
|
22
|
+
.describe(`${label} preset id from list_cinematic_presets. Omit or null for Auto.`);
|
|
23
|
+
const CINEMATIC_SCHEMA = z.object({
|
|
24
|
+
camera: cinematicDim('Camera body/format (e.g. ARRI Alexa, 16mm film, smartphone)'),
|
|
25
|
+
lens: cinematicDim('Lens character (e.g. anamorphic, vintage prime, macro)'),
|
|
26
|
+
focal_length: cinematicDim('Focal length / field of view (e.g. 24mm wide, 85mm portrait)'),
|
|
27
|
+
aperture: cinematicDim('Aperture / depth of field (e.g. f/1.4 shallow, f/8 deep)'),
|
|
28
|
+
angle: cinematicDim('Camera angle (e.g. low angle, birds-eye, eye level)'),
|
|
29
|
+
shot_type: cinematicDim('Shot type / framing (e.g. close-up, wide shot, full shot)'),
|
|
30
|
+
color_palette: cinematicDim('Color grade (e.g. teal & orange, technicolor, classic B&W)'),
|
|
31
|
+
lighting: cinematicDim('Lighting technique (e.g. rim light, golden hour, low-key)'),
|
|
32
|
+
}).optional().describe(
|
|
33
|
+
'Cinema mode — an optional deliberate photographic treatment. Pass preset ids obtained from ' +
|
|
34
|
+
'list_cinematic_presets (at most one per dimension). Only include a dimension the user actually ' +
|
|
35
|
+
'wants; every omitted/null dimension is Auto (the enhancer completes the look in the spirit of the ' +
|
|
36
|
+
'ones you set). Omit the whole object entirely for an ordinary, non-cinematic generation. Ids are ' +
|
|
37
|
+
'validated against their dimension server-side. Dimensions are data-driven — never hardcode ids.'
|
|
38
|
+
);
|
|
39
|
+
|
|
12
40
|
function registerGenerateTools(server, client, options = {}) {
|
|
13
41
|
// Only enabled by hosts that explicitly opt in (the remote HTTP connector).
|
|
14
42
|
// stdio hosts (Kolbo Code, Claude Desktop, Cursor) leave this false, so their
|
|
@@ -34,13 +62,14 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
34
62
|
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'),
|
|
35
63
|
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.'),
|
|
36
64
|
preset_id: z.string().optional().describe('Preset ID from list_presets type="image" to apply a saved style preset to this generation.'),
|
|
65
|
+
cinematic: CINEMATIC_SCHEMA,
|
|
37
66
|
project_id: projectIdField
|
|
38
67
|
},
|
|
39
|
-
async ({ prompt, model, aspect_ratio, enhance_prompt, num_images, reference_images, visual_dna_ids, moodboard_id, enable_web_search, resolution, preset_id, project_id }) => {
|
|
68
|
+
async ({ prompt, model, aspect_ratio, enhance_prompt, num_images, reference_images, visual_dna_ids, moodboard_id, enable_web_search, resolution, preset_id, cinematic, project_id }) => {
|
|
40
69
|
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
41
70
|
const gen = await client.post('/v1/generate/image', {
|
|
42
71
|
prompt, model, aspect_ratio, enhance_prompt, num_images,
|
|
43
|
-
reference_images, visual_dna_ids, moodboard_id, enable_web_search, resolution, preset_id, project_id
|
|
72
|
+
reference_images, visual_dna_ids, moodboard_id, enable_web_search, resolution, preset_id, cinematic, project_id
|
|
44
73
|
});
|
|
45
74
|
|
|
46
75
|
if (ui()) return uiGenerating({
|
|
@@ -85,13 +114,14 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
85
114
|
moodboard_id: z.string().optional().describe('Moodboard ID whose master_prompt and style_guide should be applied.'),
|
|
86
115
|
enable_web_search: z.boolean().optional().describe('Enable web-search grounding. Default: false'),
|
|
87
116
|
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.'),
|
|
117
|
+
cinematic: CINEMATIC_SCHEMA,
|
|
88
118
|
project_id: projectIdField
|
|
89
119
|
},
|
|
90
|
-
async ({ prompt, model, source_images, aspect_ratio, enhance_prompt, num_images, visual_dna_ids, moodboard_id, enable_web_search, resolution, project_id }) => {
|
|
120
|
+
async ({ prompt, model, source_images, aspect_ratio, enhance_prompt, num_images, visual_dna_ids, moodboard_id, enable_web_search, resolution, cinematic, project_id }) => {
|
|
91
121
|
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
92
122
|
const gen = await client.post('/v1/generate/image-edit', {
|
|
93
123
|
prompt, model, source_images, aspect_ratio, enhance_prompt, num_images,
|
|
94
|
-
visual_dna_ids, moodboard_id, enable_web_search, resolution, project_id
|
|
124
|
+
visual_dna_ids, moodboard_id, enable_web_search, resolution, cinematic, project_id
|
|
95
125
|
});
|
|
96
126
|
|
|
97
127
|
if (ui()) return uiGenerating({
|
|
@@ -151,11 +181,43 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
151
181
|
enhance_prompt, reference_images, visual_dna_ids, moodboard_id, moodboard_ids, resolution, project_id
|
|
152
182
|
});
|
|
153
183
|
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
184
|
+
const cdStatusUrl = `/v1/generate/creative-director/${gen.generation_id}/status`;
|
|
185
|
+
// Video mode runs N scenes as parallel video generations, each of which
|
|
186
|
+
// can take several minutes — a whole batch routinely exceeds the 10-min
|
|
187
|
+
// image window. Give video batches 30 min (the server watchdog finalizes
|
|
188
|
+
// stuck batches around 15-30 min, so this aligns client + server).
|
|
189
|
+
const cdTimeout = workflow_type === 'video' ? 1800000 : 600000;
|
|
190
|
+
|
|
191
|
+
let result;
|
|
192
|
+
try {
|
|
193
|
+
result = await pollUntilDone(client, gen.generation_id, {
|
|
194
|
+
interval: (gen.poll_interval_hint || 5) * 1000,
|
|
195
|
+
timeout: cdTimeout,
|
|
196
|
+
statusUrl: cdStatusUrl
|
|
197
|
+
});
|
|
198
|
+
} catch (err) {
|
|
199
|
+
// On a client-side poll timeout the batch is almost always STILL
|
|
200
|
+
// running (or already finished) on the server. Don't lose the work:
|
|
201
|
+
// return whatever scenes have landed plus the exact re-check path,
|
|
202
|
+
// instead of throwing an opaque timeout at the agent.
|
|
203
|
+
if (err && err.timedOut) {
|
|
204
|
+
let partial = null;
|
|
205
|
+
try { partial = await client.get(cdStatusUrl); } catch (_) { /* ignore */ }
|
|
206
|
+
const doneScenes = (partial?.scenes || []).filter(s => s.status === 'completed').map(s => ({
|
|
207
|
+
scene_number: s.scene_number, title: s.title, image_urls: s.image_urls, video_urls: s.video_urls
|
|
208
|
+
}));
|
|
209
|
+
return { content: [{ type: 'text', text: JSON.stringify({
|
|
210
|
+
state: partial?.state || 'processing',
|
|
211
|
+
generation_id: gen.generation_id,
|
|
212
|
+
scenes: doneScenes,
|
|
213
|
+
total_scenes: partial?.scenes?.length || 0,
|
|
214
|
+
completed_scenes: doneScenes.length,
|
|
215
|
+
_timed_out: true,
|
|
216
|
+
_hint: `Still running after the poll window. Call get_creative_director_status with generation_id="${gen.generation_id}" to keep checking until state="completed" — do NOT re-run generate_creative_director.`
|
|
217
|
+
}, null, 2) }] };
|
|
218
|
+
}
|
|
219
|
+
throw err;
|
|
220
|
+
}
|
|
159
221
|
|
|
160
222
|
const scenes = (result.scenes || [])
|
|
161
223
|
.filter(s => s.status === 'completed')
|
|
@@ -187,6 +249,42 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
187
249
|
}
|
|
188
250
|
);
|
|
189
251
|
|
|
252
|
+
// ─── get_creative_director_status ──────────────────────────
|
|
253
|
+
// Creative Director uses a DEDICATED status route (per-scene state + all
|
|
254
|
+
// scene image/video URLs). The generic get_generation_status hits
|
|
255
|
+
// /v1/generate/:id/status and CANNOT read a CD batch — so a long or
|
|
256
|
+
// backgrounded CD run (especially parallel VIDEO scenes that exceed the
|
|
257
|
+
// blocking poll window) needs this tool to be re-checked until done.
|
|
258
|
+
server.tool(
|
|
259
|
+
'get_creative_director_status',
|
|
260
|
+
'Check the status of a Creative Director batch (from generate_creative_director) by its generation_id. Returns overall state ("processing" until EVERY scene is terminal, then "completed"/"failed") plus each scene\'s number, title, per-scene status, and image_urls/video_urls. Use this to resume checking a batch that was still running when generate_creative_director returned `_timed_out: true` — poll it until state="completed" to collect ALL parallel scene outputs at once. Do NOT use the generic get_generation_status for Creative Director ids; it points at the wrong endpoint.',
|
|
261
|
+
{
|
|
262
|
+
generation_id: z.string().describe('The Creative Director generation_id returned by generate_creative_director.')
|
|
263
|
+
},
|
|
264
|
+
async ({ generation_id }) => {
|
|
265
|
+
const status = await client.get(`/v1/generate/creative-director/${encodeURIComponent(generation_id)}/status`);
|
|
266
|
+
const scenes = (status.scenes || []).map(s => ({
|
|
267
|
+
scene_number: s.scene_number,
|
|
268
|
+
status: s.status,
|
|
269
|
+
title: s.title,
|
|
270
|
+
image_urls: s.image_urls || null,
|
|
271
|
+
video_urls: s.video_urls || null
|
|
272
|
+
}));
|
|
273
|
+
const completed = scenes.filter(s => s.status === 'completed').length;
|
|
274
|
+
return { content: [{ type: 'text', text: JSON.stringify({
|
|
275
|
+
state: status.state,
|
|
276
|
+
generation_id,
|
|
277
|
+
progress: status.progress,
|
|
278
|
+
scenes,
|
|
279
|
+
total_scenes: scenes.length,
|
|
280
|
+
completed_scenes: completed,
|
|
281
|
+
_hint: status.state === 'completed'
|
|
282
|
+
? 'All scenes terminal. Every completed scene\'s image_urls/video_urls are final.'
|
|
283
|
+
: 'Still running — call get_creative_director_status again in a few seconds until state="completed".'
|
|
284
|
+
}, null, 2) }] };
|
|
285
|
+
}
|
|
286
|
+
);
|
|
287
|
+
|
|
190
288
|
// ─── generate_video ────────────────────────────────────────
|
|
191
289
|
// NOTE: text-to-video does NOT support Visual DNA — the textToVideoGeneration
|
|
192
290
|
// controller in kolbo-api never reads visualDnaIds. For character-consistent
|
|
@@ -204,12 +302,13 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
204
302
|
reference_images: z.array(z.string()).optional().describe('Array of image URLs used as visual references (style / composition / subject). **Cap: pass at most `max_reference_images` URLs from list_models for the chosen model — exceeding it is a deterministic 400.**'),
|
|
205
303
|
resolution: z.string().optional().describe('Video resolution tier (vertical pixels): "720p" / "1080p" / "1440p" / "2160p". Some models use labels like "512P"/"1024P"/"768P"/"1080P". Model-dependent — call list_models and read supported_resolutions. Read resolution_multipliers to predict cost.'),
|
|
206
304
|
preset_id: z.string().optional().describe('Preset ID from list_presets type="video" to apply a saved motion/style preset to this generation.'),
|
|
305
|
+
sound_enabled: z.boolean().optional().describe('Enable (`true`) or disable (`false`) AI-generated synced audio on the output video. Only honored by models with `sound_generation_type: "native"` from list_models (e.g. Veo 3.1, Kling V3/2.6, PixVerse V6). On `sound_generation_type: "none"` models the flag has no effect. Omit to use the model\'s `sound_enabled_by_default`. Pass `false` when the user says no sound / silent / mute / without audio. Enabling sound may apply `sound_credit_multiplier` to cost.'),
|
|
207
306
|
project_id: projectIdField
|
|
208
307
|
},
|
|
209
|
-
async ({ prompt, model, aspect_ratio, duration, enhance_prompt, reference_images, resolution, preset_id, project_id }) => {
|
|
308
|
+
async ({ prompt, model, aspect_ratio, duration, enhance_prompt, reference_images, resolution, preset_id, sound_enabled, project_id }) => {
|
|
210
309
|
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
211
310
|
const gen = await client.post('/v1/generate/video', {
|
|
212
|
-
prompt, model, aspect_ratio, duration, enhance_prompt, reference_images, resolution, preset_id, project_id
|
|
311
|
+
prompt, model, aspect_ratio, duration, enhance_prompt, reference_images, resolution, preset_id, sound_enabled, project_id
|
|
213
312
|
});
|
|
214
313
|
|
|
215
314
|
if (ui()) return uiGenerating({
|
|
@@ -252,13 +351,14 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
252
351
|
duration: z.number().optional().describe('Duration in seconds. Must be in `supported_durations` from list_models, OR within `min_output_duration`-`max_output_duration`. Default: 5'),
|
|
253
352
|
enhance_prompt: z.boolean().optional().describe('Enhance the motion prompt. Default: true'),
|
|
254
353
|
visual_dna_ids: z.array(z.string()).optional().describe('Array of Visual DNA profile IDs to maintain consistency with prior characters / styles. **Cap: pass at most `max_visual_dna` IDs from list_models for the chosen model; if `supports_visual_dna: false` the model ignores DNA entirely.**'),
|
|
255
|
-
resolution: z.string().optional().describe('Video resolution tier (vertical pixels): "720p" / "1080p" / "1440p" / "2160p". Some models use labels like "512P"/"1024P"/"768P"/"1080P". Model-dependent — call list_models and read supported_resolutions.
|
|
354
|
+
resolution: z.string().optional().describe('Video resolution tier (vertical pixels): "720p" / "1080p" / "1440p" / "2160p". Some models use labels like "512P"/"1024P"/"768P"/"1080P". Model-dependent — call list_models and read supported_resolutions.'),
|
|
355
|
+
sound_enabled: z.boolean().optional().describe('Enable (`true`) or disable (`false`) AI-generated synced audio on the output video. Only honored by models with `sound_generation_type: "native"` from list_models (e.g. Veo 3.1 Lite, Kling V3 4K, PixVerse V6, Kling 2.6/v3). On `sound_generation_type: "none"` models the flag has no effect. Omit to use the model\'s `sound_enabled_by_default`. Pass `false` when the user says no sound / silent / mute / without audio. Enabling sound may apply `sound_credit_multiplier` to cost.'),
|
|
256
356
|
project_id: projectIdField
|
|
257
357
|
},
|
|
258
|
-
async ({ image_url, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, resolution, project_id }) => {
|
|
358
|
+
async ({ image_url, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, resolution, sound_enabled, project_id }) => {
|
|
259
359
|
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
260
360
|
const gen = await client.post('/v1/generate/video/from-image', {
|
|
261
|
-
image_url, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, resolution, project_id
|
|
361
|
+
image_url, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, resolution, sound_enabled, project_id
|
|
262
362
|
});
|
|
263
363
|
|
|
264
364
|
if (ui()) return uiGenerating({
|
package/src/tools/presets.js
CHANGED
|
@@ -50,6 +50,35 @@ function registerPresetTools(server, client, options = {}) {
|
|
|
50
50
|
return { content: [{ type: 'text', text }] };
|
|
51
51
|
}
|
|
52
52
|
);
|
|
53
|
+
|
|
54
|
+
// ─── list_cinematic_presets ────────────────────────────────
|
|
55
|
+
server.tool(
|
|
56
|
+
'list_cinematic_presets',
|
|
57
|
+
'List Kolbo "Cinema mode" presets for image generation/editing — a deliberate photographic ' +
|
|
58
|
+
'treatment layered onto the prompt. Returns presets grouped by DIMENSION (data-driven from the ' +
|
|
59
|
+
'live catalog; today: camera, lens, focal_length, aperture, angle, shot_type, color_palette, ' +
|
|
60
|
+
'lighting). Each preset has id, name, description, thumbnail. ONLY call this when the user wants a ' +
|
|
61
|
+
'specific cinematic look; then pass the chosen ids via the `cinematic` arg of generate_image / ' +
|
|
62
|
+
'generate_image_edit — at most one id per dimension. "Auto" is the absence of a selection: omit a ' +
|
|
63
|
+
'dimension (or the whole `cinematic` object) to let the enhancer decide. For an ordinary generation ' +
|
|
64
|
+
'do not call this at all. Never hardcode ids — dimensions and presets change; always fetch here.',
|
|
65
|
+
{},
|
|
66
|
+
async () => {
|
|
67
|
+
const result = await client.get('/v1/cinematic-presets');
|
|
68
|
+
// The public route serves the raw grouped map ({ camera:[...], lens:[...] });
|
|
69
|
+
// the SDK envelope wraps it as { dimensions:{...} }. Accept either shape.
|
|
70
|
+
const dimensions = (result && result.dimensions) || result || {};
|
|
71
|
+
const text = JSON.stringify({
|
|
72
|
+
dimensions,
|
|
73
|
+
available_dimensions: Object.keys(dimensions),
|
|
74
|
+
_usage_hint: 'Include ONLY the dimensions the user actually wants; pass their ids as the ' +
|
|
75
|
+
'`cinematic` arg on generate_image / generate_image_edit, e.g. {"camera":"<id>","lighting":"<id>"}. ' +
|
|
76
|
+
'Every omitted/null dimension is Auto — the enhancer completes the look in the spirit of the ones ' +
|
|
77
|
+
'you set. Omit the whole object for a non-cinematic generation. Ids are validated per-dimension server-side.'
|
|
78
|
+
}, null, 2);
|
|
79
|
+
return { content: [{ type: 'text', text }] };
|
|
80
|
+
}
|
|
81
|
+
);
|
|
53
82
|
}
|
|
54
83
|
|
|
55
84
|
module.exports = { registerPresetTools };
|