Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
261 changes: 133 additions & 128 deletions mcp-server.mdx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
title: "Connect AI agents with the MCP server"
sidebarTitle: "MCP"
description: "Connect Claude Desktop, Cursor, claude.ai, or any MCP client to submit video jobs, upload files, and poll status without writing HTTP code."
description: "Connect Claude Code, claude.ai, or any MCP client to Rendobar over OAuth, then submit jobs, upload files, and chain job outputs from the chat."
icon: "plug"
keywords: ["mcp server", "model context protocol", "claude desktop mcp", "cursor mcp", "claude.ai connector", "chatgpt mcp", "ai agent video tools"]
keywords: ["mcp server", "model context protocol", "claude code mcp", "claude.ai connector", "oauth mcp server", "list_job_types", "upload_media", "mcp upload session", "ai agent video tools"]
canonical: "https://rendobar.com/docs/mcp-server"
---

Expand All @@ -15,185 +15,190 @@ canonical: "https://rendobar.com/docs/mcp-server"
"@type": "TechArticle",
"@id": "https://rendobar.com/docs/mcp-server/#article",
"headline": "Connect AI agents with the MCP server",
"description": "Connect Claude Desktop, Cursor, claude.ai, or any MCP client to submit video jobs, upload files, and poll status without writing HTTP code.",
"description": "Connect Claude Code, claude.ai, or any MCP client to Rendobar over OAuth, then submit jobs, upload files, and chain job outputs from the chat.",
"datePublished": "2026-06-22",
"dateModified": "2026-07-25",
"dateModified": "2026-07-26",
"author": { "@type": "Organization", "@id": "https://rendobar.com/#organization" },
"publisher": { "@type": "Organization", "@id": "https://rendobar.com/#organization" },
"isPartOf": { "@id": "https://rendobar.com/#website" }
})
}}
/>

`@rendobar/mcp` is a Model Context Protocol server that gives an AI agent the same operations as the REST API: submit jobs, upload local files, poll status. Connect the hosted server over OAuth with no API key, or run the local server with `npx` when the agent needs to upload files from your disk.
The MCP server at `https://api.rendobar.com/mcp` gives any MCP client 10 tools: submit a job, upload a file, poll status, and chain a completed job into the next one. Connect it with one command over OAuth, no API key to copy.

## Connect a client
## Connect

### Fastest: hosted server with OAuth

No API key needed. Add the hosted server and your browser opens once to approve access:
<Tabs>
<Tab title="Claude Code">

```bash
claude mcp add --transport http rendobar https://api.rendobar.com/mcp
```

The same URL works in claude.ai (Settings, Connectors, Add custom connector) and any client that speaks Streamable HTTP with OAuth. The hosted server cannot read files on your disk. For local file uploads, use the local server below.
Your browser opens once to approve access.

### Local server with an API key
</Tab>
<Tab title="claude.ai">

Get an API key at [app.rendobar.com](https://app.rendobar.com) under Settings, API Keys. Copy the `rb_...` value. Then add Rendobar to your client.
Settings, Connectors, Add custom connector. Paste the URL and approve in the browser prompt.

<Tabs>
<Tab title="Claude Code">
```text
https://api.rendobar.com/mcp
```

Run from a terminal:
</Tab>
<Tab title="Other MCP clients">

```bash
claude mcp add rendobar -s user --env RENDOBAR_API_KEY=rb_... -- npx -y @rendobar/mcp
Any client that speaks Streamable HTTP with OAuth 2.1 connects the same way: point it at the URL below and approve in the browser that opens.

```text
https://api.rendobar.com/mcp
```

If you already ran `rb login` with the Rendobar CLI, drop the `--env` part. The server reads the credentials file automatically.
Clients without OAuth support can authenticate with an API key instead: header `Authorization: Bearer rb_...`, from [Settings, API keys](https://app.rendobar.com) on the dashboard.

</Tab>
<Tab title="Claude Desktop">
</Tabs>

Edit the config file, then restart Claude Desktop.
## What you can do

- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
Call `list_job_types` first. It returns every active job type fresh, so it never goes stale like a hardcoded list would.

```json
{
"mcpServers": {
"rendobar": {
"command": "npx",
"args": ["-y", "@rendobar/mcp"],
"env": { "RENDOBAR_API_KEY": "rb_..." }
}
}
}
| Tool | What it does | Returns |
|---|---|---|
| `list_job_types` | Every active job type: use cases, what it chains with, a params digest, and an example payload. | `{ jobTypes[], guidance }` |
| `submit_job` | Submit a job. `inputs` accepts a URL string, `{ url }`, `{ content }` (inline text), or `{ job: "job_..." }` for chaining. | `{ jobId, status }` |
| `get_job` | Check status and results. `wait: true` blocks up to 45 seconds instead of polling. A completed video job includes an inline poster image. | `{ id, status, output?, cost?, error? }` |
| `list_jobs` | Recent jobs, filterable by `status` and `type`. | `{ jobs[], total }` |
| `probe_media` | Run ffprobe against a media URL as an async job. Poll `get_job` for the normalized summary and full report. | `{ jobId, status }` |
| `get_account` | Credit balance, plan limits, active jobs, and rate limit usage. | `{ balance, plan, limits }` |
| `cancel_job` | Cancel a job still `waiting` or `dispatched`. | `{ id, status: "cancelled" }` |
| `upload_media` | Open an upload session. Presigned URLs for shell-capable callers, or a `pageUrl` for the user to upload from their own device. | `{ session, pageUrl, files[] }` |
| `complete_upload` | Finalize an asset after its bytes land in storage. | `{ asset: { id, url, status } }` |
| `get_upload` | Check an upload session's progress. `wait: true` blocks up to 45 seconds. | `{ status, assets[] }` |

`{ job: "job_..." }` inside `submit_job`'s `inputs` chains a completed job straight into the next one today for `ffmpeg` inputs. For any other job type, pass the prior job's output URL (from `get_job`) as a plain URL input instead.

A failed call returns `isError: true` with `{ error: { code, message, retryable } }` instead of throwing, so the agent can react to it. See the [error catalogue](/support/errors) for codes.

## From your phone

A phone has no filesystem the agent can reach, so a media file routes through a page you open yourself.

<Steps>
<Step title="Connect on claude.ai mobile">
Settings, Connectors, Add custom connector, paste `https://api.rendobar.com/mcp`, approve.
</Step>
<Step title="Ask for the edit">
"Trim this video to the first 10 seconds and burn in captions." Claude calls `list_job_types`, then `upload_media` with no files declared, since it can't read anything off your phone.
</Step>
<Step title="Tap the upload link">
`upload_media` returns a `pageUrl`. Claude sends it to you in the chat. Open it, pick the file from your camera roll, and it uploads.
</Step>
<Step title="Get the result in chat">
Claude polls `get_upload({ wait: true })` until the asset is ready, calls `submit_job` with its URL, then `get_job({ wait: true })`, and replies with the finished clip.
</Step>
</Steps>

## From an agent with shell access

An agent that can run shell commands skips the upload page and PUTs bytes directly.

<Steps>
<Step title="Declare the file">
Pass the filename and size. A known size gets you a presigned URL right away: a single PUT under 100 MB, part URLs above it.

```text
upload_media({ files: [{ filename: "clip.mp4", size: 8421312 }] })
```
</Step>
<Step title="PUT the bytes">
```bash
curl -sS -X PUT --upload-file clip.mp4 "https://<presigned-url-from-upload_media>"
```
</Step>
<Step title="Finalize the asset">
```text
complete_upload({ assetId: "asset_8f2a1c" })
```
Returns the ready asset, including its stable `url`. For a multipart upload, pass `parts: [{ partNumber, etag }]` collected from each part's PUT response.
</Step>
<Step title="Submit the job">
```text
submit_job({
type: "ffmpeg",
inputs: { "clip.mp4": "https://api.rendobar.com/assets/asset_8f2a1c/content" },
params: { command: "ffmpeg -i clip.mp4 -vf scale=1280:720 -c:v libx264 -crf 23 out.mp4" }
})
```
Returns `{ jobId: "job_9c31", status: "waiting" }`.
</Step>
<Step title="Chain a second job off the first">
Pass the completed job's id as the next job's input. No download, no re-upload.

```text
submit_job({
type: "ffmpeg",
inputs: { "out.mp4": { job: "job_9c31" } },
params: { command: "ffmpeg -i out.mp4 -vf drawtext=text='done':x=10:y=10 final.mp4" }
})
```
</Step>
<Step title="Wait for the result">
```text
get_job({ jobId: "job_a71f", wait: true })
```
Blocks up to 45 seconds, then returns `output.file.url`.
</Step>
</Steps>

</Tab>
<Tab title="Cursor">
## Run the local server for direct disk access

Edit `~/.cursor/mcp.json` (global) or `<project>/.cursor/mcp.json` (project).
The hosted server can't read your disk, so even a shell-capable agent goes through the presigned-URL handshake above. `@rendobar/mcp`, the local stdio server, skips that: its `upload_file` tool reads a file off disk and uploads it in a single call.

```json
{
"mcpServers": {
"rendobar": {
"command": "npx",
"args": ["-y", "@rendobar/mcp"],
"env": { "RENDOBAR_API_KEY": "rb_..." }
}
}
}
Get an API key at [app.rendobar.com](https://app.rendobar.com), Settings, API keys.

<Tabs>
<Tab title="Claude Code">

```bash
claude mcp add rendobar -s user --env RENDOBAR_API_KEY=rb_... -- npx -y @rendobar/mcp
```

</Tab>
<Tab title="VS Code">
<Tab title="Claude Desktop">

VS Code 1.101+ uses `servers` and prompts for the key on first use.
Edit the config file, then restart Claude Desktop.

- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
"servers": {
"mcpServers": {
"rendobar": {
"command": "npx",
"args": ["-y", "@rendobar/mcp"],
"env": { "RENDOBAR_API_KEY": "${input:rendobarKey}" }
"env": { "RENDOBAR_API_KEY": "rb_..." }
}
},
"inputs": [
{ "id": "rendobarKey", "type": "promptString", "password": true, "description": "Rendobar API Key" }
]
}
}
```

</Tab>
<Tab title="Other clients">

Cline, Windsurf, Zed, and Continue use the same `npx -y @rendobar/mcp` command with the `RENDOBAR_API_KEY` env var. The wrapper differs:

- **Cline:** MCP panel, Configure, paste the Claude Desktop block.
- **Windsurf:** `~/.codeium/windsurf/mcp_config.json`, same schema as Claude Desktop.
- **Zed:** `~/.config/zed/settings.json`, top-level key is `context_servers` (not `mcpServers`) and add `"source": "custom"`.
- **Continue:** `.continue/mcpServers/rendobar.yaml` with `type: stdio`.
Cursor, Cline, Windsurf, Zed, and Continue all run the same `npx -y @rendobar/mcp` command with the `RENDOBAR_API_KEY` env var. Only the config file and the top-level key name differ (`mcpServers` for most, `context_servers` for Zed).

</Tab>
</Tabs>

To check it worked, ask your agent: "What's my Rendobar balance?" The agent calls `get_account` and reports your balance. If you see "No Rendobar API key found" in the client's MCP logs, the `env` block didn't reach the server. See [Common fixes](#common-fixes).

<Info>Needs Node 20.10 or later. The server checks at startup and exits with a clear message if it's older.</Info>

## Tools

Both servers expose the same five job tools. The local server adds `upload_file` (reads a file off disk in one call). The remote server swaps in `upload_media`, which hands you a curl command to upload first.

| Tool | Purpose | Returns |
|---|---|---|
| `submit_job` | Submit any active job type. `inputs` maps name to URL, `params` carries type-specific options. | `{ jobId, status, url? }` |
| `get_job` | Fetch one job by ID. Pass `wait: true` to block until the job finishes, up to about 50 seconds, then it returns a snapshot to call again. Shape grows with status (`progress` while running, `cost` and `output` when complete). | `{ id, type, status, output?, ... }` |
| `list_jobs` | Recent jobs, filterable by `status` and `type`. | `{ jobs[], total }` |
| `cancel_job` | Cancel a `waiting` or `dispatched` job. `CONFLICT` once it's running. | `{ id, status: "cancelled" }` |
| `get_account` | Balance, plan, and limits. No parameters. | `{ balance, plan, isPro, limits }` |
| `upload_file` | Local only. Read a file from disk, upload to ephemeral storage. | `{ downloadUrl, sizeBytes }` |
| `upload_media` | Remote only. Return an upload endpoint and curl command for the user to run. | `{ uploadEndpoint, instructions, ... }` |

On a completed job, read `output.file.url` for the file to play or download, or `output.data` for a computed answer. The `output` shape is the same for every job type. Failures come back as `isError: true` with `{ error: { code, message, retryable } }`. Common codes: `UNAUTHORIZED`, `INSUFFICIENT_CREDITS`, `RATE_LIMITED`, `VALIDATION_ERROR`, `NOT_FOUND`, `CONFLICT`, `INVALID_JOB_TYPE`.

`submit_job` enumerates current job types at server startup. `ffmpeg` is the only Live type today. See the [FFmpeg reference](/jobs/ffmpeg) for accepted commands.

## Worked example: burn captions

Hand Claude Desktop a local clip and an `.srt`, then say:

> Burn the captions in `~/Videos/clip.srt` into `~/Videos/clip.mp4` and give me the result. Use Rendobar.

The agent runs the flow in tool calls you can watch in the panel:

1. `upload_file` for the `.mp4`, then for the `.srt`. Each returns a `downloadUrl`.
2. `submit_job` with `type: "ffmpeg"`, both URLs as `inputs`, and a command using `-vf subtitles`.
3. `get_job` with `wait: true`, which returns as soon as `status: "complete"`, then `output.file.url`.

A 30-second clip renders in about 20 to 40 seconds. Open the link to download. Source and output files live on `cdn.rendobar.com` for 24 hours, then auto-delete.

## Remote server for web and mobile

Browser and phone clients can't run a local stdio process. Point them at the remote HTTP server at `https://api.rendobar.com/mcp` instead. Job submission and polling are identical. The one difference: the remote server can't read your disk, so `upload_media` returns a curl command you run yourself, then paste the `downloadUrl` back into the chat.

In **claude.ai**, open Settings, Connectors, Add custom connector. In **ChatGPT**, open Settings, Apps & Connectors, Custom. Both want:

- **URL:** `https://api.rendobar.com/mcp`
- **Authentication:** OAuth. The client opens a browser window to sign in and approve. A Bearer token with your `rb_...` key also works where the client supports it.

Any client supporting the Streamable HTTP transport works the same way. On desktop, prefer the local server. Uploads happen in one tool call instead of a manual curl round-trip.

## Common fixes

<AccordionGroup>
<Accordion title="No Rendobar API key found">

The `env` block in your client config didn't reach the server, or the key has the wrong prefix (it must start with `rb_`). Verify the config the client actually loaded. In Claude Desktop, Settings, Developer, Open MCP Log Folder shows the launch command and the env vars it received. The server reads credentials in order: `--api-key` flag, then `RENDOBAR_API_KEY`, then `~/.config/rendobar/credentials.json`.

</Accordion>
<Accordion title="Cursor or Windsurf on macOS can't find npx">

A client launched from the macOS Dock inherits the GUI environment, not your shell PATH. If you installed Node via nvm or asdf, `npx` isn't on that PATH. Use the absolute path from `which npx` as the `command`. On Windows, if `npx` isn't recognized, set `"command": "npx.cmd"`.

</Accordion>
<Accordion title="Tool calls hang or the agent sees no tools">

A hang almost always means the server crashed after the handshake. Read stderr in your client's MCP log (Claude Desktop: `mcp-server-rendobar.log`; Cursor and VS Code: Output panel, MCP) and look for `"level": "error"`. If the handshake succeeded but `tools/list` is empty, the package is likely corrupt: run `npm cache clean --force` then `npx -y @rendobar/mcp@latest --version`.

</Accordion>
</AccordionGroup>

## See also

- [FFmpeg reference](/jobs/ffmpeg): the main job type `submit_job` calls
- [Authentication](/quickstart): the API key model for both transports
- [Errors](/support/errors): full code list behind `isError` responses
- [Plan limits](/support/limits): file-size caps and concurrency by plan
- [FFmpeg reference](/jobs/ffmpeg): the job type `submit_job` calls in every example on this page
- [How a job works](/concepts/job): the job lifecycle and the `output` shape
- [Error codes](/support/errors): every code behind an `isError` response
- [Plan limits](/support/limits): file size caps and concurrency by plan
Loading