@thelord/mcp-arr 1.1.0 → 1.2.1

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/dist/index.js CHANGED
@@ -239,6 +239,339 @@ if (clients.sonarr) {
239
239
  },
240
240
  required: ["episodeIds"],
241
241
  },
242
+ }, {
243
+ name: "sonarr_add_series",
244
+ description: "Add a new TV series to Sonarr",
245
+ inputSchema: {
246
+ type: "object",
247
+ properties: {
248
+ tvdbId: {
249
+ type: "number",
250
+ description: "TVDB ID for the series (get from sonarr_search)",
251
+ },
252
+ title: {
253
+ type: "string",
254
+ description: "Series title",
255
+ },
256
+ qualityProfileId: {
257
+ type: "number",
258
+ description: "Quality profile ID (get from sonarr_get_quality_profiles)",
259
+ },
260
+ rootFolderPath: {
261
+ type: "string",
262
+ description: "Root folder path (get from sonarr_get_root_folders)",
263
+ },
264
+ seasonFolder: {
265
+ type: "boolean",
266
+ description: "Use season folders (default: true)",
267
+ },
268
+ monitored: {
269
+ type: "boolean",
270
+ description: "Monitor series (default: true)",
271
+ },
272
+ tags: {
273
+ type: "array",
274
+ items: { type: "number" },
275
+ description: "Tag IDs to apply",
276
+ },
277
+ },
278
+ required: ["tvdbId", "qualityProfileId", "rootFolderPath"],
279
+ },
280
+ }, {
281
+ name: "sonarr_update_series",
282
+ description: "Update an existing series configuration (monitored, tags, quality profile, path, etc.)",
283
+ inputSchema: {
284
+ type: "object",
285
+ properties: {
286
+ seriesId: {
287
+ type: "number",
288
+ description: "Series ID to update",
289
+ },
290
+ monitored: {
291
+ type: "boolean",
292
+ description: "Monitor series",
293
+ },
294
+ qualityProfileId: {
295
+ type: "number",
296
+ description: "Quality profile ID",
297
+ },
298
+ tags: {
299
+ type: "array",
300
+ items: { type: "number" },
301
+ description: "Tag IDs to apply",
302
+ },
303
+ seasonFolder: {
304
+ type: "boolean",
305
+ description: "Use season folders",
306
+ },
307
+ seriesType: {
308
+ type: "string",
309
+ description: "Series type (standard, daily, anime)",
310
+ },
311
+ },
312
+ required: ["seriesId"],
313
+ },
314
+ }, {
315
+ name: "sonarr_delete_series",
316
+ description: "Delete a series from Sonarr library. Optionally delete files and add to exclusion list.",
317
+ inputSchema: {
318
+ type: "object",
319
+ properties: {
320
+ seriesId: {
321
+ type: "number",
322
+ description: "Series ID to delete",
323
+ },
324
+ deleteFiles: {
325
+ type: "boolean",
326
+ description: "Delete all files for this series (default: false)",
327
+ },
328
+ addImportListExclusion: {
329
+ type: "boolean",
330
+ description: "Add series to import list exclusion (default: false)",
331
+ },
332
+ },
333
+ required: ["seriesId"],
334
+ },
335
+ }, {
336
+ name: "sonarr_update_episode",
337
+ description: "Update episode settings (monitored status)",
338
+ inputSchema: {
339
+ type: "object",
340
+ properties: {
341
+ episodeId: {
342
+ type: "number",
343
+ description: "Episode ID to update",
344
+ },
345
+ monitored: {
346
+ type: "boolean",
347
+ description: "Monitor episode",
348
+ },
349
+ },
350
+ required: ["episodeId"],
351
+ },
352
+ }, {
353
+ name: "sonarr_delete_episode_file",
354
+ description: "Delete an episode file. Useful for removing bad quality files to force re-download.",
355
+ inputSchema: {
356
+ type: "object",
357
+ properties: {
358
+ episodeFileId: {
359
+ type: "number",
360
+ description: "Episode file ID to delete",
361
+ },
362
+ },
363
+ required: ["episodeFileId"],
364
+ },
365
+ }, {
366
+ name: "sonarr_refresh_series",
367
+ description: "Refresh series metadata from TheTVDB",
368
+ inputSchema: {
369
+ type: "object",
370
+ properties: {
371
+ seriesId: {
372
+ type: "number",
373
+ description: "Series ID to refresh",
374
+ },
375
+ },
376
+ required: ["seriesId"],
377
+ },
378
+ }, {
379
+ name: "sonarr_rescan_series",
380
+ description: "Rescan series disk for new/changed files",
381
+ inputSchema: {
382
+ type: "object",
383
+ properties: {
384
+ seriesId: {
385
+ type: "number",
386
+ description: "Series ID to rescan",
387
+ },
388
+ },
389
+ required: ["seriesId"],
390
+ },
391
+ }, {
392
+ name: "sonarr_rename_series",
393
+ description: "Rename series files according to Sonarr naming conventions",
394
+ inputSchema: {
395
+ type: "object",
396
+ properties: {
397
+ seriesId: {
398
+ type: "number",
399
+ description: "Series ID to rename",
400
+ },
401
+ },
402
+ required: ["seriesId"],
403
+ },
404
+ }, {
405
+ name: "sonarr_rename_episodes",
406
+ description: "Rename all episode files for a series",
407
+ inputSchema: {
408
+ type: "object",
409
+ properties: {
410
+ seriesId: {
411
+ type: "number",
412
+ description: "Series ID",
413
+ },
414
+ },
415
+ required: ["seriesId"],
416
+ },
417
+ }, {
418
+ name: "sonarr_get_releases",
419
+ description: "Get grabbed releases (pushes). Shows recent downloads and their status.",
420
+ inputSchema: {
421
+ type: "object",
422
+ properties: {
423
+ seriesId: {
424
+ type: "number",
425
+ description: "Optional: filter to specific series ID",
426
+ },
427
+ page: {
428
+ type: "number",
429
+ description: "Page number (default: 1)",
430
+ },
431
+ pageSize: {
432
+ type: "number",
433
+ description: "Items per page (default: 10)",
434
+ },
435
+ },
436
+ required: [],
437
+ },
438
+ }, {
439
+ name: "sonarr_delete_release",
440
+ description: "Delete a release from history. Use sonarr_get_releases to find the GUID.",
441
+ inputSchema: {
442
+ type: "object",
443
+ properties: {
444
+ guid: {
445
+ type: "string",
446
+ description: "Release GUID (from sonarr_get_releases)",
447
+ },
448
+ },
449
+ required: ["guid"],
450
+ },
451
+ }, {
452
+ name: "sonarr_create_release_profile",
453
+ description: "Create a release profile to control which releases are grabbed or rejected",
454
+ inputSchema: {
455
+ type: "object",
456
+ properties: {
457
+ name: {
458
+ type: "string",
459
+ description: "Profile name",
460
+ },
461
+ enabled: {
462
+ type: "boolean",
463
+ description: "Enable profile (default: true)",
464
+ },
465
+ required: {
466
+ type: "array",
467
+ items: { type: "string" },
468
+ description: "Required terms (releases must contain)",
469
+ },
470
+ ignored: {
471
+ type: "array",
472
+ items: { type: "string" },
473
+ description: "Ignored terms (releases will be rejected if they contain these)",
474
+ },
475
+ tags: {
476
+ type: "array",
477
+ items: { type: "number" },
478
+ description: "Tag IDs to apply this profile to",
479
+ },
480
+ indexerId: {
481
+ type: "number",
482
+ description: "Specific indexer ID to apply profile to",
483
+ },
484
+ },
485
+ required: ["name"],
486
+ },
487
+ }, {
488
+ name: "sonarr_update_release_profile",
489
+ description: "Update an existing release profile",
490
+ inputSchema: {
491
+ type: "object",
492
+ properties: {
493
+ profileId: {
494
+ type: "number",
495
+ description: "Release profile ID to update",
496
+ },
497
+ enabled: {
498
+ type: "boolean",
499
+ description: "Enable/disable profile",
500
+ },
501
+ required: {
502
+ type: "array",
503
+ items: { type: "string" },
504
+ description: "Required terms",
505
+ },
506
+ ignored: {
507
+ type: "array",
508
+ items: { type: "string" },
509
+ description: "Ignored terms",
510
+ },
511
+ tags: {
512
+ type: "array",
513
+ items: { type: "number" },
514
+ description: "Tag IDs",
515
+ },
516
+ },
517
+ required: ["profileId"],
518
+ },
519
+ }, {
520
+ name: "sonarr_delete_release_profile",
521
+ description: "Delete a release profile",
522
+ inputSchema: {
523
+ type: "object",
524
+ properties: {
525
+ profileId: {
526
+ type: "number",
527
+ description: "Release profile ID to delete",
528
+ },
529
+ },
530
+ required: ["profileId"],
531
+ },
532
+ }, {
533
+ name: "sonarr_create_tag",
534
+ description: "Create a new tag",
535
+ inputSchema: {
536
+ type: "object",
537
+ properties: {
538
+ label: {
539
+ type: "string",
540
+ description: "Tag label/name",
541
+ },
542
+ },
543
+ required: ["label"],
544
+ },
545
+ }, {
546
+ name: "sonarr_update_tag",
547
+ description: "Update a tag label",
548
+ inputSchema: {
549
+ type: "object",
550
+ properties: {
551
+ tagId: {
552
+ type: "number",
553
+ description: "Tag ID to update",
554
+ },
555
+ label: {
556
+ type: "string",
557
+ description: "New tag label",
558
+ },
559
+ },
560
+ required: ["tagId", "label"],
561
+ },
562
+ }, {
563
+ name: "sonarr_delete_tag",
564
+ description: "Delete a tag",
565
+ inputSchema: {
566
+ type: "object",
567
+ properties: {
568
+ tagId: {
569
+ type: "number",
570
+ description: "Tag ID to delete",
571
+ },
572
+ },
573
+ required: ["tagId"],
574
+ },
242
575
  });
243
576
  }
244
577
  // Radarr tools
@@ -1137,6 +1470,304 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1137
1470
  }],
1138
1471
  };
1139
1472
  }
1473
+ case "sonarr_add_series": {
1474
+ if (!clients.sonarr)
1475
+ throw new Error("Sonarr not configured");
1476
+ const { tvdbId, title, qualityProfileId, rootFolderPath, seasonFolder, monitored, tags } = args;
1477
+ const series = await clients.sonarr.addSeries({
1478
+ tvdbId,
1479
+ title,
1480
+ qualityProfileId,
1481
+ rootFolderPath,
1482
+ seasonFolder,
1483
+ monitored,
1484
+ tags,
1485
+ });
1486
+ return {
1487
+ content: [{
1488
+ type: "text",
1489
+ text: JSON.stringify({
1490
+ success: true,
1491
+ message: `Series '${title}' added to Sonarr`,
1492
+ seriesId: series.id,
1493
+ series: {
1494
+ id: series.id,
1495
+ title: series.title,
1496
+ path: series.path,
1497
+ monitored: series.monitored,
1498
+ },
1499
+ }, null, 2),
1500
+ }],
1501
+ };
1502
+ }
1503
+ case "sonarr_update_series": {
1504
+ if (!clients.sonarr)
1505
+ throw new Error("Sonarr not configured");
1506
+ const { seriesId, ...updates } = args;
1507
+ const series = await clients.sonarr.updateSeries(seriesId, updates);
1508
+ return {
1509
+ content: [{
1510
+ type: "text",
1511
+ text: JSON.stringify({
1512
+ success: true,
1513
+ message: `Series '${series.title}' updated`,
1514
+ seriesId: series.id,
1515
+ updates: Object.keys(updates),
1516
+ }, null, 2),
1517
+ }],
1518
+ };
1519
+ }
1520
+ case "sonarr_delete_series": {
1521
+ if (!clients.sonarr)
1522
+ throw new Error("Sonarr not configured");
1523
+ const { seriesId, deleteFiles = false, addImportListExclusion = false } = args;
1524
+ await clients.sonarr.deleteSeries(seriesId, deleteFiles, addImportListExclusion);
1525
+ return {
1526
+ content: [{
1527
+ type: "text",
1528
+ text: JSON.stringify({
1529
+ success: true,
1530
+ message: `Series deleted (files: ${deleteFiles}, exclude: ${addImportListExclusion})`,
1531
+ }, null, 2),
1532
+ }],
1533
+ };
1534
+ }
1535
+ case "sonarr_update_episode": {
1536
+ if (!clients.sonarr)
1537
+ throw new Error("Sonarr not configured");
1538
+ const { episodeId, monitored } = args;
1539
+ const episode = await clients.sonarr.updateEpisode(episodeId, { monitored });
1540
+ return {
1541
+ content: [{
1542
+ type: "text",
1543
+ text: JSON.stringify({
1544
+ success: true,
1545
+ message: `Episode ${episodeId} ${monitored ? 'monitored' : 'unmonitored'}`,
1546
+ episodeId: episode.id,
1547
+ monitored: episode.monitored,
1548
+ }, null, 2),
1549
+ }],
1550
+ };
1551
+ }
1552
+ case "sonarr_delete_episode_file": {
1553
+ if (!clients.sonarr)
1554
+ throw new Error("Sonarr not configured");
1555
+ const { episodeFileId } = args;
1556
+ await clients.sonarr.deleteEpisodeFile(episodeFileId);
1557
+ return {
1558
+ content: [{
1559
+ type: "text",
1560
+ text: JSON.stringify({
1561
+ success: true,
1562
+ message: `Episode file ${episodeFileId} deleted`,
1563
+ }, null, 2),
1564
+ }],
1565
+ };
1566
+ }
1567
+ case "sonarr_refresh_series": {
1568
+ if (!clients.sonarr)
1569
+ throw new Error("Sonarr not configured");
1570
+ const { seriesId } = args;
1571
+ const result = await clients.sonarr.refreshSeries(seriesId);
1572
+ return {
1573
+ content: [{
1574
+ type: "text",
1575
+ text: JSON.stringify({
1576
+ success: true,
1577
+ message: `Series ${seriesId} metadata refresh triggered`,
1578
+ commandId: result.id,
1579
+ }, null, 2),
1580
+ }],
1581
+ };
1582
+ }
1583
+ case "sonarr_rescan_series": {
1584
+ if (!clients.sonarr)
1585
+ throw new Error("Sonarr not configured");
1586
+ const { seriesId } = args;
1587
+ const result = await clients.sonarr.rescanSeries(seriesId);
1588
+ return {
1589
+ content: [{
1590
+ type: "text",
1591
+ text: JSON.stringify({
1592
+ success: true,
1593
+ message: `Series ${seriesId} disk rescan triggered`,
1594
+ commandId: result.id,
1595
+ }, null, 2),
1596
+ }],
1597
+ };
1598
+ }
1599
+ case "sonarr_rename_series": {
1600
+ if (!clients.sonarr)
1601
+ throw new Error("Sonarr not configured");
1602
+ const { seriesId } = args;
1603
+ const result = await clients.sonarr.renameSeries(seriesId);
1604
+ return {
1605
+ content: [{
1606
+ type: "text",
1607
+ text: JSON.stringify({
1608
+ success: true,
1609
+ message: `Series ${seriesId} files will be renamed`,
1610
+ commandId: result.id,
1611
+ }, null, 2),
1612
+ }],
1613
+ };
1614
+ }
1615
+ case "sonarr_rename_episodes": {
1616
+ if (!clients.sonarr)
1617
+ throw new Error("Sonarr not configured");
1618
+ const { seriesId } = args;
1619
+ const result = await clients.sonarr.renameEpisodes(seriesId);
1620
+ return {
1621
+ content: [{
1622
+ type: "text",
1623
+ text: JSON.stringify({
1624
+ success: true,
1625
+ message: `All episodes for series ${seriesId} will be renamed`,
1626
+ commandId: result.id,
1627
+ }, null, 2),
1628
+ }],
1629
+ };
1630
+ }
1631
+ case "sonarr_get_releases": {
1632
+ if (!clients.sonarr)
1633
+ throw new Error("Sonarr not configured");
1634
+ const { seriesId, page = 1, pageSize = 10 } = args;
1635
+ const releases = await clients.sonarr.getReleases(seriesId, page, pageSize);
1636
+ return {
1637
+ content: [{
1638
+ type: "text",
1639
+ text: JSON.stringify({
1640
+ totalRecords: releases.length,
1641
+ page,
1642
+ pageSize,
1643
+ releases: releases.map((r) => ({
1644
+ guid: r.guid,
1645
+ title: r.title,
1646
+ indexer: r.indexer,
1647
+ quality: r.quality,
1648
+ size: formatBytes(r.size),
1649
+ age: r.age,
1650
+ ageHours: r.ageHours,
1651
+ rejected: r.rejected,
1652
+ rejectionReason: r.rejectionReason,
1653
+ publishedDate: r.publishedDate,
1654
+ })),
1655
+ }, null, 2),
1656
+ }],
1657
+ };
1658
+ }
1659
+ case "sonarr_delete_release": {
1660
+ if (!clients.sonarr)
1661
+ throw new Error("Sonarr not configured");
1662
+ const { guid } = args;
1663
+ await clients.sonarr.deleteRelease(guid);
1664
+ return {
1665
+ content: [{
1666
+ type: "text",
1667
+ text: JSON.stringify({
1668
+ success: true,
1669
+ message: `Release ${guid} deleted from history`,
1670
+ }, null, 2),
1671
+ }],
1672
+ };
1673
+ }
1674
+ case "sonarr_create_release_profile": {
1675
+ if (!clients.sonarr)
1676
+ throw new Error("Sonarr not configured");
1677
+ const profile = await clients.sonarr.createReleaseProfile(args);
1678
+ return {
1679
+ content: [{
1680
+ type: "text",
1681
+ text: JSON.stringify({
1682
+ success: true,
1683
+ message: `Release profile '${profile.name}' created`,
1684
+ profileId: profile.id,
1685
+ profile,
1686
+ }, null, 2),
1687
+ }],
1688
+ };
1689
+ }
1690
+ case "sonarr_update_release_profile": {
1691
+ if (!clients.sonarr)
1692
+ throw new Error("Sonarr not configured");
1693
+ const { profileId, ...updates } = args;
1694
+ const profile = await clients.sonarr.updateReleaseProfile(profileId, updates);
1695
+ return {
1696
+ content: [{
1697
+ type: "text",
1698
+ text: JSON.stringify({
1699
+ success: true,
1700
+ message: `Release profile ${profileId} updated`,
1701
+ profileId: profile.id,
1702
+ profile,
1703
+ }, null, 2),
1704
+ }],
1705
+ };
1706
+ }
1707
+ case "sonarr_delete_release_profile": {
1708
+ if (!clients.sonarr)
1709
+ throw new Error("Sonarr not configured");
1710
+ const { profileId } = args;
1711
+ await clients.sonarr.deleteReleaseProfile(profileId);
1712
+ return {
1713
+ content: [{
1714
+ type: "text",
1715
+ text: JSON.stringify({
1716
+ success: true,
1717
+ message: `Release profile ${profileId} deleted`,
1718
+ }, null, 2),
1719
+ }],
1720
+ };
1721
+ }
1722
+ case "sonarr_create_tag": {
1723
+ if (!clients.sonarr)
1724
+ throw new Error("Sonarr not configured");
1725
+ const { label } = args;
1726
+ const tag = await clients.sonarr.createTag(label);
1727
+ return {
1728
+ content: [{
1729
+ type: "text",
1730
+ text: JSON.stringify({
1731
+ success: true,
1732
+ message: `Tag '${label}' created`,
1733
+ tagId: tag.id,
1734
+ tag,
1735
+ }, null, 2),
1736
+ }],
1737
+ };
1738
+ }
1739
+ case "sonarr_update_tag": {
1740
+ if (!clients.sonarr)
1741
+ throw new Error("Sonarr not configured");
1742
+ const { tagId, label } = args;
1743
+ const tag = await clients.sonarr.updateTag(tagId, label);
1744
+ return {
1745
+ content: [{
1746
+ type: "text",
1747
+ text: JSON.stringify({
1748
+ success: true,
1749
+ message: `Tag ${tagId} updated to '${label}'`,
1750
+ tagId: tag.id,
1751
+ tag,
1752
+ }, null, 2),
1753
+ }],
1754
+ };
1755
+ }
1756
+ case "sonarr_delete_tag": {
1757
+ if (!clients.sonarr)
1758
+ throw new Error("Sonarr not configured");
1759
+ const { tagId } = args;
1760
+ await clients.sonarr.deleteTag(tagId);
1761
+ return {
1762
+ content: [{
1763
+ type: "text",
1764
+ text: JSON.stringify({
1765
+ success: true,
1766
+ message: `Tag ${tagId} deleted`,
1767
+ }, null, 2),
1768
+ }],
1769
+ };
1770
+ }
1140
1771
  // Radarr handlers
1141
1772
  case "radarr_get_movies": {
1142
1773
  if (!clients.radarr)