UNPKG

@kolbo/mcp

Version:

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

189 lines (175 loc) • 9.19 kB
'use strict'; /** * Public MCP safety contract. * * ChatGPT app review requires every exposed tool to declare all three hints. * Keep this map exact: the submission gate rejects missing, duplicate, stale, * or non-boolean entries whenever the registered tool surface changes. */ const READ_ONLY = [ 'get_flow_schema', 'list_flow_sessions', 'get_flow_session', 'validate_flow_session', 'estimate_flow_run', 'get_flow_run', 'list_flow_runs', 'list_morphious_styles', 'list_trends', 'get_trend', 'estimate_trend_run', 'get_trend_run', 'list_trend_runs', 'get_download_status', 'get_video_editor_schema', 'list_video_editor_sessions', 'get_video_editor_session', 'list_fonts', 'get_font', 'get_font_upload_status', 'get_creative_director_status', 'get_generation_status', 'list_models', 'check_credits', 'show_plans', 'get_session_usage', 'list_voices', 'chat_list_conversations', 'chat_get_messages', 'list_visual_dnas', 'get_visual_dna', 'list_visual_dna_folders', 'list_moodboards', 'get_moodboard', 'list_color_palettes', 'analyze_color_palette', 'list_media', 'list_media_folders', 'get_media', 'get_media_stats', 'list_presets', 'list_cinematic_presets', 'list_projects', 'get_project', 'list_sessions', 'list_project_context', 'get_project_profile', 'list_project_assets', 'list_project_transfers', 'list_session_generations', 'list_agents', 'list_skills', 'list_docs', 'get_doc', 'get_review_storage_usage', 'list_review_assets', 'get_review_asset', 'list_review_collections', 'list_review_comments', 'list_review_share_links', 'search_music_library', 'analyze_script_for_music', 'browse_music_library', 'get_music_library_facets', 'get_music_track_audio', 'get_music_track_related', 'get_music_track_lyrics', 'search_stock_media', 'get_stock_sources', 'get_stock_categories', 'get_stock_collections', 'get_stock_asset', 'analyze_script_for_stock', // Free (no credits, no DB write) — returns a scratch composition plan for the caller to edit. 'create_music_composition_plan', ]; const OPEN_WORLD_READ_ONLY = [ 'blender_list_sessions', 'blender_get_scene', 'blender_search_docs', 'blender_get_command_status', 'adobe_list_sessions', 'adobe_get_project', 'adobe_get_timeline', 'adobe_get_command_status', 'resolve_list_sessions', 'resolve_get_project', 'resolve_get_timeline', 'resolve_get_command_status', ]; const PRIVATE_WRITE = [ 'create_flow_session', 'duplicate_flow_session', 'move_flow_session', 'restore_flow_session', 'upload_font', 'rename_font', 'create_font_upload_ticket', 'font_upload_widget', 'create_video_editor_session', 'media_upload_widget', 'create_upload_ticket', 'upload_media', 'favorite_media', 'unfavorite_media', 'create_media_folder', 'update_media_folder', 'add_media_to_folder', 'remove_media_from_folder', 'share_media_folder', 'restore_media', 'move_media', 'bulk_restore_media', 'bulk_move_media', 'move_folder_contents', 'import_elevenlabs_voice', 'create_visual_dna', 'create_visual_dna_folder', 'update_visual_dna_folder', 'move_visual_dna_to_folder', 'create_moodboard', 'create_morphious_style', 'create_color_palette', 'activate_color_palette', 'deactivate_color_palette', 'move_session', 'bulk_move_sessions', 'move_generations_to_session', 'split_session', 'undo_session_organization', 'rename_session', 'restore_session', 'create_project', 'duplicate_project', 'update_project', 'archive_project', 'unarchive_project', 'add_project_context', 'link_project_asset', 'unlink_project_asset', 'create_agent', 'create_skill', 'create_doc', 'create_review_asset', 'update_review_asset', 'add_review_version', 'set_review_status', 'create_review_collection', 'update_review_collection', 'create_review_comment', 'reply_review_comment', 'resolve_review_comment', 'unresolve_review_comment', 'import_stock_asset', ]; const DESTRUCTIVE_WRITE = [ 'update_flow_session', 'undo_flow_edit', 'trash_flow_session', 'run_flow_session', 'cancel_flow_run', 'retry_flow_run', 'run_trend', 'cancel_trend_run', 'cancel_download', 'extend_music', 'cover_music', 'update_video_editor_session', 'delete_font', 'export_video_editor_session', // These actions spend credits, enqueue irreversible work, or cancel it. 'generate_image', 'generate_image_edit', 'generate_creative_director', 'generate_video', 'generate_video_from_image', 'generate_music', 'generate_speech', 'generate_sound', 'cancel_generation', // Both spend credits — reference-audio upload is billed by ElevenLabs like a generation, // section edit creates a new billed track. 'create_music_reference_audio', 'edit_music_section', 'generate_elements', 'generate_first_last_frame', 'generate_lipsync', 'generate_video_from_video', 'transcribe_audio', 'generate_3d', 'edit_image', 'edit_video', 'trim_video', 'clone_voice', 'chat_send_message', 'generate_character_sheet', 'acquire_clean_music_track', 'import_music_track_to_library', 'separate_audio_stems', 'clean_dialogue_leftovers', 'separate_ambience', 'analyze_video', // Deletes and whole-value replacement updates are conservatively destructive. 'delete_voice', 'update_visual_dna', 'delete_visual_dna', 'delete_visual_dna_folder', 'update_moodboard', 'delete_moodboard', 'delete_morphious_style', 'update_color_palette', 'delete_color_palette', 'delete_media_folder', 'delete_media', 'permanently_delete_media', 'bulk_delete_media', 'bulk_permanently_delete_media', 'unshare_media_folder', 'delete_session', 'delete_project_context', 'regenerate_project_profile', 'update_project_asset', // Ownership hand-off: the sender loses the project (and maybe all access) on accept. 'transfer_project', 'respond_project_transfer', 'update_agent', 'delete_agent', 'update_skill', 'delete_skill', 'update_doc', 'delete_doc', 'delete_review_asset', 'delete_review_collection', 'edit_review_comment', 'delete_review_comment', ]; const OPEN_WORLD_WRITE = [ 'download_media_from_url', 'import_music_audio', 'publish_html_artifact', 'create_review_share_link', 'blender_capture_viewport', // Adobe edits add bins, clips, sequences or caption tracks; none delete or overwrite. 'adobe_import_media', 'adobe_place_on_timeline', 'adobe_create_sequence', 'adobe_import_captions', 'adobe_capture_frame', // Resolve imports add Media Pool items; captures add a Kolbo library image. 'resolve_import_media', 'resolve_capture_frame', ]; const OPEN_WORLD_DESTRUCTIVE = [ 'share_doc', 'revoke_review_share_link', 'blender_apply_operations', 'blender_import_media', 'blender_render', 'blender_undo', 'blender_file_operation', 'blender_execute_python', // Can delete layers; approved once per batch in the Kolbo panel. 'adobe_edit_composition', 'adobe_run_script', // Can delete timeline clips; approved once per batch in the Kolbo plugin. 'resolve_edit_timeline', 'resolve_run_script', ]; const CONTRACT_GROUPS = [ [READ_ONLY, { readOnlyHint: true, openWorldHint: false, destructiveHint: false }], [OPEN_WORLD_READ_ONLY, { readOnlyHint: true, openWorldHint: true, destructiveHint: false }], [PRIVATE_WRITE, { readOnlyHint: false, openWorldHint: false, destructiveHint: false }], [DESTRUCTIVE_WRITE, { readOnlyHint: false, openWorldHint: false, destructiveHint: true }], [OPEN_WORLD_WRITE, { readOnlyHint: false, openWorldHint: true, destructiveHint: false }], [OPEN_WORLD_DESTRUCTIVE, { readOnlyHint: false, openWorldHint: true, destructiveHint: true }], ]; function buildToolAnnotations() { const annotations = Object.create(null); for (const [names, hints] of CONTRACT_GROUPS) { for (const name of names) { if (annotations[name]) throw new Error(`Duplicate safety annotation contract for tool: ${name}`); annotations[name] = Object.freeze({ ...hints }); } } return Object.freeze(annotations); } const TOOL_ANNOTATIONS = buildToolAnnotations(); function attachToolAnnotations(server) { const registered = server?._registeredTools || {}; const registeredNames = Object.keys(registered).sort(); const contractNames = Object.keys(TOOL_ANNOTATIONS).sort(); const missing = registeredNames.filter((name) => !TOOL_ANNOTATIONS[name]); // Tools a server PROFILE may legitimately leave unregistered. show_plans is // dropped under `commerce: false` (the ChatGPT app profile — OpenAI forbids // selling digital goods there); its contract row stays for every other host. const PROFILE_OPTIONAL = new Set(['show_plans']); const stale = contractNames.filter((name) => !registered[name] && !PROFILE_OPTIONAL.has(name)); if (missing.length || stale.length) { throw new Error( `Tool annotation contract mismatch. Missing: ${missing.join(', ') || 'none'}. ` + `Stale: ${stale.join(', ') || 'none'}.` ); } for (const name of registeredNames) registered[name].annotations = TOOL_ANNOTATIONS[name]; } module.exports = { TOOL_ANNOTATIONS, CONTRACT_GROUPS, attachToolAnnotations, };