Claude Code + Transcodely: An Encoding Pipeline You Can Talk To
A walkthrough for wiring Transcodely's MCP server into Claude Code: one registration command, a browser authorize, and four workflows the shipped tools actually support — host, transcode, caption, audit spend — plus the limits worth knowing before you hand an agent a billable API.
Connect Transcodely’s MCP server to Claude Code and you can run encoding work from the same terminal session where you are writing the code that consumes it — submit jobs, poll them, caption hosted videos, and check what the month has cost, all by asking.
This is the walkthrough. Every command and payload below is copy-pasteable and matches the tools as they are deployed today. Where something does not work yet, I say so rather than leaving you to find out.
Connecting the server
Register it, then authenticate. No key anywhere in this flow:
claude mcp add --transport http transcodely https://mcp.transcodely.com/mcpThen run /mcp inside Claude Code, choose transcodely, and pick Authenticate. Your browser opens, you approve the consent screen, and the connection is live. Claude Code stores the token and refreshes it for you.
Verify with /mcp again — you should see transcodely connected with seven tools.
To share the server with a repository, commit a .mcp.json at the project root:
{
"mcpServers": {
"transcodely": {
"type": "http",
"url": "https://mcp.transcodely.com/mcp"
}
}
}That file carries no credential, so it is safe to commit. Each teammate authorizes with their own account the first time they use it, and every call runs as them.
Picking which app the agent works in
Everything in Transcodely is scoped to an app — jobs, videos, origins, usage. The bare URL resolves to your organization’s default app, which is what most people want. If you belong to more than one organization, the bare URL cannot pick for you and returns an error listing the per-app URLs to use instead.
Pin an agent to a specific app by registering that app’s URL:
claude mcp add --transport http transcodely-prod https://mcp.transcodely.com/mcp/app_k1l2m3n4o5Register the same server twice under different names — transcodely-prod and transcodely-staging, each on its own app_… URL — and the agent has to name which one it is touching. That is the cheapest guard against a staging prompt hitting production.
Headless and CI
An agent with no browser cannot complete the authorize flow. Use an app-scoped API key as a bearer token instead:
claude mcp add --transport http transcodely https://mcp.transcodely.com/mcp \
--header "Authorization: Bearer ak_..."Treat any file carrying that key like a secret — prefer an environment reference or a per-developer key over committing a live ak_…. This is precisely why the browser flow is the default: there is no key to leak. A key presented at another app’s URL is rejected, so an ak_… scoped to one app cannot act on another.
Workflow 1: host a video from a URL
The shortest useful path. You have a file on a public URL and you want it transcoded, stored, CDN-delivered, and playable without touching a bucket.
Transcode and host
https://example.com/talk.mp4, title it “Q3 all-hands”, and give me a link.
Claude calls create_video_from_url:
{
"url": "https://example.com/talk.mp4",
"title": "Q3 all-hands"
}It returns immediately with status: "processing", an empty renditions array, and no playback block — the encode has not run yet. Claude polls get_video until the status flips to ready, then hands back playback.player_url: a durable player page you can paste into Slack. It does not expire.
The same block carries hls_url for embedding in your own player, qualified by expires_at. That expiry applies to hls_url only — it never applies to player_url. Fetch a fresh manifest through get_video rather than storing one.
visibility accepts public, unlisted, or private. Omit it and the video inherits your app’s default, which is unlisted unless it has been configured otherwise — that is what makes the link durable. Ask for private and you get the signed hls_url only: the player page does not serve private videos, so no player_url comes back. You can also pass description, tags, and preset — the last takes a preset ID (pst_…) or slug, which is how you pin an ingest to a recipe you already built in the dashboard.
The URL must be publicly reachable. Private and internal addresses are rejected as SSRF protection, so pointing this at http://localhost:8080/video.mp4 or an internal 10.x host fails by design.
Workflow 2: transcode into your own bucket
If you already own storage and a CDN, you want create_job instead — it writes renditions to a storage origin you control and does not put anything in managed hosting.
Transcode
s3://raw-media/interview.movto 1080p and 720p H.264 MP4, output to originori_x9y8z7w6v5.
{
"input_url": "s3://raw-media/interview.mov",
"output_origin_id": "ori_x9y8z7w6v5",
"outputs": [
{ "format": "mp4", "video_codec": "h264", "resolution": "1080p" },
{ "format": "mp4", "video_codec": "h264", "resolution": "720p" }
]
}Worth knowing about the shape of this call:
input_urltakesgs://,s3://, orhttps://.outputsholds 1–10 renditions. Each takesformat(mp4,webm,mkv,mov,hls,dash,adaptive),video_codec(h264default, orh265,vp9,av1),resolution, andquality_tier(economy,standarddefault,premium).output_origin_idis required unless the job targets managed hosting. Leave it off a normal job and it is rejected withoutput_origin_id required— a clear enough error that the agent usually fixes it on the next turn without you intervening.presetcan be set job-wide and overridden per output. Per-output fields win over the preset.webhook_urlandmetadatawork exactly as they do over REST.
Codec-by-container compatibility is validated server-side and comes back as a machine-readable error, so asking for something incoherent like AV1 in a MOV container fails fast with a reason the model can act on rather than producing a broken file.
Then poll it:
What’s the status of that job?
get_job_status returns overall status and progress, per-output status and progress, and any error code and message. It is deliberately lighter than the full job record because polling is what agents do most.
Workflow 3: caption a hosted video
Generate English captions for
vid_a1b2c3d4e5f6.
{
"video_id": "vid_a1b2c3d4e5f6",
"language": "eng"
}language is an ISO 639-2 code — eng, spa, bul. Omit it and it defaults to auto, which detects the spoken language. Speech is transcribed into a WebVTT track and attached to the video when the job finishes.
This is the one tool with proper retry protection. The idempotency key covers app + video + language, so if the agent retries — and agents retry silently — you get the original job back rather than a second billable one. Ask for English captions on the same video five times and you are charged once.
Captions bill per source minute, and the fee is only charged when a track is actually delivered. Current rates are on the pricing page.
Workflow 4: triage failures and check spend
The two read-only tools that earn their place in a terminal session.
Show me my failed jobs from this month.
{ "status": "failed", "limit": 20 }list_jobs returns compact rows — ID, status, progress, input URL, priority, estimated and actual cost, timestamps — newest first, with a next_cursor to page through. status filters on pending, probing, processing, completed, failed, canceled, partial, or awaiting_confirmation. limit runs 1–100 and defaults to 20. From there, get_job_status on any failed ID gives you the per-output error codes.
How much have I spent on video this month, and which day cost the most?
{ "month": "2026-08" }get_usage answers both from one call: videos encoded, encoding minutes, average storage, egress, request counts, and per-line and total cost in EUR, plus a per-day breakdown the agent can sort. Omit month for the current UTC month.
Limits worth knowing before you wire this up
The agent cannot undo anything. There is no delete, cancel, or update tool. Nothing over MCP removes or modifies data that already exists — three tools create work, four read. This is deliberate, but it cuts both ways: if Claude submits a job you did not want, you cancel it from the dashboard, not from the conversation.
Creates are not all idempotent. Only generate_captions is. A retried create_job or create_video_from_url is a new job and a new charge. If you are scripting an unattended loop, put the guard on your side.
No local file upload. create_video_from_url fetches a public URL and nothing else. Files on your laptop go through the dashboard or the upload API.
Auto ladders build downward from your app’s ceiling. Hand it a 720p source with a 1080p ceiling configured and you get an upscaled 1080p rung. The encoder is not recovering detail that was never in the file — check the source resolution before reading too much into the top rung.
Every call is billable at the same rate as REST. Going through an agent costs no more, but it also costs no less, and an agent iterating on a prompt can submit several jobs quickly. Each call writes an audit row with scrubbed arguments and its error code, so there is a record of what ran.
Revocation has a short tail. Removing the connector, or removing a member from the organization, stops new calls — membership is re-checked on every tool call. But a token already issued stays valid until it expires, roughly ten minutes worst case. For a hard cutoff, remove the member and treat that as the outer bound.
Seven tools is a deliberate floor, not a ceiling I hit. FFmpeg Micro’s MCP server exposes fifteen, including cancel, upload, and one-shot “run and wait” variants, and it authenticates with OAuth the same way. If your work is raw FFmpeg options and filter graphs rather than managed ladders and origins, that is the better tool.
Where to go from there
The MCP server page is the product overview with per-client connect steps, and the connect guide is the reference version of the setup above, including Cursor and claude.ai. If you want the reasoning behind which tools exist and which ones I refused to build, that is in the announcement post. For what happens after a video is hosted — storage, delivery, embed codes — start with Video Hosting.
As always, if you hit something weird, email me directly — still a one-person company, still the guy who wrote the code.
Happy transcoding.