arch-ops-server 0.1.1__tar.gz → 0.1.3__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.
- {arch_ops_server-0.1.1 → arch_ops_server-0.1.3}/PKG-INFO +31 -7
- {arch_ops_server-0.1.1 → arch_ops_server-0.1.3}/README.md +28 -5
- {arch_ops_server-0.1.1 → arch_ops_server-0.1.3}/pyproject.toml +3 -2
- {arch_ops_server-0.1.1 → arch_ops_server-0.1.3}/src/arch_ops_server/__init__.py +1 -0
- {arch_ops_server-0.1.1 → arch_ops_server-0.1.3}/src/arch_ops_server/aur.py +58 -0
- {arch_ops_server-0.1.1 → arch_ops_server-0.1.3}/src/arch_ops_server/pacman.py +1 -0
- {arch_ops_server-0.1.1 → arch_ops_server-0.1.3}/src/arch_ops_server/server.py +3 -2
- {arch_ops_server-0.1.1 → arch_ops_server-0.1.3}/src/arch_ops_server/utils.py +45 -4
- {arch_ops_server-0.1.1 → arch_ops_server-0.1.3}/src/arch_ops_server/wiki.py +1 -0
- {arch_ops_server-0.1.1 → arch_ops_server-0.1.3}/src/arch_ops_server/py.typed +0 -0
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: arch-ops-server
|
|
3
|
-
Version: 0.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
|
+

|
|
48
|
+
|
|
49
|
+
</details>
|
|
50
|
+
|
|
51
|
+
<details>
|
|
52
|
+
<summary>VS Code (with terminal)</summary>
|
|
53
|
+
|
|
54
|
+

|
|
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
|
-
##
|
|
120
|
+
## Contributing
|
|
103
121
|
|
|
104
|
-
|
|
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
|
-
|
|
133
|
+
You may use this software under the terms of either license. See [LICENSE](LICENSE) for more details.
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Arch Linux MCP Server
|
|
2
2
|
|
|
3
|
+
<a href="https://glama.ai/mcp/servers/@nihalxkumar/arch-mcp">
|
|
4
|
+
<img width="380" height="200" src="https://glama.ai/mcp/servers/@nihalxkumar/arch-mcp/badge" />
|
|
5
|
+
</a>
|
|
6
|
+
|
|
3
7
|
**Disclaimer:** Unofficial community project, not affiliated with Arch Linux.
|
|
4
8
|
|
|
5
9
|
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 +14,20 @@ Leverage AI to get output for digestible, structured results that are ready for
|
|
|
10
14
|
|
|
11
15
|
## Sneak Peak into what's available
|
|
12
16
|
|
|
17
|
+
<details open>
|
|
18
|
+
<summary>Claude Desktop (no terminal)</summary>
|
|
19
|
+
|
|
20
|
+

|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<details>
|
|
25
|
+
<summary>VS Code (with terminal)</summary>
|
|
26
|
+
|
|
27
|
+

|
|
28
|
+
|
|
29
|
+
</details>
|
|
30
|
+
|
|
13
31
|
### Resources (URI-based Access)
|
|
14
32
|
|
|
15
33
|
Direct access to Arch ecosystem data via custom URI schemes:
|
|
@@ -55,7 +73,6 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
55
73
|
```bash
|
|
56
74
|
uvx arch-ops-server
|
|
57
75
|
```
|
|
58
|
-
|
|
59
76
|
---
|
|
60
77
|
|
|
61
78
|
## Configuration
|
|
@@ -73,11 +90,17 @@ Claude / Cursor / Any MCP client that supports STDIO transport
|
|
|
73
90
|
}
|
|
74
91
|
```
|
|
75
92
|
|
|
76
|
-
##
|
|
93
|
+
## Contributing
|
|
77
94
|
|
|
78
|
-
|
|
95
|
+
Contributions are greatly appreciated. Please feel free to submit a pull request or open an issue and help make things better for everyone.
|
|
79
96
|
|
|
80
|
-
|
|
97
|
+
[Contributing Guide](https://nxk.mintlify.app/arch-mcp/contributing)
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
This project is dual-licensed under your choice of:
|
|
81
102
|
|
|
103
|
+
- **[GPL-3.0-only](https://www.gnu.org/licenses/gpl-3.0.en.html)** - See [LICENSE-GPL](LICENSE-GPL)
|
|
104
|
+
- **[MIT License](https://opensource.org/licenses/MIT)** - See [LICENSE-MIT](LICENSE-MIT)
|
|
82
105
|
|
|
83
|
-
|
|
106
|
+
You may use this software under the terms of either license. See [LICENSE](LICENSE) for more details.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "arch-ops-server"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.3"
|
|
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
|
-
license = { text = "GPL-3.0" }
|
|
6
|
+
license = { text = "GPL-3.0-only OR MIT" }
|
|
7
7
|
authors = [
|
|
8
8
|
{ name = "Nihal", email = "2tv8xupqg@mozmail.com" }
|
|
9
9
|
]
|
|
@@ -13,6 +13,7 @@ classifiers = [
|
|
|
13
13
|
"Intended Audience :: Developers",
|
|
14
14
|
"Intended Audience :: System Administrators",
|
|
15
15
|
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
16
17
|
"Operating System :: POSIX :: Linux",
|
|
17
18
|
"Programming Language :: Python :: 3",
|
|
18
19
|
"Programming Language :: Python :: 3.11",
|
|
@@ -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
|
|
@@ -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 =
|
|
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 =
|
|
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"""
|
|
@@ -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
|
|
|
File without changes
|