npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cyanheads/seerr-mcp-server

v0.2.0

Published

Search Jellyseerr/Overseerr, check availability, and create guarded media requests via MCP. STDIO or Streamable HTTP.

Readme

Version License MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework


Overview

Request movies and shows through Jellyseerr or Overseerr. Search titles, confirm the exact match, check availability and request status, and submit a request for Radarr or Sonarr to fulfill. Your Seerr instance keeps control of permissions, quotas, and routing.

Tools

| 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. |

Resources

| Resource | Description | |:---|:---| | seerr://request/{requestId} | Read-once summary of one request — decoded status, media availability, requester, and routing. Mirrors seerr_request_status. |

All request data is also reachable via tools — request enumeration is the job of seerr_list_requests (the tool-only access path).

Skills

| Skill | Description | |:---|:---| | seerr-guide | Gates for reading availability and request state (regular vs 4K tracks, what openRequest reports), requesting seasons and Specials, who owns server-made requests, the confirmation round, and what a refused submit means. Served as skill://seerr-guide/SKILL.md. |

Capability reference

seerr_search_media tool

  • Search titles with mediaType: movie | tv | all; people are excluded. page is 1–1000; limit is 1–20, default 10.
  • Returns TMDB matches and decoded status / status4k for tracked titles. Empty results return [] with a guidance notice.
  • Optional ISO 639-1 language selects localized titles and overviews.

seerr_get_media tool

  • Availability plus any existing open request for the title (avoids duplicate requests)
  • TV: omit seasonNumber for a per-season summary, or pass one to fetch that season's episode list (season 0 is Specials)
  • A TMDB ID that doesn't resolve surfaces as a typed media_not_found with a search-recovery hint (Jellyseerr's raw HTTP 500 is classified in the service layer)

seerr_list_requests tool

  • Filter by lifecycle filter, mediaType, and requestedById; sort by added or modified, ascending or descending. take is 1–100, default 20; skip selects the offset.
  • Returns decoded statuses, requester { id, displayName }, and an enrichment trailer with applied filters.
  • includeTitles: true joins media titles once per distinct title (default off); unresolved rows retain their data and are disclosed in the notice.

seerr_request_media tool

  • Guarded write. mode: preview (default) returns the resolved title and exact POST /request payload without writing. mode: request asks for confirmation bound to the same caller, title and payload; declined, cancelled or invalid answers return request_cancelled.
  • TV requests require seasons: "all" or a list such as [1, 2]; season 0 (Specials) requires special episodes to be enabled. Instance capability checks run before any POST; existing requests appear in output and duplicate rejections return duplicate_request.
  • Optional routing overrides: serverId, profileId, rootFolder, languageProfileId. Omit them to use Jellyseerr's defaults.

seerr_request_status tool

  • Accepts requestId from seerr_request_media's created.requestId or seerr_list_requests. A missing request returns request_not_found.
  • Returns decoded requestStatus, mediaStatus / mediaStatus4k, requester { id, displayName }, routing (serverId, profileName, is4k), and stateGuidance.
  • Joins title on each call; it is absent when no tmdbId exists or the lookup fails.

seerr_service_options tool

  • Returns instance version, media server, capability flags (movie4kEnabled, series4kEnabled, partialRequestsEnabled, specialEpisodesEnabled), and service IDs, defaults and quality profiles.
  • Failed service legs retain the available results with a notice.
  • includePaths: true exposes root-folder paths and free space; both are omitted by default.

seerr://request/{requestId} resource

  • Accepts requestId from seerr_request_media or seerr_list_requests; a missing request returns request_not_found.
  • Mirrors seerr_request_status, including redaction and a title join. title is absent when no tmdbId exists or the lookup fails; resources take no per-read options.

Features

Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.

HTTP requires stateful sessions for 2025-era confirmation rounds; explicit MCP_SESSION_MODE=stateless fails startup. Consent records expire after ten minutes and changed or replayed records ask again. Redemption excludes concurrent retries in one process; request creation makes one POST attempt without automatic retries. Cross-instance read-and-delete is not atomic: use shared strongly consistent filesystem, Supabase or Cloudflare D1 storage for routed retries, and account for duplicate-submission races; Cloudflare KV is unsuitable for consent records.

Seerr-specific:

  • Read + guarded-request only — admin-scope endpoints (approve/decline, retry, edit/delete, media/file deletion, user/settings/sync) are excluded by design, not by API limitation
  • PII/infra redaction centralized in one normalizer choke point — requesters project to { id, displayName }; operator email, Plex/Jellyfin tokens, and serviceUrl never reach output; filesystem paths are opt-in via includePaths
  • Status decoding centralized in one helper — request and media statuses (including the separate 4K availability) decode to { raw, label } everywhere, forward-compatible with new Jellyseerr status codes
  • Capability validation against cached instance settings catches most bad requests before they reach the API
  • A short-TTL settings cache avoids a round-trip on every preview

Agent-friendly output:

  • Provenance and disclosure — searches echo the effective query; capped lists disclose truncation; a degraded service leg surfaces a notice instead of silently dropping data
  • Typed, actionable errors — 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 prose

Getting started

Add the following to your MCP client configuration file, pointing SEERR_BASE_URL at your own Jellyseerr or Overseerr instance and supplying its API key.

{
  "mcpServers": {
    "seerr-mcp-server": {
      "type": "stdio",
      "command": "bunx",
      "args": ["@cyanheads/seerr-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info",
        "SEERR_BASE_URL": "http://localhost:5055",
        "SEERR_API_KEY": "your-api-key"
      }
    }
  }
}

Or with npx (no Bun required):

{
  "mcpServers": {
    "seerr-mcp-server": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@cyanheads/seerr-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info",
        "SEERR_BASE_URL": "http://localhost:5055",
        "SEERR_API_KEY": "your-api-key"
      }
    }
  }
}

For Streamable HTTP, set the transport and start the server:

MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 SEERR_BASE_URL=http://localhost:5055 SEERR_API_KEY=your-api-key bun run start:http
# Server listens at http://localhost:3010/mcp

Prerequisites

  • Bun v1.4.0 or higher (or Node.js v24+).
  • A running Jellyseerr or Overseerr instance, and its API key (Settings → General → API Key).

Installation

  1. Clone the repository:
git clone https://github.com/cyanheads/seerr-mcp-server.git
  1. Navigate into the directory:
cd seerr-mcp-server
  1. Install dependencies:
bun install
  1. Configure environment:
cp .env.example .env
# edit .env — set SEERR_BASE_URL and SEERR_API_KEY

Configuration

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 | | MCP_SESSION_MODE | HTTP session mode; must resolve to stateful for confirmation rounds. The example and Docker image set stateful; an unset value uses auto, resolving to stateful. Explicit stateless fails startup. | auto | | MCP_REQUEST_STATE_KEY | Optional key of at least 32 bytes, shared across instances, that seals consent record IDs. | — | | OTEL_ENABLED | Enable OpenTelemetry instrumentation. | false | | OTEL_EXPORTER_OTLP_ENDPOINT | OTLP base for traces and metrics; signal-specific endpoints override it. | — | | OTEL_EXPORTER_OTLP_LOGS_ENDPOINT | Opt-in log export endpoint; the base endpoint never enables logs. | — | | LOG_TOOL_FAILURE_PAYLOADS | Opt-in failed-call arguments/result logging; redaction matches key names, so secrets in free-form values can remain. | false |

See .env.example for the full list of optional overrides.

Running the server

Local development

  • Build and run:

    # One-time build
    bun run rebuild
    
    # Run the built server
    bun run start:stdio
    # or
    bun run start:http
  • Run checks and tests:

    bun run devcheck   # Lint, format, typecheck, security, changelog sync
    bun run test       # Vitest test suite
    bun run lint:mcp   # Validate MCP definitions against spec

Docker

docker build -t seerr-mcp-server .
docker run --rm \
  -e SEERR_BASE_URL=http://host.docker.internal:5055 \
  -e SEERR_API_KEY=your-api-key \
  -p 3010:3010 \
  seerr-mcp-server

The Dockerfile defaults to HTTP transport, stateful 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.

Project structure

| Directory | Purpose | |:---|:---| | src/index.ts | createApp() entry point — registers the six tools + one resource, serves skills/, 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. | | skills/ | Published Agent Skills, served over MCP as skill:// resources. | | tests/ | Unit and integration tests mirroring src/. |

Development guide

See CLAUDE.md/AGENTS.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic
  • Use ctx.log for request-scoped logging, ctx.state for tenant-scoped storage
  • Register new tools and resources via the barrels in src/mcp-server/*/definitions/index.ts
  • Wrap the Jellyseerr API: validate raw → normalize and redact to a domain type → return the output schema; never fabricate missing fields, and never let operator PII or paths reach output

Contributing

Issues are welcome. Run checks and tests before submitting:

bun run devcheck
bun run test

License

Apache-2.0 — see LICENSE for details.