arch-ops-server 0.1.3__py3-none-any.whl → 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- arch_ops_server/__init__.py +87 -1
- arch_ops_server/config.py +361 -0
- arch_ops_server/logs.py +345 -0
- arch_ops_server/mirrors.py +397 -0
- arch_ops_server/news.py +288 -0
- arch_ops_server/pacman.py +985 -0
- arch_ops_server/server.py +1257 -61
- arch_ops_server/system.py +307 -0
- arch_ops_server-3.0.0.dist-info/METADATA +250 -0
- arch_ops_server-3.0.0.dist-info/RECORD +16 -0
- arch_ops_server-0.1.3.dist-info/METADATA +0 -133
- arch_ops_server-0.1.3.dist-info/RECORD +0 -11
- {arch_ops_server-0.1.3.dist-info → arch_ops_server-3.0.0.dist-info}/WHEEL +0 -0
- {arch_ops_server-0.1.3.dist-info → arch_ops_server-3.0.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-only OR MIT
|
|
2
|
+
"""
|
|
3
|
+
System diagnostics and information module.
|
|
4
|
+
Provides system health checks, disk space monitoring, and service status.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Dict, Any, List
|
|
12
|
+
|
|
13
|
+
from .utils import (
|
|
14
|
+
IS_ARCH,
|
|
15
|
+
run_command,
|
|
16
|
+
create_error_response,
|
|
17
|
+
check_command_exists
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
async def get_system_info() -> Dict[str, Any]:
|
|
24
|
+
"""
|
|
25
|
+
Get core system information.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Dict with kernel, architecture, hostname, uptime, memory info
|
|
29
|
+
"""
|
|
30
|
+
logger.info("Gathering system information")
|
|
31
|
+
|
|
32
|
+
info = {}
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
# Kernel version
|
|
36
|
+
exit_code, stdout, _ = await run_command(["uname", "-r"], timeout=5, check=False)
|
|
37
|
+
if exit_code == 0:
|
|
38
|
+
info["kernel"] = stdout.strip()
|
|
39
|
+
|
|
40
|
+
# Architecture
|
|
41
|
+
exit_code, stdout, _ = await run_command(["uname", "-m"], timeout=5, check=False)
|
|
42
|
+
if exit_code == 0:
|
|
43
|
+
info["architecture"] = stdout.strip()
|
|
44
|
+
|
|
45
|
+
# Hostname
|
|
46
|
+
exit_code, stdout, _ = await run_command(["hostname"], timeout=5, check=False)
|
|
47
|
+
if exit_code == 0:
|
|
48
|
+
info["hostname"] = stdout.strip()
|
|
49
|
+
|
|
50
|
+
# Uptime
|
|
51
|
+
exit_code, stdout, _ = await run_command(["uptime", "-p"], timeout=5, check=False)
|
|
52
|
+
if exit_code == 0:
|
|
53
|
+
info["uptime"] = stdout.strip()
|
|
54
|
+
|
|
55
|
+
# Memory info from /proc/meminfo
|
|
56
|
+
try:
|
|
57
|
+
meminfo_path = Path("/proc/meminfo")
|
|
58
|
+
if meminfo_path.exists():
|
|
59
|
+
with open(meminfo_path, "r") as f:
|
|
60
|
+
meminfo = f.read()
|
|
61
|
+
|
|
62
|
+
# Parse memory values
|
|
63
|
+
mem_total_match = re.search(r"MemTotal:\s+(\d+)", meminfo)
|
|
64
|
+
mem_available_match = re.search(r"MemAvailable:\s+(\d+)", meminfo)
|
|
65
|
+
|
|
66
|
+
if mem_total_match:
|
|
67
|
+
info["memory_total_kb"] = int(mem_total_match.group(1))
|
|
68
|
+
info["memory_total_mb"] = int(mem_total_match.group(1)) // 1024
|
|
69
|
+
|
|
70
|
+
if mem_available_match:
|
|
71
|
+
info["memory_available_kb"] = int(mem_available_match.group(1))
|
|
72
|
+
info["memory_available_mb"] = int(mem_available_match.group(1)) // 1024
|
|
73
|
+
except Exception as e:
|
|
74
|
+
logger.warning(f"Failed to read memory info: {e}")
|
|
75
|
+
|
|
76
|
+
info["is_arch_linux"] = IS_ARCH
|
|
77
|
+
|
|
78
|
+
logger.info("Successfully gathered system information")
|
|
79
|
+
return info
|
|
80
|
+
|
|
81
|
+
except Exception as e:
|
|
82
|
+
logger.error(f"Failed to gather system info: {e}")
|
|
83
|
+
return create_error_response(
|
|
84
|
+
"SystemInfoError",
|
|
85
|
+
f"Failed to gather system information: {str(e)}"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
async def check_disk_space() -> Dict[str, Any]:
|
|
90
|
+
"""
|
|
91
|
+
Check disk space for critical paths.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Dict with disk usage for /, /home, /var, /var/cache/pacman/pkg
|
|
95
|
+
"""
|
|
96
|
+
logger.info("Checking disk space")
|
|
97
|
+
|
|
98
|
+
paths_to_check = ["/", "/home", "/var"]
|
|
99
|
+
|
|
100
|
+
if IS_ARCH:
|
|
101
|
+
paths_to_check.append("/var/cache/pacman/pkg")
|
|
102
|
+
|
|
103
|
+
disk_info = {}
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
for path in paths_to_check:
|
|
107
|
+
if not Path(path).exists():
|
|
108
|
+
continue
|
|
109
|
+
|
|
110
|
+
exit_code, stdout, _ = await run_command(
|
|
111
|
+
["df", "-h", path],
|
|
112
|
+
timeout=5,
|
|
113
|
+
check=False
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
if exit_code == 0:
|
|
117
|
+
lines = stdout.strip().split('\n')
|
|
118
|
+
if len(lines) >= 2:
|
|
119
|
+
# Parse df output
|
|
120
|
+
parts = lines[1].split()
|
|
121
|
+
if len(parts) >= 5:
|
|
122
|
+
disk_info[path] = {
|
|
123
|
+
"size": parts[1],
|
|
124
|
+
"used": parts[2],
|
|
125
|
+
"available": parts[3],
|
|
126
|
+
"use_percent": parts[4],
|
|
127
|
+
"mounted_on": parts[5] if len(parts) > 5 else path
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
# Check if space is critically low
|
|
131
|
+
use_pct = int(parts[4].rstrip('%'))
|
|
132
|
+
if use_pct > 90:
|
|
133
|
+
disk_info[path]["warning"] = "Critical: Less than 10% free"
|
|
134
|
+
elif use_pct > 80:
|
|
135
|
+
disk_info[path]["warning"] = "Low: Less than 20% free"
|
|
136
|
+
|
|
137
|
+
logger.info(f"Checked disk space for {len(disk_info)} paths")
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
"disk_usage": disk_info,
|
|
141
|
+
"paths_checked": len(disk_info)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
except Exception as e:
|
|
145
|
+
logger.error(f"Failed to check disk space: {e}")
|
|
146
|
+
return create_error_response(
|
|
147
|
+
"DiskCheckError",
|
|
148
|
+
f"Failed to check disk space: {str(e)}"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
async def get_pacman_cache_stats() -> Dict[str, Any]:
|
|
153
|
+
"""
|
|
154
|
+
Analyze pacman package cache.
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Dict with cache size, package count, statistics
|
|
158
|
+
"""
|
|
159
|
+
if not IS_ARCH:
|
|
160
|
+
return create_error_response(
|
|
161
|
+
"NotSupported",
|
|
162
|
+
"Pacman cache analysis is only available on Arch Linux"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
logger.info("Analyzing pacman cache")
|
|
166
|
+
|
|
167
|
+
cache_dir = Path("/var/cache/pacman/pkg")
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
if not cache_dir.exists():
|
|
171
|
+
return create_error_response(
|
|
172
|
+
"NotFound",
|
|
173
|
+
"Pacman cache directory not found"
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# Count packages
|
|
177
|
+
pkg_files = list(cache_dir.glob("*.pkg.tar.*"))
|
|
178
|
+
pkg_count = len(pkg_files)
|
|
179
|
+
|
|
180
|
+
# Calculate total size
|
|
181
|
+
total_size = sum(f.stat().st_size for f in pkg_files)
|
|
182
|
+
total_size_mb = total_size / (1024 * 1024)
|
|
183
|
+
total_size_gb = total_size_mb / 1024
|
|
184
|
+
|
|
185
|
+
logger.info(f"Cache: {pkg_count} packages, {total_size_gb:.2f} GB")
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
"cache_dir": str(cache_dir),
|
|
189
|
+
"package_count": pkg_count,
|
|
190
|
+
"total_size_bytes": total_size,
|
|
191
|
+
"total_size_mb": round(total_size_mb, 2),
|
|
192
|
+
"total_size_gb": round(total_size_gb, 2)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
except Exception as e:
|
|
196
|
+
logger.error(f"Failed to analyze cache: {e}")
|
|
197
|
+
return create_error_response(
|
|
198
|
+
"CacheAnalysisError",
|
|
199
|
+
f"Failed to analyze pacman cache: {str(e)}"
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
async def check_failed_services() -> Dict[str, Any]:
|
|
204
|
+
"""
|
|
205
|
+
Detect failed systemd services.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
Dict with list of failed services
|
|
209
|
+
"""
|
|
210
|
+
if not check_command_exists("systemctl"):
|
|
211
|
+
return create_error_response(
|
|
212
|
+
"NotSupported",
|
|
213
|
+
"systemctl not available (systemd-based system required)"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
logger.info("Checking for failed services")
|
|
217
|
+
|
|
218
|
+
try:
|
|
219
|
+
exit_code, stdout, _ = await run_command(
|
|
220
|
+
["systemctl", "--failed", "--no-pager"],
|
|
221
|
+
timeout=10,
|
|
222
|
+
check=False
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# Parse output
|
|
226
|
+
failed_services = []
|
|
227
|
+
lines = stdout.strip().split('\n')
|
|
228
|
+
|
|
229
|
+
for line in lines:
|
|
230
|
+
# Skip header and footer lines
|
|
231
|
+
if line.startswith('●') or line.startswith('UNIT'):
|
|
232
|
+
continue
|
|
233
|
+
if 'loaded units listed' in line.lower():
|
|
234
|
+
continue
|
|
235
|
+
|
|
236
|
+
# Parse service line
|
|
237
|
+
parts = line.split()
|
|
238
|
+
if parts and parts[0].endswith('.service'):
|
|
239
|
+
failed_services.append({
|
|
240
|
+
"unit": parts[0],
|
|
241
|
+
"load": parts[1] if len(parts) > 1 else "",
|
|
242
|
+
"active": parts[2] if len(parts) > 2 else "",
|
|
243
|
+
"sub": parts[3] if len(parts) > 3 else "",
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
logger.info(f"Found {len(failed_services)} failed services")
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
"failed_count": len(failed_services),
|
|
250
|
+
"failed_services": failed_services,
|
|
251
|
+
"all_ok": len(failed_services) == 0
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
except Exception as e:
|
|
255
|
+
logger.error(f"Failed to check services: {e}")
|
|
256
|
+
return create_error_response(
|
|
257
|
+
"ServiceCheckError",
|
|
258
|
+
f"Failed to check failed services: {str(e)}"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
async def get_boot_logs(lines: int = 100) -> Dict[str, Any]:
|
|
263
|
+
"""
|
|
264
|
+
Retrieve recent boot logs.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
lines: Number of lines to retrieve
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
Dict with boot log contents
|
|
271
|
+
"""
|
|
272
|
+
if not check_command_exists("journalctl"):
|
|
273
|
+
return create_error_response(
|
|
274
|
+
"NotSupported",
|
|
275
|
+
"journalctl not available (systemd-based system required)"
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
logger.info(f"Retrieving {lines} lines of boot logs")
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
exit_code, stdout, stderr = await run_command(
|
|
282
|
+
["journalctl", "-b", "-n", str(lines), "--no-pager"],
|
|
283
|
+
timeout=15,
|
|
284
|
+
check=False
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
if exit_code != 0:
|
|
288
|
+
return create_error_response(
|
|
289
|
+
"CommandError",
|
|
290
|
+
f"Failed to retrieve boot logs: {stderr}"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
log_lines = stdout.strip().split('\n')
|
|
294
|
+
|
|
295
|
+
logger.info(f"Retrieved {len(log_lines)} lines of boot logs")
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
"line_count": len(log_lines),
|
|
299
|
+
"logs": log_lines
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
except Exception as e:
|
|
303
|
+
logger.error(f"Failed to get boot logs: {e}")
|
|
304
|
+
return create_error_response(
|
|
305
|
+
"LogRetrievalError",
|
|
306
|
+
f"Failed to retrieve boot logs: {str(e)}"
|
|
307
|
+
)
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: arch-ops-server
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: MCP server bridging AI assistants with Arch Linux ecosystem (Wiki, AUR, official repos)
|
|
5
|
+
Keywords: arch-linux,mcp,model-context-protocol,aur,pacman,wiki,ai-assistant
|
|
6
|
+
Author: Nihal
|
|
7
|
+
Author-email: Nihal <2tv8xupqg@mozmail.com>
|
|
8
|
+
License: GPL-3.0-only OR MIT
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Intended Audience :: System Administrators
|
|
12
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: System :: Archiving :: Packaging
|
|
19
|
+
Classifier: Topic :: System :: Systems Administration
|
|
20
|
+
Requires-Dist: mcp>=1.0.0
|
|
21
|
+
Requires-Dist: httpx>=0.27.0
|
|
22
|
+
Requires-Dist: beautifulsoup4>=4.12.0
|
|
23
|
+
Requires-Dist: lxml>=5.0.0
|
|
24
|
+
Requires-Dist: markdownify>=0.12.0
|
|
25
|
+
Requires-Dist: pytest>=8.0.0 ; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest-asyncio>=0.23.0 ; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-cov>=4.1.0 ; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-mock>=3.12.0 ; extra == 'dev'
|
|
29
|
+
Requires-Dist: httpx>=0.27.0 ; extra == 'dev'
|
|
30
|
+
Requires-Python: >=3.11
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# Arch Linux MCP Server
|
|
35
|
+
|
|
36
|
+
<a href="https://glama.ai/mcp/servers/@nihalxkumar/arch-mcp">
|
|
37
|
+
<img width="380" height="200" src="https://glama.ai/mcp/servers/@nihalxkumar/arch-mcp/badge" />
|
|
38
|
+
</a>
|
|
39
|
+
|
|
40
|
+
**Disclaimer:** Unofficial community project, not affiliated with Arch Linux.
|
|
41
|
+
|
|
42
|
+
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.
|
|
43
|
+
|
|
44
|
+
Leverage AI to get output for digestible, structured results that are ready for follow up questions and actions.
|
|
45
|
+
|
|
46
|
+
📖 [Complete Documentation with Comfy Guides](https://nxk.mintlify.app/arch-mcp)
|
|
47
|
+
|
|
48
|
+
## Sneak Peak into what's available
|
|
49
|
+
|
|
50
|
+
<details open>
|
|
51
|
+
<summary>Claude Desktop (no terminal)</summary>
|
|
52
|
+
|
|
53
|
+

|
|
54
|
+
|
|
55
|
+
</details>
|
|
56
|
+
|
|
57
|
+
<details>
|
|
58
|
+
<summary>VS Code (with terminal)</summary>
|
|
59
|
+
|
|
60
|
+

|
|
61
|
+
|
|
62
|
+
</details>
|
|
63
|
+
|
|
64
|
+
### Resources (URI-based Access)
|
|
65
|
+
|
|
66
|
+
Direct access to Arch ecosystem data via custom URI schemes:
|
|
67
|
+
|
|
68
|
+
#### Documentation & Search
|
|
69
|
+
| URI Scheme | Example | Returns |
|
|
70
|
+
|------------|---------|---------|
|
|
71
|
+
| `archwiki://` | `archwiki://Installation_guide` | Markdown-formatted Wiki page |
|
|
72
|
+
|
|
73
|
+
#### Package Information
|
|
74
|
+
| URI Scheme | Example | Returns |
|
|
75
|
+
|------------|---------|---------|
|
|
76
|
+
| `archrepo://` | `archrepo://vim` | Official repository package details |
|
|
77
|
+
| `aur://*/info` | `aur://yay/info` | AUR package metadata (votes, maintainer, dates) |
|
|
78
|
+
| `aur://*/pkgbuild` | `aur://yay/pkgbuild` | Raw PKGBUILD with safety analysis |
|
|
79
|
+
|
|
80
|
+
#### System Packages (Arch only)
|
|
81
|
+
| URI Scheme | Example | Returns |
|
|
82
|
+
|------------|---------|---------|
|
|
83
|
+
| `pacman://installed` | `pacman://installed` | System installed packages list |
|
|
84
|
+
| `pacman://orphans` | `pacman://orphans` | Orphaned packages |
|
|
85
|
+
| `pacman://explicit` | `pacman://explicit` | Explicitly installed packages |
|
|
86
|
+
| `pacman://groups` | `pacman://groups` | All package groups |
|
|
87
|
+
| `pacman://group/*` | `pacman://group/base-devel` | Packages in specific group |
|
|
88
|
+
| `pacman://database/freshness` | `pacman://database/freshness` | Package database sync status |
|
|
89
|
+
|
|
90
|
+
#### System Monitoring & Logs
|
|
91
|
+
| URI Scheme | Example | Returns |
|
|
92
|
+
|------------|---------|---------|
|
|
93
|
+
| `system://info` | `system://info` | System information (kernel, memory, uptime) |
|
|
94
|
+
| `system://disk` | `system://disk` | Disk space usage statistics |
|
|
95
|
+
| `system://services/failed` | `system://services/failed` | Failed systemd services |
|
|
96
|
+
| `system://logs/boot` | `system://logs/boot` | Recent boot logs |
|
|
97
|
+
| `pacman://log/recent` | `pacman://log/recent` | Recent package transactions |
|
|
98
|
+
| `pacman://log/failed` | `pacman://log/failed` | Failed package transactions |
|
|
99
|
+
|
|
100
|
+
#### News & Updates
|
|
101
|
+
| URI Scheme | Example | Returns |
|
|
102
|
+
|------------|---------|---------|
|
|
103
|
+
| `archnews://latest` | `archnews://latest` | Latest Arch Linux news |
|
|
104
|
+
| `archnews://critical` | `archnews://critical` | Critical news requiring manual intervention |
|
|
105
|
+
| `archnews://since-update` | `archnews://since-update` | News since last system update |
|
|
106
|
+
|
|
107
|
+
#### Configuration
|
|
108
|
+
| URI Scheme | Example | Returns |
|
|
109
|
+
|------------|---------|---------|
|
|
110
|
+
| `config://pacman` | `config://pacman` | Parsed pacman.conf configuration |
|
|
111
|
+
| `config://makepkg` | `config://makepkg` | Parsed makepkg.conf configuration |
|
|
112
|
+
| `mirrors://active` | `mirrors://active` | Currently configured mirrors |
|
|
113
|
+
| `mirrors://health` | `mirrors://health` | Mirror configuration health status |
|
|
114
|
+
|
|
115
|
+
### Tools (Executable Functions)
|
|
116
|
+
|
|
117
|
+
#### Package Search & Information
|
|
118
|
+
| Tool | Description | Platform |
|
|
119
|
+
|------|-------------|----------|
|
|
120
|
+
| `search_archwiki` | Query Arch Wiki with ranked results | Any |
|
|
121
|
+
| `search_aur` | Search AUR (relevance/votes/popularity/modified) | Any |
|
|
122
|
+
| `get_official_package_info` | Get official package details (hybrid local/remote) | Any |
|
|
123
|
+
|
|
124
|
+
#### Package Lifecycle Management
|
|
125
|
+
| Tool | Description | Platform |
|
|
126
|
+
|------|-------------|----------|
|
|
127
|
+
| `check_updates_dry_run` | Check for available updates | Arch only |
|
|
128
|
+
| `install_package_secure` | Install with security checks (blocks malicious packages) | Arch only |
|
|
129
|
+
| `remove_package` | Remove single package (with deps, forced) | Arch only |
|
|
130
|
+
| `remove_packages_batch` | Remove multiple packages efficiently | Arch only |
|
|
131
|
+
|
|
132
|
+
#### Package Analysis & Maintenance
|
|
133
|
+
| Tool | Description | Platform |
|
|
134
|
+
|------|-------------|----------|
|
|
135
|
+
| `list_orphan_packages` | Find orphaned packages | Arch only |
|
|
136
|
+
| `remove_orphans` | Clean orphans (dry-run, exclusions) | Arch only |
|
|
137
|
+
| `verify_package_integrity` | Check file integrity (modified/missing files) | Arch only |
|
|
138
|
+
| `list_explicit_packages` | List user-installed packages | Arch only |
|
|
139
|
+
| `mark_as_explicit` | Prevent package from being orphaned | Arch only |
|
|
140
|
+
| `mark_as_dependency` | Allow package to be orphaned | Arch only |
|
|
141
|
+
|
|
142
|
+
#### Package Organization
|
|
143
|
+
| Tool | Description | Platform |
|
|
144
|
+
|------|-------------|----------|
|
|
145
|
+
| `find_package_owner` | Find which package owns a file | Arch only |
|
|
146
|
+
| `list_package_files` | List files in package (regex filtering) | Arch only |
|
|
147
|
+
| `search_package_files` | Search files across packages | Arch only |
|
|
148
|
+
| `list_package_groups` | List all groups (base, base-devel, etc.) | Arch only |
|
|
149
|
+
| `list_group_packages` | Show packages in specific group | Arch only |
|
|
150
|
+
|
|
151
|
+
#### System Monitoring & Diagnostics
|
|
152
|
+
| Tool | Description | Platform |
|
|
153
|
+
|------|-------------|----------|
|
|
154
|
+
| `get_system_info` | System info (kernel, memory, uptime) | Any |
|
|
155
|
+
| `check_disk_space` | Disk usage with warnings | Any |
|
|
156
|
+
| `get_pacman_cache_stats` | Package cache size and age | Arch only |
|
|
157
|
+
| `check_failed_services` | Find failed systemd services | systemd |
|
|
158
|
+
| `get_boot_logs` | Retrieve journalctl boot logs | systemd |
|
|
159
|
+
| `check_database_freshness` | Check package database sync status | Arch only |
|
|
160
|
+
|
|
161
|
+
#### Transaction History & Logs
|
|
162
|
+
| Tool | Description | Platform |
|
|
163
|
+
|------|-------------|----------|
|
|
164
|
+
| `get_transaction_history` | Recent package transactions (install/upgrade/remove) | Arch only |
|
|
165
|
+
| `find_when_installed` | Package installation history | Arch only |
|
|
166
|
+
| `find_failed_transactions` | Failed package operations | Arch only |
|
|
167
|
+
| `get_database_sync_history` | Database sync events | Arch only |
|
|
168
|
+
|
|
169
|
+
#### News & Safety Checks
|
|
170
|
+
| Tool | Description | Platform |
|
|
171
|
+
|------|-------------|----------|
|
|
172
|
+
| `get_latest_news` | Fetch Arch Linux news from RSS | Any |
|
|
173
|
+
| `check_critical_news` | Find critical news (manual intervention required) | Any |
|
|
174
|
+
| `get_news_since_last_update` | News posted since last system update | Arch only |
|
|
175
|
+
|
|
176
|
+
#### Mirror Management
|
|
177
|
+
| Tool | Description | Platform |
|
|
178
|
+
|------|-------------|----------|
|
|
179
|
+
| `list_active_mirrors` | Show configured mirrors | Arch only |
|
|
180
|
+
| `test_mirror_speed` | Test mirror latency | Arch only |
|
|
181
|
+
| `suggest_fastest_mirrors` | Recommend optimal mirrors by location | Any |
|
|
182
|
+
| `check_mirrorlist_health` | Verify mirror configuration | Arch only |
|
|
183
|
+
|
|
184
|
+
#### Configuration Management
|
|
185
|
+
| Tool | Description | Platform |
|
|
186
|
+
|------|-------------|----------|
|
|
187
|
+
| `analyze_pacman_conf` | Parse pacman.conf settings | Arch only |
|
|
188
|
+
| `analyze_makepkg_conf` | Parse makepkg.conf settings | Arch only |
|
|
189
|
+
| `check_ignored_packages` | List ignored packages (warns on critical) | Arch only |
|
|
190
|
+
| `get_parallel_downloads_setting` | Get parallel download config | Arch only |
|
|
191
|
+
|
|
192
|
+
#### Security Analysis
|
|
193
|
+
| Tool | Description | Platform |
|
|
194
|
+
|------|-------------|----------|
|
|
195
|
+
| `analyze_pkgbuild_safety` | Comprehensive PKGBUILD analysis (50+ red flags) | Any |
|
|
196
|
+
| `analyze_package_metadata_risk` | Package trust scoring (votes, maintainer, age) | Any |
|
|
197
|
+
|
|
198
|
+
### Prompts (Guided Workflows)
|
|
199
|
+
|
|
200
|
+
| Prompt | Purpose | Workflow |
|
|
201
|
+
|--------|---------|----------|
|
|
202
|
+
| `troubleshoot_issue` | Diagnose system errors | Extract keywords → Search Wiki → Context-aware suggestions |
|
|
203
|
+
| `audit_aur_package` | Pre-installation safety audit | Fetch metadata → Analyze PKGBUILD → Security recommendations |
|
|
204
|
+
| `analyze_dependencies` | Installation planning | Check repos → Map dependencies → Suggest install order |
|
|
205
|
+
| `safe_system_update` | Safe update workflow | Check critical news → Verify disk space → List updates → Check services → Recommendations |
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Installation
|
|
210
|
+
|
|
211
|
+
### Prerequisites
|
|
212
|
+
- Python 3.11+
|
|
213
|
+
- [uv](https://github.com/astral-sh/uv) (recommended) or pip
|
|
214
|
+
|
|
215
|
+
### Quick Install with `uvx`
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
uvx arch-ops-server
|
|
219
|
+
```
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Configuration
|
|
223
|
+
|
|
224
|
+
Claude / Cursor / Any MCP client that supports STDIO transport
|
|
225
|
+
|
|
226
|
+
```json
|
|
227
|
+
{
|
|
228
|
+
"mcpServers": {
|
|
229
|
+
"arch-ops": {
|
|
230
|
+
"command": "uvx",
|
|
231
|
+
"args": ["arch-ops-server"]
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Contributing
|
|
238
|
+
|
|
239
|
+
Contributions are greatly appreciated. Please feel free to submit a pull request or open an issue and help make things better for everyone.
|
|
240
|
+
|
|
241
|
+
[Contributing Guide](https://nxk.mintlify.app/arch-mcp/contributing)
|
|
242
|
+
|
|
243
|
+
## License
|
|
244
|
+
|
|
245
|
+
This project is dual-licensed under your choice of:
|
|
246
|
+
|
|
247
|
+
- **[GPL-3.0-only](https://www.gnu.org/licenses/gpl-3.0.en.html)** - See [LICENSE-GPL](LICENSE-GPL)
|
|
248
|
+
- **[MIT License](https://opensource.org/licenses/MIT)** - See [LICENSE-MIT](LICENSE-MIT)
|
|
249
|
+
|
|
250
|
+
You may use this software under the terms of either license. See [LICENSE](LICENSE) for more details.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
arch_ops_server/__init__.py,sha256=TUG3zfhDW_dKXxAb9bXEz8CEi3HvJ4CI8JnykfxWbag,3971
|
|
2
|
+
arch_ops_server/aur.py,sha256=poYbh2DW7I1tZfCeNp_7e10fh9ZZx8HTnYZnKKZtflQ,49808
|
|
3
|
+
arch_ops_server/config.py,sha256=4mtpS28vXSMeEVGrTWTMwZEzgIyfl0oCAYEzF7SKxE8,11076
|
|
4
|
+
arch_ops_server/logs.py,sha256=qWExDvluHmQvbZHu87veQxMnuMK8BNLBYBppZJlemEc,10558
|
|
5
|
+
arch_ops_server/mirrors.py,sha256=Evt-g20cMOTZQl9FbbkbklFd0gKWz-I7vVNrmyQO19U,13403
|
|
6
|
+
arch_ops_server/news.py,sha256=E97eASR24tq_EaVDYuamIoBl4a7QtBkpscOaUPuU0W4,9359
|
|
7
|
+
arch_ops_server/pacman.py,sha256=S1Gc53CA6o4--YavA03EkxL0dGCZNhoFFZjawlW_p20,38354
|
|
8
|
+
arch_ops_server/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
arch_ops_server/server.py,sha256=9t8KMZmy6MtQQXk7BwTz6oQNj3xwOGbINmDdxS2R-1M,72661
|
|
10
|
+
arch_ops_server/system.py,sha256=JfBUB3KD0veulQ-IIK8IOC8Jn6lqtLMCtlnryiL1n7w,9221
|
|
11
|
+
arch_ops_server/utils.py,sha256=po7MVqCx-hsdx-lOgs7uGicjoUVMf6HvuNNYl2qyFH0,10112
|
|
12
|
+
arch_ops_server/wiki.py,sha256=XB_emMGXYF3Vn5likRICkGOa72YDZvOhtZBgp_d1gg8,7350
|
|
13
|
+
arch_ops_server-3.0.0.dist-info/WHEEL,sha256=DpNsHFUm_gffZe1FgzmqwuqiuPC6Y-uBCzibcJcdupM,78
|
|
14
|
+
arch_ops_server-3.0.0.dist-info/entry_points.txt,sha256=nD6HtiLT-Xh1b63_LGcYNEjHqVlal7I2d5jeFJMtfiU,63
|
|
15
|
+
arch_ops_server-3.0.0.dist-info/METADATA,sha256=7cm5fc2uFbcCCxL26JRdU29VXGpVQv3p-WgsVo6hE_4,10581
|
|
16
|
+
arch_ops_server-3.0.0.dist-info/RECORD,,
|