roon-mcp 0.1.0

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +161 -0
  4. package/dist/BrowseSessionManager.d.ts +34 -0
  5. package/dist/BrowseSessionManager.js +97 -0
  6. package/dist/BrowseSessionManager.js.map +1 -0
  7. package/dist/BrowseSessionManager.test.d.ts +1 -0
  8. package/dist/BrowseSessionManager.test.js +83 -0
  9. package/dist/BrowseSessionManager.test.js.map +1 -0
  10. package/dist/GenreService.d.ts +22 -0
  11. package/dist/GenreService.js +177 -0
  12. package/dist/GenreService.js.map +1 -0
  13. package/dist/GenreService.test.d.ts +1 -0
  14. package/dist/GenreService.test.js +147 -0
  15. package/dist/GenreService.test.js.map +1 -0
  16. package/dist/PlaybackService.d.ts +43 -0
  17. package/dist/PlaybackService.js +291 -0
  18. package/dist/PlaybackService.js.map +1 -0
  19. package/dist/PlaybackService.test.d.ts +1 -0
  20. package/dist/PlaybackService.test.js +281 -0
  21. package/dist/PlaybackService.test.js.map +1 -0
  22. package/dist/RoonClient.d.ts +37 -0
  23. package/dist/RoonClient.js +105 -0
  24. package/dist/RoonClient.js.map +1 -0
  25. package/dist/RoonMcpServer.d.ts +21 -0
  26. package/dist/RoonMcpServer.js +218 -0
  27. package/dist/RoonMcpServer.js.map +1 -0
  28. package/dist/SearchNavigator.d.ts +38 -0
  29. package/dist/SearchNavigator.js +104 -0
  30. package/dist/SearchNavigator.js.map +1 -0
  31. package/dist/SearchService.d.ts +29 -0
  32. package/dist/SearchService.js +236 -0
  33. package/dist/SearchService.js.map +1 -0
  34. package/dist/SearchService.test.d.ts +1 -0
  35. package/dist/SearchService.test.js +197 -0
  36. package/dist/SearchService.test.js.map +1 -0
  37. package/dist/TrackExpansionService.d.ts +75 -0
  38. package/dist/TrackExpansionService.js +337 -0
  39. package/dist/TrackExpansionService.js.map +1 -0
  40. package/dist/TrackExpansionService.test.d.ts +1 -0
  41. package/dist/TrackExpansionService.test.js +382 -0
  42. package/dist/TrackExpansionService.test.js.map +1 -0
  43. package/dist/ZoneService.d.ts +52 -0
  44. package/dist/ZoneService.js +139 -0
  45. package/dist/ZoneService.js.map +1 -0
  46. package/dist/ZoneService.test.d.ts +1 -0
  47. package/dist/ZoneService.test.js +111 -0
  48. package/dist/ZoneService.test.js.map +1 -0
  49. package/dist/index.d.ts +2 -0
  50. package/dist/index.js +44 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/locator.d.ts +58 -0
  53. package/dist/locator.js +88 -0
  54. package/dist/locator.js.map +1 -0
  55. package/dist/locator.test.d.ts +1 -0
  56. package/dist/locator.test.js +37 -0
  57. package/dist/locator.test.js.map +1 -0
  58. package/dist/logger.d.ts +23 -0
  59. package/dist/logger.js +56 -0
  60. package/dist/logger.js.map +1 -0
  61. package/dist/logger.test.d.ts +1 -0
  62. package/dist/logger.test.js +53 -0
  63. package/dist/logger.test.js.map +1 -0
  64. package/dist/types.d.ts +93 -0
  65. package/dist/types.js +13 -0
  66. package/dist/types.js.map +1 -0
  67. package/package.json +60 -0
@@ -0,0 +1,281 @@
1
+ import assert from "node:assert/strict";
2
+ import { test } from "node:test";
3
+ import { BrowseSessionManager } from "./BrowseSessionManager.js";
4
+ import { encodeLocator } from "./locator.js";
5
+ import { PlaybackService } from "./PlaybackService.js";
6
+ import { TrackExpansionService } from "./TrackExpansionService.js";
7
+ import { RoonMcpError } from "./types.js";
8
+ import { ZoneService } from "./ZoneService.js";
9
+ /** Locator for a single-group/single-item search keyed by `query`. */
10
+ const loc = (query) => encodeLocator({ q: query, g: 0, i: 0 });
11
+ // Synthetic keys for the canned search tree the navigator walks:
12
+ // search(input) → [group] → [item] → opened content (items[query]).
13
+ const GROUP_KEY = "__grp";
14
+ const ITEM_KEY = "__itm";
15
+ /**
16
+ * Minimal stateful model of the search hierarchy the playback navigator walks.
17
+ * `items` maps a search query to the *opened item* content (its action menu or
18
+ * a terminal message); `drills` holds secondary keys (e.g. an action_list
19
+ * container); invoking an action key records an invocation.
20
+ */
21
+ class FakeBrowse {
22
+ items;
23
+ actionKeys;
24
+ drills;
25
+ stack = [];
26
+ query = "";
27
+ invocations = [];
28
+ failActionRemaining;
29
+ constructor(items, actionKeys, drills = {}, opts = {}) {
30
+ this.items = items;
31
+ this.actionKeys = actionKeys;
32
+ this.drills = drills;
33
+ this.failActionRemaining = opts.failActionTimes ?? 0;
34
+ }
35
+ browse(o, cb) {
36
+ if (o.pop_all) {
37
+ this.stack = [];
38
+ if (o.input !== undefined) {
39
+ this.query = o.input;
40
+ this.stack = [[{ title: "Group", item_key: GROUP_KEY, hint: "list" }]];
41
+ }
42
+ return cb(false, { action: "list" });
43
+ }
44
+ if (o.pop_levels) {
45
+ for (let i = 0; i < o.pop_levels; i++)
46
+ this.stack.pop();
47
+ return cb(false, { action: "list" });
48
+ }
49
+ if (o.item_key !== undefined) {
50
+ const key = o.item_key;
51
+ if (this.actionKeys.has(key)) {
52
+ if (this.failActionRemaining > 0) {
53
+ this.failActionRemaining--;
54
+ return cb(false, { action: "message", is_error: true, message: "ActionFailed" });
55
+ }
56
+ this.invocations.push({ itemKey: key, zone: o.zone_or_output_id });
57
+ return cb(false, { action: "none" });
58
+ }
59
+ if (key === GROUP_KEY) {
60
+ this.stack.push([{ title: "Item", item_key: ITEM_KEY, hint: "list" }]);
61
+ return cb(false, { action: "list" });
62
+ }
63
+ if (key === ITEM_KEY) {
64
+ return this.land(this.items[this.query], cb);
65
+ }
66
+ return this.land(this.drills[key], cb);
67
+ }
68
+ return cb(false, { action: "none" });
69
+ }
70
+ /** Push a child list, surface a terminal message, or reject an unknown key. */
71
+ land(target, cb) {
72
+ if (target === undefined) {
73
+ return cb("InvalidItemKey", undefined);
74
+ }
75
+ if (Array.isArray(target)) {
76
+ this.stack.push(target);
77
+ return cb(false, { action: "list", item: { title: this.query } });
78
+ }
79
+ return cb(false, { action: "message", message: target.message });
80
+ }
81
+ load(_o, cb) {
82
+ const top = this.stack[this.stack.length - 1] ?? [];
83
+ cb(false, { items: top, offset: 0, list: { title: "", count: top.length, level: 0 } });
84
+ }
85
+ }
86
+ class FakeTransport {
87
+ zones;
88
+ shuffleCalls = [];
89
+ change_settings;
90
+ constructor(zones, supportsChangeSettings = true) {
91
+ this.zones = zones;
92
+ if (supportsChangeSettings) {
93
+ this.change_settings = (zone, settings, cb) => {
94
+ this.shuffleCalls.push({ zone, shuffle: settings.shuffle });
95
+ cb?.(false);
96
+ };
97
+ }
98
+ }
99
+ get_zones(cb) {
100
+ cb(false, { zones: this.zones });
101
+ }
102
+ }
103
+ const ZONE = {
104
+ zone_id: "z1",
105
+ display_name: "Office",
106
+ state: "stopped",
107
+ outputs: [{ output_id: "o1", zone_id: "z1", display_name: "Office" }],
108
+ now_playing: { two_line: { line1: "Some Track", line2: "Some Artist" } },
109
+ };
110
+ function action(title, key) {
111
+ return { title, item_key: key, hint: "action" };
112
+ }
113
+ function build(items, actionKeys, opts = {}) {
114
+ const browse = new FakeBrowse(items, new Set(actionKeys), opts.drills ?? {}, opts.playOpts);
115
+ const transport = opts.transport ?? new FakeTransport([ZONE]);
116
+ const stub = {
117
+ waitForCore: async () => undefined,
118
+ getBrowse: () => browse,
119
+ getTransport: () => transport,
120
+ };
121
+ const mgr = new BrowseSessionManager(stub);
122
+ const zones = new ZoneService(stub);
123
+ const tracks = new TrackExpansionService(mgr);
124
+ const svc = new PlaybackService(mgr, zones, stub, tracks);
125
+ return { svc, browse, transport };
126
+ }
127
+ test("play_now discovers and invokes the Play Now action against the zone", async () => {
128
+ const { svc, browse } = build({ album: [action("Play Now", "act:play"), action("Queue", "act:queue")] }, ["act:play", "act:queue"]);
129
+ const out = await svc.playNow({ zoneId: "z1", itemKey: loc("album") });
130
+ assert.equal(out.ok, true);
131
+ assert.equal(out.queued, 1);
132
+ assert.deepEqual(out.skipped, []);
133
+ assert.equal(out.nowPlaying, "Some Track");
134
+ assert.deepEqual(browse.invocations, [{ itemKey: "act:play", zone: "z1" }]);
135
+ });
136
+ test("shuffle prefers a Shuffle action over Play Now and skips the Transport fallback", async () => {
137
+ const { svc, browse, transport } = build({ album: [action("Play Now", "act:play"), action("Shuffle", "act:shuffle")] }, ["act:play", "act:shuffle"]);
138
+ const out = await svc.playNow({ zoneId: "z1", itemKey: loc("album"), shuffle: true });
139
+ assert.equal(browse.invocations[0]?.itemKey, "act:shuffle");
140
+ assert.deepEqual(transport.shuffleCalls, []);
141
+ assert.doesNotMatch(out.message ?? "", /could not be applied/i);
142
+ });
143
+ test("shuffle falls back to the Transport setting when no shuffle action exists", async () => {
144
+ const { svc, browse, transport } = build({ album: [action("Play Now", "act:play")] }, ["act:play"]);
145
+ const out = await svc.playNow({ zoneId: "z1", itemKey: loc("album"), shuffle: true });
146
+ assert.equal(browse.invocations[0]?.itemKey, "act:play");
147
+ assert.deepEqual(transport.shuffleCalls, [{ zone: "z1", shuffle: true }]);
148
+ assert.doesNotMatch(out.message ?? "", /could not be applied/i);
149
+ });
150
+ test("shuffle reports when it cannot be applied (no action, Transport unsupported)", async () => {
151
+ const transport = new FakeTransport([ZONE], false);
152
+ const { svc } = build({ album: [action("Play Now", "act:play")] }, ["act:play"], { transport });
153
+ const out = await svc.playNow({ zoneId: "z1", itemKey: loc("album"), shuffle: true });
154
+ assert.equal(out.ok, true);
155
+ assert.match(out.message ?? "", /could not be applied/i);
156
+ });
157
+ test("an unknown zone id is rejected with ZONE_NOT_FOUND", async () => {
158
+ const { svc } = build({ album: [action("Play Now", "act:play")] }, ["act:play"]);
159
+ await assert.rejects(svc.playNow({ zoneId: "nope", itemKey: loc("album") }), (e) => e instanceof RoonMcpError && e.code === "ZONE_NOT_FOUND");
160
+ });
161
+ test("play_now without a zoneId resolves the only available zone", async () => {
162
+ const { svc, browse } = build({ album: [action("Play Now", "act:play")] }, ["act:play"]);
163
+ const out = await svc.playNow({ itemKey: loc("album") });
164
+ assert.equal(out.ok, true);
165
+ assert.equal(out.zoneId, "z1");
166
+ assert.equal(browse.invocations[0]?.zone, "z1");
167
+ });
168
+ test("an output id is accepted as the playback target", async () => {
169
+ const { svc, browse } = build({ album: [action("Play Now", "act:play")] }, ["act:play"]);
170
+ const out = await svc.playNow({ zoneId: "o1", itemKey: loc("album") });
171
+ assert.equal(out.ok, true);
172
+ assert.equal(browse.invocations[0]?.zone, "o1");
173
+ });
174
+ test("a non-locator itemKey is rejected with INVALID_ITEM_KEY", async () => {
175
+ const { svc } = build({ album: [action("Play Now", "act:play")] }, ["act:play"]);
176
+ await assert.rejects(svc.playNow({ zoneId: "z1", itemKey: "raw-key" }), (e) => e instanceof RoonMcpError && e.code === "INVALID_ITEM_KEY");
177
+ });
178
+ test("an item with no play action yields NO_PLAY_ACTION", async () => {
179
+ const { svc } = build({ album: [{ title: "Tracks", item_key: "x", hint: "list" }] }, []);
180
+ await assert.rejects(svc.playNow({ zoneId: "z1", itemKey: loc("album") }), (e) => e instanceof RoonMcpError && e.code === "NO_PLAY_ACTION");
181
+ });
182
+ test("a non-playable item (message action) yields NO_PLAY_ACTION", async () => {
183
+ const { svc } = build({ album: { message: "Not available." } }, []);
184
+ await assert.rejects(svc.playNow({ zoneId: "z1", itemKey: loc("album") }), (e) => e instanceof RoonMcpError &&
185
+ e.code === "NO_PLAY_ACTION" &&
186
+ /not available/i.test(e.message));
187
+ });
188
+ test("actions nested under an action_list container are found", async () => {
189
+ const { svc, browse } = build({ album: [{ title: "More", item_key: "al:more", hint: "action_list" }] }, ["act:play"], { drills: { "al:more": [action("Play Now", "act:play")] } });
190
+ const out = await svc.playNow({ zoneId: "z1", itemKey: loc("album") });
191
+ assert.equal(out.ok, true);
192
+ assert.equal(browse.invocations[0]?.itemKey, "act:play");
193
+ });
194
+ test("a failed action invocation reopens the item and retries once", async () => {
195
+ const { svc, browse } = build({ album: [action("Play Now", "act:play")] }, ["act:play"], {
196
+ playOpts: { failActionTimes: 1 },
197
+ });
198
+ const out = await svc.playNow({ zoneId: "z1", itemKey: loc("album") });
199
+ assert.equal(out.ok, true);
200
+ // Only the successful retry is recorded as an invocation.
201
+ assert.deepEqual(browse.invocations, [{ itemKey: "act:play", zone: "z1" }]);
202
+ });
203
+ test("enqueue starts the first item with Play Now and appends the rest with Queue", async () => {
204
+ const { svc, browse } = build({
205
+ t1: [action("Play Now", "p1"), action("Add to Queue", "q1")],
206
+ t2: [action("Play Now", "p2"), action("Add to Queue", "q2")],
207
+ t3: [action("Play Now", "p3"), action("Add to Queue", "q3")],
208
+ }, ["p1", "q1", "p2", "q2", "p3", "q3"]);
209
+ const out = await svc.enqueueAndPlay({
210
+ zoneId: "z1",
211
+ itemKeys: [loc("t1"), loc("t2"), loc("t3")],
212
+ });
213
+ assert.equal(out.ok, true);
214
+ assert.equal(out.queued, 3);
215
+ assert.equal(out.requested, 3);
216
+ assert.deepEqual(out.skipped, []);
217
+ // First via Play Now, the rest appended via Add to Queue (order preserved).
218
+ assert.deepEqual(browse.invocations.map((i) => i.itemKey), ["p1", "q2", "q3"]);
219
+ });
220
+ test("enqueue skips a non-playable middle item and queues the rest", async () => {
221
+ const { svc } = build({
222
+ t1: [action("Play Now", "p1")],
223
+ bad: { message: "Unavailable." },
224
+ t3: [action("Play Now", "p3"), action("Queue", "q3")],
225
+ }, ["p1", "p3", "q3"]);
226
+ const out = await svc.enqueueAndPlay({
227
+ zoneId: "z1",
228
+ itemKeys: [loc("t1"), loc("bad"), loc("t3")],
229
+ });
230
+ assert.equal(out.ok, true);
231
+ assert.equal(out.queued, 2);
232
+ assert.equal(out.skipped.length, 1);
233
+ assert.equal(out.skipped[0]?.itemKey, loc("bad"));
234
+ assert.match(out.skipped[0]?.reason ?? "", /unavailable/i);
235
+ });
236
+ test("enqueue falls through to the next item as the queue starter", async () => {
237
+ const { svc, browse } = build({
238
+ bad: { message: "Nope." },
239
+ t2: [action("Play Now", "p2"), action("Queue", "q2")],
240
+ t3: [action("Play Now", "p3"), action("Queue", "q3")],
241
+ }, ["p2", "q2", "p3", "q3"]);
242
+ const out = await svc.enqueueAndPlay({
243
+ zoneId: "z1",
244
+ itemKeys: [loc("bad"), loc("t2"), loc("t3")],
245
+ });
246
+ assert.equal(out.ok, true);
247
+ assert.equal(out.queued, 2);
248
+ assert.equal(out.skipped[0]?.itemKey, loc("bad"));
249
+ assert.deepEqual(browse.invocations.map((i) => i.itemKey), ["p2", "q3"]);
250
+ });
251
+ test("enqueue with no startable items reports ok:false and queued 0", async () => {
252
+ const { svc } = build({ bad1: { message: "No." }, bad2: { message: "No." } }, []);
253
+ const out = await svc.enqueueAndPlay({ zoneId: "z1", itemKeys: [loc("bad1"), loc("bad2")] });
254
+ assert.equal(out.ok, false);
255
+ assert.equal(out.queued, 0);
256
+ assert.equal(out.skipped.length, 2);
257
+ });
258
+ test("enqueue skips a stale (invalid) item key instead of failing the call", async () => {
259
+ const { svc } = build({ t1: [action("Play Now", "p1")] }, ["p1"]);
260
+ const out = await svc.enqueueAndPlay({ zoneId: "z1", itemKeys: [loc("t1"), loc("ghost")] });
261
+ assert.equal(out.ok, true);
262
+ assert.equal(out.queued, 1);
263
+ assert.equal(out.skipped[0]?.itemKey, loc("ghost"));
264
+ });
265
+ test("enqueue applies shuffle via Transport only when requested", async () => {
266
+ const transport = new FakeTransport([ZONE]);
267
+ const { svc } = build({ t1: [action("Play Now", "p1")] }, ["p1"], { transport });
268
+ await svc.enqueueAndPlay({ zoneId: "z1", itemKeys: [loc("t1")], shuffle: true });
269
+ assert.deepEqual(transport.shuffleCalls, [{ zone: "z1", shuffle: true }]);
270
+ });
271
+ test("enqueue leaves the zone's shuffle setting untouched when shuffle is omitted", async () => {
272
+ const transport = new FakeTransport([ZONE]);
273
+ const { svc } = build({ t1: [action("Play Now", "p1")] }, ["p1"], { transport });
274
+ await svc.enqueueAndPlay({ zoneId: "z1", itemKeys: [loc("t1")] });
275
+ assert.deepEqual(transport.shuffleCalls, []);
276
+ });
277
+ test("enqueue rejects an unknown zone id with ZONE_NOT_FOUND", async () => {
278
+ const { svc } = build({ t1: [action("Play Now", "p1")] }, ["p1"]);
279
+ await assert.rejects(svc.enqueueAndPlay({ zoneId: "nope", itemKeys: [loc("t1")] }), (e) => e instanceof RoonMcpError && e.code === "ZONE_NOT_FOUND");
280
+ });
281
+ //# sourceMappingURL=PlaybackService.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PlaybackService.test.js","sourceRoot":"","sources":["../src/PlaybackService.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { type RoonCore } from "node-roon-api";
2
+ import { type RoonApiBrowse as RoonBrowseService } from "node-roon-api-browse";
3
+ import { type RoonApiTransport as RoonTransportService } from "node-roon-api-transport";
4
+ export interface RoonClientOptions {
5
+ extensionId?: string;
6
+ displayName?: string;
7
+ displayVersion?: string;
8
+ publisher?: string;
9
+ email?: string;
10
+ website?: string;
11
+ /** Optional sink for diagnostics; defaults to stderr so stdout stays MCP-clean. */
12
+ log?: (message: string) => void;
13
+ }
14
+ /**
15
+ * Wraps node-roon-api setup, discovery, and pairing lifecycle, and exposes
16
+ * typed accessors for the Transport service. Fails fast when no Core is paired.
17
+ */
18
+ export declare class RoonClient {
19
+ private readonly roon;
20
+ private readonly status;
21
+ private readonly log;
22
+ private core;
23
+ private readonly coreWaiters;
24
+ constructor(options?: RoonClientOptions);
25
+ /** Initialize services and begin Core discovery. */
26
+ start(): void;
27
+ stop(): void;
28
+ isPaired(): boolean;
29
+ /** Resolve with the paired Core, waiting up to `timeoutMs` for pairing. */
30
+ waitForCore(timeoutMs?: number): Promise<RoonCore>;
31
+ /** Transport service of the currently paired Core. Throws if unpaired. */
32
+ getTransport(): RoonTransportService;
33
+ /** Browse service of the currently paired Core. Throws if unpaired. */
34
+ getBrowse(): RoonBrowseService;
35
+ private onCorePaired;
36
+ private onCoreUnpaired;
37
+ }
@@ -0,0 +1,105 @@
1
+ import RoonApi from "node-roon-api";
2
+ import RoonApiBrowse from "node-roon-api-browse";
3
+ import RoonApiStatus from "node-roon-api-status";
4
+ import RoonApiTransport from "node-roon-api-transport";
5
+ import { RoonMcpError } from "./types.js";
6
+ const DEFAULTS = {
7
+ extensionId: "com.christophhalang.roon-mcp",
8
+ displayName: "Roon MCP",
9
+ displayVersion: "0.1.0",
10
+ publisher: "Christoph Halang",
11
+ email: "christoph.halang@gmail.com",
12
+ website: "https://github.com/christophhalang/roon-mcp",
13
+ };
14
+ /**
15
+ * Wraps node-roon-api setup, discovery, and pairing lifecycle, and exposes
16
+ * typed accessors for the Transport service. Fails fast when no Core is paired.
17
+ */
18
+ export class RoonClient {
19
+ roon;
20
+ status;
21
+ log;
22
+ core;
23
+ coreWaiters = [];
24
+ constructor(options = {}) {
25
+ const cfg = { ...DEFAULTS, ...options };
26
+ this.log = options.log ?? ((m) => process.stderr.write(`[roon] ${m}\n`));
27
+ this.roon = new RoonApi({
28
+ extension_id: cfg.extensionId,
29
+ display_name: cfg.displayName,
30
+ display_version: cfg.displayVersion,
31
+ publisher: cfg.publisher,
32
+ email: cfg.email,
33
+ website: cfg.website,
34
+ core_paired: (core) => this.onCorePaired(core),
35
+ core_unpaired: (core) => this.onCoreUnpaired(core),
36
+ });
37
+ this.status = new RoonApiStatus(this.roon);
38
+ }
39
+ /** Initialize services and begin Core discovery. */
40
+ start() {
41
+ this.roon.init_services({
42
+ required_services: [RoonApiTransport, RoonApiBrowse],
43
+ provided_services: [this.status],
44
+ });
45
+ this.status.set_status("Waiting for Roon Core…", false);
46
+ this.roon.start_discovery();
47
+ this.log("discovery started; waiting for a Core to pair");
48
+ }
49
+ stop() {
50
+ // node-roon-api has no clean teardown; drop references and reject waiters.
51
+ this.core = undefined;
52
+ this.coreWaiters.length = 0;
53
+ }
54
+ isPaired() {
55
+ return this.core !== undefined;
56
+ }
57
+ /** Resolve with the paired Core, waiting up to `timeoutMs` for pairing. */
58
+ waitForCore(timeoutMs = 15_000) {
59
+ if (this.core)
60
+ return Promise.resolve(this.core);
61
+ return new Promise((resolve, reject) => {
62
+ const timer = setTimeout(() => {
63
+ const idx = this.coreWaiters.indexOf(onPaired);
64
+ if (idx >= 0)
65
+ this.coreWaiters.splice(idx, 1);
66
+ reject(new RoonMcpError("NO_CORE_PAIRED", `No Roon Core paired within ${timeoutMs}ms. ` +
67
+ "Enable the extension under Roon → Settings → Extensions."));
68
+ }, timeoutMs);
69
+ const onPaired = (core) => {
70
+ clearTimeout(timer);
71
+ resolve(core);
72
+ };
73
+ this.coreWaiters.push(onPaired);
74
+ });
75
+ }
76
+ /** Transport service of the currently paired Core. Throws if unpaired. */
77
+ getTransport() {
78
+ if (!this.core) {
79
+ throw new RoonMcpError("NO_CORE_PAIRED", "No Roon Core is currently paired.");
80
+ }
81
+ return this.core.services.RoonApiTransport;
82
+ }
83
+ /** Browse service of the currently paired Core. Throws if unpaired. */
84
+ getBrowse() {
85
+ if (!this.core) {
86
+ throw new RoonMcpError("NO_CORE_PAIRED", "No Roon Core is currently paired.");
87
+ }
88
+ return this.core.services.RoonApiBrowse;
89
+ }
90
+ onCorePaired(core) {
91
+ this.core = core;
92
+ this.log(`paired with Core "${core.display_name}" (${core.core_id})`);
93
+ this.status.set_status(`Paired with ${core.display_name}`, false);
94
+ const waiters = this.coreWaiters.splice(0, this.coreWaiters.length);
95
+ for (const waiter of waiters)
96
+ waiter(core);
97
+ }
98
+ onCoreUnpaired(core) {
99
+ this.log(`unpaired from Core "${core.display_name}" (${core.core_id})`);
100
+ if (this.core?.core_id === core.core_id)
101
+ this.core = undefined;
102
+ this.status.set_status("Waiting for Roon Core…", false);
103
+ }
104
+ }
105
+ //# sourceMappingURL=RoonClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RoonClient.js","sourceRoot":"","sources":["../src/RoonClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { PlaybackService } from "./PlaybackService.js";
2
+ import { RoonClient } from "./RoonClient.js";
3
+ import { SearchService } from "./SearchService.js";
4
+ import { TrackExpansionService } from "./TrackExpansionService.js";
5
+ import { ZoneService } from "./ZoneService.js";
6
+ /**
7
+ * Owns MCP startup and tool registration, and maps tool calls to services.
8
+ * Milestone 1 ships only `list_zones`.
9
+ */
10
+ export declare class RoonMcpServer {
11
+ private readonly roon;
12
+ private readonly zones;
13
+ private readonly search;
14
+ private readonly tracks;
15
+ private readonly playback;
16
+ private readonly server;
17
+ constructor(roon: RoonClient, zones: ZoneService, search: SearchService, tracks: TrackExpansionService, playback: PlaybackService);
18
+ registerTools(): void;
19
+ start(): Promise<void>;
20
+ stop(): Promise<void>;
21
+ }