prowlarr-mcp-server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@prowlarr-mcp-serversearch releases for 'Dune Part Two' across all indexers"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
prowlarr-mcp-server
A minimal Model Context Protocol server that connects to Prowlarr, packaged for Docker.
It runs as a standing network service (streamable-http transport, not stdio),
so any MCP client on your internal network can connect to
http://<host>:<port>/mcp — the container isn't spawned per-client, and
container lifecycle/updates can be handed off to a tool like
Dockhand.
Tools
Tool | Description |
| List indexers configured in Prowlarr, optionally filtered to only enabled ones |
| Full details for one indexer by ID |
| Trigger Prowlarr to test connectivity for an already-configured indexer |
| Run a manual release search across indexers (does not grab/download anything) |
| Prowlarr system status and health checks |
test_indexer is the only tool that reaches out and exercises a real indexer
connection. Everything else is read-only. Unlike Sonarr/Radarr, Prowlarr has
no library on disk, so there's no missing_*/diskspace-style tool here —
it manages indexers, not downloaded media.
Related MCP server: media-stack-mcp
Health endpoints
Two plain HTTP endpoints, reachable without MCP_AUTH_TOKEN (so Docker's
HEALTHCHECK, Dockhand, or any other monitor can poll them without the
secret):
Endpoint | Checks | Healthy | Unhealthy |
| The process is up and serving HTTP. Does not call Prowlarr. |
| (doesn't respond) |
|
|
|
|
They're split deliberately: /health is what the container's own
HEALTHCHECK uses (so a transient Prowlarr outage doesn't get the container
itself restarted in a loop), while /ready is for verifying config — after
changing PROWLARR_URL/PROWLARR_API_KEY, curl http://<host>:8934/ready
tells you plainly whether the host is reachable, the key is valid, or both.
Authentication
Set MCP_AUTH_TOKEN (a random shared secret — openssl rand -hex 32) and
every request must carry Authorization: Bearer <token> or the server
returns 401. This is checked by a small Starlette middleware in front of
the MCP app, not the mcp SDK's built-in OAuth support
(mcp.server.auth) — that machinery expects a full OAuth authorization
server (issuer/resource metadata, RFC 8414/8707/9068 discovery), which is
unnecessary complexity for one secret shared by trusted LAN clients.
Leave MCP_AUTH_TOKEN unset and the server runs with no auth — anything
that can reach http://<host>:<port>/mcp can call every tool, including
test_indexer. The server logs a warning on startup when it's running this
way. Either way, the trust boundary is still the network:
Do not publish this port through any reverse proxy, port-forward, or anything else reachable from outside your LAN/VLAN — the bearer token protects against anyone on the network, not against the open internet.
Bind the compose
ports:mapping to a specific internal interface (e.g.192.168.1.50:8934:8934) rather than all interfaces, if you want to be stricter about which hosts on your network can reach it at all.
Configuration
Environment variables (see .env.example):
Variable | Required | Default | Description |
| yes | — | e.g. |
| yes | — | Prowlarr > Settings > General > API Key |
| no |
| Prowlarr REST API version to call ( |
| no |
| Interface the server binds to inside the container |
| no |
| Port the server listens on |
| no | — | Shared secret required as |
API version checking
Prowlarr shares its underlying HTTP framework with Sonarr/Radarr (all three
are Servarr apps built on the same *arr common codebase), so it exposes the
same unauthenticated, unversioned GET /api endpoint reporting which API
version is current and which are deprecated (e.g. {"current": "v1", "deprecated": []}). GET /ready calls it and compares it against
PROWLARR_API_VERSION:
version matches
current, or is listed underdeprecated(still served, just on notice) → healthy, reported under the response'sapiVersionkey.version isn't offered at all any more →
503, since every tool call would otherwise start failing with 404s. BumpPROWLARR_API_VERSIONto match what Prowlarr now reports.Prowlarr doesn't have this endpoint (very old versions) or it's unreachable → non-fatal,
apiVersion: {"checked": false}.
This turns a silent break on a Prowlarr upgrade into a readiness-probe
failure instead. Prowlarr's API has been on v1 since it was introduced, so
in practice this is unlikely to trip — it's here for consistency with the
sonarr/radarr servers and as a safety net if that ever changes.
Image
Built and pushed to ghcr.io/barrow1990/prowlarr-mcp-server by
.github/workflows/ci.yml on every push to
main that passes tests, tagged :latest, :<commit-sha>, and
:prowlarr-<api-version> (e.g. :prowlarr-v1 — the Prowlarr API version
this build targets, read out of server.py's PROWLARR_API_VERSION default
so it can't drift from what the code actually calls). docker-compose.yml
pulls :latest by default; swap in build: . there instead if you'd rather
build locally from the Dockerfile.
The image is a three-stage build: builder compiles dependencies into
--target=/deps (all of them, including cryptography's compiled cffi
extension, ship musllinux wheels, so this needs no compiler even on alpine);
prep starts fresh from python:3.12-alpine, drops pip/setuptools/wheel,
strips stdlib pieces this headless server never touches (tkinter,
idlelib, lib2to3, ensurepip, ...), adds the non-root app user, and
copies in /deps and server.py; runtime then does a single
COPY --from=prep / / onto a scratch base. That last step matters more
than it looks — a plain RUN rm -rf only hides files still physically
present in the base image's own layers underneath, so it doesn't shrink a
normal layered image at all; copying the already-trimmed filesystem onto
scratch is what actually drops those bytes from what gets pushed.
That takes the published image to roughly ~98MB. The floor from here is
mcp's own dependency graph: mcp.server.request_state unconditionally
imports cryptography's AES-GCM/HKDF (spec-mandated integrity protection for
MCP's requestState, not something gated behind JWT/OAuth use), so its
~15MB native extension ships regardless. Dependencies in requirements.txt
are pinned to exact versions rather than >= ranges, so a routine
docker build can't silently pull in a heavier resolution than
the one that was actually tested.
Running with Docker Compose
cp .env.example .env # fill in PROWLARR_URL / PROWLARR_API_KEY
docker compose up -d --pull alwaysThe server is then reachable at http://<docker-host>:8934/mcp from anything
on your internal network.
Managing with Dockhand
Point Dockhand at ghcr.io/barrow1990/prowlarr-mcp-server and let it track new
tags — this is the registry-pull model Dockhand's image-update tracking
(Grype/Trivy scans, tag tracking, scheduled updates) is actually built around.
The alternative, pointing Dockhand at this repo as a Git-deployed Compose
stack with build: ., works too, but syncing new Git commits does not
imply rebuilding the image — those are two separate steps for a build-from-
source stack.
Make the GHCR package public, or every pull will need docker login ghcr.io with a PAT on each deploy host — a private package by default
requires auth even to docker pull, which most homelab boxes won't have
configured.
Set a restart policy of unless-stopped (already in docker-compose.yml) so
Dockhand-driven restarts and host reboots bring it back up without manual
intervention. The HEALTHCHECK in the Dockerfile (GET /health) drives
Docker's/Dockhand's container health status; use GET /ready (see above)
separately if you want to alert on Prowlarr connectivity specifically rather
than container liveness.
Environment variables in Dockhand: docker-compose.yml loads
PROWLARR_URL/PROWLARR_API_KEY/MCP_AUTH_TOKEN via env_file: [.env, .env.dockhand]
(both optional; .env.dockhand loads second, so it wins for any key it also
sets). This is deliberate — a Git-deployed stack's .env is whatever's
checked out from the repo (i.e. .env.example's placeholders, since real
.env is gitignored and not committed), while Dockhand writes the values you
configure in its UI to .env.dockhand instead. If you set PROWLARR_URL in
Dockhand's UI and the container is still using a placeholder, check that
Dockhand is actually writing to .env.dockhand in the stack directory (not
some other file) and that a rebuild has run since — a synced Git file change
alone doesn't rebuild the image; see GET /ready to confirm what's live.
Connecting a client
Claude Code
claude mcp add prowlarr -s user --transport http http://<docker-host>:8934/mcp \
--header "Authorization: Bearer <MCP_AUTH_TOKEN>"(Drop the --header flag if you're running with MCP_AUTH_TOKEN unset.)
Claude Desktop
Claude Desktop's built-in config expects a locally-spawned command, so for
a network server like this you'll need an HTTP-to-stdio bridge such as
mcp-remote:
{
"mcpServers": {
"prowlarr": {
"command": "npx",
"args": [
"-y", "mcp-remote", "http://<docker-host>:8934/mcp",
"--header", "Authorization: Bearer <MCP_AUTH_TOKEN>"
]
}
}
}Running without Docker
pip install -r requirements.txt
PROWLARR_URL=http://192.168.1.50:9696 PROWLARR_API_KEY=your-api-key \
MCP_AUTH_TOKEN=your-shared-secret python server.pyTesting
pip install -r requirements-dev.txt
python -m pytest tests/ -vtests/test_tools.py— each tool's logic against a mocked Prowlarr (httpx.MockTransport, no extra mocking library needed).tests/test_http.py—/health,/ready, and the bearer-auth middleware, viaserver.build_app()(the exact app__main__runs) through Starlette'sTestClient.tests/test_live_prowlarr.py— opt-in contract tests against a real Prowlarr instance, to catch drift if a Prowlarr upgrade renames/removes a field these tools depend on (id,name,enable,indexerId,title, ...). Skipped by default (no Prowlarr in CI); run with:RUN_LIVE_PROWLARR_TESTS=1 PROWLARR_URL=https://prowlarr.example.com \ PROWLARR_API_KEY=<real key> python -m pytest tests/test_live_prowlarr.py -v
CI (.github/workflows/ci.yml) runs the mocked suite on every push/PR; the
GHCR build only runs after it passes.
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server for Pentest-Tools.com: run scans, manage findings and reports via your preffered LLM.
- ZapierOAuthcom.zapier
Hosted MCP server connecting AI assistants to 9,000+ apps and 40,000+ actions via Zapier.
- QuallaaOAuthcom.quallaa
Talk to your public-facing AI from any MCP client — Claude, ChatGPT, Cursor, Cline, Windsurf.
MCP server for secureFlows: token-free URL builders and integration-linting tools for AI agents.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceConnects AI agents to Prowlarr and TorBox for autonomous torrent searching, downloading, and media extraction via MCP.MIT
- AlicenseBqualityBmaintenanceEnables control and management of a self-hosted media stack (Radarr, Sonarr, Prowlarr, SABnzbd, qBittorrent) through natural language via MCP.15GPL 3.0

profilarr-mcpofficial
AlicenseAqualityAmaintenanceMCP server exposing Profilarr's v1 REST API as tools, enabling an LLM to inspect and manage linked databases, connected Radarr/Sonarr instances, backups, jobs, announcements, and system status.20MIT- AlicenseAqualityAmaintenanceMCP server exposing Prowlarr's API v1 as tools for managing indexers, applications, download clients, and more, including cross-indexer searches and system commands.10MIT