ChrisSacrumCor commited on
Commit
b15eafa
Β·
verified Β·
1 Parent(s): dba99c4

Create initial Linux MCP server

Browse files
Files changed (1) hide show
  1. app.py +526 -0
app.py ADDED
@@ -0,0 +1,526 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import asyncio
3
+ import uvicorn
4
+ from fastapi import FastAPI, Request
5
+ from fastapi.responses import StreamingResponse
6
+ import json
7
+ from mcp.server import Server
8
+ from mcp.types import Tool, TextContent
9
+
10
+ # Create FastAPI app
11
+ app = FastAPI(title="Linux MCP Server")
12
+
13
+ # Create MCP server instance
14
+ mcp_app = Server("linux-mcp")
15
+
16
+ # ============================================================================
17
+ # LINUX MCP HANDLER FUNCTIONS
18
+ # ============================================================================
19
+
20
+ def handle_user_management(args):
21
+ """Generate Linux user management commands"""
22
+ action = args.get("action")
23
+ username = args.get("username", "")
24
+ groupname = args.get("groupname", "")
25
+ use_sudo = args.get("sudo", True)
26
+ sudo_prefix = "sudo " if use_sudo else ""
27
+
28
+ commands = []
29
+
30
+ if action == "add_user_to_group":
31
+ if not username or not groupname:
32
+ return [TextContent(type="text", text="❌ Username and groupname required for add_user_to_group")]
33
+
34
+ commands = [
35
+ f"# Add user '{username}' to group '{groupname}'",
36
+ f"{sudo_prefix}usermod -a -G {groupname} {username}",
37
+ "",
38
+ "# Alternative methods:",
39
+ f"{sudo_prefix}gpasswd -a {username} {groupname}",
40
+ f"{sudo_prefix}adduser {username} {groupname} # Ubuntu/Debian",
41
+ "",
42
+ "# Verify group membership:",
43
+ f"groups {username}",
44
+ f"id {username}",
45
+ f"getent group {groupname}"
46
+ ]
47
+
48
+ elif action == "create_user":
49
+ if not username:
50
+ return [TextContent(type="text", text="❌ Username required for create_user")]
51
+
52
+ commands = [
53
+ f"# Create new user '{username}'",
54
+ f"{sudo_prefix}useradd -m -s /bin/bash {username}",
55
+ f"{sudo_prefix}passwd {username}",
56
+ "",
57
+ "# Create user with specific group:",
58
+ f"{sudo_prefix}useradd -m -g {groupname if groupname else 'users'} -s /bin/bash {username}",
59
+ "",
60
+ "# Add to sudo group (Ubuntu/Debian):",
61
+ f"{sudo_prefix}usermod -a -G sudo {username}",
62
+ "",
63
+ "# Add to wheel group (RHEL/CentOS):",
64
+ f"{sudo_prefix}usermod -a -G wheel {username}",
65
+ "",
66
+ "# Verify user creation:",
67
+ f"id {username}",
68
+ f"getent passwd {username}"
69
+ ]
70
+
71
+ elif action == "delete_user":
72
+ if not username:
73
+ return [TextContent(type="text", text="❌ Username required for delete_user")]
74
+
75
+ commands = [
76
+ f"# Delete user '{username}'",
77
+ f"{sudo_prefix}userdel {username}",
78
+ "",
79
+ "# Delete user and home directory:",
80
+ f"{sudo_prefix}userdel -r {username}",
81
+ "",
82
+ "# Remove user from all groups:",
83
+ f"{sudo_prefix}gpasswd -d {username} groupname",
84
+ "",
85
+ "# Verify user deletion:",
86
+ f"getent passwd {username}",
87
+ "# Should return no results if deleted successfully"
88
+ ]
89
+
90
+ elif action == "list_groups":
91
+ commands = [
92
+ "# List all groups:",
93
+ "cat /etc/group",
94
+ "getent group",
95
+ "",
96
+ "# List groups for specific user:",
97
+ f"groups {username}" if username else "groups $USER",
98
+ f"id {username}" if username else "id $USER",
99
+ "",
100
+ "# List users in specific group:",
101
+ f"getent group {groupname}" if groupname else "getent group sudo",
102
+ "",
103
+ "# List all users:",
104
+ "cut -d: -f1 /etc/passwd",
105
+ "getent passwd"
106
+ ]
107
+
108
+ result = "\n".join(commands)
109
+ return [TextContent(type="text", text=result)]
110
+
111
+ def handle_file_permissions(args):
112
+ """Generate file permission and ownership commands"""
113
+ action = args.get("action")
114
+ path = args.get("path")
115
+ permissions = args.get("permissions", "755")
116
+ owner = args.get("owner", "")
117
+ recursive = args.get("recursive", False)
118
+
119
+ if not path:
120
+ return [TextContent(type="text", text="❌ Path is required")]
121
+
122
+ commands = []
123
+ recursive_flag = "-R " if recursive else ""
124
+
125
+ if action == "chmod":
126
+ commands = [
127
+ f"# Change permissions for {path}",
128
+ f"chmod {recursive_flag}{permissions} {path}",
129
+ "",
130
+ "# Common permission examples:",
131
+ "# 755 = rwxr-xr-x (executable for owner, readable for others)",
132
+ "# 644 = rw-r--r-- (readable/writable for owner, readable for others)",
133
+ "# 600 = rw------- (readable/writable for owner only)",
134
+ "# 777 = rwxrwxrwx (full permissions for all - use with caution!)",
135
+ "",
136
+ "# Symbolic notation examples:",
137
+ f"chmod {recursive_flag}u+x {path} # Add execute for owner",
138
+ f"chmod {recursive_flag}g-w {path} # Remove write for group",
139
+ f"chmod {recursive_flag}o-r {path} # Remove read for others",
140
+ "",
141
+ "# Verify permissions:",
142
+ f"ls -la {path}",
143
+ f"stat {path}"
144
+ ]
145
+
146
+ elif action == "chown":
147
+ if not owner:
148
+ return [TextContent(type="text", text="❌ Owner is required for chown")]
149
+
150
+ commands = [
151
+ f"# Change ownership for {path}",
152
+ f"sudo chown {recursive_flag}{owner} {path}",
153
+ "",
154
+ "# Change owner and group:",
155
+ f"sudo chown {recursive_flag}{owner}:{owner} {path}",
156
+ f"sudo chown {recursive_flag}{owner}:users {path}",
157
+ "",
158
+ "# Change only group:",
159
+ f"sudo chgrp {recursive_flag}{owner} {path}",
160
+ "",
161
+ "# Verify ownership:",
162
+ f"ls -la {path}",
163
+ f"stat {path}"
164
+ ]
165
+
166
+ elif action == "find_permissions":
167
+ commands = [
168
+ f"# Find files with specific permissions in {path}",
169
+ f"find {path} -type f -perm {permissions}",
170
+ f"find {path} -type f -perm -{permissions} # At least these permissions",
171
+ "",
172
+ "# Find files by owner:",
173
+ f"find {path} -user {owner}" if owner else f"find {path} -user $USER",
174
+ f"find {path} -group {owner}" if owner else f"find {path} -group users",
175
+ "",
176
+ "# Find files with dangerous permissions:",
177
+ f"find {path} -type f -perm -002 # World-writable files",
178
+ f"find {path} -type f -perm -004 # World-readable files",
179
+ f"find {path} -type f -perm -006 # World-readable and writable",
180
+ "",
181
+ "# Find SUID/SGID files:",
182
+ f"find {path} -type f -perm -4000 # SUID files",
183
+ f"find {path} -type f -perm -2000 # SGID files",
184
+ "",
185
+ "# Find directories with specific permissions:",
186
+ f"find {path} -type d -perm 777 # World-writable directories"
187
+ ]
188
+
189
+ result = "\n".join(commands)
190
+ return [TextContent(type="text", text=result)]
191
+
192
+ def handle_system_commands(args):
193
+ """Generate Linux system administration commands"""
194
+ category = args.get("category")
195
+ action = args.get("action", "")
196
+ target = args.get("target", "")
197
+
198
+ commands = []
199
+
200
+ if category == "process":
201
+ commands = [
202
+ "# Process Management Commands",
203
+ "ps aux # List all processes with details",
204
+ "ps -ef # Alternative process listing",
205
+ "pstree # Show process tree",
206
+ "",
207
+ "# Find and manage specific processes:",
208
+ f"ps aux | grep {target if target else 'nginx'} # Find specific process",
209
+ f"pgrep -f {target if target else 'nginx'} # Get PID by name",
210
+ f"pkill -f {target if target else 'nginx'} # Kill process by name",
211
+ f"killall {target if target else 'nginx'} # Kill all processes by name",
212
+ "",
213
+ "# Process control:",
214
+ "kill -15 PID # Graceful termination (SIGTERM)",
215
+ "kill -9 PID # Force kill (SIGKILL)",
216
+ "kill -HUP PID # Reload configuration (SIGHUP)",
217
+ "",
218
+ "# Background processes:",
219
+ "nohup command & # Run command in background",
220
+ "screen -S session_name # Start screen session",
221
+ "tmux new -s session_name # Start tmux session",
222
+ "",
223
+ "# Process monitoring:",
224
+ "top # Real-time process monitor",
225
+ "htop # Enhanced process monitor",
226
+ "jobs # List background jobs",
227
+ "bg # Resume job in background",
228
+ "fg # Bring job to foreground"
229
+ ]
230
+
231
+ elif category == "disk":
232
+ commands = [
233
+ "# Disk Usage and Management Commands",
234
+ "df -h # Show disk usage (human readable)",
235
+ "df -i # Show inode usage",
236
+ "du -sh /* # Show directory sizes in root",
237
+ f"du -sh {target if target else '/var/log'}/* | sort -hr # Sort by size",
238
+ "",
239
+ "# Disk analysis:",
240
+ "lsblk # List block devices",
241
+ "lsblk -f # Show filesystems",
242
+ "blkid # Show UUIDs and labels",
243
+ "fdisk -l # List disk partitions",
244
+ "parted -l # Alternative partition listing",
245
+ "",
246
+ "# Mount management:",
247
+ "mount # Show mounted filesystems",
248
+ "findmnt # Tree view of mounts",
249
+ "findmnt -D # Show duplicate mounts",
250
+ f"mount /dev/sdb1 /mnt/{target if target else 'backup'} # Mount filesystem",
251
+ f"umount /mnt/{target if target else 'backup'} # Unmount filesystem",
252
+ "",
253
+ "# Cleanup commands:",
254
+ "apt autoremove # Remove unused packages (Debian/Ubuntu)",
255
+ "yum autoremove # Remove unused packages (RHEL/CentOS)",
256
+ "journalctl --disk-usage # Check journal disk usage",
257
+ "journalctl --vacuum-time=3d # Clean old journal entries"
258
+ ]
259
+
260
+ elif category == "network":
261
+ commands = [
262
+ "# Network Configuration and Diagnostics",
263
+ "ip addr show # Show IP addresses",
264
+ "ip link show # Show network interfaces",
265
+ "ip route show # Show routing table",
266
+ "ip neigh show # Show ARP table",
267
+ "",
268
+ "# Port and connection analysis:",
269
+ "ss -tuln # Show listening ports",
270
+ "ss -tulpn # Show ports with process names",
271
+ "netstat -tuln # Alternative port listing",
272
+ "netstat -rn # Show routing table",
273
+ "",
274
+ "# Connectivity testing:",
275
+ f"ping -c 4 {target if target else 'google.com'} # Test connectivity",
276
+ f"traceroute {target if target else 'google.com'} # Trace network path",
277
+ f"mtr {target if target else 'google.com'} # Network diagnostic tool",
278
+ f"nslookup {target if target else 'google.com'} # DNS lookup",
279
+ f"dig {target if target else 'google.com'} # DNS information",
280
+ "",
281
+ "# Network configuration:",
282
+ "wget -O- ifconfig.me # Get public IP",
283
+ "curl -s ifconfig.me # Alternative public IP",
284
+ "curl -s ipinfo.io # Detailed IP information",
285
+ "",
286
+ "# Interface management:",
287
+ "sudo ip link set eth0 up # Bring interface up",
288
+ "sudo ip link set eth0 down # Bring interface down",
289
+ "sudo dhclient eth0 # Request DHCP lease"
290
+ ]
291
+
292
+ elif category == "service":
293
+ commands = [
294
+ "# Service Management (systemd)",
295
+ f"systemctl status {target if target else 'nginx'} # Check service status",
296
+ f"systemctl start {target if target else 'nginx'} # Start service",
297
+ f"systemctl stop {target if target else 'nginx'} # Stop service",
298
+ f"systemctl restart {target if target else 'nginx'} # Restart service",
299
+ f"systemctl reload {target if target else 'nginx'} # Reload configuration",
300
+ "",
301
+ "# Service persistence:",
302
+ f"systemctl enable {target if target else 'nginx'} # Enable at boot",
303
+ f"systemctl disable {target if target else 'nginx'} # Disable at boot",
304
+ f"systemctl is-enabled {target if target else 'nginx'} # Check if enabled",
305
+ "",
306
+ "# Service discovery:",
307
+ "systemctl list-units # List all active services",
308
+ "systemctl list-units --failed # List failed services",
309
+ "systemctl list-unit-files # List all unit files",
310
+ "",
311
+ "# Logs and troubleshooting:",
312
+ f"journalctl -u {target if target else 'nginx'} # View service logs",
313
+ f"journalctl -u {target if target else 'nginx'} -f # Follow service logs",
314
+ f"journalctl -u {target if target else 'nginx'} --since today # Today's logs",
315
+ "",
316
+ "# Legacy service management (SysV):",
317
+ f"service {target if target else 'nginx'} status # Check status (legacy)",
318
+ f"service {target if target else 'nginx'} start # Start service (legacy)",
319
+ f"/etc/init.d/{target if target else 'nginx'} status # Direct init script"
320
+ ]
321
+
322
+ elif category == "firewall":
323
+ commands = [
324
+ "# Firewall Management",
325
+ "sudo ufw status verbose # Check UFW status (detailed)",
326
+ "sudo ufw --version # Check UFW version",
327
+ "",
328
+ "# UFW basic operations:",
329
+ "sudo ufw enable # Enable UFW",
330
+ "sudo ufw disable # Disable UFW",
331
+ "sudo ufw reset # Reset UFW rules",
332
+ "",
333
+ "# UFW rule management:",
334
+ f"sudo ufw allow {target if target else '22'} # Allow port/service",
335
+ f"sudo ufw deny {target if target else '23'} # Block port/service",
336
+ "sudo ufw allow ssh # Allow SSH service",
337
+ "sudo ufw allow 80/tcp # Allow HTTP",
338
+ "sudo ufw allow 443/tcp # Allow HTTPS",
339
+ "sudo ufw allow from 192.168.1.0/24 # Allow from subnet",
340
+ "",
341
+ "# UFW advanced rules:",
342
+ "sudo ufw delete allow 80 # Remove rule",
343
+ "sudo ufw insert 1 allow 22 # Insert rule at position",
344
+ "sudo ufw logging on # Enable logging",
345
+ "",
346
+ "# iptables (advanced):",
347
+ "sudo iptables -L # List iptables rules",
348
+ "sudo iptables -L -n # List with numeric output",
349
+ "sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT # Allow HTTP",
350
+ "sudo iptables -A INPUT -p tcp --dport 22 -j DROP # Block SSH",
351
+ "",
352
+ "# Save iptables rules:",
353
+ "sudo iptables-save > /etc/iptables/rules.v4 # Save IPv4 rules",
354
+ "sudo ip6tables-save > /etc/iptables/rules.v6 # Save IPv6 rules"
355
+ ]
356
+
357
+ result = "\n".join(commands)
358
+ return [TextContent(type="text", text=result)]
359
+
360
+ # ============================================================================
361
+ # MCP SERVER CONFIGURATION
362
+ # ============================================================================
363
+
364
+ @mcp_app.list_tools()
365
+ async def list_tools():
366
+ """List all available Linux system administration tools"""
367
+ return [
368
+ Tool(
369
+ name="user_management",
370
+ description="Generate Linux user management commands for creating, deleting, and managing users and groups",
371
+ inputSchema={
372
+ "type": "object",
373
+ "properties": {
374
+ "action": {
375
+ "type": "string",
376
+ "enum": ["add_user_to_group", "create_user", "delete_user", "list_groups"],
377
+ "description": "User management action to perform"
378
+ },
379
+ "username": {
380
+ "type": "string",
381
+ "description": "Username for the operation"
382
+ },
383
+ "groupname": {
384
+ "type": "string",
385
+ "description": "Group name for the operation"
386
+ },
387
+ "sudo": {
388
+ "type": "boolean",
389
+ "default": True,
390
+ "description": "Whether to use sudo for privileged operations"
391
+ }
392
+ },
393
+ "required": ["action"]
394
+ }
395
+ ),
396
+ Tool(
397
+ name="file_permissions",
398
+ description="Generate file permission and ownership commands for chmod, chown, and permission discovery",
399
+ inputSchema={
400
+ "type": "object",
401
+ "properties": {
402
+ "action": {
403
+ "type": "string",
404
+ "enum": ["chmod", "chown", "find_permissions"],
405
+ "description": "File permission action to perform"
406
+ },
407
+ "path": {
408
+ "type": "string",
409
+ "description": "File or directory path"
410
+ },
411
+ "permissions": {
412
+ "type": "string",
413
+ "description": "Permission mode (e.g., 755, 644, u+x)"
414
+ },
415
+ "owner": {
416
+ "type": "string",
417
+ "description": "Owner username for chown operations"
418
+ },
419
+ "recursive": {
420
+ "type": "boolean",
421
+ "default": False,
422
+ "description": "Apply operation recursively"
423
+ }
424
+ },
425
+ "required": ["action", "path"]
426
+ }
427
+ ),
428
+ Tool(
429
+ name="system_commands",
430
+ description="Generate common Linux system administration commands for processes, disk, network, services, and firewall",
431
+ inputSchema={
432
+ "type": "object",
433
+ "properties": {
434
+ "category": {
435
+ "type": "string",
436
+ "enum": ["process", "disk", "network", "service", "firewall"],
437
+ "description": "Category of system commands"
438
+ },
439
+ "action": {
440
+ "type": "string",
441
+ "description": "Specific action within the category"
442
+ },
443
+ "target": {
444
+ "type": "string",
445
+ "description": "Target for the operation (service name, hostname, etc.)"
446
+ }
447
+ },
448
+ "required": ["category"]
449
+ }
450
+ )
451
+ ]
452
+
453
+ @mcp_app.call_tool()
454
+ async def call_tool(name: str, arguments: dict):
455
+ """Handle tool calls"""
456
+ try:
457
+ if name == "user_management":
458
+ return handle_user_management(arguments)
459
+ elif name == "file_permissions":
460
+ return handle_file_permissions(arguments)
461
+ elif name == "system_commands":
462
+ return handle_system_commands(arguments)
463
+ else:
464
+ return [TextContent(type="text", text=f"Unknown tool: {name}")]
465
+ except Exception as e:
466
+ return [TextContent(type="text", text=f"Error: {str(e)}")]
467
+
468
+ # ============================================================================
469
+ # FASTAPI WEB ENDPOINTS
470
+ # ============================================================================
471
+
472
+ @app.get("/")
473
+ async def root():
474
+ """Status endpoint"""
475
+ return {
476
+ "service": "linux-mcp-server",
477
+ "status": "running",
478
+ "mcp_endpoint": "/mcp/sse",
479
+ "tools": 3,
480
+ "categories": ["user_management", "file_permissions", "system_commands"]
481
+ }
482
+
483
+ @app.get("/health")
484
+ async def health():
485
+ """Health check"""
486
+ return {"status": "healthy", "service": "linux-mcp-server"}
487
+
488
+ @app.get("/mcp/sse")
489
+ async def mcp_sse_endpoint(request: Request):
490
+ """MCP SSE endpoint for agent connections"""
491
+
492
+ async def event_stream():
493
+ try:
494
+ while True:
495
+ if await request.is_disconnected():
496
+ break
497
+
498
+ yield f"data: {json.dumps({'type': 'keepalive'})}\n\n"
499
+ await asyncio.sleep(30)
500
+
501
+ except asyncio.CancelledError:
502
+ pass
503
+
504
+ return StreamingResponse(
505
+ event_stream(),
506
+ media_type="text/event-stream",
507
+ headers={
508
+ "Cache-Control": "no-cache",
509
+ "Connection": "keep-alive",
510
+ "Access-Control-Allow-Origin": "*",
511
+ }
512
+ )
513
+
514
+ def main():
515
+ """Main entry point"""
516
+ port = int(os.getenv("PORT", 7860))
517
+
518
+ print(f"πŸš€ Starting Linux MCP Server on port {port}")
519
+ print(f"πŸ“Š Status: http://localhost:{port}/")
520
+ print(f"πŸ”— MCP SSE: http://localhost:{port}/mcp/sse")
521
+ print(f"πŸ› οΈ Tools: User Management, File Permissions, System Commands")
522
+
523
+ uvicorn.run(app, host="0.0.0.0", port=port, log_level="info")
524
+
525
+ if __name__ == "__main__":
526
+ main()