arch-ops-server 0.1.1__tar.gz → 0.1.2__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: arch-ops-server
3
- Version: 0.1.1
3
+ Version: 0.1.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
@@ -26,6 +26,8 @@ Description-Content-Type: text/markdown
26
26
 
27
27
  # Arch Linux MCP Server
28
28
 
29
+
30
+
29
31
  **Disclaimer:** Unofficial community project, not affiliated with Arch Linux.
30
32
 
31
33
  A [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server that bridges AI assistants with the Arch Linux ecosystem. Enables intelligent, safe, and efficient access to the Arch Wiki, AUR, and official repositories for AI-assisted Arch Linux usage on Arch and non-Arch systems.
@@ -36,6 +38,20 @@ Leverage AI to get output for digestible, structured results that are ready for
36
38
 
37
39
  ## Sneak Peak into what's available
38
40
 
41
+ <details open>
42
+ <summary>Claude Desktop (no terminal)</summary>
43
+
44
+ ![Claude Desktop Demo](assets/claudedesktop_signalcli.gif)
45
+
46
+ </details>
47
+
48
+ <details>
49
+ <summary>VS Code (with terminal)</summary>
50
+
51
+ ![VS Code Demo](assets/vscode_notesnook.gif)
52
+
53
+ </details>
54
+
39
55
  ### Resources (URI-based Access)
40
56
 
41
57
  Direct access to Arch ecosystem data via custom URI schemes:
@@ -81,7 +97,6 @@ Direct access to Arch ecosystem data via custom URI schemes:
81
97
  ```bash
82
98
  uvx arch-ops-server
83
99
  ```
84
-
85
100
  ---
86
101
 
87
102
  ## Configuration
@@ -99,11 +114,16 @@ Claude / Cursor / Any MCP client that supports STDIO transport
99
114
  }
100
115
  ```
101
116
 
102
- ## License
117
+ If you are using Cursor, you can easily install and configure the MCP server by clicking the button below:
103
118
 
104
- [GPL-3.0-only](https://www.gnu.org/licenses/gpl-3.0.en.html)
119
+ [![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](cursor://anysphere.cursor-deeplink/mcp/install?name=arch-ops&config=eyJjb21tYW5kIjogInV2eCIsICJhcmdzIjogWyJhcmNoLW9wcy1zZXJ2ZXIiXX0=)
105
120
 
106
- ---
121
+ ## Contributing
107
122
 
123
+ Contibutions are greatly appreciated. Please feel free to submit a pull request or open an issue and help make things better for everyone.
124
+
125
+ [Contributing Guide](https://nxk.mintlify.app/arch-mcp/contributing)
126
+
127
+ ## License
108
128
 
109
- Built with ❤️ for the Arch Linux community
129
+ [GPL-3.0-only](https://www.gnu.org/licenses/gpl-3.0.en.html)
@@ -1,5 +1,7 @@
1
1
  # Arch Linux MCP Server
2
2
 
3
+
4
+
3
5
  **Disclaimer:** Unofficial community project, not affiliated with Arch Linux.
4
6
 
5
7
  A [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server that bridges AI assistants with the Arch Linux ecosystem. Enables intelligent, safe, and efficient access to the Arch Wiki, AUR, and official repositories for AI-assisted Arch Linux usage on Arch and non-Arch systems.
@@ -10,6 +12,20 @@ Leverage AI to get output for digestible, structured results that are ready for
10
12
 
11
13
  ## Sneak Peak into what's available
12
14
 
15
+ <details open>
16
+ <summary>Claude Desktop (no terminal)</summary>
17
+
18
+ ![Claude Desktop Demo](assets/claudedesktop_signalcli.gif)
19
+
20
+ </details>
21
+
22
+ <details>
23
+ <summary>VS Code (with terminal)</summary>
24
+
25
+ ![VS Code Demo](assets/vscode_notesnook.gif)
26
+
27
+ </details>
28
+
13
29
  ### Resources (URI-based Access)
14
30
 
15
31
  Direct access to Arch ecosystem data via custom URI schemes:
@@ -55,7 +71,6 @@ Direct access to Arch ecosystem data via custom URI schemes:
55
71
  ```bash
56
72
  uvx arch-ops-server
57
73
  ```
58
-
59
74
  ---
60
75
 
61
76
  ## Configuration
@@ -73,11 +88,16 @@ Claude / Cursor / Any MCP client that supports STDIO transport
73
88
  }
74
89
  ```
75
90
 
76
- ## License
91
+ If you are using Cursor, you can easily install and configure the MCP server by clicking the button below:
77
92
 
78
- [GPL-3.0-only](https://www.gnu.org/licenses/gpl-3.0.en.html)
93
+ [![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](cursor://anysphere.cursor-deeplink/mcp/install?name=arch-ops&config=eyJjb21tYW5kIjogInV2eCIsICJhcmdzIjogWyJhcmNoLW9wcy1zZXJ2ZXIiXX0=)
79
94
 
80
- ---
95
+ ## Contributing
81
96
 
97
+ Contibutions are greatly appreciated. Please feel free to submit a pull request or open an issue and help make things better for everyone.
98
+
99
+ [Contributing Guide](https://nxk.mintlify.app/arch-mcp/contributing)
100
+
101
+ ## License
82
102
 
83
- Built with ❤️ for the Arch Linux community
103
+ [GPL-3.0-only](https://www.gnu.org/licenses/gpl-3.0.en.html)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "arch-ops-server"
3
- version = "0.1.1"
3
+ version = "0.1.2"
4
4
  description = "MCP server bridging AI assistants with Arch Linux ecosystem (Wiki, AUR, official repos)"
5
5
  readme = {file = "README.md", content-type = "text/markdown"}
6
6
  license = { text = "GPL-3.0" }
@@ -679,6 +679,41 @@ async def install_package_secure(package_name: str) -> Dict[str, Any]:
679
679
  "messages": []
680
680
  }
681
681
 
682
+ # ========================================================================
683
+ # STEP 0: Verify sudo is configured properly
684
+ # ========================================================================
685
+ logger.info("[STEP 0/5] Verifying sudo configuration...")
686
+
687
+ # Test if sudo password is cached or passwordless sudo is configured
688
+ # Use skip_sudo_check=True to avoid recursive check
689
+ test_exit_code, _, test_stderr = await run_command(
690
+ ["sudo", "-n", "true"],
691
+ timeout=5,
692
+ check=False,
693
+ skip_sudo_check=True
694
+ )
695
+
696
+ if test_exit_code != 0:
697
+ result["messages"].append("⚠️ SUDO PASSWORD REQUIRED")
698
+ result["messages"].append("")
699
+ result["messages"].append("Package installation requires sudo privileges.")
700
+ result["messages"].append("Please choose one of these options:")
701
+ result["messages"].append("")
702
+ result["messages"].append("Option 1: Configure passwordless sudo for pacman:")
703
+ result["messages"].append(" sudo visudo -f /etc/sudoers.d/arch-package-install")
704
+ result["messages"].append(" Add: your_username ALL=(ALL) NOPASSWD: /usr/bin/pacman")
705
+ result["messages"].append("")
706
+ result["messages"].append("Option 2: Cache sudo password temporarily:")
707
+ result["messages"].append(" Run: sudo -v")
708
+ result["messages"].append(" Then retry the installation")
709
+ result["messages"].append("")
710
+ result["messages"].append("Option 3: Install manually in terminal:")
711
+ result["messages"].append(f" sudo pacman -S {package_name}")
712
+ result["security_checks"]["decision"] = "SUDO_REQUIRED"
713
+ return result
714
+
715
+ result["messages"].append("✅ Sudo privileges verified")
716
+
682
717
  # ========================================================================
683
718
  # STEP 1: Check if package is in official repos first
684
719
  # ========================================================================
@@ -714,6 +749,17 @@ async def install_package_secure(package_name: str) -> Dict[str, Any]:
714
749
  result["messages"].append(f"❌ Installation failed: {stderr}")
715
750
  logger.error(f"pacman installation failed: {stderr}")
716
751
 
752
+ # Check for sudo password issues
753
+ if "password" in stderr.lower() or "sudo" in stderr.lower():
754
+ result["messages"].append("")
755
+ result["messages"].append("⚠️ SUDO PASSWORD REQUIRED")
756
+ result["messages"].append("To enable passwordless installation, run one of these commands:")
757
+ result["messages"].append("1. For passwordless sudo (less secure):")
758
+ result["messages"].append(" sudo visudo -f /etc/sudoers.d/arch-package-install")
759
+ result["messages"].append(" Add: your_username ALL=(ALL) NOPASSWD: /usr/bin/pacman")
760
+ result["messages"].append("2. Or run the installation manually in your terminal:")
761
+ result["messages"].append(f" sudo pacman -S {package_name}")
762
+
717
763
  result["install_output"] = stdout
718
764
  result["install_errors"] = stderr
719
765
 
@@ -846,6 +892,17 @@ async def install_package_secure(package_name: str) -> Dict[str, Any]:
846
892
  result["messages"].append(f" Error: {stderr}")
847
893
  result["security_checks"]["decision"] = "INSTALL_FAILED"
848
894
  logger.error(f"AUR installation failed for {package_name}: {stderr}")
895
+
896
+ # Check for sudo password issues
897
+ if "password" in stderr.lower() or "sudo" in stderr.lower():
898
+ result["messages"].append("")
899
+ result["messages"].append("⚠️ SUDO PASSWORD REQUIRED")
900
+ result["messages"].append("To enable passwordless installation for AUR packages:")
901
+ result["messages"].append("1. For passwordless sudo for pacman:")
902
+ result["messages"].append(" sudo visudo -f /etc/sudoers.d/arch-aur-install")
903
+ result["messages"].append(" Add: your_username ALL=(ALL) NOPASSWD: /usr/bin/pacman")
904
+ result["messages"].append("2. Or run the installation manually in your terminal:")
905
+ result["messages"].append(f" {aur_helper} -S {package_name}")
849
906
 
850
907
  result["install_output"] = stdout
851
908
  result["install_errors"] = stderr
@@ -381,7 +381,7 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
381
381
 
382
382
  elif name == "analyze_package_metadata_risk":
383
383
  package_info = arguments["package_info"]
384
- result = await analyze_package_metadata_risk(package_info)
384
+ result = analyze_package_metadata_risk(package_info)
385
385
  return [TextContent(type="text", text=json.dumps(result, indent=2))]
386
386
 
387
387
  else:
@@ -513,7 +513,7 @@ async def get_prompt(name: str, arguments: dict[str, str]) -> GetPromptResult:
513
513
  pkgbuild_content = await get_pkgbuild(package_name)
514
514
 
515
515
  # Analyze both metadata and PKGBUILD
516
- metadata_risk = await analyze_package_metadata_risk(package_info)
516
+ metadata_risk = analyze_package_metadata_risk(package_info)
517
517
  pkgbuild_safety = analyze_pkgbuild_safety(pkgbuild_content)
518
518
 
519
519
  audit_summary = f"""
@@ -57,15 +57,20 @@ IS_ARCH = is_arch_linux()
57
57
  async def run_command(
58
58
  cmd: list[str],
59
59
  timeout: int = 10,
60
- check: bool = True
60
+ check: bool = True,
61
+ skip_sudo_check: bool = False
61
62
  ) -> tuple[int, str, str]:
62
63
  """
63
64
  Execute a command asynchronously with timeout protection.
64
65
 
66
+ Note: For sudo commands, stdin is properly connected to allow password input
67
+ if passwordless sudo is not configured.
68
+
65
69
  Args:
66
70
  cmd: Command and arguments as list
67
71
  timeout: Timeout in seconds (default: 10)
68
72
  check: If True, raise exception on non-zero exit code
73
+ skip_sudo_check: If True, skip the early sudo password check (for testing)
69
74
 
70
75
  Returns:
71
76
  Tuple of (exit_code, stdout, stderr)
@@ -76,21 +81,56 @@ async def run_command(
76
81
  """
77
82
  logger.debug(f"Executing command: {' '.join(cmd)}")
78
83
 
84
+ # Check if this is a sudo command and if password is cached
85
+ is_sudo_command = cmd and cmd[0] == "sudo"
86
+ if is_sudo_command and not skip_sudo_check:
87
+ # Test if sudo password is cached (non-interactive mode)
88
+ test_cmd = ["sudo", "-n", "true"]
89
+ try:
90
+ test_process = await asyncio.create_subprocess_exec(
91
+ *test_cmd,
92
+ stdout=asyncio.subprocess.PIPE,
93
+ stderr=asyncio.subprocess.PIPE
94
+ )
95
+ await test_process.communicate()
96
+ password_cached = test_process.returncode == 0
97
+ logger.debug(f"Sudo password cached: {password_cached}")
98
+
99
+ if not password_cached:
100
+ logger.warning("Sudo password is required but not cached. "
101
+ "Please run 'sudo pacman -S <package>' manually in the terminal.")
102
+ return (
103
+ 1,
104
+ "",
105
+ "Sudo password required. Please configure passwordless sudo for pacman/paru, "
106
+ "or run the installation command manually in your terminal."
107
+ )
108
+ except Exception as e:
109
+ logger.warning(f"Could not check sudo status: {e}")
110
+ password_cached = False
111
+ else:
112
+ password_cached = True
113
+
79
114
  try:
115
+ # Attach stdin to subprocess for commands that might need input
116
+ # Use asyncio.subprocess.PIPE to allow stdin interaction
80
117
  process = await asyncio.create_subprocess_exec(
81
118
  *cmd,
82
119
  stdout=asyncio.subprocess.PIPE,
83
- stderr=asyncio.subprocess.PIPE
120
+ stderr=asyncio.subprocess.PIPE,
121
+ stdin=asyncio.subprocess.PIPE if is_sudo_command else None
84
122
  )
85
123
 
124
+ # Communicate with the process
125
+ # For sudo commands, this allows password input if needed
86
126
  stdout, stderr = await asyncio.wait_for(
87
127
  process.communicate(),
88
128
  timeout=timeout
89
129
  )
90
130
 
91
131
  exit_code = process.returncode
92
- stdout_str = stdout.decode('utf-8', errors='replace')
93
- stderr_str = stderr.decode('utf-8', errors='replace')
132
+ stdout_str = stdout.decode('utf-8', errors='replace') if stdout else ""
133
+ stderr_str = stderr.decode('utf-8', errors='replace') if stderr else ""
94
134
 
95
135
  logger.debug(f"Command exit code: {exit_code}")
96
136