fcpxml-mcp-server

mcp
SUMMARY

๐ŸŽฌ The first AI-powered MCP server for Final Cut Pro XML. Control your edits with natural language.

README.md

FCPXML MCP

The bridge between Final Cut Pro and AI. 53 tools that turn timeline XML into structured data Claude can read, edit, and generate.

CI
License: MIT
Python 3.10+
MCP Compatible
Final Cut Pro
Tests
Suites
Source


Why This Exists

After directing 350+ music videos (Chief Keef, Migos, Masicka), I noticed the same editing bottlenecks on every project: counting cuts manually, extracting chapter markers one by one, hunting flash frames by scrubbing, building rough cuts clip by clip.

These are batch operations that don't need visual feedback. Export the XML, let Claude handle the tedium, import the result. That's the entire philosophy.


See It In Action

You:    "Run a health check on my wedding edit"

Claude: โœ“ Analyzed WeddingFinal.fcpxml
        โ”œโ”€ 247 clips ยท 42:18 total ยท 24fps ยท 1920ร—1080
        โ”œโ”€ 3 flash frames detected (clips 44, 112, 198)
        โ”œโ”€ 2 unintentional gaps at 12:04 and 31:47
        โ”œโ”€ 14 duplicate source clips
        โ””โ”€ Health score: 72/100

You:    "Fix the flash frames and gaps, then add chapter markers from
         this transcript"

Claude: โœ“ Extended adjacent clips to cover 3 flash frames
        โœ“ Filled 2 gaps by extending previous clips
        โœ“ Added 18 chapter markers from transcript
        โ†’ Saved: WeddingFinal_modified.fcpxml

Import the modified XML back into Final Cut Pro. Every change is non-destructive โ€” your original file is never touched.


What Claude Actually Sees

This is the magic trick. When you export XML from Final Cut Pro, your timeline becomes structured data that Claude can reason about:

<!-- What FCP exports -->
<asset-clip ref="r2" offset="342/24s" name="Interview_A"
            start="120s" duration="720/24s" format="r1">
    <marker start="48/24s" duration="1/24s" value="Key quote"/>
    <keyword start="0s" duration="720/24s" value="Interview"/>
</asset-clip>
# What Claude works with (after parsing)
Clip(
    name="Interview_A",
    offset=TimeValue(342, 24),   # timeline position: 14.25s
    start=TimeValue(120, 1),     # source in-point: 2:00
    duration=TimeValue(720, 24), # 30 seconds
    markers=[Marker(value="Key quote", start=TimeValue(48, 24))],
    keywords=["Interview"]
)

Every time value stays as a rational fraction โ€” 720/24s, not 30.0 โ€” so trim, split, and speed operations have zero rounding error across any frame rate.


How It Works

  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚ Final Cutโ”‚      โ”‚  parser.py   โ†’ Python objects โ”‚      โ”‚ Final Cutโ”‚
  โ”‚   Pro    โ”‚โ”€XMLโ”€>โ”‚  writer.py   โ†’ Modify & save  โ”‚โ”€XMLโ”€>โ”‚   Pro    โ”‚
  โ”‚          โ”‚      โ”‚  rough_cut.pyโ†’ Generate new   โ”‚      โ”‚          โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ”‚  diff.py     โ†’ Compare        โ”‚      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                    โ”‚  export.py   โ†’ Resolve / FCP7 โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                              โ–ฒ
                     Claude Desktop / MCP client
  1. Export from FCP โ€” File โ†’ Export XML...
  2. Ask Claude โ€” analyze, edit, generate, QC, export
  3. Import back โ€” File โ†’ Import โ†’ XML

What This Is NOT

  • Not a plugin โ€” it doesn't run inside Final Cut Pro
  • Not real-time โ€” you work with the XML between exports
  • Not for creative calls โ€” color, framing, motion still need your eyes

Quick Start

1. Clone & Install

git clone https://github.com/DareDev256/fcp-mcp-server.git
cd fcp-mcp-server
pip install -e .

2. Configure Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

Using uv (recommended):

{
  "mcpServers": {
    "fcpxml": {
      "command": "uv",
      "args": ["--directory", "/path/to/fcp-mcp-server", "run", "server.py"],
      "env": { "FCP_PROJECTS_DIR": "/Users/you/Movies" }
    }
  }
}

Using pip:

{
  "mcpServers": {
    "fcpxml": {
      "command": "python",
      "args": ["/path/to/fcp-mcp-server/server.py"],
      "env": { "FCP_PROJECTS_DIR": "/Users/you/Movies" }
    }
  }
}

3. Use It

Export XML from Final Cut Pro, open Claude Desktop, and ask it to work with your timeline.


When To Use This

Good For Not Ideal For
Batch marker insertion (100 chapters from a transcript) Creative editing decisions (no visual feedback)
QC before delivery (flash frames, gaps, duplicates) Real-time adjustments (export/import cycle)
Data extraction (EDL, CSV, chapter markers) Fine-tuning cuts (faster directly in FCP)
Template generation (rough cuts from tagged clips) Anything visual (color, framing, motion)
Automated assembly (montages from keywords + pacing)
Timeline health checks (validation, stats, scoring)

Prompt Cookbook

Copy-paste these into Claude Desktop. Each one maps to a real tool chain under the hood.

Analysis

"Give me a full breakdown of ProjectX.fcpxml โ€” clips, duration, frame rate, markers, everything"
"Show me pacing analysis for my timeline โ€” where are the slow sections?"
"Export an EDL and CSV of all clips with timecodes"

QC & Fixes

"Run a health check on my timeline and fix anything under 2 frames"
"Find all gaps and flash frames, then auto-fix them"
"Are there any duplicate source clips I can consolidate?"

Markers & Chapters

"Add chapter markers from this transcript: [paste transcript]"
"Import markers from my-subtitles.srt onto the timeline"
"List all markers and export them as YouTube chapter timestamps"

Generation

"Build a 60-second rough cut from clips tagged 'Interview' โ€” medium pacing"
"Generate a montage from all B-roll clips with accelerating pacing"
"Create an A/B roll: Interview_A as primary, B-roll cuts every 8 seconds"

Cross-NLE & Reformat

"Export this timeline for DaVinci Resolve"
"Convert to FCP7 XML so I can open it in Premiere"
"Reformat my 16:9 timeline to 9:16 for Instagram Reels"

Under the Hood

When you say "Run a health check on my wedding edit", Claude chains these tools:

analyze_timeline  โ†’  stats, frame rate, resolution
detect_flash_frames  โ†’  clips under threshold duration
detect_gaps  โ†’  unintentional silence/black
detect_duplicates  โ†’  repeated source media
validate_timeline  โ†’  structural health score (0-100)

Each tool returns structured text that Claude synthesizes into the summary you see. No magic โ€” just batch XML queries that would take 20 minutes by hand.


Pre-Built Prompts

Select these from Claude's prompt menu (โŒ˜/) โ€” they chain multiple tools automatically.

Prompt What It Does
qc-check Full quality control โ€” flash frames, gaps, duplicates, health score
youtube-chapters Extract chapter markers formatted for YouTube descriptions
rough-cut Guided rough cut โ€” shows clips, suggests structure, generates
timeline-summary Quick overview โ€” stats, pacing, keywords, markers, assessment
cleanup Find and auto-fix flash frames and gaps

All 53 Tools

Category Tools What It Does
Analysis 11 Stats, clips, markers, keywords, EDL/CSV, pacing
Multi-Track 3 Connected clips, compound clips, secondary lanes
Roles 4 List, assign, filter, export stems
QC & Validation 4 Flash frames, duplicates, gaps, health score
Editing 9 Markers, trim, reorder, transitions, speed, split
Batch Fixes 3 Auto-fix flash frames, rapid trim, fill gaps
Comparison 1 Diff two timelines โ€” added/removed/moved/trimmed
Reformat 1 Aspect ratio conversion (9:16, 1:1, 4:5, custom)
Silence 2 Detect and remove silence candidates
NLE Export 2 DaVinci Resolve v1.9, FCP7 XMEML v5
Generation 3 Rough cuts, montages, A/B roll
Beat Sync 2 Import beat markers, snap cuts to beats
Import 2 SRT/VTT subtitles, YouTube chapters โ†’ markers
Audio 1 Add audio clips, music beds at any lane
Compound 2 Create/flatten compound clips
Templates 2 Pre-built timeline structures (intro/outro, lower thirds, music video)
Effects 1 List FCP transition effects with UUIDs
53
Full tool reference (click to expand)

Analysis โ€” 11 tools

list_projects ยท analyze_timeline ยท list_clips ยท list_library_clips ยท list_markers ยท find_short_cuts ยท find_long_clips ยท list_keywords ยท export_edl ยท export_csv ยท analyze_pacing

Multi-Track โ€” 3 tools

list_connected_clips ยท add_connected_clip ยท list_compound_clips

Roles โ€” 4 tools

list_roles ยท assign_role ยท filter_by_role ยท export_role_stems

QC & Validation โ€” 4 tools

detect_flash_frames ยท detect_duplicates ยท detect_gaps ยท validate_timeline

Editing โ€” 9 tools

add_marker ยท batch_add_markers ยท insert_clip ยท trim_clip ยท reorder_clips ยท add_transition ยท change_speed ยท delete_clips ยท split_clip

Batch Fixes โ€” 3 tools

fix_flash_frames ยท rapid_trim ยท fill_gaps

Comparison ยท Reformat ยท Silence

diff_timelines ยท reformat_timeline ยท detect_silence_candidates ยท remove_silence_candidates

NLE Export โ€” 2 tools

export_resolve_xml (DaVinci Resolve FCPXML v1.9) ยท export_fcp7_xml (Premiere Pro / Resolve / Avid XMEML v5)

Generation โ€” 3 tools

auto_rough_cut ยท generate_montage ยท generate_ab_roll

Beat Sync โ€” 2 tools

import_beat_markers ยท snap_to_beats

Import โ€” 2 tools

import_srt_markers ยท import_transcript_markers (supports SMPTE HH:MM:SS:FF with frame-accurate placement)

v0.6.0 โ€” Audio, Compound, Templates, Effects โ€” 6 tools

list_effects ยท add_audio ยท create_compound_clip ยท flatten_compound_clip ยท list_templates ยท apply_template


Environment Variables

Variable Required Default Description
FCP_PROJECTS_DIR No ~/Movies Root directory for FCPXML file discovery via list_projects
OPENAI_BASE_URL No โ€” Route LLM calls through any OpenAI-compatible proxy (LiteLLM, OpenRouter, Ollama, vLLM)

Compatibility

Component Supported Versions
FCPXML format v1.8 โ€“ v1.11
Final Cut Pro 10.4+
Python 3.10, 3.11, 3.12
MCP protocol 1.0
Export targets
โ†’ DaVinci Resolve FCPXML v1.9
โ†’ Premiere Pro / Avid FCP7 XMEML v5

Architecture

fcp-mcp-server/           ~8.7k lines Python
โ”œโ”€โ”€ server.py              MCP entry point โ€” 53 tools, 5 prompts, resource discovery
โ”‚                          _resolve_io_paths() / _setup_modifier() / _setup_generator()
โ”‚                          _format_clip_table() / _markdown_table() / _format_batch_result()
โ”‚                          _raw_markers_to_batch()
โ”‚                          _detect_flash_frames() / _detect_gaps() / _detect_duplicate_groups()
โ”‚                          consolidate path validation, QC detection, rendering, handler boilerplate
โ”œโ”€โ”€ fcpxml/
โ”‚   โ”œโ”€โ”€ README.md          Developer guide โ€” TimeValue, clip hierarchy, type reference
โ”‚   โ”œโ”€โ”€ models.py          TimeValue, Timecode, Clip, ConnectedClip, MarkerType, Timeline
โ”‚   โ”œโ”€โ”€ parser.py          FCPXML โ†’ Python (spine, connected clips, roles, markers)
โ”‚   โ”œโ”€โ”€ writer.py          Modify & write (markers, trim, gaps, transitions, silence)
โ”‚   โ”‚                       _resolve_insert_position() / _find_neighbor_clip() / _index_elements()
โ”‚   โ”œโ”€โ”€ rough_cut.py       Generate timelines (rough cuts, montages, A/B roll)
โ”‚   โ”œโ”€โ”€ diff.py            Timeline comparison engine (identity matching, threshold docs)
โ”‚   โ”œโ”€โ”€ export.py          DaVinci Resolve v1.9 + FCP7 XMEML v5 export
โ”‚   โ”œโ”€โ”€ safe_xml.py        Centralized defusedxml wrappers (XXE/entity-bomb protection)
โ”‚   โ””โ”€โ”€ templates.py       Template system (intro/outro, lower thirds, music video)
โ”œโ”€โ”€ tests/                 759 tests across 18 suites
โ”‚   โ”œโ”€โ”€ test_models.py     TimeValue math, Timecode formatting, MarkerType contracts
โ”‚   โ”œโ”€โ”€ test_parser.py     FCPXML parsing, connected clips, edge cases
โ”‚   โ”œโ”€โ”€ test_writer.py     Clip editing, marker writing, speed changes
โ”‚   โ”œโ”€โ”€ test_fcpxml_writer.py  FCPXMLWriter generation from Python objects
โ”‚   โ”œโ”€โ”€ test_server.py     MCP tool handlers, dispatch, path validation
โ”‚   โ”œโ”€โ”€ test_rough_cut.py  Rough cut generation, montage, A/B roll
โ”‚   โ”œโ”€โ”€ test_diff.py       Moved clips, transitions, markers, clip identity
โ”‚   โ”œโ”€โ”€ test_export.py     Attribute stripping, compound flattening, audio tracks
โ”‚   โ”œโ”€โ”€ test_features_v05.py  Multi-track, roles, diff, reformat, export
โ”‚   โ”œโ”€โ”€ test_features_v06.py  Audio, compound clips, templates, effects, validation
โ”‚   โ”œโ”€โ”€ test_marker_pipeline.py  Marker builder, batch modes, output format
โ”‚   โ”œโ”€โ”€ test_speed_cutting.py  Speed cutting, montage config, pacing curves
โ”‚   โ”œโ”€โ”€ test_security.py   Input validation, XML sanitization, XXE protection
โ”‚   โ”œโ”€โ”€ test_edge_cases.py Boundary arithmetic, clip collisions, split/diff edges
โ”‚   โ”œโ”€โ”€ test_diversity.py  Boundary conditions across diff, models, validation
โ”‚   โ”œโ”€โ”€ test_server_helpers.py  _resolve_io_paths, _setup_generator composition
โ”‚   โ”œโ”€โ”€ test_targeted_gaps.py  Targeted branch coverage for diff, export, models
โ”‚   โ””โ”€โ”€ test_validation_gaps.py  Input validation, XML priority, diff edge paths
โ”œโ”€โ”€ docs/
โ”‚   โ””โ”€โ”€ WORKFLOWS.md       8 production workflow recipes
โ””โ”€โ”€ examples/
    โ””โ”€โ”€ sample.fcpxml      9 clips, 24fps โ€” test fixture

Security

Every tool handler is hardened against adversarial input โ€” critical for MCP servers where prompts may be LLM-generated, not human-typed.

Layer Protection
File I/O Path traversal blocked, null bytes rejected, symlinks resolved, 100 MB size limit
Output sandbox All generation, write, export, beat sync, subtitle, and reformat handlers enforce _validate_output_path(anchor_dir=...) โ€” restricts writes to descendants of the source file's directory, blocking LLM-generated path escapes
Subprocess bounds _ensure_video_asset resolves ffmpeg to an absolute path via shutil.which(), validates duration (0.01โ€“86400s), fps (1โ€“240), width/height (even, 2โ€“16384) before invoking โ€” prevents both PATH manipulation and resource exhaustion
Directory listing Confined to FCP_PROJECTS_DIR when set, depth-limited rglob (โ‰ค10 levels, 10K file cap), symlink-escape detection โ€” prevents workspace enumeration and traversal DoS
XML parsing defusedxml with explicit forbid_entities/external=True blocks XXE, billion laughs, entity expansion, remote DTD attacks at all 4 entry points (parser, writer, exporter, rough cut) โ€” minidom pretty-print path also hardened via defusedxml.minidom. Ruff S314/S320 rules enforce safe parsing in CI
JSON depth limit Iterative BFS depth checker rejects payloads nested beyond 50 levels โ€” immune to RecursionError even at ~1000 nesting
Marker strings Sanitized via _sanitize_xml_value() โ€” null bytes, control chars stripped before write
Role values Stripped of control characters before XML attribute assignment
URI parsing MCP resource URIs parsed via urllib.parse.urlparse() โ€” rejects scheme confusion and handles percent-encoded paths correctly
Output suffixes Path separators and special characters stripped โ€” no traversal via suffix injection
Marker types completed attribute strict-matched ('0'/'1' only) โ€” rejects "true", "1 OR 1=1", whitespace-padded values

120 security-specific tests across test_security.py covering XXE, path traversal, sandbox boundaries, output path anchoring, input validation, subprocess bounds, directory depth limits, minidom hardening, JSON depth limits, role sanitization, ffmpeg parameter bounds, and write-handler sandbox enforcement. Security events (null bytes, sandbox escapes, unhandled exceptions) are logged via Python logging for audit trails.


Timestamp Parsing โ€” How Import Tools Place Markers

All subtitle and transcript import tools (import_srt_markers, import_transcript_markers) funnel through a single internal function: _parse_timestamp_parts() in server.py. Understanding it matters when timestamps don't land where you expect.

Supported Formats

Format Example Parts Result
Minutes:Seconds 1:30 2 90.0s
H:MM:SS 1:05:30 3 3930.0s
HH:MM:SS.ms 00:02:15.500 3 135.5s
SMPTE (HH:MM:SS:FF) 01:00:10:12 4 3610.5s @ 24fps

The SMPTE 4-part format converts the frame component to fractional seconds: frames / frame_rate. The default rate is 24fps โ€” pass frame_rate= to override for 25fps (PAL) or 30fps (NTSC) projects.

The Import Pipeline

SRT / VTT / YouTube chapters / plain transcript
        โ”‚
        โ–ผ
  parse_srt()  /  parse_vtt()  /  parse_transcript_timestamps()
        โ”‚                โ”‚                      โ”‚
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
                    split on ':'
                         โ”‚
                         โ–ผ
             _parse_timestamp_parts(parts, frame_rate=24.0)
                         โ”‚
                         โ–ผ
                   total seconds (float)
                         โ”‚
                         โ–ผ
               marker placed on timeline

Edge Cases

  • Unrecognized part counts (1 part, 5+ parts) return None โ€” the marker is silently skipped, not placed incorrectly
  • Zero frame rate โ€” falls back to base seconds (frames ignored) rather than dividing by zero
  • Milliseconds โ€” only carried in 3-part format via float() on the seconds component ("15.500" โ†’ 15.5)
  • Frame rounding โ€” SMPTE frames are divided exactly (12/24 = 0.5), not rounded to the nearest frame boundary. The resulting float is converted to FCPXML's rational TimeValue downstream, preserving precision

Why This Matters

Before v0.6.20, the 4-part SMPTE parser silently dropped frames โ€” 01:00:10:12 became 3610.0s instead of 3610.5s. At 24fps, that's up to ~0.96 seconds of drift per marker. If you imported a subtitle file with SMPTE timecodes, every marker was slightly off. This was subtle enough to pass QC but visible when scrubbing.


Design Principles

Principle Implementation
Rational time, never floats All durations are fractions (600/2400s) matching FCPXML's native format โ€” zero rounding errors across trim, split, speed
Non-destructive by default Modified files get _modified, _chapters suffixes. Originals are never overwritten
Single source of truth MarkerType enum owns serialization: from_string() for input, from_xml_element() for parsing, xml_attrs for writing. INCOMPLETE is canonical; TODO is a backward-compat alias (same object)
Security-first 10-layer defense-in-depth across all 53 handlers โ€” see Security for the full matrix
Dispatch, not conditionals TOOL_HANDLERS dict maps names โ†’ async handlers. No 1000-line if/elif

Documentation

Guide What's Inside
WORKFLOWS.md 8 production recipes โ€” QC pipelines, beat-synced assembly, cross-NLE handoffs, documentary A/B roll
MCP_ECOSYSTEM.md How this server composes with GitNexus, filesystem, and memory MCP servers
CHANGELOG.md Full version history from v0.1.0 to present

Testing

uv run --extra dev pytest tests/ -v    # or: python3 -m pytest tests/ -v
ruff check . --exclude docs/           # lint โ€” must pass before committing

733 tests across 17 suites covering models, parser, writer, FCPXMLWriter generation, server handlers, rough cut generation, speed cutting & pacing curves, marker pipeline, security hardening (XXE, entity expansion, path traversal, sandbox boundaries, minidom defense-in-depth, JSON depth limits, input validation, ffmpeg bounds, write-handler sandboxing), connected clips, roles, diff, export, compound clip flattening, audio track generation, templates, effects, boundary conditions, and backward compatibility.


Requirements

  • Python 3.10+ ยท Final Cut Pro 10.4+ (FCPXML 1.8+) ยท Claude Desktop or any MCP client
  • Dependencies (auto-installed): mcp, defusedxml
  • See Compatibility for full version matrix

Roadmap

  • Core FCPXML parsing (v1.8โ€“1.11)
  • Timeline analysis, markers, EDL/CSV export
  • Clip editing (trim, reorder, split, speed, transitions)
  • QC tools (flash frames, gaps, duplicates, health scoring)
  • Generation (rough cuts, montages, A/B roll, beat sync)
  • MCP Prompts + Resources (auto-discovery)
  • Subtitle & transcript import as markers
  • Multi-track (connected clips, compound clips, roles)
  • Timeline diff + social media reformat
  • Silence detection & cleanup
  • Cross-NLE export (DaVinci Resolve, Premiere Pro, Avid)
  • Audio sync detection
  • Premiere Pro native XML support

Known Issues

Issue Impact Workaround
Still images crash FCP PNG/JPEG assets referenced directly in FCPXML crash Final Cut Pro on import (addAssetClip null pointer). Confirmed across multiple format configurations, dimension matching, and element types. Convert stills to short MOVs before referencing: ffmpeg -loop 1 -i image.png -c:v libx264 -t 2 -pix_fmt yuv420p -r 24 output.mov. This is an FCP limitation, not an FCPXML spec issue.
Non-standard timebases FCP rejects time values with denominators outside its standard set (e.g. 100800/57600s). Cross-denominator arithmetic previously produced these. Fixed in v0.5.29 โ€” TimeValue arithmetic now uses LCM, and speed changes snap to frame boundaries in 2400-tick timebase.
Malformed frameDuration crash A frameDuration with zero or negative denominator (e.g. "0/0s") in the writer's _detect_fps would silently produce 0.0 fps, causing downstream ZeroDivisionError in speed/trim operations. The parser already validated this correctly. Fixed in v0.6.23 โ€” writer now validates both numerator and denominator, falling back to 30.0 fps.

Contributing

PRs welcome. If you're a video editor who codes (or a coder who edits), let's build this together.

Credits

Built by @DareDev256 โ€” former music video director (350+ videos), now building AI tools for creators.

License

MIT โ€” see LICENSE.

Reviews (0)

No results found