ffmpeg-render-pro
This server provides ffmpeg-render-pro video rendering and post-processing tools for AI agents. You can:
Detect GPU Encoders – Probe NVENC, VideoToolbox, AMF, VA-API, and QSV with 1-frame validation and CPU fallback; results cached for 7 days.
Get System Capabilities – Report CPU cores, RAM, ffmpeg version, GPU availability, recommended worker count, and codec choices for a target resolution.
Render Video in Parallel – Run a worker script across multiple threads, with fps/resolution/workers options, live dashboard, progress notifications, cancellation, and worker retry.
Get Worker Script Template – Retrieve the worker contract and a bundled reference worker, usable directly for custom or test renders.
Color Grade Video – Apply presets (noir, warm, cool, cinematic, vintage) or custom ffmpeg filters, re-encoding with configurable codec/CRF and optional audio keep.
Merge Audio into Video – Combine video and audio with stream-copied video, AAC audio, configurable bitrate, optional looping, and loudness normalization.
Concatenate Videos – Join multiple videos via stream copy with validation of codec, resolution, fps, and pixel format (or skip validation).
Provides video rendering and processing capabilities using FFmpeg, including parallel encoding across multiple workers with hardware acceleration (NVENC, VideoToolbox, AMF, VA-API, QSV), stream-copy concatenation, color grading presets (noir, warm, cool, cinematic, vintage), and audio merging with loudness normalization.
ffmpeg-render-pro
Render video from code, in parallel. You write one function that paints a frame; ffmpeg-render-pro splits the frame range across worker threads, encodes one MP4 segment per worker, joins the segments with stream copy (no re-encode), and shows a live dashboard in your browser while it runs. It also detects GPU encoders, grades color, merges audio, and ships as a CLI, a Node library, an MCP server for AI agents, and a Claude Code skill.
Built by Beeswax Pat. Free and open source.
Start here
Three commands. You need Node.js 18 or newer and ffmpeg on your PATH.
# 1. Prove the setup works: a 5 second test render. The dashboard opens in your browser.
npx ffmpeg-render-pro benchmark
# 2. Write a starter worker script into the current folder.
npx ffmpeg-render-pro init my-worker.js
# 3. Render it. Output lands in output.mp4.
npx ffmpeg-render-pro render my-worker.js --duration=5Open my-worker.js. The only function you need to change is renderFrame(frameNum, buffer): fill the buffer with your pixels (B, G, R, A, one row after another) and everything else is already done. Derive any randomness from the seed it receives and parallel output stays identical to a sequential render.
Install it globally if you would rather not type npx:
npm install -g ffmpeg-render-proRelated MCP server: ffmpeg-mcp
Using an ffmpeg that is not on PATH
FFMPEG_RENDER_PRO_FFMPEG=/opt/ffmpeg/bin/ffmpeg # ffmpeg binary
FFMPEG_RENDER_PRO_FFPROBE=/opt/ffmpeg/bin/ffprobe # optional; the sibling ffprobe is found automatically
FFMPEG_RENDER_PRO_CACHE_DIR=~/.ffmpeg-render-pro # optional; where GPU probe results are cachedThe variables are read at call time, so a long-running process such as the MCP server picks up changes without a restart.
CLI
ffmpeg-render-pro init [my-worker.js] # write the starter worker (--force overwrites)
ffmpeg-render-pro benchmark # 5 second test render with the bundled worker
ffmpeg-render-pro render <worker.js> # render with your worker
ffmpeg-render-pro info # cores, RAM, recommended workers, ffmpeg version, GPU
ffmpeg-render-pro detect-gpu # probe hardware encoders (--cpu / --gpu force a mode)
ffmpeg-render-pro versionRender and benchmark flags: --width=1920 --height=1080 (must be even), --fps=60, --duration=60 (fractions allowed), --output=out.mp4, --workers=N, --max-workers=8, --seed=42, --title="...", --crf=20 (0-51, lower is higher quality), --encoder-preset=fast (any x264 preset). Dashboard flags: --no-dashboard, --no-open, --port=8080, --linger-ms=30000 (0 exits as soon as the render finishes). Run ffmpeg-render-pro with no arguments for the full list.
An unknown flag warns and continues. A value that does not parse, such as --fps=abc, exits 1 instead of rendering at the default.
Installed binaries: ffmpeg-render-pro (this CLI) and ffmpeg-render-pro-mcp (the MCP server). The older ffmpeg-render-mcp name still works so existing MCP configs never break.
How a render works
renderParallelchecks ffmpeg, validates the resolution, and picks a worker count from your CPU cores and RAM (never more workers than frames).It starts the dashboard server on
127.0.0.1and opens your browser.Each worker thread runs your script with a frame range in
workerData, pipes raw BGRA frames into its own ffmpeg process, and writes one MP4 segment.Segments are joined with the concat demuxer and
-c copy, which takes seconds regardless of length.Temp files are removed. A failed worker's range is retried once before the render fails.
Your worker
A worker is a Node script that runs in a worker_threads thread. init gives you one where only renderFrame needs editing; examples/basic-worker.js in the installed package is a larger reference with a particle system and seeded RNG.
Fields the renderer injects through workerData:
Field | Meaning |
| Frame size and rate |
| Derive every random value from this |
| Render exactly |
| Write this worker's MP4 here |
| Include it in every message you post |
| Whole-video totals, for global effects such as a progress bar |
anything in | Your own extra keys (the bundled workers honor |
Messages the worker posts with parentPort.postMessage:
Message | When | Fields |
| periodically |
|
| optional, before replaying state to reach |
|
| once, after the segment is fully written |
|
| on failure, never followed by |
|
Every worker must encode with the same codec, resolution, framerate, and pixel format, because the segments are stream-copied together.
Library
const {
renderParallel, // the render engine
createEncoder, // pipe raw frames into ffmpeg with backpressure
detectGPU, // hardware encoder discovery, cached 7 days
getConfig, // worker count and codec choice for a resolution
computeTotalFrames, // float-safe fps x duration
concatSegments, // stream-copy join (validates inputs by default)
colorGrade, // presets or a custom -vf chain
mergeAudio, // add a soundtrack without re-encoding video
startDashboard, // the local progress server
ProgressTracker, // per-worker progress plus dashboard JSON
saveCheckpoint, loadCheckpoint, restoreCheckpoint, generateCheckpoints,
getEncoderIO, // encoder recipe as { inputArgs, filter, outputArgs }
getCodecArgs, // the same recipe as one flat array
ffmpegBin, ffprobeBin // resolved binaries, env-var aware
} = require('ffmpeg-render-pro');renderParallel(options)
const controller = new AbortController();
const result = await renderParallel({
workerScript: './my-worker.js', // required
outputPath: './output.mp4', // required
width: 1920, height: 1080, // even numbers, up to 7680x4320
fps: 60, duration: 60,
seed: 42,
title: 'My Render', // shown in the dashboard
workerCount: undefined, // exact count; omit to auto-detect
maxWorkers: 8, // cap for auto-detect
dashboard: true, autoOpen: true, dashboardPort: 8080,
dashboardLingerMs: 0, // 0 resolves as soon as the render ends (the CLI keeps it up 30s)
quiet: false, // true keeps stdout byte-clean; status goes to stderr
signal: controller.signal, // abort() stops workers and removes temp files
workerData: {}, // extra keys for your worker
});
// result: { outputPath, elapsed, totalFrames, avgFps }Abort rejects with an error whose name is 'AbortError'. In library use set dashboardLingerMs: 0 so the call returns without holding the process open. Set FFMPEG_RENDER_PRO_DEBUG=1 for full stack traces from the CLI.
Post-processing
// Color grade with a preset (noir, warm, cool, cinematic, vintage) or a custom -vf chain
await colorGrade({ inputPath: 'raw.mp4', outputPath: 'graded.mp4', preset: 'cinematic' });
await colorGrade({ inputPath: 'raw.mp4', outputPath: 'graded.mp4', filter: 'eq=contrast=1.08:saturation=0.9', crf: 18 });
await colorGrade({ inputPath: 'final.mp4', outputPath: 'graded.mp4', preset: 'noir', keepAudio: true }); // default strips audio
// Merge audio: video is stream-copied, audio becomes AAC. loop and normalize (loudnorm) are optional.
await mergeAudio({ videoPath: 'graded.mp4', audioPath: 'track.mp3', outputPath: 'final.mp4', bitrate: 320, loop: true, normalize: true });
// Join same-codec, same-size videos with stream copy. Inputs are probed with ffprobe first; pass { validate: false } to skip.
await concatSegments(['part-000.mp4', 'part-001.mp4'], 'joined.mp4');colorGrade accepts any encoder name in codec; encoders that need their own filter (VA-API) get it merged into the grade chain automatically.
Checkpoints for long renders
For multi-hour renders, snapshot your simulation state every N frames once, so each worker replays only the frames since the nearest snapshot instead of starting from frame 0.
generateCheckpoints({ systems, totalFrames: 432000, fps: 60, checkpointDir: './.checkpoints', interval: 60000 });
// inside a worker
const cp = loadCheckpoint('./.checkpoints', startFrame);
if (cp) {
const resumeFrame = restoreCheckpoint(cp, systems);
// fast-forward from resumeFrame to startFrame, then render
}systems is an object of named modules with getState(), setState(), and update(dt). A checkpoint labeled frame F holds exactly F updates. _frame and _timestamp are reserved keys.
MCP server (for AI agents)
Seven tools over stdio, usable from Claude Code, Claude Desktop, or any MCP client.
# Claude Code, no install needed
claude mcp add --transport stdio ffmpeg-render-pro -- npx --yes --package=ffmpeg-render-pro ffmpeg-render-pro-mcp
# Claude Code, after npm install -g ffmpeg-render-pro
claude mcp add --transport stdio ffmpeg-render-pro -- ffmpeg-render-pro-mcpClaude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"ffmpeg-render-pro": {
"command": "npx",
"args": ["--yes", "--package=ffmpeg-render-pro", "ffmpeg-render-pro-mcp"]
}
}
}Tool | What it does |
| Returns the worker contract, the starter worker source, and paths to both bundled workers. Start here. |
| Parallel render from a worker script, with progress notifications and cancellation |
| Probe hardware encoders (NVENC, VideoToolbox, AMF, VA-API, QSV) |
| Cores, RAM, recommended worker count, ffmpeg version |
| Presets or a custom filter |
| Add a soundtrack, video stream-copied |
| Stream-copy join, inputs validated by default |
The agent recipe: call get_worker_template, copy starterSource to a file and replace renderFrame, then call render_video with that file as worker_script (dashboard: false, auto_open: false for headless runs). To render without writing code, pass the returned starterPath or templatePath straight to render_video. Post-process with color_grade, merge_audio, and concat_videos.
Every tool declares an outputSchema and returns structuredContent, so parse JSON instead of text. Writers overwrite output_path. render_video defaults to 30 fps (the CLI defaults to 60), emits notifications/progress every 2 seconds when the client sends a progressToken (turn on resetTimeoutOnProgress for long renders), and stops all workers on client cancellation. stdout carries only JSON-RPC frames. Missing ffmpeg returns an error that names the install page and the env var.
The tarball also ships llms.txt at the package root and a Claude Code skill:
# from a global install (macOS / Linux)
cp -r "$(npm root -g)/ffmpeg-render-pro/.claude/skills/ffmpeg-render-pipeline" ~/.claude/skills/
# from a repo clone (Windows)
xcopy .claude\skills\ffmpeg-render-pipeline %USERPROFILE%\.claude\skills\ffmpeg-render-pipeline\ /E /INVENC quick reference
The renderer detects NVENC by itself. For one-off encodes outside it:
# confirm the encoder exists before relying on it
ffmpeg -y -f lavfi -i testsrc=size=256x256:rate=30:d=1 -c:v h264_nvenc -cq 23 probe.mp4
# encode: presets p1 (fastest) to p7 (best); -cq works like CRF, lower is better
ffmpeg -i in.mp4 -c:v h264_nvenc -preset p5 -cq 21 -pix_fmt yuv420p -c:a aac -b:a 192k -movflags +faststart out.mp4h264_nvenc rejects very narrow frames (145px minimum on a Turing card) by writing a zero-byte file and exiting, so keep probes at 256x256. Both commands come from the ffmpeg Render Cookbook ($12): 29 recipes, each run on ffmpeg 8.0.1 before publication.
Security notes
Releases are published to npm by GitHub Actions through npm trusted publishing (OIDC). There is no publish token, and every version from 1.5.2 on carries a provenance attestation that ties the tarball on npm to the exact commit and workflow run that built it (see the Provenance panel on the npm page).
The dashboard binds to
127.0.0.1only and loads nothing from the network. No telemetry.render_videoandrenderParallelexecute the worker script you name with the privileges of the current user. Only run workers you wrote or trust.The MCP server reads and writes files anywhere the current user can. Run it with a trusted agent, and consider restricting its working directory when prompts are untrusted.
A custom
filterstring is file access: ffmpeg filters such asmovie=andsubtitles=read local files. Treat filter input the way you treat a file path.Concat list files are written under
os.tmpdir(); output paths are written exactly where you point them.
Tests
npm test runs 12 zero-dependency suites (255 checks): unit, smoke, a real MCP session over stdio with a byte audit of stdout, and end-to-end renders verified with ffprobe and framemd5. It skips the render suites cleanly on machines without ffmpeg. CI runs the same on Ubuntu, Windows, and macOS against Node 18, 20, 22, and 24.
Changelog and license
See CHANGELOG.md. MIT.
Available Tools
7 toolscolor_gradeColor Grade VideoADestructive
Apply a color grade to a video file and write the result to a new file. Provide either a built-in preset (noir, warm, cool, cinematic, vintage) or a custom ffmpeg -vf filter string (filter overrides preset). Re-encodes the video stream with the chosen codec; audio is stripped unless keep_audio is true. Prerequisite: ffmpeg installed. Side effect: overwrites output_path if it already exists.
| Name | Required | Description | Default |
|---|---|---|---|
| crf | No | CRF/CQ quality, 0-51. Lower is higher quality. Default 18. | |
| codec | No | Video encoder for the output. Default libx264. | libx264 |
| filter | No | Custom ffmpeg -vf filter string. Overrides preset when both are given. | |
| preset | No | Built-in color grade preset | |
| input_path | Yes | Input video file path. Must exist. | |
| keep_audio | No | Stream-copy the input audio track instead of stripping it. Default false (audio stripped). | |
| output_path | Yes | Output video file path. Overwritten if it already exists. |
Output Schema
| Name | Required | Description |
|---|---|---|
| crf | Yes | CRF/CQ quality value applied |
| codec | Yes | Encoder used for the output |
| filter | Yes | Custom filter applied, or null when a preset was used |
| preset | Yes | Preset applied, or null when a custom filter was used |
| audioKept | Yes | True when the input audio track was stream-copied into the output |
| inputPath | Yes | Absolute input file path |
| outputPath | Yes | Absolute output file path |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description openly discloses the destructive side effect of overwriting output_path, re-encoding behavior, and the default stripping of audio, all consistent with the destructiveHint annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single well-organized paragraph covering purpose, modes, processing behavior, prerequisite, and side effect without unnecessary verbosity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
It includes the required prerequisite (ffmpeg installed) and the key side effect (overwrite), making the tool's behavior fully understandable even without inspecting the output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already documents all seven parameters with descriptions, and the description adds context about re-encoding and the relationship between preset and filter beyond the schema fields.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool applies a color grade to a video file and writes the result to a new file, which is specific and distinguishes it from rendering, merging, or concatenating videos.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explains how to choose between a preset and a custom filter, and clarifies the audio handling behavior. It does not explicitly contrast with sibling tools, but the intent is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
concat_videosConcatenate VideosADestructive
Join multiple video files into one with stream copy: no re-encoding, completes in seconds regardless of file size. All inputs must share the same codec, resolution, framerate, and pixel format. By default each input is probed with ffprobe and mismatches are rejected before ffmpeg runs, because ffmpeg itself accepts mismatched inputs and writes a silently corrupt file; set validate false to skip the probes for inputs known to be uniform. Prerequisite: ffmpeg installed. Side effect: overwrites output_path if it already exists.
| Name | Required | Description | Default |
|---|---|---|---|
| validate | No | Probe every input with ffprobe and reject codec/resolution/framerate/pixel-format mismatches before concatenating. Default true. Skipped with a warning when ffprobe is not installed. | |
| input_files | Yes | Video file paths to concatenate, in playback order. All must exist. | |
| output_path | Yes | Output file path. Overwritten if it already exists. |
Output Schema
| Name | Required | Description |
|---|---|---|
| segments | Yes | Absolute paths of the concatenated inputs, in order |
| validated | Yes | True when segment compatibility validation was requested |
| outputPath | Yes | Absolute output file path |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description explicitly discloses the destructive side effect of overwriting output_path, aligning with the destructiveHint annotation. It also explains the validation behavior with ffprobe, the rejection of mismatched inputs, and the skip behavior when validate is false, giving a clear picture of execution behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is slightly long but well-structured with clear sections for purpose, constraints, validation, prerequisite, and side effect. The explanatory clause about why validation is needed is verbose but adds important rationale for the default behavior.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description provides all necessary operational context: the lossless nature of the operation, input constraints, validation defaults, prerequisite software, and destructive side effects. This is sufficient for an agent to correctly invoke the tool without needing external documentation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already covers parameter descriptions at 100%, but the tool description adds meaningful context: it explains the impact of the validate flag, restates that input files must be in playback order, and confirms that output_path is overwritten. This goes beyond the schema without being redundant.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: joining multiple video files into one using stream copy with no re-encoding. It immediately distinguishes this from a rendering or transcoding operation, making the tool's role unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explains when to use the tool (concatenation with matching codecs/parameters) and when to set validate to false for known-uniform inputs. It does not explicitly compare against sibling tools like render_video, but the 'no re-encoding' phrasing strongly implies the appropriate use case.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
detect_gpuDetect GPU EncodersARead-only
Detect the best available hardware video encoder on this machine. Probes NVENC, VideoToolbox, AMF, VA-API, and QSV with a 1-frame validation encode and falls back to CPU (libx264) when no GPU encoder works. Use before rendering or grading to confirm hardware acceleration. Results are cached for 7 days in ~/.ffmpeg-render-pro/gpu-cache.json. Requires ffmpeg on PATH or FFMPEG_RENDER_PRO_FFMPEG.
| Name | Required | Description | Default |
|---|---|---|---|
| force_mode | No | auto probes hardware then falls back to CPU (default). cpu skips probing and always returns libx264. gpu fails if no hardware encoder is found. | auto |
Output Schema
| Name | Required | Description |
|---|---|---|
| all | Yes | All validated encoders including the CPU fallback |
| h264 | Yes | Best available H.264 encoder name |
| hevc | Yes | Best available HEVC encoder name, or null |
| isGpu | Yes | True when a hardware encoder validated |
| label | Yes | Human-readable label of the selected encoder |
| ffmpegVersion | Yes | Detected ffmpeg version string |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description states that results are 'cached for 7 days in ~/.ffmpeg-render-pro/gpu-cache.json', which involves writing to the filesystem. This contradicts the readOnlyHint annotation that indicates the tool has no side effects. This is a direct conflict between the behavior described and the annotated read-only nature.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise, with four focused sentences that lead with the main purpose, then detail the probing behavior, the recommended usage, and the caching/requirements. No unnecessary words or redundant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers key prerequisites (ffmpeg on PATH), the fallback behavior, and caching. The output schema is provided, though the description does not explicitly state what the output structure will be, but that information is available in the schema. Given the tool's simplicity, this is sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema description coverage is 100%, fully documenting the single force_mode parameter with its enum values and default. The description itself adds no extra context about the parameter beyond what is already in the schema, so a baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool 'Detect the best available hardware video encoder on this machine', identifying both the action (detect) and the resource (best hardware video encoder) while scoping it to the current machine. It distinguishes itself from sibling tools like render_video or system_info by focusing on encoder detection.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly advises when to use the tool: 'Use before rendering or grading to confirm hardware acceleration.' This provides clear timing guidance and implies that it should be used before other rendering/grading operations, making the usage context unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_worker_templateGet Worker Script TemplateARead-only
Get the worker script contract required by render_video, plus the full source of the bundled starter worker (a small file where only renderFrame() needs editing) and the path of the larger reference worker. Use this before authoring a custom worker: copy starterSource to a new file, replace renderFrame(), and pass that file as worker_script. Either templatePath (test scene) or starterPath (gradient) can be passed directly as render_video's worker_script to render without writing code. Read-only: returns documentation and source text, touches nothing.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| messages | Yes | Messages the worker must post to parentPort, their fields, and when to send them |
| workerData | Yes | Fields injected into the worker via worker_threads workerData, mapped to their meaning |
| starterPath | Yes | Absolute path of the bundled starter worker (edit renderFrame only); usable directly as render_video worker_script |
| templatePath | Yes | Absolute path of the bundled reference worker (particle test scene); usable directly as render_video worker_script |
| starterSource | Yes | Full source code of the starter worker; copy it, replace renderFrame(), save as your worker_script |
| templateSource | Yes | Full source code of the bundled reference worker |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
States 'Read-only: returns documentation and source text, touches nothing.' This aligns with readOnlyHint and adds explicit assurance of no side effects. The description also clarifies it returns documentation and source, so no destructive actions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise yet comprehensive, covering all necessary details in a few sentences without redundancy. Each sentence contributes to understanding the tool's function, usage, and output.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the output schema exists, the description lists the key returned items (contract, starter source, reference path) and mentions templatePath and starterPath, providing a clear picture of the output. It also places the tool in context with render_video, making it complete for the intended use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, and the schema is empty, so no parameter description is needed. The baseline score for zero parameters is 4, and the description adds no parameter-specific info, which is acceptable.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it gets the worker script contract, starter source, and reference path, specifically for render_video. It distinguishes from siblings by focusing on template retrieval rather than rendering or system info.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs to use before authoring a custom worker, with steps to copy, replace renderFrame, and pass as worker_script. Also notes that templatePath or starterPath can be passed directly to render_video, providing alternatives. This gives clear when-to-use and how-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
merge_audioMerge Audio into VideoADestructive
Combine a video file and an audio file into one output file. The video stream is copied without re-encoding; audio is encoded to AAC at the given bitrate. Loops audio shorter than the video when loop is true, and can apply loudness normalization (ffmpeg loudnorm) for YouTube-style targets. Prerequisite: ffmpeg installed. Side effect: overwrites output_path if it already exists.
| Name | Required | Description | Default |
|---|---|---|---|
| loop | No | Loop the audio if it is shorter than the video. Default true. | |
| bitrate | No | Audio bitrate in kbps, 8-1024. Default 320. | |
| normalize | No | Apply loudness normalization (loudnorm I=-22 TP=-2 LRA=7). Default false. | |
| audio_path | Yes | Input audio file path. Must exist. | |
| video_path | Yes | Input video file path. Must exist. | |
| output_path | Yes | Output file path. Overwritten if it already exists. |
Output Schema
| Name | Required | Description |
|---|---|---|
| looped | Yes | True when audio looping was enabled |
| audioPath | Yes | Absolute input audio path |
| videoPath | Yes | Absolute input video path |
| normalized | Yes | True when loudness normalization was applied |
| outputPath | Yes | Absolute output file path |
| bitrateKbps | Yes | Audio bitrate applied, in kbps |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the destructiveHint annotation, the description explicitly states the side effect of overwriting the output path, plus details on stream copying, audio encoding, looping, and normalization. It also notes the ffmpeg prerequisite, providing full behavioral transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Concise, with the main purpose stated first and all key behaviors summarized in a few sentences. No irrelevant details.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the essential behaviors: input merging, stream handling, side effects, prerequisites, and optional flags. Given that an output schema exists, return value details need not be included. The description is complete for an agent to decide and invoke correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already covers all parameters with clear descriptions (100% coverage). The description adds minor context (e.g., AAC encoding and looping behavior) but largely overlaps with the schema, so no significant additional meaning is provided.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states the tool combines a video and audio file into an output, with specific verbs and resources. This distinguishes it from sibling tools like render_video or concat_videos.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains what the tool does but does not explicitly give when to use it versus alternatives or when not to use it. It mentions conditions like looping and normalization but no comparative guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
render_videoRender Video (Parallel Workers)ADestructive
Render a video by running a frame-generating worker script across parallel worker threads. Splits the frame range across N workers, encodes one MP4 segment per worker, then joins segments with stream copy. Prerequisites: ffmpeg installed, plus a worker script implementing the ffmpeg-render-pro worker contract; call get_worker_template first if you need to author a worker, or use its templatePath for a ready-made test scene. Side effects: overwrites output_path if it already exists and writes dashboard files under /preview. Long renders emit MCP progress notifications when the client supplies a progressToken (enable resetTimeoutOnProgress for renders longer than the client request timeout; per-frame progress detail requires dashboard true, the default). Client cancellation stops all workers and removes temp files. Note: this tool defaults to 30 fps; the CLI defaults to 60.
| Name | Required | Description | Default |
|---|---|---|---|
| fps | No | Framerate in frames per second, 1-240. Default 30 for this tool (CLI default is 60). | |
| seed | No | RNG seed passed to workers for deterministic output. Default 42. | |
| title | No | Title shown in the dashboard. Default "Render". | Render |
| width | No | Frame width in pixels. Must be even (yuv420p). Default 1920, max 7680. | |
| height | No | Frame height in pixels. Must be even (yuv420p). Default 1080, max 4320. | |
| workers | No | Exact worker thread count, overriding auto-detection. Omit to auto-detect from CPU/RAM. | |
| duration | Yes | Video duration in seconds. Required. | |
| auto_open | No | Open the dashboard in a browser. Default true; set false for headless or server use. | |
| dashboard | No | Serve the live HTML progress dashboard and write preview JSON files. Default true. | |
| linger_ms | No | How long the dashboard stays up after completion, in milliseconds. Default 30000; 0 stops it immediately. | |
| max_workers | No | Cap for the auto-detected worker count. Ignored when workers is set. Default 8. | |
| output_path | Yes | Output video file path. Overwritten if it already exists. | |
| worker_script | Yes | Absolute path to the worker .js file that generates frames. Must implement the contract returned by get_worker_template. | |
| dashboard_port | No | Dashboard starting port, 1-65535. Default 8080; increments if occupied. |
Output Schema
| Name | Required | Description |
|---|---|---|
| avgFps | Yes | Average frames encoded per second across the whole render |
| outputPath | Yes | Absolute path of the rendered video file |
| totalFrames | Yes | Total frames rendered |
| elapsedSeconds | Yes | Wall-clock render time in seconds |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, but the description goes far beyond: it spells out that output_path is overwritten, that dashboard files are written, that progress notifications depend on a progressToken and resetTimeoutOnProgress, that cancellation stops workers and removes temp files, and it discloses the fps default discrepancy. This is rich behavioral context with no contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but well-organized: it opens with the core action, then prerequisites, side effects, progress behavior, cancellation, and a final note on fps defaults. Every sentence contributes new information and the structure is logical. It is slightly long but not bloated; a 4 reflects its efficiency and front-loaded action.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 14 parameters, an output schema, and multiple behavioral nuances (destructive, progress, cancellation, dashboard), the description covers all essential usage aspects: prerequisites, side effects, progress token handling, cancellation semantics, and the CLI difference. The output schema handles return values, so nothing critical is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents every parameter (including defaults, ranges, and even the CLI fps difference for the fps param). The description itself adds little parameter-specific meaning; it references the worker contract and prerequisites but does not elaborate on individual params. Baseline 3 is appropriate since the schema carries the burden.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb-resource pair ('Render a video') and the precise mechanism ('running a frame-generating worker script across parallel worker threads'), which clearly distinguishes it from siblings like concat_videos or color_grade. It also mentions the segmentation approach (one MP4 per worker, then stream copy) so an agent understands the exact operation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear context: prerequisites (ffmpeg, worker contract), when to call get_worker_template first, and side effects. It notes a key distinction from the CLI (fps defaults) but doesn't explicitly state 'use this instead of X' for sibling tools. The guidance is sufficient for correct invocation though not exhaustive about alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
system_infoGet Render System CapabilitiesARead-only
Get render system capabilities: CPU cores, total and free RAM, the recommended parallel worker count for a target resolution, detected GPU encoder, segment and final codecs, and ffmpeg version. Use before render_video to choose a worker count or check whether this machine can handle a resolution. Requires ffmpeg on PATH or FFMPEG_RENDER_PRO_FFMPEG.
| Name | Required | Description | Default |
|---|---|---|---|
| width | No | Target render width in pixels for the worker recommendation. Default 1920, max 7680. | |
| height | No | Target render height in pixels for the worker recommendation. Default 1080, max 4320. |
Output Schema
| Name | Required | Description |
|---|---|---|
| arch | Yes | CPU architecture |
| tier | Yes | Resolution tier used for the recommendation (480p to 4k) |
| isGpu | Yes | True when a hardware encoder is available |
| workers | Yes | Recommended parallel worker count for the target resolution |
| cpuCores | Yes | Logical CPU core count |
| gpuLabel | Yes | Label of the detected GPU encoder or CPU fallback |
| platform | Yes | Node platform string (win32, linux, darwin) |
| freeRamMB | Yes | Free RAM in MB |
| finalCodec | Yes | Codec used for final single-file passes |
| totalRamMB | Yes | Total RAM in MB |
| segmentCodec | Yes | Codec used for parallel segment encoding |
| ffmpegVersion | Yes | Detected ffmpeg version string |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, and the description adds meaningful context beyond that: it requires ffmpeg on PATH or via FFMPEG_RENDER_PRO_FFMPEG. It also discloses that the tool reports a recommended worker count for a target resolution, which helps agents understand the dependency on input parameters. No contradiction with annotations exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two dense sentences deliver the full purpose and usage context, followed by a necessary prerequisite. The most actionable information is front-loaded, and every sentence earns its place without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the read-only annotation, the output schema, and fully described optional parameters, the description covers what the tool returns, when to call it, and the critical ffmpeg prerequisite. An agent has enough information to decide whether and how to invoke this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, with width and height each documented with defaults and ranges. The description adds only a general reference to 'target resolution,' which is already implied by the schema's 'worker recommendation' phrasing. The schema carries the parameter-meaning burden, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource — 'Get render system capabilities' — and enumerates the exact data returned: CPU cores, RAM, worker count, GPU encoder, codecs, and ffmpeg version. It distinguishes itself from the render_video sibling by framing this as a pre-render query rather than a rendering operation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use it: 'Use before render_video to choose a worker count or check whether this machine can handle a resolution.' It does not explicitly discuss when not to use it or name alternatives such as detect_gpu, but the primary use case is clearly communicated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
7 tool updates
v1.5.2- Added
color_grade - Added
concat_videos - Added
detect_gpu - Added
get_worker_template - Added
merge_audio - Added
render_video - Added
system_info
TDQS
Scored across 7 tools
Most tools have clearly distinct roles, but detect_gpu and system_info overlap significantly since system_info already reports the detected GPU encoder; the separate detect_gpu tool may create ambiguity about which to call.
Six of seven tools follow a clear verb_noun pattern (detect_gpu, render_video, get_worker_template, color_grade, merge_audio, concat_videos), but system_info is a noun phrase and breaks the otherwise consistent convention.
Seven tools is a well-scoped set for an ffmpeg rendering server: capability checks, the core render operation, a worker template helper, and three common post-processing operations. No redundant or excessive tools.
The surface covers the main render workflow and common edits like color grading, audio merging, and concatenation. It lacks utilities such as trimming, format conversion, or media probing, but those are arguably outside the render-focused purpose.
Maintenance
Related MCP Connectors
MCP server for Google Veo AI video generation
MCP server for Wan AI video generation
MCP server for Clipkit — gives AI agents a video toolbox via the Clipkit schema.
MCP server for Luma Dream Machine AI video generation
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceA cinema-grade video production MCP server that enables automated website recording, editing, and AI-powered narration using ffmpeg and Playwright. It provides tools for color grading, captioning, and converting videos into social media formats through natural language commands.61 npm5MIT
- FlicenseNot gradedqualityDmaintenanceAn MCP server that provides 17 FFmpeg-based tools for video and audio processing, including conversion, compression, and editing. It enables AI assistants to perform complex media tasks like extracting audio, adding watermarks, and merging videos using natural language.194 npm2-
- AlicenseBqualityBmaintenanceMCP server for video enhancement and SAM3 image segmentation, enabling tasks like upscaling videos and segmenting objects in images via natural language.422 npmMIT
- AlicenseNot gradedqualityCmaintenanceMCP server enabling interaction with Twitter, YouTube, Instagram, and video processing via 31 tools, with local Whisper transcription and frame extraction for visual verification.8MIT