deep-roon-mcp 0.3.6 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/deep-roon-mcp.js +63 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ server of its own and sends nothing anywhere else. What you ask goes to your MCP
|
|
|
22
22
|
|
|
23
23
|
## Status
|
|
24
24
|
|
|
25
|
-
Version 0.
|
|
25
|
+
Version 0.3: fifty-three tools. Qobuz, TIDAL or both — whatever is signed in on your Core.
|
|
26
26
|
|
|
27
27
|
**Reading** — changes nothing in Roon:
|
|
28
28
|
|
|
@@ -38,7 +38,7 @@ Version 0.2: fifty-one tools. Qobuz, TIDAL or both — whatever is signed in on
|
|
|
38
38
|
| `roon_status` | How Deep Roon MCP stands: Core, pairing, internal protocol, self-test, newer version |
|
|
39
39
|
| `roon_zones` | Roon's zones and groups |
|
|
40
40
|
| `roon_now_playing` | What plays in each zone, with the volume |
|
|
41
|
-
| `roon_queue` | A zone's queue |
|
|
41
|
+
| `roon_queue` | A zone's queue, numbered (1 is what plays) |
|
|
42
42
|
| `roon_cover` | The cover of what plays, as an image |
|
|
43
43
|
| `roon_history` | Roon's play history, newest first |
|
|
44
44
|
| `roon_top` | Most played albums, artists or tracks over your whole history |
|
|
@@ -68,6 +68,7 @@ Version 0.2: fifty-one tools. Qobuz, TIDAL or both — whatever is signed in on
|
|
|
68
68
|
| `roon_seek` | A position in the playing track |
|
|
69
69
|
| `roon_volume` | A zone's volume, mute |
|
|
70
70
|
| `roon_settings` | Shuffle, repeat, Roon Radio |
|
|
71
|
+
| `roon_queue_jump`, `roon_queue_remove` | Jump to an item of the queue, take items out of it |
|
|
71
72
|
| `roon_queue_clear` | Clear what comes after the playing track (or everything) |
|
|
72
73
|
| `roon_transfer` | Move what plays, with its queue, to another zone |
|
|
73
74
|
| `roon_group`, `roon_ungroup` | Group zones, and undo it |
|
|
@@ -104,7 +105,7 @@ The settings (Claude Desktop › Settings › Extensions › Deep Roon MCP):
|
|
|
104
105
|
| Setting | What it does |
|
|
105
106
|
|---|---|
|
|
106
107
|
| Roon Core address | Empty: found on your network. Fill in the Core's address for another subnet or a private VPN (Tailscale) |
|
|
107
|
-
| Essentials only | Seventeen tools instead of fifty-
|
|
108
|
+
| Essentials only | Seventeen tools instead of fifty-three, for small models |
|
|
108
109
|
| Use Roon's internal protocol | Off: only the official API — zones, playback, volume, grouping; no search, reviews, Focus or history |
|
|
109
110
|
|
|
110
111
|
### Other clients
|
package/dist/deep-roon-mcp.js
CHANGED
|
@@ -15525,7 +15525,7 @@ var require_lib2 = __commonJS({
|
|
|
15525
15525
|
var version2;
|
|
15526
15526
|
var init_package = __esm({
|
|
15527
15527
|
"package.json"() {
|
|
15528
|
-
version2 = "0.3.
|
|
15528
|
+
version2 = "0.3.8";
|
|
15529
15529
|
}
|
|
15530
15530
|
});
|
|
15531
15531
|
|
|
@@ -16353,7 +16353,12 @@ async function bio(key, max, wiki) {
|
|
|
16353
16353
|
for (const n of names2) for (const x of list(n)) {
|
|
16354
16354
|
const o = obj(c, x);
|
|
16355
16355
|
const name = field2(o, "Name");
|
|
16356
|
-
if (name && !out.some((y) => y.name === name))
|
|
16356
|
+
if (name && !out.some((y) => y.name === name)) {
|
|
16357
|
+
const r = { key: String(field2(o, "PerformerId") ?? "") || void 0, name };
|
|
16358
|
+
const img2 = imageKey(c, o, "Photo") ?? imageKey(c, o, "Avatar");
|
|
16359
|
+
if (img2) r.image = img2;
|
|
16360
|
+
out.push(r);
|
|
16361
|
+
}
|
|
16357
16362
|
}
|
|
16358
16363
|
return out.length ? out.slice(0, 12) : void 0;
|
|
16359
16364
|
}, "people");
|
|
@@ -49297,7 +49302,15 @@ async function history(limit3) {
|
|
|
49297
49302
|
closeSoon("after the history");
|
|
49298
49303
|
return {
|
|
49299
49304
|
total: out.total ?? null,
|
|
49300
|
-
plays: out.events.map((e) => ({
|
|
49305
|
+
plays: out.events.map((e) => ({
|
|
49306
|
+
at: localTime(e.playedAt),
|
|
49307
|
+
title: e.title,
|
|
49308
|
+
artist: clean(e.artist),
|
|
49309
|
+
album: e.album ?? void 0,
|
|
49310
|
+
played_pct: e.completionPct ?? void 0,
|
|
49311
|
+
track_key: e.roonTrackId ?? void 0
|
|
49312
|
+
}))
|
|
49313
|
+
// for roon_play_tracks (13 Oct 2026, Rosso's history)
|
|
49301
49314
|
};
|
|
49302
49315
|
});
|
|
49303
49316
|
}
|
|
@@ -49987,6 +50000,40 @@ function queueRow(c, oid) {
|
|
|
49987
50000
|
return r;
|
|
49988
50001
|
}
|
|
49989
50002
|
__name(queueRow, "queueRow");
|
|
50003
|
+
async function queueItemAt(c, zoneName, n) {
|
|
50004
|
+
const { z: z2, q } = queueOf(c, zoneName);
|
|
50005
|
+
const refs = await queueItems(c, q);
|
|
50006
|
+
const at = Math.max(0, field2(c.graph.getObject(q.oid), "CurrentItemIndex") ?? 0);
|
|
50007
|
+
const oid = refs[at + n - 1];
|
|
50008
|
+
if (oid === void 0) throw new RoonError(`The queue of ${z2.name} has no item ${n} (${Math.max(0, refs.length - at)} from here)`, "invalid");
|
|
50009
|
+
return { z: z2, q, oid, refs, at };
|
|
50010
|
+
}
|
|
50011
|
+
__name(queueItemAt, "queueItemAt");
|
|
50012
|
+
async function queueJump(zoneName, n) {
|
|
50013
|
+
const r = await withCore(async (c) => {
|
|
50014
|
+
const { z: z2, q, oid } = await queueItemAt(c, zoneName, n);
|
|
50015
|
+
c.remoting.callMethodNoReply(q.oid, formatMethodSignature("Queue", "PlayFromHere", [{ type: "TransportItem", name: "item" }]), buildArgs([Arg.ref(oid)]));
|
|
50016
|
+
log(`queue_jump: ${z2.name} \u2192 item ${n}`);
|
|
50017
|
+
return { zone: z2.name, n };
|
|
50018
|
+
});
|
|
50019
|
+
return { ok: true, ...r, ...await started2(r.zone, PRIORITY["now"]) };
|
|
50020
|
+
}
|
|
50021
|
+
__name(queueJump, "queueJump");
|
|
50022
|
+
async function queueRemove(zoneName, ns) {
|
|
50023
|
+
return withCore(async (c) => {
|
|
50024
|
+
const { z: z2, q } = queueOf(c, zoneName);
|
|
50025
|
+
const refs = await queueItems(c, q);
|
|
50026
|
+
const at = Math.max(0, field2(c.graph.getObject(q.oid), "CurrentItemIndex") ?? 0);
|
|
50027
|
+
const oids = [...new Set(ns)].map((n) => refs[at + n - 1]).filter((o) => o !== void 0);
|
|
50028
|
+
if (!oids.length) throw new RoonError("No such items in the queue", "invalid");
|
|
50029
|
+
c.remoting.callMethodNoReply(q.oid, formatMethodSignature("Queue", "RemoveItems", [{ type: "IEnumerable<TransportItem>", name: "items" }]), buildArgs([Arg.collection(oids.map((o) => buildArgs([Arg.ref(o)])))]));
|
|
50030
|
+
log(`queue_remove: ${z2.name} \u2212 ${oids.length} item(s)`);
|
|
50031
|
+
await wait(300);
|
|
50032
|
+
const after = await queueItems(c, q);
|
|
50033
|
+
return { ok: true, zone: z2.name, removed: oids.length, left: Math.max(0, after.length - at) };
|
|
50034
|
+
});
|
|
50035
|
+
}
|
|
50036
|
+
__name(queueRemove, "queueRemove");
|
|
49990
50037
|
async function queue2(zoneName, limit3) {
|
|
49991
50038
|
return withCore(async (c) => {
|
|
49992
50039
|
const { z: z2, q } = queueOf(c, zoneName);
|
|
@@ -50143,6 +50190,7 @@ __name(cover2, "cover");
|
|
|
50143
50190
|
// src/tools/control.ts
|
|
50144
50191
|
init_official();
|
|
50145
50192
|
init_session();
|
|
50193
|
+
var CHANGES_QUEUE = { readOnlyHint: false, destructiveHint: true, openWorldHint: false };
|
|
50146
50194
|
var PLAYS = { readOnlyHint: false, destructiveHint: false, openWorldHint: false };
|
|
50147
50195
|
var REMOVES = { readOnlyHint: false, destructiveHint: true, openWorldHint: false };
|
|
50148
50196
|
var zone = external_exports.string().describe("A Roon zone name (see roon_zones).");
|
|
@@ -50195,6 +50243,18 @@ function registerControlTools(server, internal = true) {
|
|
|
50195
50243
|
inputSchema: { zone, limit: external_exports.number().int().min(1).max(200).optional() },
|
|
50196
50244
|
annotations: { readOnlyHint: true, openWorldHint: false }
|
|
50197
50245
|
}, ({ zone: zn, limit: limit3 }) => run(() => queue2(zn, limit3 ?? 50)));
|
|
50246
|
+
reg("roon_queue_jump", {
|
|
50247
|
+
title: "Jump in the queue",
|
|
50248
|
+
description: "Play an item of the zone's queue now (`n` as roon_queue numbers them: 1 is what plays, 2 the next \u2026); what came before it is done. Only on an explicit request.",
|
|
50249
|
+
inputSchema: { zone, n: external_exports.number().int().min(1) },
|
|
50250
|
+
annotations: PLAYS
|
|
50251
|
+
}, ({ zone: zn, n }) => run(() => queueJump(zn, n)));
|
|
50252
|
+
reg("roon_queue_remove", {
|
|
50253
|
+
title: "Remove from the queue",
|
|
50254
|
+
description: "Take items out of the zone's queue, by their numbers from roon_queue (`ns`, e.g. [3, 4]). Only on an explicit request.",
|
|
50255
|
+
inputSchema: { zone, ns: external_exports.array(external_exports.number().int().min(2)).min(1).max(100).describe("Positions from roon_queue (2 and up: 1 is what plays).") },
|
|
50256
|
+
annotations: CHANGES_QUEUE
|
|
50257
|
+
}, ({ zone: zn, ns }) => run(() => queueRemove(zn, ns)));
|
|
50198
50258
|
reg("roon_queue_clear", {
|
|
50199
50259
|
title: "Clear the queue",
|
|
50200
50260
|
description: "Clear the queue of a Roon zone. By default the track that plays keeps playing and everything after it is removed; `everything: true` also removes the playing track (the zone falls silent). Only on an explicit request.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deep-roon-mcp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "An MCP server for Roon that reaches the parts the official API doesn't: reviews, biographies, Qobuz's and TIDAL's shelves, TIDAL's playlists, your history, search across your library and the streaming services",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "René Bouwmeester <21277119+renebouwmeester@users.noreply.github.com>",
|