downloader-mcp
downloader-mcp
·
claude-opus-4-8[1m] · 2026-07-07 · details
An MCP server for download clients —
SABnzbd (usenet) and qBittorrent (torrents) — packaged as a
Docker container. Companion to media-management MCPs like
servarr-mcp.
Each client is optional: configure only the ones you actually run, and only those tools register.
Tools
SABnzbd (usenet)
Tool | Description |
| Current download queue with speeds and ETAs |
| Recent history (newest first) |
| Configured categories |
| SABnzbd version info |
qBittorrent (torrents)
Tool | Description |
| List torrents, optional status filter |
| Torrent details by info-hash |
| Files inside a torrent |
| Global transfer stats |
| Configured categories |
| qBittorrent application version |
Configuration
Each client requires its full config block to enable; partial config silently disables the client.
Client | Required env vars | Default port |
SABnzbd |
| 8080 |
qBittorrent |
| 8080 |
API keys are found in each app's settings:
SABnzbd: Config → General → API Key
qBittorrent: Tools → Options → Web UI → API Key (requires qBittorrent >= v5.2.0 / WebAPI >= v2.14.1)
Note: SABnzbd and qBittorrent both default to port 8080. If you run both on the same host, remap one of them in its own config.
At least one client must be configured or the server exits with an error.
HTTP transport hardening
When running in HTTP mode (MCP_PORT set), you can enable bearer-token
auth and DNS-rebinding protection with:
Env var | Meaning |
| Shared secret. When set, every |
| Comma-separated bare-hostname Host/Origin allowlist (DNS-rebinding defense; port-independent, bracketed IPv6 like |
The provided Compose deployment requires MCP_ALLOWED_HOSTS so a missing
stack variable cannot silently expose the MCP endpoint to whatever the safe
default admits. MCP_AUTH_TOKEN stays optional even in Compose: if unset,
the server logs a startup warning and leaves /mcp unauthenticated.
Recommended MCP_AUTH_TOKEN: a random secret, e.g. openssl rand -hex 32,
passed by clients as Authorization: Bearer <token>.
Recommended MCP_ALLOWED_HOSTS: the host names/IPs clients actually use to
reach the server — fleet-canonical form is
MCP_ALLOWED_HOSTS=localhost,127.0.0.1,[::1],192.168.1.50,host.docker.internal
(bare hostnames only — a mapped HOST_PORT doesn't need to appear here, and
a host:port entry is now rejected at startup rather than silently ignored).
Run with Docker
docker build -t downloader-mcp .
docker run -i --rm \
-e SABNZBD_URL=http://192.168.1.50:8080 -e SABNZBD_API_KEY=... \
-e QBITTORRENT_URL=http://192.168.1.50:8081 \
-e QBITTORRENT_API_KEY=... \
downloader-mcpPublished image
After each push to main (docs-only changes excluded), GitHub Actions
builds and pushes an image to GHCR:
ghcr.io/carldog/downloader-mcp:latest (linux/amd64 only — every
deployment target is x86-64; see docker-publish.yml for the ARM
tradeoff if that ever changes)
Pull instead of building locally:
docker pull ghcr.io/carldog/downloader-mcp:latest
docker run -i --rm \
-e SABNZBD_URL=... -e SABNZBD_API_KEY=... \
ghcr.io/carldog/downloader-mcp:latestRun with Docker Compose (HTTP, long-lived)
The compose file runs the server in HTTP mode (Streamable HTTP) for
long-lived deployment via Portainer or Compose. It pulls the published
image from ghcr.io/carldog/downloader-mcp:latest.
# Set whichever client credentials apply:
export SABNZBD_URL=http://192.168.1.50:8080; export SABNZBD_API_KEY=...
export QBITTORRENT_URL=http://192.168.1.50:8081
export QBITTORRENT_API_KEY=...
export MCP_AUTH_TOKEN="$(openssl rand -hex 32)"
export MCP_ALLOWED_HOSTS="192.168.1.50"
export HOST_PORT=3003 # optional, defaults to 3003
docker compose upThe MCP endpoint will be at http://<host>:${HOST_PORT}/mcp.
Deploy via Portainer (Stack from Git)
In Portainer, Stacks → Add Stack → Repository.
Repository URL:
https://github.com/CarlDog/downloader-mcpCompose path:
docker-compose.ymlEnvironment variables: set whichever client credentials apply, plus
MCP_AUTH_TOKENandMCP_ALLOWED_HOSTS. Optionally setHOST_PORT.Deploy. Healthcheck reaches green within ~10 seconds.
Use with Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"downloader": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "SABNZBD_URL", "-e", "SABNZBD_API_KEY",
"-e", "QBITTORRENT_URL",
"-e", "QBITTORRENT_API_KEY",
"downloader-mcp"
],
"env": {
"SABNZBD_URL": "http://192.168.1.50:8080",
"SABNZBD_API_KEY": "...",
"QBITTORRENT_URL": "http://192.168.1.50:8081",
"QBITTORRENT_API_KEY": "..."
}
}
}
}Drop the -e/env entries for whichever client you don't run.
Local development
npm install
cp .env.example .env # then edit
SABNZBD_URL=... SABNZBD_API_KEY=... npm run devSecurity
Container runs as a non-root user (
mcp).Credentials passed via env vars — never baked into the image.
A
.githooks/pre-commitruns gitleaks (secrets) and a PII pattern check (user-home paths, personal-domain emails). Activate it once per clone:git config core.hooksPath .githooks.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/CarlDog/downloader-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server