mcp-arr-server 1.5.2 → 1.5.4
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 +7 -30
- package/dist/arr-client.d.ts +4 -128
- package/dist/arr-client.d.ts.map +1 -1
- package/dist/arr-client.js +1 -95
- package/dist/arr-client.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -456
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
package/dist/index.js
CHANGED
|
@@ -3,25 +3,23 @@
|
|
|
3
3
|
* MCP Server for *arr Media Management Suite
|
|
4
4
|
*
|
|
5
5
|
* Provides tools for managing Sonarr (TV), Radarr (Movies), Lidarr (Music),
|
|
6
|
-
*
|
|
6
|
+
* and Prowlarr (Indexers) through Claude Code.
|
|
7
7
|
*
|
|
8
8
|
* Environment variables:
|
|
9
9
|
* - SONARR_URL, SONARR_API_KEY
|
|
10
10
|
* - RADARR_URL, RADARR_API_KEY
|
|
11
11
|
* - LIDARR_URL, LIDARR_API_KEY
|
|
12
|
-
* - READARR_URL, READARR_API_KEY
|
|
13
12
|
* - PROWLARR_URL, PROWLARR_API_KEY
|
|
14
13
|
*/
|
|
15
14
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
16
15
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
17
16
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
18
|
-
import { SonarrClient, RadarrClient, LidarrClient,
|
|
17
|
+
import { SonarrClient, RadarrClient, LidarrClient, ProwlarrClient, } from "./arr-client.js";
|
|
19
18
|
import { trashClient } from "./trash-client.js";
|
|
20
19
|
const services = [
|
|
21
20
|
{ name: 'sonarr', displayName: 'Sonarr (TV)', url: process.env.SONARR_URL, apiKey: process.env.SONARR_API_KEY },
|
|
22
21
|
{ name: 'radarr', displayName: 'Radarr (Movies)', url: process.env.RADARR_URL, apiKey: process.env.RADARR_API_KEY },
|
|
23
22
|
{ name: 'lidarr', displayName: 'Lidarr (Music)', url: process.env.LIDARR_URL, apiKey: process.env.LIDARR_API_KEY },
|
|
24
|
-
{ name: 'readarr', displayName: 'Readarr (Books)', url: process.env.READARR_URL, apiKey: process.env.READARR_API_KEY },
|
|
25
23
|
{ name: 'prowlarr', displayName: 'Prowlarr (Indexers)', url: process.env.PROWLARR_URL, apiKey: process.env.PROWLARR_API_KEY },
|
|
26
24
|
];
|
|
27
25
|
// Check which services are configured
|
|
@@ -45,9 +43,6 @@ for (const service of configuredServices) {
|
|
|
45
43
|
case 'lidarr':
|
|
46
44
|
clients.lidarr = new LidarrClient(config);
|
|
47
45
|
break;
|
|
48
|
-
case 'readarr':
|
|
49
|
-
clients.readarr = new ReadarrClient(config);
|
|
50
|
-
break;
|
|
51
46
|
case 'prowlarr':
|
|
52
47
|
clients.prowlarr = new ProwlarrClient(config);
|
|
53
48
|
break;
|
|
@@ -135,8 +130,6 @@ if (clients.radarr)
|
|
|
135
130
|
addConfigTools('radarr', 'Radarr (Movies)');
|
|
136
131
|
if (clients.lidarr)
|
|
137
132
|
addConfigTools('lidarr', 'Lidarr (Music)');
|
|
138
|
-
if (clients.readarr)
|
|
139
|
-
addConfigTools('readarr', 'Readarr (Books)');
|
|
140
133
|
// Sonarr tools
|
|
141
134
|
if (clients.sonarr) {
|
|
142
135
|
TOOLS.push({
|
|
@@ -263,22 +256,6 @@ if (clients.sonarr) {
|
|
|
263
256
|
},
|
|
264
257
|
required: ["tvdbId", "title", "qualityProfileId", "rootFolderPath"],
|
|
265
258
|
},
|
|
266
|
-
}, {
|
|
267
|
-
name: "sonarr_get_root_folders",
|
|
268
|
-
description: "Get available root folders for Sonarr. Use this to find valid rootFolderPath values when adding a series.",
|
|
269
|
-
inputSchema: {
|
|
270
|
-
type: "object",
|
|
271
|
-
properties: {},
|
|
272
|
-
required: [],
|
|
273
|
-
},
|
|
274
|
-
}, {
|
|
275
|
-
name: "sonarr_get_quality_profiles",
|
|
276
|
-
description: "Get available quality profiles for Sonarr. Use this to find valid qualityProfileId values when adding a series.",
|
|
277
|
-
inputSchema: {
|
|
278
|
-
type: "object",
|
|
279
|
-
properties: {},
|
|
280
|
-
required: [],
|
|
281
|
-
},
|
|
282
259
|
});
|
|
283
260
|
}
|
|
284
261
|
// Radarr tools
|
|
@@ -377,22 +354,6 @@ if (clients.radarr) {
|
|
|
377
354
|
},
|
|
378
355
|
required: ["tmdbId", "title", "qualityProfileId", "rootFolderPath"],
|
|
379
356
|
},
|
|
380
|
-
}, {
|
|
381
|
-
name: "radarr_get_root_folders",
|
|
382
|
-
description: "Get available root folders for Radarr. Use this to find valid rootFolderPath values when adding a movie.",
|
|
383
|
-
inputSchema: {
|
|
384
|
-
type: "object",
|
|
385
|
-
properties: {},
|
|
386
|
-
required: [],
|
|
387
|
-
},
|
|
388
|
-
}, {
|
|
389
|
-
name: "radarr_get_quality_profiles",
|
|
390
|
-
description: "Get available quality profiles for Radarr. Use this to find valid qualityProfileId values when adding a movie.",
|
|
391
|
-
inputSchema: {
|
|
392
|
-
type: "object",
|
|
393
|
-
properties: {},
|
|
394
|
-
required: [],
|
|
395
|
-
},
|
|
396
357
|
});
|
|
397
358
|
}
|
|
398
359
|
// Lidarr tools
|
|
@@ -542,154 +503,6 @@ if (clients.lidarr) {
|
|
|
542
503
|
},
|
|
543
504
|
});
|
|
544
505
|
}
|
|
545
|
-
// Readarr tools
|
|
546
|
-
if (clients.readarr) {
|
|
547
|
-
TOOLS.push({
|
|
548
|
-
name: "readarr_get_authors",
|
|
549
|
-
description: "Get all authors in Readarr library",
|
|
550
|
-
inputSchema: {
|
|
551
|
-
type: "object",
|
|
552
|
-
properties: {},
|
|
553
|
-
required: [],
|
|
554
|
-
},
|
|
555
|
-
}, {
|
|
556
|
-
name: "readarr_search",
|
|
557
|
-
description: "Search for authors by name. Returns results with foreignAuthorId needed for readarr_add_author.",
|
|
558
|
-
inputSchema: {
|
|
559
|
-
type: "object",
|
|
560
|
-
properties: {
|
|
561
|
-
term: {
|
|
562
|
-
type: "string",
|
|
563
|
-
description: "Search term (author name)",
|
|
564
|
-
},
|
|
565
|
-
},
|
|
566
|
-
required: ["term"],
|
|
567
|
-
},
|
|
568
|
-
}, {
|
|
569
|
-
name: "readarr_get_queue",
|
|
570
|
-
description: "Get Readarr download queue",
|
|
571
|
-
inputSchema: {
|
|
572
|
-
type: "object",
|
|
573
|
-
properties: {},
|
|
574
|
-
required: [],
|
|
575
|
-
},
|
|
576
|
-
}, {
|
|
577
|
-
name: "readarr_get_books",
|
|
578
|
-
description: "Get books for an author in Readarr. Shows which books are available and which are missing.",
|
|
579
|
-
inputSchema: {
|
|
580
|
-
type: "object",
|
|
581
|
-
properties: {
|
|
582
|
-
authorId: {
|
|
583
|
-
type: "number",
|
|
584
|
-
description: "Author ID to get books for",
|
|
585
|
-
},
|
|
586
|
-
},
|
|
587
|
-
required: ["authorId"],
|
|
588
|
-
},
|
|
589
|
-
}, {
|
|
590
|
-
name: "readarr_search_book",
|
|
591
|
-
description: "Trigger a search for a specific book to download",
|
|
592
|
-
inputSchema: {
|
|
593
|
-
type: "object",
|
|
594
|
-
properties: {
|
|
595
|
-
bookIds: {
|
|
596
|
-
type: "array",
|
|
597
|
-
items: { type: "number" },
|
|
598
|
-
description: "Book ID(s) to search for",
|
|
599
|
-
},
|
|
600
|
-
},
|
|
601
|
-
required: ["bookIds"],
|
|
602
|
-
},
|
|
603
|
-
}, {
|
|
604
|
-
name: "readarr_search_missing",
|
|
605
|
-
description: "Trigger a search for all missing books for an author",
|
|
606
|
-
inputSchema: {
|
|
607
|
-
type: "object",
|
|
608
|
-
properties: {
|
|
609
|
-
authorId: {
|
|
610
|
-
type: "number",
|
|
611
|
-
description: "Author ID to search missing books for",
|
|
612
|
-
},
|
|
613
|
-
},
|
|
614
|
-
required: ["authorId"],
|
|
615
|
-
},
|
|
616
|
-
}, {
|
|
617
|
-
name: "readarr_get_calendar",
|
|
618
|
-
description: "Get upcoming book releases from Readarr",
|
|
619
|
-
inputSchema: {
|
|
620
|
-
type: "object",
|
|
621
|
-
properties: {
|
|
622
|
-
days: {
|
|
623
|
-
type: "number",
|
|
624
|
-
description: "Number of days to look ahead (default: 30)",
|
|
625
|
-
},
|
|
626
|
-
},
|
|
627
|
-
required: [],
|
|
628
|
-
},
|
|
629
|
-
}, {
|
|
630
|
-
name: "readarr_add_author",
|
|
631
|
-
description: "Add an author to Readarr. Use readarr_search first to find the foreignAuthorId, and readarr_get_root_folders / readarr_get_quality_profiles / readarr_get_metadata_profiles to get valid values. Use readarr_get_tags to get valid tag IDs.",
|
|
632
|
-
inputSchema: {
|
|
633
|
-
type: "object",
|
|
634
|
-
properties: {
|
|
635
|
-
foreignAuthorId: {
|
|
636
|
-
type: "string",
|
|
637
|
-
description: "Foreign author ID from readarr_search results",
|
|
638
|
-
},
|
|
639
|
-
authorName: {
|
|
640
|
-
type: "string",
|
|
641
|
-
description: "Author name",
|
|
642
|
-
},
|
|
643
|
-
qualityProfileId: {
|
|
644
|
-
type: "number",
|
|
645
|
-
description: "Quality profile ID from readarr_get_quality_profiles",
|
|
646
|
-
},
|
|
647
|
-
metadataProfileId: {
|
|
648
|
-
type: "number",
|
|
649
|
-
description: "Metadata profile ID from readarr_get_metadata_profiles",
|
|
650
|
-
},
|
|
651
|
-
rootFolderPath: {
|
|
652
|
-
type: "string",
|
|
653
|
-
description: "Root folder path from readarr_get_root_folders",
|
|
654
|
-
},
|
|
655
|
-
monitored: {
|
|
656
|
-
type: "boolean",
|
|
657
|
-
description: "Whether to monitor the author (default: true)",
|
|
658
|
-
},
|
|
659
|
-
tags: {
|
|
660
|
-
type: "array",
|
|
661
|
-
items: { type: "number" },
|
|
662
|
-
description: "Array of tag IDs from readarr_get_tags (optional)",
|
|
663
|
-
},
|
|
664
|
-
},
|
|
665
|
-
required: ["foreignAuthorId", "authorName", "qualityProfileId", "metadataProfileId", "rootFolderPath"],
|
|
666
|
-
},
|
|
667
|
-
}, {
|
|
668
|
-
name: "readarr_get_root_folders",
|
|
669
|
-
description: "Get available root folders for Readarr. Use this to find valid rootFolderPath values when adding an author.",
|
|
670
|
-
inputSchema: {
|
|
671
|
-
type: "object",
|
|
672
|
-
properties: {},
|
|
673
|
-
required: [],
|
|
674
|
-
},
|
|
675
|
-
}, {
|
|
676
|
-
name: "readarr_get_quality_profiles",
|
|
677
|
-
description: "Get available quality profiles for Readarr. Use this to find valid qualityProfileId values when adding an author.",
|
|
678
|
-
inputSchema: {
|
|
679
|
-
type: "object",
|
|
680
|
-
properties: {},
|
|
681
|
-
required: [],
|
|
682
|
-
},
|
|
683
|
-
}, {
|
|
684
|
-
name: "readarr_get_metadata_profiles",
|
|
685
|
-
description: "Get available metadata profiles for Readarr. Use this to find valid metadataProfileId values when adding an author.",
|
|
686
|
-
inputSchema: {
|
|
687
|
-
type: "object",
|
|
688
|
-
properties: {},
|
|
689
|
-
required: [],
|
|
690
|
-
},
|
|
691
|
-
});
|
|
692
|
-
}
|
|
693
506
|
// Prowlarr tools
|
|
694
507
|
if (clients.prowlarr) {
|
|
695
508
|
TOOLS.push({
|
|
@@ -931,8 +744,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
931
744
|
// Quality Profiles
|
|
932
745
|
case "sonarr_get_quality_profiles":
|
|
933
746
|
case "radarr_get_quality_profiles":
|
|
934
|
-
case "lidarr_get_quality_profiles":
|
|
935
|
-
case "readarr_get_quality_profiles": {
|
|
747
|
+
case "lidarr_get_quality_profiles": {
|
|
936
748
|
const serviceName = name.split('_')[0];
|
|
937
749
|
const client = clients[serviceName];
|
|
938
750
|
if (!client)
|
|
@@ -966,8 +778,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
966
778
|
// Health checks
|
|
967
779
|
case "sonarr_get_health":
|
|
968
780
|
case "radarr_get_health":
|
|
969
|
-
case "lidarr_get_health":
|
|
970
|
-
case "readarr_get_health": {
|
|
781
|
+
case "lidarr_get_health": {
|
|
971
782
|
const serviceName = name.split('_')[0];
|
|
972
783
|
const client = clients[serviceName];
|
|
973
784
|
if (!client)
|
|
@@ -992,8 +803,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
992
803
|
// Root folders
|
|
993
804
|
case "sonarr_get_root_folders":
|
|
994
805
|
case "radarr_get_root_folders":
|
|
995
|
-
case "lidarr_get_root_folders":
|
|
996
|
-
case "readarr_get_root_folders": {
|
|
806
|
+
case "lidarr_get_root_folders": {
|
|
997
807
|
const serviceName = name.split('_')[0];
|
|
998
808
|
const client = clients[serviceName];
|
|
999
809
|
if (!client)
|
|
@@ -1019,8 +829,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1019
829
|
// Download clients
|
|
1020
830
|
case "sonarr_get_download_clients":
|
|
1021
831
|
case "radarr_get_download_clients":
|
|
1022
|
-
case "lidarr_get_download_clients":
|
|
1023
|
-
case "readarr_get_download_clients": {
|
|
832
|
+
case "lidarr_get_download_clients": {
|
|
1024
833
|
const serviceName = name.split('_')[0];
|
|
1025
834
|
const client = clients[serviceName];
|
|
1026
835
|
if (!client)
|
|
@@ -1049,8 +858,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1049
858
|
// Naming config
|
|
1050
859
|
case "sonarr_get_naming":
|
|
1051
860
|
case "radarr_get_naming":
|
|
1052
|
-
case "lidarr_get_naming":
|
|
1053
|
-
case "readarr_get_naming": {
|
|
861
|
+
case "lidarr_get_naming": {
|
|
1054
862
|
const serviceName = name.split('_')[0];
|
|
1055
863
|
const client = clients[serviceName];
|
|
1056
864
|
if (!client)
|
|
@@ -1066,8 +874,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1066
874
|
// Tags
|
|
1067
875
|
case "sonarr_get_tags":
|
|
1068
876
|
case "radarr_get_tags":
|
|
1069
|
-
case "lidarr_get_tags":
|
|
1070
|
-
case "readarr_get_tags": {
|
|
877
|
+
case "lidarr_get_tags": {
|
|
1071
878
|
const serviceName = name.split('_')[0];
|
|
1072
879
|
const client = clients[serviceName];
|
|
1073
880
|
if (!client)
|
|
@@ -1086,8 +893,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1086
893
|
// Comprehensive setup review
|
|
1087
894
|
case "sonarr_review_setup":
|
|
1088
895
|
case "radarr_review_setup":
|
|
1089
|
-
case "lidarr_review_setup":
|
|
1090
|
-
case "readarr_review_setup": {
|
|
896
|
+
case "lidarr_review_setup": {
|
|
1091
897
|
const serviceName = name.split('_')[0];
|
|
1092
898
|
const client = clients[serviceName];
|
|
1093
899
|
if (!client)
|
|
@@ -1105,14 +911,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1105
911
|
client.getTags(),
|
|
1106
912
|
client.getIndexers(),
|
|
1107
913
|
]);
|
|
1108
|
-
// For Lidarr
|
|
914
|
+
// For Lidarr, also get metadata profiles
|
|
1109
915
|
let metadataProfiles = null;
|
|
1110
916
|
if (serviceName === 'lidarr' && clients.lidarr) {
|
|
1111
917
|
metadataProfiles = await clients.lidarr.getMetadataProfiles();
|
|
1112
918
|
}
|
|
1113
|
-
else if (serviceName === 'readarr' && clients.readarr) {
|
|
1114
|
-
metadataProfiles = await clients.readarr.getMetadataProfiles();
|
|
1115
|
-
}
|
|
1116
919
|
const review = {
|
|
1117
920
|
service: serviceName,
|
|
1118
921
|
version: status.version,
|
|
@@ -1338,28 +1141,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1338
1141
|
}],
|
|
1339
1142
|
};
|
|
1340
1143
|
}
|
|
1341
|
-
case "sonarr_get_root_folders": {
|
|
1342
|
-
if (!clients.sonarr)
|
|
1343
|
-
throw new Error("Sonarr not configured");
|
|
1344
|
-
const folders = await clients.sonarr.getRootFolders();
|
|
1345
|
-
return {
|
|
1346
|
-
content: [{
|
|
1347
|
-
type: "text",
|
|
1348
|
-
text: JSON.stringify(folders, null, 2),
|
|
1349
|
-
}],
|
|
1350
|
-
};
|
|
1351
|
-
}
|
|
1352
|
-
case "sonarr_get_quality_profiles": {
|
|
1353
|
-
if (!clients.sonarr)
|
|
1354
|
-
throw new Error("Sonarr not configured");
|
|
1355
|
-
const profiles = await clients.sonarr.getQualityProfiles();
|
|
1356
|
-
return {
|
|
1357
|
-
content: [{
|
|
1358
|
-
type: "text",
|
|
1359
|
-
text: JSON.stringify(profiles.map(p => ({ id: p.id, name: p.name })), null, 2),
|
|
1360
|
-
}],
|
|
1361
|
-
};
|
|
1362
|
-
}
|
|
1363
1144
|
// Radarr handlers
|
|
1364
1145
|
case "radarr_get_movies": {
|
|
1365
1146
|
if (!clients.radarr)
|
|
@@ -1472,28 +1253,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1472
1253
|
}],
|
|
1473
1254
|
};
|
|
1474
1255
|
}
|
|
1475
|
-
case "radarr_get_root_folders": {
|
|
1476
|
-
if (!clients.radarr)
|
|
1477
|
-
throw new Error("Radarr not configured");
|
|
1478
|
-
const folders = await clients.radarr.getRootFolders();
|
|
1479
|
-
return {
|
|
1480
|
-
content: [{
|
|
1481
|
-
type: "text",
|
|
1482
|
-
text: JSON.stringify(folders, null, 2),
|
|
1483
|
-
}],
|
|
1484
|
-
};
|
|
1485
|
-
}
|
|
1486
|
-
case "radarr_get_quality_profiles": {
|
|
1487
|
-
if (!clients.radarr)
|
|
1488
|
-
throw new Error("Radarr not configured");
|
|
1489
|
-
const profiles = await clients.radarr.getQualityProfiles();
|
|
1490
|
-
return {
|
|
1491
|
-
content: [{
|
|
1492
|
-
type: "text",
|
|
1493
|
-
text: JSON.stringify(profiles.map(p => ({ id: p.id, name: p.name })), null, 2),
|
|
1494
|
-
}],
|
|
1495
|
-
};
|
|
1496
|
-
}
|
|
1497
1256
|
// Lidarr handlers
|
|
1498
1257
|
case "lidarr_get_artists": {
|
|
1499
1258
|
if (!clients.lidarr)
|
|
@@ -1520,7 +1279,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1520
1279
|
case "lidarr_search": {
|
|
1521
1280
|
if (!clients.lidarr)
|
|
1522
1281
|
throw new Error("Lidarr not configured");
|
|
1523
|
-
const
|
|
1282
|
+
const a = args;
|
|
1283
|
+
const term = a.term ?? a.query ?? a.artist ?? a.name;
|
|
1284
|
+
if (!term)
|
|
1285
|
+
throw new Error("term required (artist name)");
|
|
1524
1286
|
const results = await clients.lidarr.searchArtists(term);
|
|
1525
1287
|
return {
|
|
1526
1288
|
content: [{
|
|
@@ -1528,9 +1290,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1528
1290
|
text: JSON.stringify({
|
|
1529
1291
|
count: results.length,
|
|
1530
1292
|
results: results.slice(0, 10).map(r => ({
|
|
1531
|
-
|
|
1293
|
+
artistName: r.artistName ?? r.title,
|
|
1294
|
+
disambiguation: r.disambiguation,
|
|
1532
1295
|
foreignArtistId: r.foreignArtistId,
|
|
1533
|
-
overview: r.overview
|
|
1296
|
+
overview: r.overview ? (r.overview.substring(0, 200) + (r.overview.length > 200 ? '...' : '')) : undefined,
|
|
1534
1297
|
})),
|
|
1535
1298
|
}, null, 2),
|
|
1536
1299
|
}],
|
|
@@ -1690,199 +1453,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1690
1453
|
}],
|
|
1691
1454
|
};
|
|
1692
1455
|
}
|
|
1693
|
-
// Readarr handlers
|
|
1694
|
-
case "readarr_get_authors": {
|
|
1695
|
-
if (!clients.readarr)
|
|
1696
|
-
throw new Error("Readarr not configured");
|
|
1697
|
-
const authors = await clients.readarr.getAuthors();
|
|
1698
|
-
return {
|
|
1699
|
-
content: [{
|
|
1700
|
-
type: "text",
|
|
1701
|
-
text: JSON.stringify({
|
|
1702
|
-
count: authors.length,
|
|
1703
|
-
authors: authors.map(a => ({
|
|
1704
|
-
id: a.id,
|
|
1705
|
-
authorName: a.authorName,
|
|
1706
|
-
status: a.status,
|
|
1707
|
-
books: a.statistics?.bookFileCount + '/' + a.statistics?.totalBookCount,
|
|
1708
|
-
sizeOnDisk: formatBytes(a.statistics?.sizeOnDisk || 0),
|
|
1709
|
-
monitored: a.monitored,
|
|
1710
|
-
})),
|
|
1711
|
-
}, null, 2),
|
|
1712
|
-
}],
|
|
1713
|
-
};
|
|
1714
|
-
}
|
|
1715
|
-
case "readarr_search": {
|
|
1716
|
-
if (!clients.readarr)
|
|
1717
|
-
throw new Error("Readarr not configured");
|
|
1718
|
-
const term = args.term;
|
|
1719
|
-
const results = await clients.readarr.searchAuthors(term);
|
|
1720
|
-
return {
|
|
1721
|
-
content: [{
|
|
1722
|
-
type: "text",
|
|
1723
|
-
text: JSON.stringify({
|
|
1724
|
-
count: results.length,
|
|
1725
|
-
results: results.slice(0, 10).map(r => ({
|
|
1726
|
-
title: r.title,
|
|
1727
|
-
foreignAuthorId: r.foreignAuthorId,
|
|
1728
|
-
overview: r.overview?.substring(0, 200) + (r.overview && r.overview.length > 200 ? '...' : ''),
|
|
1729
|
-
})),
|
|
1730
|
-
}, null, 2),
|
|
1731
|
-
}],
|
|
1732
|
-
};
|
|
1733
|
-
}
|
|
1734
|
-
case "readarr_get_queue": {
|
|
1735
|
-
if (!clients.readarr)
|
|
1736
|
-
throw new Error("Readarr not configured");
|
|
1737
|
-
const queue = await clients.readarr.getQueue();
|
|
1738
|
-
return {
|
|
1739
|
-
content: [{
|
|
1740
|
-
type: "text",
|
|
1741
|
-
text: JSON.stringify({
|
|
1742
|
-
totalRecords: queue.totalRecords,
|
|
1743
|
-
items: queue.records.map(q => ({
|
|
1744
|
-
title: q.title,
|
|
1745
|
-
status: q.status,
|
|
1746
|
-
progress: ((1 - q.sizeleft / q.size) * 100).toFixed(1) + '%',
|
|
1747
|
-
timeLeft: q.timeleft,
|
|
1748
|
-
downloadClient: q.downloadClient,
|
|
1749
|
-
})),
|
|
1750
|
-
}, null, 2),
|
|
1751
|
-
}],
|
|
1752
|
-
};
|
|
1753
|
-
}
|
|
1754
|
-
case "readarr_get_books": {
|
|
1755
|
-
if (!clients.readarr)
|
|
1756
|
-
throw new Error("Readarr not configured");
|
|
1757
|
-
const authorId = args.authorId;
|
|
1758
|
-
const books = await clients.readarr.getBooks(authorId);
|
|
1759
|
-
return {
|
|
1760
|
-
content: [{
|
|
1761
|
-
type: "text",
|
|
1762
|
-
text: JSON.stringify({
|
|
1763
|
-
count: books.length,
|
|
1764
|
-
books: books.map(b => ({
|
|
1765
|
-
id: b.id,
|
|
1766
|
-
title: b.title,
|
|
1767
|
-
releaseDate: b.releaseDate,
|
|
1768
|
-
pageCount: b.pageCount,
|
|
1769
|
-
monitored: b.monitored,
|
|
1770
|
-
hasFile: b.statistics ? b.statistics.bookFileCount > 0 : false,
|
|
1771
|
-
sizeOnDisk: formatBytes(b.statistics?.sizeOnDisk || 0),
|
|
1772
|
-
grabbed: b.grabbed,
|
|
1773
|
-
})),
|
|
1774
|
-
}, null, 2),
|
|
1775
|
-
}],
|
|
1776
|
-
};
|
|
1777
|
-
}
|
|
1778
|
-
case "readarr_search_book": {
|
|
1779
|
-
if (!clients.readarr)
|
|
1780
|
-
throw new Error("Readarr not configured");
|
|
1781
|
-
const bookIds = args.bookIds;
|
|
1782
|
-
const result = await clients.readarr.searchBook(bookIds);
|
|
1783
|
-
return {
|
|
1784
|
-
content: [{
|
|
1785
|
-
type: "text",
|
|
1786
|
-
text: JSON.stringify({
|
|
1787
|
-
success: true,
|
|
1788
|
-
message: `Search triggered for ${bookIds.length} book(s)`,
|
|
1789
|
-
commandId: result.id,
|
|
1790
|
-
}, null, 2),
|
|
1791
|
-
}],
|
|
1792
|
-
};
|
|
1793
|
-
}
|
|
1794
|
-
case "readarr_search_missing": {
|
|
1795
|
-
if (!clients.readarr)
|
|
1796
|
-
throw new Error("Readarr not configured");
|
|
1797
|
-
const authorId = args.authorId;
|
|
1798
|
-
const result = await clients.readarr.searchMissingBooks(authorId);
|
|
1799
|
-
return {
|
|
1800
|
-
content: [{
|
|
1801
|
-
type: "text",
|
|
1802
|
-
text: JSON.stringify({
|
|
1803
|
-
success: true,
|
|
1804
|
-
message: `Search triggered for missing books`,
|
|
1805
|
-
commandId: result.id,
|
|
1806
|
-
}, null, 2),
|
|
1807
|
-
}],
|
|
1808
|
-
};
|
|
1809
|
-
}
|
|
1810
|
-
case "readarr_get_calendar": {
|
|
1811
|
-
if (!clients.readarr)
|
|
1812
|
-
throw new Error("Readarr not configured");
|
|
1813
|
-
const days = args?.days || 30;
|
|
1814
|
-
const start = new Date().toISOString().split('T')[0];
|
|
1815
|
-
const end = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
|
|
1816
|
-
const calendar = await clients.readarr.getCalendar(start, end);
|
|
1817
|
-
return {
|
|
1818
|
-
content: [{
|
|
1819
|
-
type: "text",
|
|
1820
|
-
text: JSON.stringify({
|
|
1821
|
-
count: calendar.length,
|
|
1822
|
-
books: calendar.map(b => ({
|
|
1823
|
-
id: b.id,
|
|
1824
|
-
title: b.title,
|
|
1825
|
-
authorId: b.authorId,
|
|
1826
|
-
releaseDate: b.releaseDate,
|
|
1827
|
-
monitored: b.monitored,
|
|
1828
|
-
})),
|
|
1829
|
-
}, null, 2),
|
|
1830
|
-
}],
|
|
1831
|
-
};
|
|
1832
|
-
}
|
|
1833
|
-
case "readarr_add_author": {
|
|
1834
|
-
if (!clients.readarr)
|
|
1835
|
-
throw new Error("Readarr not configured");
|
|
1836
|
-
const { foreignAuthorId, authorName, qualityProfileId, metadataProfileId, rootFolderPath, monitored, tags } = args;
|
|
1837
|
-
const added = await clients.readarr.addAuthor({
|
|
1838
|
-
foreignAuthorId, authorName, qualityProfileId, metadataProfileId, rootFolderPath, monitored, tags: tags ?? [],
|
|
1839
|
-
});
|
|
1840
|
-
return {
|
|
1841
|
-
content: [{
|
|
1842
|
-
type: "text",
|
|
1843
|
-
text: JSON.stringify({
|
|
1844
|
-
success: true,
|
|
1845
|
-
message: `Added "${added.authorName}" to Readarr`,
|
|
1846
|
-
id: added.id,
|
|
1847
|
-
path: added.path,
|
|
1848
|
-
monitored: added.monitored,
|
|
1849
|
-
}, null, 2),
|
|
1850
|
-
}],
|
|
1851
|
-
};
|
|
1852
|
-
}
|
|
1853
|
-
case "readarr_get_root_folders": {
|
|
1854
|
-
if (!clients.readarr)
|
|
1855
|
-
throw new Error("Readarr not configured");
|
|
1856
|
-
const folders = await clients.readarr.getRootFolders();
|
|
1857
|
-
return {
|
|
1858
|
-
content: [{
|
|
1859
|
-
type: "text",
|
|
1860
|
-
text: JSON.stringify(folders, null, 2),
|
|
1861
|
-
}],
|
|
1862
|
-
};
|
|
1863
|
-
}
|
|
1864
|
-
case "readarr_get_quality_profiles": {
|
|
1865
|
-
if (!clients.readarr)
|
|
1866
|
-
throw new Error("Readarr not configured");
|
|
1867
|
-
const profiles = await clients.readarr.getQualityProfiles();
|
|
1868
|
-
return {
|
|
1869
|
-
content: [{
|
|
1870
|
-
type: "text",
|
|
1871
|
-
text: JSON.stringify(profiles.map(p => ({ id: p.id, name: p.name })), null, 2),
|
|
1872
|
-
}],
|
|
1873
|
-
};
|
|
1874
|
-
}
|
|
1875
|
-
case "readarr_get_metadata_profiles": {
|
|
1876
|
-
if (!clients.readarr)
|
|
1877
|
-
throw new Error("Readarr not configured");
|
|
1878
|
-
const profiles = await clients.readarr.getMetadataProfiles();
|
|
1879
|
-
return {
|
|
1880
|
-
content: [{
|
|
1881
|
-
type: "text",
|
|
1882
|
-
text: JSON.stringify(profiles.map(p => ({ id: p.id, name: p.name })), null, 2),
|
|
1883
|
-
}],
|
|
1884
|
-
};
|
|
1885
|
-
}
|
|
1886
1456
|
// Prowlarr handlers
|
|
1887
1457
|
case "prowlarr_get_indexers": {
|
|
1888
1458
|
if (!clients.prowlarr)
|
|
@@ -1996,15 +1566,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1996
1566
|
results.lidarr = { error: e instanceof Error ? e.message : String(e) };
|
|
1997
1567
|
}
|
|
1998
1568
|
}
|
|
1999
|
-
if (clients.readarr) {
|
|
2000
|
-
try {
|
|
2001
|
-
const readarrResults = await clients.readarr.searchAuthors(term);
|
|
2002
|
-
results.readarr = { count: readarrResults.length, results: readarrResults.slice(0, 5) };
|
|
2003
|
-
}
|
|
2004
|
-
catch (e) {
|
|
2005
|
-
results.readarr = { error: e instanceof Error ? e.message : String(e) };
|
|
2006
|
-
}
|
|
2007
|
-
}
|
|
2008
1569
|
return {
|
|
2009
1570
|
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
2010
1571
|
};
|