@kolbo/mcp 1.65.1 → 1.66.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skill/SKILL.md +1 -0
- package/src/index.js +2 -0
- package/src/tools/review.js +338 -0
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -125,6 +125,7 @@ Each `references/models/*.md` mirrors the matching skill prompt in `kolbo-api/sr
|
|
|
125
125
|
| `clone_voice` / `import_elevenlabs_voice` / `delete_voice` | Custom voices (clone CHARGES CREDITS — confirm first; new voices show in `list_voices`) |
|
|
126
126
|
| `trim_video` | Frame-accurate trim of a Kolbo-hosted video (tool waits and returns the URL). `edit_video` also gained `remove_background`. |
|
|
127
127
|
| `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. |
|
|
128
|
+
| `create_review_asset` / `list_review_assets` / `get_review_asset` / `update_review_asset` / `add_review_version` / `set_review_status` / `delete_review_asset` / `get_review_storage_usage` / review collection + comment + share-link tools | **Kolbo Review** (Frame.io-style): upload first via `upload_media` / ticket / widget → pass `media_id` to `create_review_asset`. Comments are text + optional timecodes. `create_review_share_link` returns a guest URL. |
|
|
128
129
|
| `chat_send_message` / `chat_list_conversations` / `chat_get_messages` | Kolbo chat with optional `media_urls` (up to 10 per call) |
|
|
129
130
|
| `publish_html_artifact` | Publish HTML / SVG / Mermaid to `sites.kolbo.ai`. Server dedupes by content hash. Strict CSP. |
|
|
130
131
|
|
package/src/index.js
CHANGED
|
@@ -72,6 +72,7 @@ const { registerArtifactTools } = require('./tools/artifacts');
|
|
|
72
72
|
const { registerProjectTools } = require('./tools/projects');
|
|
73
73
|
const { registerAgentTools } = require('./tools/agents');
|
|
74
74
|
const { registerDocTools } = require('./tools/docs');
|
|
75
|
+
const { registerReviewTools } = require('./tools/review');
|
|
75
76
|
const { registerVoiceTools } = require('./tools/voices');
|
|
76
77
|
const { registerMusicLibraryTools } = require('./tools/music_library');
|
|
77
78
|
const { registerStockLibraryTools } = require('./tools/stock_library');
|
|
@@ -156,6 +157,7 @@ function createServer(opts = {}) {
|
|
|
156
157
|
registerProjectTools(server, client, toolOptions);
|
|
157
158
|
registerAgentTools(server, client, toolOptions);
|
|
158
159
|
registerDocTools(server, client, toolOptions);
|
|
160
|
+
registerReviewTools(server, client, toolOptions);
|
|
159
161
|
registerMusicLibraryTools(server, client, toolOptions);
|
|
160
162
|
registerStockLibraryTools(server, client, toolOptions);
|
|
161
163
|
|
|
@@ -0,0 +1,338 @@
|
|
|
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
|
+
const { projectIdField } = require('./_shared');
|
|
8
|
+
|
|
9
|
+
const REVIEW_STATUS = z.enum(['in_progress', 'needs_review', 'approved', 'changes_requested']);
|
|
10
|
+
|
|
11
|
+
function registerReviewTools(server, client) {
|
|
12
|
+
// ─── get_review_storage_usage ────────────────────────────────
|
|
13
|
+
server.tool(
|
|
14
|
+
'get_review_storage_usage',
|
|
15
|
+
'Get Kolbo Review storage usage for the API-key owner (5GB cap across all review versions).',
|
|
16
|
+
{},
|
|
17
|
+
async () => {
|
|
18
|
+
const result = await client.get('/v1/review/storage-usage');
|
|
19
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// ─── list_review_assets ──────────────────────────────────────
|
|
24
|
+
server.tool(
|
|
25
|
+
'list_review_assets',
|
|
26
|
+
'List review assets in a project (Frame.io-style client review). Pass `project_id` from `list_projects`. Optional filters: `collection_id`, `status`, pagination.',
|
|
27
|
+
{
|
|
28
|
+
project_id: projectIdField,
|
|
29
|
+
collection_id: z.string().optional().describe('Filter to one review collection.'),
|
|
30
|
+
status: REVIEW_STATUS.optional(),
|
|
31
|
+
page: z.number().optional(),
|
|
32
|
+
limit: z.number().optional(),
|
|
33
|
+
},
|
|
34
|
+
async ({ project_id, collection_id, status, page, limit }) => {
|
|
35
|
+
const params = new URLSearchParams();
|
|
36
|
+
if (project_id) params.set('project_id', project_id);
|
|
37
|
+
if (collection_id) params.set('collection_id', collection_id);
|
|
38
|
+
if (status) params.set('status', status);
|
|
39
|
+
if (page) params.set('page', String(page));
|
|
40
|
+
if (limit) params.set('limit', String(limit));
|
|
41
|
+
const qs = params.toString();
|
|
42
|
+
const result = await client.get(`/v1/review/assets${qs ? '?' + qs : ''}`);
|
|
43
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// ─── get_review_asset ────────────────────────────────────────
|
|
48
|
+
server.tool(
|
|
49
|
+
'get_review_asset',
|
|
50
|
+
'Fetch one review asset with all versions, status, and media URLs.',
|
|
51
|
+
{
|
|
52
|
+
asset_id: z.string().describe('Review asset ObjectId from list_review_assets or create_review_asset.'),
|
|
53
|
+
},
|
|
54
|
+
async ({ asset_id }) => {
|
|
55
|
+
const result = await client.get(`/v1/review/assets/${encodeURIComponent(asset_id)}`);
|
|
56
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// ─── create_review_asset ─────────────────────────────────────
|
|
61
|
+
server.tool(
|
|
62
|
+
'create_review_asset',
|
|
63
|
+
'Create a Kolbo Review asset with v1 media attached. Upload first via `upload_media`, `create_upload_ticket`, or `media_upload_widget`, then pass the returned `media_id`. Requires `project_id` when the user named a project.',
|
|
64
|
+
{
|
|
65
|
+
name: z.string().describe('Display name for the review asset.'),
|
|
66
|
+
media_id: z.string().describe('MediaLibraryItem id from upload_media / list_media.'),
|
|
67
|
+
project_id: projectIdField,
|
|
68
|
+
collection_id: z.string().optional().describe('Optional review collection folder id.'),
|
|
69
|
+
version_note: z.string().optional().describe('Optional note on the first version (max 1000 chars).'),
|
|
70
|
+
},
|
|
71
|
+
async (args) => {
|
|
72
|
+
const result = await client.post('/v1/review/assets', args);
|
|
73
|
+
return {
|
|
74
|
+
content: [{
|
|
75
|
+
type: 'text',
|
|
76
|
+
text: JSON.stringify({
|
|
77
|
+
...result,
|
|
78
|
+
_hint: 'Asset created. Share with clients via create_review_share_link, or add feedback with create_review_comment.',
|
|
79
|
+
}, null, 2),
|
|
80
|
+
}],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// ─── update_review_asset ─────────────────────────────────────
|
|
86
|
+
server.tool(
|
|
87
|
+
'update_review_asset',
|
|
88
|
+
'Rename a review asset, move it to a collection, or switch the active version index.',
|
|
89
|
+
{
|
|
90
|
+
asset_id: z.string(),
|
|
91
|
+
name: z.string().optional(),
|
|
92
|
+
collection_id: z.string().nullable().optional().describe('Collection id, or null to uncollected.'),
|
|
93
|
+
current_version_index: z.number().optional(),
|
|
94
|
+
},
|
|
95
|
+
async ({ asset_id, ...body }) => {
|
|
96
|
+
const result = await client.patch(`/v1/review/assets/${encodeURIComponent(asset_id)}`, body);
|
|
97
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// ─── add_review_version ──────────────────────────────────────
|
|
102
|
+
server.tool(
|
|
103
|
+
'add_review_version',
|
|
104
|
+
'Append a new version to an existing review asset from an uploaded `media_id`.',
|
|
105
|
+
{
|
|
106
|
+
asset_id: z.string(),
|
|
107
|
+
media_id: z.string(),
|
|
108
|
+
version_note: z.string().optional(),
|
|
109
|
+
},
|
|
110
|
+
async ({ asset_id, media_id, version_note }) => {
|
|
111
|
+
const result = await client.post(`/v1/review/assets/${encodeURIComponent(asset_id)}/versions`, {
|
|
112
|
+
media_id,
|
|
113
|
+
version_note,
|
|
114
|
+
});
|
|
115
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// ─── set_review_status ───────────────────────────────────────
|
|
120
|
+
server.tool(
|
|
121
|
+
'set_review_status',
|
|
122
|
+
'Update review workflow status on an asset (in_progress, needs_review, approved, changes_requested).',
|
|
123
|
+
{
|
|
124
|
+
asset_id: z.string(),
|
|
125
|
+
review_status: REVIEW_STATUS,
|
|
126
|
+
},
|
|
127
|
+
async ({ asset_id, review_status }) => {
|
|
128
|
+
const result = await client.post(`/v1/review/assets/${encodeURIComponent(asset_id)}/status`, {
|
|
129
|
+
review_status,
|
|
130
|
+
});
|
|
131
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// ─── delete_review_asset ─────────────────────────────────────
|
|
136
|
+
server.tool(
|
|
137
|
+
'delete_review_asset',
|
|
138
|
+
'Soft-delete a review asset and its underlying review media.',
|
|
139
|
+
{ asset_id: z.string() },
|
|
140
|
+
async ({ asset_id }) => {
|
|
141
|
+
const result = await client.delete(`/v1/review/assets/${encodeURIComponent(asset_id)}`);
|
|
142
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// ─── Collections ─────────────────────────────────────────────
|
|
147
|
+
server.tool(
|
|
148
|
+
'list_review_collections',
|
|
149
|
+
'List review collection folders in a project.',
|
|
150
|
+
{ project_id: projectIdField },
|
|
151
|
+
async ({ project_id }) => {
|
|
152
|
+
const params = project_id ? `?project_id=${encodeURIComponent(project_id)}` : '';
|
|
153
|
+
const result = await client.get(`/v1/review/collections${params}`);
|
|
154
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
server.tool(
|
|
159
|
+
'create_review_collection',
|
|
160
|
+
'Create a review collection folder inside a project.',
|
|
161
|
+
{
|
|
162
|
+
name: z.string(),
|
|
163
|
+
project_id: projectIdField,
|
|
164
|
+
},
|
|
165
|
+
async (args) => {
|
|
166
|
+
const result = await client.post('/v1/review/collections', args);
|
|
167
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
server.tool(
|
|
172
|
+
'update_review_collection',
|
|
173
|
+
'Rename a review collection.',
|
|
174
|
+
{
|
|
175
|
+
collection_id: z.string(),
|
|
176
|
+
name: z.string(),
|
|
177
|
+
},
|
|
178
|
+
async ({ collection_id, name }) => {
|
|
179
|
+
const result = await client.patch(`/v1/review/collections/${encodeURIComponent(collection_id)}`, { name });
|
|
180
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
server.tool(
|
|
185
|
+
'delete_review_collection',
|
|
186
|
+
'Soft-delete a review collection (assets become uncollected).',
|
|
187
|
+
{ collection_id: z.string() },
|
|
188
|
+
async ({ collection_id }) => {
|
|
189
|
+
const result = await client.delete(`/v1/review/collections/${encodeURIComponent(collection_id)}`);
|
|
190
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
// ─── Comments ────────────────────────────────────────────────
|
|
195
|
+
server.tool(
|
|
196
|
+
'list_review_comments',
|
|
197
|
+
'List text comments on a review asset (default: current version media). Optional `version_media_id` for a specific version.',
|
|
198
|
+
{
|
|
199
|
+
asset_id: z.string(),
|
|
200
|
+
version_media_id: z.string().optional(),
|
|
201
|
+
},
|
|
202
|
+
async ({ asset_id, version_media_id }) => {
|
|
203
|
+
const params = version_media_id ? `?version_media_id=${encodeURIComponent(version_media_id)}` : '';
|
|
204
|
+
const result = await client.get(`/v1/review/assets/${encodeURIComponent(asset_id)}/comments${params}`);
|
|
205
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
server.tool(
|
|
210
|
+
'create_review_comment',
|
|
211
|
+
'Add a text comment on a review asset. Optional video timecodes: time_start / time_end (seconds).',
|
|
212
|
+
{
|
|
213
|
+
asset_id: z.string(),
|
|
214
|
+
body: z.string(),
|
|
215
|
+
time_start: z.number().optional(),
|
|
216
|
+
time_end: z.number().optional(),
|
|
217
|
+
version_media_id: z.string().optional(),
|
|
218
|
+
},
|
|
219
|
+
async ({ asset_id, ...body }) => {
|
|
220
|
+
const result = await client.post(`/v1/review/assets/${encodeURIComponent(asset_id)}/comments`, body);
|
|
221
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
222
|
+
}
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
server.tool(
|
|
226
|
+
'reply_review_comment',
|
|
227
|
+
'Reply to an existing review comment (one level of threading).',
|
|
228
|
+
{
|
|
229
|
+
note_id: z.string().describe('Comment id from list_review_comments.'),
|
|
230
|
+
body: z.string(),
|
|
231
|
+
},
|
|
232
|
+
async ({ note_id, body }) => {
|
|
233
|
+
const result = await client.post(`/v1/review/comments/${encodeURIComponent(note_id)}/reply`, { body });
|
|
234
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
235
|
+
}
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
server.tool(
|
|
239
|
+
'edit_review_comment',
|
|
240
|
+
'Edit your own review comment text and/or timecodes.',
|
|
241
|
+
{
|
|
242
|
+
note_id: z.string(),
|
|
243
|
+
body: z.string().optional(),
|
|
244
|
+
time_start: z.number().optional(),
|
|
245
|
+
time_end: z.number().optional(),
|
|
246
|
+
},
|
|
247
|
+
async ({ note_id, ...body }) => {
|
|
248
|
+
const result = await client.patch(`/v1/review/comments/${encodeURIComponent(note_id)}`, body);
|
|
249
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
server.tool(
|
|
254
|
+
'delete_review_comment',
|
|
255
|
+
'Delete a review comment (own comment, or any comment if you have full project access).',
|
|
256
|
+
{ note_id: z.string() },
|
|
257
|
+
async ({ note_id }) => {
|
|
258
|
+
const result = await client.delete(`/v1/review/comments/${encodeURIComponent(note_id)}`);
|
|
259
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
server.tool(
|
|
264
|
+
'resolve_review_comment',
|
|
265
|
+
'Mark a review comment thread as resolved.',
|
|
266
|
+
{ note_id: z.string() },
|
|
267
|
+
async ({ note_id }) => {
|
|
268
|
+
const result = await client.post(`/v1/review/comments/${encodeURIComponent(note_id)}/resolve`, {});
|
|
269
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
270
|
+
}
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
server.tool(
|
|
274
|
+
'unresolve_review_comment',
|
|
275
|
+
'Re-open a resolved review comment thread.',
|
|
276
|
+
{ note_id: z.string() },
|
|
277
|
+
async ({ note_id }) => {
|
|
278
|
+
const result = await client.post(`/v1/review/comments/${encodeURIComponent(note_id)}/unresolve`, {});
|
|
279
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
280
|
+
}
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
// ─── Share links ─────────────────────────────────────────────
|
|
284
|
+
server.tool(
|
|
285
|
+
'create_review_share_link',
|
|
286
|
+
'Create a guest review link for an asset or collection. Returns share_url for clients (no Kolbo account needed).',
|
|
287
|
+
{
|
|
288
|
+
target_type: z.enum(['asset', 'collection']),
|
|
289
|
+
target_id: z.string(),
|
|
290
|
+
role_label: z.string().optional().describe('Guest role label shown in the UI (e.g. Client).'),
|
|
291
|
+
require_email: z.boolean().optional(),
|
|
292
|
+
permissions: z.object({
|
|
293
|
+
canComment: z.boolean().optional(),
|
|
294
|
+
canDownload: z.boolean().optional(),
|
|
295
|
+
canViewOtherComments: z.boolean().optional(),
|
|
296
|
+
canResolveOwn: z.boolean().optional(),
|
|
297
|
+
canSwitchVersions: z.boolean().optional(),
|
|
298
|
+
canSetStatus: z.boolean().optional(),
|
|
299
|
+
}).optional(),
|
|
300
|
+
password: z.string().optional(),
|
|
301
|
+
allowed_emails: z.array(z.string()).optional(),
|
|
302
|
+
expires_at: z.string().optional().describe('ISO8601 expiry datetime.'),
|
|
303
|
+
},
|
|
304
|
+
async ({ target_type, target_id, ...body }) => {
|
|
305
|
+
const path = target_type === 'collection'
|
|
306
|
+
? `/v1/review/collections/${encodeURIComponent(target_id)}/share-links`
|
|
307
|
+
: `/v1/review/assets/${encodeURIComponent(target_id)}/share-links`;
|
|
308
|
+
const result = await client.post(path, body);
|
|
309
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
server.tool(
|
|
314
|
+
'list_review_share_links',
|
|
315
|
+
'List active share links for a review asset or collection.',
|
|
316
|
+
{
|
|
317
|
+
target_type: z.enum(['asset', 'collection']),
|
|
318
|
+
target_id: z.string(),
|
|
319
|
+
},
|
|
320
|
+
async ({ target_type, target_id }) => {
|
|
321
|
+
const params = new URLSearchParams({ target_type, target_id });
|
|
322
|
+
const result = await client.get(`/v1/review/share-links?${params}`);
|
|
323
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
324
|
+
}
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
server.tool(
|
|
328
|
+
'revoke_review_share_link',
|
|
329
|
+
'Revoke a guest review share link by link id.',
|
|
330
|
+
{ link_id: z.string() },
|
|
331
|
+
async ({ link_id }) => {
|
|
332
|
+
const result = await client.post(`/v1/review/share-links/${encodeURIComponent(link_id)}/revoke`, {});
|
|
333
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
334
|
+
}
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
module.exports = { registerReviewTools };
|