Skip to main content
Glama
bestimmaa
by bestimmaa

roon-mcp

npm version

An MCP server to control the Roon music player, so an AI agent can find music and start playback. Built on the official Roon Extension API (node-roon-api + node-roon-api-transport / node-roon-api-browse).


"Play a female vocal trance mix on Roon (Zone: This Computer) — 2000s Dutch trance vibes, the kind of set you'd hear at Amnesia in its prime. Start with something like 4 Strings' Take Me Away."

Claude searches across your library and streaming, hand-picks the tracks, and queues them — all from a single natural-language prompt.


Requirements

  • Node.js 20+

  • A running Roon Core on the local network to pair with

  • git on the install host — the node-roon-api* dependencies are published on GitHub (not npm) and are fetched via git URLs during install

Related MCP server: lms-mcp

MCP Client Configuration

Add this to your MCP client config. npx fetches the package on first run:

{
  "mcpServers": {
    "roon": {
      "command": "npx",
      "args": ["-y", "roon-mcp"],
      "env": { "ROON_DEFAULT_ZONE": "Office" }
    }
  }
}

On first launch, open Roon → Settings → Extensions and enable Roon MCP to pair. Pairing status is logged to stderr; stdout is reserved for the MCP protocol. The pairing token is persisted to ~/.config/roon-mcp/config.json (override with ROON_MCP_CONFIG), so the extension stays authorized across restarts rather than appearing as a new "Discovered" entry each time.

Global install (optional)

npm install -g roon-mcp
{
  "mcpServers": {
    "roon": {
      "command": "roon-mcp",
      "env": { "ROON_DEFAULT_ZONE": "Office" }
    }
  }
}

Configuration

Env var

Purpose

ROON_DEFAULT_ZONE

Optional fallback target for play_now / enqueue_and_play when no zoneId is given — a zone/output id or a display-name substring. If unset, the server falls back to the only zone, an Office zone, or the currently-playing zone; if it still can't decide it returns ZONE_AMBIGUOUS so the agent can ask.

ROON_HOST

Optional direct-connection host for the Core's API, bypassing SOOD multicast discovery — for VLAN-segmented networks, VPNs, or containers where discovery doesn't survive. Unset (default) uses discovery as before.

ROON_PORT

WebSocket port of the Core's API when ROON_HOST is set. Defaults to 9330.

ROON_MCP_CONFIG

Optional path for the persisted pairing token. A value ending in .json is the file itself; anything else is a directory to hold config.json. Defaults to $XDG_CONFIG_HOME/roon-mcp/config.json (i.e. ~/.config/roon-mcp/config.json).

Tools

Tool

Purpose

list_zones()

List playable zones/outputs (id, name, state, output ids).

search_music({ query, type?, limit?, includeStreaming? })

Resolve a text query into ranked browse candidates (opaque, session-scoped item keys). type (artist/album/track/genre/playlist/radio) restricts the category; for non-genre types an empty typed search broadens to all categories. See Streaming search for type:"genre"/type:"artist" and includeStreaming.

get_tracks_for({ itemKey, limit? })

Expand an artist/album/genre/playlist candidate into concrete playable tracks.

play_now({ zoneId?, itemKey, shuffle?, addToQueue? })

Play or queue one search candidate. By default replaces the queue and starts immediately. Pass addToQueue: true to append to the existing queue instead. zoneId optional (defaults as above).

enqueue_and_play({ zoneId?, itemKeys, shuffle? })

Build an ad-hoc queue from curated item keys and start it (replaces the zone's queue); reports queued/skipped.

now_playing({ zoneId? })

Snapshot of the zone's current track — state, title, artist, album, seek position. title/artist/album are undefined when nothing is playing.

control_playback({ zoneId?, action })

Run a transport verb: pause / resume / next / previous / stop / playpause (toggle play/pause).

seek({ zoneId?, seconds, mode? })

Seek within the current track. mode:"absolute" (default) seeks to seconds (0 = start); "relative" moves by seconds (negative skips backward).

set_loop({ zoneId?, mode })

Set the loop/repeat mode: off / all / one.

set_volume({ zoneId?, level })

Set the zone's volume to level percent (0–100). Rescales to each output's native range; incremental outputs are reported as skipped.

mute({ zoneId?, muted })

Mute (muted: true) or unmute (muted: false) every output in the zone.

pause_all()

Pause every zone on the Core in one call. Resume per zone with control_playback.

mute_all({ muted })

Mute (muted: true) or unmute (muted: false) every mutable output on the Core.

set_auto_radio({ zoneId?, enabled })

Turn Roon Radio on/off for a zone: enabled: true appends similar tracks once the queue ends, false stops playback at queue end.

Streaming search (genre and artist)

  • Genre search fuzzy-matches what you type — "psychedelic trance" will find the right genre even if the name isn't exact. By default results come from your library.

  • includeStreaming: true (on type:"genre" or type:"artist") extends the search to your streaming service, so you can play artists or genres that aren't in your local collection.

Notes

  • After starting playback, now_playing reflects the track that just started, not whatever was playing before.

  • Volume is set as a percentage (0–100) and works correctly across grouped zones.

Concurrent instances

Roon pairs only one running instance of an extension at a time. Since every roon-mcp process shares the same identity and persisted pairing token, starting a second instance (a second MCP client session, or an orphaned process from a closed terminal) no longer fights the first one for the pairing slot — instead, whichever instance starts first claims a lock file (instance.lock, next to config.json) and every other instance fails fast with a CORE_PAIRING_HELD error naming the pid that holds it, instead of flapping between paired/unpaired.

MCP clients don't always reap server processes when a session ends, so orphaned roon-mcp instances can pile up and hold the lock long after their terminal is gone. If you hit CORE_PAIRING_HELD unexpectedly, clear them out:

pkill -f roon-mcp

The next instance you start will then acquire the lock normally.

Assumptions

  • Core language: English. Category/action label matching (Artists, Play Now, Top Tracks, …) assumes an English Core.

  • Sources: local library + your configured streaming service. The server goes through Roon's Browse API, which routes to whatever streaming service you have set up in Roon (Tidal, Qobuz, etc.). Developed and tested against Tidal — other services should work but are untested. Results differ on a local-only Core.

  • Queue: replace. enqueue_and_play starts a fresh queue rather than adding to whatever is already playing.

  • Curation is agent-side. Dedupe / cap-per-artist / ordering / trimming stay in the agent; the server has no curation logic.

There is no save_playlist tool: Roon exposes no official playlist-write service, so durable playlists are out of scope. Curated playback is delivered by enqueue_and_play (an ad-hoc, in-the-moment queue).

Logging

Every Roon API call (browse, load, get_zones, control, change_volume, mute, seek, change_settings, pause_all, mute_all, …) emits one structured line to stderr (stdout stays reserved for MCP JSON-RPC):

[roon-call] {"t":"2026-06-19T18:00:00.000Z","lvl":"info","op":"browse","ms":12,"params":{"hierarchy":"search","item_key":"…"},"result":{"action":"list","count":7}}

Failures log lvl:"error" with the mapped error code, and retries surface as repeated lines for the same op. Grep stderr for [roon-call] to trace a flow.

Development

npm install              # pulls node-roon-api packages from RoonLabs' GitHub
npm run build
npm test                 # builds, then runs node:test
npm run dev              # tsc --watch
npm run typecheck

Run the built server directly:

npm start

Integration smoke test

scripts/integration.mjs spawns the built server over stdio (like a real MCP client) and runs list → search → expand against your Core. Audible steps are opt-in so it never blasts music by accident:

npm run build
node scripts/integration.mjs                 # list_zones + search only (read-only)
ROON_PLAY=1 node scripts/integration.mjs     # also play_now a shuffled genre
ROON_ENQUEUE=1 node scripts/integration.mjs  # also enqueue a few curated tracks
# overrides: ROON_ZONE="Office"  ROON_QUERY="Tycho"

Enable Roon MCP under Roon → Settings → Extensions on the first run (the script retries list_zones for ~45s while it waits to pair).

Releasing

npm run release -- <patch|minor|major>

Requires a clean worktree on main and a matching ## [<version>] entry in CHANGELOG.md. The script runs the tests, bumps the version, tags the commit, and verifies the package with npm pack. It then prints the push and npm publish commands to run.

License

MIT

Available Tools

9 tools
control_playbackRun a Roon transport verb (pause/resume/next/previous/stop)A

Use this for the common transport verbs — pause, resume, skip, next track, previous track, stop (e.g. "pause", "pause the kitchen", "skip", "next track", "next song", "play the next song", "resume", "resume in the office", "stop", "stop the music"). Runs one transport verb against the resolved zone. For "louder"/"softer" without a number, call now_playing first to read the current state, then set_volume with a target percent — this tool is verbs only. zoneId is optional and resolves like now_playing. Returns the resolved zoneId, the action taken, and the resulting playback state.

ParametersJSON Schema
NameRequiredDescriptionDefault
zoneIdNoTarget zone id or output id from list_zones. Omit to use ROON_DEFAULT_ZONE or fall back automatically (see now_playing).
actionYesTransport verb to run: 'pause' to stop playback, 'resume' to restart it, 'next' to skip to the next track, 'previous' to go back, 'stop' to release the audio device.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries burden. Describes that it runs a verb on a resolved zone and returns zoneId, action, and playback state. Also explains zoneId resolution. Could mention idempotency or error handling, but overall transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One dense paragraph with front-loaded purpose, examples, and caveats. Efficient but some redundancy in listing examples, still concise and structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given moderate complexity, no output schema, and full param coverage, description adequately explains what it does, return values, and usage boundaries. Missing error scenarios but not required.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage 100%, baseline 3. Description adds value by explaining zoneId resolution behavior and giving natural language examples for action values, helping map user intent to enum.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states it runs transport verbs (pause/resume/next/previous/stop) against a zone. Distinguishes from sibling tools like set_volume by explicitly saying 'verbs only' and directing volume adjustments to now_playing+set_volume.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says when to use (common transport verbs) and when not (for 'louder'/'softer' without number, use now_playing then set_volume). Provides examples and conditions, though does not directly name all sibling alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

enqueue_and_playBuild and start a curated Roon queueA

Use this when the user wants a custom lineup — a mix of artists, a shuffled selection across albums, a hand-picked set of tracks, or any time "queue", "setlist", "mix of", or "play these in order" comes up (e.g. "queue up five Tycho tracks then some Boards of Canada", "shuffle 10 ambient tracks", "build a set: artist A, then B, then C"). Builds an ad-hoc queue from an ordered list of curated item keys and starts playback in the target zone. This replaces the zone's current queue: the first playable item starts immediately (Play Now), the rest are appended in order. Pass itemKeys from recent get_tracks_for / search_music results (use them promptly — they are session-scoped). zoneId is optional (omit to use the default zone; see play_now). Optionally shuffle. Returns a PlaybackResult with queued/skipped counts so you can backfill skipped items.

ParametersJSON Schema
NameRequiredDescriptionDefault
zoneIdNoTarget zone id or output id from list_zones. Omit to use the default zone (see play_now).
itemKeysYesOrdered item keys to queue, from recent get_tracks_for or search_music results (tracks, albums, artists, etc.).
shuffleNoShuffle the queue; omit to leave the zone's setting unchanged.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. Discloses that it replaces the queue, starts playback immediately, and itemKeys are session-scoped and should be used promptly. Returns PlaybackResult with queued/skipped counts. Does not mention authorization or rate limits, but given no annotations, this is sufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured and front-loaded with purpose and usage. Contains two sentences of purpose, then parameter details. Efficiently packs context without redundancy. Could benefit from slight formatting (e.g., bullet points) but is concise and clear.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 3-parameter tool with no output schema, the description covers purpose, usage, parameter semantics, and behavioral traits. It explains what is returned (PlaybackResult with counts). Missing error handling details, but overall adequate given the tool's complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but description adds significant meaning: explains that zoneId can be omitted for default zone (references play_now), clarifies itemKeys come from get_tracks_for/search_music and are session-scoped, and explains shuffle behavior ('omit to leave unchanged'). Adds value beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states the tool builds an ad-hoc queue and starts playback. Uses specific verb 'builds' and 'starts', resource is a curated Roon queue. Distinguishes from siblings by giving usage cues like 'queue', 'setlist', 'mix' and contrasting with play_now for default zone.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly tells when to use ('when the user wants a custom lineup...') with clear examples. Mentions that it replaces the current queue, first item plays now, rest appended. Also provides context on zoneId optionality and references alternative tool play_now for default zones.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_tracks_forExpand a Roon item into tracksA

Use this after search_music when the user wants a concrete list of songs — to preview tracks, build a queue, or pick one to start with (e.g. "what tracks are on this album?", "give me 5 tracks of Dark Ambient", "what's on this playlist?"). Expands an artist, album, genre, or playlist candidate into concrete playable tracks. Pass an itemKey from a recent search_music result. Returns track candidates with session-scoped item keys (use them promptly with enqueue_and_play). Non-expandable items return empty tracks with a skipped reason rather than an error.

ParametersJSON Schema
NameRequiredDescriptionDefault
itemKeyYesItem key to expand — from a recent search_music candidate (artist, album, genre, or playlist).
limitNoMax tracks to return (default 10).

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full burden and does an excellent job. It discloses that the tool returns track candidates with session-scoped item keys (which expire soon), that non-expandable items return empty tracks with a 'skipped reason' rather than throwing an error, and that results should be used promptly with enqueue_and_play. This provides critical behavioral context for safe invocation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and well-structured. It starts with the primary use case and action, then explains the input source (itemKey from search_music), then describes the output and error behavior. Every sentence adds value, and there is no redundancy or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (expanding items to tracks) and absence of an output schema, the description provides sufficient context: input type, output nature (track candidates with session-scoped keys), lifecycle hint (use promptly), error handling (empty tracks with skipped reason), and limit parameter. It feels complete and leaves no major questions.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Both parameters have descriptions in the schema (100% coverage), so the baseline is 3. The description adds value by reinforcing that itemKey comes from a recent search_music candidate and clarifying the default limit (10). This extra context helps the agent understand the usage pattern, earning a 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: expanding a Roon item (artist, album, genre, or playlist) into a concrete list of playable tracks. It uses specific verb+resource ('expand a Roon item into tracks') and provides concrete use case examples ('what tracks are on this album?'). It distinguishes itself from sibling tools like search_music (which returns candidates) and enqueue_and_play (which plays tracks).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use the tool: 'after search_music when the user wants a concrete list of songs'. It provides clear context for use cases like previewing tracks, building a queue, or picking a track. It also implies when not to use it (e.g., not for playback control) and indirectly points to alternatives like enqueue_and_play for playing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_zonesList Roon zonesA

Use this when the user asks which rooms, speakers, or outputs Roon can play to, or before starting playback when the target zone is unclear (e.g. "what zones are on?", "play in the kitchen", "which speaker is in the office?"). Lists every zone/output the paired Core exposes with its id, display name, current playback state, and output ids. Call this first if no zone is obvious and ROON_DEFAULT_ZONE is not set.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, but description discloses what is returned (id, display name, playback state, output ids) and implies read-only operation. Could be more explicit about non-destructive nature, but sufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences: first gives usage context, second details output. Efficient and well-structured with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no parameters and no output schema, description fully covers what the tool does and when to use it. Complete for a list tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

No parameters in schema, so baseline 4. Description adds no parameter info but that is unnecessary; it mentions return fields which are useful context.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it lists zones/outputs from the Roon Core and gives specific use cases like asking which rooms or before playback when target is unclear, distinguishing it from sibling tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says when to use it (when user asks about zones, before playback if unclear) and implies when not needed (if default zone set). Does not explicitly list alternatives but context is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

muteMute or unmute a Roon zoneA

Use this when the user wants to mute or unmute a zone (e.g. "mute", "mute the kitchen", "unmute", "silence the office", "stop the noise"). Mutes (or unmutes) every output in the resolved zone. zoneId is optional and resolves like now_playing. muted: true mutes, muted: false unmutes.

ParametersJSON Schema
NameRequiredDescriptionDefault
zoneIdNoTarget zone id or output id from list_zones. Omit to use ROON_DEFAULT_ZONE or fall back automatically.
mutedYes`true` to mute, `false` to unmute.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It states that muting affects every output in the zone and explains zoneId resolution. This is sufficient for a simple action, though it lacks detail on side effects or error cases.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three sentences, front-loaded with purpose, and every sentence adds necessary context. No unnecessary words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema and simple action, the description adequately covers usage and behavior. It could mention that no return value is expected, but it's not critical for a mute operation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but the description adds value by clarifying zoneId resolves like now_playing and defining the effect of the muted boolean. This goes beyond the schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool mutes or unmutes a Roon zone, with examples of user queries. It distinguishes from sibling tools like set_volume by focusing on mute/unmute rather than volume control.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says 'Use this when the user wants to mute or unmute a zone' and provides example utterances. It does not explicitly mention when not to use it or alternatives, but the context is clear enough.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

now_playingGet the currently-playing track in a Roon zoneA

Use this when the user asks what is currently playing, what song is on, who's singing, what just started, or wants a snapshot before skipping or pausing (e.g. "what's playing?", "what's playing in the kitchen?", "who's this?", "what's the current track?", "what song is this?"). Returns a structured snapshot: zone id and name, playback state (playing/paused/loading/stopped), title, artist, album, and the current seek position when available. title/artist/album are undefined when nothing is playing. Call this before pause/skip/volume changes when the user hasn't named a zone — it confirms where to act and what the state is. zoneId is optional: omit to use ROON_DEFAULT_ZONE, or fall back to the only zone / an "Office" zone / the currently-playing zone; if it still can't decide it returns ZONE_AMBIGUOUS so the agent can ask the user or call list_zones.

ParametersJSON Schema
NameRequiredDescriptionDefault
zoneIdNoTarget zone id or output id from list_zones (e.g. an id, or a name substring like 'Office'). Omit to use ROON_DEFAULT_ZONE or fall back automatically.

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description bears full burden. Describes return structure (zone id/name, playback state, title/artist/album, seek position), undefined values when nothing playing, and ZONE_AMBIGUOUS response. Lacks explicit statement about destructiveness, but context implies read-only.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is relatively long but well-structured, starting with usage scenarios followed by return details. Every sentence adds value; however, it could be slightly more concise without losing clarity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given one optional parameter, no output schema, and no annotations, the description fully covers purpose, usage, parameter semantics, return structure, error handling, and side effects (none). It is complete for an agent to select and invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, explaining zoneId is optional. Description adds meaningful detail: fallback logic using ROON_DEFAULT_ZONE, ambiguous zone resolution, and error case. This adds value beyond the schema alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool gets the currently-playing track, with explicit example queries (e.g., 'what's playing?', 'who's this?') and distinguishes from sibling tools like control_playback by focusing on snapshot rather than control.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use instructions: user asks what's playing, before pause/skip/volume changes when zone isn't named. Also explains fallback behavior and error case (ZONE_AMBIGUOUS) with a suggestion to call list_zones.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

play_nowPlay an item now in a Roon zoneA

Use this when the user wants one specific thing playing right now — an album, artist, playlist, genre mix, or single track (e.g. "play Tycho", "put on In Rainbows", "start some Psytrance in the office", "play that track"). Immediately plays a single search candidate in the target zone and replaces whatever was queued. Pass an itemKey from a recent search_music (or get_tracks_for) result — item keys are session-scoped, so use a fresh one. zoneId is optional: omit it to use ROON_DEFAULT_ZONE, or fall back to the only zone / an "Office" zone / the currently-playing zone; if it still can't decide it returns ZONE_AMBIGUOUS so the agent can ask the user or call list_zones. Optionally shuffle. Returns a PlaybackResult.

ParametersJSON Schema
NameRequiredDescriptionDefault
zoneIdNoTarget zone id or output id from list_zones (e.g. an id, or a name substring like 'Office'). Omit to use ROON_DEFAULT_ZONE or fall back automatically.
itemKeyYesWhat to play — item key from a recent search_music or get_tracks_for result (album, artist, playlist, genre, track, etc.).
shuffleNoShuffle the selection when starting playback.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden. It discloses immediate playback, queue replacement, itemKey session scope, zone selection fallback, optional shuffle, and return type. No mention of destructive or rate-limiting behaviors, but core behavioral traits are well covered.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, well-structured paragraph with no wasted words. Each sentence adds essential information in a logical order: purpose, behavior, parameter details, optionality, and return value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema, the description mentions the return type 'PlaybackResult'. All three parameters are adequately described in the description or schema. The tool's role among siblings is clear. Minor gap: no mention of error cases beyond ZONE_AMBIGUOUS.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds significant value for 'itemKey' (session-scoped, source) and 'zoneId' (fallback logic). 'shuffle' is straightforward. This exceeds the baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb ('play now'), specific resource ('a single search candidate in the target zone'), and distinguishes from siblings by emphasizing immediate playback and queue replacement. Examples further clarify the purpose.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says 'Use this when the user wants one specific thing playing right now' and provides examples. It is contrasted with alternatives implicitly via the 'replaces whatever was queued' behavior, but no explicit 'when not to use' is given. The zone fallback logic is clearly explained.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_musicSearch Roon musicA

Use this when the user names music to find — an artist, album, track, playlist, radio station, or genre (e.g. "find Tycho", "look up the album In Rainbows", "play some Psytrance", "anything by Ryuichi Sakamoto?"). Resolves a free-text query into ranked Roon browse candidates. Optionally restrict to one item type (artist, album, track, genre, playlist, radio); for non-genre types, an empty typed search broadens to all categories. type:"genre" is special — genres don't appear in Roon's flat search, so the server walks the dedicated Genres tree and returns the nearest-match genre nodes (with parent path in the subtitle) without broadening; e.g. "Psychedelic Trance" yields "Psytrance"/"Trance". Set includeStreaming:true (only meaningful for type:"genre") to also pull a track mix from streaming services (e.g. TIDAL): the server takes the genre-relevant albums and samples tracks across them, so library genre nodes come first and ready-to-play streaming tracks are appended after. Returns opaque, session-scoped item keys for use by the playback tools — pair with get_tracks_for to expand, then play_now or enqueue_and_play.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesWhat to search for — an artist, album, track, playlist, radio, or genre name (e.g. 'Tycho', 'In Rainbows', 'Dark Ambient').
typeNoRestrict the search to one item type. Omit to broaden across all non-genre categories. Use 'genre' for music-genre lookups.
limitNoMax candidates to return (default 10).
includeStreamingNoOnly for type 'genre': also pull a track mix from streaming services (e.g. TIDAL). Library genre nodes come first, then sampled streaming tracks. Default false (library only).

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so description fully covers behavior: explains free-text resolution, optional type restriction, special genre tree walking, broadening on empty type, streaming option scope, and that output is opaque session-scoped keys. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is detailed and well-structured, front-loading purpose then special cases. Slightly verbose but every sentence adds value; could be marginally tightened without losing clarity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 4 parameters, no output schema, and no annotations, the description fully informs an agent: what it does, how to use each parameter, special behaviors, and expected output (opaque keys for playback). Complete for effective invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but description adds significant nuance: explains genre type is special, empty type broadens, includeStreaming only works with genre and appends streaming tracks, and output is opaque keys. Adds meaning beyond schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool searches Roon music by free-text query, listing specific item types (artist, album, etc.), and distinguishes it from sibling playback tools by mentioning it returns item keys for use with them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Use this when the user names music to find', and instructs to pair with get_tracks_for, play_now, or enqueue_and_play, providing clear guidance on when to use this tool vs alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_volumeSet a Roon zone's volume to a target percentA

Use this when the user wants to change how loud a zone is — turn it up, turn it down, set the volume, or pick a level for a specific room (e.g. "turn it up to 60", "volume to 30 in the office", "set the kitchen to 50", "louder" — see note). Sets the zone's volume to level percent (0 = silent, 100 = max). The server rescales to each output's native range, so a single value works across mixed devices in a grouped zone. Incremental outputs (IR blasters with no numeric range) are reported as skipped rather than guessed at. zoneId is optional and resolves like now_playing. NOTE on relative changes ("louder" / "softer" without a number): this tool is absolute — read the current state with now_playing isn't enough on its own (volume isn't exposed there), so for relative changes, ask the user for a target percent or apply a reasonable default delta.

ParametersJSON Schema
NameRequiredDescriptionDefault
zoneIdNoTarget zone id or output id from list_zones. Omit to use ROON_DEFAULT_ZONE or fall back automatically.
levelYesTarget volume in percent (0 = silent, 100 = max). Mapped to each output's native range.

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Discloses key behaviors: server rescales to native range, incremental outputs are reported as skipped. No annotations provided, so description carries the burden. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is concise and front-loaded with purpose. Includes a structured note on relative changes. Could use clearer sectioning, but overall efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers main functionality, zone resolution, and special case of incremental outputs. No output schema but behavior is sufficiently described. Minor omission: error handling on invalid inputs.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but description adds context: zoneId resolution like now_playing, and note on relative changes for the level parameter. Adds practical usage value beyond schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Title and description clearly state the tool's purpose: setting a Roon zone's volume to a target percent. It distinguishes from sibling tools like mute (mute/unmute) and control_playback (play/pause) by focusing on absolute volume setting.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use: 'when the user wants to change how loud a zone is'. Notes relative changes require a target percent or default delta, guiding against misuse. However, it does not explicitly mention when not to use or list specific alternative tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 9 tool updatesv0.1.0
    • First observedcontrol_playback
    • First observedenqueue_and_play
    • First observedget_tracks_for
    • First observedlist_zones
    • First observedmute
    • First observednow_playing
    • First observedplay_now
    • First observedsearch_music
    • First observedset_volume

TDQS

A4.5/5.0

Scored across 9 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: control_playback handles transport commands, play_now starts a single item, enqueue_and_play builds a queue, search_music finds candidates, get_tracks_for expands them, etc. No two tools overlap in functionality.

Naming Consistency4/5

Tool names follow a mostly consistent snake_case pattern with verbs leading (list_zones, set_volume, get_tracks_for). However, 'now_playing' and 'play_now' are verb-phrase based, and 'mute' is a single verb, introducing minor inconsistency.

Tool Count5/5

With 9 tools, the server is well-scoped for a Roon music control system. Each tool handles a essential aspect (search, playback, queue, volume, zone management) without being bloated or underpowered.

Completeness4/5

Core workflows are covered: search, browse, play, control, volume, and state. Minor gaps exist, such as no playlist management or fine-grained queue manipulation (e.g., insert, remove), but these are not critical for basic operation.

Maintenance

ActivityMaintained
ResponsivenessUnresponsive

Related MCP Connectors

Related MCP Servers