Enhanced Proxmox MCP Server - A Model Context Protocol server for interacting with Proxmox hypervisors with advanced features including multi-target support, job management, metrics, and approval policies.
Static source inference · medium confidence · detected: Logging
Deprecated protocol patterns detected
Summary
This server exhibits pervasive, critical deficiencies in tool definition quality. Of 57 tools, 53 rely on external description constants that are not present in the provided source code (e.g., 'GET_NODES_DESC from tool definitions'). This makes it impossible to verify whether descriptions exist, meet quality baselines, or properly guide LLM tool selection. Only 4 tools (list_targets, get_containers, proxmox_code_search, proxmox_code_get_schema) have descriptions directly visible. Input schemas are largely absent from the visible source, only tools 1, 26, 55, 56, 57 show explicit schema definitions. The server exhibits no tool annotations (readOnlyHint, destructiveHint, idempotentHint), no error guidance patterns, no parameter-level descriptions for most tools, and no output schema documentation. While the tool names follow verb_noun conventions (get_vm_config, create_vm, delete_snapshot), the lack of verifiable descriptions and schemas places this server well into 'F' territory for definition quality. The risk classifications (READ_ONLY, WRITE, DESTRUCTIVE) suggest security awareness but are not surfaced to the LLM via annotations.
53 of 57 tools lack visible descriptions; descriptions are delegated to undefined constants (e.g., 'GET_NODES_DESC from tool definitions') that cannot be verified in the provided source.
Inline all tool descriptions directly into the tool registration code (src/proxmox_mcp/services/builtin_tool_plugins.py). Replace 'GET_NODES_DESC from tool definitions' with actual human-readable text like 'List all compute nodes in a Proxmox cluster. Returns node name, status (online/offline), CPU, memory, and disk usage.' (target 50 - 200 characters per pattern:tool-description baseline).
Define explicit input schemas for all 52 tools that currently lack them. Use JSON Schema with type, description, and enum/pattern constraints for each parameter. Example for get_nodes: { 'target': { 'type': 'string', 'description': 'Configured Proxmox target name (e.g., "production", "lab"). Required when multiple targets are configured.' } }.
Add tool annotations to all tools. Use the 'tools' array in the list_tools response to include 'readOnlyHint' for READ_ONLY tools, 'destructiveHint' for DESTRUCTIVE tools, and 'idempotentHint' where applicable. This enables the LLM to reason about safety and retry logic.
Document output schemas for all tools. In descriptions or in a separate schema map, specify what fields the response contains. Example: 'Returns an object with keys: {nodes: [{name, status, uptime, cpu_usage, memory_total, memory_used}], total_count, error?: string}'.
Add per-parameter descriptions to tools without them. For each required or optional parameter, provide a clear description in the schema. Example: 'vmid: integer, the unique VM identifier (e.g., 100). Required.'
Implement structured error responses that guide recovery. Return error codes and actionable messages. Example: 'error_code: "VM_NOT_FOUND", message: "VM with ID 999 not found on target \'production\'. Available VMs: 100 (web-server), 101 (db-master). Did you mean 100?"'
Spec posture evidence
Inferred effective spec: <=2025-11-25.
Relies on Logging (deprecated) - log to stderr or use OpenTelemetry
Input schemas are missing or not visible for 52 of 57 tools. Only list_targets, get_containers, proxmox_code_search, proxmox_code_get_schema, and proxmox_code_execute show explicit schemas.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint) are present in the visible code, despite clear risk classification (READ_ONLY, WRITE, DESTRUCTIVE) available in the metadata. The LLM cannot see which tools are safe to retry, which cause side effects, or which are destructive without explicit annotations.
No output schemas are documented in the visible source code. LLMs cannot plan downstream tool calls or extract chained data (e.g., after get_nodes, what fields will be returned to pass to get_node_status?). Response structures are opaque.
Parameter descriptions are absent or minimal. Only 5 tools (list_targets, get_containers, proxmox_code_search, proxmox_code_get_schema, proxmox_code_execute) show parameter-level descriptions. The LLM cannot infer whether 'target' means a hostname, config name, or ID without explicit guidance.
No error recovery guidance. Tool responses do not include actionable error messages (e.g., 'User not found. Try search_users() with a partial name.'). LLMs cannot self-correct on failures.
proxmox_code_execute accepts arbitrary Python code with a 64KB limit. No visible input sanitization, timeout protection, or sandboxing verification. This is a code-injection risk if the LLM is prompted maliciously or if user input flows into the 'code' parameter.
proxmox_code_execute
Secure proxmox_code_execute by: (1) validating code syntax before execution, (2) enforcing strict timeouts (e.g., 5 seconds), (3) logging all code execution with timestamps and user context, (4) restricting imports to a whitelist (e.g., json, math only), (5) disabling __import__ and eval() unless strictly necessary. Document these constraints in the tool description.
Add pagination support and response limits to list tools (list_jobs, list_backups, list_isos, etc.). Accept 'limit' (default 20, max 100) and 'offset' or 'cursor' parameters. Return total_count so the LLM knows if more results exist.
Create a discovery tool or resource that maps Proxmox capabilities and available tools to the LLM in a single call, reducing trial-and-error tool selection. proxmox_code_search partially addresses this but a dedicated 'describe_proxmox_topology' tool would help.
Add confirmation or dry-run support to destructive tools (delete_vm, delete_container, delete_backup, delete_iso, delete_snapshot). Either require an explicit 'confirm=true' parameter or return a confirmation request (MRTR pattern: result with type='input_required') asking the LLM to confirm before executing the deletion.