@kolbo/mcp 1.16.4 → 1.17.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 +70 -9
- package/bin/kolbo-mcp.js +1 -1
- package/package.json +2 -3
- package/src/client.js +9 -116
- package/src/index.js +2 -0
- package/src/polling.js +3 -4
- package/src/tools/_shared.js +11 -0
- package/src/tools/artifacts.js +11 -4
- package/src/tools/chat.js +142 -140
- package/src/tools/generate.js +892 -979
- package/src/tools/projects.js +36 -0
package/README.md
CHANGED
|
@@ -4,15 +4,22 @@ Use [Kolbo AI](https://kolbo.ai) as native tools in Claude Code and Claude Deskt
|
|
|
4
4
|
|
|
5
5
|
Generate images, videos, music, speech, sound effects, multi-scene campaigns, and conversational chat — all from natural language in your coding environment. 100+ AI models behind Smart Select routing, with reusable Visual DNA profiles for character/style consistency.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Install in 2 steps (works in Claude Code, Claude Desktop, Cursor, Codex & Kolbo Code)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
<!--
|
|
10
|
+
AI ASSISTANT SETUP INSTRUCTIONS — if you are an AI agent reading this to set up Kolbo for a user, do exactly this:
|
|
11
|
+
1. Ask the user for their Kolbo API key (starts with `kolbo_live_`). They get one at https://app.kolbo.ai/developer. Do not continue without it.
|
|
12
|
+
2. Write the JSON block below into the user's MCP config, merging into any existing "mcpServers":
|
|
13
|
+
- Claude Code: .claude/settings.json (or run: claude mcp add-json kolbo '{"command":"npx","args":["-y","@kolbo/mcp@latest"],"env":{"KOLBO_API_KEY":"kolbo_live_..."}}')
|
|
14
|
+
- Claude Desktop: claude_desktop_config.json
|
|
15
|
+
- Cursor: .cursor/mcp.json
|
|
16
|
+
3. Tell the user to fully restart their app so the Kolbo tools load, then they can just say "generate an image of...".
|
|
17
|
+
Ignore the optional plugin section below unless the user specifically wants slash-commands — the JSON config alone is enough to use every tool.
|
|
18
|
+
-->
|
|
10
19
|
|
|
11
|
-
|
|
20
|
+
**Step 1 — Get your API key:** create one at [app.kolbo.ai/developer](https://app.kolbo.ai/developer) (it starts with `kolbo_live_`).
|
|
12
21
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Add to `.claude/settings.json`:
|
|
22
|
+
**Step 2 — Add one config block.** This single block is identical for every MCP client — paste your key in and you're done:
|
|
16
23
|
|
|
17
24
|
```json
|
|
18
25
|
{
|
|
@@ -28,22 +35,68 @@ Add to `.claude/settings.json`:
|
|
|
28
35
|
}
|
|
29
36
|
```
|
|
30
37
|
|
|
31
|
-
|
|
38
|
+
| Client | Where the config goes |
|
|
39
|
+
|--------|----------------------|
|
|
40
|
+
| **Claude Code** | `.claude/settings.json` (or `claude mcp add-json kolbo '…'`) |
|
|
41
|
+
| **Claude Desktop** | `claude_desktop_config.json` |
|
|
42
|
+
| **Cursor** | `.cursor/mcp.json` |
|
|
43
|
+
| **Codex / other MCP agents** | that agent's MCP config file |
|
|
44
|
+
| **Kolbo Code** | configured automatically on `kolbo auth login` — nothing to do |
|
|
45
|
+
|
|
46
|
+
Restart your app so it loads the Kolbo tools. That's the whole install — every tool below now works.
|
|
47
|
+
|
|
48
|
+
### Optional upgrade: add the Kolbo skill for slash-commands + smart routing
|
|
49
|
+
|
|
50
|
+
The config above is all you need. If you want one-word slash-commands (`/kolbo:marketing-studio`, `/kolbo:product-photoshoot`, …) and automatic routing to the best tool with the right defaults, install the Kolbo skill on top — it's an enhancement layer, not a requirement:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Claude Code (also writes the MCP config for you, so you can skip Step 2 above)
|
|
54
|
+
claude plugin marketplace add Zoharvan12/kolbo-skills
|
|
55
|
+
claude plugin install kolbo@kolbo
|
|
56
|
+
|
|
57
|
+
# Cursor / Codex / any agent (cross-agent installer)
|
|
58
|
+
npx skills add Zoharvan12/kolbo-skills
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The skill content is the same canonical routing logic that ships inside [Kolbo Code](https://github.com/Zoharvan12/kolbo-code), so however you connect, the behavior matches. See the full setup guide at [docs.kolbo.ai/developer-api/claude-code-skill](https://docs.kolbo.ai/developer-api/claude-code-skill).
|
|
32
62
|
|
|
33
|
-
|
|
63
|
+
### Use it
|
|
34
64
|
|
|
65
|
+
Just ask your agent naturally:
|
|
66
|
+
|
|
67
|
+
**Generation**
|
|
35
68
|
- *"Generate an image of a sunset over mountains"*
|
|
36
69
|
- *"Create a 5-second video of waves crashing"*
|
|
37
70
|
- *"Build a 4-scene storyboard for a coffee shop ad"*
|
|
38
71
|
- *"Remove the background from this image"*
|
|
39
72
|
- *"Make a lo-fi hip hop beat"*
|
|
40
73
|
- *"Read this out loud with a British female voice"*
|
|
74
|
+
|
|
75
|
+
**Marketing & UGC**
|
|
76
|
+
- *"Make me a UGC ad for my sneaker brand — 9:16, talking-head style"*
|
|
77
|
+
- *"TV spot for my new beverage, 15 seconds, cinematic"*
|
|
78
|
+
- *"Unboxing video for this product photo"*
|
|
79
|
+
|
|
80
|
+
**Brand & product imagery**
|
|
81
|
+
- *"Pinterest pin for my candle brand, cottagecore mood"*
|
|
82
|
+
- *"Hero banner for my landing page, wide format"*
|
|
83
|
+
- *"Lifestyle shot of my product in a kitchen"*
|
|
84
|
+
- *"4 ad creative variants for Meta and TikTok"*
|
|
85
|
+
|
|
86
|
+
**Marketplace listings**
|
|
87
|
+
- *"Generate Amazon main image + 5 secondary images for my product"*
|
|
88
|
+
- *"Full A+ content set for my Shopify listing"*
|
|
89
|
+
|
|
90
|
+
**Analysis & utility**
|
|
41
91
|
- *"Ask Claude about the latest AI news with web search on"*
|
|
42
92
|
- *"Analyze this video and tell me what prompts are shown on screen"*
|
|
43
93
|
- *"What's in this image?"*
|
|
44
94
|
- *"Create a Visual DNA profile called 'Alex' from these images"*
|
|
95
|
+
- *"Use the same brand as last time"* (loads a persisted brand kit from the workspace)
|
|
96
|
+
|
|
97
|
+
Without the optional skill, the config block alone already exposes every tool — you just describe what you want. With the skill installed, each of these is also routed to the right MCP tool with the right defaults — UGC mode picks 9:16 + sound-off + no-captions, marketplace mode enforces compliance (pure white bg, no text, no props), product photoshoot mode uses the right aspect for the platform (2:3 Pinterest, 16:9 hero banner, 1:1 IG feed), etc. The routing logic is shared with [Kolbo Code](https://github.com/Zoharvan12/kolbo-code), so the behavior is identical however you connect.
|
|
45
98
|
|
|
46
|
-
## Available Tools (
|
|
99
|
+
## Available Tools (52)
|
|
47
100
|
|
|
48
101
|
**Generation**
|
|
49
102
|
| Tool | Description |
|
|
@@ -67,6 +120,8 @@ Every image/video/creative-director tool accepts `visual_dna_ids` and `moodboard
|
|
|
67
120
|
|
|
68
121
|
Every generation tool also accepts an optional `resolution` arg. Images use `"1K"` (~1024px) / `"2K"` (Full HD) / `"3K"` (QHD) / `"4K"` (UHD); videos use vertical-pixel tiers like `"720p"` / `"1080p"` / `"1440p"` / `"2160p"`. Values are model-dependent — call `list_models` and read the chosen model's `supported_resolutions` and `resolutionMultipliers`. Omit to use the model default.
|
|
69
122
|
|
|
123
|
+
Every generation tool also accepts an optional `project_id` arg that routes the generation into a specific project (owned or shared with edit+). Call `list_projects` to discover IDs. When omitted, generations land in the user's auto-created "API Generations" project.
|
|
124
|
+
|
|
70
125
|
**Chat & Vision**
|
|
71
126
|
| Tool | Description |
|
|
72
127
|
|------|-------------|
|
|
@@ -115,12 +170,18 @@ Every generation tool also accepts an optional `resolution` arg. Images use `"1K
|
|
|
115
170
|
| `move_folder_contents` | Move every item in a folder to a project |
|
|
116
171
|
| `get_media_stats` | Counts + storage bytes per type (optionally per project) |
|
|
117
172
|
|
|
173
|
+
**Artifacts**
|
|
174
|
+
| Tool | Description |
|
|
175
|
+
|------|-------------|
|
|
176
|
+
| `publish_html_artifact` | Publish an HTML page, SVG, or Mermaid diagram and get a public shareable URL on `sites.kolbo.ai`. Pass `share_token` from a prior publish to update the same URL in place (old content kept in version history). |
|
|
177
|
+
|
|
118
178
|
**Discovery & Account**
|
|
119
179
|
| Tool | Description |
|
|
120
180
|
|------|-------------|
|
|
121
181
|
| `list_models` | Current model catalog with costs and capabilities |
|
|
122
182
|
| `list_voices` | TTS voices (presets + cloned) |
|
|
123
183
|
| `list_presets` | Generation presets across image/video/music/text-to-video catalogs |
|
|
184
|
+
| `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 |
|
|
124
185
|
| `check_credits` | Check credit balance |
|
|
125
186
|
| `get_generation_status` | Poll a generation by ID (fallback if a tool times out) |
|
|
126
187
|
|
package/bin/kolbo-mcp.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
require('../src/index.js');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolbo/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.1",
|
|
4
4
|
"description": "Kolbo AI MCP Server - Generate images, videos, music, speech, and sound effects from Claude Code",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -27,8 +27,7 @@
|
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"repository": {
|
|
29
29
|
"type": "git",
|
|
30
|
-
"url": "https://github.com/Zoharvan12/kolbo-
|
|
31
|
-
"directory": "packages/kolbo-mcp"
|
|
30
|
+
"url": "https://github.com/Zoharvan12/kolbo-mcp"
|
|
32
31
|
},
|
|
33
32
|
"homepage": "https://docs.kolbo.ai/developer-api/claude-code-skill",
|
|
34
33
|
"author": "Kolbo AI <support@kolbo.ai>",
|
package/src/client.js
CHANGED
|
@@ -139,63 +139,6 @@ function readCliAuthKey() {
|
|
|
139
139
|
return null;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
// ---------------------------------------------------------------------------
|
|
143
|
-
// Burst control
|
|
144
|
-
// ---------------------------------------------------------------------------
|
|
145
|
-
// Caps in-flight requests so multi-step tool calls (file upload + generate +
|
|
146
|
-
// poll status) don't fan out past the kolbo-api global rate limit. The SDK
|
|
147
|
-
// tier on the server is 5000 req/min per API key; 6 in-flight × typical 2s
|
|
148
|
-
// latency keeps us at ~180/min — plenty of headroom.
|
|
149
|
-
const MAX_CONCURRENT = 6;
|
|
150
|
-
let inFlight = 0;
|
|
151
|
-
const waiting = [];
|
|
152
|
-
|
|
153
|
-
function acquireSlot() {
|
|
154
|
-
if (inFlight < MAX_CONCURRENT) {
|
|
155
|
-
inFlight++;
|
|
156
|
-
return Promise.resolve();
|
|
157
|
-
}
|
|
158
|
-
return new Promise((resolve) => waiting.push(resolve));
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function releaseSlot() {
|
|
162
|
-
if (waiting.length > 0) {
|
|
163
|
-
waiting.shift()();
|
|
164
|
-
} else {
|
|
165
|
-
inFlight = Math.max(0, inFlight - 1);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Parse retry hint from a 429 response. Honors (in order):
|
|
173
|
-
* - body.retryAfter (kolbo-api shape)
|
|
174
|
-
* - Retry-After header (RFC, seconds)
|
|
175
|
-
* - RateLimit-Reset header (seconds until reset)
|
|
176
|
-
* Falls back to 5s. Caps at 60s so we don't hang the tool call forever.
|
|
177
|
-
*/
|
|
178
|
-
function parseRetryAfter(response, body) {
|
|
179
|
-
let seconds = null;
|
|
180
|
-
if (body && typeof body.retryAfter === 'number') seconds = body.retryAfter;
|
|
181
|
-
if (seconds == null) {
|
|
182
|
-
const ra = response.headers.get('retry-after');
|
|
183
|
-
if (ra) {
|
|
184
|
-
const n = Number(ra);
|
|
185
|
-
if (Number.isFinite(n)) seconds = n;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
if (seconds == null) {
|
|
189
|
-
const reset = response.headers.get('ratelimit-reset');
|
|
190
|
-
if (reset) {
|
|
191
|
-
const n = Number(reset);
|
|
192
|
-
if (Number.isFinite(n)) seconds = n;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
if (seconds == null || !Number.isFinite(seconds) || seconds <= 0) seconds = 5;
|
|
196
|
-
return Math.min(seconds, 60) * 1000;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
142
|
// ---------------------------------------------------------------------------
|
|
200
143
|
// HTTP client
|
|
201
144
|
// ---------------------------------------------------------------------------
|
|
@@ -246,31 +189,13 @@ class KolboClient {
|
|
|
246
189
|
}
|
|
247
190
|
|
|
248
191
|
async request(method, reqPath, body = null) {
|
|
249
|
-
await
|
|
250
|
-
try {
|
|
251
|
-
const result = await this._doRequestWithRetry(method, reqPath, body);
|
|
192
|
+
const result = await this._doRequest(method, reqPath, body);
|
|
252
193
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
257
|
-
return result;
|
|
258
|
-
} finally {
|
|
259
|
-
releaseSlot();
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
async _doRequestWithRetry(method, reqPath, body = null) {
|
|
264
|
-
try {
|
|
265
|
-
return await this._doRequest(method, reqPath, body);
|
|
266
|
-
} catch (err) {
|
|
267
|
-
if (err instanceof KolboApiError && err.status === 429) {
|
|
268
|
-
const delay = err.data?._kolboRetryAfterMs ?? 5000;
|
|
269
|
-
await sleep(delay);
|
|
270
|
-
return this._doRequest(method, reqPath, body);
|
|
271
|
-
}
|
|
272
|
-
throw err;
|
|
194
|
+
// On 401, try re-reading auth store and retry once
|
|
195
|
+
if (result._status === 401 && this._tryRefreshKey()) {
|
|
196
|
+
return this._doRequest(method, reqPath, body);
|
|
273
197
|
}
|
|
198
|
+
return result;
|
|
274
199
|
}
|
|
275
200
|
|
|
276
201
|
async _doRequest(method, reqPath, body = null) {
|
|
@@ -307,13 +232,6 @@ class KolboClient {
|
|
|
307
232
|
});
|
|
308
233
|
}
|
|
309
234
|
|
|
310
|
-
if (response.status === 429) {
|
|
311
|
-
throw new KolboApiError('Rate limited by Kolbo API. Retrying after backoff.', {
|
|
312
|
-
status: 429,
|
|
313
|
-
data: { ...(data || {}), _kolboRetryAfterMs: parseRetryAfter(response, data) },
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
|
|
317
235
|
if (!response.ok || data.success === false) {
|
|
318
236
|
const message = data.error || data.message || `API error: ${response.status}`;
|
|
319
237
|
const code = data.code || null;
|
|
@@ -360,29 +278,11 @@ class KolboClient {
|
|
|
360
278
|
}
|
|
361
279
|
|
|
362
280
|
async postMultipart(reqPath, formData) {
|
|
363
|
-
await
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
if (result._status === 401 && this._tryRefreshKey()) {
|
|
367
|
-
return this._doMultipartWithRetry(reqPath, formData);
|
|
368
|
-
}
|
|
369
|
-
return result;
|
|
370
|
-
} finally {
|
|
371
|
-
releaseSlot();
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
async _doMultipartWithRetry(reqPath, formData) {
|
|
376
|
-
try {
|
|
377
|
-
return await this._doMultipart(reqPath, formData);
|
|
378
|
-
} catch (err) {
|
|
379
|
-
if (err instanceof KolboApiError && err.status === 429) {
|
|
380
|
-
const delay = err.data?._kolboRetryAfterMs ?? 5000;
|
|
381
|
-
await sleep(delay);
|
|
382
|
-
return this._doMultipart(reqPath, formData);
|
|
383
|
-
}
|
|
384
|
-
throw err;
|
|
281
|
+
const result = await this._doMultipart(reqPath, formData);
|
|
282
|
+
if (result._status === 401 && this._tryRefreshKey()) {
|
|
283
|
+
return this._doMultipart(reqPath, formData);
|
|
385
284
|
}
|
|
285
|
+
return result;
|
|
386
286
|
}
|
|
387
287
|
|
|
388
288
|
async _doMultipart(reqPath, formData) {
|
|
@@ -419,13 +319,6 @@ class KolboClient {
|
|
|
419
319
|
});
|
|
420
320
|
}
|
|
421
321
|
|
|
422
|
-
if (response.status === 429) {
|
|
423
|
-
throw new KolboApiError('Rate limited by Kolbo API. Retrying after backoff.', {
|
|
424
|
-
status: 429,
|
|
425
|
-
data: { ...(data || {}), _kolboRetryAfterMs: parseRetryAfter(response, data) },
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
|
|
429
322
|
if (!response.ok || data.success === false) {
|
|
430
323
|
const message = data.error || data.message || `API error: ${response.status}`;
|
|
431
324
|
const code = data.code || null;
|
package/src/index.js
CHANGED
|
@@ -68,6 +68,7 @@ const { registerMediaTools } = require('./tools/media');
|
|
|
68
68
|
const { registerPresetTools } = require('./tools/presets');
|
|
69
69
|
const { registerAppBuilderTools } = require('./tools/app_builder');
|
|
70
70
|
const { registerArtifactTools } = require('./tools/artifacts');
|
|
71
|
+
const { registerProjectTools } = require('./tools/projects');
|
|
71
72
|
|
|
72
73
|
async function main() {
|
|
73
74
|
const client = new KolboClient();
|
|
@@ -87,6 +88,7 @@ async function main() {
|
|
|
87
88
|
registerPresetTools(server, client);
|
|
88
89
|
registerAppBuilderTools(server, client);
|
|
89
90
|
registerArtifactTools(server, client);
|
|
91
|
+
registerProjectTools(server, client);
|
|
90
92
|
|
|
91
93
|
// Start the server with stdio transport
|
|
92
94
|
const transport = new StdioServerTransport();
|
package/src/polling.js
CHANGED
|
@@ -6,10 +6,9 @@ class PollingTimeoutError extends Error {
|
|
|
6
6
|
constructor(generationId, timeoutMs) {
|
|
7
7
|
const seconds = Math.round(timeoutMs / 1000);
|
|
8
8
|
super(
|
|
9
|
-
`Generation timed out after ${seconds}s of polling. The generation
|
|
10
|
-
`
|
|
11
|
-
`
|
|
12
|
-
`Videos, deep-think chat, and large batches can legitimately take 5–15+ minutes beyond the default polling window.`
|
|
9
|
+
`Generation timed out after ${seconds}s of polling. The generation may STILL be running on the server — ` +
|
|
10
|
+
`call get_generation_status with generation_id="${generationId}" to check its current state. ` +
|
|
11
|
+
`Videos, deep-think chat, and large batches can take longer than the default polling window.`
|
|
13
12
|
);
|
|
14
13
|
this.name = 'PollingTimeoutError';
|
|
15
14
|
this.generationId = generationId;
|
package/src/tools/_shared.js
CHANGED
|
@@ -230,6 +230,16 @@ function creditFields(polledResult) {
|
|
|
230
230
|
return out;
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
// Shared zod schema for the optional `project_id` arg every generation tool
|
|
234
|
+
// accepts. Keep this in one place so the description never drifts across the
|
|
235
|
+
// 17 tools that use it. When omitted, the generation lands in the user's
|
|
236
|
+
// auto-created "API Generations" project. Call `list_projects` first to
|
|
237
|
+
// resolve a name → ObjectId.
|
|
238
|
+
const { z } = require('zod');
|
|
239
|
+
const projectIdField = z.string().optional().describe(
|
|
240
|
+
'Project ObjectId to drop this generation into. Call `list_projects` to discover IDs (the API has no concept of project names — only ObjectIds). Omit to use the user\'s default "API Generations" project. Requires owner / edit / full permission on the project; view-only is rejected.'
|
|
241
|
+
);
|
|
242
|
+
|
|
233
243
|
module.exports = {
|
|
234
244
|
MAX_FILE_BYTES,
|
|
235
245
|
VISUAL_DNA_MAX_BYTES,
|
|
@@ -240,4 +250,5 @@ module.exports = {
|
|
|
240
250
|
guessContentType,
|
|
241
251
|
resolveToBuffer,
|
|
242
252
|
creditFields,
|
|
253
|
+
projectIdField,
|
|
243
254
|
};
|
package/src/tools/artifacts.js
CHANGED
|
@@ -9,23 +9,29 @@ function registerArtifactTools(server, client) {
|
|
|
9
9
|
// ─── publish_html_artifact ─────────────────────────────────────
|
|
10
10
|
server.tool(
|
|
11
11
|
'publish_html_artifact',
|
|
12
|
-
'Publish an HTML page (or SVG / Mermaid diagram) to kolbo.ai and return a public shareable URL. Use this when the user explicitly asks to share, publish, or deploy a built artifact so they can send the URL to someone. The content is hosted at https://sites.kolbo.ai/<slug>; the page is served with restrictive CSP (no fetch/XHR/form-action) so it cannot exfiltrate data. Identical content uploaded twice returns the same URL (server dedup).',
|
|
12
|
+
'Publish an HTML page (or SVG / Mermaid diagram) to kolbo.ai and return a public shareable URL. Use this when the user explicitly asks to share, publish, or deploy a built artifact so they can send the URL to someone. The content is hosted at https://sites.kolbo.ai/<slug>; the page is served with restrictive CSP (no fetch/XHR/form-action) so it cannot exfiltrate data. Identical content uploaded twice returns the same URL (server dedup). To update a previously-published page in place (keeping the same URL), pass the `share_token` returned from the prior publish — the old content is preserved in version history.',
|
|
13
13
|
{
|
|
14
14
|
title: z.string().describe('Human-friendly title for the page (also used to generate the SEO slug). Keep under ~60 chars.'),
|
|
15
15
|
content: z.string().describe('The raw artifact body. For type="html" this is a full HTML document (DOCTYPE + html/head/body). For "svg" it is an <svg> document. For "mermaid" it is the Mermaid source text.'),
|
|
16
16
|
type: z.enum(['html', 'svg', 'mermaid']).optional().describe('Artifact type. Default: "html".'),
|
|
17
17
|
allow_js: z.boolean().optional().describe('Allow inline <script> execution on the published page. Default: false. Required for Tailwind JIT, Chart.js, Three.js, React-from-CDN etc.'),
|
|
18
|
+
share_token: z.string().optional().describe('Optional. Pass the `shareToken` returned from a previous publish to update that artifact in place. The public URL stays the same and the old content is moved into version history. Omit this on the first publish.'),
|
|
18
19
|
},
|
|
19
|
-
async ({ title, content, type, allow_js }) => {
|
|
20
|
+
async ({ title, content, type, allow_js, share_token }) => {
|
|
20
21
|
if (!title || !title.trim()) throw new Error('title is required');
|
|
21
22
|
if (typeof content !== 'string' || !content.length) throw new Error('content is required');
|
|
22
23
|
|
|
23
|
-
const
|
|
24
|
+
const body = {
|
|
24
25
|
title: title.trim(),
|
|
25
26
|
content,
|
|
26
27
|
type: type || 'html',
|
|
27
28
|
allowJs: allow_js === true,
|
|
28
|
-
}
|
|
29
|
+
};
|
|
30
|
+
if (typeof share_token === 'string' && share_token.trim()) {
|
|
31
|
+
body.shareToken = share_token.trim();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const result = await client.post('/artifact/quick-share', body);
|
|
29
35
|
|
|
30
36
|
const artifact = result?.data || {};
|
|
31
37
|
const slug = artifact.shareableSlug || artifact.shareToken;
|
|
@@ -49,6 +55,7 @@ function registerArtifactTools(server, client) {
|
|
|
49
55
|
shareToken: artifact.shareToken,
|
|
50
56
|
shareableSlug: slug,
|
|
51
57
|
duplicate: result?.duplicate === true,
|
|
58
|
+
updated: result?.updated === true,
|
|
52
59
|
title: artifact.title,
|
|
53
60
|
}),
|
|
54
61
|
}],
|