| name | rustarr |
| description | This skill should be used when the user wants to query or automate their media automation stack — Sonarr (TV shows), Radarr (movies), Prowlarr (indexers), Tautulli (Plex stats), Overseerr (media requests), SABnzbd (Usenet downloads), qBittorrent (torrents), Plex (media server), or Jellyfin. Trigger phrases include: "what's downloading", "add a movie to Radarr", "search for a TV show", "Sonarr queue", "Radarr library", "what's in my download queue", "Plex status", "Prowlarr indexers", "check Overseerr requests", "qBittorrent torrents", "SABnzbd queue", "Tautulli stats", "is Sonarr healthy", "media stack status", "arr services", "show me what's being downloaded". Always use rustarr for these — do not attempt to reach service APIs directly without it.
|
rustarr — Media Automation Stack
Rust MCP bridge to the *arr media stack and related services. The MCP surface is
one tool, yarr: it runs a JavaScript async arrow function (code) in a
sandbox (Code Mode) that reaches the whole fleet. Credentials are handled
server-side. Inside the script the fleet is reached through per-service callables
with the service baked in — generated OpenAPI operations for the 6 spec-backed
services, curated commands for download/stats, plus api.<service> raw passthrough
and callTool. Discover what's available with codemode.search/codemode.describe.
The yarr tool + Code Mode actions
yarr({ code }) runs the codemode action. Inside code you have:
- Per-service callables with the service baked in (no
service param):
sonarr.get_series(), radarr.post_movie({ body }), prowlarr.get_indexer(),
plex.get_sessions(), … For the 6 spec-backed services these are generated from
the upstream OpenAPI spec (the full API surface). DELETE operations are refused
mid-script.
- Raw passthrough:
api.<service>.get/post/put/delete(path, body).
- Discovery:
codemode.search(query) returns fully-qualified callables;
codemode.describe(path) returns a callable's signature OR a response type's
TypeScript interface (e.g. codemode.describe("sonarr.SeriesResource")).
- Snippets:
codemode.run(name, input) and codemode.snippets().
- Artifacts:
writeArtifact(path, content, options?).
The supporting actions (MCP-only; also on the CLI as rustarr codemode /
rustarr snippet): codemode, op (generated-operation dispatch),
snippet_list, snippet_save, snippet_run, snippet_delete. The generic
service actions remain: service_status, api_get, api_post, api_put,
api_delete, help.
The fleet kinds are sonarr, radarr, prowlarr, overseerr, tautulli,
plex, tracearr, sabnzbd, qbittorrent, jellyfin, and bazarr.
Quick-Reference Examples
Discovery and health
async () => {
const status = await sonarr.get_system_status();
const found = codemode.search("add movie").results.map(r => r.path);
return { sonarr: status.version, found };
}
Per-service callables (Tier 2)
Every example below is a yarr({ code }) call — code is one async arrow fn.
For the 6 spec-backed services (sonarr/radarr/prowlarr/overseerr/jellyfin/plex)
prefer the generated callables (full upstream API); fall back to
api.<service>.get/post/...(path, body) for anything not covered and for the
doc-based services (tautulli/sabnzbd/qbittorrent/bazarr/tracearr). Run
codemode.search("...") to find the exact callable + signature, and
codemode.describe("sonarr.SeriesResource") for a response type.
Sonarr (TV shows) / Radarr (movies)
async () => {
const series = await sonarr.get_series();
const queue = await sonarr.get_queue();
const movies = await radarr.get_movie();
await sonarr.post_command({ body: { name: "SeriesSearch", seriesId: 123 } });
await radarr.post_command({ body: { name: "MoviesSearch", movieIds: [456] } });
return { series: series.length, queued: queue.records?.length, movies: movies.length };
}
Prowlarr (indexers) / Overseerr (requests) / Plex
async () => {
const indexers = await prowlarr.get_indexer();
const pending = await overseerr.get_request({ filter: "pending" });
const sessions = await plex.get_sessions();
return { indexers: indexers.length, pending: pending.results?.length, sessions };
}
Tautulli / download clients (curated callables)
Curated commands surface as <service>.<action>():
async () => {
const activity = await tautulli.stats_activity();
const history = await tautulli.stats_history({ length: 20 });
const sabQueue = await sabnzbd.download_queue();
const torrents = await qbittorrent.download_queue();
return { activity, sabQueue, torrents };
}
Raw passthrough (any service)
When no callable fits, hit the upstream API directly. Never put credentials in
the path — the server injects auth.
async () => {
const homeStats = await api.tautulli.get("/api/v2?cmd=get_home_stats");
const libraries = await api.plex.get("/library/sections");
return { homeStats, libraries };
}
Common Workflows
"What's downloading right now?"
async () => ({
sonarr: (await sonarr.get_queue()).records ?? [],
radarr: (await radarr.get_queue()).records ?? [],
sab: await sabnzbd.download_queue(),
qbit: await qbittorrent.download_queue(),
})
"Is everything healthy?"
async () => {
const kinds = ["sonarr", "radarr", "prowlarr"];
const out = {};
for (const k of kinds) out[k] = await callTool("service_status", { service: k });
return out;
}
service_status is also a per-service callable: await sonarr.service_status().
"Who's watching Plex right now?"
async () => ({
tautulli: await tautulli.stats_activity(),
plex: await plex.get_sessions(),
})
Gotchas
-
The MCP surface is one tool, yarr. There are no mcp__rustarr__<service>
tools — pass a code script to yarr and reach services via per-service
callables, api.<service>, or callTool.
-
api_get/api_post/api_put require write scope. The generic passthrough
dispatches arbitrary upstream requests, so all of it is write-gated to prevent
credential leakage via crafted paths. Your MCP token must have write scope.
-
Writes run immediately; only destructive DELETEs are gated. Mutating
generated ops and api_post/api_put run without confirmation. Destructive
deletes (DELETE ops, api_delete, curated deletes like download_remove) are
refused mid-script in Code Mode — use the CLI with --confirm, or set
RUSTARR_ALLOW_DESTRUCTIVE on a disposable test stack.
-
Never include credentials in path. Configured service credentials live in
server environment variables; the server injects auth automatically. Do not
append ?apikey=... or &X-Api-Key=....
-
service names are exact. Use sonarr, radarr, prowlarr, tautulli,
overseerr, sabnzbd, qbittorrent, plex, jellyfin, bazarr, tracearr.
An unknown name returns a structured unknown_service error listing the
configured names.
-
API paths are service-version-specific. Sonarr/Radarr use /api/v3/;
Prowlarr/Overseerr use /api/v1/; Tautulli uses query params (/api/v2?cmd=...).
Generated callables already encode the right path — prefer them over raw api_*.
-
Discovery is the fastest diagnostic. codemode.search("queue") lists every
matching callable with its fully-qualified path; codemode.describe(path) shows
the signature or response type. Use them before guessing op names.