yarr
Rust MCP and CLI server for a media automation fleet: Sonarr, Radarr, Prowlarr, Tautulli, Overseerr, Bazarr, Tracearr, SABnzbd, qBittorrent, Plex, and Jellyfin.
If you run Claude Code, Codex, or Gemini CLI against a self-hosted media stack,
yarr gives an agent one consistent way to query and control all of it instead
of eleven different ad hoc integrations. It is an upstream-client MCP server:
it does not replace those applications or mirror every REST endpoint as a web
UI. Its job is to provide one consistent tool surface for agents and one
equivalent CLI surface for operators.
Not for: a general-purpose REST gateway to arbitrary services, or a
scheduler/automation engine in its own right. yarr only talks to the service
kinds it knows about, and only does what you or your agent ask it to do.
Contents
- Naming
- Capabilities And Boundaries
- Install
- Quickstart
- Client Configuration
- Runtime Surfaces
- MCP Tool Reference
- CLI Reference
- Configuration
- Authentication
- Safety And Trust Model
- Architecture
- Distribution Contract
- Development
- Verification
- Deployment
- Troubleshooting
- Related Servers
- Documentation
- License
Naming
This repository is published at github.com/jmagar/yarr.
The Rust package and installed binary are both yarr. The npm launcher package
is yarr-mcp because the shorter yarr name is occupied on npm; installing the
launcher still gives you a yarr command. The MCP registry name is
tv.tootie/yarr-mcp, and Docker images use ghcr.io/jmagar/yarr:<version>.
Plugin naming is intentionally split:
yarris the full MCP server plugin with the bundledyarrbinary and every per-service fallback skill.sonarr,radarr,prowlarr,overseerr,sabnzbd,qbittorrent,plex,jellyfin,tautulli,tracearr, andbazarrare skills-only plugins with no MCP server and noyarrbinary.
Capabilities And Boundaries
yarr wraps a configured media automation fleet through one action-dispatched
service layer. The same implementation backs MCP and CLI calls, so behavior
does not drift between "agent used the tool" and "operator ran the command."
Primary capabilities:
- Fleet status checks across the configured services.
- Credentialed upstream API passthrough for known service kinds.
- Generated OpenAPI operations for Sonarr, Radarr, Prowlarr, Overseerr, Jellyfin, and Plex.
- Curated commands for SABnzbd, qBittorrent, Tautulli, Bazarr, and Tracearr, whose upstreams do not ship usable machine-readable specs.
- Code Mode over MCP for multi-step media automation scripts.
- Snippet storage and execution for repeatable Code Mode workflows.
- Skills-only direct-HTTP plugin fallbacks for each individual service.
Boundaries:
yarrdoes not store or schedule media jobs on its own.- It does not expose a local REST action API or embedded web UI.
- It does not accept arbitrary unknown service kinds.
- MCP callers never provide credentials, tokens, keys, or secrets as action arguments. Credentials come from environment variables, config files, or plugin user configuration.
Install
The recommended install path is the Node launcher package:
# Run the stdio MCP server without a permanent install.
npx -y yarr-mcp mcp
# Or install the launcher globally.
npm i -g yarr-mcp
yarr --version
yarr mcp
The npm package downloads the matching GitHub Release binary during install and
adds yarr to PATH. It does not expose legacy command aliases.
For machines without npm, use the release installer:
curl -fsSL https://raw.githubusercontent.com/jmagar/yarr/main/scripts/install.sh | bash
That script installs yarr into ~/.local/bin.
Quickstart
The first-screen 30-second path is:
export YARR_SERVICES=sonarr
export YARR_SONARR_URL=http://127.0.0.1:8989
export YARR_SONARR_API_KEY=...
npx -y yarr-mcp sonarr status
npx -y yarr-mcp mcp
Then point an MCP client at the stdio command:
{
"mcpServers": {
"yarr": {
"command": "npx",
"args": ["-y", "yarr-mcp", "mcp"],
"env": {
"YARR_SERVICES": "sonarr",
"YARR_SONARR_URL": "http://127.0.0.1:8989",
"YARR_SONARR_API_KEY": "${YARR_SONARR_API_KEY}"
}
}
}
}
For Claude Code plugin installs, use the marketplace commands inside a Claude Code chat session, not in a shell:
/plugin marketplace add jmagar/yarr
/plugin install yarr@yarr
Install one skills-only plugin when you want direct service scripts without the MCP server:
/plugin install sonarr@yarr
/plugin install plex@yarr
Client Configuration
stdio
stdio is the preferred local MCP transport. It starts yarr mcp on demand and
does not require binding a local HTTP port.
{
"mcpServers": {
"yarr": {
"command": "yarr",
"args": ["mcp"],
"env": {
"RUST_LOG": "info,yarr=debug"
}
}
}
}
Streamable HTTP
Run a persistent server when several clients or machines should share one MCP endpoint:
YARR_MCP_TOKEN=change-me yarr serve
{
"mcpServers": {
"yarr": {
"url": "http://127.0.0.1:40070/mcp",
"headers": {
"Authorization": "Bearer ${YARR_MCP_TOKEN}"
}
}
}
}
HTTP MCP smoke call:
curl -s http://127.0.0.1:40070/mcp \
-H "Authorization: Bearer $YARR_MCP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"yarr","arguments":{"code":"async () => await sonarr.get_system_status()"}}}'
Runtime Surfaces
| Surface | Status | Purpose |
|---|---|---|
| MCP | Required | One default yarr Code Mode tool over the whole fleet |
| CLI | Required | Scriptable parity surface for debugging and automation |
| REST | Not shipped | Upstream-client servers do not expose a local REST action API |
| Web | Not shipped | Upstream-client servers do not serve an embedded web UI |
Set YARR_MCP_TOOL_MODE=flat to advertise one action-dispatched MCP tool per
configured service instead of the single Code Mode tool. That mode is useful
behind a gateway such as Labby that already provides its own discovery and
sandbox layer. codemode is the default and the right choice for standalone
MCP clients.
MCP Tool Reference
By default, MCP exposes one tool named yarr.
| Field | Type | Required | Notes |
|---|---|---|---|
code | string | yes | JavaScript async arrow function executed in the in-process Code Mode sandbox |
Inside Code Mode, scripts can use:
- Per-service callables such as
sonarr.get_series(),radarr.post_movie({ body }),prowlarr.get_indexer(), andplex.get_sessions(). - Curated commands such as
qbittorrent.download_queue()andtautulli.stats_activity(). - Raw passthrough helpers at
api.<service>.get/post/put/delete(path, body). callTool(action, params)for the underlying action-dispatch escape hatch.codemode.search(query)andcodemode.describe(path)for discovery.codemode.run(name, input),codemode.snippets(), andwriteArtifact(...)for reusable scripts and artifacts.
Example:
async () => {
const queue = await radarr.get_queue();
await radarr.post_command({
body: { name: "MoviesSearch", movieIds: [456] }
});
return { queued: queue.records?.length };
}
Generic Actions
These actions work for every configured service kind:
| Action | Scope | CLI | Description |
|---|---|---|---|
service_status | yarr:read | yarr <service> status | Fetch an upstream service status endpoint |
api_get | yarr:write | yarr <service> get --path <path> | Proxy a credentialed GET request |
api_post | yarr:write | yarr <service> post --path <path> --body <json> | Proxy a POST request |
api_put | yarr:write | yarr <service> put --path <path> --body <json> | Proxy a PUT request |
api_delete | yarr:write | yarr <service> delete --path <path> | Proxy a DELETE request |
help | public | yarr help | Return action reference |
Code Mode Actions
| Action | Scope | Surface | Description |
|---|---|---|---|
codemode | yarr:write | yarr tool / yarr codemode --code <JS> | Run a JS arrow function over the fleet |
op | yarr:write | <service>.<operation>() / yarr <service> op <name> | Dispatch a generated OpenAPI operation |
snippet_list | yarr:read | yarr snippet list / codemode.snippets() | List saved snippets |
snippet_save | yarr:write | yarr snippet save | Save a reusable snippet |
snippet_run | yarr:write | yarr snippet run / codemode.run(name, input) | Run a saved snippet |
snippet_delete | yarr:write | yarr snippet delete | Delete a saved snippet |
There is no confirm argument. CLI and Code Mode calls dispatch immediately;
the MCP surface additionally uses elicitation before destructive deletes.
CLI Reference
The CLI is service-grouped:
yarr help
yarr radarr status
yarr sonarr get --path /api/v3/system/status
yarr radarr post --path /api/v3/command --body '{"name":"RefreshMovie"}'
yarr sonarr put --path /api/v3/series/editor --body '{"seriesIds":[1],"qualityProfileId":4}'
yarr radarr delete --path /api/v3/movie/12
# Generated operations for spec-backed services.
yarr sonarr op get_series
yarr radarr op post_command --args '{"body":{"name":"MoviesSearch","movieIds":[456]}}'
# Curated commands for doc-only services.
yarr qbittorrent queue
yarr tautulli activity
# Code Mode and snippets.
yarr codemode --code 'async () => sonarr.get_system_status()'
yarr snippet list
There is no --service flag. Infra commands such as help, codemode, and
snippet are service-less.
Configuration
Copy .env.example or use config.example.toml as a starting point. Common
environment variables:
YARR_MCP_HOST=127.0.0.1
YARR_MCP_PORT=40070
YARR_MCP_TOKEN=change-me
YARR_SERVICES=sonarr,radarr,prowlarr,tautulli,overseerr,bazarr,tracearr,sabnzbd,qbittorrent,plex,jellyfin
YARR_SONARR_URL=http://sonarr:8989
YARR_SONARR_API_KEY=...
YARR_RADARR_URL=http://radarr:7878
YARR_RADARR_API_KEY=...
YARR_QBITTORRENT_URL=http://qbittorrent:8080
YARR_QBITTORRENT_USERNAME=...
YARR_QBITTORRENT_PASSWORD=...
YARR_PLEX_URL=http://plex:32400
YARR_PLEX_TOKEN=...
Supported service kinds are sonarr, radarr, prowlarr, tautulli,
overseerr, bazarr, tracearr, sabnzbd, qbittorrent, plex, and
jellyfin.
*_API_KEY covers most Arr-style services. qBittorrent uses username/password
login. Plex and Jellyfin token headers are handled separately.
YARR_MCP_TOOL_MODE=codemode is the default. Use
YARR_MCP_TOOL_MODE=flat only when a gateway should see separate per-service
tools.
Authentication
YARR_MCP_TOKEN gates every request once yarr is reachable beyond loopback.
On 127.0.0.1 or localhost with no explicit token, auth is bypassed for
local development. Set a real token, or set YARR_MCP_AUTH_MODE=oauth for
Google OAuth, before exposing this on a network.
Auth states:
| State | Condition | Behavior |
|---|---|---|
LoopbackDev | loopback bind or explicit loopback no-auth | no auth, no scopes |
TrustedGatewayUnscoped | YARR_NOAUTH=true behind a trusted gateway | no local auth or scopes |
Mounted bearer | non-loopback with YARR_MCP_TOKEN | bearer auth and scope checks |
Mounted OAuth | YARR_MCP_AUTH_MODE=oauth | OAuth/JWT auth and scope checks |
Unauthenticated health endpoints are /health, /ready, /status, and
/metrics. /status redacts secrets; /metrics exposes only request
rate/latency/status counters.
Safety And Trust Model
Secrets stay server-side. MCP clients provide action parameters, never upstream
tokens. Query-string secrets such as apikey=, token=, and X-Plex-Token
are rejected by path validation.
help is public at the action layer, but mounted HTTP transports still require
bearer or OAuth transport auth. service_status requires yarr:read.
Credentialed passthrough, generated operations, curated write operations, and
Code Mode require yarr:write; write satisfies read.
Generated DELETE operations, api_delete, download_remove,
stats_delete_image_cache, and trace_terminate_stream are destructive. CLI
and Code Mode dispatch them immediately. MCP callers get an interactive
elicitation prompt before the destructive action dispatches, with no call
argument that can skip that prompt.
Responses are capped by the shared token-limit layer before they are returned to MCP clients.
Architecture
MCP shim (src/mcp/tools.rs) CLI shim (src/cli.rs)
JSON args -> dispatch argv -> dispatch
\ /
execute_service_action (src/actions/dispatch.rs)
shared validation + curated-command dispatch
|
YarrService (src/app.rs + src/app/*.rs)
validation, service lookup, response shaping
|
YarrClient (src/yarr.rs)
network calls and auth headers
src/mcp.rs and src/cli.rs are thin facades re-exporting their own
submodules. execute_service_action makes CLI-to-MCP parity structural rather
than something kept in sync by hand.
The service layer owns:
- supported service catalog and config lookup
- safe path validation
- credential redaction
- qBittorrent login flow
- response normalization
Distribution Contract
The source of truth for release identity is the version shared by Cargo.toml,
Cargo.lock, xtask/Cargo.toml, .release-please-manifest.json,
packages/yarr-mcp/package.json, and server.json.
Generated code and docs must be regenerated from the committed source inputs, not patched by hand:
- Generated OpenAPI operations live under
src/openapi/generated/and come from vendored specs inspecs/. - Curated actions live in the handwritten action registries and docs.
- Plugin manifests stay versionless; marketplaces derive plugin version from the git commit SHA.
- The npm package version and the GitHub Release tag must match.
server.jsonpackage identifiers must point at the current OCI image tag.- The Docker image path is
ghcr.io/jmagar/yarr:<version>.
Development
cargo run -- help
cargo fmt --check
cargo test
cargo clippy -- -D warnings
cargo build --release
When changing generated operations:
cargo xtask gen-openapi
cargo test --test parity
When changing plugin packaging:
cargo test --test plugin_contract
cargo test --test template_invariants
Verification
Use the README guide checker before landing documentation changes:
python3 /home/jmagar/workspace/soma/scripts/check-readme-guide.py README.md
Use the package and Rust checks for distribution-sensitive work:
npm --prefix packages/yarr-mcp run check
cargo fmt --check
cargo check
cargo test
git diff --check
For live install verification, use the three public install paths:
curl -fsSL https://raw.githubusercontent.com/jmagar/yarr/main/scripts/install.sh | bash
npm i -g yarr-mcp
npx -y yarr-mcp mcp
Deployment
Run as a persistent HTTP MCP server:
YARR_MCP_HOST=0.0.0.0 \
YARR_MCP_PORT=40070 \
YARR_MCP_TOKEN=change-me \
yarr serve
Docker Compose deployments are covered by docker-compose.yml,
docker-compose.prod.yml, docs/DOCKER.md, and docs/DEPLOYMENT.md.
Production deployments should put yarr behind a trusted reverse proxy or MCP
gateway when exposed outside loopback.
Troubleshooting
401or403from HTTP MCP: confirmYARR_MCP_TOKEN, OAuth mode, and gateway headers.unknown service: confirm the service is listed inYARR_SERVICESand has a matchingYARR_<SERVICE>_URL.- upstream
401: confirm the service-specific API key or token in the server environment, not in tool arguments. query-string secret rejected: remove tokens from--pathand put them in config.- plugin skill cannot reach a service: rerun the plugin setup hook or reinstall
the plugin so credentials are bridged into
~/.config/lab-<service>/config.env. - Code Mode cannot find a callable: use
codemode.search(...)andcodemode.describe(...); generated names follow upstream OpenAPI operation IDs after normalization.
Related Servers
unifi-rmcp / rustifi- UniFi controller REST API bridge.tailscale-rmcp / rustscale- Tailscale API bridge for devices, users, and tailnet operations.unraid-rmcp / unrust- Unraid GraphQL bridge for NAS and server management.apprise-rmcp- Apprise notification fan-out bridge for many delivery backends.gotify-rmcp- Gotify push notification bridge for sends, messages, apps, and clients.arcane-rmcp- Arcane Docker management bridge for containers and related resources.ytdl-mcp- Media download and metadata workflow server.synapse- Local Synapse workflow server for scout and flux actions.cortex- Syslog and homelab log aggregation MCP server.axon- RAG, crawl, scrape, extract, and semantic search project.lab- Homelab control plane and Labby gateway project.lumen- Local semantic code search MCP server.nugs- Project/package management helper for local agent workflows.agentcast- Agent transcript and activity publishing project.soma- RMCP scaffold/runtime template for new provider-backed servers.
Documentation
The source of truth docs split is:
docs/API.mdfor action contracts and Code Mode call shape.docs/CONFIG.mdfor environment variables, auth states, and tool modes.docs/QUICKSTART.mdfor local smoke tests.docs/MCP_SCHEMA.mdfor schema drift rules.docs/AUTH.mdfor bearer and OAuth auth.docs/DEPLOYMENT.mdanddocs/DOCKER.mdfor production runtime.docs/PATTERNS.mdfor conventions shared across the RMCP server family.docs/PLUGINS.mdfor marketplace plugin packaging.plugins/README.mdfor theyarrbundle versus per-service plugin layout.plugins/yarr/README.mdandplugins/yarr/skills/yarr/SKILL.mdfor the full plugin package.CLAUDE.mdfor repo-local agent memory and the "How to add an action" checklist.