ffmpeg-mcp-video-editor
Provides video editing capabilities such as trimming, concatenation, format conversion, transformation, speed ramping, color grading, LUT application, caption burning, audio mixing, normalization, and more through ffmpeg.
Provides face detection, tracking, cropping, and blurring capabilities via MediaPipe.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ffmpeg-mcp-video-editor turn my landscape video into a vertical short with captions"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Features
Typed tools, not a shell — ffmpeg's editing surface arrives as 38 schema'd MCP tools, so the calling model gets parameters and guardrails instead of hand-writing filter graphs.
Nothing blocks — anything that encodes frames returns a job id immediately, with real progress parsed from ffmpeg's own output and a cancel that actually kills the subprocess.
Zero setup — finds your ffmpeg, or downloads a verified static build for your OS on first run. The Whisper and face models fetch themselves too.
Captions that survive their own text — a subtitle containing
Time: 12:30, [note]; it's 50%would corrupt a naively built filter graph. Here it doesn't, and there's a test proving it.Sees faces — detect them, follow one with a smoothed crop to turn landscape into vertical, or blur every face except your subject.
Hears speech — Whisper transcription, translation, and one-shot auto-captioning with word-level timestamps.
Renders whole timelines — clips, transitions, overlays, captions and ducked audio tracks compile into a single ffmpeg pass.
Two front doors, one backend — the optional local UI shares the same job store, so it and your MCP client see and cancel each other's work.
Can see what it edits — extract a frame or a contact sheet, measure brightness, colour and loudness. Editing is a loop, so the server can check its own output rather than rendering blind.
Tested where it counts — 727 tests; the unit tests run with no ffmpeg installed at all.
Related MCP server: mcpCut
Quick start
uv sync --all-extras # core + Whisper + vision + UI
uv run ffmpeg-mcp-serverRegister it with your MCP client. Rather than hand-editing paths, print the block with your real ones already filled in:
printf '{\n "mcpServers": {\n "ffmpeg-mcp": {\n "command": "%s",\n "args": ["run", "--directory", "%s", "ffmpeg-mcp-server"]\n }\n }\n}\n' "$(command -v uv)" "$PWD"Paste the result into your client's MCP config, then restart it. Both paths must
be absolute: a GUI app does not inherit your shell's PATH, and it launches
the server from an unrelated working directory.
That is uv failing before the server ever starts, and it almost always means
the --directory path does not exist — most often a placeholder that was pasted
verbatim. Run the command above to get the correct one. You can check it
directly with:
uv run --directory /your/path ffmpeg-mcp-server # should print startup logs, not exitIf uv itself is not found, use its absolute path (command -v uv) as
"command".
Then just ask for what you want:
"Take
interview.mp4, follow the speaker's face, make it a vertical Reel, and burn in captions."
track_and_crop → job 8f3a… ▸ done interview_reframed.mp4
auto_caption → job b71c… ▸ done interview_reframed_captioned.mp4 + .srtprobe_media and list_capabilities answer instantly. Everything else returns a
job id — poll job_status, then job_result. cancel_job stops it mid-render.
Give it real paths on your machine. The server runs natively, so it reads your filesystem — not files you attach to the chat, which live in the assistant's own sandbox. Say
~/Downloads/clip.mov, not/mnt/user-data/uploads/clip.mov. Paths must also sit underFFMPEG_MCP_ALLOWED_ROOTS, which defaults to your home directory.
Projects
Several sessions can share one server without mixing together. Name a project and everything that session does is filed under it:
set_project { "name": "dress-reel" } // one session
set_project { "name": "wedding-teaser" } // another, same serverJobs are stamped with the project, unspecified outputs land in
<workspace>/projects/<name>/, and list_jobs shows only that project by
default — so a busy shared queue stays readable. project: "all" spans them,
and list_projects shows what exists with per-project counts.
Long queues page rather than dumping everything:
list_jobs { "limit": 25, "offset": 50 } // returns total and has_moreWorkers still share the queue, so projects divide the bookkeeping without dividing the compute.
How it works
Tools validate their arguments and enqueue; a worker pool picks the job up and runs it. The job store is plain SQLite inside the workspace, which is what lets the MCP server and the UI be two processes over one queue — either can enqueue, either can watch progress, and either can cancel a job the other one started, because cancellation is a cooperative flag the owning worker polls.
Every ffmpeg invocation goes through one async execution path that owns
timeouts, progress parsing and error wrapping. Arguments are always a list;
shell=True appears nowhere in the project.
Reframing for Reels and Shorts
The most-asked-for edit, as one call. resize_video takes a named preset
(19 of them), an aspect_ratio like 9:16, or explicit dimensions — and fit
decides what happens to the picture that no longer fits.
{ "input_path": "talk.mp4", "preset": "reel", "fit": "blur" } // blurred bars, nothing cropped
{ "input_path": "talk.mp4", "aspect_ratio": "1:1", "focus": "top" } // crop, keep the topFor a talking head that must stay in frame, prefer track_and_crop — it
follows the face and smooths the crop path, because a crop that snaps frame to
frame looks worse than a slightly imperfect one that glides.
The tools
Area | Tools | |
🎬 | Core |
|
⏱ | Jobs |
|
🗂 | Projects |
|
🎨 | Colour |
|
💬 | Text |
|
🗣 | Speech |
|
👤 | Vision |
|
🎞 | Compose |
|
🔊 | Audio |
|
📐 | Format |
|
🔍 | Inspect |
|
Six tools are read-only and answer synchronously — probe_media, list_capabilities, list_resolution_presets, and the three job queries. The rest return a job id.
The local UI
A separate process over the same workspace and job store — run it alongside the MCP server, or entirely on its own.
uv run ffmpeg-mcp-ui # http://127.0.0.1:8756Panel | What it does | |
📊 | Jobs | Live queue pushed over a WebSocket, with progress bars and cancel. Jobs started by your MCP client appear here too. |
▶️ | Preview | Plays inputs and outputs in-browser, with a before/after view whose two players stay in step — grading and cropping are hard to judge from a still. |
🧰 | Tools | A form for every tool, generated from its JSON schema, so the list can never drift from what the tools accept. |
✂️ | Timeline | Drag clips to reorder, drag their edges to trim, scrub a playhead synced to the preview. Render emits exactly the structure |
📥 | Drop in | Drag media from anywhere on your machine; it lands in the workspace and is immediately editable. |
Dark, glassmorphic, one orange accent. Built assets are committed, so running the UI needs no Node toolchain.
Requirements
Python 3.11 or 3.12 (managed with
uv)ffmpeg 6+ — or let it download a static build on first run
macOS, Linux, or Windows
Configuration
Environment variables, all prefixed FFMPEG_MCP_:
Variable | Default | Meaning |
|
| Outputs, job store, cached binaries and models. |
|
| Roots that inputs and outputs must sit under. |
| 16 GiB | Rejects oversized inputs up front. |
| 10800 | Wall-clock cap per job. |
| 24 | How long finished jobs and their files are kept. |
| 2 | Jobs running at once, per process. |
| — | Use specific binaries instead of resolving one. |
|
| Allow downloading a static ffmpeg build. |
|
|
|
|
|
How ffmpeg is found
Configured path → build cached in the workspace → a system ffmpeg ≥ 6 → a downloaded static build.
Builds from BtbN (Linux, Windows) are verified against the checksums.sha256
published with the release, and a mismatch is fatal. evermeet.cx (macOS)
publishes only a GPG signature and no digest, so macOS downloads are pinned on
first use — the hash is recorded in <workspace>/bin/manifest.json and any
later download of the same URL must match. Prefer to avoid that? Install ffmpeg
yourself and it'll be used instead.
The MediaPipe face model (~230 KB) is fetched on first vision call and verified against a pinned SHA-256.
Security
Path allowlist — every input and output is resolved through symlinks before being checked, so a symlink in the workspace can't reach
/etc.No shell, ever — ffmpeg arguments are always a list.
shell=Trueappears nowhere.Filter escaping, verified — filter strings are built in one module applying both levels of ffmpeg's escaping. Overlay text goes to a sidecar file referenced with
textfile=andexpansion=none, so caption text never enters the graph at all.Auditable — every job records its resolved ffmpeg command line. File contents are never logged.
The UI is the same trust boundary — on loopback it runs unauthenticated; bound anywhere else it generates and requires a token.
Uploads are rebuilt, not filtered — the directory component is discarded (
../../etc/passwd→passwd), the stem reduced to[A-Za-z0-9._-], and the extension must be a media type the tools read.
Development
uv run pytest # 727 tests
uv run pytest -m "not integration" # most need no ffmpeg
uv run ruff check . && uv run ruff format --check .
uv run mypyUnit tests cover the pure logic — filter builders and escaping, path validation, the job store, SRT and LUT parsing, tracking geometry, timeline compilation. Integration tests generate small fixture clips on first run and verify rendered output by probing it.
Rebuilding the frontend:
cd ui-src && npm install && npm run build # emits into src/ffmpeg_mcp/ui/static/Notes
blur_faces covers up to 12 tracked faces and says so when it truncates. A
missed detection means an unblurred face — review the output before publishing
anything sensitive.
License
MIT © AbyAbyss.
ffmpeg itself is separately licensed, and the static builds this server can
download for you are GPL-configured. Those run as a separate process that
this project invokes over a command line — it links no ffmpeg code — so the MIT
terms above cover this codebase only. If you redistribute a bundle that ships an
ffmpeg binary alongside it, check that binary's own terms. Point
FFMPEG_MCP_FFMPEG_PATH at an LGPL build if you would rather avoid GPL
components entirely.
Available Tools
38 toolsadd_transitionAdd a transitionA
Join two clips with a cross-fade or wipe-style transition.
The clips overlap by 'duration' seconds, so the result is shorter than the two clips added together by exactly that much. The second clip is conformed to the first one's resolution and frame rate first, since xfade requires both sides to match.
Available transitions include fade, fadeblack, dissolve, the wipe family (wipeleft/right/up/down), the slide family, and circleopen/circleclose.
Inputs: first_path (required), second_path (required), encode, duration, +3 more. Returns: What every work-performing tool returns immediately.
analyze_videoAnalyse brightness and colourARead-onlyIdempotent
Measure brightness and colourfulness across sampled frames.
This is how a grade gets checked rather than guessed at: whether contrast crushed the shadows, whether a highlight is clipping, whether a clip is genuinely greyscale, and how evenly exposed a cut is across its shots.
Answers immediately; each sample is a separate fast seek.
Inputs: input_path (required), end, count, start. Returns: What the sampled frames say about the picture.
apply_curvesAdjust curvesA
Apply a tone curve, either a named preset or your own control points.
Points are given in 0..1 input/output coordinates and may be listed in any order; they are sorted and validated before the job runs, because ffmpeg silently ignores a malformed curve rather than reporting an error. Give a preset or points, not both.
Inputs: input_path (required), red, blue, green, +4 more. Returns: What every work-performing tool returns immediately.
apply_lutApply a LUTA
Apply a .cube colour lookup table.
The LUT is parsed and validated before the job is queued — declared size, row count and numeric content — because ffmpeg reports a malformed cube with an error that gives no hint what is wrong. Use 'strength' below 1.0 to blend the graded result back against the original.
Inputs: lut_path (required), input_path (required), encode, strength, +2 more. Returns: What every work-performing tool returns immediately.
auto_captionAuto-caption a videoA
Transcribe a video and burn the captions in, as one job.
Chains transcription, SRT generation and caption burning so you do not have to orchestrate three jobs. The generated SRT is kept alongside the video, so you can correct the text and re-burn it with burn_captions if Whisper mishears something.
Set translate_to_english to caption foreign-language speech in English.
Inputs: input_path (required), style, encode, options, +5 more. Returns: What every work-performing tool returns immediately.
blur_facesBlur facesA
Blur every face in a clip, following each one as it moves.
Each tracked face gets its own blurred region whose position is driven over time, so the blur stays on the person rather than covering a fixed rectangle. Boxes are expanded past the detected face by default so hair and chin are covered too.
Set exclude_primary to keep the main subject sharp and blur everyone else — the usual requirement for street interviews. Because a missed detection means an unblurred face, review the output before publishing it.
Inputs: input_path (required), encode, expand, max_frames, +6 more. Returns: What every work-performing tool returns immediately.
build_srtBuild an SRT fileA
Turn a list of timed text segments into a valid SRT subtitle file.
Pure text handling — no ffmpeg call, so this answers immediately. Segments are sorted, empty ones dropped, and overlapping cues truncated so they do not fight each other on screen. Long lines are word-wrapped. Feed the result to burn_captions, or edit it first.
Inputs: segments (required), max_lines, output_path, max_chars_per_line. Returns: The generated subtitle file.
burn_captionsBurn in captionsA
Burn an SRT or ASS subtitle file permanently into the video.
Font, size, fill and outline colour, position and margins are all controllable. Colours are given as hex (#RRGGBB) and converted to ASS's inverted BGR form internally. The subtitle file is handed to ffmpeg by path, so caption text containing colons, commas or brackets cannot corrupt the filter graph.
For styling of an .ass file's own embedded styles, note that these overrides replace them.
Inputs: input_path (required), subtitle_path (required), style, encode, +2 more. Returns: What every work-performing tool returns immediately.
cancel_jobCancel a jobA
Stop a queued or running job.
A queued job is cancelled immediately. A running one is asked to stop: the worker that owns it terminates the ffmpeg subprocess within a second or so, which works even when the job was started by a different process such as the local UI. Jobs that already finished are left alone.
Inputs: job_id (required). Returns: Outcome of a cancellation request.
color_gradeColour gradeA
Adjust brightness, contrast, saturation, gamma and colour temperature.
All five are applied in one filter chain, so the whole grade costs a single re-encode. Stages left at their neutral value are omitted entirely. Temperature is a relative artistic warm/cool control, not an absolute white balance in Kelvin.
Inputs: input_path (required), gamma, encode, contrast, +4 more. Returns: What every work-performing tool returns immediately.
concatConcatenate clipsA
Join two or more clips end to end into a single file.
When every clip already shares a codec, resolution and frame rate, this remuxes with no re-encoding. Otherwise the clips are normalised first — scaled and padded to a common resolution, resampled to a common frame rate and audio layout — and then concatenated, which requires a re-encode.
Inputs: input_paths (required), encode, target_fps, output_path, +1 more. Returns: What every work-performing tool returns immediately.
convert_formatConvert formatA
Transcode a file to a different container or codec.
Sensible codec defaults are chosen per container (H.264/AAC for mp4 and mov, VP9/Opus for webm, MP3 or AAC for audio-only targets); override them through 'encode'. Set audio_only to extract the audio track.
Inputs: input_path (required), encode, container, audio_only, +1 more. Returns: What every work-performing tool returns immediately.
detect_facesDetect facesA
Find faces in a video, sampled over time.
Returns per-timestamp bounding boxes with confidence, in both source pixels and 0..1 normalised coordinates, plus 'tracks' — detections linked across frames into one entry per person, with the likely main subject flagged.
Sampling at 2 fps is usually enough to follow a talking head; raise sample_fps for fast movement. Needs the 'vision' extra; the small detection model is downloaded and cached on first use.
Inputs: input_path (required), max_frames, sample_fps, analysis_width, +2 more. Returns: What every work-performing tool returns immediately.
detect_scenesDetect scene cutsA
Find hard cuts in a video and report the shots between them.
Useful for chopping raw footage into clips: feed the returned scene start and end times straight into trim. Uses ffmpeg's own scene-change score, so unlike the other phase 4 tools this needs no vision dependency.
Lower the threshold to catch softer cuts, raise it if handheld camera motion is being reported as cuts.
Inputs: input_path (required), threshold, min_scene_seconds. Returns: What every work-performing tool returns immediately.
extract_filmstripExtract a filmstripARead-onlyIdempotent
Tile several frames into one contact sheet, to survey footage at a glance.
The fastest way to find out what is actually in a clip — where the good moments are, where the camera settles, which shots are worth cutting to. Answers immediately: each frame is a separate fast seek, so the cost tracks the number of frames, not the length of the video.
Inputs: input_path (required), end, count, start, +3 more. Returns: A contact sheet and the timestamps it covers.
extract_frameExtract a frameARead-onlyIdempotent
Save a single frame as an image so it can actually be looked at.
Answers immediately. Seeking is done before decoding, so grabbing a frame from an hour-long file costs the same as from a short one.
Use it to check what is in footage before cutting, and to confirm a render looks the way it was meant to.
Inputs: input_path (required), time, width, format, +1 more. Returns: A still written to disk.
fade_audioFade audioA
Fade a file's audio in at the start, out at the end, or both.
The fade-out is positioned from the file's measured duration, so you give its length rather than working out its start time yourself. Video is stream- copied, so only the audio is re-encoded.
Inputs: input_path (required), encode, fade_in, fade_out, +1 more. Returns: What every work-performing tool returns immediately.
job_resultJob resultARead-onlyIdempotent
Fetch the output paths of a completed job.
Errors if the job has not finished yet — poll job_status first. For a failed job this raises with the structured failure reason; for a successful one the 'result' field carries the output path and a probe of the rendered file.
Inputs: job_id (required). Returns: Outputs of a finished job.
job_statusJob statusARead-onlyIdempotent
Check how a queued job is progressing.
Returns one of queued, running, done, failed or cancelled, with a progress percentage parsed from ffmpeg's own output. Poll this after queueing work, then call job_result once the status is 'done'.
Inputs: job_id (required). Returns: Current state of one job.
list_capabilitiesList ffmpeg capabilitiesARead-onlyIdempotent
Report the resolved ffmpeg build and which encoders and filters it has.
Use this before assuming something is available — hardware encoding (videotoolbox, nvenc, qsv), newer filters like xfade, or this server's optional Whisper and vision extras. Answers immediately.
Inputs: check_filters, check_encoders. Returns: What the resolved ffmpeg build can do.
list_jobsList jobsARead-onlyIdempotent
List jobs newest first, scoped to a project and returned a page at a time.
Defaults to the active project, so a long-running server shared by several sessions does not bury your work in everyone else's. Pass project='all' to see everything, and use offset with limit to page through a busy queue — 'total' and 'has_more' say whether there is another page.
Inputs: limit, offset, status, project. Returns: A page of jobs plus counts for the scope being listed.
list_projectsList projectsARead-onlyIdempotent
List every project in the job store, with how much work each holds.
Shows which one this session is currently working in, and how many jobs each project has queued, running, done and failed — a quick way to find the name of work started earlier, or in another session.
Inputs: no named parameters. Returns: Every project the job store has seen.
list_resolution_presetsList resolution presetsARead-onlyIdempotent
List the named resolution presets, fit modes and focus points resize_video accepts.
Answers immediately. Use it when you want to name a target platform rather than work out its pixel dimensions.
Inputs: no named parameters. Returns: The named resolution targets this server understands.
measure_audioMeasure loudnessA
Measure a file's loudness: mean and peak level, and EBU R128 LUFS.
Use it to decide whether audio is worth keeping — room tone and handling noise measure very differently from speech — and to check what a mix or a normalisation actually did.
This one is a job rather than an instant answer: integrated loudness is defined over the whole file, so it cannot be sampled.
Inputs: input_path (required). Returns: What every work-performing tool returns immediately.
mix_audioMix audio tracksA
Mix background music or effects under a primary voice track.
Set 'duck' on a track to have it automatically drop in level whenever the voice track is loud, which is what makes music sit under narration without manual level automation. Per-track gain and start offsets are applied before the mix.
If the primary input is a video, its picture is carried through untouched.
Inputs: tracks (required), voice_path (required), encode, duration, +2 more. Returns: What every work-performing tool returns immediately.
normalize_audioNormalise loudnessA
Normalise a file's loudness to a target level (EBU R128).
By default this runs two passes: the first measures the actual loudness, the second corrects to the target using those measurements. That is noticeably more accurate than the single streaming pass, which has to guess as it goes.
-16 LUFS suits online video, -23 LUFS is the broadcast standard.
Inputs: input_path (required), encode, two_pass, true_peak, +3 more. Returns: What every work-performing tool returns immediately.
overlay_mediaOverlay mediaA
Composite an image or video on top of another — picture-in-picture, watermark, or a chroma-keyed composite.
Position with a named corner or explicit x/y, scale with 'width', and fade it in and out of existence with 'start'/'end'. Set chroma_key to a hex colour to key out a green or blue screen from the overlay before compositing.
Inputs: input_path (required), overlay_path (required), x, y, +11 more. Returns: What every work-performing tool returns immediately.
probe_mediaProbe mediaARead-onlyIdempotent
Read a media file's technical metadata.
Returns container format, duration in seconds, per-stream codec, resolution, frame rate, bit rate, audio channels and sample rate, and subtitle streams.
This answers immediately rather than returning a job id. Call it before any edit that depends on the source dimensions or duration, and call it again on an output to verify a render.
Inputs: input_path (required). Returns: Everything the probe tool reports about one media file.
render_timelineRender a timelineA
Render a complete edit — clips, transitions, overlays, captions, audio — in one pass.
This is the entry point for driving the server from a script rather than calling tools one at a time, and it is exactly the structure the local UI's timeline editor produces.
The timeline declares an output width, height and frame rate; every clip is scaled and padded to fit, so sources may differ. Each clip has in and out points into its source, an optional speed, and an optional transition into the next one. Text overlays, media overlays (picture-in-picture or watermarks), a burned-in subtitle file, and extra audio tracks with optional ducking all layer on top.
Inputs: timeline (required), encode, output_path. Returns: What every work-performing tool returns immediately.
resize_videoResize / change aspect ratioA
Convert a video to a different resolution or aspect ratio.
Use a named preset for the common targets — 'reel', 'tiktok', 'youtube_short' and 'story' are all 1080x1920; 'youtube_1080p', 'youtube_4k', 'instagram_square' and 'instagram_portrait' do what they say — or give an explicit width and height, or an aspect_ratio such as '9:16'.
'fit' decides what happens to the picture that no longer fits when the shape changes:
cover (default) zooms and crops to fill; nothing is letterboxed but the edges are lost. Use 'focus' to choose which part survives.
contain fits the whole picture inside and pads with solid bars.
blur fits the whole picture inside over a blurred, zoomed copy of itself — the usual look for turning landscape footage vertical.
stretch distorts the picture to fit exactly.
For a talking-head video where the subject must stay in frame, prefer track_and_crop, which follows the face instead of cropping to a fixed point.
Inputs: input_path (required), fit, focus, width, +7 more. Returns: What every work-performing tool returns immediately.
set_projectSwitch projectA
Work under a named project, so parallel sessions do not mix together.
Everything queued afterwards is filed under this name, and outputs without an explicit path land in the project's own directory. list_jobs then shows only this project by default, which keeps a busy shared queue readable.
Two sessions editing different videos should each set their own project. The name is created on first use — there is nothing to set up beforehand.
Inputs: name (required). Returns: The project this session is now working in.
speed_rampChange playback speedA
Speed a clip up or slow it down, video and audio independently.
By default the audio is time-stretched so pitch is preserved, chained through as many atempo stages as the factor needs. Set keep_pitch false for a tape speed-up sound, or drop_audio to discard the track. Set audio_speed to hold audio at a different rate from the video.
Inputs: speed (required), input_path (required), encode, drop_audio, +3 more. Returns: What every work-performing tool returns immediately.
text_overlayAdd text overlaysA
Draw titles and lower-thirds over the video, with timing and animation.
Each item has its own in and out point, named position (or explicit x/y), font size and colour, optional outline or background box, and an animation: fade, or a slide from any direction. Everything renders in one pass.
The text itself is written to a sidecar file and referenced by path rather than embedded in the filter graph, so any characters are safe — including colons, commas, brackets, quotes and %{...} sequences, which are drawn literally.
Inputs: items (required), input_path (required), encode, output_path. Returns: What every work-performing tool returns immediately.
track_and_cropReframe following a faceA
Reframe a clip to a new aspect ratio, following a face across the timeline.
The classic use is turning a horizontal interview into a vertical clip that keeps the speaker in frame. The crop path is smoothed before rendering — a crop that snaps frame to frame looks worse than a slightly imperfect one that glides — and clamped so it never runs off the edge of the source.
Call detect_faces first if you want to choose which person to follow, then pass its track_id. With no face found, this falls back to a centre crop unless fallback is 'fail'.
Inputs: input_path (required), encode, fallback, track_id, +7 more. Returns: What every work-performing tool returns immediately.
transcribe_audioTranscribe audioA
Transcribe speech in an audio or video file using Whisper.
Returns timed segments and, if word_timestamps is set, per-word timings. The spoken language is auto-detected unless you name one. Pass srt_path to have the transcript written straight out as a subtitle file.
This can take a while — roughly real-time on CPU with the 'base' model, and several times that with 'large-v3' — so poll job_status. The first run with a given model also downloads its weights.
Inputs: input_path (required), options, srt_path. Returns: What every work-performing tool returns immediately.
transformCrop, scale, rotateA
Apply geometric transforms — crop, scale, rotate, flip — in one pass.
Operations compose into a single filter chain in the order crop, scale, pad, rotate, flip, so the whole change costs one re-encode. Give only one of scale_width/scale_height to preserve the aspect ratio; give both with pad_to_fit to letterbox rather than stretch.
Inputs: input_path (required), crop, encode, rotate, +6 more. Returns: What every work-performing tool returns immediately.
translate_transcriptTranslate speech to EnglishA
Transcribe non-English speech and translate it into English.
This uses Whisper's built-in translate mode, which only ever outputs English — Whisper cannot translate into any other target language. To reach a different language you would need a separate translation step applied to the transcript this returns.
Inputs: input_path (required), options, srt_path. Returns: What every work-performing tool returns immediately.
trimTrim a clipA
Cut a segment out of a video or audio file.
Stream-copies when the source codecs allow it, which is near-instant but snaps the cut to the nearest keyframe; pass mode='reencode' when the in and out points must be frame-accurate. The finished job reports the output's actual duration so you can confirm what you got.
Inputs: input_path (required), end, mode, start, +2 more. Returns: What every work-performing tool returns immediately.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 38 tools
Most tools have distinct purposes, but there is some overlap between transform/resize_video/track_and_crop for scaling and apply_curves/color_grade for color adjustments. Descriptions are detailed enough to differentiate, but an agent could still mis-select without careful reading.
The majority of tools follow a verb_noun pattern (e.g., transcribe_audio, overlay_media), but a few use bare verbs (trim, concat) or noun-first compounds (job_status, text_overlay, color_grade). The naming is readable and mostly predictable, with only minor deviations.
At 38 tools, this is far above the typical well-scoped range. While the server covers a broad video-editing domain, the large number of granular tools could overwhelm an agent and many could be consolidated into broader operations.
The tool set covers the core video-editing lifecycle: ingest, trim, concat, transitions, color, captions, audio, AI analysis, and rendering. Minor gaps exist, such as no reverse effect, stabilization, or simple volume adjustment, but these are workaround-able.
Maintenance
Related MCP Connectors
MCP server for Clipkit — gives AI agents a video toolbox via the Clipkit schema.
AI video editor for agents and humans: timeline, captions, color, audio and generation as MCP tools.
AI video editor your agent can drive: plan scenes, edit a multi-track timeline, and render MP4s.
FFmpeg as a service for AI agents: typed video editing tools, async jobs, downloadable outputs.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceAn MCP server that exposes FFmpeg as a structured tool set for AI agents, enabling timeline-based video editing, preview, rendering, and analysis with an optional LLM autopilot.MIT
- AlicenseNot gradedqualityCmaintenanceA real video editor for AI agents, served over MCP, enabling journaled timeline editing, rendering via FFmpeg/MLT, and deterministic CLI operation.1MIT
- AlicenseNot gradedqualityDmaintenanceAn MCP server that gives Claude a conversational video-editing toolkit — trim, crop, color grade, caption, beat-sync, stabilize, and more — built on ffmpeg, faster-whisper, and yt-dlp, with optional DaVinci Resolve and CapCut integration.1MIT
- AlicenseNot gradedqualityCmaintenanceAn MCP server that gives AI agents a stateful multi-track video editor powered by MLT, enabling them to assemble timelines, add media and text, apply filters and transforms, preview downscaled frames, and render final videos to disk.MIT