Skip to main content

MCP server giving any MCP-capable agent a clean, intelligent, unified interface to Sonarr, Radarr, Lidarr, Prowlarr, and Jellyfin.

Project description

arr-stack-mcp

A Model Context Protocol server that exposes Sonarr, Radarr, Lidarr, and Jellyfin to any MCP client (Claude Desktop, Claude Code, n8n, ibis-bot, custom agents) through a hand-curated tool layer written for LLM consumption.

License: MIT Python 3.12+

What this gives an agent

28 curated tools split across four services:

Service Tools Highlights
Sonarr 8 series search, TVDB lookup, queue, calendar, missing episodes, add/delete
Radarr 8 movie search, TMDB lookup, queue, calendar, missing, add/delete
Lidarr 7 artist + album search, MusicBrainz lookup, queue, add/delete
Jellyfin 5 library search, recent additions, now-playing sessions, library scan

Every tool:

  • Has a description written for an agent's decision-making, not for human docs.
  • Pins its input and output with Pydantic v2, so the MCP catalogue carries strict JSON Schema.
  • Returns a compact, self-describing envelope (ok, query, count, total, items).
  • Translates upstream HTTP errors into structured ToolError envelopes with self-suggesting hints.
  • Destructive operations require a two-call confirm-token flow.

Quickstart

uvx (one-shot)

uvx arr-stack-mcp serve --transport stdio

Docker

docker run --rm -i \
  -e SONARR_URL=http://host.docker.internal:8989 \
  -e SONARR_API_KEY=$SONARR_API_KEY \
  -e RADARR_URL=http://host.docker.internal:7878 \
  -e RADARR_API_KEY=$RADARR_API_KEY \
  -e LIDARR_URL=http://host.docker.internal:8686 \
  -e LIDARR_API_KEY=$LIDARR_API_KEY \
  -e JELLYFIN_URL=http://host.docker.internal:8096 \
  -e JELLYFIN_API_KEY=$JELLYFIN_API_KEY \
  ghcr.io/new-usemame/arr-stack-mcp:0.1.0

Local development (uv)

git clone https://github.com/new-usemame/arr-stack-mcp
cd arr-stack-mcp
uv sync
uv run arr-stack-mcp init           # writes a starter arr-stack-mcp.yaml
uv run arr-stack-mcp serve          # boots stdio MCP server

Claude Desktop config

Add to ~/Library/Application Support/Claude/claude_desktop_config.json on macOS, or %APPDATA%\Claude\claude_desktop_config.json on Windows:

{
  "mcpServers": {
    "arr-stack": {
      "command": "uvx",
      "args": ["arr-stack-mcp", "serve"],
      "env": {
        "SONARR_URL": "http://localhost:8989",
        "SONARR_API_KEY": "...",
        "RADARR_URL": "http://localhost:7878",
        "RADARR_API_KEY": "...",
        "LIDARR_URL": "http://localhost:8686",
        "LIDARR_API_KEY": "...",
        "JELLYFIN_URL": "http://localhost:8096",
        "JELLYFIN_API_KEY": "..."
      }
    }
  }
}

Config reference

arr-stack-mcp init writes a starter arr-stack-mcp.yaml. Every secret can also be supplied via env var. Inline expansion of ${VAR} and ${VAR:-default} is supported.

log_level: info

transport:
  stdio: true
  http_enabled: false
  http_host: 127.0.0.1
  http_port: 8080

policy:
  read_only: false
  disable_destructive: false
  confirm_token_ttl_seconds: 300

services:
  sonarr:
    enabled: true
    url: http://localhost:8989
    api_key: ${SONARR_API_KEY}
    verify_tls: true
    timeout_seconds: 30
  radarr:
    enabled: true
    url: http://localhost:7878
    api_key: ${RADARR_API_KEY}
  lidarr:
    enabled: true
    url: http://localhost:8686
    api_key: ${LIDARR_API_KEY}
  jellyfin:
    enabled: true
    url: http://localhost:8096
    api_key: ${JELLYFIN_API_KEY}
    default_user_id: 4f8b6d2e-3a9c-4f1d-9c2a-1b3c5d7e9f01

Runtime flags

Flag Env Effect
--read-only ARRSTACK_READ_ONLY=true Skip every tool tagged write or destructive at registration.
--disable-destructive ARRSTACK_DISABLE_DESTRUCTIVE=true Skip only the destructive tools (deletes).
--transport stdio Default. Use for Claude Desktop and Claude Code.
--transport streamable-http HTTP transport for n8n / remote consumers.
--config path/to/config.yaml Override config path. Defaults to ./arr-stack-mcp.yaml, then XDG.

Capability matrix

Tools follow the <service>.<verb_object> convention. Tags drive flag-based gating.

Sonarr

Tool Tag Use
sonarr.system_status read Version + branch + uptime. First call when diagnosing.
sonarr.series_search read Search the existing library (already-added).
sonarr.series_lookup read Search TVDB to discover series to add.
sonarr.queue read Active downloads with progress.
sonarr.calendar read Upcoming + recently-aired episodes.
sonarr.missing read Monitored episodes not on disk.
sonarr.series_add write Add a series by TVDB id. Idempotent.
sonarr.series_delete destructive Two-call confirm-token flow.

Radarr

Tool Tag Use
radarr.system_status read Mirrors Sonarr.
radarr.movie_search read Search the existing library.
radarr.movie_lookup read Search TMDB to discover movies to add.
radarr.queue read Active downloads.
radarr.calendar read Upcoming + recently-released movies.
radarr.missing read Monitored movies not on disk.
radarr.movie_add write Add a movie by TMDB id. Idempotent.
radarr.movie_delete destructive Two-call confirm-token flow.

Lidarr

Tool Tag Use
lidarr.system_status read
lidarr.artist_search read Search the existing library.
lidarr.artist_lookup read Search MusicBrainz to discover artists.
lidarr.artist_albums read List albums under one artist.
lidarr.queue read Active downloads.
lidarr.artist_add write Add an artist by MusicBrainz id. Idempotent.
lidarr.artist_delete destructive Two-call confirm-token flow.

Jellyfin

Tool Tag Use
jellyfin.system_info read Version + server name. Public endpoint.
jellyfin.library_search read Search items by name across libraries.
jellyfin.recent_additions read Newest items by date_added.
jellyfin.now_playing read Currently-active sessions with progress.
jellyfin.scan_library write Trigger a library refresh.

Confirm-token flow

Destructive tools (*_delete) implement a two-call confirm:

# Call 1: returns plan + token, no side effect
> sonarr.series_delete(sonarr_id=42, delete_files=False)
{
  "ok": true,
  "needs_confirm": true,
  "confirm_token": "abc123",
  "summary": "remove 'Foo Bar' (sonarr_id=42) from Sonarr; keep files on disk",
  "expires_in_seconds": 300
}

# Call 2: executes
> sonarr.series_delete(sonarr_id=42, delete_files=False, confirm_token="abc123")
{
  "ok": true,
  "deleted_sonarr_id": 42,
  "title": "Foo Bar",
  "files_deleted": false,
  "msg": "deleted sonarr_id=42"
}

Tokens are single-use, time-limited, and bound to the request payload — a token from one tool will not confirm another.

Architecture

Two layers:

┌────────────────────────────────────────────────────────────┐
│  Curated MCP tools (arr_stack_mcp.tools.<service>.tools)  │
│  Hand-written, LLM-friendly names, descriptions, schemas. │
├────────────────────────────────────────────────────────────┤
│  Generated thin clients (arr_stack_mcp.generated.<svc>)   │
│  openapi-python-client output. Do not hand-edit; regen    │
│  via scripts/regen-clients.sh on upstream version bumps.  │
├────────────────────────────────────────────────────────────┤
│  httpx + pydantic + structlog + mcp.server.fastmcp         │
└────────────────────────────────────────────────────────────┘

See docs/ARCHITECTURE.md for the longer walk-through.

Development

uv sync                              # install runtime + dev deps
uv run pytest -q                     # fast unit tests
uv run ruff check src/ tests/        # lint
uv run mypy src/ tests/              # type check (strict)
uv run ruff format src/ tests/       # format

Integration tests require the docker test stack:

scripts/test-stack-up.sh             # boots Sonarr/Radarr/Lidarr/Prowlarr/Jellyfin
uv run pytest -m integration         # runs once stack is healthy
scripts/test-stack-down.sh --clean   # teardown

Contributing

See CONTRIBUTING.md for the OpenAPI regeneration flow and the verification checklist that gates every release.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

arr_stack_mcp-0.1.1.tar.gz (1.0 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

arr_stack_mcp-0.1.1-py3-none-any.whl (2.7 MB view details)

Uploaded Python 3

File details

Details for the file arr_stack_mcp-0.1.1.tar.gz.

File metadata

  • Download URL: arr_stack_mcp-0.1.1.tar.gz
  • Upload date:
  • Size: 1.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for arr_stack_mcp-0.1.1.tar.gz
Algorithm Hash digest
SHA256 1fdee02cce8e4958a0d46f52a0853189b8828a8ab99b9c5d33c6700f6946e565
MD5 78ae5058a85c9e1facb1c1b38b71d0b4
BLAKE2b-256 11b22f7b95ba03d4b9e2b16a35213eb7f46a8139ff817276aa14d17a02b3e907

See more details on using hashes here.

Provenance

The following attestation bundles were made for arr_stack_mcp-0.1.1.tar.gz:

Publisher: publish.yml on new-usemame/arr-stack-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file arr_stack_mcp-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: arr_stack_mcp-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 2.7 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for arr_stack_mcp-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f50715a2900318ce4652a05808e6336ed2b9c370fbd0633234b270ba6a7e9e1e
MD5 f9d707b409900fe60ed4e28544397f47
BLAKE2b-256 f259bfc92d4ef10d1f1b70b695ba211b1efdd6bd3de3773f77529008b03f53dc

See more details on using hashes here.

Provenance

The following attestation bundles were made for arr_stack_mcp-0.1.1-py3-none-any.whl:

Publisher: publish.yml on new-usemame/arr-stack-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page