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
serviceparam):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)andcodemode.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
// One yarr call: discover, then health-check across services.
async () => {
const status = await sonarr.get_system_status(); // generated op (live)
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(); // list all series
const queue = await sonarr.get_queue(); // download queue
const movies = await radarr.get_movie(); // list all movies
// Trigger a search command (generated POST op; runs immediately, no confirm):
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(); // who's watching
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(); // currently playing
const history = await tautulli.stats_history({ length: 20 });
const sabQueue = await sabnzbd.download_queue(); // SABnzbd queue
const torrents = await qbittorrent.download_queue(); // qBittorrent torrents
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 () => {
// Tautulli is query-param driven:
const homeStats = await api.tautulli.get("/api/v2?cmd=get_home_stats");
// Plex libraries:
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 nomcp__rustarr__<service>tools — pass acodescript toyarrand reach services via per-service callables,api.<service>, orcallTool.api_get/api_post/api_putrequire 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_putrun without confirmation. Destructive deletes (DELETE ops,api_delete, curated deletes likedownload_remove) are refused mid-script in Code Mode — use the CLI with--confirm, or setRUSTARR_ALLOW_DESTRUCTIVEon 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=....servicenames are exact. Usesonarr,radarr,prowlarr,tautulli,overseerr,sabnzbd,qbittorrent,plex,jellyfin,bazarr,tracearr. An unknown name returns a structuredunknown_serviceerror 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 rawapi_*.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.