UNPKG

@kolbo/mcp

Version:

Kolbo AI MCP Server - Generate images, videos, music, speech, and sound effects from Claude Code

339 lines (313 loc) • 14.4 kB
/* ⛔ BACKWARD COMPATIBILITY: Tool names and arg names below are a PUBLIC * CONTRACT. Never rename, remove, or break an existing tool/arg — old cached * `npx @kolbo/mcp` installs in the wild will break silently. Add new tools or * new OPTIONAL args only. Full rules: ../index.js top-of-file and CLAUDE.md. */ const { z } = require('zod'); const { projectIdField } = require('./_shared'); const REVIEW_STATUS = z.enum(['in_progress', 'needs_review', 'approved', 'changes_requested']); function registerReviewTools(server, client) { // ─── get_review_storage_usage ──────────────────────────────── server.tool( 'get_review_storage_usage', 'Get Kolbo Review storage usage for the API-key owner (5GB cap across all review versions).', {}, async () => { const result = await client.get('/v1/review/storage-usage'); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); // ─── list_review_assets ────────────────────────────────────── server.tool( 'list_review_assets', 'List review assets in a project (Frame.io-style client review). Pass `project_id` from `list_projects`. Optional filters: `collection_id`, `status`, pagination.', { project_id: projectIdField, collection_id: z.string().optional().describe('Filter to one review collection.'), status: REVIEW_STATUS.optional(), page: z.number().optional(), limit: z.number().optional(), }, async ({ project_id, collection_id, status, page, limit }) => { const params = new URLSearchParams(); if (project_id) params.set('project_id', project_id); if (collection_id) params.set('collection_id', collection_id); if (status) params.set('status', status); if (page) params.set('page', String(page)); if (limit) params.set('limit', String(limit)); const qs = params.toString(); const result = await client.get(`/v1/review/assets${qs ? '?' + qs : ''}`); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); // ─── get_review_asset ──────────────────────────────────────── server.tool( 'get_review_asset', 'Fetch one review asset with all versions, status, and media URLs.', { asset_id: z.string().describe('Review asset ObjectId from list_review_assets or create_review_asset.'), }, async ({ asset_id }) => { const result = await client.get(`/v1/review/assets/${encodeURIComponent(asset_id)}`); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); // ─── create_review_asset ───────────────────────────────────── server.tool( 'create_review_asset', '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.', { name: z.string().describe('Display name for the review asset.'), media_id: z.string().describe('MediaLibraryItem id from upload_media / list_media.'), project_id: projectIdField, collection_id: z.string().optional().describe('Optional review collection folder id.'), version_note: z.string().optional().describe('Optional note on the first version (max 1000 chars).'), }, async (args) => { const result = await client.post('/v1/review/assets', args); return { content: [{ type: 'text', text: JSON.stringify({ ...result, _hint: 'Asset created. Share with clients via create_review_share_link, or add feedback with create_review_comment.', }, null, 2), }], }; } ); // ─── update_review_asset ───────────────────────────────────── server.tool( 'update_review_asset', 'Rename a review asset, move it to a collection, or switch the active version index.', { asset_id: z.string(), name: z.string().optional(), collection_id: z.string().nullable().optional().describe('Collection id, or null to uncollected.'), current_version_index: z.number().optional(), }, async ({ asset_id, ...body }) => { const result = await client.patch(`/v1/review/assets/${encodeURIComponent(asset_id)}`, body); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); // ─── add_review_version ────────────────────────────────────── server.tool( 'add_review_version', 'Append a new version to an existing review asset from an uploaded `media_id`.', { asset_id: z.string(), media_id: z.string(), version_note: z.string().optional(), }, async ({ asset_id, media_id, version_note }) => { const result = await client.post(`/v1/review/assets/${encodeURIComponent(asset_id)}/versions`, { media_id, version_note, }); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); // ─── set_review_status ─────────────────────────────────────── server.tool( 'set_review_status', 'Update review workflow status on an asset (in_progress, needs_review, approved, changes_requested).', { asset_id: z.string(), review_status: REVIEW_STATUS, }, async ({ asset_id, review_status }) => { const result = await client.post(`/v1/review/assets/${encodeURIComponent(asset_id)}/status`, { review_status, }); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); // ─── delete_review_asset ───────────────────────────────────── server.tool( 'delete_review_asset', 'Soft-delete a review asset and its underlying review media.', { asset_id: z.string() }, async ({ asset_id }) => { const result = await client.delete(`/v1/review/assets/${encodeURIComponent(asset_id)}`); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); // ─── Collections ───────────────────────────────────────────── server.tool( 'list_review_collections', 'List review collection folders in a project.', { project_id: projectIdField }, async ({ project_id }) => { const qs = project_id ? `project_id=${encodeURIComponent(project_id)}` : ''; const result = await client.get(`/v1/review/collections${qs ? '?' + qs : ''}`); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); server.tool( 'create_review_collection', 'Create a review collection folder inside a project.', { name: z.string(), project_id: projectIdField, }, async (args) => { const result = await client.post('/v1/review/collections', args); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); server.tool( 'update_review_collection', 'Rename a review collection.', { collection_id: z.string(), name: z.string(), }, async ({ collection_id, name }) => { const result = await client.patch(`/v1/review/collections/${encodeURIComponent(collection_id)}`, { name }); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); server.tool( 'delete_review_collection', 'Soft-delete a review collection (assets become uncollected).', { collection_id: z.string() }, async ({ collection_id }) => { const result = await client.delete(`/v1/review/collections/${encodeURIComponent(collection_id)}`); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); // ─── Comments ──────────────────────────────────────────────── server.tool( 'list_review_comments', 'List text comments on a review asset (default: current version media). Optional `version_media_id` for a specific version.', { asset_id: z.string(), version_media_id: z.string().optional(), }, async ({ asset_id, version_media_id }) => { const qs = version_media_id ? `version_media_id=${encodeURIComponent(version_media_id)}` : ''; const result = await client.get(`/v1/review/assets/${encodeURIComponent(asset_id)}/comments${qs ? '?' + qs : ''}`); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); server.tool( 'create_review_comment', 'Add a text comment on a review asset. Optional video timecodes: time_start / time_end (seconds).', { asset_id: z.string(), body: z.string(), time_start: z.number().optional(), time_end: z.number().optional(), version_media_id: z.string().optional(), }, async ({ asset_id, ...body }) => { const result = await client.post(`/v1/review/assets/${encodeURIComponent(asset_id)}/comments`, body); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); server.tool( 'reply_review_comment', 'Reply to an existing review comment (one level of threading).', { note_id: z.string().describe('Comment id from list_review_comments.'), body: z.string(), }, async ({ note_id, body }) => { const result = await client.post(`/v1/review/comments/${encodeURIComponent(note_id)}/reply`, { body }); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); server.tool( 'edit_review_comment', 'Edit your own review comment text and/or timecodes.', { note_id: z.string(), body: z.string().optional(), time_start: z.number().optional(), time_end: z.number().optional(), }, async ({ note_id, ...body }) => { const result = await client.patch(`/v1/review/comments/${encodeURIComponent(note_id)}`, body); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); server.tool( 'delete_review_comment', 'Delete a review comment (own comment, or any comment if you have full project access).', { note_id: z.string() }, async ({ note_id }) => { const result = await client.delete(`/v1/review/comments/${encodeURIComponent(note_id)}`); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); server.tool( 'resolve_review_comment', 'Mark a review comment thread as resolved.', { note_id: z.string() }, async ({ note_id }) => { const result = await client.post(`/v1/review/comments/${encodeURIComponent(note_id)}/resolve`, {}); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); server.tool( 'unresolve_review_comment', 'Re-open a resolved review comment thread.', { note_id: z.string() }, async ({ note_id }) => { const result = await client.post(`/v1/review/comments/${encodeURIComponent(note_id)}/unresolve`, {}); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); // ─── Share links ───────────────────────────────────────────── server.tool( 'create_review_share_link', 'Create a guest review link for an asset or collection. Returns share_url for clients (no Kolbo account needed).', { target_type: z.enum(['asset', 'collection']), target_id: z.string(), role_label: z.string().optional().describe('Guest role label shown in the UI (e.g. Client).'), require_email: z.boolean().optional(), permissions: z.object({ canComment: z.boolean().optional(), canDownload: z.boolean().optional(), canViewOtherComments: z.boolean().optional(), canResolveOwn: z.boolean().optional(), canSwitchVersions: z.boolean().optional(), canSetStatus: z.boolean().optional(), }).optional(), password: z.string().optional().describe('Optional NEW password to set on this guest review link. This is not the user\'s Kolbo password and must never contain an account credential.'), allowed_emails: z.array(z.string()).optional(), expires_at: z.string().optional().describe('ISO8601 expiry datetime.'), }, async ({ target_type, target_id, ...body }) => { const path = target_type === 'collection' ? `/v1/review/collections/${encodeURIComponent(target_id)}/share-links` : `/v1/review/assets/${encodeURIComponent(target_id)}/share-links`; const result = await client.post(path, body); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); server.tool( 'list_review_share_links', 'List active share links for a review asset or collection.', { target_type: z.enum(['asset', 'collection']), target_id: z.string(), }, async ({ target_type, target_id }) => { const params = new URLSearchParams({ target_type, target_id }); const result = await client.get(`/v1/review/share-links?${params}`); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); server.tool( 'revoke_review_share_link', 'Revoke a guest review share link by link id.', { link_id: z.string() }, async ({ link_id }) => { const result = await client.post(`/v1/review/share-links/${encodeURIComponent(link_id)}/revoke`, {}); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } ); } module.exports = { registerReviewTools };