@thelord/mcp-arr 1.3.0 → 1.5.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.
- package/dist/arr-client.d.ts +416 -3
- package/dist/arr-client.d.ts.map +1 -1
- package/dist/arr-client.js +383 -4
- package/dist/arr-client.js.map +1 -1
- package/dist/index.js +2097 -509
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -572,6 +572,99 @@ if (clients.sonarr) {
|
|
|
572
572
|
},
|
|
573
573
|
required: ["tagId"],
|
|
574
574
|
},
|
|
575
|
+
}, {
|
|
576
|
+
name: "sonarr_get_history",
|
|
577
|
+
description: "Get Sonarr download/import history",
|
|
578
|
+
inputSchema: {
|
|
579
|
+
type: "object",
|
|
580
|
+
properties: {
|
|
581
|
+
page: {
|
|
582
|
+
type: "number",
|
|
583
|
+
description: "Page number (default: 1)",
|
|
584
|
+
},
|
|
585
|
+
pageSize: {
|
|
586
|
+
type: "number",
|
|
587
|
+
description: "Items per page (default: 20)",
|
|
588
|
+
},
|
|
589
|
+
seriesId: {
|
|
590
|
+
type: "number",
|
|
591
|
+
description: "Optional: filter to specific series ID",
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
required: [],
|
|
595
|
+
},
|
|
596
|
+
}, {
|
|
597
|
+
name: "sonarr_get_wanted",
|
|
598
|
+
description: "Get list of missing/wanted episodes",
|
|
599
|
+
inputSchema: {
|
|
600
|
+
type: "object",
|
|
601
|
+
properties: {
|
|
602
|
+
page: {
|
|
603
|
+
type: "number",
|
|
604
|
+
description: "Page number (default: 1)",
|
|
605
|
+
},
|
|
606
|
+
pageSize: {
|
|
607
|
+
type: "number",
|
|
608
|
+
description: "Items per page (default: 20)",
|
|
609
|
+
},
|
|
610
|
+
},
|
|
611
|
+
required: [],
|
|
612
|
+
},
|
|
613
|
+
}, {
|
|
614
|
+
name: "sonarr_get_cutoff_unmet",
|
|
615
|
+
description: "Get episodes with quality below cutoff that could be upgraded",
|
|
616
|
+
inputSchema: {
|
|
617
|
+
type: "object",
|
|
618
|
+
properties: {
|
|
619
|
+
page: {
|
|
620
|
+
type: "number",
|
|
621
|
+
description: "Page number (default: 1)",
|
|
622
|
+
},
|
|
623
|
+
pageSize: {
|
|
624
|
+
type: "number",
|
|
625
|
+
description: "Items per page (default: 20)",
|
|
626
|
+
},
|
|
627
|
+
},
|
|
628
|
+
required: [],
|
|
629
|
+
},
|
|
630
|
+
}, {
|
|
631
|
+
name: "sonarr_get_blocklist",
|
|
632
|
+
description: "Get blocklisted releases (releases that failed or were manually blocklisted)",
|
|
633
|
+
inputSchema: {
|
|
634
|
+
type: "object",
|
|
635
|
+
properties: {
|
|
636
|
+
page: {
|
|
637
|
+
type: "number",
|
|
638
|
+
description: "Page number (default: 1)",
|
|
639
|
+
},
|
|
640
|
+
pageSize: {
|
|
641
|
+
type: "number",
|
|
642
|
+
description: "Items per page (default: 20)",
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
required: [],
|
|
646
|
+
},
|
|
647
|
+
}, {
|
|
648
|
+
name: "sonarr_delete_blocklist_item",
|
|
649
|
+
description: "Remove an item from the blocklist",
|
|
650
|
+
inputSchema: {
|
|
651
|
+
type: "object",
|
|
652
|
+
properties: {
|
|
653
|
+
blocklistId: {
|
|
654
|
+
type: "number",
|
|
655
|
+
description: "Blocklist item ID to delete",
|
|
656
|
+
},
|
|
657
|
+
},
|
|
658
|
+
required: ["blocklistId"],
|
|
659
|
+
},
|
|
660
|
+
}, {
|
|
661
|
+
name: "sonarr_search_all_missing",
|
|
662
|
+
description: "Trigger a search for all missing episodes across all series",
|
|
663
|
+
inputSchema: {
|
|
664
|
+
type: "object",
|
|
665
|
+
properties: {},
|
|
666
|
+
required: [],
|
|
667
|
+
},
|
|
575
668
|
});
|
|
576
669
|
}
|
|
577
670
|
// Radarr tools
|
|
@@ -619,190 +712,526 @@ if (clients.radarr) {
|
|
|
619
712
|
required: [],
|
|
620
713
|
},
|
|
621
714
|
}, {
|
|
622
|
-
name: "
|
|
623
|
-
description: "
|
|
715
|
+
name: "radarr_add_movie",
|
|
716
|
+
description: "Add a new movie to Radarr",
|
|
624
717
|
inputSchema: {
|
|
625
718
|
type: "object",
|
|
626
719
|
properties: {
|
|
627
|
-
|
|
720
|
+
tmdbId: {
|
|
628
721
|
type: "number",
|
|
629
|
-
description: "
|
|
722
|
+
description: "TMDB ID for the movie (get from radarr_search)",
|
|
723
|
+
},
|
|
724
|
+
title: {
|
|
725
|
+
type: "string",
|
|
726
|
+
description: "Movie title",
|
|
727
|
+
},
|
|
728
|
+
qualityProfileId: {
|
|
729
|
+
type: "number",
|
|
730
|
+
description: "Quality profile ID (get from radarr_get_quality_profiles)",
|
|
731
|
+
},
|
|
732
|
+
rootFolderPath: {
|
|
733
|
+
type: "string",
|
|
734
|
+
description: "Root folder path (get from radarr_get_root_folders)",
|
|
735
|
+
},
|
|
736
|
+
monitored: {
|
|
737
|
+
type: "boolean",
|
|
738
|
+
description: "Monitor movie (default: true)",
|
|
739
|
+
},
|
|
740
|
+
minimumAvailability: {
|
|
741
|
+
type: "string",
|
|
742
|
+
description: "When to consider movie available: announced, inCinemas, released (default: released)",
|
|
743
|
+
},
|
|
744
|
+
tags: {
|
|
745
|
+
type: "array",
|
|
746
|
+
items: { type: "number" },
|
|
747
|
+
description: "Tag IDs to apply",
|
|
630
748
|
},
|
|
631
749
|
},
|
|
632
|
-
required: ["
|
|
750
|
+
required: ["tmdbId", "qualityProfileId", "rootFolderPath"],
|
|
633
751
|
},
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
if (clients.lidarr) {
|
|
638
|
-
TOOLS.push({
|
|
639
|
-
name: "lidarr_get_artists",
|
|
640
|
-
description: "Get all artists in Lidarr library",
|
|
752
|
+
}, {
|
|
753
|
+
name: "radarr_update_movie",
|
|
754
|
+
description: "Update an existing movie configuration (monitored, tags, quality profile, etc.)",
|
|
641
755
|
inputSchema: {
|
|
642
756
|
type: "object",
|
|
643
|
-
properties: {
|
|
644
|
-
|
|
757
|
+
properties: {
|
|
758
|
+
movieId: {
|
|
759
|
+
type: "number",
|
|
760
|
+
description: "Movie ID to update",
|
|
761
|
+
},
|
|
762
|
+
monitored: {
|
|
763
|
+
type: "boolean",
|
|
764
|
+
description: "Monitor movie",
|
|
765
|
+
},
|
|
766
|
+
qualityProfileId: {
|
|
767
|
+
type: "number",
|
|
768
|
+
description: "Quality profile ID",
|
|
769
|
+
},
|
|
770
|
+
minimumAvailability: {
|
|
771
|
+
type: "string",
|
|
772
|
+
description: "Minimum availability: announced, inCinemas, released",
|
|
773
|
+
},
|
|
774
|
+
tags: {
|
|
775
|
+
type: "array",
|
|
776
|
+
items: { type: "number" },
|
|
777
|
+
description: "Tag IDs to apply",
|
|
778
|
+
},
|
|
779
|
+
},
|
|
780
|
+
required: ["movieId"],
|
|
645
781
|
},
|
|
646
782
|
}, {
|
|
647
|
-
name: "
|
|
648
|
-
description: "
|
|
783
|
+
name: "radarr_delete_movie",
|
|
784
|
+
description: "Delete a movie from Radarr library. Optionally delete files and add to exclusion list.",
|
|
649
785
|
inputSchema: {
|
|
650
786
|
type: "object",
|
|
651
787
|
properties: {
|
|
652
|
-
|
|
653
|
-
type: "
|
|
654
|
-
description: "
|
|
788
|
+
movieId: {
|
|
789
|
+
type: "number",
|
|
790
|
+
description: "Movie ID to delete",
|
|
791
|
+
},
|
|
792
|
+
deleteFiles: {
|
|
793
|
+
type: "boolean",
|
|
794
|
+
description: "Delete all files for this movie (default: false)",
|
|
795
|
+
},
|
|
796
|
+
addImportExclusion: {
|
|
797
|
+
type: "boolean",
|
|
798
|
+
description: "Add movie to import exclusion list (default: false)",
|
|
655
799
|
},
|
|
656
800
|
},
|
|
657
|
-
required: ["
|
|
801
|
+
required: ["movieId"],
|
|
658
802
|
},
|
|
659
803
|
}, {
|
|
660
|
-
name: "
|
|
661
|
-
description: "
|
|
804
|
+
name: "radarr_search_movie",
|
|
805
|
+
description: "Trigger a search to download a movie that's already in your library",
|
|
662
806
|
inputSchema: {
|
|
663
807
|
type: "object",
|
|
664
|
-
properties: {
|
|
665
|
-
|
|
808
|
+
properties: {
|
|
809
|
+
movieId: {
|
|
810
|
+
type: "number",
|
|
811
|
+
description: "Movie ID to search for",
|
|
812
|
+
},
|
|
813
|
+
},
|
|
814
|
+
required: ["movieId"],
|
|
666
815
|
},
|
|
667
816
|
}, {
|
|
668
|
-
name: "
|
|
669
|
-
description: "Get
|
|
817
|
+
name: "radarr_get_history",
|
|
818
|
+
description: "Get Radarr download/import history",
|
|
670
819
|
inputSchema: {
|
|
671
820
|
type: "object",
|
|
672
821
|
properties: {
|
|
673
|
-
|
|
822
|
+
page: {
|
|
674
823
|
type: "number",
|
|
675
|
-
description: "
|
|
824
|
+
description: "Page number (default: 1)",
|
|
825
|
+
},
|
|
826
|
+
pageSize: {
|
|
827
|
+
type: "number",
|
|
828
|
+
description: "Items per page (default: 20)",
|
|
829
|
+
},
|
|
830
|
+
movieId: {
|
|
831
|
+
type: "number",
|
|
832
|
+
description: "Optional: filter to specific movie ID",
|
|
676
833
|
},
|
|
677
834
|
},
|
|
678
|
-
required: [
|
|
835
|
+
required: [],
|
|
679
836
|
},
|
|
680
837
|
}, {
|
|
681
|
-
name: "
|
|
682
|
-
description: "
|
|
838
|
+
name: "radarr_refresh_movie",
|
|
839
|
+
description: "Refresh movie metadata from TMDB",
|
|
683
840
|
inputSchema: {
|
|
684
841
|
type: "object",
|
|
685
842
|
properties: {
|
|
686
|
-
|
|
843
|
+
movieId: {
|
|
687
844
|
type: "number",
|
|
688
|
-
description: "
|
|
845
|
+
description: "Movie ID to refresh",
|
|
689
846
|
},
|
|
690
847
|
},
|
|
691
|
-
required: ["
|
|
848
|
+
required: ["movieId"],
|
|
692
849
|
},
|
|
693
850
|
}, {
|
|
694
|
-
name: "
|
|
695
|
-
description: "
|
|
851
|
+
name: "radarr_rescan_movie",
|
|
852
|
+
description: "Rescan movie disk for new/changed files",
|
|
696
853
|
inputSchema: {
|
|
697
854
|
type: "object",
|
|
698
855
|
properties: {
|
|
699
|
-
|
|
856
|
+
movieId: {
|
|
700
857
|
type: "number",
|
|
701
|
-
description: "
|
|
858
|
+
description: "Movie ID to rescan",
|
|
702
859
|
},
|
|
703
860
|
},
|
|
704
|
-
required: ["
|
|
861
|
+
required: ["movieId"],
|
|
705
862
|
},
|
|
706
863
|
}, {
|
|
707
|
-
name: "
|
|
708
|
-
description: "
|
|
864
|
+
name: "radarr_rename_movie",
|
|
865
|
+
description: "Rename movie files according to Radarr naming conventions",
|
|
709
866
|
inputSchema: {
|
|
710
867
|
type: "object",
|
|
711
868
|
properties: {
|
|
712
|
-
|
|
869
|
+
movieId: {
|
|
713
870
|
type: "number",
|
|
714
|
-
description: "
|
|
871
|
+
description: "Movie ID to rename",
|
|
715
872
|
},
|
|
716
873
|
},
|
|
717
|
-
required: [],
|
|
874
|
+
required: ["movieId"],
|
|
718
875
|
},
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if (clients.readarr) {
|
|
723
|
-
TOOLS.push({
|
|
724
|
-
name: "readarr_get_authors",
|
|
725
|
-
description: "Get all authors in Readarr library",
|
|
876
|
+
}, {
|
|
877
|
+
name: "radarr_get_collections",
|
|
878
|
+
description: "Get all movie collections in Radarr",
|
|
726
879
|
inputSchema: {
|
|
727
880
|
type: "object",
|
|
728
881
|
properties: {},
|
|
729
882
|
required: [],
|
|
730
883
|
},
|
|
731
884
|
}, {
|
|
732
|
-
name: "
|
|
733
|
-
description: "
|
|
885
|
+
name: "radarr_update_collection",
|
|
886
|
+
description: "Update a collection (monitored status, quality profile, root folder)",
|
|
734
887
|
inputSchema: {
|
|
735
888
|
type: "object",
|
|
736
889
|
properties: {
|
|
737
|
-
|
|
890
|
+
collectionId: {
|
|
891
|
+
type: "number",
|
|
892
|
+
description: "Collection ID to update",
|
|
893
|
+
},
|
|
894
|
+
monitored: {
|
|
895
|
+
type: "boolean",
|
|
896
|
+
description: "Monitor collection (automatically add new movies)",
|
|
897
|
+
},
|
|
898
|
+
qualityProfileId: {
|
|
899
|
+
type: "number",
|
|
900
|
+
description: "Quality profile ID for new movies",
|
|
901
|
+
},
|
|
902
|
+
rootFolderPath: {
|
|
738
903
|
type: "string",
|
|
739
|
-
description: "
|
|
904
|
+
description: "Root folder for new movies",
|
|
905
|
+
},
|
|
906
|
+
searchOnAdd: {
|
|
907
|
+
type: "boolean",
|
|
908
|
+
description: "Search for movies when added from collection",
|
|
740
909
|
},
|
|
741
910
|
},
|
|
742
|
-
required: ["
|
|
911
|
+
required: ["collectionId"],
|
|
743
912
|
},
|
|
744
913
|
}, {
|
|
745
|
-
name: "
|
|
746
|
-
description: "Get
|
|
914
|
+
name: "radarr_get_movie_files",
|
|
915
|
+
description: "Get movie file details including quality, size, and media info",
|
|
747
916
|
inputSchema: {
|
|
748
917
|
type: "object",
|
|
749
|
-
properties: {
|
|
750
|
-
|
|
918
|
+
properties: {
|
|
919
|
+
movieId: {
|
|
920
|
+
type: "number",
|
|
921
|
+
description: "Movie ID to get files for",
|
|
922
|
+
},
|
|
923
|
+
},
|
|
924
|
+
required: ["movieId"],
|
|
751
925
|
},
|
|
752
926
|
}, {
|
|
753
|
-
name: "
|
|
754
|
-
description: "
|
|
927
|
+
name: "radarr_delete_movie_file",
|
|
928
|
+
description: "Delete a movie file. Useful for removing bad quality files to force re-download.",
|
|
755
929
|
inputSchema: {
|
|
756
930
|
type: "object",
|
|
757
931
|
properties: {
|
|
758
|
-
|
|
932
|
+
movieFileId: {
|
|
759
933
|
type: "number",
|
|
760
|
-
description: "
|
|
934
|
+
description: "Movie file ID to delete",
|
|
761
935
|
},
|
|
762
936
|
},
|
|
763
|
-
required: ["
|
|
937
|
+
required: ["movieFileId"],
|
|
764
938
|
},
|
|
765
939
|
}, {
|
|
766
|
-
name: "
|
|
767
|
-
description: "
|
|
940
|
+
name: "radarr_get_blocklist",
|
|
941
|
+
description: "Get blocklisted releases (releases that failed or were manually blocklisted)",
|
|
768
942
|
inputSchema: {
|
|
769
943
|
type: "object",
|
|
770
944
|
properties: {
|
|
771
|
-
|
|
772
|
-
type: "
|
|
773
|
-
|
|
774
|
-
|
|
945
|
+
page: {
|
|
946
|
+
type: "number",
|
|
947
|
+
description: "Page number (default: 1)",
|
|
948
|
+
},
|
|
949
|
+
pageSize: {
|
|
950
|
+
type: "number",
|
|
951
|
+
description: "Items per page (default: 20)",
|
|
775
952
|
},
|
|
776
953
|
},
|
|
777
|
-
required: [
|
|
954
|
+
required: [],
|
|
778
955
|
},
|
|
779
956
|
}, {
|
|
780
|
-
name: "
|
|
781
|
-
description: "
|
|
957
|
+
name: "radarr_delete_blocklist_item",
|
|
958
|
+
description: "Remove an item from the blocklist",
|
|
782
959
|
inputSchema: {
|
|
783
960
|
type: "object",
|
|
784
961
|
properties: {
|
|
785
|
-
|
|
962
|
+
blocklistId: {
|
|
786
963
|
type: "number",
|
|
787
|
-
description: "
|
|
964
|
+
description: "Blocklist item ID to delete",
|
|
788
965
|
},
|
|
789
966
|
},
|
|
790
|
-
required: ["
|
|
967
|
+
required: ["blocklistId"],
|
|
791
968
|
},
|
|
792
969
|
}, {
|
|
793
|
-
name: "
|
|
794
|
-
description: "Get
|
|
970
|
+
name: "radarr_get_extra_files",
|
|
971
|
+
description: "Get extra files for a movie (subtitles, nfo files, etc.)",
|
|
795
972
|
inputSchema: {
|
|
796
973
|
type: "object",
|
|
797
974
|
properties: {
|
|
798
|
-
|
|
975
|
+
movieId: {
|
|
799
976
|
type: "number",
|
|
800
|
-
description: "
|
|
977
|
+
description: "Movie ID to get extra files for",
|
|
801
978
|
},
|
|
802
979
|
},
|
|
803
|
-
required: [],
|
|
980
|
+
required: ["movieId"],
|
|
804
981
|
},
|
|
805
|
-
}
|
|
982
|
+
}, {
|
|
983
|
+
name: "radarr_get_credits",
|
|
984
|
+
description: "Get cast and crew credits for a movie",
|
|
985
|
+
inputSchema: {
|
|
986
|
+
type: "object",
|
|
987
|
+
properties: {
|
|
988
|
+
movieId: {
|
|
989
|
+
type: "number",
|
|
990
|
+
description: "Movie ID to get credits for",
|
|
991
|
+
},
|
|
992
|
+
},
|
|
993
|
+
required: ["movieId"],
|
|
994
|
+
},
|
|
995
|
+
}, {
|
|
996
|
+
name: "radarr_get_wanted",
|
|
997
|
+
description: "Get list of missing/wanted movies",
|
|
998
|
+
inputSchema: {
|
|
999
|
+
type: "object",
|
|
1000
|
+
properties: {
|
|
1001
|
+
page: {
|
|
1002
|
+
type: "number",
|
|
1003
|
+
description: "Page number (default: 1)",
|
|
1004
|
+
},
|
|
1005
|
+
pageSize: {
|
|
1006
|
+
type: "number",
|
|
1007
|
+
description: "Items per page (default: 20)",
|
|
1008
|
+
},
|
|
1009
|
+
},
|
|
1010
|
+
required: [],
|
|
1011
|
+
},
|
|
1012
|
+
}, {
|
|
1013
|
+
name: "radarr_search_missing",
|
|
1014
|
+
description: "Trigger a search for all missing/wanted movies",
|
|
1015
|
+
inputSchema: {
|
|
1016
|
+
type: "object",
|
|
1017
|
+
properties: {},
|
|
1018
|
+
required: [],
|
|
1019
|
+
},
|
|
1020
|
+
}, {
|
|
1021
|
+
name: "radarr_create_tag",
|
|
1022
|
+
description: "Create a new tag",
|
|
1023
|
+
inputSchema: {
|
|
1024
|
+
type: "object",
|
|
1025
|
+
properties: {
|
|
1026
|
+
label: {
|
|
1027
|
+
type: "string",
|
|
1028
|
+
description: "Tag label/name",
|
|
1029
|
+
},
|
|
1030
|
+
},
|
|
1031
|
+
required: ["label"],
|
|
1032
|
+
},
|
|
1033
|
+
}, {
|
|
1034
|
+
name: "radarr_update_tag",
|
|
1035
|
+
description: "Update a tag label",
|
|
1036
|
+
inputSchema: {
|
|
1037
|
+
type: "object",
|
|
1038
|
+
properties: {
|
|
1039
|
+
tagId: {
|
|
1040
|
+
type: "number",
|
|
1041
|
+
description: "Tag ID to update",
|
|
1042
|
+
},
|
|
1043
|
+
label: {
|
|
1044
|
+
type: "string",
|
|
1045
|
+
description: "New tag label",
|
|
1046
|
+
},
|
|
1047
|
+
},
|
|
1048
|
+
required: ["tagId", "label"],
|
|
1049
|
+
},
|
|
1050
|
+
}, {
|
|
1051
|
+
name: "radarr_delete_tag",
|
|
1052
|
+
description: "Delete a tag",
|
|
1053
|
+
inputSchema: {
|
|
1054
|
+
type: "object",
|
|
1055
|
+
properties: {
|
|
1056
|
+
tagId: {
|
|
1057
|
+
type: "number",
|
|
1058
|
+
description: "Tag ID to delete",
|
|
1059
|
+
},
|
|
1060
|
+
},
|
|
1061
|
+
required: ["tagId"],
|
|
1062
|
+
},
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
// Lidarr tools
|
|
1066
|
+
if (clients.lidarr) {
|
|
1067
|
+
TOOLS.push({
|
|
1068
|
+
name: "lidarr_get_artists",
|
|
1069
|
+
description: "Get all artists in Lidarr library",
|
|
1070
|
+
inputSchema: {
|
|
1071
|
+
type: "object",
|
|
1072
|
+
properties: {},
|
|
1073
|
+
required: [],
|
|
1074
|
+
},
|
|
1075
|
+
}, {
|
|
1076
|
+
name: "lidarr_search",
|
|
1077
|
+
description: "Search for artists to add to Lidarr",
|
|
1078
|
+
inputSchema: {
|
|
1079
|
+
type: "object",
|
|
1080
|
+
properties: {
|
|
1081
|
+
term: {
|
|
1082
|
+
type: "string",
|
|
1083
|
+
description: "Search term (artist name)",
|
|
1084
|
+
},
|
|
1085
|
+
},
|
|
1086
|
+
required: ["term"],
|
|
1087
|
+
},
|
|
1088
|
+
}, {
|
|
1089
|
+
name: "lidarr_get_queue",
|
|
1090
|
+
description: "Get Lidarr download queue",
|
|
1091
|
+
inputSchema: {
|
|
1092
|
+
type: "object",
|
|
1093
|
+
properties: {},
|
|
1094
|
+
required: [],
|
|
1095
|
+
},
|
|
1096
|
+
}, {
|
|
1097
|
+
name: "lidarr_get_albums",
|
|
1098
|
+
description: "Get albums for an artist in Lidarr. Shows which albums are available and which are missing.",
|
|
1099
|
+
inputSchema: {
|
|
1100
|
+
type: "object",
|
|
1101
|
+
properties: {
|
|
1102
|
+
artistId: {
|
|
1103
|
+
type: "number",
|
|
1104
|
+
description: "Artist ID to get albums for",
|
|
1105
|
+
},
|
|
1106
|
+
},
|
|
1107
|
+
required: ["artistId"],
|
|
1108
|
+
},
|
|
1109
|
+
}, {
|
|
1110
|
+
name: "lidarr_search_album",
|
|
1111
|
+
description: "Trigger a search for a specific album to download",
|
|
1112
|
+
inputSchema: {
|
|
1113
|
+
type: "object",
|
|
1114
|
+
properties: {
|
|
1115
|
+
albumId: {
|
|
1116
|
+
type: "number",
|
|
1117
|
+
description: "Album ID to search for",
|
|
1118
|
+
},
|
|
1119
|
+
},
|
|
1120
|
+
required: ["albumId"],
|
|
1121
|
+
},
|
|
1122
|
+
}, {
|
|
1123
|
+
name: "lidarr_search_missing",
|
|
1124
|
+
description: "Trigger a search for all missing albums for an artist",
|
|
1125
|
+
inputSchema: {
|
|
1126
|
+
type: "object",
|
|
1127
|
+
properties: {
|
|
1128
|
+
artistId: {
|
|
1129
|
+
type: "number",
|
|
1130
|
+
description: "Artist ID to search missing albums for",
|
|
1131
|
+
},
|
|
1132
|
+
},
|
|
1133
|
+
required: ["artistId"],
|
|
1134
|
+
},
|
|
1135
|
+
}, {
|
|
1136
|
+
name: "lidarr_get_calendar",
|
|
1137
|
+
description: "Get upcoming album releases from Lidarr",
|
|
1138
|
+
inputSchema: {
|
|
1139
|
+
type: "object",
|
|
1140
|
+
properties: {
|
|
1141
|
+
days: {
|
|
1142
|
+
type: "number",
|
|
1143
|
+
description: "Number of days to look ahead (default: 30)",
|
|
1144
|
+
},
|
|
1145
|
+
},
|
|
1146
|
+
required: [],
|
|
1147
|
+
},
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
// Readarr tools
|
|
1151
|
+
if (clients.readarr) {
|
|
1152
|
+
TOOLS.push({
|
|
1153
|
+
name: "readarr_get_authors",
|
|
1154
|
+
description: "Get all authors in Readarr library",
|
|
1155
|
+
inputSchema: {
|
|
1156
|
+
type: "object",
|
|
1157
|
+
properties: {},
|
|
1158
|
+
required: [],
|
|
1159
|
+
},
|
|
1160
|
+
}, {
|
|
1161
|
+
name: "readarr_search",
|
|
1162
|
+
description: "Search for authors to add to Readarr",
|
|
1163
|
+
inputSchema: {
|
|
1164
|
+
type: "object",
|
|
1165
|
+
properties: {
|
|
1166
|
+
term: {
|
|
1167
|
+
type: "string",
|
|
1168
|
+
description: "Search term (author name)",
|
|
1169
|
+
},
|
|
1170
|
+
},
|
|
1171
|
+
required: ["term"],
|
|
1172
|
+
},
|
|
1173
|
+
}, {
|
|
1174
|
+
name: "readarr_get_queue",
|
|
1175
|
+
description: "Get Readarr download queue",
|
|
1176
|
+
inputSchema: {
|
|
1177
|
+
type: "object",
|
|
1178
|
+
properties: {},
|
|
1179
|
+
required: [],
|
|
1180
|
+
},
|
|
1181
|
+
}, {
|
|
1182
|
+
name: "readarr_get_books",
|
|
1183
|
+
description: "Get books for an author in Readarr. Shows which books are available and which are missing.",
|
|
1184
|
+
inputSchema: {
|
|
1185
|
+
type: "object",
|
|
1186
|
+
properties: {
|
|
1187
|
+
authorId: {
|
|
1188
|
+
type: "number",
|
|
1189
|
+
description: "Author ID to get books for",
|
|
1190
|
+
},
|
|
1191
|
+
},
|
|
1192
|
+
required: ["authorId"],
|
|
1193
|
+
},
|
|
1194
|
+
}, {
|
|
1195
|
+
name: "readarr_search_book",
|
|
1196
|
+
description: "Trigger a search for a specific book to download",
|
|
1197
|
+
inputSchema: {
|
|
1198
|
+
type: "object",
|
|
1199
|
+
properties: {
|
|
1200
|
+
bookIds: {
|
|
1201
|
+
type: "array",
|
|
1202
|
+
items: { type: "number" },
|
|
1203
|
+
description: "Book ID(s) to search for",
|
|
1204
|
+
},
|
|
1205
|
+
},
|
|
1206
|
+
required: ["bookIds"],
|
|
1207
|
+
},
|
|
1208
|
+
}, {
|
|
1209
|
+
name: "readarr_search_missing",
|
|
1210
|
+
description: "Trigger a search for all missing books for an author",
|
|
1211
|
+
inputSchema: {
|
|
1212
|
+
type: "object",
|
|
1213
|
+
properties: {
|
|
1214
|
+
authorId: {
|
|
1215
|
+
type: "number",
|
|
1216
|
+
description: "Author ID to search missing books for",
|
|
1217
|
+
},
|
|
1218
|
+
},
|
|
1219
|
+
required: ["authorId"],
|
|
1220
|
+
},
|
|
1221
|
+
}, {
|
|
1222
|
+
name: "readarr_get_calendar",
|
|
1223
|
+
description: "Get upcoming book releases from Readarr",
|
|
1224
|
+
inputSchema: {
|
|
1225
|
+
type: "object",
|
|
1226
|
+
properties: {
|
|
1227
|
+
days: {
|
|
1228
|
+
type: "number",
|
|
1229
|
+
description: "Number of days to look ahead (default: 30)",
|
|
1230
|
+
},
|
|
1231
|
+
},
|
|
1232
|
+
required: [],
|
|
1233
|
+
},
|
|
1234
|
+
});
|
|
806
1235
|
}
|
|
807
1236
|
// Prowlarr tools
|
|
808
1237
|
if (clients.prowlarr) {
|
|
@@ -843,20 +1272,274 @@ if (clients.prowlarr) {
|
|
|
843
1272
|
properties: {},
|
|
844
1273
|
required: [],
|
|
845
1274
|
},
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
1275
|
+
}, {
|
|
1276
|
+
name: "prowlarr_get_indexer",
|
|
1277
|
+
description: "Get a specific indexer by ID",
|
|
1278
|
+
inputSchema: {
|
|
1279
|
+
type: "object",
|
|
1280
|
+
properties: {
|
|
1281
|
+
indexerId: {
|
|
1282
|
+
type: "number",
|
|
1283
|
+
description: "Indexer ID",
|
|
1284
|
+
},
|
|
1285
|
+
},
|
|
1286
|
+
required: ["indexerId"],
|
|
1287
|
+
},
|
|
1288
|
+
}, {
|
|
1289
|
+
name: "prowlarr_update_indexer",
|
|
1290
|
+
description: "Update an indexer (enable/disable, priority, tags)",
|
|
1291
|
+
inputSchema: {
|
|
1292
|
+
type: "object",
|
|
1293
|
+
properties: {
|
|
1294
|
+
indexerId: {
|
|
1295
|
+
type: "number",
|
|
1296
|
+
description: "Indexer ID to update",
|
|
1297
|
+
},
|
|
1298
|
+
enable: {
|
|
1299
|
+
type: "boolean",
|
|
1300
|
+
description: "Enable or disable the indexer",
|
|
1301
|
+
},
|
|
1302
|
+
priority: {
|
|
1303
|
+
type: "number",
|
|
1304
|
+
description: "Indexer priority (1-50, lower is higher priority)",
|
|
1305
|
+
},
|
|
1306
|
+
tags: {
|
|
1307
|
+
type: "array",
|
|
1308
|
+
items: { type: "number" },
|
|
1309
|
+
description: "Tag IDs to apply",
|
|
1310
|
+
},
|
|
1311
|
+
},
|
|
1312
|
+
required: ["indexerId"],
|
|
1313
|
+
},
|
|
1314
|
+
}, {
|
|
1315
|
+
name: "prowlarr_delete_indexer",
|
|
1316
|
+
description: "Delete an indexer from Prowlarr",
|
|
1317
|
+
inputSchema: {
|
|
1318
|
+
type: "object",
|
|
1319
|
+
properties: {
|
|
1320
|
+
indexerId: {
|
|
1321
|
+
type: "number",
|
|
1322
|
+
description: "Indexer ID to delete",
|
|
1323
|
+
},
|
|
1324
|
+
},
|
|
1325
|
+
required: ["indexerId"],
|
|
1326
|
+
},
|
|
1327
|
+
}, {
|
|
1328
|
+
name: "prowlarr_test_indexer",
|
|
1329
|
+
description: "Test a specific indexer connection",
|
|
1330
|
+
inputSchema: {
|
|
1331
|
+
type: "object",
|
|
1332
|
+
properties: {
|
|
1333
|
+
indexerId: {
|
|
1334
|
+
type: "number",
|
|
1335
|
+
description: "Indexer ID to test",
|
|
1336
|
+
},
|
|
1337
|
+
},
|
|
1338
|
+
required: ["indexerId"],
|
|
1339
|
+
},
|
|
1340
|
+
}, {
|
|
1341
|
+
name: "prowlarr_get_applications",
|
|
1342
|
+
description: "Get all applications (Sonarr, Radarr, etc. connections)",
|
|
1343
|
+
inputSchema: {
|
|
1344
|
+
type: "object",
|
|
1345
|
+
properties: {},
|
|
1346
|
+
required: [],
|
|
1347
|
+
},
|
|
1348
|
+
}, {
|
|
1349
|
+
name: "prowlarr_get_application",
|
|
1350
|
+
description: "Get a specific application by ID",
|
|
1351
|
+
inputSchema: {
|
|
1352
|
+
type: "object",
|
|
1353
|
+
properties: {
|
|
1354
|
+
applicationId: {
|
|
1355
|
+
type: "number",
|
|
1356
|
+
description: "Application ID",
|
|
1357
|
+
},
|
|
1358
|
+
},
|
|
1359
|
+
required: ["applicationId"],
|
|
1360
|
+
},
|
|
1361
|
+
}, {
|
|
1362
|
+
name: "prowlarr_update_application",
|
|
1363
|
+
description: "Update an application connection",
|
|
1364
|
+
inputSchema: {
|
|
1365
|
+
type: "object",
|
|
1366
|
+
properties: {
|
|
1367
|
+
applicationId: {
|
|
1368
|
+
type: "number",
|
|
1369
|
+
description: "Application ID to update",
|
|
1370
|
+
},
|
|
1371
|
+
syncLevel: {
|
|
1372
|
+
type: "string",
|
|
1373
|
+
description: "Sync level: disabled, addOnly, fullSync",
|
|
1374
|
+
},
|
|
1375
|
+
tags: {
|
|
1376
|
+
type: "array",
|
|
1377
|
+
items: { type: "number" },
|
|
1378
|
+
description: "Tag IDs to apply",
|
|
1379
|
+
},
|
|
1380
|
+
},
|
|
1381
|
+
required: ["applicationId"],
|
|
1382
|
+
},
|
|
1383
|
+
}, {
|
|
1384
|
+
name: "prowlarr_delete_application",
|
|
1385
|
+
description: "Delete an application from Prowlarr",
|
|
1386
|
+
inputSchema: {
|
|
1387
|
+
type: "object",
|
|
1388
|
+
properties: {
|
|
1389
|
+
applicationId: {
|
|
1390
|
+
type: "number",
|
|
1391
|
+
description: "Application ID to delete",
|
|
1392
|
+
},
|
|
1393
|
+
},
|
|
1394
|
+
required: ["applicationId"],
|
|
1395
|
+
},
|
|
1396
|
+
}, {
|
|
1397
|
+
name: "prowlarr_test_application",
|
|
1398
|
+
description: "Test an application connection",
|
|
1399
|
+
inputSchema: {
|
|
1400
|
+
type: "object",
|
|
1401
|
+
properties: {
|
|
1402
|
+
applicationId: {
|
|
1403
|
+
type: "number",
|
|
1404
|
+
description: "Application ID to test",
|
|
1405
|
+
},
|
|
1406
|
+
},
|
|
1407
|
+
required: ["applicationId"],
|
|
1408
|
+
},
|
|
1409
|
+
}, {
|
|
1410
|
+
name: "prowlarr_sync_applications",
|
|
1411
|
+
description: "Sync indexers to all applications (push indexer config to Sonarr/Radarr/etc.)",
|
|
1412
|
+
inputSchema: {
|
|
1413
|
+
type: "object",
|
|
1414
|
+
properties: {},
|
|
1415
|
+
required: [],
|
|
1416
|
+
},
|
|
1417
|
+
}, {
|
|
1418
|
+
name: "prowlarr_get_app_profiles",
|
|
1419
|
+
description: "Get app sync profiles (control which indexers sync to which apps)",
|
|
1420
|
+
inputSchema: {
|
|
1421
|
+
type: "object",
|
|
1422
|
+
properties: {},
|
|
1423
|
+
required: [],
|
|
1424
|
+
},
|
|
1425
|
+
}, {
|
|
1426
|
+
name: "prowlarr_get_history",
|
|
1427
|
+
description: "Get Prowlarr history (searches, grabs, failures)",
|
|
1428
|
+
inputSchema: {
|
|
1429
|
+
type: "object",
|
|
1430
|
+
properties: {
|
|
1431
|
+
page: {
|
|
1432
|
+
type: "number",
|
|
1433
|
+
description: "Page number (default: 1)",
|
|
1434
|
+
},
|
|
1435
|
+
pageSize: {
|
|
1436
|
+
type: "number",
|
|
1437
|
+
description: "Items per page (default: 20)",
|
|
1438
|
+
},
|
|
1439
|
+
indexerId: {
|
|
1440
|
+
type: "number",
|
|
1441
|
+
description: "Filter by indexer ID",
|
|
1442
|
+
},
|
|
1443
|
+
},
|
|
1444
|
+
required: [],
|
|
1445
|
+
},
|
|
1446
|
+
}, {
|
|
1447
|
+
name: "prowlarr_get_tags",
|
|
1448
|
+
description: "Get all tags defined in Prowlarr",
|
|
1449
|
+
inputSchema: {
|
|
1450
|
+
type: "object",
|
|
1451
|
+
properties: {},
|
|
1452
|
+
required: [],
|
|
1453
|
+
},
|
|
1454
|
+
}, {
|
|
1455
|
+
name: "prowlarr_create_tag",
|
|
1456
|
+
description: "Create a new tag in Prowlarr",
|
|
1457
|
+
inputSchema: {
|
|
1458
|
+
type: "object",
|
|
1459
|
+
properties: {
|
|
1460
|
+
label: {
|
|
1461
|
+
type: "string",
|
|
1462
|
+
description: "Tag label/name",
|
|
1463
|
+
},
|
|
1464
|
+
},
|
|
1465
|
+
required: ["label"],
|
|
1466
|
+
},
|
|
1467
|
+
}, {
|
|
1468
|
+
name: "prowlarr_update_tag",
|
|
1469
|
+
description: "Update a tag label in Prowlarr",
|
|
1470
|
+
inputSchema: {
|
|
1471
|
+
type: "object",
|
|
1472
|
+
properties: {
|
|
1473
|
+
tagId: {
|
|
1474
|
+
type: "number",
|
|
1475
|
+
description: "Tag ID to update",
|
|
1476
|
+
},
|
|
1477
|
+
label: {
|
|
1478
|
+
type: "string",
|
|
1479
|
+
description: "New tag label",
|
|
1480
|
+
},
|
|
1481
|
+
},
|
|
1482
|
+
required: ["tagId", "label"],
|
|
1483
|
+
},
|
|
1484
|
+
}, {
|
|
1485
|
+
name: "prowlarr_delete_tag",
|
|
1486
|
+
description: "Delete a tag from Prowlarr",
|
|
1487
|
+
inputSchema: {
|
|
1488
|
+
type: "object",
|
|
1489
|
+
properties: {
|
|
1490
|
+
tagId: {
|
|
1491
|
+
type: "number",
|
|
1492
|
+
description: "Tag ID to delete",
|
|
1493
|
+
},
|
|
1494
|
+
},
|
|
1495
|
+
required: ["tagId"],
|
|
1496
|
+
},
|
|
1497
|
+
}, {
|
|
1498
|
+
name: "prowlarr_get_indexer_schemas",
|
|
1499
|
+
description: "Get available indexer schemas (templates for adding new indexers)",
|
|
1500
|
+
inputSchema: {
|
|
1501
|
+
type: "object",
|
|
1502
|
+
properties: {},
|
|
1503
|
+
required: [],
|
|
1504
|
+
},
|
|
1505
|
+
}, {
|
|
1506
|
+
name: "prowlarr_get_notifications",
|
|
1507
|
+
description: "Get configured notification connections",
|
|
1508
|
+
inputSchema: {
|
|
1509
|
+
type: "object",
|
|
1510
|
+
properties: {},
|
|
1511
|
+
required: [],
|
|
1512
|
+
},
|
|
1513
|
+
}, {
|
|
1514
|
+
name: "prowlarr_rss_sync",
|
|
1515
|
+
description: "Trigger RSS sync for all indexers",
|
|
1516
|
+
inputSchema: {
|
|
1517
|
+
type: "object",
|
|
1518
|
+
properties: {},
|
|
1519
|
+
required: [],
|
|
1520
|
+
},
|
|
1521
|
+
}, {
|
|
1522
|
+
name: "prowlarr_get_download_clients",
|
|
1523
|
+
description: "Get configured download clients",
|
|
1524
|
+
inputSchema: {
|
|
1525
|
+
type: "object",
|
|
1526
|
+
properties: {},
|
|
1527
|
+
required: [],
|
|
1528
|
+
},
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
// Lingarr tools - imported from separate module to reduce merge conflicts
|
|
1532
|
+
if (clients.lingarr) {
|
|
1533
|
+
TOOLS.push(...getLingarrTools());
|
|
1534
|
+
}
|
|
1535
|
+
// Bazarr tools - imported from separate module
|
|
1536
|
+
if (clients.bazarr) {
|
|
1537
|
+
TOOLS.push(...bazarrTools);
|
|
1538
|
+
}
|
|
1539
|
+
// Cross-service search tool
|
|
1540
|
+
TOOLS.push({
|
|
1541
|
+
name: "arr_search_all",
|
|
1542
|
+
description: "Search across all configured *arr services for any media",
|
|
860
1543
|
inputSchema: {
|
|
861
1544
|
type: "object",
|
|
862
1545
|
properties: {
|
|
@@ -1182,193 +1865,770 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1182
1865
|
content: [{
|
|
1183
1866
|
type: "text",
|
|
1184
1867
|
text: JSON.stringify({
|
|
1185
|
-
count: downloadClients.length,
|
|
1186
|
-
clients: downloadClients.map((c) => ({
|
|
1187
|
-
id: c.id,
|
|
1188
|
-
name: c.name,
|
|
1189
|
-
implementation: c.implementationName,
|
|
1190
|
-
protocol: c.protocol,
|
|
1191
|
-
enabled: c.enable,
|
|
1192
|
-
priority: c.priority,
|
|
1193
|
-
removeCompletedDownloads: c.removeCompletedDownloads,
|
|
1194
|
-
removeFailedDownloads: c.removeFailedDownloads,
|
|
1195
|
-
tags: c.tags,
|
|
1868
|
+
count: downloadClients.length,
|
|
1869
|
+
clients: downloadClients.map((c) => ({
|
|
1870
|
+
id: c.id,
|
|
1871
|
+
name: c.name,
|
|
1872
|
+
implementation: c.implementationName,
|
|
1873
|
+
protocol: c.protocol,
|
|
1874
|
+
enabled: c.enable,
|
|
1875
|
+
priority: c.priority,
|
|
1876
|
+
removeCompletedDownloads: c.removeCompletedDownloads,
|
|
1877
|
+
removeFailedDownloads: c.removeFailedDownloads,
|
|
1878
|
+
tags: c.tags,
|
|
1879
|
+
})),
|
|
1880
|
+
}, null, 2),
|
|
1881
|
+
}],
|
|
1882
|
+
};
|
|
1883
|
+
}
|
|
1884
|
+
// Naming config
|
|
1885
|
+
case "sonarr_get_naming":
|
|
1886
|
+
case "radarr_get_naming":
|
|
1887
|
+
case "lidarr_get_naming":
|
|
1888
|
+
case "readarr_get_naming": {
|
|
1889
|
+
const serviceName = name.split('_')[0];
|
|
1890
|
+
const client = clients[serviceName];
|
|
1891
|
+
if (!client)
|
|
1892
|
+
throw new Error(`${serviceName} not configured`);
|
|
1893
|
+
const naming = await client.getNamingConfig();
|
|
1894
|
+
return {
|
|
1895
|
+
content: [{
|
|
1896
|
+
type: "text",
|
|
1897
|
+
text: JSON.stringify(naming, null, 2),
|
|
1898
|
+
}],
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
// Tags
|
|
1902
|
+
case "sonarr_get_tags":
|
|
1903
|
+
case "radarr_get_tags":
|
|
1904
|
+
case "lidarr_get_tags":
|
|
1905
|
+
case "readarr_get_tags": {
|
|
1906
|
+
const serviceName = name.split('_')[0];
|
|
1907
|
+
const client = clients[serviceName];
|
|
1908
|
+
if (!client)
|
|
1909
|
+
throw new Error(`${serviceName} not configured`);
|
|
1910
|
+
const tags = await client.getTags();
|
|
1911
|
+
return {
|
|
1912
|
+
content: [{
|
|
1913
|
+
type: "text",
|
|
1914
|
+
text: JSON.stringify({
|
|
1915
|
+
count: tags.length,
|
|
1916
|
+
tags: tags.map((t) => ({ id: t.id, label: t.label })),
|
|
1917
|
+
}, null, 2),
|
|
1918
|
+
}],
|
|
1919
|
+
};
|
|
1920
|
+
}
|
|
1921
|
+
// Comprehensive setup review
|
|
1922
|
+
case "sonarr_review_setup":
|
|
1923
|
+
case "radarr_review_setup":
|
|
1924
|
+
case "lidarr_review_setup":
|
|
1925
|
+
case "readarr_review_setup": {
|
|
1926
|
+
const serviceName = name.split('_')[0];
|
|
1927
|
+
const client = clients[serviceName];
|
|
1928
|
+
if (!client)
|
|
1929
|
+
throw new Error(`${serviceName} not configured`);
|
|
1930
|
+
// Gather all configuration data
|
|
1931
|
+
const [status, health, qualityProfiles, qualityDefinitions, downloadClients, naming, mediaManagement, rootFolders, tags, indexers] = await Promise.all([
|
|
1932
|
+
client.getStatus(),
|
|
1933
|
+
client.getHealth(),
|
|
1934
|
+
client.getQualityProfiles(),
|
|
1935
|
+
client.getQualityDefinitions(),
|
|
1936
|
+
client.getDownloadClients(),
|
|
1937
|
+
client.getNamingConfig(),
|
|
1938
|
+
client.getMediaManagement(),
|
|
1939
|
+
client.getRootFoldersDetailed(),
|
|
1940
|
+
client.getTags(),
|
|
1941
|
+
client.getIndexers(),
|
|
1942
|
+
]);
|
|
1943
|
+
// For Lidarr/Readarr, also get metadata profiles
|
|
1944
|
+
let metadataProfiles = null;
|
|
1945
|
+
if (serviceName === 'lidarr' && clients.lidarr) {
|
|
1946
|
+
metadataProfiles = await clients.lidarr.getMetadataProfiles();
|
|
1947
|
+
}
|
|
1948
|
+
else if (serviceName === 'readarr' && clients.readarr) {
|
|
1949
|
+
metadataProfiles = await clients.readarr.getMetadataProfiles();
|
|
1950
|
+
}
|
|
1951
|
+
const review = {
|
|
1952
|
+
service: serviceName,
|
|
1953
|
+
version: status.version,
|
|
1954
|
+
appName: status.appName,
|
|
1955
|
+
platform: {
|
|
1956
|
+
os: status.osName,
|
|
1957
|
+
isDocker: status.isDocker,
|
|
1958
|
+
},
|
|
1959
|
+
health: {
|
|
1960
|
+
issueCount: health.length,
|
|
1961
|
+
issues: health,
|
|
1962
|
+
},
|
|
1963
|
+
storage: {
|
|
1964
|
+
rootFolders: rootFolders.map((f) => ({
|
|
1965
|
+
path: f.path,
|
|
1966
|
+
accessible: f.accessible,
|
|
1967
|
+
freeSpace: formatBytes(f.freeSpace),
|
|
1968
|
+
freeSpaceBytes: f.freeSpace,
|
|
1969
|
+
unmappedFolderCount: f.unmappedFolders?.length || 0,
|
|
1970
|
+
})),
|
|
1971
|
+
},
|
|
1972
|
+
qualityProfiles: qualityProfiles.map((p) => ({
|
|
1973
|
+
id: p.id,
|
|
1974
|
+
name: p.name,
|
|
1975
|
+
upgradeAllowed: p.upgradeAllowed,
|
|
1976
|
+
cutoff: p.cutoff,
|
|
1977
|
+
allowedQualities: p.items
|
|
1978
|
+
.filter((i) => i.allowed)
|
|
1979
|
+
.map((i) => i.quality?.name || i.name || (i.items?.map((q) => q.quality.name).join(', ')))
|
|
1980
|
+
.filter(Boolean),
|
|
1981
|
+
customFormatsWithScores: p.formatItems?.filter((f) => f.score !== 0).length || 0,
|
|
1982
|
+
minFormatScore: p.minFormatScore,
|
|
1983
|
+
})),
|
|
1984
|
+
qualityDefinitions: qualityDefinitions.map((d) => ({
|
|
1985
|
+
quality: d.quality.name,
|
|
1986
|
+
minSize: d.minSize + ' MB/min',
|
|
1987
|
+
maxSize: d.maxSize === 0 ? 'unlimited' : d.maxSize + ' MB/min',
|
|
1988
|
+
preferredSize: d.preferredSize + ' MB/min',
|
|
1989
|
+
})),
|
|
1990
|
+
downloadClients: downloadClients.map((c) => ({
|
|
1991
|
+
name: c.name,
|
|
1992
|
+
type: c.implementationName,
|
|
1993
|
+
protocol: c.protocol,
|
|
1994
|
+
enabled: c.enable,
|
|
1995
|
+
priority: c.priority,
|
|
1996
|
+
})),
|
|
1997
|
+
indexers: indexers.map((i) => ({
|
|
1998
|
+
name: i.name,
|
|
1999
|
+
protocol: i.protocol,
|
|
2000
|
+
enableRss: i.enableRss,
|
|
2001
|
+
enableAutomaticSearch: i.enableAutomaticSearch,
|
|
2002
|
+
enableInteractiveSearch: i.enableInteractiveSearch,
|
|
2003
|
+
priority: i.priority,
|
|
2004
|
+
})),
|
|
2005
|
+
naming: naming,
|
|
2006
|
+
mediaManagement: {
|
|
2007
|
+
recycleBin: mediaManagement.recycleBin || 'not set',
|
|
2008
|
+
recycleBinCleanupDays: mediaManagement.recycleBinCleanupDays,
|
|
2009
|
+
downloadPropersAndRepacks: mediaManagement.downloadPropersAndRepacks,
|
|
2010
|
+
deleteEmptyFolders: mediaManagement.deleteEmptyFolders,
|
|
2011
|
+
copyUsingHardlinks: mediaManagement.copyUsingHardlinks,
|
|
2012
|
+
importExtraFiles: mediaManagement.importExtraFiles,
|
|
2013
|
+
extraFileExtensions: mediaManagement.extraFileExtensions,
|
|
2014
|
+
},
|
|
2015
|
+
tags: tags.map((t) => t.label),
|
|
2016
|
+
...(metadataProfiles && { metadataProfiles }),
|
|
2017
|
+
};
|
|
2018
|
+
return {
|
|
2019
|
+
content: [{
|
|
2020
|
+
type: "text",
|
|
2021
|
+
text: JSON.stringify(review, null, 2),
|
|
2022
|
+
}],
|
|
2023
|
+
};
|
|
2024
|
+
}
|
|
2025
|
+
// Sonarr handlers
|
|
2026
|
+
case "sonarr_get_series": {
|
|
2027
|
+
if (!clients.sonarr)
|
|
2028
|
+
throw new Error("Sonarr not configured");
|
|
2029
|
+
const series = await clients.sonarr.getSeries();
|
|
2030
|
+
return {
|
|
2031
|
+
content: [{
|
|
2032
|
+
type: "text",
|
|
2033
|
+
text: JSON.stringify({
|
|
2034
|
+
count: series.length,
|
|
2035
|
+
series: series.map(s => ({
|
|
2036
|
+
id: s.id,
|
|
2037
|
+
title: s.title,
|
|
2038
|
+
year: s.year,
|
|
2039
|
+
status: s.status,
|
|
2040
|
+
network: s.network,
|
|
2041
|
+
seasons: s.statistics?.seasonCount,
|
|
2042
|
+
episodes: s.statistics?.episodeFileCount + '/' + s.statistics?.totalEpisodeCount,
|
|
2043
|
+
sizeOnDisk: formatBytes(s.statistics?.sizeOnDisk || 0),
|
|
2044
|
+
monitored: s.monitored,
|
|
2045
|
+
})),
|
|
2046
|
+
}, null, 2),
|
|
2047
|
+
}],
|
|
2048
|
+
};
|
|
2049
|
+
}
|
|
2050
|
+
case "sonarr_search": {
|
|
2051
|
+
if (!clients.sonarr)
|
|
2052
|
+
throw new Error("Sonarr not configured");
|
|
2053
|
+
const term = args.term;
|
|
2054
|
+
const results = await clients.sonarr.searchSeries(term);
|
|
2055
|
+
return {
|
|
2056
|
+
content: [{
|
|
2057
|
+
type: "text",
|
|
2058
|
+
text: JSON.stringify({
|
|
2059
|
+
count: results.length,
|
|
2060
|
+
results: results.slice(0, 10).map(r => ({
|
|
2061
|
+
title: r.title,
|
|
2062
|
+
year: r.year,
|
|
2063
|
+
tvdbId: r.tvdbId,
|
|
2064
|
+
overview: r.overview?.substring(0, 200) + (r.overview && r.overview.length > 200 ? '...' : ''),
|
|
2065
|
+
})),
|
|
2066
|
+
}, null, 2),
|
|
2067
|
+
}],
|
|
2068
|
+
};
|
|
2069
|
+
}
|
|
2070
|
+
case "sonarr_get_queue": {
|
|
2071
|
+
if (!clients.sonarr)
|
|
2072
|
+
throw new Error("Sonarr not configured");
|
|
2073
|
+
const queue = await clients.sonarr.getQueue();
|
|
2074
|
+
return {
|
|
2075
|
+
content: [{
|
|
2076
|
+
type: "text",
|
|
2077
|
+
text: JSON.stringify({
|
|
2078
|
+
totalRecords: queue.totalRecords,
|
|
2079
|
+
items: queue.records.map(q => ({
|
|
2080
|
+
title: q.title,
|
|
2081
|
+
status: q.status,
|
|
2082
|
+
progress: ((1 - q.sizeleft / q.size) * 100).toFixed(1) + '%',
|
|
2083
|
+
timeLeft: q.timeleft,
|
|
2084
|
+
downloadClient: q.downloadClient,
|
|
2085
|
+
})),
|
|
2086
|
+
}, null, 2),
|
|
2087
|
+
}],
|
|
2088
|
+
};
|
|
2089
|
+
}
|
|
2090
|
+
case "sonarr_get_calendar": {
|
|
2091
|
+
if (!clients.sonarr)
|
|
2092
|
+
throw new Error("Sonarr not configured");
|
|
2093
|
+
const days = args?.days || 7;
|
|
2094
|
+
const start = new Date().toISOString().split('T')[0];
|
|
2095
|
+
const end = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
|
|
2096
|
+
const calendar = await clients.sonarr.getCalendar(start, end);
|
|
2097
|
+
return {
|
|
2098
|
+
content: [{ type: "text", text: JSON.stringify(calendar, null, 2) }],
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
case "sonarr_get_episodes": {
|
|
2102
|
+
if (!clients.sonarr)
|
|
2103
|
+
throw new Error("Sonarr not configured");
|
|
2104
|
+
const { seriesId, seasonNumber } = args;
|
|
2105
|
+
const episodes = await clients.sonarr.getEpisodes(seriesId, seasonNumber);
|
|
2106
|
+
return {
|
|
2107
|
+
content: [{
|
|
2108
|
+
type: "text",
|
|
2109
|
+
text: JSON.stringify({
|
|
2110
|
+
count: episodes.length,
|
|
2111
|
+
episodes: episodes.map(e => ({
|
|
2112
|
+
id: e.id,
|
|
2113
|
+
seasonNumber: e.seasonNumber,
|
|
2114
|
+
episodeNumber: e.episodeNumber,
|
|
2115
|
+
title: e.title,
|
|
2116
|
+
airDate: e.airDate,
|
|
2117
|
+
hasFile: e.hasFile,
|
|
2118
|
+
monitored: e.monitored,
|
|
2119
|
+
})),
|
|
2120
|
+
}, null, 2),
|
|
2121
|
+
}],
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
case "sonarr_search_missing": {
|
|
2125
|
+
if (!clients.sonarr)
|
|
2126
|
+
throw new Error("Sonarr not configured");
|
|
2127
|
+
const seriesId = args.seriesId;
|
|
2128
|
+
const result = await clients.sonarr.searchMissing(seriesId);
|
|
2129
|
+
return {
|
|
2130
|
+
content: [{
|
|
2131
|
+
type: "text",
|
|
2132
|
+
text: JSON.stringify({
|
|
2133
|
+
success: true,
|
|
2134
|
+
message: `Search triggered for missing episodes`,
|
|
2135
|
+
commandId: result.id,
|
|
2136
|
+
}, null, 2),
|
|
2137
|
+
}],
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
2140
|
+
case "sonarr_search_episode": {
|
|
2141
|
+
if (!clients.sonarr)
|
|
2142
|
+
throw new Error("Sonarr not configured");
|
|
2143
|
+
const episodeIds = args.episodeIds;
|
|
2144
|
+
const result = await clients.sonarr.searchEpisode(episodeIds);
|
|
2145
|
+
return {
|
|
2146
|
+
content: [{
|
|
2147
|
+
type: "text",
|
|
2148
|
+
text: JSON.stringify({
|
|
2149
|
+
success: true,
|
|
2150
|
+
message: `Search triggered for ${episodeIds.length} episode(s)`,
|
|
2151
|
+
commandId: result.id,
|
|
2152
|
+
}, null, 2),
|
|
2153
|
+
}],
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
case "sonarr_add_series": {
|
|
2157
|
+
if (!clients.sonarr)
|
|
2158
|
+
throw new Error("Sonarr not configured");
|
|
2159
|
+
const { tvdbId, title, qualityProfileId, rootFolderPath, seasonFolder, monitored, tags } = args;
|
|
2160
|
+
const series = await clients.sonarr.addSeries({
|
|
2161
|
+
tvdbId,
|
|
2162
|
+
title,
|
|
2163
|
+
qualityProfileId,
|
|
2164
|
+
rootFolderPath,
|
|
2165
|
+
seasonFolder,
|
|
2166
|
+
monitored,
|
|
2167
|
+
tags,
|
|
2168
|
+
});
|
|
2169
|
+
return {
|
|
2170
|
+
content: [{
|
|
2171
|
+
type: "text",
|
|
2172
|
+
text: JSON.stringify({
|
|
2173
|
+
success: true,
|
|
2174
|
+
message: `Series '${title}' added to Sonarr`,
|
|
2175
|
+
seriesId: series.id,
|
|
2176
|
+
series: {
|
|
2177
|
+
id: series.id,
|
|
2178
|
+
title: series.title,
|
|
2179
|
+
path: series.path,
|
|
2180
|
+
monitored: series.monitored,
|
|
2181
|
+
},
|
|
2182
|
+
}, null, 2),
|
|
2183
|
+
}],
|
|
2184
|
+
};
|
|
2185
|
+
}
|
|
2186
|
+
case "sonarr_update_series": {
|
|
2187
|
+
if (!clients.sonarr)
|
|
2188
|
+
throw new Error("Sonarr not configured");
|
|
2189
|
+
const { seriesId, ...updates } = args;
|
|
2190
|
+
const series = await clients.sonarr.updateSeries(seriesId, updates);
|
|
2191
|
+
return {
|
|
2192
|
+
content: [{
|
|
2193
|
+
type: "text",
|
|
2194
|
+
text: JSON.stringify({
|
|
2195
|
+
success: true,
|
|
2196
|
+
message: `Series '${series.title}' updated`,
|
|
2197
|
+
seriesId: series.id,
|
|
2198
|
+
updates: Object.keys(updates),
|
|
2199
|
+
}, null, 2),
|
|
2200
|
+
}],
|
|
2201
|
+
};
|
|
2202
|
+
}
|
|
2203
|
+
case "sonarr_delete_series": {
|
|
2204
|
+
if (!clients.sonarr)
|
|
2205
|
+
throw new Error("Sonarr not configured");
|
|
2206
|
+
const { seriesId, deleteFiles = false, addImportListExclusion = false } = args;
|
|
2207
|
+
await clients.sonarr.deleteSeries(seriesId, deleteFiles, addImportListExclusion);
|
|
2208
|
+
return {
|
|
2209
|
+
content: [{
|
|
2210
|
+
type: "text",
|
|
2211
|
+
text: JSON.stringify({
|
|
2212
|
+
success: true,
|
|
2213
|
+
message: `Series deleted (files: ${deleteFiles}, exclude: ${addImportListExclusion})`,
|
|
2214
|
+
}, null, 2),
|
|
2215
|
+
}],
|
|
2216
|
+
};
|
|
2217
|
+
}
|
|
2218
|
+
case "sonarr_update_episode": {
|
|
2219
|
+
if (!clients.sonarr)
|
|
2220
|
+
throw new Error("Sonarr not configured");
|
|
2221
|
+
const { episodeId, monitored } = args;
|
|
2222
|
+
const episode = await clients.sonarr.updateEpisode(episodeId, { monitored });
|
|
2223
|
+
return {
|
|
2224
|
+
content: [{
|
|
2225
|
+
type: "text",
|
|
2226
|
+
text: JSON.stringify({
|
|
2227
|
+
success: true,
|
|
2228
|
+
message: `Episode ${episodeId} ${monitored ? 'monitored' : 'unmonitored'}`,
|
|
2229
|
+
episodeId: episode.id,
|
|
2230
|
+
monitored: episode.monitored,
|
|
2231
|
+
}, null, 2),
|
|
2232
|
+
}],
|
|
2233
|
+
};
|
|
2234
|
+
}
|
|
2235
|
+
case "sonarr_delete_episode_file": {
|
|
2236
|
+
if (!clients.sonarr)
|
|
2237
|
+
throw new Error("Sonarr not configured");
|
|
2238
|
+
const { episodeFileId } = args;
|
|
2239
|
+
await clients.sonarr.deleteEpisodeFile(episodeFileId);
|
|
2240
|
+
return {
|
|
2241
|
+
content: [{
|
|
2242
|
+
type: "text",
|
|
2243
|
+
text: JSON.stringify({
|
|
2244
|
+
success: true,
|
|
2245
|
+
message: `Episode file ${episodeFileId} deleted`,
|
|
2246
|
+
}, null, 2),
|
|
2247
|
+
}],
|
|
2248
|
+
};
|
|
2249
|
+
}
|
|
2250
|
+
case "sonarr_refresh_series": {
|
|
2251
|
+
if (!clients.sonarr)
|
|
2252
|
+
throw new Error("Sonarr not configured");
|
|
2253
|
+
const { seriesId } = args;
|
|
2254
|
+
const result = await clients.sonarr.refreshSeries(seriesId);
|
|
2255
|
+
return {
|
|
2256
|
+
content: [{
|
|
2257
|
+
type: "text",
|
|
2258
|
+
text: JSON.stringify({
|
|
2259
|
+
success: true,
|
|
2260
|
+
message: `Series ${seriesId} metadata refresh triggered`,
|
|
2261
|
+
commandId: result.id,
|
|
2262
|
+
}, null, 2),
|
|
2263
|
+
}],
|
|
2264
|
+
};
|
|
2265
|
+
}
|
|
2266
|
+
case "sonarr_rescan_series": {
|
|
2267
|
+
if (!clients.sonarr)
|
|
2268
|
+
throw new Error("Sonarr not configured");
|
|
2269
|
+
const { seriesId } = args;
|
|
2270
|
+
const result = await clients.sonarr.rescanSeries(seriesId);
|
|
2271
|
+
return {
|
|
2272
|
+
content: [{
|
|
2273
|
+
type: "text",
|
|
2274
|
+
text: JSON.stringify({
|
|
2275
|
+
success: true,
|
|
2276
|
+
message: `Series ${seriesId} disk rescan triggered`,
|
|
2277
|
+
commandId: result.id,
|
|
2278
|
+
}, null, 2),
|
|
2279
|
+
}],
|
|
2280
|
+
};
|
|
2281
|
+
}
|
|
2282
|
+
case "sonarr_rename_series": {
|
|
2283
|
+
if (!clients.sonarr)
|
|
2284
|
+
throw new Error("Sonarr not configured");
|
|
2285
|
+
const { seriesId } = args;
|
|
2286
|
+
const result = await clients.sonarr.renameSeries(seriesId);
|
|
2287
|
+
return {
|
|
2288
|
+
content: [{
|
|
2289
|
+
type: "text",
|
|
2290
|
+
text: JSON.stringify({
|
|
2291
|
+
success: true,
|
|
2292
|
+
message: `Series ${seriesId} files will be renamed`,
|
|
2293
|
+
commandId: result.id,
|
|
2294
|
+
}, null, 2),
|
|
2295
|
+
}],
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
2298
|
+
case "sonarr_rename_episodes": {
|
|
2299
|
+
if (!clients.sonarr)
|
|
2300
|
+
throw new Error("Sonarr not configured");
|
|
2301
|
+
const { seriesId } = args;
|
|
2302
|
+
const result = await clients.sonarr.renameEpisodes(seriesId);
|
|
2303
|
+
return {
|
|
2304
|
+
content: [{
|
|
2305
|
+
type: "text",
|
|
2306
|
+
text: JSON.stringify({
|
|
2307
|
+
success: true,
|
|
2308
|
+
message: `All episodes for series ${seriesId} will be renamed`,
|
|
2309
|
+
commandId: result.id,
|
|
2310
|
+
}, null, 2),
|
|
2311
|
+
}],
|
|
2312
|
+
};
|
|
2313
|
+
}
|
|
2314
|
+
case "sonarr_get_releases": {
|
|
2315
|
+
if (!clients.sonarr)
|
|
2316
|
+
throw new Error("Sonarr not configured");
|
|
2317
|
+
const { seriesId, page = 1, pageSize = 10 } = args;
|
|
2318
|
+
const releases = await clients.sonarr.getReleases(seriesId, page, pageSize);
|
|
2319
|
+
return {
|
|
2320
|
+
content: [{
|
|
2321
|
+
type: "text",
|
|
2322
|
+
text: JSON.stringify({
|
|
2323
|
+
totalRecords: releases.length,
|
|
2324
|
+
page,
|
|
2325
|
+
pageSize,
|
|
2326
|
+
releases: releases.map((r) => ({
|
|
2327
|
+
guid: r.guid,
|
|
2328
|
+
title: r.title,
|
|
2329
|
+
indexer: r.indexer,
|
|
2330
|
+
quality: r.quality,
|
|
2331
|
+
size: formatBytes(r.size),
|
|
2332
|
+
age: r.age,
|
|
2333
|
+
ageHours: r.ageHours,
|
|
2334
|
+
rejected: r.rejected,
|
|
2335
|
+
rejectionReason: r.rejectionReason,
|
|
2336
|
+
publishedDate: r.publishedDate,
|
|
2337
|
+
})),
|
|
2338
|
+
}, null, 2),
|
|
2339
|
+
}],
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
case "sonarr_delete_release": {
|
|
2343
|
+
if (!clients.sonarr)
|
|
2344
|
+
throw new Error("Sonarr not configured");
|
|
2345
|
+
const { guid } = args;
|
|
2346
|
+
await clients.sonarr.deleteRelease(guid);
|
|
2347
|
+
return {
|
|
2348
|
+
content: [{
|
|
2349
|
+
type: "text",
|
|
2350
|
+
text: JSON.stringify({
|
|
2351
|
+
success: true,
|
|
2352
|
+
message: `Release ${guid} deleted from history`,
|
|
2353
|
+
}, null, 2),
|
|
2354
|
+
}],
|
|
2355
|
+
};
|
|
2356
|
+
}
|
|
2357
|
+
case "sonarr_create_release_profile": {
|
|
2358
|
+
if (!clients.sonarr)
|
|
2359
|
+
throw new Error("Sonarr not configured");
|
|
2360
|
+
const profile = await clients.sonarr.createReleaseProfile(args);
|
|
2361
|
+
return {
|
|
2362
|
+
content: [{
|
|
2363
|
+
type: "text",
|
|
2364
|
+
text: JSON.stringify({
|
|
2365
|
+
success: true,
|
|
2366
|
+
message: `Release profile '${profile.name}' created`,
|
|
2367
|
+
profileId: profile.id,
|
|
2368
|
+
profile,
|
|
2369
|
+
}, null, 2),
|
|
2370
|
+
}],
|
|
2371
|
+
};
|
|
2372
|
+
}
|
|
2373
|
+
case "sonarr_update_release_profile": {
|
|
2374
|
+
if (!clients.sonarr)
|
|
2375
|
+
throw new Error("Sonarr not configured");
|
|
2376
|
+
const { profileId, ...updates } = args;
|
|
2377
|
+
const profile = await clients.sonarr.updateReleaseProfile(profileId, updates);
|
|
2378
|
+
return {
|
|
2379
|
+
content: [{
|
|
2380
|
+
type: "text",
|
|
2381
|
+
text: JSON.stringify({
|
|
2382
|
+
success: true,
|
|
2383
|
+
message: `Release profile ${profileId} updated`,
|
|
2384
|
+
profileId: profile.id,
|
|
2385
|
+
profile,
|
|
2386
|
+
}, null, 2),
|
|
2387
|
+
}],
|
|
2388
|
+
};
|
|
2389
|
+
}
|
|
2390
|
+
case "sonarr_delete_release_profile": {
|
|
2391
|
+
if (!clients.sonarr)
|
|
2392
|
+
throw new Error("Sonarr not configured");
|
|
2393
|
+
const { profileId } = args;
|
|
2394
|
+
await clients.sonarr.deleteReleaseProfile(profileId);
|
|
2395
|
+
return {
|
|
2396
|
+
content: [{
|
|
2397
|
+
type: "text",
|
|
2398
|
+
text: JSON.stringify({
|
|
2399
|
+
success: true,
|
|
2400
|
+
message: `Release profile ${profileId} deleted`,
|
|
2401
|
+
}, null, 2),
|
|
2402
|
+
}],
|
|
2403
|
+
};
|
|
2404
|
+
}
|
|
2405
|
+
case "sonarr_create_tag": {
|
|
2406
|
+
if (!clients.sonarr)
|
|
2407
|
+
throw new Error("Sonarr not configured");
|
|
2408
|
+
const { label } = args;
|
|
2409
|
+
const tag = await clients.sonarr.createTag(label);
|
|
2410
|
+
return {
|
|
2411
|
+
content: [{
|
|
2412
|
+
type: "text",
|
|
2413
|
+
text: JSON.stringify({
|
|
2414
|
+
success: true,
|
|
2415
|
+
message: `Tag '${label}' created`,
|
|
2416
|
+
tagId: tag.id,
|
|
2417
|
+
tag,
|
|
2418
|
+
}, null, 2),
|
|
2419
|
+
}],
|
|
2420
|
+
};
|
|
2421
|
+
}
|
|
2422
|
+
case "sonarr_update_tag": {
|
|
2423
|
+
if (!clients.sonarr)
|
|
2424
|
+
throw new Error("Sonarr not configured");
|
|
2425
|
+
const { tagId, label } = args;
|
|
2426
|
+
const tag = await clients.sonarr.updateTag(tagId, label);
|
|
2427
|
+
return {
|
|
2428
|
+
content: [{
|
|
2429
|
+
type: "text",
|
|
2430
|
+
text: JSON.stringify({
|
|
2431
|
+
success: true,
|
|
2432
|
+
message: `Tag ${tagId} updated to '${label}'`,
|
|
2433
|
+
tagId: tag.id,
|
|
2434
|
+
tag,
|
|
2435
|
+
}, null, 2),
|
|
2436
|
+
}],
|
|
2437
|
+
};
|
|
2438
|
+
}
|
|
2439
|
+
case "sonarr_delete_tag": {
|
|
2440
|
+
if (!clients.sonarr)
|
|
2441
|
+
throw new Error("Sonarr not configured");
|
|
2442
|
+
const { tagId } = args;
|
|
2443
|
+
await clients.sonarr.deleteTag(tagId);
|
|
2444
|
+
return {
|
|
2445
|
+
content: [{
|
|
2446
|
+
type: "text",
|
|
2447
|
+
text: JSON.stringify({
|
|
2448
|
+
success: true,
|
|
2449
|
+
message: `Tag ${tagId} deleted`,
|
|
2450
|
+
}, null, 2),
|
|
2451
|
+
}],
|
|
2452
|
+
};
|
|
2453
|
+
}
|
|
2454
|
+
case "sonarr_get_history": {
|
|
2455
|
+
if (!clients.sonarr)
|
|
2456
|
+
throw new Error("Sonarr not configured");
|
|
2457
|
+
const { page = 1, pageSize = 20, seriesId } = args;
|
|
2458
|
+
if (seriesId) {
|
|
2459
|
+
const history = await clients.sonarr.getSeriesHistory(seriesId);
|
|
2460
|
+
return {
|
|
2461
|
+
content: [{
|
|
2462
|
+
type: "text",
|
|
2463
|
+
text: JSON.stringify({
|
|
2464
|
+
seriesId,
|
|
2465
|
+
count: history.length,
|
|
2466
|
+
records: history.map((h) => ({
|
|
2467
|
+
id: h.id,
|
|
2468
|
+
date: h.date,
|
|
2469
|
+
eventType: h.eventType,
|
|
2470
|
+
sourceTitle: h.sourceTitle,
|
|
2471
|
+
quality: h.quality?.quality?.name,
|
|
2472
|
+
episodeId: h.episodeId,
|
|
2473
|
+
})),
|
|
2474
|
+
}, null, 2),
|
|
2475
|
+
}],
|
|
2476
|
+
};
|
|
2477
|
+
}
|
|
2478
|
+
const history = await clients.sonarr.getHistory(page, pageSize);
|
|
2479
|
+
return {
|
|
2480
|
+
content: [{
|
|
2481
|
+
type: "text",
|
|
2482
|
+
text: JSON.stringify({
|
|
2483
|
+
page,
|
|
2484
|
+
pageSize,
|
|
2485
|
+
totalRecords: history.totalRecords,
|
|
2486
|
+
records: history.records.map((h) => ({
|
|
2487
|
+
id: h.id,
|
|
2488
|
+
seriesId: h.seriesId,
|
|
2489
|
+
episodeId: h.episodeId,
|
|
2490
|
+
date: h.date,
|
|
2491
|
+
eventType: h.eventType,
|
|
2492
|
+
sourceTitle: h.sourceTitle,
|
|
2493
|
+
quality: h.quality?.quality?.name,
|
|
2494
|
+
})),
|
|
2495
|
+
}, null, 2),
|
|
2496
|
+
}],
|
|
2497
|
+
};
|
|
2498
|
+
}
|
|
2499
|
+
case "sonarr_get_wanted": {
|
|
2500
|
+
if (!clients.sonarr)
|
|
2501
|
+
throw new Error("Sonarr not configured");
|
|
2502
|
+
const { page = 1, pageSize = 20 } = args;
|
|
2503
|
+
const wanted = await clients.sonarr.getWanted(page, pageSize);
|
|
2504
|
+
return {
|
|
2505
|
+
content: [{
|
|
2506
|
+
type: "text",
|
|
2507
|
+
text: JSON.stringify({
|
|
2508
|
+
page,
|
|
2509
|
+
pageSize,
|
|
2510
|
+
totalRecords: wanted.totalRecords,
|
|
2511
|
+
episodes: wanted.records.map((e) => ({
|
|
2512
|
+
id: e.id,
|
|
2513
|
+
seriesId: e.seriesId,
|
|
2514
|
+
seasonNumber: e.seasonNumber,
|
|
2515
|
+
episodeNumber: e.episodeNumber,
|
|
2516
|
+
title: e.title,
|
|
2517
|
+
airDate: e.airDate,
|
|
2518
|
+
monitored: e.monitored,
|
|
2519
|
+
})),
|
|
2520
|
+
}, null, 2),
|
|
2521
|
+
}],
|
|
2522
|
+
};
|
|
2523
|
+
}
|
|
2524
|
+
case "sonarr_get_cutoff_unmet": {
|
|
2525
|
+
if (!clients.sonarr)
|
|
2526
|
+
throw new Error("Sonarr not configured");
|
|
2527
|
+
const { page = 1, pageSize = 20 } = args;
|
|
2528
|
+
const cutoff = await clients.sonarr.getCutoffUnmet(page, pageSize);
|
|
2529
|
+
return {
|
|
2530
|
+
content: [{
|
|
2531
|
+
type: "text",
|
|
2532
|
+
text: JSON.stringify({
|
|
2533
|
+
page,
|
|
2534
|
+
pageSize,
|
|
2535
|
+
totalRecords: cutoff.totalRecords,
|
|
2536
|
+
episodes: cutoff.records.map((e) => ({
|
|
2537
|
+
id: e.id,
|
|
2538
|
+
seriesId: e.seriesId,
|
|
2539
|
+
seasonNumber: e.seasonNumber,
|
|
2540
|
+
episodeNumber: e.episodeNumber,
|
|
2541
|
+
title: e.title,
|
|
2542
|
+
currentQuality: e.episodeFile?.quality?.quality?.name,
|
|
1196
2543
|
})),
|
|
1197
2544
|
}, null, 2),
|
|
1198
2545
|
}],
|
|
1199
2546
|
};
|
|
1200
2547
|
}
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
const serviceName = name.split('_')[0];
|
|
1207
|
-
const client = clients[serviceName];
|
|
1208
|
-
if (!client)
|
|
1209
|
-
throw new Error(`${serviceName} not configured`);
|
|
1210
|
-
const naming = await client.getNamingConfig();
|
|
2548
|
+
case "sonarr_get_blocklist": {
|
|
2549
|
+
if (!clients.sonarr)
|
|
2550
|
+
throw new Error("Sonarr not configured");
|
|
2551
|
+
const { page = 1, pageSize = 20 } = args;
|
|
2552
|
+
const blocklist = await clients.sonarr.getBlocklist(page, pageSize);
|
|
1211
2553
|
return {
|
|
1212
2554
|
content: [{
|
|
1213
2555
|
type: "text",
|
|
1214
|
-
text: JSON.stringify(
|
|
2556
|
+
text: JSON.stringify({
|
|
2557
|
+
page,
|
|
2558
|
+
pageSize,
|
|
2559
|
+
totalRecords: blocklist.totalRecords,
|
|
2560
|
+
records: blocklist.records.map((b) => ({
|
|
2561
|
+
id: b.id,
|
|
2562
|
+
seriesId: b.seriesId,
|
|
2563
|
+
sourceTitle: b.sourceTitle,
|
|
2564
|
+
date: b.date,
|
|
2565
|
+
protocol: b.protocol,
|
|
2566
|
+
indexer: b.indexer,
|
|
2567
|
+
message: b.message,
|
|
2568
|
+
})),
|
|
2569
|
+
}, null, 2),
|
|
1215
2570
|
}],
|
|
1216
2571
|
};
|
|
1217
2572
|
}
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
const serviceName = name.split('_')[0];
|
|
1224
|
-
const client = clients[serviceName];
|
|
1225
|
-
if (!client)
|
|
1226
|
-
throw new Error(`${serviceName} not configured`);
|
|
1227
|
-
const tags = await client.getTags();
|
|
2573
|
+
case "sonarr_delete_blocklist_item": {
|
|
2574
|
+
if (!clients.sonarr)
|
|
2575
|
+
throw new Error("Sonarr not configured");
|
|
2576
|
+
const { blocklistId } = args;
|
|
2577
|
+
await clients.sonarr.deleteBlocklistItem(blocklistId);
|
|
1228
2578
|
return {
|
|
1229
2579
|
content: [{
|
|
1230
2580
|
type: "text",
|
|
1231
2581
|
text: JSON.stringify({
|
|
1232
|
-
|
|
1233
|
-
|
|
2582
|
+
success: true,
|
|
2583
|
+
message: `Blocklist item ${blocklistId} deleted`,
|
|
1234
2584
|
}, null, 2),
|
|
1235
2585
|
}],
|
|
1236
2586
|
};
|
|
1237
2587
|
}
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
case "readarr_review_setup": {
|
|
1243
|
-
const serviceName = name.split('_')[0];
|
|
1244
|
-
const client = clients[serviceName];
|
|
1245
|
-
if (!client)
|
|
1246
|
-
throw new Error(`${serviceName} not configured`);
|
|
1247
|
-
// Gather all configuration data
|
|
1248
|
-
const [status, health, qualityProfiles, qualityDefinitions, downloadClients, naming, mediaManagement, rootFolders, tags, indexers] = await Promise.all([
|
|
1249
|
-
client.getStatus(),
|
|
1250
|
-
client.getHealth(),
|
|
1251
|
-
client.getQualityProfiles(),
|
|
1252
|
-
client.getQualityDefinitions(),
|
|
1253
|
-
client.getDownloadClients(),
|
|
1254
|
-
client.getNamingConfig(),
|
|
1255
|
-
client.getMediaManagement(),
|
|
1256
|
-
client.getRootFoldersDetailed(),
|
|
1257
|
-
client.getTags(),
|
|
1258
|
-
client.getIndexers(),
|
|
1259
|
-
]);
|
|
1260
|
-
// For Lidarr/Readarr, also get metadata profiles
|
|
1261
|
-
let metadataProfiles = null;
|
|
1262
|
-
if (serviceName === 'lidarr' && clients.lidarr) {
|
|
1263
|
-
metadataProfiles = await clients.lidarr.getMetadataProfiles();
|
|
1264
|
-
}
|
|
1265
|
-
else if (serviceName === 'readarr' && clients.readarr) {
|
|
1266
|
-
metadataProfiles = await clients.readarr.getMetadataProfiles();
|
|
1267
|
-
}
|
|
1268
|
-
const review = {
|
|
1269
|
-
service: serviceName,
|
|
1270
|
-
version: status.version,
|
|
1271
|
-
appName: status.appName,
|
|
1272
|
-
platform: {
|
|
1273
|
-
os: status.osName,
|
|
1274
|
-
isDocker: status.isDocker,
|
|
1275
|
-
},
|
|
1276
|
-
health: {
|
|
1277
|
-
issueCount: health.length,
|
|
1278
|
-
issues: health,
|
|
1279
|
-
},
|
|
1280
|
-
storage: {
|
|
1281
|
-
rootFolders: rootFolders.map((f) => ({
|
|
1282
|
-
path: f.path,
|
|
1283
|
-
accessible: f.accessible,
|
|
1284
|
-
freeSpace: formatBytes(f.freeSpace),
|
|
1285
|
-
freeSpaceBytes: f.freeSpace,
|
|
1286
|
-
unmappedFolderCount: f.unmappedFolders?.length || 0,
|
|
1287
|
-
})),
|
|
1288
|
-
},
|
|
1289
|
-
qualityProfiles: qualityProfiles.map((p) => ({
|
|
1290
|
-
id: p.id,
|
|
1291
|
-
name: p.name,
|
|
1292
|
-
upgradeAllowed: p.upgradeAllowed,
|
|
1293
|
-
cutoff: p.cutoff,
|
|
1294
|
-
allowedQualities: p.items
|
|
1295
|
-
.filter((i) => i.allowed)
|
|
1296
|
-
.map((i) => i.quality?.name || i.name || (i.items?.map((q) => q.quality.name).join(', ')))
|
|
1297
|
-
.filter(Boolean),
|
|
1298
|
-
customFormatsWithScores: p.formatItems?.filter((f) => f.score !== 0).length || 0,
|
|
1299
|
-
minFormatScore: p.minFormatScore,
|
|
1300
|
-
})),
|
|
1301
|
-
qualityDefinitions: qualityDefinitions.map((d) => ({
|
|
1302
|
-
quality: d.quality.name,
|
|
1303
|
-
minSize: d.minSize + ' MB/min',
|
|
1304
|
-
maxSize: d.maxSize === 0 ? 'unlimited' : d.maxSize + ' MB/min',
|
|
1305
|
-
preferredSize: d.preferredSize + ' MB/min',
|
|
1306
|
-
})),
|
|
1307
|
-
downloadClients: downloadClients.map((c) => ({
|
|
1308
|
-
name: c.name,
|
|
1309
|
-
type: c.implementationName,
|
|
1310
|
-
protocol: c.protocol,
|
|
1311
|
-
enabled: c.enable,
|
|
1312
|
-
priority: c.priority,
|
|
1313
|
-
})),
|
|
1314
|
-
indexers: indexers.map((i) => ({
|
|
1315
|
-
name: i.name,
|
|
1316
|
-
protocol: i.protocol,
|
|
1317
|
-
enableRss: i.enableRss,
|
|
1318
|
-
enableAutomaticSearch: i.enableAutomaticSearch,
|
|
1319
|
-
enableInteractiveSearch: i.enableInteractiveSearch,
|
|
1320
|
-
priority: i.priority,
|
|
1321
|
-
})),
|
|
1322
|
-
naming: naming,
|
|
1323
|
-
mediaManagement: {
|
|
1324
|
-
recycleBin: mediaManagement.recycleBin || 'not set',
|
|
1325
|
-
recycleBinCleanupDays: mediaManagement.recycleBinCleanupDays,
|
|
1326
|
-
downloadPropersAndRepacks: mediaManagement.downloadPropersAndRepacks,
|
|
1327
|
-
deleteEmptyFolders: mediaManagement.deleteEmptyFolders,
|
|
1328
|
-
copyUsingHardlinks: mediaManagement.copyUsingHardlinks,
|
|
1329
|
-
importExtraFiles: mediaManagement.importExtraFiles,
|
|
1330
|
-
extraFileExtensions: mediaManagement.extraFileExtensions,
|
|
1331
|
-
},
|
|
1332
|
-
tags: tags.map((t) => t.label),
|
|
1333
|
-
...(metadataProfiles && { metadataProfiles }),
|
|
1334
|
-
};
|
|
2588
|
+
case "sonarr_search_all_missing": {
|
|
2589
|
+
if (!clients.sonarr)
|
|
2590
|
+
throw new Error("Sonarr not configured");
|
|
2591
|
+
const result = await clients.sonarr.searchAllMissing();
|
|
1335
2592
|
return {
|
|
1336
2593
|
content: [{
|
|
1337
2594
|
type: "text",
|
|
1338
|
-
text: JSON.stringify(
|
|
2595
|
+
text: JSON.stringify({
|
|
2596
|
+
success: true,
|
|
2597
|
+
message: 'Search triggered for all missing episodes',
|
|
2598
|
+
commandId: result.id,
|
|
2599
|
+
}, null, 2),
|
|
1339
2600
|
}],
|
|
1340
2601
|
};
|
|
1341
2602
|
}
|
|
1342
|
-
//
|
|
1343
|
-
case "
|
|
1344
|
-
if (!clients.
|
|
1345
|
-
throw new Error("
|
|
1346
|
-
const
|
|
2603
|
+
// Radarr handlers
|
|
2604
|
+
case "radarr_get_movies": {
|
|
2605
|
+
if (!clients.radarr)
|
|
2606
|
+
throw new Error("Radarr not configured");
|
|
2607
|
+
const movies = await clients.radarr.getMovies();
|
|
1347
2608
|
return {
|
|
1348
2609
|
content: [{
|
|
1349
2610
|
type: "text",
|
|
1350
2611
|
text: JSON.stringify({
|
|
1351
|
-
count:
|
|
1352
|
-
|
|
1353
|
-
id:
|
|
1354
|
-
title:
|
|
1355
|
-
year:
|
|
1356
|
-
status:
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
monitored: s.monitored,
|
|
2612
|
+
count: movies.length,
|
|
2613
|
+
movies: movies.map(m => ({
|
|
2614
|
+
id: m.id,
|
|
2615
|
+
title: m.title,
|
|
2616
|
+
year: m.year,
|
|
2617
|
+
status: m.status,
|
|
2618
|
+
hasFile: m.hasFile,
|
|
2619
|
+
sizeOnDisk: formatBytes(m.sizeOnDisk),
|
|
2620
|
+
monitored: m.monitored,
|
|
2621
|
+
studio: m.studio,
|
|
1362
2622
|
})),
|
|
1363
2623
|
}, null, 2),
|
|
1364
2624
|
}],
|
|
1365
2625
|
};
|
|
1366
2626
|
}
|
|
1367
|
-
case "
|
|
1368
|
-
if (!clients.
|
|
1369
|
-
throw new Error("
|
|
2627
|
+
case "radarr_search": {
|
|
2628
|
+
if (!clients.radarr)
|
|
2629
|
+
throw new Error("Radarr not configured");
|
|
1370
2630
|
const term = args.term;
|
|
1371
|
-
const results = await clients.
|
|
2631
|
+
const results = await clients.radarr.searchMovies(term);
|
|
1372
2632
|
return {
|
|
1373
2633
|
content: [{
|
|
1374
2634
|
type: "text",
|
|
@@ -1377,17 +2637,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1377
2637
|
results: results.slice(0, 10).map(r => ({
|
|
1378
2638
|
title: r.title,
|
|
1379
2639
|
year: r.year,
|
|
1380
|
-
|
|
2640
|
+
tmdbId: r.tmdbId,
|
|
2641
|
+
imdbId: r.imdbId,
|
|
1381
2642
|
overview: r.overview?.substring(0, 200) + (r.overview && r.overview.length > 200 ? '...' : ''),
|
|
1382
2643
|
})),
|
|
1383
2644
|
}, null, 2),
|
|
1384
2645
|
}],
|
|
1385
2646
|
};
|
|
1386
2647
|
}
|
|
1387
|
-
case "
|
|
1388
|
-
if (!clients.
|
|
1389
|
-
throw new Error("
|
|
1390
|
-
const queue = await clients.
|
|
2648
|
+
case "radarr_get_queue": {
|
|
2649
|
+
if (!clients.radarr)
|
|
2650
|
+
throw new Error("Radarr not configured");
|
|
2651
|
+
const queue = await clients.radarr.getQueue();
|
|
1391
2652
|
return {
|
|
1392
2653
|
content: [{
|
|
1393
2654
|
type: "text",
|
|
@@ -1404,83 +2665,44 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1404
2665
|
}],
|
|
1405
2666
|
};
|
|
1406
2667
|
}
|
|
1407
|
-
case "
|
|
1408
|
-
if (!clients.
|
|
1409
|
-
throw new Error("
|
|
1410
|
-
const days = args?.days ||
|
|
2668
|
+
case "radarr_get_calendar": {
|
|
2669
|
+
if (!clients.radarr)
|
|
2670
|
+
throw new Error("Radarr not configured");
|
|
2671
|
+
const days = args?.days || 30;
|
|
1411
2672
|
const start = new Date().toISOString().split('T')[0];
|
|
1412
2673
|
const end = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
|
|
1413
|
-
const calendar = await clients.
|
|
1414
|
-
return {
|
|
1415
|
-
content: [{ type: "text", text: JSON.stringify(calendar, null, 2) }],
|
|
1416
|
-
};
|
|
1417
|
-
}
|
|
1418
|
-
case "sonarr_get_episodes": {
|
|
1419
|
-
if (!clients.sonarr)
|
|
1420
|
-
throw new Error("Sonarr not configured");
|
|
1421
|
-
const { seriesId, seasonNumber } = args;
|
|
1422
|
-
const episodes = await clients.sonarr.getEpisodes(seriesId, seasonNumber);
|
|
1423
|
-
return {
|
|
1424
|
-
content: [{
|
|
1425
|
-
type: "text",
|
|
1426
|
-
text: JSON.stringify({
|
|
1427
|
-
count: episodes.length,
|
|
1428
|
-
episodes: episodes.map(e => ({
|
|
1429
|
-
id: e.id,
|
|
1430
|
-
seasonNumber: e.seasonNumber,
|
|
1431
|
-
episodeNumber: e.episodeNumber,
|
|
1432
|
-
title: e.title,
|
|
1433
|
-
airDate: e.airDate,
|
|
1434
|
-
hasFile: e.hasFile,
|
|
1435
|
-
monitored: e.monitored,
|
|
1436
|
-
})),
|
|
1437
|
-
}, null, 2),
|
|
1438
|
-
}],
|
|
1439
|
-
};
|
|
1440
|
-
}
|
|
1441
|
-
case "sonarr_search_missing": {
|
|
1442
|
-
if (!clients.sonarr)
|
|
1443
|
-
throw new Error("Sonarr not configured");
|
|
1444
|
-
const seriesId = args.seriesId;
|
|
1445
|
-
const result = await clients.sonarr.searchMissing(seriesId);
|
|
2674
|
+
const calendar = await clients.radarr.getCalendar(start, end);
|
|
1446
2675
|
return {
|
|
1447
|
-
content: [{
|
|
1448
|
-
type: "text",
|
|
1449
|
-
text: JSON.stringify({
|
|
1450
|
-
success: true,
|
|
1451
|
-
message: `Search triggered for missing episodes`,
|
|
1452
|
-
commandId: result.id,
|
|
1453
|
-
}, null, 2),
|
|
1454
|
-
}],
|
|
2676
|
+
content: [{ type: "text", text: JSON.stringify(calendar, null, 2) }],
|
|
1455
2677
|
};
|
|
1456
2678
|
}
|
|
1457
|
-
case "
|
|
1458
|
-
if (!clients.
|
|
1459
|
-
throw new Error("
|
|
1460
|
-
const
|
|
1461
|
-
const result = await clients.
|
|
2679
|
+
case "radarr_search_movie": {
|
|
2680
|
+
if (!clients.radarr)
|
|
2681
|
+
throw new Error("Radarr not configured");
|
|
2682
|
+
const movieId = args.movieId;
|
|
2683
|
+
const result = await clients.radarr.searchMovie(movieId);
|
|
1462
2684
|
return {
|
|
1463
2685
|
content: [{
|
|
1464
2686
|
type: "text",
|
|
1465
2687
|
text: JSON.stringify({
|
|
1466
2688
|
success: true,
|
|
1467
|
-
message: `Search triggered for
|
|
2689
|
+
message: `Search triggered for movie`,
|
|
1468
2690
|
commandId: result.id,
|
|
1469
2691
|
}, null, 2),
|
|
1470
2692
|
}],
|
|
1471
2693
|
};
|
|
1472
2694
|
}
|
|
1473
|
-
case "
|
|
1474
|
-
if (!clients.
|
|
1475
|
-
throw new Error("
|
|
1476
|
-
const {
|
|
1477
|
-
const
|
|
1478
|
-
|
|
2695
|
+
case "radarr_add_movie": {
|
|
2696
|
+
if (!clients.radarr)
|
|
2697
|
+
throw new Error("Radarr not configured");
|
|
2698
|
+
const { tmdbId, title, qualityProfileId, rootFolderPath, monitored, minimumAvailability, tags } = args;
|
|
2699
|
+
const movie = await clients.radarr.addMovie({
|
|
2700
|
+
tmdbId,
|
|
1479
2701
|
title,
|
|
1480
2702
|
qualityProfileId,
|
|
1481
2703
|
rootFolderPath,
|
|
1482
|
-
seasonFolder,
|
|
1483
2704
|
monitored,
|
|
2705
|
+
minimumAvailability: minimumAvailability || 'released',
|
|
1484
2706
|
tags,
|
|
1485
2707
|
});
|
|
1486
2708
|
return {
|
|
@@ -1488,374 +2710,396 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1488
2710
|
type: "text",
|
|
1489
2711
|
text: JSON.stringify({
|
|
1490
2712
|
success: true,
|
|
1491
|
-
message: `
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
id:
|
|
1495
|
-
title:
|
|
1496
|
-
path:
|
|
1497
|
-
monitored:
|
|
2713
|
+
message: `Movie '${movie.title}' added to Radarr`,
|
|
2714
|
+
movieId: movie.id,
|
|
2715
|
+
movie: {
|
|
2716
|
+
id: movie.id,
|
|
2717
|
+
title: movie.title,
|
|
2718
|
+
path: movie.path,
|
|
2719
|
+
monitored: movie.monitored,
|
|
1498
2720
|
},
|
|
1499
2721
|
}, null, 2),
|
|
1500
2722
|
}],
|
|
1501
2723
|
};
|
|
1502
2724
|
}
|
|
1503
|
-
case "
|
|
1504
|
-
if (!clients.
|
|
1505
|
-
throw new Error("
|
|
1506
|
-
const {
|
|
1507
|
-
const
|
|
2725
|
+
case "radarr_update_movie": {
|
|
2726
|
+
if (!clients.radarr)
|
|
2727
|
+
throw new Error("Radarr not configured");
|
|
2728
|
+
const { movieId, ...updates } = args;
|
|
2729
|
+
const movie = await clients.radarr.updateMovie(movieId, updates);
|
|
1508
2730
|
return {
|
|
1509
2731
|
content: [{
|
|
1510
2732
|
type: "text",
|
|
1511
2733
|
text: JSON.stringify({
|
|
1512
2734
|
success: true,
|
|
1513
|
-
message: `
|
|
1514
|
-
|
|
2735
|
+
message: `Movie '${movie.title}' updated`,
|
|
2736
|
+
movieId: movie.id,
|
|
1515
2737
|
updates: Object.keys(updates),
|
|
1516
2738
|
}, null, 2),
|
|
1517
2739
|
}],
|
|
1518
2740
|
};
|
|
1519
2741
|
}
|
|
1520
|
-
case "
|
|
1521
|
-
if (!clients.
|
|
1522
|
-
throw new Error("
|
|
1523
|
-
const {
|
|
1524
|
-
await clients.
|
|
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);
|
|
2742
|
+
case "radarr_delete_movie": {
|
|
2743
|
+
if (!clients.radarr)
|
|
2744
|
+
throw new Error("Radarr not configured");
|
|
2745
|
+
const { movieId, deleteFiles = false, addImportExclusion = false } = args;
|
|
2746
|
+
await clients.radarr.deleteMovie(movieId, deleteFiles, addImportExclusion);
|
|
1557
2747
|
return {
|
|
1558
2748
|
content: [{
|
|
1559
2749
|
type: "text",
|
|
1560
2750
|
text: JSON.stringify({
|
|
1561
2751
|
success: true,
|
|
1562
|
-
message: `
|
|
2752
|
+
message: `Movie deleted (files: ${deleteFiles}, exclude: ${addImportExclusion})`,
|
|
1563
2753
|
}, null, 2),
|
|
1564
2754
|
}],
|
|
1565
2755
|
};
|
|
1566
2756
|
}
|
|
1567
|
-
case "
|
|
1568
|
-
if (!clients.
|
|
1569
|
-
throw new Error("
|
|
1570
|
-
const {
|
|
1571
|
-
|
|
2757
|
+
case "radarr_get_history": {
|
|
2758
|
+
if (!clients.radarr)
|
|
2759
|
+
throw new Error("Radarr not configured");
|
|
2760
|
+
const { page = 1, pageSize = 20, movieId } = args;
|
|
2761
|
+
if (movieId) {
|
|
2762
|
+
const history = await clients.radarr.getMovieHistory(movieId);
|
|
2763
|
+
return {
|
|
2764
|
+
content: [{
|
|
2765
|
+
type: "text",
|
|
2766
|
+
text: JSON.stringify({
|
|
2767
|
+
movieId,
|
|
2768
|
+
count: history.length,
|
|
2769
|
+
records: history.map((h) => ({
|
|
2770
|
+
id: h.id,
|
|
2771
|
+
date: h.date,
|
|
2772
|
+
eventType: h.eventType,
|
|
2773
|
+
sourceTitle: h.sourceTitle,
|
|
2774
|
+
quality: h.quality?.quality?.name,
|
|
2775
|
+
downloadId: h.downloadId,
|
|
2776
|
+
})),
|
|
2777
|
+
}, null, 2),
|
|
2778
|
+
}],
|
|
2779
|
+
};
|
|
2780
|
+
}
|
|
2781
|
+
const history = await clients.radarr.getHistory(page, pageSize);
|
|
1572
2782
|
return {
|
|
1573
2783
|
content: [{
|
|
1574
2784
|
type: "text",
|
|
1575
2785
|
text: JSON.stringify({
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
2786
|
+
page,
|
|
2787
|
+
pageSize,
|
|
2788
|
+
totalRecords: history.totalRecords,
|
|
2789
|
+
records: history.records.map((h) => ({
|
|
2790
|
+
id: h.id,
|
|
2791
|
+
movieId: h.movieId,
|
|
2792
|
+
date: h.date,
|
|
2793
|
+
eventType: h.eventType,
|
|
2794
|
+
sourceTitle: h.sourceTitle,
|
|
2795
|
+
quality: h.quality?.quality?.name,
|
|
2796
|
+
})),
|
|
1579
2797
|
}, null, 2),
|
|
1580
2798
|
}],
|
|
1581
2799
|
};
|
|
1582
2800
|
}
|
|
1583
|
-
case "
|
|
1584
|
-
if (!clients.
|
|
1585
|
-
throw new Error("
|
|
1586
|
-
const {
|
|
1587
|
-
const result = await clients.
|
|
2801
|
+
case "radarr_refresh_movie": {
|
|
2802
|
+
if (!clients.radarr)
|
|
2803
|
+
throw new Error("Radarr not configured");
|
|
2804
|
+
const { movieId } = args;
|
|
2805
|
+
const result = await clients.radarr.refreshMovie(movieId);
|
|
1588
2806
|
return {
|
|
1589
2807
|
content: [{
|
|
1590
2808
|
type: "text",
|
|
1591
2809
|
text: JSON.stringify({
|
|
1592
2810
|
success: true,
|
|
1593
|
-
message: `
|
|
2811
|
+
message: `Movie ${movieId} metadata refresh triggered`,
|
|
1594
2812
|
commandId: result.id,
|
|
1595
2813
|
}, null, 2),
|
|
1596
2814
|
}],
|
|
1597
2815
|
};
|
|
1598
2816
|
}
|
|
1599
|
-
case "
|
|
1600
|
-
if (!clients.
|
|
1601
|
-
throw new Error("
|
|
1602
|
-
const {
|
|
1603
|
-
const result = await clients.
|
|
2817
|
+
case "radarr_rescan_movie": {
|
|
2818
|
+
if (!clients.radarr)
|
|
2819
|
+
throw new Error("Radarr not configured");
|
|
2820
|
+
const { movieId } = args;
|
|
2821
|
+
const result = await clients.radarr.rescanMovie(movieId);
|
|
1604
2822
|
return {
|
|
1605
2823
|
content: [{
|
|
1606
2824
|
type: "text",
|
|
1607
2825
|
text: JSON.stringify({
|
|
1608
2826
|
success: true,
|
|
1609
|
-
message: `
|
|
2827
|
+
message: `Movie ${movieId} disk rescan triggered`,
|
|
1610
2828
|
commandId: result.id,
|
|
1611
2829
|
}, null, 2),
|
|
1612
2830
|
}],
|
|
1613
2831
|
};
|
|
1614
2832
|
}
|
|
1615
|
-
case "
|
|
1616
|
-
if (!clients.
|
|
1617
|
-
throw new Error("
|
|
1618
|
-
const {
|
|
1619
|
-
const result = await clients.
|
|
2833
|
+
case "radarr_rename_movie": {
|
|
2834
|
+
if (!clients.radarr)
|
|
2835
|
+
throw new Error("Radarr not configured");
|
|
2836
|
+
const { movieId } = args;
|
|
2837
|
+
const result = await clients.radarr.renameMovie(movieId);
|
|
1620
2838
|
return {
|
|
1621
2839
|
content: [{
|
|
1622
2840
|
type: "text",
|
|
1623
2841
|
text: JSON.stringify({
|
|
1624
2842
|
success: true,
|
|
1625
|
-
message: `
|
|
2843
|
+
message: `Movie ${movieId} files will be renamed`,
|
|
1626
2844
|
commandId: result.id,
|
|
1627
2845
|
}, null, 2),
|
|
1628
2846
|
}],
|
|
1629
2847
|
};
|
|
1630
2848
|
}
|
|
1631
|
-
case "
|
|
1632
|
-
if (!clients.
|
|
1633
|
-
throw new Error("
|
|
1634
|
-
const
|
|
1635
|
-
const releases = await clients.sonarr.getReleases(seriesId, page, pageSize);
|
|
2849
|
+
case "radarr_get_collections": {
|
|
2850
|
+
if (!clients.radarr)
|
|
2851
|
+
throw new Error("Radarr not configured");
|
|
2852
|
+
const collections = await clients.radarr.getCollections();
|
|
1636
2853
|
return {
|
|
1637
2854
|
content: [{
|
|
1638
2855
|
type: "text",
|
|
1639
2856
|
text: JSON.stringify({
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
age: r.age,
|
|
1650
|
-
ageHours: r.ageHours,
|
|
1651
|
-
rejected: r.rejected,
|
|
1652
|
-
rejectionReason: r.rejectionReason,
|
|
1653
|
-
publishedDate: r.publishedDate,
|
|
2857
|
+
count: collections.length,
|
|
2858
|
+
collections: collections.map((c) => ({
|
|
2859
|
+
id: c.id,
|
|
2860
|
+
title: c.title,
|
|
2861
|
+
tmdbId: c.tmdbId,
|
|
2862
|
+
monitored: c.monitored,
|
|
2863
|
+
movieCount: c.movies?.length || 0,
|
|
2864
|
+
qualityProfileId: c.qualityProfileId,
|
|
2865
|
+
rootFolderPath: c.rootFolderPath,
|
|
1654
2866
|
})),
|
|
1655
2867
|
}, null, 2),
|
|
1656
2868
|
}],
|
|
1657
2869
|
};
|
|
1658
2870
|
}
|
|
1659
|
-
case "
|
|
1660
|
-
if (!clients.
|
|
1661
|
-
throw new Error("
|
|
1662
|
-
const {
|
|
1663
|
-
await clients.
|
|
2871
|
+
case "radarr_update_collection": {
|
|
2872
|
+
if (!clients.radarr)
|
|
2873
|
+
throw new Error("Radarr not configured");
|
|
2874
|
+
const { collectionId, ...updates } = args;
|
|
2875
|
+
const collection = await clients.radarr.updateCollection(collectionId, updates);
|
|
1664
2876
|
return {
|
|
1665
2877
|
content: [{
|
|
1666
2878
|
type: "text",
|
|
1667
2879
|
text: JSON.stringify({
|
|
1668
2880
|
success: true,
|
|
1669
|
-
message: `
|
|
2881
|
+
message: `Collection '${collection.title}' updated`,
|
|
2882
|
+
collectionId: collection.id,
|
|
2883
|
+
updates: Object.keys(updates),
|
|
1670
2884
|
}, null, 2),
|
|
1671
2885
|
}],
|
|
1672
2886
|
};
|
|
1673
2887
|
}
|
|
1674
|
-
case "
|
|
1675
|
-
if (!clients.
|
|
1676
|
-
throw new Error("
|
|
1677
|
-
const
|
|
2888
|
+
case "radarr_get_movie_files": {
|
|
2889
|
+
if (!clients.radarr)
|
|
2890
|
+
throw new Error("Radarr not configured");
|
|
2891
|
+
const { movieId } = args;
|
|
2892
|
+
const files = await clients.radarr.getMovieFiles(movieId);
|
|
1678
2893
|
return {
|
|
1679
2894
|
content: [{
|
|
1680
2895
|
type: "text",
|
|
1681
2896
|
text: JSON.stringify({
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
2897
|
+
movieId,
|
|
2898
|
+
count: files.length,
|
|
2899
|
+
files: files.map((f) => ({
|
|
2900
|
+
id: f.id,
|
|
2901
|
+
relativePath: f.relativePath,
|
|
2902
|
+
size: formatBytes(f.size),
|
|
2903
|
+
quality: f.quality?.quality?.name,
|
|
2904
|
+
dateAdded: f.dateAdded,
|
|
2905
|
+
mediaInfo: f.mediaInfo ? {
|
|
2906
|
+
resolution: f.mediaInfo.resolution,
|
|
2907
|
+
videoCodec: f.mediaInfo.videoCodec,
|
|
2908
|
+
audioCodec: f.mediaInfo.audioCodec,
|
|
2909
|
+
audioChannels: f.mediaInfo.audioChannels,
|
|
2910
|
+
runTime: f.mediaInfo.runTime,
|
|
2911
|
+
} : null,
|
|
2912
|
+
})),
|
|
1686
2913
|
}, null, 2),
|
|
1687
2914
|
}],
|
|
1688
2915
|
};
|
|
1689
2916
|
}
|
|
1690
|
-
case "
|
|
1691
|
-
if (!clients.
|
|
1692
|
-
throw new Error("
|
|
1693
|
-
const {
|
|
1694
|
-
|
|
2917
|
+
case "radarr_delete_movie_file": {
|
|
2918
|
+
if (!clients.radarr)
|
|
2919
|
+
throw new Error("Radarr not configured");
|
|
2920
|
+
const { movieFileId } = args;
|
|
2921
|
+
await clients.radarr.deleteMovieFile(movieFileId);
|
|
1695
2922
|
return {
|
|
1696
2923
|
content: [{
|
|
1697
2924
|
type: "text",
|
|
1698
2925
|
text: JSON.stringify({
|
|
1699
2926
|
success: true,
|
|
1700
|
-
message: `
|
|
1701
|
-
profileId: profile.id,
|
|
1702
|
-
profile,
|
|
2927
|
+
message: `Movie file ${movieFileId} deleted`,
|
|
1703
2928
|
}, null, 2),
|
|
1704
2929
|
}],
|
|
1705
2930
|
};
|
|
1706
2931
|
}
|
|
1707
|
-
case "
|
|
1708
|
-
if (!clients.
|
|
1709
|
-
throw new Error("
|
|
1710
|
-
const {
|
|
1711
|
-
await clients.
|
|
2932
|
+
case "radarr_get_blocklist": {
|
|
2933
|
+
if (!clients.radarr)
|
|
2934
|
+
throw new Error("Radarr not configured");
|
|
2935
|
+
const { page = 1, pageSize = 20 } = args;
|
|
2936
|
+
const blocklist = await clients.radarr.getBlocklist(page, pageSize);
|
|
1712
2937
|
return {
|
|
1713
2938
|
content: [{
|
|
1714
2939
|
type: "text",
|
|
1715
2940
|
text: JSON.stringify({
|
|
1716
|
-
|
|
1717
|
-
|
|
2941
|
+
page,
|
|
2942
|
+
pageSize,
|
|
2943
|
+
totalRecords: blocklist.totalRecords,
|
|
2944
|
+
records: blocklist.records.map((b) => ({
|
|
2945
|
+
id: b.id,
|
|
2946
|
+
movieId: b.movieId,
|
|
2947
|
+
sourceTitle: b.sourceTitle,
|
|
2948
|
+
date: b.date,
|
|
2949
|
+
protocol: b.protocol,
|
|
2950
|
+
indexer: b.indexer,
|
|
2951
|
+
message: b.message,
|
|
2952
|
+
})),
|
|
1718
2953
|
}, null, 2),
|
|
1719
2954
|
}],
|
|
1720
2955
|
};
|
|
1721
2956
|
}
|
|
1722
|
-
case "
|
|
1723
|
-
if (!clients.
|
|
1724
|
-
throw new Error("
|
|
1725
|
-
const {
|
|
1726
|
-
|
|
2957
|
+
case "radarr_delete_blocklist_item": {
|
|
2958
|
+
if (!clients.radarr)
|
|
2959
|
+
throw new Error("Radarr not configured");
|
|
2960
|
+
const { blocklistId } = args;
|
|
2961
|
+
await clients.radarr.deleteBlocklistItem(blocklistId);
|
|
1727
2962
|
return {
|
|
1728
2963
|
content: [{
|
|
1729
2964
|
type: "text",
|
|
1730
2965
|
text: JSON.stringify({
|
|
1731
2966
|
success: true,
|
|
1732
|
-
message: `
|
|
1733
|
-
tagId: tag.id,
|
|
1734
|
-
tag,
|
|
2967
|
+
message: `Blocklist item ${blocklistId} deleted`,
|
|
1735
2968
|
}, null, 2),
|
|
1736
2969
|
}],
|
|
1737
2970
|
};
|
|
1738
2971
|
}
|
|
1739
|
-
case "
|
|
1740
|
-
if (!clients.
|
|
1741
|
-
throw new Error("
|
|
1742
|
-
const {
|
|
1743
|
-
const
|
|
2972
|
+
case "radarr_get_extra_files": {
|
|
2973
|
+
if (!clients.radarr)
|
|
2974
|
+
throw new Error("Radarr not configured");
|
|
2975
|
+
const { movieId } = args;
|
|
2976
|
+
const files = await clients.radarr.getExtraFiles(movieId);
|
|
1744
2977
|
return {
|
|
1745
2978
|
content: [{
|
|
1746
2979
|
type: "text",
|
|
1747
2980
|
text: JSON.stringify({
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
2981
|
+
movieId,
|
|
2982
|
+
count: files.length,
|
|
2983
|
+
files: files.map((f) => ({
|
|
2984
|
+
id: f.id,
|
|
2985
|
+
relativePath: f.relativePath,
|
|
2986
|
+
extension: f.extension,
|
|
2987
|
+
type: f.type,
|
|
2988
|
+
})),
|
|
1752
2989
|
}, null, 2),
|
|
1753
2990
|
}],
|
|
1754
2991
|
};
|
|
1755
2992
|
}
|
|
1756
|
-
case "
|
|
1757
|
-
if (!clients.
|
|
1758
|
-
throw new Error("
|
|
1759
|
-
const {
|
|
1760
|
-
await clients.
|
|
2993
|
+
case "radarr_get_credits": {
|
|
2994
|
+
if (!clients.radarr)
|
|
2995
|
+
throw new Error("Radarr not configured");
|
|
2996
|
+
const { movieId } = args;
|
|
2997
|
+
const credits = await clients.radarr.getCredits(movieId);
|
|
1761
2998
|
return {
|
|
1762
2999
|
content: [{
|
|
1763
3000
|
type: "text",
|
|
1764
3001
|
text: JSON.stringify({
|
|
1765
|
-
|
|
1766
|
-
|
|
3002
|
+
movieId,
|
|
3003
|
+
count: credits.length,
|
|
3004
|
+
cast: credits.filter((c) => c.type === 'cast').slice(0, 20).map((c) => ({
|
|
3005
|
+
name: c.personName,
|
|
3006
|
+
character: c.character,
|
|
3007
|
+
order: c.order,
|
|
3008
|
+
})),
|
|
3009
|
+
crew: credits.filter((c) => c.type === 'crew').slice(0, 10).map((c) => ({
|
|
3010
|
+
name: c.personName,
|
|
3011
|
+
job: c.job,
|
|
3012
|
+
department: c.department,
|
|
3013
|
+
})),
|
|
1767
3014
|
}, null, 2),
|
|
1768
3015
|
}],
|
|
1769
3016
|
};
|
|
1770
3017
|
}
|
|
1771
|
-
|
|
1772
|
-
case "radarr_get_movies": {
|
|
3018
|
+
case "radarr_get_wanted": {
|
|
1773
3019
|
if (!clients.radarr)
|
|
1774
3020
|
throw new Error("Radarr not configured");
|
|
1775
|
-
const
|
|
3021
|
+
const { page = 1, pageSize = 20 } = args;
|
|
3022
|
+
const wanted = await clients.radarr.getWanted(page, pageSize);
|
|
1776
3023
|
return {
|
|
1777
3024
|
content: [{
|
|
1778
3025
|
type: "text",
|
|
1779
3026
|
text: JSON.stringify({
|
|
1780
|
-
|
|
1781
|
-
|
|
3027
|
+
page,
|
|
3028
|
+
pageSize,
|
|
3029
|
+
totalRecords: wanted.totalRecords,
|
|
3030
|
+
movies: wanted.records.map((m) => ({
|
|
1782
3031
|
id: m.id,
|
|
1783
3032
|
title: m.title,
|
|
1784
3033
|
year: m.year,
|
|
1785
3034
|
status: m.status,
|
|
1786
|
-
hasFile: m.hasFile,
|
|
1787
|
-
sizeOnDisk: formatBytes(m.sizeOnDisk),
|
|
1788
3035
|
monitored: m.monitored,
|
|
1789
|
-
|
|
3036
|
+
isAvailable: m.isAvailable,
|
|
3037
|
+
minimumAvailability: m.minimumAvailability,
|
|
1790
3038
|
})),
|
|
1791
3039
|
}, null, 2),
|
|
1792
3040
|
}],
|
|
1793
3041
|
};
|
|
1794
3042
|
}
|
|
1795
|
-
case "
|
|
3043
|
+
case "radarr_search_missing": {
|
|
1796
3044
|
if (!clients.radarr)
|
|
1797
3045
|
throw new Error("Radarr not configured");
|
|
1798
|
-
const
|
|
1799
|
-
const results = await clients.radarr.searchMovies(term);
|
|
3046
|
+
const result = await clients.radarr.searchMissing();
|
|
1800
3047
|
return {
|
|
1801
3048
|
content: [{
|
|
1802
|
-
type: "text",
|
|
1803
|
-
text: JSON.stringify({
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
year: r.year,
|
|
1808
|
-
tmdbId: r.tmdbId,
|
|
1809
|
-
imdbId: r.imdbId,
|
|
1810
|
-
overview: r.overview?.substring(0, 200) + (r.overview && r.overview.length > 200 ? '...' : ''),
|
|
1811
|
-
})),
|
|
3049
|
+
type: "text",
|
|
3050
|
+
text: JSON.stringify({
|
|
3051
|
+
success: true,
|
|
3052
|
+
message: 'Search triggered for all missing movies',
|
|
3053
|
+
commandId: result.id,
|
|
1812
3054
|
}, null, 2),
|
|
1813
3055
|
}],
|
|
1814
3056
|
};
|
|
1815
3057
|
}
|
|
1816
|
-
case "
|
|
3058
|
+
case "radarr_create_tag": {
|
|
1817
3059
|
if (!clients.radarr)
|
|
1818
3060
|
throw new Error("Radarr not configured");
|
|
1819
|
-
const
|
|
3061
|
+
const { label } = args;
|
|
3062
|
+
const tag = await clients.radarr.createTag(label);
|
|
1820
3063
|
return {
|
|
1821
3064
|
content: [{
|
|
1822
3065
|
type: "text",
|
|
1823
3066
|
text: JSON.stringify({
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
progress: ((1 - q.sizeleft / q.size) * 100).toFixed(1) + '%',
|
|
1829
|
-
timeLeft: q.timeleft,
|
|
1830
|
-
downloadClient: q.downloadClient,
|
|
1831
|
-
})),
|
|
3067
|
+
success: true,
|
|
3068
|
+
message: `Tag '${label}' created`,
|
|
3069
|
+
tagId: tag.id,
|
|
3070
|
+
tag,
|
|
1832
3071
|
}, null, 2),
|
|
1833
3072
|
}],
|
|
1834
3073
|
};
|
|
1835
3074
|
}
|
|
1836
|
-
case "
|
|
3075
|
+
case "radarr_update_tag": {
|
|
1837
3076
|
if (!clients.radarr)
|
|
1838
3077
|
throw new Error("Radarr not configured");
|
|
1839
|
-
const
|
|
1840
|
-
const
|
|
1841
|
-
const end = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
|
|
1842
|
-
const calendar = await clients.radarr.getCalendar(start, end);
|
|
3078
|
+
const { tagId, label } = args;
|
|
3079
|
+
const tag = await clients.radarr.updateTag(tagId, label);
|
|
1843
3080
|
return {
|
|
1844
|
-
content: [{
|
|
3081
|
+
content: [{
|
|
3082
|
+
type: "text",
|
|
3083
|
+
text: JSON.stringify({
|
|
3084
|
+
success: true,
|
|
3085
|
+
message: `Tag ${tagId} updated to '${label}'`,
|
|
3086
|
+
tagId: tag.id,
|
|
3087
|
+
tag,
|
|
3088
|
+
}, null, 2),
|
|
3089
|
+
}],
|
|
1845
3090
|
};
|
|
1846
3091
|
}
|
|
1847
|
-
case "
|
|
3092
|
+
case "radarr_delete_tag": {
|
|
1848
3093
|
if (!clients.radarr)
|
|
1849
3094
|
throw new Error("Radarr not configured");
|
|
1850
|
-
const
|
|
1851
|
-
|
|
3095
|
+
const { tagId } = args;
|
|
3096
|
+
await clients.radarr.deleteTag(tagId);
|
|
1852
3097
|
return {
|
|
1853
3098
|
content: [{
|
|
1854
3099
|
type: "text",
|
|
1855
3100
|
text: JSON.stringify({
|
|
1856
3101
|
success: true,
|
|
1857
|
-
message: `
|
|
1858
|
-
commandId: result.id,
|
|
3102
|
+
message: `Tag ${tagId} deleted`,
|
|
1859
3103
|
}, null, 2),
|
|
1860
3104
|
}],
|
|
1861
3105
|
};
|
|
@@ -2147,7 +3391,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2147
3391
|
case "prowlarr_get_indexers": {
|
|
2148
3392
|
if (!clients.prowlarr)
|
|
2149
3393
|
throw new Error("Prowlarr not configured");
|
|
2150
|
-
const indexers = await clients.prowlarr.
|
|
3394
|
+
const indexers = await clients.prowlarr.getProwlarrIndexers();
|
|
2151
3395
|
return {
|
|
2152
3396
|
content: [{
|
|
2153
3397
|
type: "text",
|
|
@@ -2157,9 +3401,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2157
3401
|
id: i.id,
|
|
2158
3402
|
name: i.name,
|
|
2159
3403
|
protocol: i.protocol,
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
3404
|
+
enable: i.enable,
|
|
3405
|
+
supportsRss: i.supportsRss,
|
|
3406
|
+
supportsSearch: i.supportsSearch,
|
|
2163
3407
|
priority: i.priority,
|
|
2164
3408
|
})),
|
|
2165
3409
|
}, null, 2),
|
|
@@ -2179,7 +3423,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2179
3423
|
if (!clients.prowlarr)
|
|
2180
3424
|
throw new Error("Prowlarr not configured");
|
|
2181
3425
|
const results = await clients.prowlarr.testAllIndexers();
|
|
2182
|
-
const indexers = await clients.prowlarr.
|
|
3426
|
+
const indexers = await clients.prowlarr.getProwlarrIndexers();
|
|
2183
3427
|
const indexerMap = new Map(indexers.map(i => [i.id, i.name]));
|
|
2184
3428
|
return {
|
|
2185
3429
|
content: [{
|
|
@@ -2225,6 +3469,350 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2225
3469
|
}],
|
|
2226
3470
|
};
|
|
2227
3471
|
}
|
|
3472
|
+
case "prowlarr_get_indexer": {
|
|
3473
|
+
if (!clients.prowlarr)
|
|
3474
|
+
throw new Error("Prowlarr not configured");
|
|
3475
|
+
const { indexerId } = args;
|
|
3476
|
+
const indexer = await clients.prowlarr.getIndexerById(indexerId);
|
|
3477
|
+
return {
|
|
3478
|
+
content: [{
|
|
3479
|
+
type: "text",
|
|
3480
|
+
text: JSON.stringify(indexer, null, 2),
|
|
3481
|
+
}],
|
|
3482
|
+
};
|
|
3483
|
+
}
|
|
3484
|
+
case "prowlarr_update_indexer": {
|
|
3485
|
+
if (!clients.prowlarr)
|
|
3486
|
+
throw new Error("Prowlarr not configured");
|
|
3487
|
+
const { indexerId, enable, priority, tags } = args;
|
|
3488
|
+
const updates = {};
|
|
3489
|
+
if (enable !== undefined)
|
|
3490
|
+
updates.enable = enable;
|
|
3491
|
+
if (priority !== undefined)
|
|
3492
|
+
updates.priority = priority;
|
|
3493
|
+
if (tags !== undefined)
|
|
3494
|
+
updates.tags = tags;
|
|
3495
|
+
const updated = await clients.prowlarr.updateIndexer(indexerId, updates);
|
|
3496
|
+
return {
|
|
3497
|
+
content: [{
|
|
3498
|
+
type: "text",
|
|
3499
|
+
text: JSON.stringify({
|
|
3500
|
+
success: true,
|
|
3501
|
+
indexer: {
|
|
3502
|
+
id: updated.id,
|
|
3503
|
+
name: updated.name,
|
|
3504
|
+
enable: updated.enable,
|
|
3505
|
+
priority: updated.priority,
|
|
3506
|
+
tags: updated.tags,
|
|
3507
|
+
},
|
|
3508
|
+
}, null, 2),
|
|
3509
|
+
}],
|
|
3510
|
+
};
|
|
3511
|
+
}
|
|
3512
|
+
case "prowlarr_delete_indexer": {
|
|
3513
|
+
if (!clients.prowlarr)
|
|
3514
|
+
throw new Error("Prowlarr not configured");
|
|
3515
|
+
const { indexerId } = args;
|
|
3516
|
+
await clients.prowlarr.deleteIndexer(indexerId);
|
|
3517
|
+
return {
|
|
3518
|
+
content: [{
|
|
3519
|
+
type: "text",
|
|
3520
|
+
text: JSON.stringify({ success: true, message: `Indexer ${indexerId} deleted` }, null, 2),
|
|
3521
|
+
}],
|
|
3522
|
+
};
|
|
3523
|
+
}
|
|
3524
|
+
case "prowlarr_test_indexer": {
|
|
3525
|
+
if (!clients.prowlarr)
|
|
3526
|
+
throw new Error("Prowlarr not configured");
|
|
3527
|
+
const { indexerId } = args;
|
|
3528
|
+
const result = await clients.prowlarr.testIndexer(indexerId);
|
|
3529
|
+
const indexer = await clients.prowlarr.getIndexerById(indexerId);
|
|
3530
|
+
return {
|
|
3531
|
+
content: [{
|
|
3532
|
+
type: "text",
|
|
3533
|
+
text: JSON.stringify({
|
|
3534
|
+
id: indexerId,
|
|
3535
|
+
name: indexer.name,
|
|
3536
|
+
isValid: result.isValid,
|
|
3537
|
+
errors: result.validationFailures.map(f => f.errorMessage),
|
|
3538
|
+
}, null, 2),
|
|
3539
|
+
}],
|
|
3540
|
+
};
|
|
3541
|
+
}
|
|
3542
|
+
case "prowlarr_get_applications": {
|
|
3543
|
+
if (!clients.prowlarr)
|
|
3544
|
+
throw new Error("Prowlarr not configured");
|
|
3545
|
+
const apps = await clients.prowlarr.getApplications();
|
|
3546
|
+
return {
|
|
3547
|
+
content: [{
|
|
3548
|
+
type: "text",
|
|
3549
|
+
text: JSON.stringify({
|
|
3550
|
+
count: apps.length,
|
|
3551
|
+
applications: apps.map(a => ({
|
|
3552
|
+
id: a.id,
|
|
3553
|
+
name: a.name,
|
|
3554
|
+
implementation: a.implementation,
|
|
3555
|
+
syncLevel: a.syncLevel,
|
|
3556
|
+
tags: a.tags,
|
|
3557
|
+
})),
|
|
3558
|
+
}, null, 2),
|
|
3559
|
+
}],
|
|
3560
|
+
};
|
|
3561
|
+
}
|
|
3562
|
+
case "prowlarr_get_application": {
|
|
3563
|
+
if (!clients.prowlarr)
|
|
3564
|
+
throw new Error("Prowlarr not configured");
|
|
3565
|
+
const { applicationId } = args;
|
|
3566
|
+
const app = await clients.prowlarr.getApplicationById(applicationId);
|
|
3567
|
+
return {
|
|
3568
|
+
content: [{
|
|
3569
|
+
type: "text",
|
|
3570
|
+
text: JSON.stringify(app, null, 2),
|
|
3571
|
+
}],
|
|
3572
|
+
};
|
|
3573
|
+
}
|
|
3574
|
+
case "prowlarr_update_application": {
|
|
3575
|
+
if (!clients.prowlarr)
|
|
3576
|
+
throw new Error("Prowlarr not configured");
|
|
3577
|
+
const { applicationId, syncLevel, tags } = args;
|
|
3578
|
+
const updates = {};
|
|
3579
|
+
if (syncLevel !== undefined)
|
|
3580
|
+
updates.syncLevel = syncLevel;
|
|
3581
|
+
if (tags !== undefined)
|
|
3582
|
+
updates.tags = tags;
|
|
3583
|
+
const updated = await clients.prowlarr.updateApplication(applicationId, updates);
|
|
3584
|
+
return {
|
|
3585
|
+
content: [{
|
|
3586
|
+
type: "text",
|
|
3587
|
+
text: JSON.stringify({
|
|
3588
|
+
success: true,
|
|
3589
|
+
application: {
|
|
3590
|
+
id: updated.id,
|
|
3591
|
+
name: updated.name,
|
|
3592
|
+
syncLevel: updated.syncLevel,
|
|
3593
|
+
tags: updated.tags,
|
|
3594
|
+
},
|
|
3595
|
+
}, null, 2),
|
|
3596
|
+
}],
|
|
3597
|
+
};
|
|
3598
|
+
}
|
|
3599
|
+
case "prowlarr_delete_application": {
|
|
3600
|
+
if (!clients.prowlarr)
|
|
3601
|
+
throw new Error("Prowlarr not configured");
|
|
3602
|
+
const { applicationId } = args;
|
|
3603
|
+
await clients.prowlarr.deleteApplication(applicationId);
|
|
3604
|
+
return {
|
|
3605
|
+
content: [{
|
|
3606
|
+
type: "text",
|
|
3607
|
+
text: JSON.stringify({ success: true, message: `Application ${applicationId} deleted` }, null, 2),
|
|
3608
|
+
}],
|
|
3609
|
+
};
|
|
3610
|
+
}
|
|
3611
|
+
case "prowlarr_test_application": {
|
|
3612
|
+
if (!clients.prowlarr)
|
|
3613
|
+
throw new Error("Prowlarr not configured");
|
|
3614
|
+
const { applicationId } = args;
|
|
3615
|
+
const result = await clients.prowlarr.testApplication(applicationId);
|
|
3616
|
+
const app = await clients.prowlarr.getApplicationById(applicationId);
|
|
3617
|
+
return {
|
|
3618
|
+
content: [{
|
|
3619
|
+
type: "text",
|
|
3620
|
+
text: JSON.stringify({
|
|
3621
|
+
id: applicationId,
|
|
3622
|
+
name: app.name,
|
|
3623
|
+
isValid: result.isValid,
|
|
3624
|
+
errors: result.validationFailures.map(f => f.errorMessage),
|
|
3625
|
+
}, null, 2),
|
|
3626
|
+
}],
|
|
3627
|
+
};
|
|
3628
|
+
}
|
|
3629
|
+
case "prowlarr_sync_applications": {
|
|
3630
|
+
if (!clients.prowlarr)
|
|
3631
|
+
throw new Error("Prowlarr not configured");
|
|
3632
|
+
const result = await clients.prowlarr.syncApplications();
|
|
3633
|
+
return {
|
|
3634
|
+
content: [{
|
|
3635
|
+
type: "text",
|
|
3636
|
+
text: JSON.stringify({
|
|
3637
|
+
success: true,
|
|
3638
|
+
message: "Application indexer sync triggered",
|
|
3639
|
+
commandId: result.id,
|
|
3640
|
+
}, null, 2),
|
|
3641
|
+
}],
|
|
3642
|
+
};
|
|
3643
|
+
}
|
|
3644
|
+
case "prowlarr_get_app_profiles": {
|
|
3645
|
+
if (!clients.prowlarr)
|
|
3646
|
+
throw new Error("Prowlarr not configured");
|
|
3647
|
+
const profiles = await clients.prowlarr.getAppProfiles();
|
|
3648
|
+
return {
|
|
3649
|
+
content: [{
|
|
3650
|
+
type: "text",
|
|
3651
|
+
text: JSON.stringify({
|
|
3652
|
+
count: profiles.length,
|
|
3653
|
+
profiles: profiles.map(p => ({
|
|
3654
|
+
id: p.id,
|
|
3655
|
+
name: p.name,
|
|
3656
|
+
enableRss: p.enableRss,
|
|
3657
|
+
enableAutomaticSearch: p.enableAutomaticSearch,
|
|
3658
|
+
enableInteractiveSearch: p.enableInteractiveSearch,
|
|
3659
|
+
minimumSeeders: p.minimumSeeders,
|
|
3660
|
+
})),
|
|
3661
|
+
}, null, 2),
|
|
3662
|
+
}],
|
|
3663
|
+
};
|
|
3664
|
+
}
|
|
3665
|
+
case "prowlarr_get_history": {
|
|
3666
|
+
if (!clients.prowlarr)
|
|
3667
|
+
throw new Error("Prowlarr not configured");
|
|
3668
|
+
const { page, pageSize, indexerId } = args;
|
|
3669
|
+
let history;
|
|
3670
|
+
if (indexerId) {
|
|
3671
|
+
const records = await clients.prowlarr.getIndexerHistory(indexerId);
|
|
3672
|
+
history = { records, totalRecords: records.length };
|
|
3673
|
+
}
|
|
3674
|
+
else {
|
|
3675
|
+
history = await clients.prowlarr.getHistory(page || 1, pageSize || 20);
|
|
3676
|
+
}
|
|
3677
|
+
return {
|
|
3678
|
+
content: [{
|
|
3679
|
+
type: "text",
|
|
3680
|
+
text: JSON.stringify({
|
|
3681
|
+
totalRecords: history.totalRecords,
|
|
3682
|
+
records: history.records.map((r) => ({
|
|
3683
|
+
id: r.id,
|
|
3684
|
+
indexerId: r.indexerId,
|
|
3685
|
+
eventType: r.eventType,
|
|
3686
|
+
successful: r.successful,
|
|
3687
|
+
date: r.date,
|
|
3688
|
+
query: r.data?.query,
|
|
3689
|
+
})),
|
|
3690
|
+
}, null, 2),
|
|
3691
|
+
}],
|
|
3692
|
+
};
|
|
3693
|
+
}
|
|
3694
|
+
case "prowlarr_get_tags": {
|
|
3695
|
+
if (!clients.prowlarr)
|
|
3696
|
+
throw new Error("Prowlarr not configured");
|
|
3697
|
+
const tags = await clients.prowlarr.getTags();
|
|
3698
|
+
return {
|
|
3699
|
+
content: [{
|
|
3700
|
+
type: "text",
|
|
3701
|
+
text: JSON.stringify({ count: tags.length, tags }, null, 2),
|
|
3702
|
+
}],
|
|
3703
|
+
};
|
|
3704
|
+
}
|
|
3705
|
+
case "prowlarr_create_tag": {
|
|
3706
|
+
if (!clients.prowlarr)
|
|
3707
|
+
throw new Error("Prowlarr not configured");
|
|
3708
|
+
const { label } = args;
|
|
3709
|
+
const tag = await clients.prowlarr.createTag(label);
|
|
3710
|
+
return {
|
|
3711
|
+
content: [{
|
|
3712
|
+
type: "text",
|
|
3713
|
+
text: JSON.stringify({ success: true, tag }, null, 2),
|
|
3714
|
+
}],
|
|
3715
|
+
};
|
|
3716
|
+
}
|
|
3717
|
+
case "prowlarr_update_tag": {
|
|
3718
|
+
if (!clients.prowlarr)
|
|
3719
|
+
throw new Error("Prowlarr not configured");
|
|
3720
|
+
const { tagId, label } = args;
|
|
3721
|
+
const tag = await clients.prowlarr.updateTag(tagId, label);
|
|
3722
|
+
return {
|
|
3723
|
+
content: [{
|
|
3724
|
+
type: "text",
|
|
3725
|
+
text: JSON.stringify({ success: true, tag }, null, 2),
|
|
3726
|
+
}],
|
|
3727
|
+
};
|
|
3728
|
+
}
|
|
3729
|
+
case "prowlarr_delete_tag": {
|
|
3730
|
+
if (!clients.prowlarr)
|
|
3731
|
+
throw new Error("Prowlarr not configured");
|
|
3732
|
+
const { tagId } = args;
|
|
3733
|
+
await clients.prowlarr.deleteTag(tagId);
|
|
3734
|
+
return {
|
|
3735
|
+
content: [{
|
|
3736
|
+
type: "text",
|
|
3737
|
+
text: JSON.stringify({ success: true, message: `Tag ${tagId} deleted` }, null, 2),
|
|
3738
|
+
}],
|
|
3739
|
+
};
|
|
3740
|
+
}
|
|
3741
|
+
case "prowlarr_get_indexer_schemas": {
|
|
3742
|
+
if (!clients.prowlarr)
|
|
3743
|
+
throw new Error("Prowlarr not configured");
|
|
3744
|
+
const schemas = await clients.prowlarr.getIndexerSchemas();
|
|
3745
|
+
return {
|
|
3746
|
+
content: [{
|
|
3747
|
+
type: "text",
|
|
3748
|
+
text: JSON.stringify({
|
|
3749
|
+
count: schemas.length,
|
|
3750
|
+
schemas: schemas.map(s => ({
|
|
3751
|
+
implementation: s.implementation,
|
|
3752
|
+
name: s.name,
|
|
3753
|
+
protocol: s.protocol,
|
|
3754
|
+
privacy: s.privacy,
|
|
3755
|
+
})),
|
|
3756
|
+
}, null, 2),
|
|
3757
|
+
}],
|
|
3758
|
+
};
|
|
3759
|
+
}
|
|
3760
|
+
case "prowlarr_get_notifications": {
|
|
3761
|
+
if (!clients.prowlarr)
|
|
3762
|
+
throw new Error("Prowlarr not configured");
|
|
3763
|
+
const notifications = await clients.prowlarr.getNotifications();
|
|
3764
|
+
return {
|
|
3765
|
+
content: [{
|
|
3766
|
+
type: "text",
|
|
3767
|
+
text: JSON.stringify({
|
|
3768
|
+
count: notifications.length,
|
|
3769
|
+
notifications: notifications.map(n => ({
|
|
3770
|
+
id: n.id,
|
|
3771
|
+
name: n.name,
|
|
3772
|
+
implementation: n.implementation,
|
|
3773
|
+
onHealthIssue: n.onHealthIssue,
|
|
3774
|
+
onApplicationUpdate: n.onApplicationUpdate,
|
|
3775
|
+
})),
|
|
3776
|
+
}, null, 2),
|
|
3777
|
+
}],
|
|
3778
|
+
};
|
|
3779
|
+
}
|
|
3780
|
+
case "prowlarr_rss_sync": {
|
|
3781
|
+
if (!clients.prowlarr)
|
|
3782
|
+
throw new Error("Prowlarr not configured");
|
|
3783
|
+
const result = await clients.prowlarr.rssSync();
|
|
3784
|
+
return {
|
|
3785
|
+
content: [{
|
|
3786
|
+
type: "text",
|
|
3787
|
+
text: JSON.stringify({
|
|
3788
|
+
success: true,
|
|
3789
|
+
message: "RSS sync triggered for all indexers",
|
|
3790
|
+
commandId: result.id,
|
|
3791
|
+
}, null, 2),
|
|
3792
|
+
}],
|
|
3793
|
+
};
|
|
3794
|
+
}
|
|
3795
|
+
case "prowlarr_get_download_clients": {
|
|
3796
|
+
if (!clients.prowlarr)
|
|
3797
|
+
throw new Error("Prowlarr not configured");
|
|
3798
|
+
const downloadClients = await clients.prowlarr.getDownloadClients();
|
|
3799
|
+
return {
|
|
3800
|
+
content: [{
|
|
3801
|
+
type: "text",
|
|
3802
|
+
text: JSON.stringify({
|
|
3803
|
+
count: downloadClients.length,
|
|
3804
|
+
clients: downloadClients.map(c => ({
|
|
3805
|
+
id: c.id,
|
|
3806
|
+
name: c.name,
|
|
3807
|
+
implementation: c.implementation,
|
|
3808
|
+
protocol: c.protocol,
|
|
3809
|
+
enable: c.enable,
|
|
3810
|
+
priority: c.priority,
|
|
3811
|
+
})),
|
|
3812
|
+
}, null, 2),
|
|
3813
|
+
}],
|
|
3814
|
+
};
|
|
3815
|
+
}
|
|
2228
3816
|
// Cross-service search
|
|
2229
3817
|
case "arr_search_all": {
|
|
2230
3818
|
const term = args.term;
|