seerr-mcp
by Neoo-Blue
README.md
# seerr-mcp
A remote MCP server that lets an AI assistant search your media catalog and add
movies and shows to [Overseerr](https://overseerr.dev) or
[Jellyseerr](https://github.com/fallenbagel/jellyseerr). Ask for a title in
plain language, the assistant finds it, confirms the match, and requests it.
Radarr and Sonarr take it from there.
It runs as a single Cloudflare Worker with no database and no build step. Any
MCP client that speaks Streamable HTTP can connect: Claude Code, Claude Desktop,
a hosted voice agent that answers a phone number, or your own code.
```
MCP client -> POST /mcp on your Worker (bearer gated by MCP_TOKEN)
-> Overseerr / Jellyseerr (/api/v1, X-Api-Key)
-> Radarr / Sonarr add
```
## Tools
| Tool | What it does |
|---|---|
| `search_media` | Search the catalog. Returns up to five matches with title, year, type, current availability, and a tmdb_id |
| `request_media` | Add a confirmed movie or show. TV defaults to all seasons. Approved automatically unless you turn that off |
| `check_status` | Report whether a title is already available, downloading, or already requested |
## What you need
* A running Overseerr or Jellyseerr that this Worker can reach over the public
internet, plus an admin api key from Settings, General, API Key.
* A Cloudflare account. The free plan is enough.
* Node 18 or newer, for `npx wrangler`.
The api key should belong to an admin. Requests then approve themselves, and the
availability lookups that stop duplicate requests are allowed to run.
## Setup
```bash
git clone https://github.com/Neoo-Blue/seerr-mcp.git
cd seerr-mcp
npm install
```
Generate a bearer token. This is the only thing standing between the open
internet and write access to your media stack, so make it a real random value
and keep it out of shell history you share:
```bash
openssl rand -hex 24
```
Set the three secrets. Wrangler prompts for each value and never writes it to
disk:
```bash
npx wrangler secret put MCP_TOKEN # the value you just generated
npx wrangler secret put SEERR_URL # e.g. https://seerr.example.com
npx wrangler secret put SEERR_APIKEY # Seerr Settings, General, API Key
```
Deploy:
```bash
npx wrangler deploy
```
You get back a url like `https://seerr-mcp.your-subdomain.workers.dev`. That is
already public https, so it works as is. To serve it from your own hostname
instead, uncomment the `routes` block in `wrangler.jsonc` and set your zone.
## Check it works
```bash
export SEERR_MCP=https://seerr-mcp.your-subdomain.workers.dev
export MCP_TOKEN=the-token-you-generated
# health, no auth needed
curl $SEERR_MCP/
# list the tools
curl -s -X POST $SEERR_MCP/mcp \
-H "Authorization: Bearer $MCP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# search the catalog
curl -s -X POST $SEERR_MCP/mcp \
-H "Authorization: Bearer $MCP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"search_media","arguments":{"query":"Dune"}}}'
```
A wrong token returns `unauthorized` with a 401. If `tools/list` works but
`search_media` says it could not reach the library, the Worker cannot see your
`SEERR_URL` or the api key is wrong.
## Connect a client
### Claude Code
```bash
claude mcp add --transport http seerr https://seerr-mcp.your-subdomain.workers.dev/mcp \
--header "Authorization: Bearer YOUR_MCP_TOKEN"
```
Then just ask: "add Dune to the library" or "is The Bear available yet".
### Claude Desktop and other MCP clients
Add a remote MCP server with the url `https://your-worker/mcp` and an
`Authorization: Bearer YOUR_MCP_TOKEN` header. Clients that only speak stdio can
reach it through [mcp-remote](https://www.npmjs.com/package/mcp-remote):
```json
{
"mcpServers": {
"seerr": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://seerr-mcp.your-subdomain.workers.dev/mcp",
"--header", "Authorization: Bearer YOUR_MCP_TOKEN"
]
}
}
}
```
### A voice agent on a phone number
The same three tools turn this into a phone line people can call to add a movie.
See [docs/voice-agent.md](docs/voice-agent.md) for the wiring and a ready made
concierge prompt.
## Local development
```bash
cp .dev.vars.example .dev.vars # fill in the three values
npm run dev
```
`.dev.vars` is gitignored. Point your curl calls at `http://localhost:8787`.
## Configuration
| Name | Where | Meaning |
|---|---|---|
| `MCP_TOKEN` | secret | Bearer every client must present on `/mcp`. Required. With none set, the endpoint rejects everything |
| `SEERR_URL` | secret | Base url of your Overseerr or Jellyseerr, no trailing path |
| `SEERR_APIKEY` | secret | Admin api key, sent as `X-Api-Key` |
| `SEERR_AUTOAPPROVE` | var in `wrangler.jsonc` | `1` approves requests immediately. `0` leaves them pending for review in Seerr |
## Notes on behaviour
* Overseerr accepts repeat requests for titles that are already available or
already downloading, with no error, which quietly creates duplicate rows.
`request_media` therefore checks real availability first and only posts a
request when the title is genuinely absent. If it is already there, it says so
instead of pretending it started a download.
* `search_media` returns the tmdb_id in its text so the model can pass it to
`request_media`, and the tool description tells the model never to read that
number out loud. Worth keeping if you edit the prompts.
* The Worker never exposes your Seerr UI. It only calls `/api/v1` server side,
so `SEERR_URL` and the api key stay on Cloudflare and never reach the client.
## Security
Anyone holding `MCP_TOKEN` can add anything to your library. Treat it like a
password: rotate it with `npx wrangler secret put MCP_TOKEN` if it ever lands in
a chat log, a screenshot, or a repo. Set `SEERR_AUTOAPPROVE` to `0` if you would
rather approve each request by hand.
## License
MIT. See [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues