Skip to content

Add MCP Server for AI-Powered Final Cut Pro Automation#3514

Open
elliotttate wants to merge 2 commits intoCommandPost:developfrom
elliotttate:mcp-server
Open

Add MCP Server for AI-Powered Final Cut Pro Automation#3514
elliotttate wants to merge 2 commits intoCommandPost:developfrom
elliotttate:mcp-server

Conversation

@elliotttate
Copy link

Summary

This PR adds a Model Context Protocol (MCP) server to CommandPost, enabling AI assistants (such as Claude, ChatGPT, or any MCP-compatible client) to control Final Cut Pro and macOS workflows through CommandPost's existing automation infrastructure.

The server exposes 76 tools spanning timeline editing, effects, transitions, retiming, markers, keywords, generators, titles, import/export, color correction, and arbitrary Lua scripting — all accessible over a standard JSON-RPC protocol.


Architecture

AI Assistant (Claude Code, etc.)
        │
        │ stdio (JSON-RPC / MCP protocol)
        ▼
  MCP Server (Node.js / TypeScript)
        │
        │ WebSocket (port 27480, authenticated)
        ▼
  CommandPost (Hammerspoon runtime)
        │
        │ Accessibility API + Lua scripting
        ▼
  Final Cut Pro / macOS

How It Works

  1. MCP Protocol Layer (src/mcp-server/src/index.ts) — A Node.js server implementing the Model Context Protocol specification. It communicates with AI assistants via stdio using JSON-RPC, translating tool calls into CommandPost operations.

  2. WebSocket Transport (src/mcp-server/src/commandpost.ts) — A WebSocket client that maintains a persistent connection to CommandPost's built-in WebSocket server. Handles message correlation via unique IDs, automatic reconnection, request timeouts, deduplication of broadcast responses, and back-pressure protection (max 100 pending requests).

  3. CommandPost WebSocket Server (src/plugins/core/websocket/manager/) — Enhanced with new message types (execute, batch), token-based authentication, audit logging, and improved plugin matching. The server generates a SHA-256 session token on startup, writes it to a well-known file path, and validates every incoming message.

  4. Lua Execution Environment — Tool handlers generate Lua code that runs inside CommandPost's Hammerspoon environment with full access to hs.* APIs, cp.* modules, Final Cut Pro's accessibility tree, and all registered CommandPost plugins.

Authentication

The WebSocket connection uses shared-secret authentication:

  • On server start, CommandPost generates a random SHA-256 token from timestamp + random data + process ID
  • The token is written to ~/Library/Application Support/CommandPost/mcp-auth-token with chmod 600
  • The MCP server reads this token automatically (with a 60-second TTL cache, force-refreshed on auth failures)
  • Every WebSocket message includes an auth field; messages without valid tokens are rejected
  • The token can also be set via the COMMANDPOST_AUTH_TOKEN environment variable

Operation Verification

Editing tools use a withVerification wrapper that captures timeline state before and after operations:

{
  "before": { "clipCount": 4, "timecode": "00:00:15:00" },
  "after": {
    "clipCount": 5,
    "timecode": "00:00:15:00",
    "undoText": "Undo Blade",
    "clips": [...]
  },
  "verified": {
    "undoText": "Undo Blade",
    "clipDelta": 1,
    "tcChanged": false
  }
}

Three verification signals confirm success:

  • undoText — FCP's own name for the last action (read via AX, not menu reads that steal focus)
  • clipDelta — Change in clip count (+1 = added, -1 = removed)
  • tcChanged — Whether the playhead moved

Focus Management

FCP keyboard shortcuts require the app to be frontmost. The server:

  • Calls activateFinalCutPro() before shortcut-based operations
  • Checks for and dismisses modal dialogs (sheets, system dialogs) that would block interaction
  • Dismisses speed popovers that persist after retime operations
  • Uses AX-based undo text reading instead of getMenuItems() to avoid stealing focus during verification
  • Uses {plain = true} on all selectMenu calls to prevent Lua pattern matching crashes from special characters in menu item names

Dialog Handling

A handleFCPDialog helper automatically detects and handles FCP modal dialogs (e.g., "not enough extra media beyond clip edges" when applying transitions). It scans for AXSheets and AXModal windows, reads their text content, and clicks the appropriate button. This is integrated into both withVerification and individual tool handlers.


Complete Tools Reference (76 tools)

System & Connection (9 tools)

Tool Description
commandpost_ping Health check — verifies CommandPost is running and WebSocket is connected
commandpost_list_handlers List all registered action handlers (fcpx_menu, fcpx_shortcuts, etc.)
commandpost_get_handler_info Get handler details with paginated choice lists and parameter schemas
commandpost_execute_lua Execute arbitrary Lua in CommandPost's Hammerspoon environment
commandpost_execute_action Execute a registered action by handler ID and action ID
commandpost_chain Execute multiple operations sequentially with result passing via _prev
commandpost_get_preference Read a CommandPost preference value by key
commandpost_set_preference Write a CommandPost preference value
commandpost_alert Display an on-screen HUD alert

Final Cut Pro — Application (6 tools)

Tool Description
fcp_launch Launch FCP or bring to front if already running
fcp_quit Quit Final Cut Pro
fcp_restart Quit and relaunch Final Cut Pro
fcp_status Running state, version, frontmost status, active libraries with paths
fcp_select_menu Select any FCP menu item by path array (e.g., ["File", "Share", "Master File..."])
fcp_do_shortcut Execute a keyboard shortcut by FCP command ID

Final Cut Pro — Timeline (9 tools)

Tool Description
fcp_timeline_show Show/focus timeline on primary or secondary display
fcp_timeline_playback Play, pause, or toggle playback
fcp_timeline_navigate Jump to beginning/end, next/previous frame or edit, or specific timecode
fcp_timeline_select Select clips: all, none, at_playhead, or by_index (1-based, skips transitions)
fcp_timeline_blade Blade (cut) at current playhead with before/after clip count verification
fcp_timeline_delete Delete selected clips (auto-selects at playhead if nothing selected)
fcp_timeline_clipboard Copy, cut, or paste timeline content
fcp_timeline_zoom Zoom in, out, or fit to window
fcp_timeline_get_info Full timeline state: clip list with descriptions/durations, playhead timecode, undo text, focus state

Final Cut Pro — Effects & Plugins (4 tools)

Tool Description
fcp_apply_effect Apply video or audio effect by simplified path (e.g., "Blur/Gaussian", "Stylize/Comic Book")
fcp_apply_transition Apply transition via CommandPost's action handler system (e.g., "Dissolves/Cross Dissolve") with automatic dialog handling
fcp_apply_generator Apply generator (e.g., "Solids/Custom", "Backgrounds/Gradient")
fcp_apply_title Apply title (e.g., "Basic Title", "Bumper/Opener/Basic Title")

Final Cut Pro — Discovery (6 tools)

Tool Description
fcp_list_effects List all installed video effects with categories
fcp_list_audio_effects List all installed audio effects
fcp_list_transitions List all installed transitions
fcp_list_generators List all installed generators
fcp_list_titles List all installed titles
fcp_list_markers List markers in the current timeline via AX tree

Final Cut Pro — Browser & Libraries (3 tools)

Tool Description
fcp_browser_show Show browser panel (libraries, media, or generators)
fcp_browser_list_libraries List all open libraries with filesystem paths
fcp_browser_select_library Select a specific library by name

Final Cut Pro — Inspector & Viewer (2 tools)

Tool Description
fcp_inspector_show Show inspector, optionally selecting a tab (audio, video, info, color, share, text, generator)
fcp_viewer_show Show viewer on primary or secondary display

Final Cut Pro — Color (1 tool)

Tool Description
fcp_color_board Adjust Color Board: color/saturation/exposure for master/shadows/midtones/highlights

Final Cut Pro — Export & Import (4 tools)

Tool Description
fcp_export Export/share with optional destination preset
fcp_export_xml Export FCPXML via File > Export XML
fcp_import_media Import media file or folder via Media Import window with Go-to-Folder support
fcp_import_xml Import FCPXML file

Final Cut Pro — Projects & Playhead (5 tools)

Tool Description
fcp_open_project Open a project by name
fcp_project_properties Get project properties (resolution, frame rate, etc.)
fcp_get_playhead_position Get current playhead timecode
fcp_set_playhead_position Navigate playhead to specific timecode
fcp_get_project_settings Full project settings (resolution, frame rate, color space, audio config)

Final Cut Pro — Markers & Keywords (2 tools)

Tool Description
fcp_add_marker Add standard, to-do, or chapter marker with optional name
fcp_add_keyword Add keyword to selected clips via Keyword Editor (auto-opens/closes)

Final Cut Pro — Clip Operations (10 tools)

Tool Description
fcp_rename_clip Rename selected clip via inline text field (supports FCP v12 Clip menu location)
fcp_rate_clip Rate as favorite, reject, or unrate
fcp_get_selected_clips Get selected clip info (names, positions, durations) via AX
fcp_get_clip_properties Get transform/compositing (position, scale, rotation, opacity, blend mode)
fcp_set_clip_properties Set transform or compositing properties on selected clip
fcp_duplicate_clip Duplicate selected clips (copy + paste as connected)
fcp_enable_disable_clip Toggle clip enabled state
fcp_create_compound_clip Create compound clip from selection
fcp_create_audition Create audition from selection
fcp_break_apart_compound Break apart a compound clip

Final Cut Pro — Speed & Retime (2 tools)

Tool Description
fcp_retime Speed presets: slow (50%/25%/10%), fast (2x/4x/8x/20x), normal, reverse, hold
fcp_speed_custom Set arbitrary speed percentage

Final Cut Pro — Advanced (9 tools)

Tool Description
fcp_split_at_timecode Blade at a specific timecode (navigates playhead, then blades)
fcp_set_range Set timeline in/out range selection
fcp_clear_range Clear range selection
fcp_captions Add, extract, or import captions
fcp_multicam_switch_angle Switch multicam angle (video, audio, or both)
fcp_assign_role Assign role to selected clips
fcp_stabilization Toggle stabilization on selected clip
fcp_proxy_toggle Toggle proxy/optimized/original media
fcp_batch_apply_transition Apply transition to all edit points in selection

Final Cut Pro — Window & Undo (3 tools)

Tool Description
fcp_window_layout Show/hide browser, inspector, timeline, viewer; toggle fullscreen
fcp_undo_redo Undo or redo with optional count for multiple operations
fcp_pasteboard Access CommandPost's clipboard history

Final Cut Pro — Workflow (1 tool)

Tool Description
fcp_assemble_rough_cut Assemble rough cut from operation sequence in one call

FCP v12 Compatibility Fixes

This PR includes several fixes for Final Cut Pro v12 (12.0+):

PrimaryToolbar.lua

FCP v12 no longer wraps toolbar checkboxes in AXGroup elements — they are now direct children of the toolbar. Updated all five checkbox properties (keywordEditor, backgroundTasksWindow, browserShowing, timelineShowing, inspectorShowing) to fall back to AXDescription-based matching when the legacy AXGroup wrapper is not found. Fully backwards-compatible with pre-v12.

Menu Item Relocation

"Rename Clip" moved from the Modify menu to the Clip menu in FCP v12. The rename tool tries Clip > Rename Clip first, falling back to Modify > Rename Clip.

Lua Pattern Safety

All selectMenu calls now pass {plain = true} to disable Lua pattern matching. This prevents crashes when menu items contain special pattern characters like % (e.g., "Normal (100%)"), ., (, or ).

Plugin Name-Only Matching

findFCPXPluginBySimplifiedPath in the WebSocket message handler now supports name-only matching as a fallback. This allows matching "Cross Dissolve" without requiring the full "Dissolves/Cross Dissolve" path — important for transitions and other plugins that lack filesystem path metadata.

Defensive Nil Handling

  • Contents:clipsUI() and Contents:positionClipsUI() return empty tables {} instead of nil to prevent downstream nil index errors
  • Timeline:isShowing() uses a safer prop composition
  • Menu finder adds nil checks before calling :gsub() and :find() on potentially nil strings

Files Changed

New Files

File Lines Description
src/mcp-server/src/index.ts 6,337 MCP server: tool definitions, Lua code generation, verification wrappers
src/mcp-server/src/commandpost.ts 408 WebSocket client with auth, reconnection, deduplication
src/mcp-server/README.md 462 Full documentation with setup, tools reference, architecture
src/mcp-server/package.json 31 Dependencies: @modelcontextprotocol/sdk, ws
src/mcp-server/test.mjs 1,163 Protocol-level test suite (offline + live modes)
src/mcp-server/test-edit-workflow.mjs 955 End-to-end timeline editing test
src/mcp-server/test-edit-workflow-mini.mjs 195 Quick smoke test (markers + navigation)
src/mcp-server/test-edit-workflow-storm.mjs 419 Stress test (rapid sequential operations)
.mcp.json 8 Claude Code MCP server configuration

Modified Files

File Description
src/plugins/core/websocket/manager/message-handler.lua +802 lines: execute/batch message types, auth, audit logging, plugin matching
src/plugins/core/websocket/manager/init.lua +91 lines: auth token generation, getAuthTokenPath(), server-only mode
src/extensions/cp/apple/finalcutpro/main/PrimaryToolbar.lua FCP v12 toolbar checkbox compatibility
src/extensions/cp/apple/finalcutpro/import/MediaImport.lua setPath() and importFiles() methods for programmatic media import
src/extensions/cp/apple/finalcutpro/timeline/Contents.lua Return {} instead of nil from clip query methods
src/extensions/cp/apple/finalcutpro/timeline/Timeline.lua Safer isShowing prop
src/extensions/cp/apple/finalcutpro/menu.lua Nil-safe pattern matching in menu finder

Setup

# 1. Enable WebSocket in CommandPost
defaults write org.latenitefilms.CommandPost "cp.websocket.enabled" -int 1
# Then restart CommandPost

# 2. Build the MCP server
cd src/mcp-server
npm install
npm run build

# 3. Add to Claude Code's .mcp.json
# {
#   "mcpServers": {
#     "commandpost": {
#       "command": "node",
#       "args": ["/path/to/CommandPost/src/mcp-server/dist/index.js"]
#     }
#   }
# }

# 4. Restart Claude Code session

Test Plan

  • Blade cuts (3 cuts creating 4 clips) — verified clip count delta
  • Timeline navigation (beginning, next edit, end, timecode) — verified timecode changes
  • Select clip at playhead and by index — verified AX selection
  • Apply video effect (Gaussian Blur) — verified via action handler
  • Apply transition (Cross Dissolve) — verified transition count delta
  • Retime slow 50% and fast 4x — verified undo text "Speed Adjustment"
  • Reset to normal speed — verified undo text "Retime Reset Clip"
  • Add standard and chapter markers — verified marker added
  • Add keyword via Keyword Editor — verified with PrimaryToolbar v12 fix
  • Rename clip — verified via Clip menu (FCP v12 location)
  • Duplicate clip — verified clip count delta and undo text "Paste"
  • Apply generator (Custom Solid) — verified via action handler
  • Apply title (Basic Title) — verified via action handler
  • Delete clip — verified clip count delta
  • Undo/Redo — verified undo title changes
  • Select all / select none — verified count changes

Adds a Model Context Protocol (MCP) server that exposes CommandPost's
full automation capabilities to AI assistants like Claude. 76 tools
covering timeline editing, effects, transitions, retiming, markers,
keywords, generators, titles, import/export, and arbitrary Lua scripting.

Key components:
- MCP server (TypeScript/Node.js) communicating via WebSocket
- Token-based authentication with auto-generated SHA-256 session tokens
- Operation verification (before/after state, undo text, clip deltas)
- Automatic FCP dialog detection and handling
- Operation chaining with result passing between steps

CommandPost changes:
- WebSocket message handler: execute/batch message types, auth, audit log,
  plugin name-only matching for transitions
- WebSocket manager: auth token generation, server-only mode
- PrimaryToolbar: FCP v12 compatibility (direct checkbox matching)
- Timeline/Contents: defensive nil handling, empty table returns
- MediaImport: Go-to-folder path support
- Menu finder: safe nil checks for pattern matching
@elliotttate
Copy link
Author

elliotttate commented Mar 19, 2026 •

Here's a video showing it performing different actions like responding to the request "Make a cut any time a scene change occurs on this clip": https://youtu.be/kAegHnTTVOk

New fcp_scene_detect_and_cut tool that uses ffmpeg's scdet filter to
detect scene changes in a video file, then blades the FCP timeline at
all detected cut points in a single batch operation.

Features:
- Configurable sensitivity threshold and minimum gap between cuts
- Dry run mode to preview detected cuts without blading
- Auto-detects video frame rate for accurate timecode conversion
- Per-cut status reporting for retry on failure
- Uses Trim > Blade All menu for reliable batch cutting
@elliotttate
Copy link
Author

elliotttate commented Mar 19, 2026 •

Added an example of how you can do almost anything and a practical use case.

I just told it to make cuts to all the scene changes in my clip. It used FFMPEG to detect them, then made cuts and then added an MCP tool to do this faster in the future. Video example: https://youtu.be/kAegHnTTVOk

@latenitefilms
Copy link
Contributor

Well, this is pretty epic. THANK YOU!

I'll try properly review today and watch all your videos.

Curious why we need a Node server? Can't we just do the same thing in Lua? I believe we already have a pull request that does this?

Will likely merge in and then address any issues.

Stay tuned.

@randomeizer
Copy link
Contributor

Yeah, I would be interested in seeing if this could be reimplemented in Lua. I understand why: there are lots of Node MCP servers out there already, makes it easier to get it up and running.

It’s a toss-up: building from scratch is also going to introduce bugs etc.

@narimangafurov
Copy link

Great work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants