The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Seerr MCP Server listing page.
Search Jellyseerr/Overseerr, check availability, and create guarded media requests via MCP. STDIO or Streamable HTTP.
A workflow MCP server over a self-hosted Jellyseerr / Overseerr instance — the request layer that fronts Jellyfin/Plex/Emby plus Radarr and Sonarr. The unit of work is not "download a movie"; it is search → resolve the exact TMDB-backed title → check availability and request state → create a guarded request that Radarr/Sonarr act on. Jellyseerr owns permissions, quotas, routing, and status; this server never touches Radarr/Sonarr directly.
Two properties make it safe to hand an agent:
seerr_request_media) defaults to mode: preview — it resolves the title and returns the exact payload that would be submitted without writing anything. The real request fires only on mode: request, which asks for an explicit confirmation and submits only once that confirmation comes back accepted.src/services/seerr/normalizers.ts) that allow-lists the fields it emits, so operator email, Plex/Jellyfin tokens and media-server IDs, and internal serviceUrl hosts never reach the model — requesters are projected to { id, displayName }. Redaction is always on; filesystem paths are the single opt-in, behind includePaths.Six tools covering the request workflow — discover (search) → confirm (get) → understand routing (service_options) → request (request_media) → track (request_status / list_requests):
| Tool | Description |
|---|---|
seerr_search_media | Search movies and TV by title; returns ranked matches with TMDB ID, year, overview, and decoded availability when Jellyseerr already tracks the title. The required first step before requesting. |
seerr_get_media | Fetch exact movie/show details by TMDB ID + media type to confirm the title before a write; for TV, a per-season summary or one season's episode list. |
seerr_list_requests | List recent requests with status/type/requester filters; echoes the applied filters and decodes every numeric status. Titles are opt-in via includeTitles. |
seerr_request_media | Guarded write. Previews the request payload by default (mode: preview); creates the request only on mode: request, and only after an accepted confirmation. |
seerr_request_status | Fetch one request by ID — title, decoded request + media availability (incl. 4K), requester, routing summary, and a state-tuned next-step hint. |
seerr_service_options | Summarize configured Radarr/Sonarr services, default quality profiles, and instance capability flags (4K, partial requests, specials, media server). Filesystem paths redacted unless includePaths. |
Every status field is decoded to { raw, label } — both the numeric code Jellyseerr returns and a human label — so an agent never has to hardcode the enum mapping.
seerr_search_mediaTitle disambiguation entry point. Wraps GET /search, filters to movies and TV (people are always excluded), and decodes availability when the title is tracked.
movie / tv / all media-type filterstatus, plus status4k when 4K is enabled) for tracked titles onlylimit to cap output sizelanguage for localized titles/overviews[] with a guidance notice, not an errorseerr_get_mediaConfirm the exact title before a write. Wraps GET /movie/{id} or GET /tv/{id}, optionally a season's episodes.
seasonNumber for a per-season summary, or pass one to fetch that season's episode list (season 0 is Specials)media_not_found with a search-recovery hint (Jellyseerr's raw HTTP 500 is classified in the service layer)seerr_list_requestsReview recent requests and their lifecycle. Wraps GET /request.
filter (pending, processing, available, failed, …), mediaType, and requestedById filtersadded) or last-changed (modified), ascending or descendingtake / skip pagination; the enrichment trailer echoes the filter set the server applied{ id, displayName }includeTitles: true joins them from the media records — one lookup per distinct title on the page, so a 4K and a non-4K request for one film cost one call. Default is off, keeping the call to a single upstream read. Rows that can't be resolved keep every other field and are disclosed in the noticeseerr_request_mediaThe only mutation in the surface, and it is triple-guarded:
mode: preview (default) resolves the title and returns the exact POST /request payload that would be submitted — no write. A sloppy call shows the payload and changes nothing.mode: request asks for an explicit confirmation and submits only once it comes back accepted. Declining, cancelling, or answering with anything the confirmation schema rejects cancels before submission (request_cancelled) — there is no branch that proceeds without an acceptance, so a client that never answers leaves the write un-run.destructiveHint: true surfaces the risk in client-side approval flows.Over HTTP this needs MCP_SESSION_MODE to resolve to stateful (the default auto does): 2025-era clients answer the confirmation over a live session, which stateless has no way to hold open.
seasons: "all" or an explicit list (e.g. [1, 2]); Specials are excluded unless the instance enables themserverId, profileId, rootFolder, languageProfileId) — omit to use Jellyseerr's defaults (recommended)duplicate_request pointing back at itseerr_service_optionsLets an agent reason about request capability and routing without a separate status tool. Fans out service + settings + version reads with Promise.allSettled, so one failed leg degrades to a disclosed notice rather than failing the call.
movie4kEnabled / series4kEnabled / partialRequestsEnabled / specialEpisodesEnabled flagsincludePaths: true| Type | Name | Description |
|---|---|---|
| Resource | seerr://request/{requestId} | Read-once summary of one request — title, decoded status + media availability + routing. Mirrors seerr_request_status. |
All request data is also reachable via tools — request enumeration is the job of seerr_list_requests (filterable, the tool-only access path), so the collection is intentionally not exposed as a resource. There are no prompts; the guarded-write workflow lives in the tool, not a prompt template.
Built on @cyanheads/mcp-ts-core:
none, jwt, oauthin-memory, filesystem, Supabase, Cloudflare KV/R2/D1Jellyseerr-specific:
{ raw, label } everywhere, forward-compatible with new Jellyseerr status codesAgent-friendly output:
media_not_found, request_not_found, seasons_required, four_k_not_enabled, duplicate_request, and more carry a recovery hint so callers can branch and retry without parsing proseThis server connects to your own Jellyseerr/Overseerr instance — there is no public hosted endpoint. Add the following to your MCP client configuration file, pointing SEERR_BASE_URL at your instance and supplying its API key.
Or with npx (no Bun required):
For Streamable HTTP, set the transport and start the server:
All configuration is validated at startup via Zod schemas in src/config/server-config.ts. Key environment variables:
| Variable | Description | Default |
|---|---|---|
SEERR_BASE_URL | Required. Base URL of the Jellyseerr/Overseerr instance, e.g. http://localhost:5055. The service appends /api/v1 — no /api/v1 suffix, no trailing slash. | — |
SEERR_API_KEY | Required. Jellyseerr API key (Settings → General → API Key). Sent as the X-Api-Key header. | — |
SEERR_REQUEST_TIMEOUT_MS | Per-request HTTP timeout in milliseconds. | 15000 |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | Port for the HTTP server. | 3010 |
MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (RFC 5424). | info |
LOGS_DIR | Directory for log files (Node.js only). | <project-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend. | in-memory |
OTEL_ENABLED | Enable OpenTelemetry instrumentation. | false |
See .env.example for the full list of optional overrides.
Build and run:
Run checks and tests:
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/seerr-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers the six tools + one resource and inits the Seerr service. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). |
src/mcp-server/resources | Resource definitions (*.resource.ts). |
src/services/seerr | Jellyseerr API client, status decoders, and the PII/infra redaction normalizers. |
tests/ | Unit and integration tests mirroring src/. |
See CLAUDE.md/AGENTS.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for request-scoped logging, ctx.state for tenant-scoped storagesrc/mcp-server/*/definitions/index.tsIssues and pull requests are welcome. Run checks and tests before submitting:
Apache-2.0 — see LICENSE for details.