claude-configs/skills/proxmox/mcp_server.py
Cal Corum 8a1d15911f Initial commit: Claude Code configuration backup
Version control Claude Code configuration including:
- Global instructions (CLAUDE.md)
- User settings (settings.json)
- Custom agents (architect, designer, engineer, etc.)
- Custom skills (create-skill templates and workflows)

Excludes session data, secrets, cache, and temporary files per .gitignore.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-03 16:34:21 -06:00

480 lines
17 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Proxmox MCP Server
Exposes Proxmox operations as MCP tools for Claude Code
"""
import json
import sys
from typing import Any, Dict, List
from proxmox_client import ProxmoxClient
def create_mcp_server():
"""Create MCP server configuration"""
client = ProxmoxClient()
# Define MCP tools
tools = [
{
"name": "proxmox_list_vms",
"description": "List all virtual machines with their current status, resource usage (CPU, memory), and configuration",
"inputSchema": {
"type": "object",
"properties": {
"node": {
"type": "string",
"description": "Proxmox node name (optional, defaults to configured node)"
}
}
}
},
{
"name": "proxmox_get_vm",
"description": "Get detailed information about a specific VM including configuration, status, and resource usage",
"inputSchema": {
"type": "object",
"properties": {
"vmid": {
"type": "integer",
"description": "VM ID number"
},
"node": {
"type": "string",
"description": "Proxmox node name (optional)"
}
},
"required": ["vmid"]
}
},
{
"name": "proxmox_start_vm",
"description": "Start a virtual machine",
"inputSchema": {
"type": "object",
"properties": {
"vmid": {
"type": "integer",
"description": "VM ID to start"
},
"node": {
"type": "string",
"description": "Proxmox node name (optional)"
}
},
"required": ["vmid"]
}
},
{
"name": "proxmox_stop_vm",
"description": "Stop a virtual machine (graceful shutdown or force)",
"inputSchema": {
"type": "object",
"properties": {
"vmid": {
"type": "integer",
"description": "VM ID to stop"
},
"force": {
"type": "boolean",
"description": "Force immediate shutdown (default: false)",
"default": False
},
"node": {
"type": "string",
"description": "Proxmox node name (optional)"
}
},
"required": ["vmid"]
}
},
{
"name": "proxmox_restart_vm",
"description": "Restart a virtual machine",
"inputSchema": {
"type": "object",
"properties": {
"vmid": {
"type": "integer",
"description": "VM ID to restart"
},
"node": {
"type": "string",
"description": "Proxmox node name (optional)"
}
},
"required": ["vmid"]
}
},
{
"name": "proxmox_create_snapshot",
"description": "Create a snapshot of a VM for backup or before making changes",
"inputSchema": {
"type": "object",
"properties": {
"vmid": {
"type": "integer",
"description": "VM ID"
},
"snapname": {
"type": "string",
"description": "Snapshot name (e.g., 'before-upgrade')"
},
"description": {
"type": "string",
"description": "Human-readable description",
"default": ""
},
"vmstate": {
"type": "boolean",
"description": "Include VM RAM state (for running VMs)",
"default": False
},
"node": {
"type": "string",
"description": "Proxmox node name (optional)"
}
},
"required": ["vmid", "snapname"]
}
},
{
"name": "proxmox_list_snapshots",
"description": "List all snapshots for a VM",
"inputSchema": {
"type": "object",
"properties": {
"vmid": {
"type": "integer",
"description": "VM ID"
},
"node": {
"type": "string",
"description": "Proxmox node name (optional)"
}
},
"required": ["vmid"]
}
},
{
"name": "proxmox_rollback_snapshot",
"description": "Rollback a VM to a previous snapshot",
"inputSchema": {
"type": "object",
"properties": {
"vmid": {
"type": "integer",
"description": "VM ID"
},
"snapname": {
"type": "string",
"description": "Snapshot name to rollback to"
},
"node": {
"type": "string",
"description": "Proxmox node name (optional)"
}
},
"required": ["vmid", "snapname"]
}
},
{
"name": "proxmox_delete_snapshot",
"description": "Delete a VM snapshot",
"inputSchema": {
"type": "object",
"properties": {
"vmid": {
"type": "integer",
"description": "VM ID"
},
"snapname": {
"type": "string",
"description": "Snapshot name to delete"
},
"node": {
"type": "string",
"description": "Proxmox node name (optional)"
}
},
"required": ["vmid", "snapname"]
}
},
{
"name": "proxmox_clone_vm",
"description": "Clone an existing VM to create a new one",
"inputSchema": {
"type": "object",
"properties": {
"vmid": {
"type": "integer",
"description": "Source VM ID to clone from"
},
"newid": {
"type": "integer",
"description": "New VM ID for the clone"
},
"name": {
"type": "string",
"description": "Name for the cloned VM (optional)"
},
"full": {
"type": "boolean",
"description": "Create full clone (true) or linked clone (false)",
"default": True
},
"node": {
"type": "string",
"description": "Proxmox node name (optional)"
}
},
"required": ["vmid", "newid"]
}
},
{
"name": "proxmox_get_node_status",
"description": "Get Proxmox node status including CPU, memory, storage, and uptime",
"inputSchema": {
"type": "object",
"properties": {
"node": {
"type": "string",
"description": "Proxmox node name (optional)"
}
}
}
},
{
"name": "proxmox_list_containers",
"description": "List all LXC containers",
"inputSchema": {
"type": "object",
"properties": {
"node": {
"type": "string",
"description": "Proxmox node name (optional)"
}
}
}
},
{
"name": "proxmox_start_container",
"description": "Start an LXC container",
"inputSchema": {
"type": "object",
"properties": {
"vmid": {
"type": "integer",
"description": "Container ID to start"
},
"node": {
"type": "string",
"description": "Proxmox node name (optional)"
}
},
"required": ["vmid"]
}
},
{
"name": "proxmox_stop_container",
"description": "Stop an LXC container",
"inputSchema": {
"type": "object",
"properties": {
"vmid": {
"type": "integer",
"description": "Container ID to stop"
},
"node": {
"type": "string",
"description": "Proxmox node name (optional)"
}
},
"required": ["vmid"]
}
},
{
"name": "proxmox_list_storage",
"description": "List all storage pools and their capacity",
"inputSchema": {
"type": "object",
"properties": {
"node": {
"type": "string",
"description": "Proxmox node name (optional)"
}
}
}
}
]
return {
"tools": tools,
"client": client
}
def handle_tool_call(tool_name: str, arguments: Dict[str, Any], client: ProxmoxClient) -> Dict[str, Any]:
"""Handle MCP tool calls"""
try:
if tool_name == "proxmox_list_vms":
vms = client.get_all_vms_status(arguments.get("node"))
return {"content": [{"type": "text", "text": json.dumps(vms, indent=2)}]}
elif tool_name == "proxmox_get_vm":
vm = client.get_vm(arguments["vmid"], arguments.get("node"))
return {"content": [{"type": "text", "text": json.dumps(vm, indent=2)}]}
elif tool_name == "proxmox_start_vm":
upid = client.start_vm(arguments["vmid"], arguments.get("node"))
return {"content": [{"type": "text", "text": f"VM {arguments['vmid']} starting. Task ID: {upid}"}]}
elif tool_name == "proxmox_stop_vm":
upid = client.stop_vm(
arguments["vmid"],
arguments.get("node"),
arguments.get("force", False)
)
action = "Force stopping" if arguments.get("force") else "Stopping"
return {"content": [{"type": "text", "text": f"{action} VM {arguments['vmid']}. Task ID: {upid}"}]}
elif tool_name == "proxmox_restart_vm":
upid = client.restart_vm(arguments["vmid"], arguments.get("node"))
return {"content": [{"type": "text", "text": f"Restarting VM {arguments['vmid']}. Task ID: {upid}"}]}
elif tool_name == "proxmox_create_snapshot":
upid = client.create_snapshot(
arguments["vmid"],
arguments["snapname"],
arguments.get("description", ""),
arguments.get("vmstate", False),
arguments.get("node")
)
return {"content": [{"type": "text", "text": f"Creating snapshot '{arguments['snapname']}' for VM {arguments['vmid']}. Task ID: {upid}"}]}
elif tool_name == "proxmox_list_snapshots":
snapshots = client.list_snapshots(arguments["vmid"], arguments.get("node"))
return {"content": [{"type": "text", "text": json.dumps(snapshots, indent=2)}]}
elif tool_name == "proxmox_rollback_snapshot":
upid = client.rollback_snapshot(
arguments["vmid"],
arguments["snapname"],
arguments.get("node")
)
return {"content": [{"type": "text", "text": f"Rolling back VM {arguments['vmid']} to snapshot '{arguments['snapname']}'. Task ID: {upid}"}]}
elif tool_name == "proxmox_delete_snapshot":
upid = client.delete_snapshot(
arguments["vmid"],
arguments["snapname"],
arguments.get("node")
)
return {"content": [{"type": "text", "text": f"Deleting snapshot '{arguments['snapname']}' from VM {arguments['vmid']}. Task ID: {upid}"}]}
elif tool_name == "proxmox_clone_vm":
upid = client.clone_vm(
arguments["vmid"],
arguments["newid"],
arguments.get("name"),
arguments.get("node"),
arguments.get("full", True)
)
clone_type = "full" if arguments.get("full", True) else "linked"
return {"content": [{"type": "text", "text": f"Cloning VM {arguments['vmid']} to {arguments['newid']} ({clone_type} clone). Task ID: {upid}"}]}
elif tool_name == "proxmox_get_node_status":
status = client.get_node_status(arguments.get("node"))
return {"content": [{"type": "text", "text": json.dumps(status, indent=2)}]}
elif tool_name == "proxmox_list_containers":
containers = client.list_containers(arguments.get("node"))
return {"content": [{"type": "text", "text": json.dumps(containers, indent=2)}]}
elif tool_name == "proxmox_start_container":
upid = client.start_container(arguments["vmid"], arguments.get("node"))
return {"content": [{"type": "text", "text": f"Starting container {arguments['vmid']}. Task ID: {upid}"}]}
elif tool_name == "proxmox_stop_container":
upid = client.stop_container(arguments["vmid"], arguments.get("node"))
return {"content": [{"type": "text", "text": f"Stopping container {arguments['vmid']}. Task ID: {upid}"}]}
elif tool_name == "proxmox_list_storage":
storage = client.list_storage(arguments.get("node"))
return {"content": [{"type": "text", "text": json.dumps(storage, indent=2)}]}
else:
return {"content": [{"type": "text", "text": f"Unknown tool: {tool_name}"}], "isError": True}
except Exception as e:
return {"content": [{"type": "text", "text": f"Error: {str(e)}"}], "isError": True}
def main():
"""MCP stdio server main loop"""
server = create_mcp_server()
client = server["client"]
tools = server["tools"]
# Read messages from stdin
for line in sys.stdin:
try:
message = json.loads(line)
if message.get("method") == "tools/list":
response = {
"jsonrpc": "2.0",
"id": message.get("id"),
"result": {"tools": tools}
}
print(json.dumps(response), flush=True)
elif message.get("method") == "tools/call":
params = message.get("params", {})
tool_name = params.get("name")
arguments = params.get("arguments", {})
result = handle_tool_call(tool_name, arguments, client)
response = {
"jsonrpc": "2.0",
"id": message.get("id"),
"result": result
}
print(json.dumps(response), flush=True)
elif message.get("method") == "initialize":
response = {
"jsonrpc": "2.0",
"id": message.get("id"),
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": {}
},
"serverInfo": {
"name": "proxmox-mcp-server",
"version": "1.0.0"
}
}
}
print(json.dumps(response), flush=True)
except Exception as e:
error_response = {
"jsonrpc": "2.0",
"id": message.get("id") if 'message' in locals() else None,
"error": {
"code": -32603,
"message": str(e)
}
}
print(json.dumps(error_response), flush=True)
if __name__ == "__main__":
main()