Add an FFmpeg Tool to Your AI Agent (Claude Agent SDK, OpenAI Agents SDK, LangChain)
Give an AI agent an FFmpeg tool in a few lines: connect the Xora MCP server from the Claude Agent SDK, the OpenAI Agents SDK or LangChain, with code for each.
To add an FFmpeg tool to an AI agent, connect it to an MCP server that runs FFmpeg. With Xora, the server is https://api.xora.sh/v1/mcp with Authorization: Bearer YOUR_API_KEY, and the agent gets five tools: create_job, get_job, create_upload, list_recipes and cancel_job.
Below is the code for the Claude Agent SDK, the OpenAI Agents SDK and LangChain, a REST option for frameworks without MCP, and the instructions that make agents use the tools well. For clients such as Claude Code and Cursor, see FFmpeg for AI agents.
What the agent gets
| Tool | What it does |
|---|---|
list_recipes | Lists the recipes (compress, trim, thumbnail, webReady and others) and their parameters. |
create_job | Starts a job: a recipe, raw FFmpeg arguments, or a probe. Returns { id, state } at once. |
get_job | Returns the state, the progress, and when complete, a signed URL for each output. |
create_upload | Gives a presigned URL for a local file that has no URL. |
cancel_job | Stops a job that is not finished. |
Raw FFmpeg arguments go as a JSON array, so there is no shell, and -filter_complex, multiple inputs and codec flags pass through as written.
Claude Agent SDK
Pass the server in mcpServers with type: "http". Tools are named mcp__<server>__<tool>, so mcp__xora__* allows all Xora tools.
import { query } from '@anthropic-ai/claude-agent-sdk'
for await (const message of query({ prompt: 'Make a 720p MP4 of https://example.com/talk.mov and give me the link.', options: { mcpServers: { xora: { type: 'http', url: 'https://api.xora.sh/v1/mcp', headers: { Authorization: `Bearer ${process.env.XORA_API_KEY}` }, }, }, allowedTools: ['mcp__xora__*'], },})) { if (message.type === 'result' && message.subtype === 'success') console.log(message.result)}import asyncio, osfrom claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
options = ClaudeAgentOptions( mcp_servers={ "xora": { "type": "http", "url": "https://api.xora.sh/v1/mcp", "headers": {"Authorization": f"Bearer {os.environ['XORA_API_KEY']}"}, } }, allowed_tools=["mcp__xora__*"],)
async def main(): async for message in query( prompt="Make a 720p MP4 of https://example.com/talk.mov and give me the link.", options=options, ): if isinstance(message, ResultMessage): print(message.result)
asyncio.run(main())Source: Claude Agent SDK MCP docs.
OpenAI Agents SDK
Use MCPServerStreamableHttp as an async context manager and attach it to the agent with mcp_servers.
import asyncio, osfrom agents import Agent, Runnerfrom agents.mcp import MCPServerStreamableHttp
async def main() -> None: async with MCPServerStreamableHttp( name="xora", params={ "url": "https://api.xora.sh/v1/mcp", "headers": {"Authorization": f"Bearer {os.environ['XORA_API_KEY']}"}, }, cache_tools_list=True, ) as xora: agent = Agent( name="Video assistant", instructions="Use the Xora tools to process video. Poll get_job until the job ends.", mcp_servers=[xora], ) result = await Runner.run(agent, "Make a 720p MP4 of https://example.com/talk.mov and give me the link.") print(result.final_output)
asyncio.run(main())Source: OpenAI Agents SDK MCP docs.
LangChain
Current LangChain versions include MCP support in langchain[mcp] (beta). A URL target uses Streamable HTTP, and a string in auth is sent as a bearer token:
import asyncio, osfrom fastmcp.client import Clientfrom langchain.agents import create_agentfrom langchain.mcp import MCPAdapter
async def main(): client = Client("https://api.xora.sh/v1/mcp", auth=os.environ["XORA_API_KEY"]) async with MCPAdapter(client) as adapter: tools = await adapter.list_tools() agent = create_agent("anthropic:claude-sonnet-5-5", tools) result = await agent.ainvoke( {"messages": [{"role": "user", "content": "Make a 720p MP4 of https://example.com/talk.mov and give me the link."}]} ) print(result["messages"][-1].content)
asyncio.run(main())On older projects that still use langchain-mcp-adapters, the same server goes in MultiServerMCPClient with "transport": "http", the URL and a headers dict, and await client.get_tools() returns the tools. That package is now archived. Source: LangChain MCP docs.
Without MCP: a REST function tool
Any framework that can call a function can use the REST API. The tool starts the job, polls it, and returns the URL:
import os, time, requests
API = "https://api.xora.sh/v1"HEADERS = {"Authorization": f"Bearer {os.environ['XORA_API_KEY']}"}
def run_ffmpeg(input_url: str, args: list[str], output_name: str) -> dict: """Run FFmpeg args on input_url. Use {{in_1}} for the input and {{out_1}} for the output.""" job = requests.post(f"{API}/jobs", headers=HEADERS, json={ "mode": "ffmpeg", "input_files": {"in_1": input_url}, "output_files": {"out_1": output_name}, "ffmpeg": {"args": args}, }).json() while True: status = requests.get(f"{API}/jobs/{job['id']}", headers=HEADERS).json() if status["state"] in ("completed", "failed", "rejected", "cancelled"): return status time.sleep(3)Register run_ffmpeg with your framework’s tool decorator. The OpenAPI spec and /llms.txt describe every field.
Instructions that help
Add a few lines to the agent’s instructions:
You can process video and audio with the Xora tools.- Before you write an FFmpeg command for a file you have not seen, run create_job with mode "probe".- Prefer a recipe (call list_recipes once) when one fits. Use mode "ffmpeg" with an args array for anything else.- Output file names use letters, numbers and underscores, plus an extension.- Send an idempotencyKey with every create_job, and reuse it if you retry the same job.- Poll get_job until the state is completed, failed, rejected or cancelled.- If a job fails, read error.code and error.message, fix the request, and try again once.More on why this works: LLMs write good FFmpeg commands.
Cost and limits
An agent’s job is a normal Xora job. It bills credits for machine time and output size, with the same plan limits as a REST request, and failed, rejected and cancelled jobs cost nothing. Give the agent its own API key, so you can see and limit what it uses. See pricing.
Next steps
- FFmpeg for AI agents: MCP server, REST API and agent skill.
- FFmpeg in AI agent sandboxes: why the sandbox is the wrong place for video jobs.
- MCP server guide: setup for every client.
Frequently asked questions
How do I give an AI agent an FFmpeg tool?
Connect a remote MCP server that runs FFmpeg, such as Xora at https://api.xora.sh/v1/mcp with an API key in the Authorization header. The agent then gets tools such as create_job and get_job, and the FFmpeg commands run in the cloud, not in the agent's process.
Which agent frameworks can use the Xora MCP server?
Any framework with an MCP client that supports Streamable HTTP. This guide shows the Claude Agent SDK (TypeScript and Python), the OpenAI Agents SDK (Python) and LangChain (Python). Frameworks without MCP can call the REST API from a normal function tool.
Why not run FFmpeg inside the agent's own process?
Video jobs need CPU, disk and time, and an agent's host or sandbox often has little of each. FFmpeg may not be installed, and running model-written commands through a shell is a security risk. A separate service takes an argument array, runs it in isolation and returns a URL.
How does the agent know when the job is done?
create_job returns at once with a job ID and the state queued. The agent calls get_job until the state is completed, failed, rejected or cancelled, then gives the user the signed URL of the output.