arch-ops-server 3.1.0__py3-none-any.whl → 3.3.0__py3-none-any.whl
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.
- arch_ops_server/__init__.py +3 -1
- arch_ops_server/server.py +200 -76
- arch_ops_server/system_health_check.py +189 -0
- arch_ops_server/tool_metadata.py +19 -0
- {arch_ops_server-3.1.0.dist-info → arch_ops_server-3.3.0.dist-info}/METADATA +27 -7
- {arch_ops_server-3.1.0.dist-info → arch_ops_server-3.3.0.dist-info}/RECORD +8 -7
- {arch_ops_server-3.1.0.dist-info → arch_ops_server-3.3.0.dist-info}/WHEEL +2 -2
- {arch_ops_server-3.1.0.dist-info → arch_ops_server-3.3.0.dist-info}/entry_points.txt +0 -0
arch_ops_server/__init__.py
CHANGED
|
@@ -6,7 +6,7 @@ A Model Context Protocol server that bridges AI assistants with the Arch Linux
|
|
|
6
6
|
ecosystem, providing access to the Arch Wiki, AUR, and official repositories.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
__version__ = "3.
|
|
9
|
+
__version__ = "3.3.0"
|
|
10
10
|
|
|
11
11
|
from .wiki import search_wiki, get_wiki_page, get_wiki_page_as_text
|
|
12
12
|
from .aur import (
|
|
@@ -43,6 +43,7 @@ from .system import (
|
|
|
43
43
|
check_failed_services,
|
|
44
44
|
get_boot_logs
|
|
45
45
|
)
|
|
46
|
+
from .system_health_check import run_system_health_check
|
|
46
47
|
from .news import (
|
|
47
48
|
get_latest_news,
|
|
48
49
|
check_critical_news,
|
|
@@ -147,6 +148,7 @@ __all__ = [
|
|
|
147
148
|
"get_pacman_cache_stats",
|
|
148
149
|
"check_failed_services",
|
|
149
150
|
"get_boot_logs",
|
|
151
|
+
"run_system_health_check",
|
|
150
152
|
# News
|
|
151
153
|
"get_latest_news",
|
|
152
154
|
"check_critical_news",
|
arch_ops_server/server.py
CHANGED
|
@@ -15,6 +15,7 @@ from mcp.server import Server
|
|
|
15
15
|
from mcp.types import (
|
|
16
16
|
Resource,
|
|
17
17
|
Tool,
|
|
18
|
+
ToolAnnotations,
|
|
18
19
|
TextContent,
|
|
19
20
|
ImageContent,
|
|
20
21
|
EmbeddedResource,
|
|
@@ -76,6 +77,8 @@ from . import (
|
|
|
76
77
|
analyze_makepkg_conf,
|
|
77
78
|
check_ignored_packages,
|
|
78
79
|
get_parallel_downloads_setting,
|
|
80
|
+
# System health check
|
|
81
|
+
run_system_health_check,
|
|
79
82
|
# Utils
|
|
80
83
|
IS_ARCH,
|
|
81
84
|
run_command,
|
|
@@ -92,6 +95,57 @@ server = Server("arch-ops-server")
|
|
|
92
95
|
# HELPER FUNCTIONS
|
|
93
96
|
# ============================================================================
|
|
94
97
|
|
|
98
|
+
def create_platform_error_message(tool_name: str, current_platform: str = None) -> str:
|
|
99
|
+
"""
|
|
100
|
+
Create an informative error message with recovery hints for platform-specific tools.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
tool_name: Name of the tool that requires Arch Linux
|
|
104
|
+
current_platform: Current platform/OS (auto-detected if not provided)
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Formatted error message with recovery suggestions
|
|
108
|
+
"""
|
|
109
|
+
import platform
|
|
110
|
+
|
|
111
|
+
if current_platform is None:
|
|
112
|
+
try:
|
|
113
|
+
if IS_ARCH:
|
|
114
|
+
current_platform = "Arch Linux"
|
|
115
|
+
else:
|
|
116
|
+
import distro
|
|
117
|
+
current_platform = f"{distro.name()} {distro.version()}" if distro.name() else platform.system()
|
|
118
|
+
except:
|
|
119
|
+
current_platform = platform.system()
|
|
120
|
+
|
|
121
|
+
error_msg = f"""Error: '{tool_name}' requires Arch Linux
|
|
122
|
+
|
|
123
|
+
Current system: {current_platform}
|
|
124
|
+
|
|
125
|
+
This tool requires a running Arch Linux system to function. However, you can still:
|
|
126
|
+
|
|
127
|
+
Alternative actions:
|
|
128
|
+
1. Use platform-agnostic tools:
|
|
129
|
+
- search_archwiki: Search Arch Wiki documentation (works anywhere)
|
|
130
|
+
- search_aur: Search AUR packages (works anywhere)
|
|
131
|
+
- get_official_package_info: Get package info from archlinux.org (works anywhere)
|
|
132
|
+
- get_latest_news: Check Arch Linux news (works anywhere)
|
|
133
|
+
- check_critical_news: Check for critical Arch news (works anywhere)
|
|
134
|
+
|
|
135
|
+
2. Browse documentation resources:
|
|
136
|
+
- archwiki://<page_title> - Read any Arch Wiki page
|
|
137
|
+
- aur://<package>/info - Get AUR package metadata
|
|
138
|
+
- archrepo://<package> - Get official package details
|
|
139
|
+
|
|
140
|
+
3. If you're planning to use Arch Linux:
|
|
141
|
+
- Visit the Arch Wiki Installation Guide: archwiki://Installation_guide
|
|
142
|
+
- Check latest Arch news before installing: get_latest_news
|
|
143
|
+
|
|
144
|
+
Note: Tools marked with [DISCOVERY], [SECURITY], and news-related tools work on any system."""
|
|
145
|
+
|
|
146
|
+
return error_msg
|
|
147
|
+
|
|
148
|
+
|
|
95
149
|
def create_standard_output_schema(data_schema: dict, description: str = "") -> dict:
|
|
96
150
|
"""
|
|
97
151
|
Create a standard output schema with status, data, error fields.
|
|
@@ -302,6 +356,13 @@ async def list_resources() -> list[Resource]:
|
|
|
302
356
|
mimeType="application/json",
|
|
303
357
|
description="Check when package databases were last synchronized"
|
|
304
358
|
),
|
|
359
|
+
# System health resources
|
|
360
|
+
Resource(
|
|
361
|
+
uri="system://health",
|
|
362
|
+
name="System - Health Check",
|
|
363
|
+
mimeType="application/json",
|
|
364
|
+
description="Comprehensive system health check report"
|
|
365
|
+
),
|
|
305
366
|
]
|
|
306
367
|
|
|
307
368
|
|
|
@@ -392,7 +453,7 @@ async def read_resource(uri: str) -> str:
|
|
|
392
453
|
|
|
393
454
|
elif scheme == "pacman":
|
|
394
455
|
if not IS_ARCH:
|
|
395
|
-
raise ValueError(
|
|
456
|
+
raise ValueError(create_platform_error_message("pacman:// resources"))
|
|
396
457
|
|
|
397
458
|
resource_path = parsed.netloc or parsed.path.lstrip('/')
|
|
398
459
|
|
|
@@ -482,6 +543,11 @@ async def read_resource(uri: str) -> str:
|
|
|
482
543
|
else:
|
|
483
544
|
raise ValueError(result.get("error", "Failed to get boot logs"))
|
|
484
545
|
|
|
546
|
+
elif resource_path == "health":
|
|
547
|
+
# Get system health check
|
|
548
|
+
result = await run_system_health_check()
|
|
549
|
+
return json.dumps(result, indent=2)
|
|
550
|
+
|
|
485
551
|
else:
|
|
486
552
|
raise ValueError(f"Unsupported system resource: {resource_path}")
|
|
487
553
|
|
|
@@ -508,7 +574,7 @@ async def read_resource(uri: str) -> str:
|
|
|
508
574
|
|
|
509
575
|
elif scheme == "mirrors":
|
|
510
576
|
if not IS_ARCH:
|
|
511
|
-
raise ValueError(
|
|
577
|
+
raise ValueError(create_platform_error_message("mirrors:// resources"))
|
|
512
578
|
|
|
513
579
|
resource_path = parsed.netloc or parsed.path.lstrip('/')
|
|
514
580
|
|
|
@@ -527,7 +593,7 @@ async def read_resource(uri: str) -> str:
|
|
|
527
593
|
|
|
528
594
|
elif scheme == "config":
|
|
529
595
|
if not IS_ARCH:
|
|
530
|
-
raise ValueError(
|
|
596
|
+
raise ValueError(create_platform_error_message("config:// resources"))
|
|
531
597
|
|
|
532
598
|
resource_path = parsed.netloc or parsed.path.lstrip('/')
|
|
533
599
|
|
|
@@ -579,7 +645,8 @@ async def list_tools() -> list[Tool]:
|
|
|
579
645
|
}
|
|
580
646
|
},
|
|
581
647
|
"required": ["query"]
|
|
582
|
-
}
|
|
648
|
+
},
|
|
649
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
583
650
|
),
|
|
584
651
|
|
|
585
652
|
# AUR tools
|
|
@@ -606,7 +673,8 @@ async def list_tools() -> list[Tool]:
|
|
|
606
673
|
}
|
|
607
674
|
},
|
|
608
675
|
"required": ["query"]
|
|
609
|
-
}
|
|
676
|
+
},
|
|
677
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
610
678
|
),
|
|
611
679
|
|
|
612
680
|
Tool(
|
|
@@ -621,7 +689,8 @@ async def list_tools() -> list[Tool]:
|
|
|
621
689
|
}
|
|
622
690
|
},
|
|
623
691
|
"required": ["package_name"]
|
|
624
|
-
}
|
|
692
|
+
},
|
|
693
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
625
694
|
),
|
|
626
695
|
|
|
627
696
|
Tool(
|
|
@@ -630,7 +699,8 @@ async def list_tools() -> list[Tool]:
|
|
|
630
699
|
inputSchema={
|
|
631
700
|
"type": "object",
|
|
632
701
|
"properties": {}
|
|
633
|
-
}
|
|
702
|
+
},
|
|
703
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
634
704
|
),
|
|
635
705
|
|
|
636
706
|
Tool(
|
|
@@ -645,7 +715,8 @@ async def list_tools() -> list[Tool]:
|
|
|
645
715
|
}
|
|
646
716
|
},
|
|
647
717
|
"required": ["package_name"]
|
|
648
|
-
}
|
|
718
|
+
},
|
|
719
|
+
annotations=ToolAnnotations(destructiveHint=True)
|
|
649
720
|
),
|
|
650
721
|
|
|
651
722
|
Tool(
|
|
@@ -660,7 +731,8 @@ async def list_tools() -> list[Tool]:
|
|
|
660
731
|
}
|
|
661
732
|
},
|
|
662
733
|
"required": ["pkgbuild_content"]
|
|
663
|
-
}
|
|
734
|
+
},
|
|
735
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
664
736
|
),
|
|
665
737
|
|
|
666
738
|
Tool(
|
|
@@ -675,7 +747,8 @@ async def list_tools() -> list[Tool]:
|
|
|
675
747
|
}
|
|
676
748
|
},
|
|
677
749
|
"required": ["package_info"]
|
|
678
|
-
}
|
|
750
|
+
},
|
|
751
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
679
752
|
),
|
|
680
753
|
|
|
681
754
|
# Package Removal Tools
|
|
@@ -701,7 +774,8 @@ async def list_tools() -> list[Tool]:
|
|
|
701
774
|
}
|
|
702
775
|
},
|
|
703
776
|
"required": ["package_name"]
|
|
704
|
-
}
|
|
777
|
+
},
|
|
778
|
+
annotations=ToolAnnotations(destructiveHint=True)
|
|
705
779
|
),
|
|
706
780
|
|
|
707
781
|
Tool(
|
|
@@ -722,7 +796,8 @@ async def list_tools() -> list[Tool]:
|
|
|
722
796
|
}
|
|
723
797
|
},
|
|
724
798
|
"required": ["package_names"]
|
|
725
|
-
}
|
|
799
|
+
},
|
|
800
|
+
annotations=ToolAnnotations(destructiveHint=True)
|
|
726
801
|
),
|
|
727
802
|
|
|
728
803
|
# Orphan Package Management
|
|
@@ -732,7 +807,8 @@ async def list_tools() -> list[Tool]:
|
|
|
732
807
|
inputSchema={
|
|
733
808
|
"type": "object",
|
|
734
809
|
"properties": {}
|
|
735
|
-
}
|
|
810
|
+
},
|
|
811
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
736
812
|
),
|
|
737
813
|
|
|
738
814
|
Tool(
|
|
@@ -753,7 +829,8 @@ async def list_tools() -> list[Tool]:
|
|
|
753
829
|
}
|
|
754
830
|
},
|
|
755
831
|
"required": []
|
|
756
|
-
}
|
|
832
|
+
},
|
|
833
|
+
annotations=ToolAnnotations(destructiveHint=True)
|
|
757
834
|
),
|
|
758
835
|
|
|
759
836
|
# Package Ownership Tools
|
|
@@ -769,7 +846,8 @@ async def list_tools() -> list[Tool]:
|
|
|
769
846
|
}
|
|
770
847
|
},
|
|
771
848
|
"required": ["file_path"]
|
|
772
|
-
}
|
|
849
|
+
},
|
|
850
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
773
851
|
),
|
|
774
852
|
|
|
775
853
|
Tool(
|
|
@@ -788,7 +866,8 @@ async def list_tools() -> list[Tool]:
|
|
|
788
866
|
}
|
|
789
867
|
},
|
|
790
868
|
"required": ["package_name"]
|
|
791
|
-
}
|
|
869
|
+
},
|
|
870
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
792
871
|
),
|
|
793
872
|
|
|
794
873
|
Tool(
|
|
@@ -803,7 +882,8 @@ async def list_tools() -> list[Tool]:
|
|
|
803
882
|
}
|
|
804
883
|
},
|
|
805
884
|
"required": ["filename_pattern"]
|
|
806
|
-
}
|
|
885
|
+
},
|
|
886
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
807
887
|
),
|
|
808
888
|
|
|
809
889
|
# Package Verification
|
|
@@ -824,7 +904,8 @@ async def list_tools() -> list[Tool]:
|
|
|
824
904
|
}
|
|
825
905
|
},
|
|
826
906
|
"required": ["package_name"]
|
|
827
|
-
}
|
|
907
|
+
},
|
|
908
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
828
909
|
),
|
|
829
910
|
|
|
830
911
|
# Package Groups
|
|
@@ -834,7 +915,8 @@ async def list_tools() -> list[Tool]:
|
|
|
834
915
|
inputSchema={
|
|
835
916
|
"type": "object",
|
|
836
917
|
"properties": {}
|
|
837
|
-
}
|
|
918
|
+
},
|
|
919
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
838
920
|
),
|
|
839
921
|
|
|
840
922
|
Tool(
|
|
@@ -849,7 +931,8 @@ async def list_tools() -> list[Tool]:
|
|
|
849
931
|
}
|
|
850
932
|
},
|
|
851
933
|
"required": ["group_name"]
|
|
852
|
-
}
|
|
934
|
+
},
|
|
935
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
853
936
|
),
|
|
854
937
|
|
|
855
938
|
# Install Reason Management
|
|
@@ -859,7 +942,8 @@ async def list_tools() -> list[Tool]:
|
|
|
859
942
|
inputSchema={
|
|
860
943
|
"type": "object",
|
|
861
944
|
"properties": {}
|
|
862
|
-
}
|
|
945
|
+
},
|
|
946
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
863
947
|
),
|
|
864
948
|
|
|
865
949
|
Tool(
|
|
@@ -874,7 +958,8 @@ async def list_tools() -> list[Tool]:
|
|
|
874
958
|
}
|
|
875
959
|
},
|
|
876
960
|
"required": ["package_name"]
|
|
877
|
-
}
|
|
961
|
+
},
|
|
962
|
+
annotations=ToolAnnotations(destructiveHint=True)
|
|
878
963
|
),
|
|
879
964
|
|
|
880
965
|
Tool(
|
|
@@ -889,7 +974,8 @@ async def list_tools() -> list[Tool]:
|
|
|
889
974
|
}
|
|
890
975
|
},
|
|
891
976
|
"required": ["package_name"]
|
|
892
|
-
}
|
|
977
|
+
},
|
|
978
|
+
annotations=ToolAnnotations(destructiveHint=True)
|
|
893
979
|
),
|
|
894
980
|
|
|
895
981
|
# System Diagnostic Tools
|
|
@@ -899,7 +985,8 @@ async def list_tools() -> list[Tool]:
|
|
|
899
985
|
inputSchema={
|
|
900
986
|
"type": "object",
|
|
901
987
|
"properties": {}
|
|
902
|
-
}
|
|
988
|
+
},
|
|
989
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
903
990
|
),
|
|
904
991
|
|
|
905
992
|
Tool(
|
|
@@ -908,7 +995,8 @@ async def list_tools() -> list[Tool]:
|
|
|
908
995
|
inputSchema={
|
|
909
996
|
"type": "object",
|
|
910
997
|
"properties": {}
|
|
911
|
-
}
|
|
998
|
+
},
|
|
999
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
912
1000
|
),
|
|
913
1001
|
|
|
914
1002
|
Tool(
|
|
@@ -917,7 +1005,8 @@ async def list_tools() -> list[Tool]:
|
|
|
917
1005
|
inputSchema={
|
|
918
1006
|
"type": "object",
|
|
919
1007
|
"properties": {}
|
|
920
|
-
}
|
|
1008
|
+
},
|
|
1009
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
921
1010
|
),
|
|
922
1011
|
|
|
923
1012
|
Tool(
|
|
@@ -926,7 +1015,8 @@ async def list_tools() -> list[Tool]:
|
|
|
926
1015
|
inputSchema={
|
|
927
1016
|
"type": "object",
|
|
928
1017
|
"properties": {}
|
|
929
|
-
}
|
|
1018
|
+
},
|
|
1019
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
930
1020
|
),
|
|
931
1021
|
|
|
932
1022
|
Tool(
|
|
@@ -942,7 +1032,8 @@ async def list_tools() -> list[Tool]:
|
|
|
942
1032
|
}
|
|
943
1033
|
},
|
|
944
1034
|
"required": []
|
|
945
|
-
}
|
|
1035
|
+
},
|
|
1036
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
946
1037
|
),
|
|
947
1038
|
|
|
948
1039
|
# News Tools
|
|
@@ -963,7 +1054,8 @@ async def list_tools() -> list[Tool]:
|
|
|
963
1054
|
}
|
|
964
1055
|
},
|
|
965
1056
|
"required": []
|
|
966
|
-
}
|
|
1057
|
+
},
|
|
1058
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
967
1059
|
),
|
|
968
1060
|
|
|
969
1061
|
Tool(
|
|
@@ -979,7 +1071,8 @@ async def list_tools() -> list[Tool]:
|
|
|
979
1071
|
}
|
|
980
1072
|
},
|
|
981
1073
|
"required": []
|
|
982
|
-
}
|
|
1074
|
+
},
|
|
1075
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
983
1076
|
),
|
|
984
1077
|
|
|
985
1078
|
Tool(
|
|
@@ -988,7 +1081,8 @@ async def list_tools() -> list[Tool]:
|
|
|
988
1081
|
inputSchema={
|
|
989
1082
|
"type": "object",
|
|
990
1083
|
"properties": {}
|
|
991
|
-
}
|
|
1084
|
+
},
|
|
1085
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
992
1086
|
),
|
|
993
1087
|
|
|
994
1088
|
# Transaction Log Tools
|
|
@@ -1011,7 +1105,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1011
1105
|
}
|
|
1012
1106
|
},
|
|
1013
1107
|
"required": []
|
|
1014
|
-
}
|
|
1108
|
+
},
|
|
1109
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1015
1110
|
),
|
|
1016
1111
|
|
|
1017
1112
|
Tool(
|
|
@@ -1026,7 +1121,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1026
1121
|
}
|
|
1027
1122
|
},
|
|
1028
1123
|
"required": ["package_name"]
|
|
1029
|
-
}
|
|
1124
|
+
},
|
|
1125
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1030
1126
|
),
|
|
1031
1127
|
|
|
1032
1128
|
Tool(
|
|
@@ -1035,7 +1131,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1035
1131
|
inputSchema={
|
|
1036
1132
|
"type": "object",
|
|
1037
1133
|
"properties": {}
|
|
1038
|
-
}
|
|
1134
|
+
},
|
|
1135
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1039
1136
|
),
|
|
1040
1137
|
|
|
1041
1138
|
Tool(
|
|
@@ -1051,7 +1148,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1051
1148
|
}
|
|
1052
1149
|
},
|
|
1053
1150
|
"required": []
|
|
1054
|
-
}
|
|
1151
|
+
},
|
|
1152
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1055
1153
|
),
|
|
1056
1154
|
|
|
1057
1155
|
# Mirror Management Tools
|
|
@@ -1061,7 +1159,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1061
1159
|
inputSchema={
|
|
1062
1160
|
"type": "object",
|
|
1063
1161
|
"properties": {}
|
|
1064
|
-
}
|
|
1162
|
+
},
|
|
1163
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1065
1164
|
),
|
|
1066
1165
|
|
|
1067
1166
|
Tool(
|
|
@@ -1076,7 +1175,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1076
1175
|
}
|
|
1077
1176
|
},
|
|
1078
1177
|
"required": []
|
|
1079
|
-
}
|
|
1178
|
+
},
|
|
1179
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1080
1180
|
),
|
|
1081
1181
|
|
|
1082
1182
|
Tool(
|
|
@@ -1096,7 +1196,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1096
1196
|
}
|
|
1097
1197
|
},
|
|
1098
1198
|
"required": []
|
|
1099
|
-
}
|
|
1199
|
+
},
|
|
1200
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1100
1201
|
),
|
|
1101
1202
|
|
|
1102
1203
|
Tool(
|
|
@@ -1105,7 +1206,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1105
1206
|
inputSchema={
|
|
1106
1207
|
"type": "object",
|
|
1107
1208
|
"properties": {}
|
|
1108
|
-
}
|
|
1209
|
+
},
|
|
1210
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1109
1211
|
),
|
|
1110
1212
|
|
|
1111
1213
|
# Configuration Tools
|
|
@@ -1115,7 +1217,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1115
1217
|
inputSchema={
|
|
1116
1218
|
"type": "object",
|
|
1117
1219
|
"properties": {}
|
|
1118
|
-
}
|
|
1220
|
+
},
|
|
1221
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1119
1222
|
),
|
|
1120
1223
|
|
|
1121
1224
|
Tool(
|
|
@@ -1124,7 +1227,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1124
1227
|
inputSchema={
|
|
1125
1228
|
"type": "object",
|
|
1126
1229
|
"properties": {}
|
|
1127
|
-
}
|
|
1230
|
+
},
|
|
1231
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1128
1232
|
),
|
|
1129
1233
|
|
|
1130
1234
|
Tool(
|
|
@@ -1133,7 +1237,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1133
1237
|
inputSchema={
|
|
1134
1238
|
"type": "object",
|
|
1135
1239
|
"properties": {}
|
|
1136
|
-
}
|
|
1240
|
+
},
|
|
1241
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1137
1242
|
),
|
|
1138
1243
|
|
|
1139
1244
|
Tool(
|
|
@@ -1142,7 +1247,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1142
1247
|
inputSchema={
|
|
1143
1248
|
"type": "object",
|
|
1144
1249
|
"properties": {}
|
|
1145
|
-
}
|
|
1250
|
+
},
|
|
1251
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1146
1252
|
),
|
|
1147
1253
|
|
|
1148
1254
|
Tool(
|
|
@@ -1151,7 +1257,18 @@ async def list_tools() -> list[Tool]:
|
|
|
1151
1257
|
inputSchema={
|
|
1152
1258
|
"type": "object",
|
|
1153
1259
|
"properties": {}
|
|
1154
|
-
}
|
|
1260
|
+
},
|
|
1261
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1262
|
+
),
|
|
1263
|
+
|
|
1264
|
+
Tool(
|
|
1265
|
+
name="run_system_health_check",
|
|
1266
|
+
description="[MONITORING] Run a comprehensive system health check. Integrates multiple diagnostics to provide a complete overview of system status, including disk space, failed services, updates, orphan packages, and more. Only works on Arch Linux.",
|
|
1267
|
+
inputSchema={
|
|
1268
|
+
"type": "object",
|
|
1269
|
+
"properties": {}
|
|
1270
|
+
},
|
|
1271
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1155
1272
|
),
|
|
1156
1273
|
]
|
|
1157
1274
|
|
|
@@ -1193,14 +1310,14 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1193
1310
|
|
|
1194
1311
|
elif name == "check_updates_dry_run":
|
|
1195
1312
|
if not IS_ARCH:
|
|
1196
|
-
return [TextContent(type="text", text="
|
|
1313
|
+
return [TextContent(type="text", text=create_platform_error_message("check_updates_dry_run"))]
|
|
1197
1314
|
|
|
1198
1315
|
result = await check_updates_dry_run()
|
|
1199
1316
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
1200
1317
|
|
|
1201
1318
|
elif name == "install_package_secure":
|
|
1202
1319
|
if not IS_ARCH:
|
|
1203
|
-
return [TextContent(type="text", text="
|
|
1320
|
+
return [TextContent(type="text", text=create_platform_error_message("install_package_secure"))]
|
|
1204
1321
|
|
|
1205
1322
|
package_name = arguments["package_name"]
|
|
1206
1323
|
result = await install_package_secure(package_name)
|
|
@@ -1219,7 +1336,7 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1219
1336
|
# Package Removal Tools
|
|
1220
1337
|
elif name == "remove_package":
|
|
1221
1338
|
if not IS_ARCH:
|
|
1222
|
-
return [TextContent(type="text", text="
|
|
1339
|
+
return [TextContent(type="text", text=create_platform_error_message("remove_package"))]
|
|
1223
1340
|
|
|
1224
1341
|
package_name = arguments["package_name"]
|
|
1225
1342
|
remove_dependencies = arguments.get("remove_dependencies", False)
|
|
@@ -1229,7 +1346,7 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1229
1346
|
|
|
1230
1347
|
elif name == "remove_packages_batch":
|
|
1231
1348
|
if not IS_ARCH:
|
|
1232
|
-
return [TextContent(type="text", text="
|
|
1349
|
+
return [TextContent(type="text", text=create_platform_error_message("remove_packages_batch"))]
|
|
1233
1350
|
|
|
1234
1351
|
package_names = arguments["package_names"]
|
|
1235
1352
|
remove_dependencies = arguments.get("remove_dependencies", False)
|
|
@@ -1239,14 +1356,14 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1239
1356
|
# Orphan Package Management
|
|
1240
1357
|
elif name == "list_orphan_packages":
|
|
1241
1358
|
if not IS_ARCH:
|
|
1242
|
-
return [TextContent(type="text", text="
|
|
1359
|
+
return [TextContent(type="text", text=create_platform_error_message("list_orphan_packages"))]
|
|
1243
1360
|
|
|
1244
1361
|
result = await list_orphan_packages()
|
|
1245
1362
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
1246
1363
|
|
|
1247
1364
|
elif name == "remove_orphans":
|
|
1248
1365
|
if not IS_ARCH:
|
|
1249
|
-
return [TextContent(type="text", text="
|
|
1366
|
+
return [TextContent(type="text", text=create_platform_error_message("remove_orphans"))]
|
|
1250
1367
|
|
|
1251
1368
|
dry_run = arguments.get("dry_run", True)
|
|
1252
1369
|
exclude = arguments.get("exclude", None)
|
|
@@ -1256,7 +1373,7 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1256
1373
|
# Package Ownership Tools
|
|
1257
1374
|
elif name == "find_package_owner":
|
|
1258
1375
|
if not IS_ARCH:
|
|
1259
|
-
return [TextContent(type="text", text="
|
|
1376
|
+
return [TextContent(type="text", text=create_platform_error_message("find_package_owner"))]
|
|
1260
1377
|
|
|
1261
1378
|
file_path = arguments["file_path"]
|
|
1262
1379
|
result = await find_package_owner(file_path)
|
|
@@ -1264,7 +1381,7 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1264
1381
|
|
|
1265
1382
|
elif name == "list_package_files":
|
|
1266
1383
|
if not IS_ARCH:
|
|
1267
|
-
return [TextContent(type="text", text="
|
|
1384
|
+
return [TextContent(type="text", text=create_platform_error_message("list_package_files"))]
|
|
1268
1385
|
|
|
1269
1386
|
package_name = arguments["package_name"]
|
|
1270
1387
|
filter_pattern = arguments.get("filter_pattern", None)
|
|
@@ -1273,7 +1390,7 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1273
1390
|
|
|
1274
1391
|
elif name == "search_package_files":
|
|
1275
1392
|
if not IS_ARCH:
|
|
1276
|
-
return [TextContent(type="text", text="
|
|
1393
|
+
return [TextContent(type="text", text=create_platform_error_message("search_package_files"))]
|
|
1277
1394
|
|
|
1278
1395
|
filename_pattern = arguments["filename_pattern"]
|
|
1279
1396
|
result = await search_package_files(filename_pattern)
|
|
@@ -1282,7 +1399,7 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1282
1399
|
# Package Verification
|
|
1283
1400
|
elif name == "verify_package_integrity":
|
|
1284
1401
|
if not IS_ARCH:
|
|
1285
|
-
return [TextContent(type="text", text="
|
|
1402
|
+
return [TextContent(type="text", text=create_platform_error_message("verify_package_integrity"))]
|
|
1286
1403
|
|
|
1287
1404
|
package_name = arguments["package_name"]
|
|
1288
1405
|
thorough = arguments.get("thorough", False)
|
|
@@ -1292,14 +1409,14 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1292
1409
|
# Package Groups
|
|
1293
1410
|
elif name == "list_package_groups":
|
|
1294
1411
|
if not IS_ARCH:
|
|
1295
|
-
return [TextContent(type="text", text="
|
|
1412
|
+
return [TextContent(type="text", text=create_platform_error_message("list_package_groups"))]
|
|
1296
1413
|
|
|
1297
1414
|
result = await list_package_groups()
|
|
1298
1415
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
1299
1416
|
|
|
1300
1417
|
elif name == "list_group_packages":
|
|
1301
1418
|
if not IS_ARCH:
|
|
1302
|
-
return [TextContent(type="text", text="
|
|
1419
|
+
return [TextContent(type="text", text=create_platform_error_message("list_group_packages"))]
|
|
1303
1420
|
|
|
1304
1421
|
group_name = arguments["group_name"]
|
|
1305
1422
|
result = await list_group_packages(group_name)
|
|
@@ -1308,14 +1425,14 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1308
1425
|
# Install Reason Management
|
|
1309
1426
|
elif name == "list_explicit_packages":
|
|
1310
1427
|
if not IS_ARCH:
|
|
1311
|
-
return [TextContent(type="text", text="
|
|
1428
|
+
return [TextContent(type="text", text=create_platform_error_message("list_explicit_packages"))]
|
|
1312
1429
|
|
|
1313
1430
|
result = await list_explicit_packages()
|
|
1314
1431
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
1315
1432
|
|
|
1316
1433
|
elif name == "mark_as_explicit":
|
|
1317
1434
|
if not IS_ARCH:
|
|
1318
|
-
return [TextContent(type="text", text="
|
|
1435
|
+
return [TextContent(type="text", text=create_platform_error_message("mark_as_explicit"))]
|
|
1319
1436
|
|
|
1320
1437
|
package_name = arguments["package_name"]
|
|
1321
1438
|
result = await mark_as_explicit(package_name)
|
|
@@ -1323,7 +1440,7 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1323
1440
|
|
|
1324
1441
|
elif name == "mark_as_dependency":
|
|
1325
1442
|
if not IS_ARCH:
|
|
1326
|
-
return [TextContent(type="text", text="
|
|
1443
|
+
return [TextContent(type="text", text=create_platform_error_message("mark_as_dependency"))]
|
|
1327
1444
|
|
|
1328
1445
|
package_name = arguments["package_name"]
|
|
1329
1446
|
result = await mark_as_dependency(package_name)
|
|
@@ -1340,7 +1457,7 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1340
1457
|
|
|
1341
1458
|
elif name == "get_pacman_cache_stats":
|
|
1342
1459
|
if not IS_ARCH:
|
|
1343
|
-
return [TextContent(type="text", text="
|
|
1460
|
+
return [TextContent(type="text", text=create_platform_error_message("get_pacman_cache_stats"))]
|
|
1344
1461
|
|
|
1345
1462
|
result = await get_pacman_cache_stats()
|
|
1346
1463
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
@@ -1368,7 +1485,7 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1368
1485
|
|
|
1369
1486
|
elif name == "get_news_since_last_update":
|
|
1370
1487
|
if not IS_ARCH:
|
|
1371
|
-
return [TextContent(type="text", text="
|
|
1488
|
+
return [TextContent(type="text", text=create_platform_error_message("get_news_since_last_update"))]
|
|
1372
1489
|
|
|
1373
1490
|
result = await get_news_since_last_update()
|
|
1374
1491
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
@@ -1376,7 +1493,7 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1376
1493
|
# Transaction log tools
|
|
1377
1494
|
elif name == "get_transaction_history":
|
|
1378
1495
|
if not IS_ARCH:
|
|
1379
|
-
return [TextContent(type="text", text="
|
|
1496
|
+
return [TextContent(type="text", text=create_platform_error_message("get_transaction_history"))]
|
|
1380
1497
|
|
|
1381
1498
|
limit = arguments.get("limit", 50)
|
|
1382
1499
|
transaction_type = arguments.get("transaction_type", "all")
|
|
@@ -1385,7 +1502,7 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1385
1502
|
|
|
1386
1503
|
elif name == "find_when_installed":
|
|
1387
1504
|
if not IS_ARCH:
|
|
1388
|
-
return [TextContent(type="text", text="
|
|
1505
|
+
return [TextContent(type="text", text=create_platform_error_message("find_when_installed"))]
|
|
1389
1506
|
|
|
1390
1507
|
package_name = arguments.get("package_name")
|
|
1391
1508
|
if not package_name:
|
|
@@ -1396,14 +1513,14 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1396
1513
|
|
|
1397
1514
|
elif name == "find_failed_transactions":
|
|
1398
1515
|
if not IS_ARCH:
|
|
1399
|
-
return [TextContent(type="text", text="
|
|
1516
|
+
return [TextContent(type="text", text=create_platform_error_message("find_failed_transactions"))]
|
|
1400
1517
|
|
|
1401
1518
|
result = await find_failed_transactions()
|
|
1402
1519
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
1403
1520
|
|
|
1404
1521
|
elif name == "get_database_sync_history":
|
|
1405
1522
|
if not IS_ARCH:
|
|
1406
|
-
return [TextContent(type="text", text="
|
|
1523
|
+
return [TextContent(type="text", text=create_platform_error_message("get_database_sync_history"))]
|
|
1407
1524
|
|
|
1408
1525
|
limit = arguments.get("limit", 20)
|
|
1409
1526
|
result = await get_database_sync_history(limit=limit)
|
|
@@ -1412,14 +1529,14 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1412
1529
|
# Mirror management tools
|
|
1413
1530
|
elif name == "list_active_mirrors":
|
|
1414
1531
|
if not IS_ARCH:
|
|
1415
|
-
return [TextContent(type="text", text="
|
|
1532
|
+
return [TextContent(type="text", text=create_platform_error_message("list_active_mirrors"))]
|
|
1416
1533
|
|
|
1417
1534
|
result = await list_active_mirrors()
|
|
1418
1535
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
1419
1536
|
|
|
1420
1537
|
elif name == "test_mirror_speed":
|
|
1421
1538
|
if not IS_ARCH:
|
|
1422
|
-
return [TextContent(type="text", text="
|
|
1539
|
+
return [TextContent(type="text", text=create_platform_error_message("test_mirror_speed"))]
|
|
1423
1540
|
|
|
1424
1541
|
mirror_url = arguments.get("mirror_url")
|
|
1425
1542
|
result = await test_mirror_speed(mirror_url=mirror_url)
|
|
@@ -1433,7 +1550,7 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1433
1550
|
|
|
1434
1551
|
elif name == "check_mirrorlist_health":
|
|
1435
1552
|
if not IS_ARCH:
|
|
1436
|
-
return [TextContent(type="text", text="
|
|
1553
|
+
return [TextContent(type="text", text=create_platform_error_message("check_mirrorlist_health"))]
|
|
1437
1554
|
|
|
1438
1555
|
result = await check_mirrorlist_health()
|
|
1439
1556
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
@@ -1441,35 +1558,42 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1441
1558
|
# Configuration tools
|
|
1442
1559
|
elif name == "analyze_pacman_conf":
|
|
1443
1560
|
if not IS_ARCH:
|
|
1444
|
-
return [TextContent(type="text", text="
|
|
1561
|
+
return [TextContent(type="text", text=create_platform_error_message("analyze_pacman_conf"))]
|
|
1445
1562
|
|
|
1446
1563
|
result = await analyze_pacman_conf()
|
|
1447
1564
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
1448
1565
|
|
|
1449
1566
|
elif name == "analyze_makepkg_conf":
|
|
1450
1567
|
if not IS_ARCH:
|
|
1451
|
-
return [TextContent(type="text", text="
|
|
1568
|
+
return [TextContent(type="text", text=create_platform_error_message("analyze_makepkg_conf"))]
|
|
1452
1569
|
|
|
1453
1570
|
result = await analyze_makepkg_conf()
|
|
1454
1571
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
1455
1572
|
|
|
1456
1573
|
elif name == "check_ignored_packages":
|
|
1457
1574
|
if not IS_ARCH:
|
|
1458
|
-
return [TextContent(type="text", text="
|
|
1575
|
+
return [TextContent(type="text", text=create_platform_error_message("check_ignored_packages"))]
|
|
1459
1576
|
|
|
1460
1577
|
result = await check_ignored_packages()
|
|
1461
1578
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
1462
1579
|
|
|
1463
1580
|
elif name == "get_parallel_downloads_setting":
|
|
1464
1581
|
if not IS_ARCH:
|
|
1465
|
-
return [TextContent(type="text", text="
|
|
1582
|
+
return [TextContent(type="text", text=create_platform_error_message("get_parallel_downloads_setting"))]
|
|
1466
1583
|
|
|
1467
1584
|
result = await get_parallel_downloads_setting()
|
|
1468
1585
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
1586
|
+
|
|
1587
|
+
elif name == "run_system_health_check":
|
|
1588
|
+
if not IS_ARCH:
|
|
1589
|
+
return [TextContent(type="text", text=create_platform_error_message("run_system_health_check"))]
|
|
1590
|
+
|
|
1591
|
+
result = await run_system_health_check()
|
|
1592
|
+
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
1469
1593
|
|
|
1470
1594
|
elif name == "check_database_freshness":
|
|
1471
1595
|
if not IS_ARCH:
|
|
1472
|
-
return [TextContent(type="text", text="
|
|
1596
|
+
return [TextContent(type="text", text=create_platform_error_message("check_database_freshness"))]
|
|
1473
1597
|
|
|
1474
1598
|
result = await check_database_freshness()
|
|
1475
1599
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
@@ -1810,7 +1934,7 @@ paru -S {package_name} # or yay -S {package_name}
|
|
|
1810
1934
|
role="assistant",
|
|
1811
1935
|
content=PromptMessage.TextContent(
|
|
1812
1936
|
type="text",
|
|
1813
|
-
text="
|
|
1937
|
+
text=create_platform_error_message("safe_system_update prompt")
|
|
1814
1938
|
)
|
|
1815
1939
|
)
|
|
1816
1940
|
]
|
|
@@ -1965,7 +2089,7 @@ paru -S {package_name} # or yay -S {package_name}
|
|
|
1965
2089
|
role="assistant",
|
|
1966
2090
|
content=PromptMessage.TextContent(
|
|
1967
2091
|
type="text",
|
|
1968
|
-
text="
|
|
2092
|
+
text=create_platform_error_message("cleanup_system prompt")
|
|
1969
2093
|
)
|
|
1970
2094
|
)
|
|
1971
2095
|
]
|
|
@@ -2141,7 +2265,7 @@ Be detailed and provide specific mirror URLs and configuration commands."""
|
|
|
2141
2265
|
role="assistant",
|
|
2142
2266
|
content=PromptMessage.TextContent(
|
|
2143
2267
|
type="text",
|
|
2144
|
-
text="
|
|
2268
|
+
text=create_platform_error_message("system_health_check prompt")
|
|
2145
2269
|
)
|
|
2146
2270
|
)
|
|
2147
2271
|
]
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-only OR MIT
|
|
2
|
+
"""
|
|
3
|
+
System health check module.
|
|
4
|
+
Provides a comprehensive system health check by integrating multiple system diagnostics.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Dict, Any
|
|
9
|
+
|
|
10
|
+
from .utils import IS_ARCH
|
|
11
|
+
from . import (
|
|
12
|
+
get_system_info,
|
|
13
|
+
check_disk_space,
|
|
14
|
+
check_failed_services,
|
|
15
|
+
get_pacman_cache_stats,
|
|
16
|
+
check_updates_dry_run,
|
|
17
|
+
check_critical_news,
|
|
18
|
+
list_orphan_packages,
|
|
19
|
+
check_database_freshness,
|
|
20
|
+
check_mirrorlist_health
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def run_system_health_check() -> Dict[str, Any]:
|
|
27
|
+
"""
|
|
28
|
+
Run a comprehensive system health check.
|
|
29
|
+
|
|
30
|
+
This function integrates multiple system diagnostics to provide a complete
|
|
31
|
+
overview of the system's health status in a single call.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Dict with comprehensive health check results
|
|
35
|
+
"""
|
|
36
|
+
logger.info("Starting comprehensive system health check")
|
|
37
|
+
|
|
38
|
+
health_report = {
|
|
39
|
+
"status": "success",
|
|
40
|
+
"system_info": {},
|
|
41
|
+
"disk_space": {},
|
|
42
|
+
"services": {},
|
|
43
|
+
"pacman_cache": {},
|
|
44
|
+
"updates": {},
|
|
45
|
+
"news": {},
|
|
46
|
+
"orphans": {},
|
|
47
|
+
"database": {},
|
|
48
|
+
"mirrors": {},
|
|
49
|
+
"issues": [],
|
|
50
|
+
"suggestions": []
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
# System information
|
|
55
|
+
logger.info("Checking system information")
|
|
56
|
+
system_info = await get_system_info()
|
|
57
|
+
health_report["system_info"] = system_info
|
|
58
|
+
|
|
59
|
+
# Disk space check
|
|
60
|
+
logger.info("Checking disk space")
|
|
61
|
+
disk_space = await check_disk_space()
|
|
62
|
+
health_report["disk_space"] = disk_space
|
|
63
|
+
|
|
64
|
+
# Check for low disk space
|
|
65
|
+
if disk_space.get("status") == "success":
|
|
66
|
+
for partition in disk_space.get("data", []):
|
|
67
|
+
if partition.get("used_percent", 0) > 90:
|
|
68
|
+
health_report["issues"].append({
|
|
69
|
+
"type": "critical",
|
|
70
|
+
"message": f"Low disk space on {partition['mount_point']}: {partition['used_percent']}% used",
|
|
71
|
+
"suggestion": "Clean up unnecessary files or resize the partition"
|
|
72
|
+
})
|
|
73
|
+
elif partition.get("used_percent", 0) > 80:
|
|
74
|
+
health_report["issues"].append({
|
|
75
|
+
"type": "warning",
|
|
76
|
+
"message": f"Disk space getting low on {partition['mount_point']}: {partition['used_percent']}% used",
|
|
77
|
+
"suggestion": "Consider cleaning up files to free up space"
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
# Failed services check
|
|
81
|
+
logger.info("Checking for failed services")
|
|
82
|
+
failed_services = await check_failed_services()
|
|
83
|
+
health_report["services"] = failed_services
|
|
84
|
+
|
|
85
|
+
if failed_services.get("status") == "success" and failed_services.get("data"):
|
|
86
|
+
health_report["issues"].append({
|
|
87
|
+
"type": "warning",
|
|
88
|
+
"message": f"{len(failed_services['data'])} failed systemd services detected",
|
|
89
|
+
"suggestion": "Check systemd journal logs for details about failed services"
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
# Pacman cache statistics
|
|
93
|
+
logger.info("Checking pacman cache")
|
|
94
|
+
cache_stats = await get_pacman_cache_stats()
|
|
95
|
+
health_report["pacman_cache"] = cache_stats
|
|
96
|
+
|
|
97
|
+
if cache_stats.get("status") == "success":
|
|
98
|
+
cache_size = cache_stats.get("data", {}).get("total_size_mb", 0)
|
|
99
|
+
if cache_size > 5000: # 5GB
|
|
100
|
+
health_report["suggestions"].append({
|
|
101
|
+
"message": f"Pacman cache is large ({cache_size:.1f}MB)",
|
|
102
|
+
"action": "Run 'paccache -r' to clean old packages"
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
# Updates check
|
|
106
|
+
logger.info("Checking for available updates")
|
|
107
|
+
updates = await check_updates_dry_run()
|
|
108
|
+
health_report["updates"] = updates
|
|
109
|
+
|
|
110
|
+
if updates.get("status") == "success":
|
|
111
|
+
if updates.get("updates_available"):
|
|
112
|
+
count = updates.get("count", 0)
|
|
113
|
+
health_report["suggestions"].append({
|
|
114
|
+
"message": f"{count} updates available",
|
|
115
|
+
"action": "Run 'sudo pacman -Syu' to update the system"
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
# Critical news check
|
|
119
|
+
logger.info("Checking for critical news")
|
|
120
|
+
critical_news = await check_critical_news()
|
|
121
|
+
health_report["news"] = critical_news
|
|
122
|
+
|
|
123
|
+
if critical_news.get("status") == "success" and critical_news.get("data"):
|
|
124
|
+
health_report["issues"].append({
|
|
125
|
+
"type": "critical",
|
|
126
|
+
"message": f"{len(critical_news['data'])} critical news items require attention",
|
|
127
|
+
"suggestion": "Review the news items before updating"
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
# Orphan packages check
|
|
131
|
+
logger.info("Checking for orphan packages")
|
|
132
|
+
orphans = await list_orphan_packages()
|
|
133
|
+
health_report["orphans"] = orphans
|
|
134
|
+
|
|
135
|
+
if orphans.get("status") == "success":
|
|
136
|
+
orphan_count = len(orphans.get("data", []))
|
|
137
|
+
if orphan_count > 0:
|
|
138
|
+
health_report["suggestions"].append({
|
|
139
|
+
"message": f"{orphan_count} orphan packages detected",
|
|
140
|
+
"action": "Run 'sudo pacman -Rns $(pacman -Qtdq)' to remove orphans"
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
# Database freshness
|
|
144
|
+
logger.info("Checking database freshness")
|
|
145
|
+
db_freshness = await check_database_freshness()
|
|
146
|
+
health_report["database"] = db_freshness
|
|
147
|
+
|
|
148
|
+
# Mirrorlist health
|
|
149
|
+
logger.info("Checking mirrorlist health")
|
|
150
|
+
mirror_health = await check_mirrorlist_health()
|
|
151
|
+
health_report["mirrors"] = mirror_health
|
|
152
|
+
|
|
153
|
+
if mirror_health.get("status") == "success":
|
|
154
|
+
if not mirror_health.get("data", {}).get("healthy", True):
|
|
155
|
+
health_report["issues"].append({
|
|
156
|
+
"type": "warning",
|
|
157
|
+
"message": "Mirrorlist configuration has issues",
|
|
158
|
+
"suggestion": "Run 'reflector' to update your mirrorlist"
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
# Overall health assessment
|
|
162
|
+
issue_count = len(health_report["issues"])
|
|
163
|
+
suggestion_count = len(health_report["suggestions"])
|
|
164
|
+
|
|
165
|
+
health_report["summary"] = {
|
|
166
|
+
"total_issues": issue_count,
|
|
167
|
+
"critical_issues": len([i for i in health_report["issues"] if i["type"] == "critical"]),
|
|
168
|
+
"warnings": len([i for i in health_report["issues"] if i["type"] == "warning"]),
|
|
169
|
+
"suggestions": suggestion_count
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
logger.info(f"Health check completed: {issue_count} issues, {suggestion_count} suggestions")
|
|
173
|
+
|
|
174
|
+
return health_report
|
|
175
|
+
|
|
176
|
+
except Exception as e:
|
|
177
|
+
logger.error(f"Health check failed: {e}")
|
|
178
|
+
return {
|
|
179
|
+
"status": "error",
|
|
180
|
+
"error": str(e),
|
|
181
|
+
"issues": [],
|
|
182
|
+
"suggestions": [],
|
|
183
|
+
"summary": {
|
|
184
|
+
"total_issues": 1,
|
|
185
|
+
"critical_issues": 1,
|
|
186
|
+
"warnings": 0,
|
|
187
|
+
"suggestions": 0
|
|
188
|
+
}
|
|
189
|
+
}
|
arch_ops_server/tool_metadata.py
CHANGED
|
@@ -457,6 +457,25 @@ TOOL_METADATA = {
|
|
|
457
457
|
related_tools=["analyze_pacman_conf"],
|
|
458
458
|
prerequisite_tools=[]
|
|
459
459
|
),
|
|
460
|
+
"run_system_health_check": ToolMetadata(
|
|
461
|
+
name="run_system_health_check",
|
|
462
|
+
category="monitoring",
|
|
463
|
+
platform="arch",
|
|
464
|
+
permission="read",
|
|
465
|
+
workflow="diagnose",
|
|
466
|
+
related_tools=[
|
|
467
|
+
"get_system_info",
|
|
468
|
+
"check_disk_space",
|
|
469
|
+
"check_failed_services",
|
|
470
|
+
"get_pacman_cache_stats",
|
|
471
|
+
"check_updates_dry_run",
|
|
472
|
+
"check_critical_news",
|
|
473
|
+
"list_orphan_packages",
|
|
474
|
+
"check_database_freshness",
|
|
475
|
+
"check_mirrorlist_health"
|
|
476
|
+
],
|
|
477
|
+
prerequisite_tools=[]
|
|
478
|
+
),
|
|
460
479
|
}
|
|
461
480
|
|
|
462
481
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: arch-ops-server
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0
|
|
4
4
|
Summary: MCP server bridging AI assistants with Arch Linux ecosystem (Wiki, AUR, official repos)
|
|
5
5
|
Keywords: arch-linux,mcp,model-context-protocol,aur,pacman,wiki,ai-assistant
|
|
6
6
|
Author: Nihal
|
|
@@ -52,17 +52,18 @@ Leverage AI to get output for digestible, structured results that are ready for
|
|
|
52
52
|
|
|
53
53
|
## Sneak Peak into what's available
|
|
54
54
|
|
|
55
|
-
<details
|
|
56
|
-
|
|
55
|
+
<details>
|
|
56
|
+
|
|
57
|
+
<summary>Using VS Code Sonnet 3.5 for Safe Installation from AUR</summary>
|
|
57
58
|
|
|
58
|
-

|
|
59
60
|
|
|
60
61
|
</details>
|
|
61
62
|
|
|
62
63
|
<details>
|
|
63
|
-
<summary>
|
|
64
|
+
<summary> Asking Claude Code Sonnet 4.5 for fedora equivalent command </summary>
|
|
64
65
|
|
|
65
|
-

|
|
66
67
|
|
|
67
68
|
</details>
|
|
68
69
|
|
|
@@ -71,11 +72,13 @@ Leverage AI to get output for digestible, structured results that are ready for
|
|
|
71
72
|
Direct access to Arch ecosystem data via custom URI schemes:
|
|
72
73
|
|
|
73
74
|
#### Documentation & Search
|
|
75
|
+
|
|
74
76
|
| URI Scheme | Example | Returns |
|
|
75
77
|
|------------|---------|---------|
|
|
76
78
|
| `archwiki://` | `archwiki://Installation_guide` | Markdown-formatted Wiki page |
|
|
77
79
|
|
|
78
80
|
#### Package Information
|
|
81
|
+
|
|
79
82
|
| URI Scheme | Example | Returns |
|
|
80
83
|
|------------|---------|---------|
|
|
81
84
|
| `archrepo://` | `archrepo://vim` | Official repository package details |
|
|
@@ -83,6 +86,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
83
86
|
| `aur://*/pkgbuild` | `aur://yay/pkgbuild` | Raw PKGBUILD with safety analysis |
|
|
84
87
|
|
|
85
88
|
#### System Packages (Arch only)
|
|
89
|
+
|
|
86
90
|
| URI Scheme | Example | Returns |
|
|
87
91
|
|------------|---------|---------|
|
|
88
92
|
| `pacman://installed` | `pacman://installed` | System installed packages list |
|
|
@@ -93,6 +97,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
93
97
|
| `pacman://database/freshness` | `pacman://database/freshness` | Package database sync status |
|
|
94
98
|
|
|
95
99
|
#### System Monitoring & Logs
|
|
100
|
+
|
|
96
101
|
| URI Scheme | Example | Returns |
|
|
97
102
|
|------------|---------|---------|
|
|
98
103
|
| `system://info` | `system://info` | System information (kernel, memory, uptime) |
|
|
@@ -103,6 +108,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
103
108
|
| `pacman://log/failed` | `pacman://log/failed` | Failed package transactions |
|
|
104
109
|
|
|
105
110
|
#### News & Updates
|
|
111
|
+
|
|
106
112
|
| URI Scheme | Example | Returns |
|
|
107
113
|
|------------|---------|---------|
|
|
108
114
|
| `archnews://latest` | `archnews://latest` | Latest Arch Linux news |
|
|
@@ -110,6 +116,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
110
116
|
| `archnews://since-update` | `archnews://since-update` | News since last system update |
|
|
111
117
|
|
|
112
118
|
#### Configuration
|
|
119
|
+
|
|
113
120
|
| URI Scheme | Example | Returns |
|
|
114
121
|
|------------|---------|---------|
|
|
115
122
|
| `config://pacman` | `config://pacman` | Parsed pacman.conf configuration |
|
|
@@ -120,6 +127,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
120
127
|
### Tools (Executable Functions)
|
|
121
128
|
|
|
122
129
|
#### Package Search & Information
|
|
130
|
+
|
|
123
131
|
| Tool | Description | Platform |
|
|
124
132
|
|------|-------------|----------|
|
|
125
133
|
| `search_archwiki` | Query Arch Wiki with ranked results | Any |
|
|
@@ -127,6 +135,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
127
135
|
| `get_official_package_info` | Get official package details (hybrid local/remote) | Any |
|
|
128
136
|
|
|
129
137
|
#### Package Lifecycle Management
|
|
138
|
+
|
|
130
139
|
| Tool | Description | Platform |
|
|
131
140
|
|------|-------------|----------|
|
|
132
141
|
| `check_updates_dry_run` | Check for available updates | Arch only |
|
|
@@ -135,6 +144,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
135
144
|
| `remove_packages_batch` | Remove multiple packages efficiently | Arch only |
|
|
136
145
|
|
|
137
146
|
#### Package Analysis & Maintenance
|
|
147
|
+
|
|
138
148
|
| Tool | Description | Platform |
|
|
139
149
|
|------|-------------|----------|
|
|
140
150
|
| `list_orphan_packages` | Find orphaned packages | Arch only |
|
|
@@ -145,6 +155,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
145
155
|
| `mark_as_dependency` | Allow package to be orphaned | Arch only |
|
|
146
156
|
|
|
147
157
|
#### Package Organization
|
|
158
|
+
|
|
148
159
|
| Tool | Description | Platform |
|
|
149
160
|
|------|-------------|----------|
|
|
150
161
|
| `find_package_owner` | Find which package owns a file | Arch only |
|
|
@@ -154,6 +165,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
154
165
|
| `list_group_packages` | Show packages in specific group | Arch only |
|
|
155
166
|
|
|
156
167
|
#### System Monitoring & Diagnostics
|
|
168
|
+
|
|
157
169
|
| Tool | Description | Platform |
|
|
158
170
|
|------|-------------|----------|
|
|
159
171
|
| `get_system_info` | System info (kernel, memory, uptime) | Any |
|
|
@@ -164,6 +176,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
164
176
|
| `check_database_freshness` | Check package database sync status | Arch only |
|
|
165
177
|
|
|
166
178
|
#### Transaction History & Logs
|
|
179
|
+
|
|
167
180
|
| Tool | Description | Platform |
|
|
168
181
|
|------|-------------|----------|
|
|
169
182
|
| `get_transaction_history` | Recent package transactions (install/upgrade/remove) | Arch only |
|
|
@@ -172,6 +185,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
172
185
|
| `get_database_sync_history` | Database sync events | Arch only |
|
|
173
186
|
|
|
174
187
|
#### News & Safety Checks
|
|
188
|
+
|
|
175
189
|
| Tool | Description | Platform |
|
|
176
190
|
|------|-------------|----------|
|
|
177
191
|
| `get_latest_news` | Fetch Arch Linux news from RSS | Any |
|
|
@@ -179,6 +193,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
179
193
|
| `get_news_since_last_update` | News posted since last system update | Arch only |
|
|
180
194
|
|
|
181
195
|
#### Mirror Management
|
|
196
|
+
|
|
182
197
|
| Tool | Description | Platform |
|
|
183
198
|
|------|-------------|----------|
|
|
184
199
|
| `list_active_mirrors` | Show configured mirrors | Arch only |
|
|
@@ -187,6 +202,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
187
202
|
| `check_mirrorlist_health` | Verify mirror configuration | Arch only |
|
|
188
203
|
|
|
189
204
|
#### Configuration Management
|
|
205
|
+
|
|
190
206
|
| Tool | Description | Platform |
|
|
191
207
|
|------|-------------|----------|
|
|
192
208
|
| `analyze_pacman_conf` | Parse pacman.conf settings | Arch only |
|
|
@@ -195,6 +211,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
195
211
|
| `get_parallel_downloads_setting` | Get parallel download config | Arch only |
|
|
196
212
|
|
|
197
213
|
#### Security Analysis
|
|
214
|
+
|
|
198
215
|
| Tool | Description | Platform |
|
|
199
216
|
|------|-------------|----------|
|
|
200
217
|
| `analyze_pkgbuild_safety` | Comprehensive PKGBUILD analysis (50+ red flags) | Any |
|
|
@@ -214,6 +231,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
214
231
|
## Installation
|
|
215
232
|
|
|
216
233
|
### Prerequisites
|
|
234
|
+
|
|
217
235
|
- Python 3.11+
|
|
218
236
|
- [uv](https://github.com/astral-sh/uv) (recommended) or pip
|
|
219
237
|
|
|
@@ -222,6 +240,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
222
240
|
```bash
|
|
223
241
|
uvx arch-ops-server
|
|
224
242
|
```
|
|
243
|
+
|
|
225
244
|
---
|
|
226
245
|
|
|
227
246
|
## Configuration
|
|
@@ -254,4 +273,5 @@ This project is dual-licensed under your choice of:
|
|
|
254
273
|
|
|
255
274
|
You may use this software under the terms of either license. When redistributing or modifying this software, you may choose which license to apply.
|
|
256
275
|
|
|
257
|
-
By contributing to this project, you agree that your contributions will be licensed under both licenses.
|
|
276
|
+
By contributing to this project, you agree that your contributions will be licensed under both licenses.
|
|
277
|
+
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
arch_ops_server/__init__.py,sha256=
|
|
1
|
+
arch_ops_server/__init__.py,sha256=UiV6PqYZHQzRx_tOZG8G7gMBTXtWG9pkPtPgWn-crzw,4343
|
|
2
2
|
arch_ops_server/aur.py,sha256=poYbh2DW7I1tZfCeNp_7e10fh9ZZx8HTnYZnKKZtflQ,49808
|
|
3
3
|
arch_ops_server/config.py,sha256=4mtpS28vXSMeEVGrTWTMwZEzgIyfl0oCAYEzF7SKxE8,11076
|
|
4
4
|
arch_ops_server/http_server.py,sha256=wZ3hY6o6EftbN1OZiTUau7861LB9ihKWap6gevev_No,31810
|
|
@@ -7,12 +7,13 @@ arch_ops_server/mirrors.py,sha256=Evt-g20cMOTZQl9FbbkbklFd0gKWz-I7vVNrmyQO19U,13
|
|
|
7
7
|
arch_ops_server/news.py,sha256=E97eASR24tq_EaVDYuamIoBl4a7QtBkpscOaUPuU0W4,9359
|
|
8
8
|
arch_ops_server/pacman.py,sha256=S1Gc53CA6o4--YavA03EkxL0dGCZNhoFFZjawlW_p20,38354
|
|
9
9
|
arch_ops_server/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
arch_ops_server/server.py,sha256=
|
|
10
|
+
arch_ops_server/server.py,sha256=aAYEzkxp1PY6Bqv1ZZNKuxZbTAE8N8A8a7X_zhUXs4k,89743
|
|
11
11
|
arch_ops_server/system.py,sha256=JfBUB3KD0veulQ-IIK8IOC8Jn6lqtLMCtlnryiL1n7w,9221
|
|
12
|
-
arch_ops_server/
|
|
12
|
+
arch_ops_server/system_health_check.py,sha256=G_hgPaVz-6tQ_ZAoPEk7aX2rm4lIwbO0xdXcwdBDoaE,7196
|
|
13
|
+
arch_ops_server/tool_metadata.py,sha256=Z0ZhtS1bxo9T_erJlGx9Xf0fKk4oZ0L6UQ8gJu7WEcA,20468
|
|
13
14
|
arch_ops_server/utils.py,sha256=po7MVqCx-hsdx-lOgs7uGicjoUVMf6HvuNNYl2qyFH0,10112
|
|
14
15
|
arch_ops_server/wiki.py,sha256=XB_emMGXYF3Vn5likRICkGOa72YDZvOhtZBgp_d1gg8,7350
|
|
15
|
-
arch_ops_server-3.
|
|
16
|
-
arch_ops_server-3.
|
|
17
|
-
arch_ops_server-3.
|
|
18
|
-
arch_ops_server-3.
|
|
16
|
+
arch_ops_server-3.3.0.dist-info/WHEEL,sha256=fAguSjoiATBe7TNBkJwOjyL1Tt4wwiaQGtNtjRPNMQA,80
|
|
17
|
+
arch_ops_server-3.3.0.dist-info/entry_points.txt,sha256=ZS2crFEqE9TteC4j2HmYS1wKvoBOCCXT2FJXJW5C4-E,117
|
|
18
|
+
arch_ops_server-3.3.0.dist-info/METADATA,sha256=TTHK9ymnrPpDClCfPJ7krmYCFbVflLKp595jBiKbjpk,11356
|
|
19
|
+
arch_ops_server-3.3.0.dist-info/RECORD,,
|
|
File without changes
|