@kolbo/mcp 1.88.4 → 1.89.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 +8 -2
- package/package.json +1 -1
- package/src/apps/bridge.js +53 -19
- package/src/apps/html.js +2 -0
- package/src/apps/index.js +8 -8
- package/src/apps/theme.js +58 -10
- package/src/apps/widgets/generation.js +13 -4
- package/src/apps/widgets/list.js +67 -25
- package/src/apps/widgets/mediaGrid.js +148 -79
- package/src/index.js +269 -267
- package/src/toolAnnotations.js +11 -9
- package/src/tools/_shared.js +10 -5
- package/src/tools/agents.js +51 -25
- package/src/tools/chat.js +3 -1
- package/src/tools/editor.js +22 -0
- package/src/tools/generate.js +19 -7
- package/src/tools/media.js +10 -10
- package/src/tools/models.js +11 -5
- package/src/tools/visual_dna.js +1 -1
package/src/index.js
CHANGED
|
@@ -1,267 +1,269 @@
|
|
|
1
|
-
/* ============================================================================
|
|
2
|
-
* @kolbo/mcp — Kolbo AI MCP Server
|
|
3
|
-
*
|
|
4
|
-
* ⛔ STOP. READ THIS BEFORE TOUCHING ANY TOOL REGISTRATION. ⛔
|
|
5
|
-
*
|
|
6
|
-
* This package is published to npm and installed via `npx -y @kolbo/mcp`.
|
|
7
|
-
* Thousands of users have it CACHED on their machines, pinned to old versions
|
|
8
|
-
* by npx's cache. Every tool name, every arg name, every response shape
|
|
9
|
-
* registered below is a PUBLIC CONTRACT. Breaking it silently strands users
|
|
10
|
-
* whose LLM will keep calling tool names their cached server no longer
|
|
11
|
-
* registers — or worse, calls new-style args that the old server can't parse.
|
|
12
|
-
*
|
|
13
|
-
* THE THREE COMMANDMENTS
|
|
14
|
-
*
|
|
15
|
-
* 1. NEVER RENAME AN EXISTING TOOL.
|
|
16
|
-
* Not `generate_image` → `create_image`. Not `list_models` → `get_models`.
|
|
17
|
-
* Not "just cleaning up the name." Old cached clients break the instant
|
|
18
|
-
* you rename. If you must rename, keep the OLD name as an alias that
|
|
19
|
-
* forwards to the new implementation for at least one full major version.
|
|
20
|
-
*
|
|
21
|
-
* 2. NEVER REMOVE AN EXISTING TOOL.
|
|
22
|
-
* Deprecate it in the description ("[DEPRECATED: use X]") and keep it
|
|
23
|
-
* working. Only remove in a major version bump with release notes.
|
|
24
|
-
*
|
|
25
|
-
* 3. NEVER CHANGE AN EXISTING TOOL'S ARG NAMES, TYPES, OR REQUIRED STATUS
|
|
26
|
-
* IN A BACKWARD-INCOMPATIBLE WAY.
|
|
27
|
-
* Adding a new OPTIONAL arg with a sensible default is fine. Everything
|
|
28
|
-
* else below is forbidden in a minor release:
|
|
29
|
-
* - renaming `prompt` to `text`
|
|
30
|
-
* - making a previously-optional arg required
|
|
31
|
-
* - changing `aspect_ratio: string` to `aspect_ratio: { w, h }`
|
|
32
|
-
* - removing an arg (even one you think nobody uses)
|
|
33
|
-
*
|
|
34
|
-
* VERSION BUMPS
|
|
35
|
-
*
|
|
36
|
-
* - minor (1.1.0 → 1.2.0): new tool, new optional arg, description tweak
|
|
37
|
-
* - patch (1.1.0 → 1.1.1): internal refactor, bug fix with no user impact
|
|
38
|
-
* - major (1.1.0 → 2.0.0): ANY breaking change from commandments 1–3 above,
|
|
39
|
-
* AND only after going through the deprecation path in CLAUDE.md.
|
|
40
|
-
*
|
|
41
|
-
* WHY THIS MATTERS
|
|
42
|
-
*
|
|
43
|
-
* Users install via `npx -y @kolbo/mcp` — npx CACHES packages. A user who
|
|
44
|
-
* installed 3 months ago may still be running v1.0 until their cache
|
|
45
|
-
* invalidates. When their Claude Desktop starts the MCP server, it
|
|
46
|
-
* registers whatever tools ITS VERSION knows about. Their LLM sees that
|
|
47
|
-
* list and calls those names. You cannot force-update them.
|
|
48
|
-
*
|
|
49
|
-
* The matching backend SDK routes in
|
|
50
|
-
* `kolbo-api/src/modules/sdk/index.js` are the same kind of public
|
|
51
|
-
* contract and follow the same rules — never rename, never remove.
|
|
52
|
-
*
|
|
53
|
-
* Full rules, deprecation path, and parity-audit instructions: CLAUDE.md
|
|
54
|
-
*
|
|
55
|
-
* If you are a coding agent about to rename/remove a tool or arg: STOP and
|
|
56
|
-
* ask the human first. This is not optional.
|
|
57
|
-
* ==========================================================================*/
|
|
58
|
-
|
|
59
|
-
const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
|
|
60
|
-
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
61
|
-
const KolboClient = require('./client');
|
|
62
|
-
const { LOCAL_FILE_ROUTING, attachFileInputHints } = require('./tools/_shared');
|
|
63
|
-
const { registerGenerateTools } = require('./tools/generate');
|
|
64
|
-
const { registerModelTools } = require('./tools/models');
|
|
65
|
-
const { registerChatTools } = require('./tools/chat');
|
|
66
|
-
const { registerVisualDnaTools } = require('./tools/visual_dna');
|
|
67
|
-
const { registerMoodboardTools } = require('./tools/moodboards');
|
|
68
|
-
const { registerColorPaletteTools } = require('./tools/color_palettes');
|
|
69
|
-
const { registerFontTools } = require('./tools/fonts');
|
|
70
|
-
const {
|
|
71
|
-
const {
|
|
72
|
-
const {
|
|
73
|
-
const {
|
|
74
|
-
const {
|
|
75
|
-
const {
|
|
76
|
-
const {
|
|
77
|
-
const {
|
|
78
|
-
const {
|
|
79
|
-
const {
|
|
80
|
-
const {
|
|
81
|
-
const {
|
|
82
|
-
const {
|
|
83
|
-
const {
|
|
84
|
-
const {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* -
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* @param {
|
|
95
|
-
* @param {string} [opts.
|
|
96
|
-
* @param {
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
'
|
|
121
|
-
'
|
|
122
|
-
'
|
|
123
|
-
'
|
|
124
|
-
'
|
|
125
|
-
'
|
|
126
|
-
'
|
|
127
|
-
'
|
|
128
|
-
'
|
|
129
|
-
'
|
|
130
|
-
'
|
|
131
|
-
'
|
|
132
|
-
'
|
|
133
|
-
'
|
|
134
|
-
'
|
|
135
|
-
'
|
|
136
|
-
'
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
// the
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
//
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
//
|
|
209
|
-
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
|
|
213
|
-
//
|
|
214
|
-
|
|
215
|
-
//
|
|
216
|
-
|
|
217
|
-
//
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
//
|
|
251
|
-
//
|
|
252
|
-
//
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
//
|
|
261
|
-
//
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* @kolbo/mcp — Kolbo AI MCP Server
|
|
3
|
+
*
|
|
4
|
+
* ⛔ STOP. READ THIS BEFORE TOUCHING ANY TOOL REGISTRATION. ⛔
|
|
5
|
+
*
|
|
6
|
+
* This package is published to npm and installed via `npx -y @kolbo/mcp`.
|
|
7
|
+
* Thousands of users have it CACHED on their machines, pinned to old versions
|
|
8
|
+
* by npx's cache. Every tool name, every arg name, every response shape
|
|
9
|
+
* registered below is a PUBLIC CONTRACT. Breaking it silently strands users
|
|
10
|
+
* whose LLM will keep calling tool names their cached server no longer
|
|
11
|
+
* registers — or worse, calls new-style args that the old server can't parse.
|
|
12
|
+
*
|
|
13
|
+
* THE THREE COMMANDMENTS
|
|
14
|
+
*
|
|
15
|
+
* 1. NEVER RENAME AN EXISTING TOOL.
|
|
16
|
+
* Not `generate_image` → `create_image`. Not `list_models` → `get_models`.
|
|
17
|
+
* Not "just cleaning up the name." Old cached clients break the instant
|
|
18
|
+
* you rename. If you must rename, keep the OLD name as an alias that
|
|
19
|
+
* forwards to the new implementation for at least one full major version.
|
|
20
|
+
*
|
|
21
|
+
* 2. NEVER REMOVE AN EXISTING TOOL.
|
|
22
|
+
* Deprecate it in the description ("[DEPRECATED: use X]") and keep it
|
|
23
|
+
* working. Only remove in a major version bump with release notes.
|
|
24
|
+
*
|
|
25
|
+
* 3. NEVER CHANGE AN EXISTING TOOL'S ARG NAMES, TYPES, OR REQUIRED STATUS
|
|
26
|
+
* IN A BACKWARD-INCOMPATIBLE WAY.
|
|
27
|
+
* Adding a new OPTIONAL arg with a sensible default is fine. Everything
|
|
28
|
+
* else below is forbidden in a minor release:
|
|
29
|
+
* - renaming `prompt` to `text`
|
|
30
|
+
* - making a previously-optional arg required
|
|
31
|
+
* - changing `aspect_ratio: string` to `aspect_ratio: { w, h }`
|
|
32
|
+
* - removing an arg (even one you think nobody uses)
|
|
33
|
+
*
|
|
34
|
+
* VERSION BUMPS
|
|
35
|
+
*
|
|
36
|
+
* - minor (1.1.0 → 1.2.0): new tool, new optional arg, description tweak
|
|
37
|
+
* - patch (1.1.0 → 1.1.1): internal refactor, bug fix with no user impact
|
|
38
|
+
* - major (1.1.0 → 2.0.0): ANY breaking change from commandments 1–3 above,
|
|
39
|
+
* AND only after going through the deprecation path in CLAUDE.md.
|
|
40
|
+
*
|
|
41
|
+
* WHY THIS MATTERS
|
|
42
|
+
*
|
|
43
|
+
* Users install via `npx -y @kolbo/mcp` — npx CACHES packages. A user who
|
|
44
|
+
* installed 3 months ago may still be running v1.0 until their cache
|
|
45
|
+
* invalidates. When their Claude Desktop starts the MCP server, it
|
|
46
|
+
* registers whatever tools ITS VERSION knows about. Their LLM sees that
|
|
47
|
+
* list and calls those names. You cannot force-update them.
|
|
48
|
+
*
|
|
49
|
+
* The matching backend SDK routes in
|
|
50
|
+
* `kolbo-api/src/modules/sdk/index.js` are the same kind of public
|
|
51
|
+
* contract and follow the same rules — never rename, never remove.
|
|
52
|
+
*
|
|
53
|
+
* Full rules, deprecation path, and parity-audit instructions: CLAUDE.md
|
|
54
|
+
*
|
|
55
|
+
* If you are a coding agent about to rename/remove a tool or arg: STOP and
|
|
56
|
+
* ask the human first. This is not optional.
|
|
57
|
+
* ==========================================================================*/
|
|
58
|
+
|
|
59
|
+
const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
|
|
60
|
+
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
61
|
+
const KolboClient = require('./client');
|
|
62
|
+
const { LOCAL_FILE_ROUTING, attachFileInputHints } = require('./tools/_shared');
|
|
63
|
+
const { registerGenerateTools } = require('./tools/generate');
|
|
64
|
+
const { registerModelTools } = require('./tools/models');
|
|
65
|
+
const { registerChatTools } = require('./tools/chat');
|
|
66
|
+
const { registerVisualDnaTools } = require('./tools/visual_dna');
|
|
67
|
+
const { registerMoodboardTools } = require('./tools/moodboards');
|
|
68
|
+
const { registerColorPaletteTools } = require('./tools/color_palettes');
|
|
69
|
+
const { registerFontTools } = require('./tools/fonts');
|
|
70
|
+
const { registerEditorTools } = require('./tools/editor');
|
|
71
|
+
const { registerMediaTools } = require('./tools/media');
|
|
72
|
+
const { registerPresetTools } = require('./tools/presets');
|
|
73
|
+
const { registerArtifactTools } = require('./tools/artifacts');
|
|
74
|
+
const { registerProjectTools } = require('./tools/projects');
|
|
75
|
+
const { registerAgentTools } = require('./tools/agents');
|
|
76
|
+
const { registerDocTools } = require('./tools/docs');
|
|
77
|
+
const { registerReviewTools } = require('./tools/review');
|
|
78
|
+
const { registerVoiceTools } = require('./tools/voices');
|
|
79
|
+
const { registerMusicLibraryTools } = require('./tools/music_library');
|
|
80
|
+
const { registerStockLibraryTools } = require('./tools/stock_library');
|
|
81
|
+
const { registerAudioStemTools } = require('./tools/audio_stems');
|
|
82
|
+
const { registerAnalyzeTools } = require('./tools/analyze');
|
|
83
|
+
const { registerBlenderTools } = require('./tools/blender');
|
|
84
|
+
const { registerApps, attachToolWidgetMeta } = require('./apps');
|
|
85
|
+
const { attachToolAnnotations } = require('./toolAnnotations');
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Build a fully-configured Kolbo MCP server (all tool groups registered)
|
|
89
|
+
* WITHOUT connecting a transport. This is the reusable core shared by:
|
|
90
|
+
* - the stdio entrypoint below (npx / Kolbo Code), and
|
|
91
|
+
* - a remote HTTP host (kolbo-api) that creates one server per request with
|
|
92
|
+
* the caller's key injected via `opts.apiKey`.
|
|
93
|
+
*
|
|
94
|
+
* @param {object} [opts]
|
|
95
|
+
* @param {string} [opts.apiKey] Per-instance Kolbo API key (overrides env).
|
|
96
|
+
* @param {string} [opts.apiBase] API base URL override.
|
|
97
|
+
* @param {boolean} [opts.apps] Force-enable MCP Apps widget results. Set by
|
|
98
|
+
* the kolbo-api remote connector (claude.ai),
|
|
99
|
+
* whose stateless transport hides client
|
|
100
|
+
* capabilities. stdio hosts are auto-detected
|
|
101
|
+
* from the initialize handshake instead.
|
|
102
|
+
* @returns {McpServer} a server ready to `.connect(transport)`.
|
|
103
|
+
*/
|
|
104
|
+
function createServer(opts = {}) {
|
|
105
|
+
const client = new KolboClient(opts);
|
|
106
|
+
|
|
107
|
+
const server = new McpServer({
|
|
108
|
+
name: 'kolbo',
|
|
109
|
+
title: 'Kolbo',
|
|
110
|
+
version: '1.0.0',
|
|
111
|
+
websiteUrl: 'https://kolbo.ai',
|
|
112
|
+
// Connector avatar for hosts that render server icons (claude.ai tool
|
|
113
|
+
// headers show this instead of a letter monogram).
|
|
114
|
+
icons: [{ src: 'https://api.kolbo.ai/assets/kolbo-ai.png', mimeType: 'image/png', sizes: ['512x512'] }]
|
|
115
|
+
}, {
|
|
116
|
+
// Server-level instructions surfaced to the host model on initialize.
|
|
117
|
+
// The single most common failure mode is project confusion — spell out
|
|
118
|
+
// the project contract here so every client gets it without a skill file.
|
|
119
|
+
instructions: [
|
|
120
|
+
'PERSONAL FONTS: use list_fonts/get_font, upload_font for local stdio, create_font_upload_ticket for remote shell, or font_upload_widget for browser uploads. Never use media upload for fonts. Poll get_font_upload_status until ready, then pass up to three family IDs as font_ids to image creation/editing or image-mode Creative Director. Verify supports_custom_fonts in model discovery. The backend prepares private specimens; never render or attach specimens yourself.',
|
|
121
|
+
'LOCAL FILES: never upload a user file with your own cloud credentials, an S3/Spaces script, or a third-party host — Kolbo owns this. ' + LOCAL_FILE_ROUTING,
|
|
122
|
+
'PROMPT CONVENTIONS (Kolbo-specific — these change the OUTPUT, not just the metadata):',
|
|
123
|
+
'A. Visual DNA: passing `visual_dna_ids` is not enough — every DNA in play must ALSO be tagged inside the prompt text as `@Name`, using the DNA name (e.g. "@Kobi walks into frame"). Moodboards are referenced the same way with `#Name`. Resolve names via `list_visual_dnas` / `list_moodboards`.',
|
|
124
|
+
'B. The full Kolbo skill is available to you as MCP RESOURCES under `kolbo://skill/`. Read `kolbo://skill/SKILL.md` first — it is the core rules plus a routing index — then read the matching `kolbo://skill/references/...` file before writing prompts for a specific model or workflow (per-model prompt rules, Visual DNA workflow, Creative Director, marketing, cost validation). Do this instead of guessing; the references exist precisely because the rules differ per model.',
|
|
125
|
+
'PROJECT CONTRACT (read this before generating anything):',
|
|
126
|
+
'Everything in Kolbo lives inside a PROJECT — sessions, generations, and media are all project-scoped.',
|
|
127
|
+
'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 SAME id as `project_id` on EVERY subsequent generate_* / chat_send_message / upload_media / create_doc call in THIS conversation. There is no server-side sticky store — omitting `project_id` on any later call silently lands in the default "API Generations" bucket (flagged is_default:true). Once resolved, treat that id as required for the rest of the conversation.',
|
|
128
|
+
'2. `list_projects` lists the user\'s platform projects (for generations/media/chat). `list_sessions` returns `project_id` on every row — echo that id on follow-up generate/chat/upload calls for work in that session. Do not confuse a generation `session_id` with a project id; they are not interchangeable. Empty leftover sessions after `move_session` can be removed with `delete_session` (soft-delete; `restore_session` undoes it).',
|
|
129
|
+
'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.',
|
|
130
|
+
'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.',
|
|
131
|
+
'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`).',
|
|
132
|
+
'6. TIMEOUT HANDLING (applies to EVERY generate_* / edit_* / chat_send_message / transcribe_audio tool): each tool blocks and polls internally, then gives up after its own window if the job is not yet terminal. A timeout is NOT a failure — it returns `_timed_out:true` with the `generation_id` (not an error), because the job is almost always STILL RUNNING on the server (or already finished). Call `get_generation_status` with that `generation_id` and `wait=true` to keep checking until state="completed". NEVER conclude the generation failed and re-run the same tool from scratch after a `_timed_out:true` result — that wastes the user\'s credits by paying twice. DIRECTOR / BATCH JOBS follow the same convention through a dedicated tool: generate_creative_director runs its scenes (image OR video) in parallel and only reports state="completed" once EVERY scene is terminal; on `_timed_out:true` call `get_creative_director_status` (not get_generation_status) with the returned generation_id and keep checking. If scenes already carry image_urls/video_urls, they are done; do not regenerate.',
|
|
133
|
+
'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.',
|
|
134
|
+
'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 → pass it directly. A URL from generate_* / list_media / upload_media / media.kolbo.ai / any *.kolbo.ai host is ALREADY hosted — NEVER call upload_media on it (that duplicates the file). External (non-Kolbo) URLs may need one upload_media re-host; Kolbo URLs never do. 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.',
|
|
135
|
+
'9. MODEL SELECTION — NAMED MODEL WINS, THEN STRENGTHS SUMMARY: 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). If the user named a model this turn OR earlier in the conversation (including a compaction "Locked choices" / summary), that name is a FAMILY LOCK: pass it (or its display name) on every follow-up, including when the tool changes (text-to-video → image-to-video). Identifier resolution remaps a t2v id to the family\'s i2v sibling automatically. NEVER substitute a different brand because it is cheaper, faster, or "best balance" (Grok Imagine named → do not fire Seedance). If the named family has no variant for this modality, ASK — do not silently switch. Cheapest-summary routing applies ONLY when no model was named on this task: call `list_models` with the matching `type` and read each model\'s STRENGTHS SUMMARY — the "— …" clause printed after the credit cost — 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.',
|
|
136
|
+
'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`. Auto-pick only Nano Banana 2 (`nano-banana-2` / `nano-banana-2-image-editing`) or GPT Image 2 (`gpt-image-2` / `gpt-image-2/edit`) for photoreal photo edits, object removal, keep-subject/remove-others, or crowd cleanup. Do NOT auto-pick Flux 2 / flux-2/edit / Flux Klein — those are generate-from-scratch / style, named-only for editing. 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.',
|
|
137
|
+
'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.'
|
|
138
|
+
].join('\n')
|
|
139
|
+
});
|
|
140
|
+
const progress = require('./progress');
|
|
141
|
+
const { insufficientCreditsResult } = require('./tools/_shared');
|
|
142
|
+
const tool = server.tool.bind(server);
|
|
143
|
+
server.tool = (...args) => {
|
|
144
|
+
const index = args.length - 1;
|
|
145
|
+
const handler = args[index];
|
|
146
|
+
if (typeof handler !== 'function') return tool(...args);
|
|
147
|
+
args[index] = (params, extra) => progress.run(extra, async () => {
|
|
148
|
+
try {
|
|
149
|
+
return await handler(params, extra);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
// Running out of credits is the one refusal with an obvious next step,
|
|
152
|
+
// so it gets a card instead of raw error prose. Wrapped HERE rather
|
|
153
|
+
// than at each of the ~17 generation submit sites: any tool the server
|
|
154
|
+
// can refuse for credits (generation, chat, stems) routes through this
|
|
155
|
+
// single seam, which is also the only way a tool added later inherits
|
|
156
|
+
// the behavior for free.
|
|
157
|
+
const card = await insufficientCreditsResult(client, err, toolOptions).catch(() => null);
|
|
158
|
+
if (card) return card;
|
|
159
|
+
throw err;
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
return tool(...args);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Register all tools. `inlineImages` (off by default) is opt-in: only the
|
|
166
|
+
// remote HTTP host enables it, so stdio clients (Kolbo Code / Desktop / Cursor)
|
|
167
|
+
// keep identical text-URL output. `apps` gates interactive widget results
|
|
168
|
+
// (MCP Apps) the same way — see src/apps/index.js.
|
|
169
|
+
// `commerce: false` strips every surface that sells or points at buying
|
|
170
|
+
// subscriptions / credit packs: `show_plans` is not registered and a credit
|
|
171
|
+
// refusal renders a plain "out of credits" card with no prices or pricing
|
|
172
|
+
// link. OpenAI rejected the Kolbo.AI ChatGPT app twice on 2026-09-06 for
|
|
173
|
+
// "commerce for disallowed offerings (digital goods/services)" — the plans
|
|
174
|
+
// card IS that offering. kolbo-api enables this for `?client=chatgpt`.
|
|
175
|
+
const toolOptions = {
|
|
176
|
+
inlineImages: !!opts.inlineImages,
|
|
177
|
+
remote: !!opts.remote || !!opts.apps,
|
|
178
|
+
apps: !!opts.apps,
|
|
179
|
+
asyncGenerations: !!opts.asyncGenerations,
|
|
180
|
+
commerce: opts.commerce !== false,
|
|
181
|
+
};
|
|
182
|
+
registerGenerateTools(server, client, toolOptions);
|
|
183
|
+
registerModelTools(server, client, toolOptions);
|
|
184
|
+
registerVoiceTools(server, client, toolOptions);
|
|
185
|
+
registerChatTools(server, client, toolOptions);
|
|
186
|
+
registerVisualDnaTools(server, client, toolOptions);
|
|
187
|
+
registerMoodboardTools(server, client, toolOptions);
|
|
188
|
+
registerColorPaletteTools(server, client, toolOptions);
|
|
189
|
+
registerFontTools(server, client, { allowLocalFiles: opts.allowLocalFiles === true && !toolOptions.remote });
|
|
190
|
+
registerEditorTools(server, client);
|
|
191
|
+
registerAnalyzeTools(server, client, toolOptions);
|
|
192
|
+
registerMediaTools(server, client, toolOptions);
|
|
193
|
+
registerPresetTools(server, client, toolOptions);
|
|
194
|
+
registerArtifactTools(server, client, toolOptions);
|
|
195
|
+
registerProjectTools(server, client, toolOptions);
|
|
196
|
+
registerAgentTools(server, client, toolOptions);
|
|
197
|
+
registerDocTools(server, client, toolOptions);
|
|
198
|
+
registerReviewTools(server, client, toolOptions);
|
|
199
|
+
registerMusicLibraryTools(server, client, toolOptions);
|
|
200
|
+
registerStockLibraryTools(server, client, toolOptions);
|
|
201
|
+
registerAudioStemTools(server, client, toolOptions);
|
|
202
|
+
registerBlenderTools(server, client, toolOptions);
|
|
203
|
+
|
|
204
|
+
// MCP Apps widget resources (ui://kolbo/*). Registering resources is inert
|
|
205
|
+
// for text-only hosts — they never fetch them.
|
|
206
|
+
registerApps(server);
|
|
207
|
+
// Serve skill/ as standard MCP resources so connector clients — which never
|
|
208
|
+
// run `npx @kolbo/mcp install` — can still read the operating guidance.
|
|
209
|
+
registerSkillResources(server);
|
|
210
|
+
// Every media-input tool advertises the local-file route that works on THIS
|
|
211
|
+
// transport. Without it, a remote-connector model reads "absolute local path",
|
|
212
|
+
// sees no filesystem, and tells the user Kolbo cannot accept their file —
|
|
213
|
+
// the single most-reported failure, despite the upload tools existing.
|
|
214
|
+
attachFileInputHints(server, toolOptions);
|
|
215
|
+
// OpenAI public-app review requires every exposed tool to declare the three
|
|
216
|
+
// safety hints explicitly. The exact contract also fails closed when a tool
|
|
217
|
+
// is added or removed without a classification.
|
|
218
|
+
attachToolAnnotations(server);
|
|
219
|
+
// Declaration-level `_meta['ui/resourceUri']` on every widget-carrying tool —
|
|
220
|
+
// claude.ai prepares the widget iframe from tools/list, not from the result.
|
|
221
|
+
attachToolWidgetMeta(server);
|
|
222
|
+
|
|
223
|
+
return server;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function main() {
|
|
227
|
+
const server = createServer({ allowLocalFiles: true });
|
|
228
|
+
|
|
229
|
+
// Node kills the process on an unhandled rejection / uncaught exception. In a
|
|
230
|
+
// long-lived stdio server that is not a stack trace the user ever sees — the
|
|
231
|
+
// host just reports "MCP server disconnected", mid-conversation, with the
|
|
232
|
+
// generation still running server-side. A tool error is recoverable; a dead
|
|
233
|
+
// process is not, so log to stderr (stdout is the JSON-RPC channel) and stay
|
|
234
|
+
// up. Only the stdio entrypoint does this — an embedding host (kolbo-api)
|
|
235
|
+
// keeps its own process semantics.
|
|
236
|
+
process.on('unhandledRejection', (err) => {
|
|
237
|
+
console.error('[kolbo-mcp] unhandled rejection (server staying up):', err);
|
|
238
|
+
});
|
|
239
|
+
process.on('uncaughtException', (err) => {
|
|
240
|
+
console.error('[kolbo-mcp] uncaught exception (server staying up):', err);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Start the server with stdio transport
|
|
244
|
+
const transport = new StdioServerTransport();
|
|
245
|
+
await server.connect(transport);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Widget plumbing for HOST developers embedding Kolbo widgets themselves
|
|
249
|
+
// (e.g. kolbo-api's Kobi Act) — everything else in ./apps stays internal.
|
|
250
|
+
// UI: tool-name-agnostic resource URI map. TOOL_WIDGETS: tool name -> URI, so
|
|
251
|
+
// a host can resolve which widget a given tool call carries without deep-
|
|
252
|
+
// requiring internals. uiMeta/widgetHtml: same helpers registerApps() uses
|
|
253
|
+
// internally, re-exported so a host never has to re-derive them. Additive
|
|
254
|
+
// only — existing consumers (claude.ai, Desktop, npx) are unaffected.
|
|
255
|
+
const { UI, TOOL_WIDGETS, uiMeta, widgetHtml } = require('./apps');
|
|
256
|
+
const { registerSkillResources } = require('./skillResources');
|
|
257
|
+
|
|
258
|
+
module.exports = { main, createServer, UI, TOOL_WIDGETS, uiMeta, widgetHtml };
|
|
259
|
+
|
|
260
|
+
// Auto-run when invoked directly (e.g. `node src/index.js` or via the published
|
|
261
|
+
// bin/kolbo-mcp.js wrapper). Consumers that `require()` this module to embed it
|
|
262
|
+
// inside another process (the Kolbo Code CLI's `kolbo mcp serve` subcommand)
|
|
263
|
+
// should call `main()` themselves.
|
|
264
|
+
if (require.main === module || require.main?.filename?.endsWith('kolbo-mcp.js')) {
|
|
265
|
+
main().catch(err => {
|
|
266
|
+
console.error('Failed to start Kolbo MCP server:', err);
|
|
267
|
+
process.exit(1);
|
|
268
|
+
});
|
|
269
|
+
}
|