@ffmpeg-micro/mcp-server
An MCP server that lets AI agents run and manage FFmpeg Micro video transcoding, audio transcription, and file upload jobs.
Create transcodes (
transcode_video): one to tengs:///https://inputs (concatenated in order), output mp4/webm/mov, using quality/resolution presets or raw FFmpeg/virtual options.Transcode in one call (
transcode_and_wait): creates a job, polls to a terminal state (configurable timeout/poll interval), returns the job plus a signed download URL.Monitor jobs (
get_transcode,list_transcodes): fetch one job's status/output, or list jobs filtered by status and time range with pagination.Cancel jobs (
cancel_transcode): cancel queued or processing transcodes.Get output links (
get_download_url): 10-minute signed HTTPS URL for a completed transcode's output.Transcribe audio/video (
transcribe_audio,get_transcribe,get_transcribe_download): Whisper SRT generation (transcribe or translate, optional language hint), with a signed SRT URL usable in asubtitlesfilter.Upload local files (
request_upload_url,confirm_upload): presigned PUT flow returning ags://URL plus probe metadata for use as transcode/transcribe input.
Provides tools to interact with the FFmpeg Micro REST API for video transcoding, including creating, monitoring, canceling jobs, and uploading local files for processing.
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-micro/mcp-serverTranscode this video to 720p mp4."
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.
@ffmpeg-micro/mcp-server
A Model Context Protocol server that lets AI agents — Claude Code, Claude Desktop, Cursor, Windsurf, VS Code, and any other MCP-compatible client — create, monitor, and download video transcodes through the FFmpeg Micro REST API.
What it does
Exposes tools that map onto FFmpeg Micro's public API:
Tool | What it does |
| Create a transcode job from one or more input videos ( |
| Fetch the current state of a single job. |
| List jobs with optional |
| Cancel a pending or processing job. |
| Generate a 10-minute signed HTTPS URL for a completed job's output file. |
| Convenience: create a job, poll until it finishes, return the signed download URL in one call. |
| Step 1 of the direct-upload flow. Returns a presigned HTTPS URL that the host PUTs the file bytes to. |
| Step 2 of the direct-upload flow. Returns the final |
| Start a blueprint run — a pre-built video workflow (captioning, resizing, watermarking, ads, and more). |
| Fetch a blueprint run's status, step, and output URLs (multi-output blueprints return labeled |
| Convenience: start a blueprint run and poll until it completes, fails, or pauses for transcript review. |
| Resume a |
Blueprints
Blueprints are pre-built workflows behind POST /v1/blueprints/{slug}/runs. The tool descriptions document each blueprint's input fields. Notes:
Most blueprints run on the FFmpeg lane and meter plan compute minutes (no tokens). Generative blueprints (
product-ad) charge tokens; a402 insufficient_tokensresponse means the account needs a token pack (dashboard).caption-videopauses inawaiting_reviewwith the transcript (srt_text) so the agent can review/edit before rendering; resume withcontinue_blueprint_run.Multi-output blueprints (
listing-kit,hook-variants) return anoutputsarray of{label, url}— prefer it overoutput_urlwhen present.Output URLs are signed with a 10-minute TTL; re-fetch the run for fresh links.
Uploading a local file
The request_upload_url + confirm_upload pair lets an MCP host upload a local file to the FFmpeg Micro storage bucket without dealing with raw API keys or gs:// URLs:
Host calls
request_upload_urlwith{filename, contentType, fileSize}→ receives a short-lived presigned HTTPS URL.Host PUTs the file bytes to that URL with the same
Content-Type.Host calls
confirm_uploadwith{filename: <storage filename from step 1>, fileSize}→ receives the finalgs://...fileUrl.Host passes that
fileUrltotranscribe_audio/transcode_video/transcode_and_wait.
Related MCP server: ffmpeg-mcp
Quick start
Add this to your project's .mcp.json (or your MCP client's config):
{
"mcpServers": {
"ffmpeg-micro": {
"type": "http",
"url": "https://mcp.ffmpeg-micro.com"
}
}
}That's it. The first time your AI tool connects, it will open a browser window for you to sign in with your FFmpeg Micro account via OAuth. After you approve, the token is cached and you won't be asked again.
No API keys to copy, no environment variables to set.
Authentication
OAuth (recommended)
The MCP server supports OAuth 2.1 with PKCE and dynamic client registration. Your MCP client handles the entire flow automatically:
Client discovers OAuth endpoints via
/.well-known/oauth-authorization-serverClient registers itself dynamically
Browser opens for you to sign in and approve access
Token is exchanged and cached — subsequent connections are instant
This is the default when you use the config above with no headers or env block.
API key (alternative)
If you prefer to use an API key directly (e.g., for automation or CI), you can pass it as a Bearer token:
{
"mcpServers": {
"ffmpeg-micro": {
"type": "http",
"url": "https://mcp.ffmpeg-micro.com",
"headers": {
"Authorization": "Bearer your_api_key_here"
}
}
}
}Get your API key from the dashboard.
stdio (local install)
Runs the server as a local process using npx. Requires Node.js 22.14 or later.
{
"mcpServers": {
"ffmpeg-micro": {
"command": "npx",
"args": ["-y", "@ffmpeg-micro/mcp-server"],
"env": {
"FFMPEG_MICRO_API_KEY": "your_api_key_here"
}
}
}
}npx -y fetches the latest version each time. Any MCP client that supports stdio servers works with this config.
Compatible tools
The HTTP config (OAuth) works with any MCP client that supports streamable HTTP transport:
Claude Code (CLI)
Claude Desktop
Cursor
Windsurf
VS Code (GitHub Copilot MCP)
The stdio config works with any MCP client that supports stdio transport.
Example prompts
Once connected, you can ask things like:
"Transcode this video to 720p MP4 and give me the download URL when it's done."
"Crop this landscape video to a square."
"Add a text overlay saying 'Episode 12' to my video."
"List my failed jobs from this week."
"Cancel job
b5f5a9c0-9e33-4e77-8a5b-6a0c2cd9c0b3."
Development
git clone https://github.com/ffmpeg-micro/ffmpeg-micro-mcp.git
cd ffmpeg-micro-mcp
./scripts/setup.shsetup.sh installs dependencies, builds, and wires up the git hooks.
Point your MCP client at the local build to iterate:
{
"mcpServers": {
"ffmpeg-micro-dev": {
"command": "node",
"args": ["/absolute/path/to/ffmpeg-micro-mcp/dist/index.js"],
"env": { "FFMPEG_MICRO_API_KEY": "…" }
}
}
}The MCP Inspector is the fastest way to iterate on tool schemas and responses:
npx @modelcontextprotocol/inspector node dist/index.jsTo run the HTTP server locally against a local API gateway:
FFMPEG_MICRO_API_URL=http://localhost:8081 npm run serveRunning integration tests locally
FFMPEG_MICRO_API_KEY=your_key npm run test:integrationIntegration tests hit the real FFmpeg Micro production API. They are read-only (no jobs are created).
Smoke-testing the upload tools end-to-end
Unit tests use a mocked fetch, so they prove tool registration + Zod schemas + URL paths but not that the wire shapes match what the gateway actually returns. Two smoke scripts exercise the full request_upload_url → PUT → confirm_upload flow against a real MCP server using a real API key. Run them in order — stdio first (fastest signal), then a deployed HTTP server before/after merge:
# 1. stdio (local dist build) — spawns dist/index.js as a subprocess
npm run build
FFMPEG_MICRO_API_KEY=your_key node scripts/smoke-upload-stdio.mjs <local-file>
# 2. HTTP (any deployed server — local `npm run serve`, Vercel preview, or prod)
FFMPEG_MICRO_API_KEY=your_key MCP_URL=https://mcp.ffmpeg-micro.com/ \
node scripts/smoke-upload-http.mjs <local-file>Both scripts hit the production API by default and consume billable minutes (the stdio script chains into transcribe_audio for an end-to-end check). Pass a small file like 15-second.mp3 to keep the cost negligible.
A third script smoke-tests the blueprint tools (run_blueprint + get_blueprint_run polled to completion on resize-format, then run_blueprint_and_wait on hook-variants to exercise multi-output). It uses FFmpeg-lane blueprints only, so it consumes plan compute minutes but no tokens:
npm run build
FFMPEG_MICRO_API_KEY=your_key node scripts/smoke-blueprints-stdio.mjsHitting protection-protected Vercel previews
Vercel preview deployments are gated by Deployment Protection by default. To exercise the HTTP smoke script against a preview URL, generate a Protection-Bypass-for-Automation token in the project's Vercel settings and pass it via VERCEL_BYPASS:
FFMPEG_MICRO_API_KEY=your_key \
MCP_URL=https://your-preview.vercel.app/ \
VERCEL_BYPASS=your_bypass_token \
node scripts/smoke-upload-http.mjs <local-file>The script sends the token as the x-vercel-protection-bypass header on every request. It does not send x-vercel-set-bypass-cookie: true — that variant triggers a 307 cookie-setting redirect on POST that the MCP SDK's StreamableHTTPClientTransport does not follow, so the request fails. The header alone returns 200 directly without the redirect dance.
Release process
Releases are published to npm via trusted publishing and to the MCP Registry as com.ffmpeg-micro/mcp-server, authenticated via an Ed25519 DNS TXT record on ffmpeg-micro.com. The corresponding private key lives in the MCP_PRIVATE_KEY GitHub Actions secret. The npm side uses OIDC trusted publishing, so no npm token is stored.
Releases are automated via Changesets. Contributors don't manually bump versions, tag commits, or run publish commands — they attach a changeset to their PR and the release pipeline handles the rest.
Contributor flow (every PR)
Every PR that changes shipped code must include a changeset. A CI check enforces this.
# While working on your PR:
npx changesetThe CLI prompts for bump type (major/minor/patch) and a short summary. It writes a markdown file under .changeset/ — commit that file with your PR.
Escape hatches for non-release PRs (docs, CI, internal refactor, test changes with no behavioral impact):
Add the
no-changesetlabel to the PR, ornpx changeset --emptyto explicitly declare "no release needed."
Maintainer flow (cutting a release)
You don't manually cut releases. The pipeline does it:
PRs land on
mainwith changeset files attached..github/workflows/release.ymlruns on every push tomain. When pending changesets exist, it opens (or updates) achore(release): version packagesPR authored by the action. That PR:Runs
changeset versionto consume the pending changesetsBumps
package.jsonRe-syncs
server.jsonandsrc/version.tsviascripts/sync-server-version.mjsAppends entries to
CHANGELOG.mdCommits the result to its own branch
Review and merge the Version Packages PR when you're ready to ship. You can let several changesets accumulate before merging — the PR updates itself as more land on
main.On merge, the release workflow runs again. This time there are no pending changesets, so
changesets/actiondetects the version bump and:changeset publish, which runsnpm publish(OIDC trusted publishing, with provenance attestation) only if that version isn't on npm yet, so every other push tomainpasses this stepCreates the GitHub Release + git tag automatically
The workflow's final steps install
mcp-publisher, authenticate via the DNS private key, and publish to the MCP Registry ascom.ffmpeg-micro/mcp-server.
Version-sync guard
.github/workflows/release.yml still runs a version-parity check on every push to main. If package.json.version, server.json.version, and server.json.packages[0].version ever drift, the build fails loudly. SERVER_VERSION in src/version.ts is covered by tests/version.test.ts, which checks the version reported in a real initialize response from the shared server builder (src/mcpServer.ts, used by both entry points) and from the hosted HTTP app. Normally scripts/sync-server-version.mjs keeps them aligned, but the guard catches manual edits that missed the sync.
Verify
After the Version Packages PR is merged and the workflow is green:
npm view @ffmpeg-micro/mcp-server version
curl -s "https://registry.modelcontextprotocol.io/v0/servers?search=com.ffmpeg-micro/mcp-server" | jq '.servers[] | {v: .server.version, isLatest: ._meta."io.modelcontextprotocol.registry/official".isLatest}'Example: contributor walkthrough
Suppose you're adding a new delete_transcode tool. Your PR flow:
git switch -c feat/delete-transcode
# ... make the code + test changes ...
npx changeset
# ? Which packages would you like to include? › @ffmpeg-micro/mcp-server
# ? Which type of change is this for @ffmpeg-micro/mcp-server? › minor
# ? Please enter a summary for this change › Add delete_transcode tool
git add .changeset/*.md src/ tests/
git commit -m "feat: add delete_transcode tool"
git push -u origin feat/delete-transcode
gh pr createCI runs three checks:
test— unit testscheck(Require changeset) — confirms.changeset/*.mdis presentVercel— preview deploy
After merge, the Version Packages PR either opens or updates itself to include your entry. Merge that when you're ready to ship.
Rules
Never edit version fields in
server.json,package.jsonorsrc/version.tsby hand. Changesets owns them —scripts/sync-server-version.mjsmirrorspackage.jsonintoserver.jsonandsrc/version.ts. The CI drift guard fails the release if they diverge.Never
git taga release manually.changesets/actioncreates the tag + GitHub Release as part of publish. Manual tags aren't picked up by the new workflow.Never bypass the Require-changeset check by committing changes to
.changeset/config.jsonor.changeset/README.md(those don't count). Usenpx changeset, theno-changesetlabel, ornpx changeset --empty.
Release-related files
package.json— source of truth for version. Also holdsmcpName(required by the MCP Registry for npm package validation). Bumped bychangeset version.server.json— MCP Registry metadata. Version fields are auto-synced frompackage.json..changeset/config.json— Changesets configuration (public access, GitHub-aware changelog formatter)..changeset/*.md— pending release notes waiting to be consumed by the nextchangeset versionrun.scripts/sync-server-version.mjs— mirrorspackage.jsonversion intoserver.jsonandsrc/version.ts(the version the server reports ininitialize)..github/workflows/release.yml— the publish pipeline (changesets/action + MCP Registry step)..github/workflows/require-changeset.yml— enforces changeset presence on PRs.
Troubleshooting
Require changesetcheck fails on my PR — runnpx changesetand commit the generated file. For docs-only / CI-only PRs, add theno-changesetlabel ornpx changeset --empty.CI fails at the version-sync guard step —
server.jsonwas edited manually. Locally:node scripts/sync-server-version.mjs, commit, push. The guard comparespackage.json.version,server.json.version, andserver.json.packages[0].version. Iftests/version.test.tsfails instead,src/version.tsdrifted: the samenode scripts/sync-server-version.mjsfixes it.changesets/actiondidn't open a Version Packages PR after my feature PR merged — check that your PR's.changeset/*.mdfile actually had content (non-empty front matter with a bump type and summary). Empty changesets signal "no release needed" and are intentionally ignored.mcp-publisher publishfails with "package not found" — npm hasn't finished propagating the new version yet. The release workflow'sDetermine if MCP Registry publish is neededstep retriesnpm viewfor up to ~50 seconds and backs off if the version still isn't live, deferring the registry publish to the next push to main (which self-heals the drift). If you see this in a manual run, just wait 30s and re-publish.MCP Registry stuck a version behind npm — the
Determine if MCP Registry publish is neededstep skipped (or returnedneeded=false). Push any commit to main to trigger a re-run; the gate comparespackage.json↔ npm ↔ registry and catches up automatically. If it keeps skipping, check the step's log output for which version each source reported.mcp-publisher publishfails validation with "mcpName mismatch" —package.jsonmcpNamemust equalserver.jsonname(both should becom.ffmpeg-micro/mcp-server).mcp-publisher login dnsfails with "public key mismatch" — theMCP_PRIVATE_KEYsecret no longer matches the TXT record onffmpeg-micro.com. Regenerate the keypair locally, update both the TXT record and the GitHub secret.
License
MIT — see LICENSE.
Available Tools
11 toolscancel_transcodeCancel TranscodeAInspect
Cancel a queued or processing transcode job. Jobs that are already completed, failed, or cancelled cannot be cancelled and return an error.
Inputs: id (required).
confirm_uploadConfirm UploadAInspect
Step 2 of the direct-upload flow. Call after PUTting the file bytes to the URL returned by request_upload_url. Returns the final gs://... fileUrl plus probe metadata (duration, format, codecs). Use the fileUrl directly as a media_url for transcribe_audio or as an inputs[].url for transcode_video.
Inputs: fileSize (required), filename (required), uploadId.
get_download_urlGet Download URLAInspect
Generate a short-lived (10 minute) signed HTTPS URL for a completed transcode's output file. The job must be in completed status. Use this instead of the output_url field on the job object, which is a gs:// URL that HTTP clients cannot fetch directly.
Inputs: id (required).
get_transcodeGet TranscodeAInspect
Fetch the current state of a single transcode job by ID, including status (queued/processing/completed/failed) and output_url when completed.
Inputs: id (required).
get_transcribeGet TranscribeAInspect
Fetch the current state of a single transcribe job by ID, including status (queued/processing/completed/failed) and output_url when completed. Mirrors get_transcode but for SRT generation jobs created via transcribe_audio.
Inputs: id (required).
get_transcribe_downloadGet Transcribe Download URLAInspect
Generate a short-lived (10 minute) signed HTTPS URL for a completed transcribe job's SRT file. The job must be in completed status. The returned URL can be dropped directly into a transcode's subtitles='<url>' filter to burn captions into a video.
Inputs: id (required).
list_transcodesList TranscodesAInspect
List transcode jobs for the authenticated account, with optional filters for status and time range. Paginated (default page 1, limit 20).
Inputs: page, limit, since, until, +1 more.
request_upload_urlRequest Upload URLAInspect
Step 1 of the direct-upload flow. Returns a short-lived presigned HTTPS URL that the caller PUTs the file bytes to (with the same Content-Type that was passed in). After the PUT succeeds, call confirm_upload with the same filename and fileSize to receive the final gs:// URL for use as a transcode/transcribe input. The presigned URL expires in ~15 minutes.
Inputs: fileSize (required), filename (required), contentType (required).
transcode_and_waitTranscode and WaitAInspect
One-shot convenience tool: creates a transcode job, polls until it reaches a terminal state (completed/failed/cancelled) or the timeout expires, and returns the final job plus a signed download URL if completed. Use this when you want the full transcode in one step without managing polling yourself.
Inputs: inputs (required), outputFormat (required), preset, options, +2 more.
transcode_videoTranscode VideoAInspect
Create a video transcode job on FFmpeg Micro. Accepts one or more input videos (gs:// or https://) and an output format. Returns immediately with a queued job — use get_transcode, list_transcodes, or transcode_and_wait to follow progress.
Inputs: inputs (required), outputFormat (required), preset, options.
transcribe_audioTranscribe AudioAInspect
Generate an SRT subtitle file from an audio or video URL using Whisper. Returns a queued job envelope immediately — poll with get_transcribe until status is completed, then fetch the signed SRT URL with get_transcribe_download. The SRT URL can be dropped directly into a transcode's subtitles='<url>' filter to burn captions into a video.
Inputs: media_url (required), task, language.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
TDQS
Scored across 11 tools
Most tools have distinct purposes: transcode vs transcribe vs upload. The overlap between 'transcode_video' and 'transcode_and_wait' is clarified by description, and 'get_download_url' vs 'get_transcribe_download' are distinguished by resource type. Minor potential confusion between 'get_transcode' and 'get_transcribe' but their names indicate the difference.
Naming patterns are mixed: 'transcode_video', 'get_transcode', 'list_transcodes', 'cancel_transcode' follow verb_noun, but 'transcode_and_wait' uses a conjunction, and 'get_transcribe_download' breaks the pattern. Also 'request_upload_url' and 'confirm_upload' are not prefixed with 'upload'. Overall inconsistent.
11 tools is appropriate for the scope: covers transcode creation, monitoring, cancellation, convenience wrapper; transcribe creation and download; upload flow; and download URL generation. Neither too few nor too many for a focused MCP server.
The tool set covers core transcode and transcribe workflows but lacks listing and cancellation for transcribe jobs, while these exist for transcode. There is also no delete or update for completed jobs. Minor gaps that could hinder workflows.
Maintenance
Related MCP Connectors
FFmpeg as a service for AI agents: typed video editing tools, async jobs, downloadable outputs.
FFmpeg Micro - The FFmpeg Api - www.ffmpeg-micro.com Transcode videos from n8n or Make using FFmpeg in the cloud. MCP Server: https://github.com/ffmpeg-micro/ffmpeg-micro-mcp/
Video, audio, and image processing for AI agents: convert, transcribe, upscale - 150+ operations.
Transform video, audio and images, and generate media from prompts. FFmpeg, captions, models.
Related MCP Servers
- AlicenseBqualityDmaintenanceProvides powerful video and audio editing capabilities through FFmpeg, enabling AI assistants to perform professional-grade operations including format conversion, trimming, overlays, transitions, and advanced audio processing.2789MIT
- AlicenseBqualityBmaintenanceProvides video and audio manipulation tools powered by FFmpeg, enabling AI assistants to perform media operations such as cutting, converting, and removing silence.6169 npm2MIT

Transcodelyofficial
AlicenseAqualityBmaintenanceTranscodely is agent-native video infrastructure: transcode, host, and get a playable link back from one natural-language prompt. Connect with one OAuth click, then create a job, poll status, and hand back a durable player URL - 7 tools, including AI-generated captions.7MIT- AlicenseBqualityCmaintenanceEnables AI agents to programmatically edit videos via FFmpeg, providing tools for probing, trimming, concatenating, timeline composition, LUT generation/application, rendering, and edit verification over MCP.162MIT