Console & logging

This commit is contained in:
canvrno
2025-02-18 22:30:50 -07:00
parent 4aab2c838b
commit b0be553b27
3 changed files with 99 additions and 20 deletions

View File

@@ -12,9 +12,7 @@ A Python-based Model Context Protocol (MCP) server for interacting with Proxmox
- Type-safe implementation with Pydantic
- Full integration with Claude Desktop
## Installation for Cline Users
If you're using this MCP server with Cline, follow these steps for installation:
## Installation
1. Create a directory for your MCP servers (if you haven't already):
```bash
@@ -31,7 +29,7 @@ If you're using this MCP server with Cline, follow these steps for installation:
pip install -e "proxmox-mcp[dev]"
```
3. Create your configuration:
3. Create and verify your configuration:
```bash
# Create config directory
mkdir proxmox-config
@@ -42,7 +40,7 @@ If you're using this MCP server with Cline, follow these steps for installation:
```json
{
"proxmox": {
"host": "your-proxmox-host",
"host": "your-proxmox-host", # Must be a valid hostname or IP
"port": 8006,
"verify_ssl": true,
"service": "PVE"
@@ -60,15 +58,16 @@ If you're using this MCP server with Cline, follow these steps for installation:
}
```
4. Install in Claude Desktop:
```bash
cd ~/Documents/Cline/MCP
mcp install proxmox-mcp/src/proxmox_mcp/server.py \
--name "Proxmox Manager" \
-v PROXMOX_MCP_CONFIG=./proxmox-config/config.json
```
Important: Verify your configuration:
- Ensure the config file exists at the specified path
- The `host` field must be properly set to your Proxmox server's hostname or IP address
- You can validate your config by running:
```bash
python3 -c "import json; print(json.load(open('config.json'))['proxmox']['host'])"
```
- This should print your Proxmox host address. If it prints an empty string or raises an error, your config needs to be fixed.
5. The server will now be available in Cline with these tools:
4. The server provides these tools:
- `get_nodes`: List all nodes in the cluster
- `get_node_status`: Get detailed status of a node
- `get_vms`: List all VMs

View File

@@ -98,12 +98,25 @@ class ProxmoxMCPServer:
def _setup_logging(self) -> None:
"""Configure logging based on settings."""
import os
# Convert relative path to absolute
log_file = self.config.logging.file
if log_file and not os.path.isabs(log_file):
log_file = os.path.join(os.getcwd(), log_file)
# Configure root logger
logging.basicConfig(
level=getattr(logging, self.config.logging.level.upper()),
format=self.config.logging.format,
filename=self.config.logging.file,
handlers=[
logging.FileHandler(log_file),
logging.StreamHandler() # Also log to console
]
)
self.logger = logging.getLogger("proxmox-mcp")
self.logger.info(f"Logging initialized. File: {log_file}, Level: {self.config.logging.level}")
def _setup_proxmox(self) -> ProxmoxAPI:
"""Initialize Proxmox API connection."""

View File

@@ -40,17 +40,84 @@ class VMConsoleManager:
raise ValueError(f"VM {vmid} on node {node} is not running")
# Get VM's console
console = self.proxmox.nodes(node).qemu(vmid).agent.exec.post(
command=command
)
self.logger.info(f"Executing command on VM {vmid} (node: {node}): {command}")
# Get the API endpoint
# Use the guest agent exec endpoint
endpoint = self.proxmox.nodes(node).qemu(vmid).agent
self.logger.debug(f"Using API endpoint: {endpoint}")
# Execute the command using two-step process
try:
# Start command execution
self.logger.info("Starting command execution...")
try:
print(f"Executing command via agent: {command}")
exec_result = endpoint("exec").post(command=command)
print(f"Raw exec response: {exec_result}")
self.logger.info(f"Command started with result: {exec_result}")
except Exception as e:
self.logger.error(f"Failed to start command: {str(e)}")
raise RuntimeError(f"Failed to start command: {str(e)}")
if 'pid' not in exec_result:
raise RuntimeError("No PID returned from command execution")
pid = exec_result['pid']
self.logger.info(f"Waiting for command completion (PID: {pid})...")
# Add a small delay to allow command to complete
import asyncio
await asyncio.sleep(1)
# Get command output using exec-status
try:
print(f"Getting status for PID {pid}...")
console = endpoint("exec-status").get(pid=pid)
print(f"Raw exec-status response: {console}")
if not console:
raise RuntimeError("No response from exec-status")
except Exception as e:
self.logger.error(f"Failed to get command status: {str(e)}")
raise RuntimeError(f"Failed to get command status: {str(e)}")
self.logger.info(f"Command completed with status: {console}")
print(f"Command completed with status: {console}")
except Exception as e:
self.logger.error(f"API call failed: {str(e)}")
print(f"API call error: {str(e)}") # Print error for immediate feedback
raise RuntimeError(f"API call failed: {str(e)}")
self.logger.info(f"Raw API response type: {type(console)}")
self.logger.info(f"Raw API response: {console}")
print(f"Raw API response: {console}") # Print to stdout for immediate feedback
# Handle different response structures
if isinstance(console, dict):
# Handle exec-status response format
output = console.get("out-data", "")
error = console.get("err-data", "")
exit_code = console.get("exitcode", 0)
exited = console.get("exited", 0)
if not exited:
self.logger.warning("Command may not have completed")
else:
# Some versions might return data differently
self.logger.debug(f"Unexpected response type: {type(console)}")
output = str(console)
error = ""
exit_code = 0
self.logger.debug(f"Processed output: {output}")
self.logger.debug(f"Processed error: {error}")
self.logger.debug(f"Processed exit code: {exit_code}")
self.logger.debug(f"Executed command '{command}' on VM {vmid} (node: {node})")
return {
"success": True,
"output": console.get("out", ""),
"error": console.get("err", ""),
"exit_code": console.get("exitcode", 0)
"output": output,
"error": error,
"exit_code": exit_code
}
except ValueError: