This guide covers how to configure, run, and operate ProxmoxMCP-Plus.
ProxmoxMCP-Plus can be used in two main ways:
MCP stdio mode: assistants launch the server directly and call tools over MCPMCP Streamable HTTP mode: the server exposes a native MCP endpoint at/mcpOpenAPI mode: FastAPI wraps the MCP server and exposes HTTP endpoints for other clients
The core tool set is the same in both modes. The difference is only the transport.
- Python 3.11 or newer
- Access to a Proxmox VE API endpoint
- A Proxmox API token with the permissions your workflows need
- Network access from the machine running ProxmoxMCP-Plus to the Proxmox API
Optional:
- SSH access from the MCP host to Proxmox nodes if you want LXC command execution tools
The main config file is proxmox-config/config.json.
Start from the example:
cp proxmox-config/config.example.json proxmox-config/config.jsonThe main sections are:
proxmox: host, port, TLS verification, service typeapi_tunnel: optional SSH local forward for the Proxmox APIauth: Proxmox API user and tokenlogging: log level, format, optional log filemcp: MCP host, port, transport, and optional transport Host/Origin allowlistssecurity: currently includesdev_modejobs: SQLite path for persistent job trackingcommand_policy: rules forexecute_*tools and high-risk mutating operationsssh: optional SSH settings for LXC command execution
If PROXMOX_MCP_CONFIG is not set or the file is missing, the loader falls back to environment variables.
Common variables:
PROXMOX_HOSTPROXMOX_PORTPROXMOX_USERPROXMOX_TOKEN_NAMEPROXMOX_TOKEN_VALUEPROXMOX_VERIFY_SSLPROXMOX_SERVICELOG_LEVELMCP_HOSTMCP_PORTMCP_TRANSPORTMCP_DNS_REBINDING_PROTECTIONMCP_ALLOWED_HOSTSMCP_ALLOWED_ORIGINSPROXMOX_DEV_MODECOMMAND_POLICY_MODEPROXMOX_JOBS_SQLITE_PATH
uv venv
uv pip install -e ".[dev]"
$env:PROXMOX_MCP_CONFIG="D:\\PycharmProject\\ProxmoxMCP-Plus\\proxmox-config\\config.json"
python main.pyIf startup succeeds, the server stays attached to stdio and waits for MCP clients.
Set the MCP transport to STREAMABLE_HTTP and bind to an address reachable by the client:
MCP_API_KEY="$(openssl rand -hex 32)" \
MCP_HOST=0.0.0.0 MCP_PORT=8000 MCP_TRANSPORT=STREAMABLE_HTTP \
python -m proxmox_mcp.serverThe MCP endpoint is:
http://<host>:8000/mcp
This is the correct target for MCP clients that support Streamable HTTP. It is separate from the OpenAPI service on port 8811.
Clients should send Authorization: Bearer <MCP_API_KEY>. If MCP_API_KEY is unset,
the endpoint remains unauthenticated for backward compatibility and the server logs a
security warning. PROXMOX_API_KEY does not protect this endpoint.
For reverse proxy deployments, configure the external hostnames explicitly instead of disabling DNS rebinding protection:
MCP_HOST=0.0.0.0 \
MCP_PORT=8000 \
MCP_TRANSPORT=STREAMABLE_HTTP \
MCP_API_KEY="$MCP_API_KEY" \
MCP_DNS_REBINDING_PROTECTION=true \
MCP_ALLOWED_HOSTS=mcp.example.com:*,localhost:* \
MCP_ALLOWED_ORIGINS=https://mcp.example.com \
python -m proxmox_mcp.serverYou can run the OpenAPI wrapper directly:
export PROXMOX_API_KEY="$(openssl rand -hex 32)"
python -m proxmox_mcp.openapi_proxy --host 0.0.0.0 --port 8811 -- python main.pyOpenAPI mode refuses to start without PROXMOX_API_KEY unless
PROXMOX_ALLOW_NO_AUTH=true is set for local unauthenticated development.
HTTP clients should send the key as Authorization: Bearer <PROXMOX_API_KEY>.
Available routes:
/returns basic service metadata/docsserves Swagger UI/openapi.jsonserves the generated schema/livezreturns minimal unauthenticated process liveness/readyzreturns503until the proxy is connected to the MCP backend, then200/healthis a readiness alias for/readyz/metricsexposes Prometheus-style request metrics/jobsexposes direct job query and control routes when a localJobStoreis available
The repository includes docker-compose.yml and Dockerfile.
Default Compose behavior:
- Builds the local image
- Mounts
./proxmox-configread-only into/app/proxmox-config - Exposes
8811 - Keeps OpenAPI mode as the default Docker runtime
- Sets
PROXMOX_MCP_CONFIG=/app/proxmox-config/config.json - Requires
PROXMOX_API_KEYfrom your shell or Compose.envfile - Adds a container liveness health check against
http://localhost:8811/livez
Start it with:
export PROXMOX_API_KEY="${PROXMOX_API_KEY:-$(openssl rand -hex 32)}"
docker compose up -d --buildTo run the native MCP Streamable HTTP service from Docker Compose:
export MCP_API_KEY="${MCP_API_KEY:-$(openssl rand -hex 32)}"
docker compose --profile mcp-http up -d proxmox-mcp-httpThen connect Streamable HTTP MCP clients to http://<docker-host>:8000/mcp and send
Authorization: Bearer <MCP_API_KEY>.
The same image can also be run directly:
docker run --rm -p 8000:8000 \
-e PROXMOX_MCP_MODE=mcp-http \
-e MCP_HOST=0.0.0.0 \
-e MCP_PORT=8000 \
-e MCP_TRANSPORT=STREAMABLE_HTTP \
-e MCP_API_KEY="$MCP_API_KEY" \
-v "$(pwd)/proxmox-config/config.json:/app/proxmox-config/config.json:ro" \
ghcr.io/rekklesna/proxmoxmcp-plus:latestBefore exposing the service to users:
- Confirm the Proxmox API token has only the permissions you intend to expose
- Keep
proxmox.verify_ssl=trueunless you are explicitly in development mode - Keep
security.dev_mode=falseoutside local testing - Set
PROXMOX_API_KEYfor OpenAPI mode; only usePROXMOX_ALLOW_NO_AUTH=truefor local unauthenticated development - Set
MCP_API_KEYfor every remotely reachable native MCP HTTP deployment - For MCP HTTP behind a proxy, keep
MCP_DNS_REBINDING_PROTECTION=trueand setMCP_ALLOWED_HOSTSto the exact public hostnames - Restrict ingress to networks you control
- Monitor
/livezfor process liveness and authenticated/healthor/readyzfor backend readiness - Monitor
/metricsif you scrape the service with Prometheus-compatible tooling - Persist the configured
jobs.sqlite_pathon durable storage if job history matters across restarts - Store logs somewhere persistent if you need auditability
There are two command execution paths:
execute_vm_command: uses QEMU Guest Agent inside VMsexecute_container_command: uses SSH to the Proxmox node and thenpct execinside containers
Container command execution is optional and only appears when an ssh section exists in the config.
For setup details, see Container Command Execution.
Asynchronous Proxmox actions now register a persistent job record. This applies to operations such as:
- VM create, start, stop, shutdown, reset, and delete
- container create, start, stop, restart, and delete
- snapshot create, delete, and rollback
- backup create, restore, and delete
- ISO download and delete
Operational guidance:
- Treat
job_idas the stable identifier you hand back to users, agents, and automation systems. - Treat
task_idorUPIDas Proxmox internals that may change after a retry. - Keep
jobs.sqlite_pathon a persistent volume in Docker or any long-lived service deployment. - Use
/jobs/{job_id}/pollor MCPpoll_jobto refresh progress from Proxmox. - Use
/jobs/{job_id}/retryonly after reviewinglast_error,result, andaudit_log.
After deployment, test in this order:
- Start the service and confirm there are no config validation errors
- Call read-only tools first:
get_nodes,get_vms,get_storage,get_cluster_status - In OpenAPI mode, confirm
/livezresponds and authenticated/healthand/docsrequests work - Confirm
/jobsresponds if you expect persistent job tracking - If you enabled SSH-backed container commands, confirm
execute_container_commandappears in the tool list - Only then test mutating tools such as create, start, delete, snapshot, or backup
- Application logging is configured under the
loggingsection main.pyprints early startup messages to stderr to make bootstrap failures visible- The OpenAPI wrapper reports
degradeduntil it is connected to the MCP subprocess - authenticated
/healthreports whether direct job routes are enabled in the OpenAPI process