arch-ops-server 0.1.1__py3-none-any.whl → 0.1.3__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.
@@ -1,3 +1,4 @@
1
+ # SPDX-License-Identifier: GPL-3.0-only OR MIT
1
2
  """
2
3
  Arch Linux MCP Server
3
4
 
arch_ops_server/aur.py CHANGED
@@ -1,3 +1,4 @@
1
+ # SPDX-License-Identifier: GPL-3.0-only OR MIT
1
2
  """
2
3
  AUR (Arch User Repository) interface module.
3
4
  Provides search, package info, and PKGBUILD retrieval via AUR RPC v5.
@@ -679,6 +680,41 @@ async def install_package_secure(package_name: str) -> Dict[str, Any]:
679
680
  "messages": []
680
681
  }
681
682
 
683
+ # ========================================================================
684
+ # STEP 0: Verify sudo is configured properly
685
+ # ========================================================================
686
+ logger.info("[STEP 0/5] Verifying sudo configuration...")
687
+
688
+ # Test if sudo password is cached or passwordless sudo is configured
689
+ # Use skip_sudo_check=True to avoid recursive check
690
+ test_exit_code, _, test_stderr = await run_command(
691
+ ["sudo", "-n", "true"],
692
+ timeout=5,
693
+ check=False,
694
+ skip_sudo_check=True
695
+ )
696
+
697
+ if test_exit_code != 0:
698
+ result["messages"].append("⚠️ SUDO PASSWORD REQUIRED")
699
+ result["messages"].append("")
700
+ result["messages"].append("Package installation requires sudo privileges.")
701
+ result["messages"].append("Please choose one of these options:")
702
+ result["messages"].append("")
703
+ result["messages"].append("Option 1: Configure passwordless sudo for pacman:")
704
+ result["messages"].append(" sudo visudo -f /etc/sudoers.d/arch-package-install")
705
+ result["messages"].append(" Add: your_username ALL=(ALL) NOPASSWD: /usr/bin/pacman")
706
+ result["messages"].append("")
707
+ result["messages"].append("Option 2: Cache sudo password temporarily:")
708
+ result["messages"].append(" Run: sudo -v")
709
+ result["messages"].append(" Then retry the installation")
710
+ result["messages"].append("")
711
+ result["messages"].append("Option 3: Install manually in terminal:")
712
+ result["messages"].append(f" sudo pacman -S {package_name}")
713
+ result["security_checks"]["decision"] = "SUDO_REQUIRED"
714
+ return result
715
+
716
+ result["messages"].append("✅ Sudo privileges verified")
717
+
682
718
  # ========================================================================
683
719
  # STEP 1: Check if package is in official repos first
684
720
  # ========================================================================
@@ -714,6 +750,17 @@ async def install_package_secure(package_name: str) -> Dict[str, Any]:
714
750
  result["messages"].append(f"❌ Installation failed: {stderr}")
715
751
  logger.error(f"pacman installation failed: {stderr}")
716
752
 
753
+ # Check for sudo password issues
754
+ if "password" in stderr.lower() or "sudo" in stderr.lower():
755
+ result["messages"].append("")
756
+ result["messages"].append("⚠️ SUDO PASSWORD REQUIRED")
757
+ result["messages"].append("To enable passwordless installation, run one of these commands:")
758
+ result["messages"].append("1. For passwordless sudo (less secure):")
759
+ result["messages"].append(" sudo visudo -f /etc/sudoers.d/arch-package-install")
760
+ result["messages"].append(" Add: your_username ALL=(ALL) NOPASSWD: /usr/bin/pacman")
761
+ result["messages"].append("2. Or run the installation manually in your terminal:")
762
+ result["messages"].append(f" sudo pacman -S {package_name}")
763
+
717
764
  result["install_output"] = stdout
718
765
  result["install_errors"] = stderr
719
766
 
@@ -846,6 +893,17 @@ async def install_package_secure(package_name: str) -> Dict[str, Any]:
846
893
  result["messages"].append(f" Error: {stderr}")
847
894
  result["security_checks"]["decision"] = "INSTALL_FAILED"
848
895
  logger.error(f"AUR installation failed for {package_name}: {stderr}")
896
+
897
+ # Check for sudo password issues
898
+ if "password" in stderr.lower() or "sudo" in stderr.lower():
899
+ result["messages"].append("")
900
+ result["messages"].append("⚠️ SUDO PASSWORD REQUIRED")
901
+ result["messages"].append("To enable passwordless installation for AUR packages:")
902
+ result["messages"].append("1. For passwordless sudo for pacman:")
903
+ result["messages"].append(" sudo visudo -f /etc/sudoers.d/arch-aur-install")
904
+ result["messages"].append(" Add: your_username ALL=(ALL) NOPASSWD: /usr/bin/pacman")
905
+ result["messages"].append("2. Or run the installation manually in your terminal:")
906
+ result["messages"].append(f" {aur_helper} -S {package_name}")
849
907
 
850
908
  result["install_output"] = stdout
851
909
  result["install_errors"] = stderr
arch_ops_server/pacman.py CHANGED
@@ -1,3 +1,4 @@
1
+ # SPDX-License-Identifier: GPL-3.0-only OR MIT
1
2
  """
2
3
  Pacman/Official Repository interface module.
3
4
  Provides package info and update checks with hybrid local/remote approach.
arch_ops_server/server.py CHANGED
@@ -1,3 +1,4 @@
1
+ # SPDX-License-Identifier: GPL-3.0-only OR MIT
1
2
  """
2
3
  MCP Server setup for Arch Linux operations.
3
4
 
@@ -381,7 +382,7 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
381
382
 
382
383
  elif name == "analyze_package_metadata_risk":
383
384
  package_info = arguments["package_info"]
384
- result = await analyze_package_metadata_risk(package_info)
385
+ result = analyze_package_metadata_risk(package_info)
385
386
  return [TextContent(type="text", text=json.dumps(result, indent=2))]
386
387
 
387
388
  else:
@@ -513,7 +514,7 @@ async def get_prompt(name: str, arguments: dict[str, str]) -> GetPromptResult:
513
514
  pkgbuild_content = await get_pkgbuild(package_name)
514
515
 
515
516
  # Analyze both metadata and PKGBUILD
516
- metadata_risk = await analyze_package_metadata_risk(package_info)
517
+ metadata_risk = analyze_package_metadata_risk(package_info)
517
518
  pkgbuild_safety = analyze_pkgbuild_safety(pkgbuild_content)
518
519
 
519
520
  audit_summary = f"""
arch_ops_server/utils.py CHANGED
@@ -1,3 +1,4 @@
1
+ # SPDX-License-Identifier: GPL-3.0-only OR MIT
1
2
  """
2
3
  Utility functions for Arch Linux MCP Server.
3
4
  Provides platform detection, subprocess execution, and error handling.
@@ -57,15 +58,20 @@ IS_ARCH = is_arch_linux()
57
58
  async def run_command(
58
59
  cmd: list[str],
59
60
  timeout: int = 10,
60
- check: bool = True
61
+ check: bool = True,
62
+ skip_sudo_check: bool = False
61
63
  ) -> tuple[int, str, str]:
62
64
  """
63
65
  Execute a command asynchronously with timeout protection.
64
66
 
67
+ Note: For sudo commands, stdin is properly connected to allow password input
68
+ if passwordless sudo is not configured.
69
+
65
70
  Args:
66
71
  cmd: Command and arguments as list
67
72
  timeout: Timeout in seconds (default: 10)
68
73
  check: If True, raise exception on non-zero exit code
74
+ skip_sudo_check: If True, skip the early sudo password check (for testing)
69
75
 
70
76
  Returns:
71
77
  Tuple of (exit_code, stdout, stderr)
@@ -76,21 +82,56 @@ async def run_command(
76
82
  """
77
83
  logger.debug(f"Executing command: {' '.join(cmd)}")
78
84
 
85
+ # Check if this is a sudo command and if password is cached
86
+ is_sudo_command = cmd and cmd[0] == "sudo"
87
+ if is_sudo_command and not skip_sudo_check:
88
+ # Test if sudo password is cached (non-interactive mode)
89
+ test_cmd = ["sudo", "-n", "true"]
90
+ try:
91
+ test_process = await asyncio.create_subprocess_exec(
92
+ *test_cmd,
93
+ stdout=asyncio.subprocess.PIPE,
94
+ stderr=asyncio.subprocess.PIPE
95
+ )
96
+ await test_process.communicate()
97
+ password_cached = test_process.returncode == 0
98
+ logger.debug(f"Sudo password cached: {password_cached}")
99
+
100
+ if not password_cached:
101
+ logger.warning("Sudo password is required but not cached. "
102
+ "Please run 'sudo pacman -S <package>' manually in the terminal.")
103
+ return (
104
+ 1,
105
+ "",
106
+ "Sudo password required. Please configure passwordless sudo for pacman/paru, "
107
+ "or run the installation command manually in your terminal."
108
+ )
109
+ except Exception as e:
110
+ logger.warning(f"Could not check sudo status: {e}")
111
+ password_cached = False
112
+ else:
113
+ password_cached = True
114
+
79
115
  try:
116
+ # Attach stdin to subprocess for commands that might need input
117
+ # Use asyncio.subprocess.PIPE to allow stdin interaction
80
118
  process = await asyncio.create_subprocess_exec(
81
119
  *cmd,
82
120
  stdout=asyncio.subprocess.PIPE,
83
- stderr=asyncio.subprocess.PIPE
121
+ stderr=asyncio.subprocess.PIPE,
122
+ stdin=asyncio.subprocess.PIPE if is_sudo_command else None
84
123
  )
85
124
 
125
+ # Communicate with the process
126
+ # For sudo commands, this allows password input if needed
86
127
  stdout, stderr = await asyncio.wait_for(
87
128
  process.communicate(),
88
129
  timeout=timeout
89
130
  )
90
131
 
91
132
  exit_code = process.returncode
92
- stdout_str = stdout.decode('utf-8', errors='replace')
93
- stderr_str = stderr.decode('utf-8', errors='replace')
133
+ stdout_str = stdout.decode('utf-8', errors='replace') if stdout else ""
134
+ stderr_str = stderr.decode('utf-8', errors='replace') if stderr else ""
94
135
 
95
136
  logger.debug(f"Command exit code: {exit_code}")
96
137
 
arch_ops_server/wiki.py CHANGED
@@ -1,3 +1,4 @@
1
+ # SPDX-License-Identifier: GPL-3.0-only OR MIT
1
2
  """
2
3
  Arch Wiki interface module.
3
4
  Provides search and page retrieval via MediaWiki API with BeautifulSoup fallback.
@@ -1,15 +1,16 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: arch-ops-server
3
- Version: 0.1.1
3
+ Version: 0.1.3
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
7
7
  Author-email: Nihal <2tv8xupqg@mozmail.com>
8
- License: GPL-3.0
8
+ License: GPL-3.0-only OR MIT
9
9
  Classifier: Development Status :: 4 - Beta
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: Intended Audience :: System Administrators
12
12
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
13
+ Classifier: License :: OSI Approved :: MIT License
13
14
  Classifier: Operating System :: POSIX :: Linux
14
15
  Classifier: Programming Language :: Python :: 3
15
16
  Classifier: Programming Language :: Python :: 3.11
@@ -26,6 +27,10 @@ Description-Content-Type: text/markdown
26
27
 
27
28
  # Arch Linux MCP Server
28
29
 
30
+ <a href="https://glama.ai/mcp/servers/@nihalxkumar/arch-mcp">
31
+ <img width="380" height="200" src="https://glama.ai/mcp/servers/@nihalxkumar/arch-mcp/badge" />
32
+ </a>
33
+
29
34
  **Disclaimer:** Unofficial community project, not affiliated with Arch Linux.
30
35
 
31
36
  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 +41,20 @@ Leverage AI to get output for digestible, structured results that are ready for
36
41
 
37
42
  ## Sneak Peak into what's available
38
43
 
44
+ <details open>
45
+ <summary>Claude Desktop (no terminal)</summary>
46
+
47
+ ![Claude Desktop Demo](assets/claudedesktop_signalcli.gif)
48
+
49
+ </details>
50
+
51
+ <details>
52
+ <summary>VS Code (with terminal)</summary>
53
+
54
+ ![VS Code Demo](assets/vscode_notesnook.gif)
55
+
56
+ </details>
57
+
39
58
  ### Resources (URI-based Access)
40
59
 
41
60
  Direct access to Arch ecosystem data via custom URI schemes:
@@ -81,7 +100,6 @@ Direct access to Arch ecosystem data via custom URI schemes:
81
100
  ```bash
82
101
  uvx arch-ops-server
83
102
  ```
84
-
85
103
  ---
86
104
 
87
105
  ## Configuration
@@ -99,11 +117,17 @@ Claude / Cursor / Any MCP client that supports STDIO transport
99
117
  }
100
118
  ```
101
119
 
102
- ## License
120
+ ## Contributing
103
121
 
104
- [GPL-3.0-only](https://www.gnu.org/licenses/gpl-3.0.en.html)
122
+ Contributions are greatly appreciated. Please feel free to submit a pull request or open an issue and help make things better for everyone.
105
123
 
106
- ---
124
+ [Contributing Guide](https://nxk.mintlify.app/arch-mcp/contributing)
125
+
126
+ ## License
127
+
128
+ This project is dual-licensed under your choice of:
107
129
 
130
+ - **[GPL-3.0-only](https://www.gnu.org/licenses/gpl-3.0.en.html)** - See [LICENSE-GPL](LICENSE-GPL)
131
+ - **[MIT License](https://opensource.org/licenses/MIT)** - See [LICENSE-MIT](LICENSE-MIT)
108
132
 
109
- Built with ❤️ for the Arch Linux community
133
+ You may use this software under the terms of either license. See [LICENSE](LICENSE) for more details.
@@ -0,0 +1,11 @@
1
+ arch_ops_server/__init__.py,sha256=XPekyNNo3kc3id4udFE_qzdLNkWGF4qePeSTKlRynWE,1953
2
+ arch_ops_server/aur.py,sha256=poYbh2DW7I1tZfCeNp_7e10fh9ZZx8HTnYZnKKZtflQ,49808
3
+ arch_ops_server/pacman.py,sha256=l46iWgwAWl1MVLsfBYrA2xwuE2BCluaVMZPJ1iZzwAA,9909
4
+ arch_ops_server/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ arch_ops_server/server.py,sha256=Pij1KtsrhEdSlXn-4k9dC6AAywxZQ6D_6fZLy29baqQ,25235
6
+ arch_ops_server/utils.py,sha256=po7MVqCx-hsdx-lOgs7uGicjoUVMf6HvuNNYl2qyFH0,10112
7
+ arch_ops_server/wiki.py,sha256=XB_emMGXYF3Vn5likRICkGOa72YDZvOhtZBgp_d1gg8,7350
8
+ arch_ops_server-0.1.3.dist-info/WHEEL,sha256=DpNsHFUm_gffZe1FgzmqwuqiuPC6Y-uBCzibcJcdupM,78
9
+ arch_ops_server-0.1.3.dist-info/entry_points.txt,sha256=nD6HtiLT-Xh1b63_LGcYNEjHqVlal7I2d5jeFJMtfiU,63
10
+ arch_ops_server-0.1.3.dist-info/METADATA,sha256=pKI-2c6BdaAWxonxi6qhFguWJMzUAiDdnYTpYGOp6S4,5117
11
+ arch_ops_server-0.1.3.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.9.4
2
+ Generator: uv 0.9.8
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,11 +0,0 @@
1
- arch_ops_server/__init__.py,sha256=m0KiRT4iLqY5eyP3Ul-anwQVdpjTghWWW1QOqk6n1NI,1906
2
- arch_ops_server/aur.py,sha256=_BoPsKyBPa2KWi9i1J_RSsqUT53wU03guS0V1sasOXg,46328
3
- arch_ops_server/pacman.py,sha256=nD1B8KgHuwaxHtcO4sd1WpiFcpocjD6yxs1JtVkTjPU,9862
4
- arch_ops_server/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- arch_ops_server/server.py,sha256=5KTPOwzREv5_YUOe9AxquLOy48z8y00jgu3u5uWuNDE,25200
6
- arch_ops_server/utils.py,sha256=-v47tnfxRP-IqvKeqhpAIYVIPhTIh5DMe9jWvCMNRGM,8145
7
- arch_ops_server/wiki.py,sha256=P6znxzV2e9JVUq8yyD0e9pP0Fm7EkS9vmDb2HUdwQpc,7303
8
- arch_ops_server-0.1.1.dist-info/WHEEL,sha256=k57ZwB-NkeM_6AsPnuOHv5gI5KM5kPD6Vx85WmGEcI0,78
9
- arch_ops_server-0.1.1.dist-info/entry_points.txt,sha256=nD6HtiLT-Xh1b63_LGcYNEjHqVlal7I2d5jeFJMtfiU,63
10
- arch_ops_server-0.1.1.dist-info/METADATA,sha256=1KpPcE2_j0yAaY4J8ToGEV6XK1g8cmr8hLhb4Vxzt_A,4183
11
- arch_ops_server-0.1.1.dist-info/RECORD,,