## Summary
This PR adds a **Model Context Protocol (MCP) server** to CommandPos…t, 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](https://modelcontextprotocol.io) 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:
```json
{
"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
```bash
# 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
- [x] Blade cuts (3 cuts creating 4 clips) — verified clip count delta
- [x] Timeline navigation (beginning, next edit, end, timecode) — verified timecode changes
- [x] Select clip at playhead and by index — verified AX selection
- [x] Apply video effect (Gaussian Blur) — verified via action handler
- [x] Apply transition (Cross Dissolve) — verified transition count delta
- [x] Retime slow 50% and fast 4x — verified undo text "Speed Adjustment"
- [x] Reset to normal speed — verified undo text "Retime Reset Clip"
- [x] Add standard and chapter markers — verified marker added
- [x] Add keyword via Keyword Editor — verified with PrimaryToolbar v12 fix
- [x] Rename clip — verified via Clip menu (FCP v12 location)
- [x] Duplicate clip — verified clip count delta and undo text "Paste"
- [x] Apply generator (Custom Solid) — verified via action handler
- [x] Apply title (Basic Title) — verified via action handler
- [x] Delete clip — verified clip count delta
- [x] Undo/Redo — verified undo title changes
- [x] Select all / select none — verified count changes