FCP MCP Server - Implementation Roadmap
Step-by-step plan to build all editing features.
Current State
✅ DONE:
- Repository structure
- Basic parser (read FCPXML)
- 10 read-only MCP tools
- Claude Desktop integration config
🔄 IN PROGRESS:
- Writer module architecture
- Tool schemas for editing
Implementation Order
Sprint 1: Core Write Operations (Week 1)
Day 1-2: TimeValue & Writer Foundation
Tasks:
□ Implement TimeValue class with all operations
□ Create FCPXMLWriter base class
□ Implement _detect_fps()
□ Implement _build_clip_index()
□ Implement save()
□ Write unit tests for TimeValue
Day 3-4: Marker Operations
Tasks:
□ Implement add_marker()
□ Implement batch_add_markers()
□ Handle marker types (standard, chapter, todo)
□ Handle marker colors
□ Test with real FCP export
□ Verify re-import into FCP
Day 5-6: Trim Operations
Tasks:
□ Implement trim_clip() - absolute timecodes
□ Implement trim_clip() - delta trimming
□ Implement _ripple_after_clip()
□ Test with ripple=True and ripple=False
□ Edge case: trim to zero duration (should fail)
□ Edge case: trim beyond clip bounds (should clamp)
Day 7: Reorder Operations
Tasks:
□ Implement reorder_clips() - move to start/end
□ Implement reorder_clips() - move to timecode
□ Implement reorder_clips() - after/before clip
□ Implement _recalculate_offsets()
□ Test multi-clip moves
Sprint 2: Advanced Edit Operations (Week 2)
Day 1-2: Transitions
Tasks:
□ Implement add_transition() - cross-dissolve
□ Implement add_transition() - fade variants
□ Find FCP effect reference IDs
□ Test position: start, end, both
□ Handle transition overlap calculation
Day 3-4: Speed Changes
Tasks:
□ Implement change_speed() - constant speed
□ Implement change_speed() - speed ramps
□ Create timeMap XML structure
□ Test slow-mo (0.5x) and fast (2x)
□ Test speed ramp with different curves
□ Verify frame blending options
Day 5-6: Split & Delete
Tasks:
□ Implement split_clip() - single split point
□ Implement split_clip() - multiple split points
□ Implement delete_clip() - with ripple
□ Implement delete_clip() - with gap
□ Test split preserves clip attributes
□ Test delete updates clip index
Day 7: Selection & Batch
Tasks:
□ Implement select_by_keyword()
□ Implement batch_trim()
□ Test keyword matching modes (any, all, none)
□ Test batch operations on 10+ clips
Sprint 3: Auto Rough Cut (Week 3)
Day 1-2: Ingest Phase
Tasks:
□ Implement ingest_source_clips()
□ Parse asset-clips from library exports
□ Parse clips from event exports
□ Extract all metadata (keywords, ratings, favorites)
□ Test with 100+ clip library
Day 3-4: Score Phase
Tasks:
□ Implement score_clips()
□ Implement keyword matching scoring
□ Implement rating/favorite scoring
□ Implement duration fit scoring
□ Test scoring produces sensible rankings
Day 5-6: Select Phase
Tasks:
□ Implement select_clips_for_segment()
□ Implement all priority modes
□ Implement pacing-based duration calculation
□ Test clip selection respects already_used
□ Test segment duration targets
Day 7: Assemble Phase
Tasks:
□ Implement assemble_rough_cut()
□ Create proper FCPXML document structure
□ Add asset resources correctly
□ Add transitions between segments
□ Test complete rough cut generation
□ Import result into FCP - verify it works
Sprint 4: Polish & Ship (Week 4)
Day 1-2: Error Handling
Tasks:
□ Add validation for all inputs
□ Graceful handling of malformed FCPXML
□ Clear error messages for common issues
□ Logging throughout
Day 3-4: Integration Testing
Tasks:
□ Create test suite with real FCP exports
□ Test each tool end-to-end
□ Test tool combinations (marker + trim + reorder)
□ Performance testing with large projects
Day 5-6: Documentation
Tasks:
□ Complete README with all tools
□ Add usage examples for each tool
□ Create tutorial: "Your first rough cut"
□ Add troubleshooting guide
□ Record demo video
Day 7: Launch
Tasks:
□ Final testing pass
□ Version bump to 1.0.0
□ Push to GitHub
□ Submit to MCP registry
□ Write launch posts
□ Share with FCP communities
Testing Strategy
Unit Tests
# tests/test_timevalue.py
def test_from_timecode_hmsf():
tv = TimeValue.from_timecode("00:01:30:15", fps=30)
assert tv.to_seconds() == 90.5
def test_from_timecode_fcpxml():
tv = TimeValue.from_timecode("2700/30s")
assert tv.to_seconds() == 90.0
def test_addition():
a = TimeValue(30, 30) # 1 second
b = TimeValue(60, 30) # 2 seconds
c = a + b
assert c.to_seconds() == 3.0
def test_simplify():
tv = TimeValue(60, 30)
simplified = tv.simplify()
assert simplified.numerator == 2
assert simplified.denominator == 1
Integration Tests
# tests/test_writer_integration.py
def test_add_marker_reimports():
"""Marker added by writer should be visible in FCP."""
# 1. Create modified FCPXML
writer = FCPXMLWriter("fixtures/sample.fcpxml")
writer.add_marker("clip_0", "00:00:10:00", "Test Marker", MarkerType.CHAPTER)
writer.save("output/test_marker.fcpxml")
# 2. Parse it back
parser = FCPXMLParser("output/test_marker.fcpxml")
timeline = parser.parse()
# 3. Verify marker exists
marker_names = [m.name for m in timeline.markers]
assert "Test Marker" in marker_names
def test_trim_preserves_structure():
"""Trimming shouldn't corrupt other timeline elements."""
writer = FCPXMLWriter("fixtures/sample.fcpxml")
original_clip_count = len(writer.clips)
writer.trim_clip("clip_0", trim_end="-1s")
writer.save("output/test_trim.fcpxml")
parser = FCPXMLParser("output/test_trim.fcpxml")
timeline = parser.parse()
# Same number of clips
assert len(timeline.clips) == original_clip_count
Golden Set Tests
# tests/golden_set.py
"""
Golden set: known-good FCPXML files that must parse correctly.
If any fail, we broke backward compatibility.
"""
GOLDEN_FILES = [
"fixtures/golden/simple_timeline.fcpxml",
"fixtures/golden/multicam_project.fcpxml",
"fixtures/golden/compound_clips.fcpxml",
"fixtures/golden/with_effects.fcpxml",
"fixtures/golden/fcp_10_6_export.fcpxml",
"fixtures/golden/fcp_10_7_export.fcpxml",
"fixtures/golden/fcp_10_8_export.fcpxml",
]
@pytest.mark.parametrize("filepath", GOLDEN_FILES)
def test_golden_file_parses(filepath):
parser = FCPXMLParser(filepath)
timeline = parser.parse()
assert timeline is not None
assert len(timeline.clips) > 0
Risk Mitigation
Risk: FCPXML format changes between FCP versions
Mitigation:
- Test against multiple FCP export versions (10.6, 10.7, 10.8)
- Use conservative parsing (ignore unknown elements)
- Version detection in parser
- Golden set tests for each version
Risk: Generated FCPXML rejected by FCP
Mitigation:
- Always validate output XML
- Round-trip testing (export → modify → import)
- Use FCP's own exports as templates
- Keep original attributes we don't understand
Risk: Data loss from edit operations
Mitigation:
- Never overwrite original by default
- Backup before destructive operations
- Validate timeline integrity after each operation
- Undo capability (save original state)
Risk: Performance with large projects
Mitigation:
- Lazy loading of clip metadata
- Index-based clip lookup
- Stream parsing for very large files
- Progress callbacks for long operations
File Structure (Final)
fcp-mcp-server/
├── server.py # MCP server entry point
├── fcpxml/
│ ├── __init__.py
│ ├── parser.py # Read operations
│ ├── writer.py # Write operations
│ ├── rough_cut.py # Auto rough cut algorithm
│ ├── models.py # Data classes
│ └── utils.py # TimeValue, converters
├── tests/
│ ├── __init__.py
│ ├── test_parser.py
│ ├── test_writer.py
│ ├── test_rough_cut.py
│ ├── test_timevalue.py
│ ├── golden_set.py
│ └── fixtures/
│ ├── sample.fcpxml
│ └── golden/
├── examples/
│ ├── basic_usage.py
│ ├── rough_cut_example.py
│ └── batch_markers.py
├── docs/
│ ├── TOOL_REFERENCE.md
│ ├── FCPXML_GUIDE.md
│ └── TROUBLESHOOTING.md
├── pyproject.toml
├── requirements.txt
├── LICENSE
└── README.md
Definition of Done
A feature is complete when:
- ✅ Code implemented and working
- ✅ Unit tests passing
- ✅ Integration test with real FCP export
- ✅ Re-import into FCP verified
- ✅ Error handling for edge cases
- ✅ Documented in README
- ✅ Example usage provided