clipwright-render
MCP tool to realize OTIO timelines with FFmpeg.
Clipwright is a toolkit centered on "separation of detection (detect) and application (render)". detect-type tools only return annotations to OTIO without modifying media, and this single clipwright-render tool performs all realization in one pass (completes segment extraction, concatenation, and trimming in a single transcode pass).
Prerequisites
This tool targets materials and timelines that meet the following conditions. Inputs outside these conditions return errors.
| Condition | Details |
|---|---|
| Frame rate | CFR (constant frame rate) only. VFR (variable frame rate) not supported |
| Resolution | Fixed resolution only. Materials with per-frame resolution changes not supported |
| Source count | Only single source (1 file) in timeline |
| Video track | Required. No video not supported |
| Audio track | 0 or 1 stream only. If multiple, first audio stream adopted |
Out of Scope (Planned for Future)
- VFR / resolution-changing materials
- Multiple source file concatenation
- Subtitle burn-in
- Transitions
- 2+ video tracks in timeline
FFmpeg Setup
FFmpeg / FFprobe are not bundled with this package. Install in your environment.
# macOS (Homebrew)
brew install ffmpeg
# Ubuntu / Debian
sudo apt install ffmpeg
If ffmpeg / ffprobe are on PATH, it works as-is. In environments where PATH cannot be modified, explicitly specify paths with environment variables.
export CLIPWRIGHT_FFMPEG=/usr/local/bin/ffmpeg
export CLIPWRIGHT_FFPROBE=/usr/local/bin/ffprobe
About license: This wrapper package itself is MIT licensed. Since FFmpeg binaries are not bundled, FFmpeg's LGPL / GPL redistribution obligations do not apply to this wrapper. Verify FFmpeg's own license (LGPL v2.1 / GPL v2) in your environment.
Installation
uv sync
MCP Client Registration
Add the following to your MCP client configuration (e.g. .mcp.json):
{
"mcpServers": {
"clipwright-render": {
"command": "clipwright-render",
"args": []
}
}
}
If ffmpeg / ffprobe are not on PATH, pass them as environment variables:
{
"mcpServers": {
"clipwright-render": {
"command": "clipwright-render",
"args": [],
"env": {
"CLIPWRIGHT_FFMPEG": "/path/to/ffmpeg",
"CLIPWRIGHT_FFPROBE": "/path/to/ffprobe"
}
}
}
}
Usage
MCP Tool (clipwright_render)
Invoked from Claude / agents via MCP.
{
"tool": "clipwright_render",
"arguments": {
"timeline": "/path/to/timeline.otio",
"output": "/path/to/output.mp4",
"dry_run": false,
"options": {
"video_codec": "libx264",
"audio_codec": "aac",
"width": 1920,
"height": 1080,
"fps": 29.97,
"crf": 23,
"overwrite": false
}
}
}
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
timeline |
string | yes | Input OTIO file path |
output |
string | yes | Output file path (.mp4 / .mkv / .mov / .webm) |
dry_run |
bool | optional (default false) |
If true, returns plan without actual rendering |
options |
object | optional | Output options (see RenderOptions below) |
RenderOptions
| Field | Type | Description |
|---|---|---|
video_codec |
string | null | Video codec (e.g. libx264, default: inherit from source) |
audio_codec |
string | null | Audio codec (e.g. aac, default: inherit from source) |
width |
int | null | Output width (must be set with height) |
height |
int | null | Output height (must be set with width) |
fps |
float | null | Output frame rate |
crf |
int | null | Quality CRF value (0-51, software encoders only) |
overwrite |
bool | If true, overwrite existing output file (default false) |
hw_encoder |
string | Hardware encoder selection: "none" (default) / "auto" / "nvenc" / "amf" / "qsv" / "vaapi" / "videotoolbox" |
hwaccel_decode |
bool | If true, use GPU-accelerated decode (default false). v1: frames are downloaded to system memory before CPU filters. Full HW↔HW filtergraph is out of v1 scope. |
quality |
int | null | Encoder-neutral quality (0-51). When unset, crf is used for software encoders. Maps to -crf (x264/x265), -cq + -rc vbr (NVENC), -global_quality (QSV/VAAPI), -qp_i/-qp_p (AMF). -crf is never emitted for hardware encoders. |
width / height must both be specified or both null. Specifying only one is an error.
Hardware Encoder Behaviour
hw_encoder="auto": probe-then-test detection — checksffmpeg -encoders, runs a 1-frame throwaway encode to-f null -, picks the first vendor that succeeds. Falls back tolibx264with awarnings[]entry if no hardware encoder is usable. Render always completes.hw_encoder="nvenc"(or any explicit vendor): if the encoder fails to initialise, returnsUNSUPPORTED_OPERATIONwith an actionable hint.hw_encoder="none"(default): uses the software encoder path unchanged (backward compatible).
Hardware Encoder Verification Status
| Vendor | Option | Status |
|---|---|---|
NVIDIA (h264_nvenc, hevc_nvenc) |
"nvenc" |
Verified on maintainer's dev box (RTX-class GPU, Windows) |
AMD Radeon (h264_amf, hevc_amf) |
"amf" |
Experimental — community verification needed |
Intel Arc / iGPU (h264_qsv, hevc_qsv) |
"qsv" |
Experimental — community verification needed |
Linux VAAPI (h264_vaapi, hevc_vaapi) |
"vaapi" |
Experimental — community verification needed |
Apple VideoToolbox (h264_videotoolbox) |
"videotoolbox" |
Experimental — community verification needed |
Note: hardware encoders trade some size/quality fidelity for speed. NVENC default rate control produced a notably smaller file than
libx264 -crf 21at comparable subjective quality during verification (not a rigorous quality-matched comparison).
Return Value (Success)
{
"ok": true,
"summary": "2 clips → 45.2 sec / 42.1 MB / outputs/out.mp4",
"data": {
"output_path": "/path/to/output.mp4",
"duration_sec": 45.2,
"size_bytes": 44150784,
"clip_count": 2
},
"artifacts": ["/path/to/output.mp4"],
"warnings": []
}
Return Value (dry_run)
{
"ok": true,
"summary": "dry_run: 2 segments / estimated 45.2 sec / approx 42.1 MB",
"data": {
"dry_run": true,
"clip_count": 2,
"estimated_duration_sec": 45.2,
"estimated_size_bytes": 44150784,
"ffmpeg_args": ["ffmpeg", "-i", "source.mp4", "-filter_complex", "..."]
},
"artifacts": [],
"warnings": []
}
estimated_size_bytes is calculated from source bitrate obtained by FFprobe and output duration. If bitrate cannot be obtained, it is null with reason in warnings. If any of video_codec / width / height / fps / crf are specified, estimation based on source bitrate may differ significantly from actual, so warnings includes a note.
Return Value (Error)
{
"ok": false,
"error": {
"code": "FILE_NOT_FOUND",
"message": "Timeline file not found: /path/to/timeline.otio",
"hint": "Verify the file path"
}
}
Main error codes:
| Code | Meaning |
|---|---|
FILE_NOT_FOUND |
Timeline / source / output directory does not exist |
INVALID_INPUT |
Invalid extension / existing output with overwrite=false / empty timeline |
PATH_NOT_ALLOWED |
Output path is same as input source |
UNSUPPORTED_OPERATION |
No video / multiple sources / Transition / 2+ video tracks |
PROBE_FAILED |
FFprobe analysis failed |
SUBPROCESS_FAILED |
FFmpeg exit code non-zero |
SUBPROCESS_TIMEOUT |
FFmpeg timeout (max(300, duration_sec × 10) seconds) |
DEPENDENCY_MISSING |
ffmpeg / ffprobe not found in PATH or environment variables |
Testing
Unit Tests (FFmpeg Not Required)
uv run --package clipwright-render pytest clipwright-render/tests/ -m "not integration"
Integration Tests (FFmpeg Required)
Tests that verify single-source concatenation and output using actual FFmpeg. Automatically skipped in environments without FFmpeg.
Set environment variables before running:
export CLIPWRIGHT_FFMPEG=/path/to/ffmpeg
export CLIPWRIGHT_FFPROBE=/path/to/ffprobe
uv run --package clipwright-render pytest clipwright-render/tests/ -m integration
Integration tests skip if
CLIPWRIGHT_FFMPEG/CLIPWRIGHT_FFPROBEare not set. Set these variables when running in CI.
License
This wrapper package itself is MIT licensed.
Since FFmpeg binaries are not bundled, FFmpeg's LGPL v2.1 / GPL v2 redistribution obligations do not apply to this package.
Metadata
Release files for clipwright-render 0.19.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| clipwright_render-0.19.2.tar.gz | 98.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| clipwright_render-0.19.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 199.9 kB
Release files / clipwright_render-0.19.2.tar.gz
| Download URL | clipwright_render-0.19.2.tar.gz |
|---|---|
| Size | 98.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ced2e69c7adae907749f233e85c732fc8a0a3d7d954dcf6b1c8b41c4f40ac601
|
|
BLAKE2b-256 checksum How to use checksums |
0ac131c5214caca2d5509cfadb3ab84cee78fa20656c2f6dc9fa73183c459310
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 25, 2026.
Transparency logRelease files / clipwright_render-0.19.2-py3-none-any.whl
| Download URL | clipwright_render-0.19.2-py3-none-any.whl |
|---|---|
| Size | 101.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
51a6bc5ca8d83a3e0517ee9a4de1905853b770f25a8c507cd459a283034980f5
|
|
BLAKE2b-256 checksum How to use checksums |
6b03f0f072cb533788b57a830a6dc6af5643af22b25319f9dfe7d3e7425c8807
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 25, 2026.
Transparency log