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.
@@ -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.1.0"
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(f"pacman:// resources only available on Arch Linux systems")
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(f"mirrors:// resources only available on Arch Linux systems")
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(f"config:// resources only available on Arch Linux systems")
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="Error: check_updates_dry_run only available on Arch Linux systems")]
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="Error: install_package_secure only available on Arch Linux systems")]
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="Error: remove_package only available on Arch Linux systems")]
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="Error: remove_packages_batch only available on Arch Linux systems")]
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="Error: list_orphan_packages only available on Arch Linux systems")]
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="Error: remove_orphans only available on Arch Linux systems")]
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="Error: find_package_owner only available on Arch Linux systems")]
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="Error: list_package_files only available on Arch Linux systems")]
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="Error: search_package_files only available on Arch Linux systems")]
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="Error: verify_package_integrity only available on Arch Linux systems")]
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="Error: list_package_groups only available on Arch Linux systems")]
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="Error: list_group_packages only available on Arch Linux systems")]
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="Error: list_explicit_packages only available on Arch Linux systems")]
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="Error: mark_as_explicit only available on Arch Linux systems")]
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="Error: mark_as_dependency only available on Arch Linux systems")]
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="Error: get_pacman_cache_stats only available on Arch Linux systems")]
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="Error: get_news_since_last_update only available on Arch Linux systems")]
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="Error: get_transaction_history only available on Arch Linux systems")]
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="Error: find_when_installed only available on Arch Linux systems")]
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="Error: find_failed_transactions only available on Arch Linux systems")]
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="Error: get_database_sync_history only available on Arch Linux systems")]
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="Error: list_active_mirrors only available on Arch Linux systems")]
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="Error: test_mirror_speed only available on Arch Linux systems")]
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="Error: check_mirrorlist_health only available on Arch Linux systems")]
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="Error: analyze_pacman_conf only available on Arch Linux systems")]
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="Error: analyze_makepkg_conf only available on Arch Linux systems")]
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="Error: check_ignored_packages only available on Arch Linux systems")]
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="Error: get_parallel_downloads_setting only available on Arch Linux systems")]
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="Error: check_database_freshness only available on Arch Linux systems")]
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="Error: safe_system_update prompt only available on Arch Linux systems"
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="Error: cleanup_system prompt only available on Arch Linux systems"
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="Error: system_health_check prompt only available on Arch Linux systems"
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
+ }
@@ -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.1.0
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 open>
56
- <summary>Claude Desktop (no terminal)</summary>
55
+ <details>
56
+
57
+ <summary>Using VS Code Sonnet 3.5 for Safe Installation from AUR</summary>
57
58
 
58
- ![Claude Desktop Demo](assets/claudedesktop_signalcli.gif)
59
+ ![VS Code Demo](assets/vscode_notesnook.gif)
59
60
 
60
61
  </details>
61
62
 
62
63
  <details>
63
- <summary>VS Code (with terminal)</summary>
64
+ <summary> Asking Claude Code Sonnet 4.5 for fedora equivalent command </summary>
64
65
 
65
- ![VS Code Demo](assets/vscode_notesnook.gif)
66
+ ![Equivalent Command Demo](assets/equivalent-commands.gif)
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=8UZTb4O3wQD4WGrspE06O9AscuAQi_pEh4VR9y5yM2U,4255
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=u3KIZ1j8PR8l1oF8nL6nIKHIrrxybTUnWHj9T4LL3JQ,84493
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/tool_metadata.py,sha256=C2EQuR6BlzrKNWwdMi3EPexnlKYw18yBNvq5X2JovLU,19875
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.1.0.dist-info/WHEEL,sha256=w4ZtLaDgMAZW2MMZZwtH8zENekoQYBCeullI-zsXJQk,78
16
- arch_ops_server-3.1.0.dist-info/entry_points.txt,sha256=ZS2crFEqE9TteC4j2HmYS1wKvoBOCCXT2FJXJW5C4-E,117
17
- arch_ops_server-3.1.0.dist-info/METADATA,sha256=vbVTjHuSEe3wgMJvPSrjd3agtohpXK2H2_zmJouQxyE,11275
18
- arch_ops_server-3.1.0.dist-info/RECORD,,
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,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.9.9
2
+ Generator: uv 0.9.28
3
3
  Root-Is-Purelib: true
4
- Tag: py3-none-any
4
+ Tag: py3-none-any