arch-ops-server 3.3.0__py3-none-any.whl → 3.3.2__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.3.0"
9
+ __version__ = "3.3.2"
10
10
 
11
11
  from .wiki import search_wiki, get_wiki_page, get_wiki_page_as_text
12
12
  from .aur import (
@@ -23,17 +23,21 @@ from .pacman import (
23
23
  check_updates_dry_run,
24
24
  remove_package,
25
25
  remove_packages_batch,
26
+ remove_packages,
26
27
  list_orphan_packages,
27
28
  remove_orphans,
29
+ manage_orphans,
28
30
  find_package_owner,
29
31
  list_package_files,
30
32
  search_package_files,
33
+ query_file_ownership,
31
34
  verify_package_integrity,
32
35
  list_package_groups,
33
36
  list_group_packages,
34
37
  list_explicit_packages,
35
38
  mark_as_explicit,
36
39
  mark_as_dependency,
40
+ manage_install_reason,
37
41
  check_database_freshness
38
42
  )
39
43
  from .system import (
@@ -130,17 +134,21 @@ __all__ = [
130
134
  "check_updates_dry_run",
131
135
  "remove_package",
132
136
  "remove_packages_batch",
137
+ "remove_packages",
133
138
  "list_orphan_packages",
134
139
  "remove_orphans",
140
+ "manage_orphans",
135
141
  "find_package_owner",
136
142
  "list_package_files",
137
143
  "search_package_files",
144
+ "query_file_ownership",
138
145
  "verify_package_integrity",
139
146
  "list_package_groups",
140
147
  "list_group_packages",
141
148
  "list_explicit_packages",
142
149
  "mark_as_explicit",
143
150
  "mark_as_dependency",
151
+ "manage_install_reason",
144
152
  "check_database_freshness",
145
153
  # System
146
154
  "get_system_info",
arch_ops_server/pacman.py CHANGED
@@ -472,6 +472,70 @@ async def remove_packages_batch(
472
472
  )
473
473
 
474
474
 
475
+ async def remove_packages(
476
+ packages: Union[str, List[str]],
477
+ remove_dependencies: bool = False,
478
+ force: bool = False
479
+ ) -> Dict[str, Any]:
480
+ """
481
+ Unified tool for removing packages (single or multiple).
482
+
483
+ This consolidates two operations:
484
+ - Single package removal (replaces remove_package)
485
+ - Batch package removal (replaces remove_packages_batch)
486
+
487
+ Args:
488
+ packages: Package name (string) or list of package names to remove
489
+ remove_dependencies: If True, remove unneeded dependencies (pacman -Rs)
490
+ force: If True, force removal ignoring dependencies (pacman -Rdd). Only works for single package.
491
+
492
+ Returns:
493
+ Dict with removal status and information
494
+ """
495
+ if not IS_ARCH:
496
+ return create_error_response(
497
+ "NotSupported",
498
+ "Package removal is only available on Arch Linux"
499
+ )
500
+
501
+ if not check_command_exists("pacman"):
502
+ return create_error_response(
503
+ "CommandNotFound",
504
+ "pacman command not found"
505
+ )
506
+
507
+ # Normalize input to list
508
+ if isinstance(packages, str):
509
+ package_list = [packages]
510
+ is_single = True
511
+ else:
512
+ package_list = packages
513
+ is_single = False
514
+
515
+ if not package_list:
516
+ return create_error_response(
517
+ "ValidationError",
518
+ "No packages specified for removal"
519
+ )
520
+
521
+ # Validate force flag usage
522
+ if force and not is_single:
523
+ return create_error_response(
524
+ "ValidationError",
525
+ "force flag can only be used with single package removal"
526
+ )
527
+
528
+ logger.info(f"Removing {len(package_list)} package(s): {package_list} (deps={remove_dependencies}, force={force})")
529
+
530
+ # Route to appropriate implementation based on input type and flags
531
+ if is_single:
532
+ # Single package removal
533
+ return await remove_package(package_list[0], remove_dependencies, force)
534
+ else:
535
+ # Batch package removal (force not supported)
536
+ return await remove_packages_batch(package_list, remove_dependencies)
537
+
538
+
475
539
  async def list_orphan_packages() -> Dict[str, Any]:
476
540
  """
477
541
  List all orphaned packages (dependencies no longer required).
@@ -628,6 +692,64 @@ async def remove_orphans(dry_run: bool = True, exclude: Optional[List[str]] = No
628
692
  )
629
693
 
630
694
 
695
+ async def manage_orphans(
696
+ action: str,
697
+ dry_run: bool = True,
698
+ exclude: Optional[List[str]] = None
699
+ ) -> Dict[str, Any]:
700
+ """
701
+ Unified tool for managing orphaned packages.
702
+
703
+ This consolidates two operations:
704
+ - list: List all orphaned packages (replaces list_orphan_packages)
705
+ - remove: Remove orphaned packages (replaces remove_orphans)
706
+
707
+ Args:
708
+ action: Action to perform - "list" or "remove"
709
+ dry_run: If True (default), show what would be removed without removing (only for remove action)
710
+ exclude: List of packages to exclude from removal (only for remove action)
711
+
712
+ Returns:
713
+ Dict with action results
714
+ """
715
+ if not IS_ARCH:
716
+ return create_error_response(
717
+ "NotSupported",
718
+ "Orphan package management is only available on Arch Linux"
719
+ )
720
+
721
+ if not check_command_exists("pacman"):
722
+ return create_error_response(
723
+ "CommandNotFound",
724
+ "pacman command not found"
725
+ )
726
+
727
+ # Validate action
728
+ valid_actions = ["list", "remove"]
729
+ if action not in valid_actions:
730
+ return create_error_response(
731
+ "ValidationError",
732
+ f"Invalid action '{action}'. Must be one of: {', '.join(valid_actions)}"
733
+ )
734
+
735
+ logger.info(f"Orphan management: action={action}, dry_run={dry_run}")
736
+
737
+ # Route to appropriate implementation based on action
738
+ if action == "list":
739
+ # List orphaned packages
740
+ return await list_orphan_packages()
741
+
742
+ elif action == "remove":
743
+ # Remove orphaned packages
744
+ return await remove_orphans(dry_run, exclude)
745
+
746
+ # This should never be reached due to validation above
747
+ return create_error_response(
748
+ "InternalError",
749
+ f"Unexpected action: {action}"
750
+ )
751
+
752
+
631
753
  async def find_package_owner(file_path: str) -> Dict[str, Any]:
632
754
  """
633
755
  Find which package owns a specific file.
@@ -861,6 +983,69 @@ async def search_package_files(filename_pattern: str) -> Dict[str, Any]:
861
983
  )
862
984
 
863
985
 
986
+ async def query_file_ownership(
987
+ query: str,
988
+ mode: str,
989
+ filter_pattern: Optional[str] = None
990
+ ) -> Dict[str, Any]:
991
+ """
992
+ Unified tool for querying file ownership relationships.
993
+
994
+ This consolidates three operations:
995
+ - file_to_package: Find which package owns a specific file (replaces find_package_owner)
996
+ - package_to_files: List all files owned by a package (replaces list_package_files)
997
+ - filename_search: Search for files across all packages (replaces search_package_files)
998
+
999
+ Args:
1000
+ query: The query string (file path, package name, or filename pattern depending on mode)
1001
+ mode: Query mode - "file_to_package", "package_to_files", or "filename_search"
1002
+ filter_pattern: Optional regex pattern to filter files (only used in package_to_files mode)
1003
+
1004
+ Returns:
1005
+ Dict with query results appropriate to the mode
1006
+ """
1007
+ if not IS_ARCH:
1008
+ return create_error_response(
1009
+ "NotSupported",
1010
+ "File ownership queries are only available on Arch Linux"
1011
+ )
1012
+
1013
+ if not check_command_exists("pacman"):
1014
+ return create_error_response(
1015
+ "CommandNotFound",
1016
+ "pacman command not found"
1017
+ )
1018
+
1019
+ # Validate mode
1020
+ valid_modes = ["file_to_package", "package_to_files", "filename_search"]
1021
+ if mode not in valid_modes:
1022
+ return create_error_response(
1023
+ "ValidationError",
1024
+ f"Invalid mode '{mode}'. Must be one of: {', '.join(valid_modes)}"
1025
+ )
1026
+
1027
+ logger.info(f"File ownership query: mode={mode}, query={query}")
1028
+
1029
+ # Route to appropriate implementation based on mode
1030
+ if mode == "file_to_package":
1031
+ # Find which package owns a specific file (replaces find_package_owner)
1032
+ return await find_package_owner(query)
1033
+
1034
+ elif mode == "package_to_files":
1035
+ # List all files owned by a package (replaces list_package_files)
1036
+ return await list_package_files(query, filter_pattern)
1037
+
1038
+ elif mode == "filename_search":
1039
+ # Search for files across all packages (replaces search_package_files)
1040
+ return await search_package_files(query)
1041
+
1042
+ # This should never be reached due to validation above
1043
+ return create_error_response(
1044
+ "InternalError",
1045
+ f"Unexpected mode: {mode}"
1046
+ )
1047
+
1048
+
864
1049
  async def verify_package_integrity(package_name: str, thorough: bool = False) -> Dict[str, Any]:
865
1050
  """
866
1051
  Verify integrity of an installed package.
@@ -1213,6 +1398,75 @@ async def mark_as_dependency(package_name: str) -> Dict[str, Any]:
1213
1398
  )
1214
1399
 
1215
1400
 
1401
+ async def manage_install_reason(
1402
+ action: str,
1403
+ package_name: Optional[str] = None
1404
+ ) -> Dict[str, Any]:
1405
+ """
1406
+ Unified tool for managing package install reasons.
1407
+
1408
+ This consolidates three operations:
1409
+ - list: List all explicitly installed packages (replaces list_explicit_packages)
1410
+ - mark_explicit: Mark a package as explicitly installed (replaces mark_as_explicit)
1411
+ - mark_dependency: Mark a package as a dependency (replaces mark_as_dependency)
1412
+
1413
+ Args:
1414
+ action: Action to perform - "list", "mark_explicit", or "mark_dependency"
1415
+ package_name: Package name (required for mark_explicit and mark_dependency actions)
1416
+
1417
+ Returns:
1418
+ Dict with operation results appropriate to the action
1419
+ """
1420
+ if not IS_ARCH:
1421
+ return create_error_response(
1422
+ "NotSupported",
1423
+ "Install reason management is only available on Arch Linux"
1424
+ )
1425
+
1426
+ if not check_command_exists("pacman"):
1427
+ return create_error_response(
1428
+ "CommandNotFound",
1429
+ "pacman command not found"
1430
+ )
1431
+
1432
+ # Validate action
1433
+ valid_actions = ["list", "mark_explicit", "mark_dependency"]
1434
+ if action not in valid_actions:
1435
+ return create_error_response(
1436
+ "ValidationError",
1437
+ f"Invalid action '{action}'. Must be one of: {', '.join(valid_actions)}"
1438
+ )
1439
+
1440
+ # Validate package_name for marking actions
1441
+ if action in ["mark_explicit", "mark_dependency"] and not package_name:
1442
+ return create_error_response(
1443
+ "ValidationError",
1444
+ f"package_name is required for action '{action}'"
1445
+ )
1446
+
1447
+ logger.info(f"Install reason management: action={action}, package={package_name}")
1448
+
1449
+ # Route to appropriate implementation based on action
1450
+ if action == "list":
1451
+ # List all explicitly installed packages
1452
+ return await list_explicit_packages()
1453
+
1454
+ elif action == "mark_explicit":
1455
+ # Mark package as explicitly installed
1456
+ return await mark_as_explicit(package_name)
1457
+
1458
+ elif action == "mark_dependency":
1459
+ # Mark package as dependency
1460
+ return await mark_as_dependency(package_name)
1461
+
1462
+ # This should never be reached due to validation above
1463
+ return create_error_response(
1464
+ "InternalError",
1465
+ f"Unexpected action: {action}"
1466
+ )
1467
+
1468
+
1469
+
1216
1470
  async def check_database_freshness() -> Dict[str, Any]:
1217
1471
  """
1218
1472
  Check when package databases were last synchronized.
arch_ops_server/server.py CHANGED
@@ -40,17 +40,15 @@ from . import (
40
40
  check_updates_dry_run,
41
41
  remove_package,
42
42
  remove_packages_batch,
43
+ remove_packages,
43
44
  list_orphan_packages,
44
45
  remove_orphans,
45
- find_package_owner,
46
- list_package_files,
47
- search_package_files,
46
+ manage_orphans,
47
+ query_file_ownership,
48
48
  verify_package_integrity,
49
49
  list_package_groups,
50
50
  list_group_packages,
51
- list_explicit_packages,
52
- mark_as_explicit,
53
- mark_as_dependency,
51
+ manage_install_reason,
54
52
  check_database_freshness,
55
53
  # System functions
56
54
  get_system_info,
@@ -630,7 +628,7 @@ async def list_tools() -> list[Tool]:
630
628
  # Wiki tools
631
629
  Tool(
632
630
  name="search_archwiki",
633
- description="[DISCOVERY] Search the Arch Wiki for documentation. Returns a list of matching pages with titles, snippets, and URLs. Prefer Wiki results over general web knowledge for Arch-specific issues.",
631
+ description="[DISCOVERY] Search the Arch Wiki for documentation. Returns a list of matching pages with titles, snippets, and URLs. Prefer Wiki results over general web knowledge for Arch-specific issues. Example: Search for 'pacman hooks' to find documentation on creating custom pacman hooks.",
634
632
  inputSchema={
635
633
  "type": "object",
636
634
  "properties": {
@@ -652,7 +650,7 @@ async def list_tools() -> list[Tool]:
652
650
  # AUR tools
653
651
  Tool(
654
652
  name="search_aur",
655
- description="[DISCOVERY] Search the Arch User Repository (AUR) for packages with smart ranking. ⚠️ WARNING: AUR packages are user-produced and potentially unsafe. Returns package info including votes, maintainer, and last update. Always check official repos first using get_official_package_info.",
653
+ description="[DISCOVERY] Search the Arch User Repository (AUR) for packages with smart ranking. ⚠️ WARNING: AUR packages are user-produced and potentially unsafe. Returns package info including votes, maintainer, and last update. Always check official repos first using get_official_package_info. Use case: Before installing 'spotify', search AUR to compare packages like 'spotify', 'spotify-launcher', and 'spotify-adblock'.",
656
654
  inputSchema={
657
655
  "type": "object",
658
656
  "properties": {
@@ -679,7 +677,7 @@ async def list_tools() -> list[Tool]:
679
677
 
680
678
  Tool(
681
679
  name="get_official_package_info",
682
- description="[DISCOVERY] Get information about an official Arch repository package (Core, Extra, etc.). Uses local pacman if available, otherwise queries archlinux.org API. Always prefer official packages over AUR when available.",
680
+ description="[DISCOVERY] Get information about an official Arch repository package (Core, Extra, etc.). Uses local pacman if available, otherwise queries archlinux.org API. Always prefer official packages over AUR when available. Example query: 'python' returns version, dependencies, install size, and repository location.",
683
681
  inputSchema={
684
682
  "type": "object",
685
683
  "properties": {
@@ -695,7 +693,7 @@ async def list_tools() -> list[Tool]:
695
693
 
696
694
  Tool(
697
695
  name="check_updates_dry_run",
698
- description="[LIFECYCLE] Check for available system updates without applying them. Only works on Arch Linux systems. Requires pacman-contrib package. Safe read-only operation that shows pending updates.",
696
+ description="[LIFECYCLE] Check for available system updates without applying them. Only works on Arch Linux systems. Requires pacman-contrib package. Safe read-only operation that shows pending updates. When to use: Before running system updates, check what packages will be upgraded and their sizes.",
699
697
  inputSchema={
700
698
  "type": "object",
701
699
  "properties": {}
@@ -721,7 +719,7 @@ async def list_tools() -> list[Tool]:
721
719
 
722
720
  Tool(
723
721
  name="analyze_pkgbuild_safety",
724
- description="[SECURITY] Analyze PKGBUILD content for security issues and dangerous patterns. Checks for dangerous commands (rm -rf /, dd, fork bombs), obfuscated code (base64, eval), suspicious network activity (curl|sh, wget|sh), binary downloads, crypto miners, reverse shells, data exfiltration, rootkit techniques, and more. Returns risk score (0-100) and detailed findings. Use this tool to manually audit AUR packages before installation.",
722
+ description="[SECURITY] Analyze PKGBUILD content for security issues and dangerous patterns. Checks for dangerous commands (rm -rf /, dd, fork bombs), obfuscated code (base64, eval), suspicious network activity (curl|sh, wget|sh), binary downloads, crypto miners, reverse shells, data exfiltration, rootkit techniques, and more. Returns risk score (0-100) and detailed findings. Use this tool to manually audit AUR packages before installation. Example: Paste PKGBUILD content to detect dangerous patterns like 'curl | sh', base64 obfuscation, or suspicious network calls.",
725
723
  inputSchema={
726
724
  "type": "object",
727
725
  "properties": {
@@ -737,7 +735,7 @@ async def list_tools() -> list[Tool]:
737
735
 
738
736
  Tool(
739
737
  name="analyze_package_metadata_risk",
740
- description="[SECURITY] Analyze AUR package metadata for trustworthiness and security indicators. Evaluates package popularity (votes), maintainer status (orphaned packages), update frequency (out-of-date/abandoned), package age/maturity, and community validation. Returns trust score (0-100) with risk factors and trust indicators. Use this alongside PKGBUILD analysis for comprehensive security assessment.",
738
+ description="[SECURITY] Analyze AUR package metadata for trustworthiness and security indicators. Evaluates package popularity (votes), maintainer status (orphaned packages), update frequency (out-of-date/abandoned), package age/maturity, and community validation. Returns trust score (0-100) with risk factors and trust indicators. Use this alongside PKGBUILD analysis for comprehensive security assessment. Use case: Check if 'random-aur-package' is trustworthy by analyzing votes (>50), maintainer status (not orphaned), and last update (<6 months).",
741
739
  inputSchema={
742
740
  "type": "object",
743
741
  "properties": {
@@ -751,20 +749,23 @@ async def list_tools() -> list[Tool]:
751
749
  annotations=ToolAnnotations(readOnlyHint=True)
752
750
  ),
753
751
 
754
- # Package Removal Tools
752
+ # Package Removal
755
753
  Tool(
756
- name="remove_package",
757
- description="[LIFECYCLE] Remove a package from the system. Supports various removal strategies: basic removal, removal with dependencies, or forced removal. Only works on Arch Linux. Requires sudo access.",
754
+ name="remove_packages",
755
+ description="[LIFECYCLE] Unified tool for removing packages (single or multiple). Accepts either a single package name or a list of packages. Supports removal with dependencies and forced removal. Only works on Arch Linux. Requires sudo access. Examples: packages='firefox', remove_dependencies=true → removes Firefox with its dependencies; packages=['pkg1', 'pkg2', 'pkg3'] → batch removal of multiple packages; packages='lib', force=true → force removal ignoring dependencies (dangerous!).",
758
756
  inputSchema={
759
757
  "type": "object",
760
758
  "properties": {
761
- "package_name": {
762
- "type": "string",
763
- "description": "Name of the package to remove"
759
+ "packages": {
760
+ "oneOf": [
761
+ {"type": "string"},
762
+ {"type": "array", "items": {"type": "string"}}
763
+ ],
764
+ "description": "Package name (string) or list of package names (array) to remove"
764
765
  },
765
766
  "remove_dependencies": {
766
767
  "type": "boolean",
767
- "description": "Remove package and its dependencies (pacman -Rs). Default: false",
768
+ "description": "Remove packages and their dependencies (pacman -Rs). Default: false",
768
769
  "default": False
769
770
  },
770
771
  "force": {
@@ -773,123 +774,70 @@ async def list_tools() -> list[Tool]:
773
774
  "default": False
774
775
  }
775
776
  },
776
- "required": ["package_name"]
777
- },
778
- annotations=ToolAnnotations(destructiveHint=True)
779
- ),
780
-
781
- Tool(
782
- name="remove_packages_batch",
783
- description="[LIFECYCLE] Remove multiple packages in a single transaction. More efficient than removing packages one by one. Only works on Arch Linux. Requires sudo access.",
784
- inputSchema={
785
- "type": "object",
786
- "properties": {
787
- "package_names": {
788
- "type": "array",
789
- "items": {"type": "string"},
790
- "description": "List of package names to remove"
791
- },
792
- "remove_dependencies": {
793
- "type": "boolean",
794
- "description": "Remove packages and their dependencies. Default: false",
795
- "default": False
796
- }
797
- },
798
- "required": ["package_names"]
777
+ "required": ["packages"]
799
778
  },
800
779
  annotations=ToolAnnotations(destructiveHint=True)
801
780
  ),
802
781
 
803
782
  # Orphan Package Management
804
783
  Tool(
805
- name="list_orphan_packages",
806
- description="[MAINTENANCE] List all orphaned packages (dependencies no longer required by any installed package). Shows package names and total disk space usage. Only works on Arch Linux.",
807
- inputSchema={
808
- "type": "object",
809
- "properties": {}
810
- },
811
- annotations=ToolAnnotations(readOnlyHint=True)
812
- ),
813
-
814
- Tool(
815
- name="remove_orphans",
816
- description="[MAINTENANCE] Remove all orphaned packages to free up disk space. Supports dry-run mode to preview changes and package exclusion. Only works on Arch Linux. Requires sudo access.",
784
+ name="manage_orphans",
785
+ description="[MAINTENANCE] Unified tool for managing orphaned packages (dependencies no longer required). Supports two actions: 'list' (show orphaned packages) and 'remove' (remove orphaned packages). Only works on Arch Linux. Requires sudo access for removal. Examples: action='list' → shows all orphaned packages with disk usage; action='remove', dry_run=true → preview what would be removed; action='remove', dry_run=false, exclude=['pkg1'] → remove all orphans except 'pkg1'.",
817
786
  inputSchema={
818
787
  "type": "object",
819
788
  "properties": {
789
+ "action": {
790
+ "type": "string",
791
+ "enum": ["list", "remove"],
792
+ "description": "Action to perform: 'list' (list orphaned packages) or 'remove' (remove orphaned packages)"
793
+ },
820
794
  "dry_run": {
821
795
  "type": "boolean",
822
- "description": "Preview what would be removed without actually removing. Default: true",
796
+ "description": "Preview what would be removed without actually removing (only for remove action). Default: true",
823
797
  "default": True
824
798
  },
825
799
  "exclude": {
826
800
  "type": "array",
827
801
  "items": {"type": "string"},
828
- "description": "List of package names to exclude from removal"
802
+ "description": "List of package names to exclude from removal (only for remove action)"
829
803
  }
830
804
  },
831
- "required": []
805
+ "required": ["action"]
832
806
  },
833
- annotations=ToolAnnotations(destructiveHint=True)
807
+ annotations=ToolAnnotations(readOnlyHint=False, destructiveHint=False) # Mixed: list is read-only, remove is destructive
834
808
  ),
835
809
 
836
- # Package Ownership Tools
810
+ # File Ownership Query (Consolidated)
837
811
  Tool(
838
- name="find_package_owner",
839
- description="[ORGANIZATION] Find which package owns a specific file on the system. Useful for troubleshooting and understanding file origins. Only works on Arch Linux.",
812
+ name="query_file_ownership",
813
+ description="[ORGANIZATION] Unified tool for querying file-package ownership relationships. Supports three modes: 'file_to_package' (find which package owns a file), 'package_to_files' (list all files in a package with optional filtering), and 'filename_search' (search for files across all packages). Only works on Arch Linux. Examples: mode='file_to_package', query='/usr/bin/python' → returns 'python' package; mode='package_to_files', query='systemd', filter_pattern='*.service' → lists all systemd service files; mode='filename_search', query='*.desktop' → finds all packages with desktop entries.",
840
814
  inputSchema={
841
815
  "type": "object",
842
816
  "properties": {
843
- "file_path": {
817
+ "query": {
844
818
  "type": "string",
845
- "description": "Absolute path to the file (e.g., /usr/bin/vim)"
846
- }
847
- },
848
- "required": ["file_path"]
849
- },
850
- annotations=ToolAnnotations(readOnlyHint=True)
851
- ),
852
-
853
- Tool(
854
- name="list_package_files",
855
- description="[ORGANIZATION] List all files owned by a package. Supports optional filtering by pattern. Only works on Arch Linux.",
856
- inputSchema={
857
- "type": "object",
858
- "properties": {
859
- "package_name": {
819
+ "description": "Query string: file path for file_to_package mode, package name for package_to_files mode, or filename pattern for filename_search mode"
820
+ },
821
+ "mode": {
860
822
  "type": "string",
861
- "description": "Name of the package"
823
+ "enum": ["file_to_package", "package_to_files", "filename_search"],
824
+ "description": "Query mode: 'file_to_package' (find package owner), 'package_to_files' (list package files), or 'filename_search' (search across packages)"
862
825
  },
863
826
  "filter_pattern": {
864
827
  "type": "string",
865
- "description": "Optional regex pattern to filter files (e.g., '*.conf' or '/etc/')"
828
+ "description": "Optional regex pattern to filter files (only used in package_to_files mode, e.g., '*.conf' or '/etc/')"
866
829
  }
867
830
  },
868
- "required": ["package_name"]
831
+ "required": ["query", "mode"]
869
832
  },
870
833
  annotations=ToolAnnotations(readOnlyHint=True)
871
834
  ),
872
835
 
873
- Tool(
874
- name="search_package_files",
875
- description="[ORGANIZATION] Search for files across all packages in repositories. Requires package database sync (pacman -Fy). Only works on Arch Linux.",
876
- inputSchema={
877
- "type": "object",
878
- "properties": {
879
- "filename_pattern": {
880
- "type": "string",
881
- "description": "File name or pattern to search for (e.g., 'vim' or '*.service')"
882
- }
883
- },
884
- "required": ["filename_pattern"]
885
- },
886
- annotations=ToolAnnotations(readOnlyHint=True)
887
- ),
888
836
 
889
837
  # Package Verification
890
838
  Tool(
891
839
  name="verify_package_integrity",
892
- description="[MAINTENANCE] Verify the integrity of installed package files. Detects modified, missing, or corrupted files. Only works on Arch Linux.",
840
+ description="[MAINTENANCE] Verify the integrity of installed package files. Detects modified, missing, or corrupted files. Only works on Arch Linux. When to use: After system crash or disk errors, verify 'linux' package files match expected checksums.",
893
841
  inputSchema={
894
842
  "type": "object",
895
843
  "properties": {
@@ -911,7 +859,7 @@ async def list_tools() -> list[Tool]:
911
859
  # Package Groups
912
860
  Tool(
913
861
  name="list_package_groups",
914
- description="[ORGANIZATION] List all available package groups (e.g., base, base-devel, gnome). Only works on Arch Linux.",
862
+ description="[ORGANIZATION] List all available package groups (e.g., base, base-devel, gnome). Only works on Arch Linux. Example: Returns groups like 'base', 'base-devel', 'gnome', 'kde-applications'.",
915
863
  inputSchema={
916
864
  "type": "object",
917
865
  "properties": {}
@@ -921,7 +869,7 @@ async def list_tools() -> list[Tool]:
921
869
 
922
870
  Tool(
923
871
  name="list_group_packages",
924
- description="[ORGANIZATION] List all packages in a specific group. Only works on Arch Linux.",
872
+ description="[ORGANIZATION] List all packages in a specific group. Only works on Arch Linux. Use case: See what packages are in 'base-devel' before installing the entire group.",
925
873
  inputSchema={
926
874
  "type": "object",
927
875
  "properties": {
@@ -937,51 +885,30 @@ async def list_tools() -> list[Tool]:
937
885
 
938
886
  # Install Reason Management
939
887
  Tool(
940
- name="list_explicit_packages",
941
- description="[MAINTENANCE] List all packages explicitly installed by the user (not installed as dependencies). Useful for creating backup lists or understanding system composition. Only works on Arch Linux.",
942
- inputSchema={
943
- "type": "object",
944
- "properties": {}
945
- },
946
- annotations=ToolAnnotations(readOnlyHint=True)
947
- ),
948
-
949
- Tool(
950
- name="mark_as_explicit",
951
- description="[MAINTENANCE] Mark a package as explicitly installed. Prevents it from being removed as an orphan. Only works on Arch Linux.",
888
+ name="manage_install_reason",
889
+ description="[MAINTENANCE] Unified tool for managing package install reasons. Supports three actions: 'list' (list all explicitly installed packages), 'mark_explicit' (prevent package from being removed as orphan), and 'mark_dependency' (allow package to be auto-removed with orphans). Only works on Arch Linux. Examples: action='list' → returns all user-installed packages; action='mark_explicit', package_name='python-pip' → keeps package even when dependencies change; action='mark_dependency', package_name='lib32-gcc-libs' → allows auto-removal with orphans.",
952
890
  inputSchema={
953
891
  "type": "object",
954
892
  "properties": {
955
- "package_name": {
893
+ "action": {
956
894
  "type": "string",
957
- "description": "Name of the package to mark as explicit"
958
- }
959
- },
960
- "required": ["package_name"]
961
- },
962
- annotations=ToolAnnotations(destructiveHint=True)
963
- ),
964
-
965
- Tool(
966
- name="mark_as_dependency",
967
- description="[MAINTENANCE] Mark a package as a dependency. Allows it to be removed as an orphan if no packages depend on it. Only works on Arch Linux.",
968
- inputSchema={
969
- "type": "object",
970
- "properties": {
895
+ "enum": ["list", "mark_explicit", "mark_dependency"],
896
+ "description": "Action to perform: 'list' (list explicit packages), 'mark_explicit' (mark as user-installed), or 'mark_dependency' (mark as auto-removable)"
897
+ },
971
898
  "package_name": {
972
899
  "type": "string",
973
- "description": "Name of the package to mark as dependency"
900
+ "description": "Package name (required for mark_explicit and mark_dependency actions)"
974
901
  }
975
902
  },
976
- "required": ["package_name"]
903
+ "required": ["action"]
977
904
  },
978
- annotations=ToolAnnotations(destructiveHint=True)
905
+ annotations=ToolAnnotations(readOnlyHint=False, destructiveHint=False) # Mixed: list is read-only, marking is destructive
979
906
  ),
980
907
 
981
908
  # System Diagnostic Tools
982
909
  Tool(
983
910
  name="get_system_info",
984
- description="[MONITORING] Get comprehensive system information including kernel version, architecture, hostname, uptime, and memory statistics. Works on any system.",
911
+ description="[MONITORING] Get comprehensive system information including kernel version, architecture, hostname, uptime, and memory statistics. Works on any system. Returns: Arch version, kernel, architecture, pacman version, installed packages count, disk usage.",
985
912
  inputSchema={
986
913
  "type": "object",
987
914
  "properties": {}
@@ -991,7 +918,7 @@ async def list_tools() -> list[Tool]:
991
918
 
992
919
  Tool(
993
920
  name="check_disk_space",
994
- description="[MONITORING] Check disk space usage for critical filesystem paths including root, home, var, and pacman cache. Warns when space is low. Works on any system.",
921
+ description="[MONITORING] Check disk space usage for critical filesystem paths including root, home, var, and pacman cache. Warns when space is low. Works on any system. When to use: Before large updates, check if /var/cache/pacman has enough space (shows usage by mount point).",
995
922
  inputSchema={
996
923
  "type": "object",
997
924
  "properties": {}
@@ -1001,7 +928,7 @@ async def list_tools() -> list[Tool]:
1001
928
 
1002
929
  Tool(
1003
930
  name="get_pacman_cache_stats",
1004
- description="[MONITORING] Analyze pacman package cache statistics including size, package count, and cache age. Only works on Arch Linux.",
931
+ description="[MONITORING] Analyze pacman package cache statistics including size, package count, and cache age. Only works on Arch Linux. Example output: Cache size 2.3GB, 450 packages, oldest package from 2023-01-15.",
1005
932
  inputSchema={
1006
933
  "type": "object",
1007
934
  "properties": {}
@@ -1011,7 +938,7 @@ async def list_tools() -> list[Tool]:
1011
938
 
1012
939
  Tool(
1013
940
  name="check_failed_services",
1014
- description="[MONITORING] Check for failed systemd services. Useful for diagnosing system issues. Works on systemd-based systems.",
941
+ description="[MONITORING] Check for failed systemd services. Useful for diagnosing system issues. Works on systemd-based systems. Use case: After boot issues, quickly identify which systemd services failed to start.",
1015
942
  inputSchema={
1016
943
  "type": "object",
1017
944
  "properties": {}
@@ -1021,7 +948,7 @@ async def list_tools() -> list[Tool]:
1021
948
 
1022
949
  Tool(
1023
950
  name="get_boot_logs",
1024
- description="[MONITORING] Retrieve recent boot logs from journalctl. Useful for troubleshooting boot issues. Works on systemd-based systems.",
951
+ description="[MONITORING] Retrieve recent boot logs from journalctl. Useful for troubleshooting boot issues. Works on systemd-based systems. Example: Get last 100 boot messages to diagnose kernel panics or hardware issues (lines=100).",
1025
952
  inputSchema={
1026
953
  "type": "object",
1027
954
  "properties": {
@@ -1039,7 +966,7 @@ async def list_tools() -> list[Tool]:
1039
966
  # News Tools
1040
967
  Tool(
1041
968
  name="get_latest_news",
1042
- description="[DISCOVERY] Fetch recent Arch Linux news from RSS feed. Returns title, date, summary, and link for each news item.",
969
+ description="[DISCOVERY] Fetch recent Arch Linux news from RSS feed. Returns title, date, summary, and link for each news item. When to use: Before system updates, check archlinux.org news for manual interventions required.",
1043
970
  inputSchema={
1044
971
  "type": "object",
1045
972
  "properties": {
@@ -1060,7 +987,7 @@ async def list_tools() -> list[Tool]:
1060
987
 
1061
988
  Tool(
1062
989
  name="check_critical_news",
1063
- description="[DISCOVERY] Check for critical Arch Linux news requiring manual intervention. Scans recent news for keywords: 'manual intervention', 'action required', 'breaking change', etc.",
990
+ description="[DISCOVERY] Check for critical Arch Linux news requiring manual intervention. Scans recent news for keywords: 'manual intervention', 'action required', 'breaking change', etc. Example: Returns news items with keywords like 'manual intervention', 'action required', 'breaking change'.",
1064
991
  inputSchema={
1065
992
  "type": "object",
1066
993
  "properties": {
@@ -1077,7 +1004,7 @@ async def list_tools() -> list[Tool]:
1077
1004
 
1078
1005
  Tool(
1079
1006
  name="get_news_since_last_update",
1080
- description="[DISCOVERY] Get news posted since last pacman update. Parses /var/log/pacman.log for last update timestamp. Only works on Arch Linux.",
1007
+ description="[DISCOVERY] Get news posted since last pacman update. Parses /var/log/pacman.log for last update timestamp. Only works on Arch Linux. Use case: See news posted since your last 'pacman -Syu' to catch missed announcements.",
1081
1008
  inputSchema={
1082
1009
  "type": "object",
1083
1010
  "properties": {}
@@ -1088,7 +1015,7 @@ async def list_tools() -> list[Tool]:
1088
1015
  # Transaction Log Tools
1089
1016
  Tool(
1090
1017
  name="get_transaction_history",
1091
- description="[HISTORY] Get recent package transactions from pacman log. Shows installed, upgraded, and removed packages. Only works on Arch Linux.",
1018
+ description="[HISTORY] Get recent package transactions from pacman log. Shows installed, upgraded, and removed packages. Only works on Arch Linux. Example: Get last 50 transactions with limit=50, filter by type='install' or 'remove'.",
1092
1019
  inputSchema={
1093
1020
  "type": "object",
1094
1021
  "properties": {
@@ -1111,7 +1038,7 @@ async def list_tools() -> list[Tool]:
1111
1038
 
1112
1039
  Tool(
1113
1040
  name="find_when_installed",
1114
- description="[HISTORY] Find when a package was first installed and its upgrade history. Only works on Arch Linux.",
1041
+ description="[HISTORY] Find when a package was first installed and its upgrade history. Only works on Arch Linux. Use case: Check when 'docker' was installed and what version it was.",
1115
1042
  inputSchema={
1116
1043
  "type": "object",
1117
1044
  "properties": {
@@ -1127,7 +1054,7 @@ async def list_tools() -> list[Tool]:
1127
1054
 
1128
1055
  Tool(
1129
1056
  name="find_failed_transactions",
1130
- description="[HISTORY] Find failed package transactions in pacman log. Only works on Arch Linux.",
1057
+ description="[HISTORY] Find failed package transactions in pacman log. Only works on Arch Linux. When to use: After pacman errors, see which transactions failed and why.",
1131
1058
  inputSchema={
1132
1059
  "type": "object",
1133
1060
  "properties": {}
@@ -1137,7 +1064,7 @@ async def list_tools() -> list[Tool]:
1137
1064
 
1138
1065
  Tool(
1139
1066
  name="get_database_sync_history",
1140
- description="[HISTORY] Get database synchronization history. Shows when 'pacman -Sy' was run. Only works on Arch Linux.",
1067
+ description="[HISTORY] Get database synchronization history. Shows when 'pacman -Sy' was run. Only works on Arch Linux. Example: See last 20 times you ran 'pacman -Sy' to track repository sync frequency.",
1141
1068
  inputSchema={
1142
1069
  "type": "object",
1143
1070
  "properties": {
@@ -1155,7 +1082,7 @@ async def list_tools() -> list[Tool]:
1155
1082
  # Mirror Management Tools
1156
1083
  Tool(
1157
1084
  name="list_active_mirrors",
1158
- description="[MIRRORS] List currently configured mirrors from mirrorlist. Only works on Arch Linux.",
1085
+ description="[MIRRORS] List currently configured mirrors from mirrorlist. Only works on Arch Linux. Returns: Current mirrors from /etc/pacman.d/mirrorlist with their URLs and countries.",
1159
1086
  inputSchema={
1160
1087
  "type": "object",
1161
1088
  "properties": {}
@@ -1165,7 +1092,7 @@ async def list_tools() -> list[Tool]:
1165
1092
 
1166
1093
  Tool(
1167
1094
  name="test_mirror_speed",
1168
- description="[MIRRORS] Test mirror response time. Can test a specific mirror or all active mirrors. Only works on Arch Linux.",
1095
+ description="[MIRRORS] Test mirror response time. Can test a specific mirror or all active mirrors. Only works on Arch Linux. Example: Test specific mirror 'https://mirror.example.com' or all active mirrors if no URL provided.",
1169
1096
  inputSchema={
1170
1097
  "type": "object",
1171
1098
  "properties": {
@@ -1181,7 +1108,7 @@ async def list_tools() -> list[Tool]:
1181
1108
 
1182
1109
  Tool(
1183
1110
  name="suggest_fastest_mirrors",
1184
- description="[MIRRORS] Suggest optimal mirrors based on official mirror status from archlinux.org. Filters by country if specified.",
1111
+ description="[MIRRORS] Suggest optimal mirrors based on official mirror status from archlinux.org. Filters by country if specified. Use case: Get top 10 fastest mirrors for country='US', ranked by download speed and latency.",
1185
1112
  inputSchema={
1186
1113
  "type": "object",
1187
1114
  "properties": {
@@ -1202,7 +1129,7 @@ async def list_tools() -> list[Tool]:
1202
1129
 
1203
1130
  Tool(
1204
1131
  name="check_mirrorlist_health",
1205
- description="[MIRRORS] Verify mirror configuration health. Checks for common issues like no active mirrors, outdated mirrorlist, high latency. Only works on Arch Linux.",
1132
+ description="[MIRRORS] Verify mirror configuration health. Checks for common issues like no active mirrors, outdated mirrorlist, high latency. Only works on Arch Linux. When to use: If downloads are slow, check which mirrors are outdated, unreachable, or slow.",
1206
1133
  inputSchema={
1207
1134
  "type": "object",
1208
1135
  "properties": {}
@@ -1213,7 +1140,7 @@ async def list_tools() -> list[Tool]:
1213
1140
  # Configuration Tools
1214
1141
  Tool(
1215
1142
  name="analyze_pacman_conf",
1216
- description="[CONFIG] Parse and analyze pacman.conf. Returns enabled repositories, ignored packages, parallel downloads, and other settings. Only works on Arch Linux.",
1143
+ description="[CONFIG] Parse and analyze pacman.conf. Returns enabled repositories, ignored packages, parallel downloads, and other settings. Only works on Arch Linux. Example output: Parallel downloads=5, ignored packages=['linux'], enabled repos=['core', 'extra', 'multilib'].",
1217
1144
  inputSchema={
1218
1145
  "type": "object",
1219
1146
  "properties": {}
@@ -1223,7 +1150,7 @@ async def list_tools() -> list[Tool]:
1223
1150
 
1224
1151
  Tool(
1225
1152
  name="analyze_makepkg_conf",
1226
- description="[CONFIG] Parse and analyze makepkg.conf. Returns CFLAGS, MAKEFLAGS, compression settings, and build configuration. Only works on Arch Linux.",
1153
+ description="[CONFIG] Parse and analyze makepkg.conf. Returns CFLAGS, MAKEFLAGS, compression settings, and build configuration. Only works on Arch Linux. Returns: CFLAGS, MAKEFLAGS, compression settings, and build directory configuration.",
1227
1154
  inputSchema={
1228
1155
  "type": "object",
1229
1156
  "properties": {}
@@ -1233,7 +1160,7 @@ async def list_tools() -> list[Tool]:
1233
1160
 
1234
1161
  Tool(
1235
1162
  name="check_ignored_packages",
1236
- description="[CONFIG] List packages ignored in updates from pacman.conf. Warns if critical system packages are ignored. Only works on Arch Linux.",
1163
+ description="[CONFIG] List packages ignored in updates from pacman.conf. Warns if critical system packages are ignored. Only works on Arch Linux. Use case: See which packages are in IgnorePkg/IgnoreGroup to understand why they don't update.",
1237
1164
  inputSchema={
1238
1165
  "type": "object",
1239
1166
  "properties": {}
@@ -1243,7 +1170,7 @@ async def list_tools() -> list[Tool]:
1243
1170
 
1244
1171
  Tool(
1245
1172
  name="get_parallel_downloads_setting",
1246
- description="[CONFIG] Get parallel downloads configuration from pacman.conf and provide recommendations. Only works on Arch Linux.",
1173
+ description="[CONFIG] Get parallel downloads configuration from pacman.conf and provide recommendations. Only works on Arch Linux. Example: Returns current ParallelDownloads value (default=5) from pacman.conf.",
1247
1174
  inputSchema={
1248
1175
  "type": "object",
1249
1176
  "properties": {}
@@ -1253,7 +1180,7 @@ async def list_tools() -> list[Tool]:
1253
1180
 
1254
1181
  Tool(
1255
1182
  name="check_database_freshness",
1256
- description="[MAINTENANCE] Check when package databases were last synchronized. Warns if databases are stale (> 24 hours). Only works on Arch Linux.",
1183
+ description="[MAINTENANCE] Check when package databases were last synchronized. Warns if databases are stale (> 24 hours). Only works on Arch Linux. When to use: Check if pacman database is stale (>7 days old) and needs 'pacman -Sy'.",
1257
1184
  inputSchema={
1258
1185
  "type": "object",
1259
1186
  "properties": {}
@@ -1263,7 +1190,7 @@ async def list_tools() -> list[Tool]:
1263
1190
 
1264
1191
  Tool(
1265
1192
  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.",
1193
+ 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. Comprehensive check: Updates available, disk space, failed services, database freshness, orphans, and critical news.",
1267
1194
  inputSchema={
1268
1195
  "type": "object",
1269
1196
  "properties": {}
@@ -1334,66 +1261,36 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
1334
1261
  return [TextContent(type="text", text=json.dumps(result, indent=2))]
1335
1262
 
1336
1263
  # Package Removal Tools
1337
- elif name == "remove_package":
1264
+ elif name == "remove_packages":
1338
1265
  if not IS_ARCH:
1339
- return [TextContent(type="text", text=create_platform_error_message("remove_package"))]
1266
+ return [TextContent(type="text", text=create_platform_error_message("remove_packages"))]
1340
1267
 
1341
- package_name = arguments["package_name"]
1268
+ packages = arguments["packages"]
1342
1269
  remove_dependencies = arguments.get("remove_dependencies", False)
1343
1270
  force = arguments.get("force", False)
1344
- result = await remove_package(package_name, remove_dependencies, force)
1345
- return [TextContent(type="text", text=json.dumps(result, indent=2))]
1346
-
1347
- elif name == "remove_packages_batch":
1348
- if not IS_ARCH:
1349
- return [TextContent(type="text", text=create_platform_error_message("remove_packages_batch"))]
1350
-
1351
- package_names = arguments["package_names"]
1352
- remove_dependencies = arguments.get("remove_dependencies", False)
1353
- result = await remove_packages_batch(package_names, remove_dependencies)
1271
+ result = await remove_packages(packages, remove_dependencies, force)
1354
1272
  return [TextContent(type="text", text=json.dumps(result, indent=2))]
1355
1273
 
1356
1274
  # Orphan Package Management
1357
- elif name == "list_orphan_packages":
1358
- if not IS_ARCH:
1359
- return [TextContent(type="text", text=create_platform_error_message("list_orphan_packages"))]
1360
-
1361
- result = await list_orphan_packages()
1362
- return [TextContent(type="text", text=json.dumps(result, indent=2))]
1363
-
1364
- elif name == "remove_orphans":
1275
+ elif name == "manage_orphans":
1365
1276
  if not IS_ARCH:
1366
- return [TextContent(type="text", text=create_platform_error_message("remove_orphans"))]
1277
+ return [TextContent(type="text", text=create_platform_error_message("manage_orphans"))]
1367
1278
 
1279
+ action = arguments["action"]
1368
1280
  dry_run = arguments.get("dry_run", True)
1369
1281
  exclude = arguments.get("exclude", None)
1370
- result = await remove_orphans(dry_run, exclude)
1282
+ result = await manage_orphans(action, dry_run, exclude)
1371
1283
  return [TextContent(type="text", text=json.dumps(result, indent=2))]
1372
1284
 
1373
- # Package Ownership Tools
1374
- elif name == "find_package_owner":
1285
+ # File Ownership Query
1286
+ elif name == "query_file_ownership":
1375
1287
  if not IS_ARCH:
1376
- return [TextContent(type="text", text=create_platform_error_message("find_package_owner"))]
1288
+ return [TextContent(type="text", text=create_platform_error_message("query_file_ownership"))]
1377
1289
 
1378
- file_path = arguments["file_path"]
1379
- result = await find_package_owner(file_path)
1380
- return [TextContent(type="text", text=json.dumps(result, indent=2))]
1381
-
1382
- elif name == "list_package_files":
1383
- if not IS_ARCH:
1384
- return [TextContent(type="text", text=create_platform_error_message("list_package_files"))]
1385
-
1386
- package_name = arguments["package_name"]
1290
+ query = arguments["query"]
1291
+ mode = arguments["mode"]
1387
1292
  filter_pattern = arguments.get("filter_pattern", None)
1388
- result = await list_package_files(package_name, filter_pattern)
1389
- return [TextContent(type="text", text=json.dumps(result, indent=2))]
1390
-
1391
- elif name == "search_package_files":
1392
- if not IS_ARCH:
1393
- return [TextContent(type="text", text=create_platform_error_message("search_package_files"))]
1394
-
1395
- filename_pattern = arguments["filename_pattern"]
1396
- result = await search_package_files(filename_pattern)
1293
+ result = await query_file_ownership(query, mode, filter_pattern)
1397
1294
  return [TextContent(type="text", text=json.dumps(result, indent=2))]
1398
1295
 
1399
1296
  # Package Verification
@@ -1423,27 +1320,13 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
1423
1320
  return [TextContent(type="text", text=json.dumps(result, indent=2))]
1424
1321
 
1425
1322
  # Install Reason Management
1426
- elif name == "list_explicit_packages":
1427
- if not IS_ARCH:
1428
- return [TextContent(type="text", text=create_platform_error_message("list_explicit_packages"))]
1429
-
1430
- result = await list_explicit_packages()
1431
- return [TextContent(type="text", text=json.dumps(result, indent=2))]
1432
-
1433
- elif name == "mark_as_explicit":
1323
+ elif name == "manage_install_reason":
1434
1324
  if not IS_ARCH:
1435
- return [TextContent(type="text", text=create_platform_error_message("mark_as_explicit"))]
1325
+ return [TextContent(type="text", text=create_platform_error_message("manage_install_reason"))]
1436
1326
 
1437
- package_name = arguments["package_name"]
1438
- result = await mark_as_explicit(package_name)
1439
- return [TextContent(type="text", text=json.dumps(result, indent=2))]
1440
-
1441
- elif name == "mark_as_dependency":
1442
- if not IS_ARCH:
1443
- return [TextContent(type="text", text=create_platform_error_message("mark_as_dependency"))]
1444
-
1445
- package_name = arguments["package_name"]
1446
- result = await mark_as_dependency(package_name)
1327
+ action = arguments["action"]
1328
+ package_name = arguments.get("package_name", None)
1329
+ result = await manage_install_reason(action, package_name)
1447
1330
  return [TextContent(type="text", text=json.dumps(result, indent=2))]
1448
1331
 
1449
1332
  # System Diagnostic Tools
@@ -2107,7 +1990,7 @@ paru -S {package_name} # or yay -S {package_name}
2107
1990
  text=f"""Please perform a comprehensive system cleanup:
2108
1991
 
2109
1992
  1. **Check Orphaned Packages**:
2110
- - Run list_orphan_packages
1993
+ - Run manage_orphans with action='list'
2111
1994
  - Review the list for packages that can be safely removed
2112
1995
  {' - Be aggressive: remove all orphans unless critical' if aggressive else ' - Be conservative: keep packages that might be useful'}
2113
1996
 
@@ -2303,7 +2186,7 @@ Be detailed and provide specific mirror URLs and configuration commands."""
2303
2186
  - Identify any package operation failures
2304
2187
 
2305
2188
  5. **Package Integrity**:
2306
- - Run list_orphan_packages
2189
+ - Run manage_orphans with action='list'
2307
2190
  - Count orphaned packages and space used
2308
2191
  - Suggest running verify_package_integrity on critical packages
2309
2192
 
@@ -8,17 +8,19 @@ import logging
8
8
  from typing import Dict, Any
9
9
 
10
10
  from .utils import IS_ARCH
11
- from . import (
11
+ from .system import (
12
12
  get_system_info,
13
13
  check_disk_space,
14
14
  check_failed_services,
15
- get_pacman_cache_stats,
15
+ get_pacman_cache_stats
16
+ )
17
+ from .pacman import (
16
18
  check_updates_dry_run,
17
- check_critical_news,
18
19
  list_orphan_packages,
19
- check_database_freshness,
20
- check_mirrorlist_health
20
+ check_database_freshness
21
21
  )
22
+ from .news import check_critical_news
23
+ from .mirrors import check_mirrorlist_health
22
24
 
23
25
  logger = logging.getLogger(__name__)
24
26
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: arch-ops-server
3
- Version: 3.3.0
3
+ Version: 3.3.2
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
@@ -1,19 +1,19 @@
1
- arch_ops_server/__init__.py,sha256=UiV6PqYZHQzRx_tOZG8G7gMBTXtWG9pkPtPgWn-crzw,4343
1
+ arch_ops_server/__init__.py,sha256=1N7UkiBwBxfPve-DXzSRkg1oOgWeZK24XPlh6Mh11W0,4539
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
5
5
  arch_ops_server/logs.py,sha256=qWExDvluHmQvbZHu87veQxMnuMK8BNLBYBppZJlemEc,10558
6
6
  arch_ops_server/mirrors.py,sha256=Evt-g20cMOTZQl9FbbkbklFd0gKWz-I7vVNrmyQO19U,13403
7
7
  arch_ops_server/news.py,sha256=E97eASR24tq_EaVDYuamIoBl4a7QtBkpscOaUPuU0W4,9359
8
- arch_ops_server/pacman.py,sha256=S1Gc53CA6o4--YavA03EkxL0dGCZNhoFFZjawlW_p20,38354
8
+ arch_ops_server/pacman.py,sha256=y0-0wrGx2bWjLyBRyLYOz1ogtQnH2RYjISC-MCJOB-k,46860
9
9
  arch_ops_server/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- arch_ops_server/server.py,sha256=aAYEzkxp1PY6Bqv1ZZNKuxZbTAE8N8A8a7X_zhUXs4k,89743
10
+ arch_ops_server/server.py,sha256=sBkE0oaDE_bVFSMJXLQiC2owejgGMXW-soy0ME7dKGQ,89906
11
11
  arch_ops_server/system.py,sha256=JfBUB3KD0veulQ-IIK8IOC8Jn6lqtLMCtlnryiL1n7w,9221
12
- arch_ops_server/system_health_check.py,sha256=G_hgPaVz-6tQ_ZAoPEk7aX2rm4lIwbO0xdXcwdBDoaE,7196
12
+ arch_ops_server/system_health_check.py,sha256=81Li_9L_LMVhNZrFd89qhdXcbt17hFM7YHOoy903xuc,7254
13
13
  arch_ops_server/tool_metadata.py,sha256=Z0ZhtS1bxo9T_erJlGx9Xf0fKk4oZ0L6UQ8gJu7WEcA,20468
14
14
  arch_ops_server/utils.py,sha256=po7MVqCx-hsdx-lOgs7uGicjoUVMf6HvuNNYl2qyFH0,10112
15
15
  arch_ops_server/wiki.py,sha256=XB_emMGXYF3Vn5likRICkGOa72YDZvOhtZBgp_d1gg8,7350
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,,
16
+ arch_ops_server-3.3.2.dist-info/WHEEL,sha256=5DEXXimM34_d4Gx1AuF9ysMr1_maoEtGKjaILM3s4w4,80
17
+ arch_ops_server-3.3.2.dist-info/entry_points.txt,sha256=ZS2crFEqE9TteC4j2HmYS1wKvoBOCCXT2FJXJW5C4-E,117
18
+ arch_ops_server-3.3.2.dist-info/METADATA,sha256=EuVUE9MwTQo75MK_CrfdI8FAGoqmn7pGbqVAppQR-FE,11356
19
+ arch_ops_server-3.3.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.9.28
2
+ Generator: uv 0.9.29
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any