Proxmox MCP Server
Manages any Proxmox VE cluster through the official PVE API, providing 22 tools for cluster status, VM/CT management, storage, snapshots, and provisioning (start/stop/reboot/suspend/resume VMs, create QEMU VMs, clone, snapshot, storage listing, node stats, and more).
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Proxmox MCP Servershow me the current status of all VMs and containers on the cluster"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Proxmox MCP Server
MCP server that manages any Proxmox VE cluster through the official PVE API — 22 tools for cluster status, VMs/CTs, storage, snapshots, and provisioning. Works over stdio (Hermes / Claude / any MCP client) or HTTP (StreamableHTTP) with optional OAuth for Gemini.
Configuration
Point it at your server with either a YAML config file or environment
variables. Env vars always win. See proxmox-mcp.example.yaml for a fully
commented template (including multi-server).
# 1) config file — copy the example and fill it in
cp proxmox-mcp.example.yaml proxmox-mcp.yaml
# pve_url: "https://pve.yourhost:8006"
# pve_token: "root@pam!mcp=<secret>" # or pve_user + pve_password
# 2) …or env vars (equivalent)
export PVE_URL="https://pve.yourhost:8006"
export PVE_TOKEN="root@pam!mcp=<secret>" # preferred: API token
# export PVE_USER="root@pam" # fallback: password auth
# export PVE_PASSWORD="..."Config file discovery order: $PROXMOX_MCP_CONFIG → ./proxmox-mcp.yaml →
~/.config/proxmox-mcp/config.yaml.
Key | Env | Notes |
|
|
|
|
|
|
|
| fallback auth |
|
|
|
|
|
|
|
| DNS-rebinding allowlist (ngrok) |
|
|
|
|
| external base URL (OAuth issuer) |
|
| static token for |
|
| bind addr, default |
Multi-server: define servers: {name: {…}, …} + default_server, pick at
runtime with PROXMOX_SERVER=name. MCP-side keys (mcp_*) stay global.
Related MCP server: Proxmox MCP Server
Run (stdio)
.venv/bin/python server.py # needs PVE_URL + creds from config or envTools (22)
Read-only
cluster_status— version + nodes + all VMs/CTslist_nodes,node_stats(cpu/mem/disk/net per node)list_vms,list_containers,vm_status,vm_configlist_storage,list_snapshots,list_pools
Control (safe)
vm_start,vm_shutdown,vm_reboot,vm_suspend,vm_resumevm_snapshot,vm_snapshot_delete
Control (destructive — require explicit require_confirm='YES')
vm_stop(hard kill),vm_delete(permanent + disks)node_restart(reboots a physical node)
Provisioning
vm_create_qemu(vmid, name, node, cores, memory, disk_size, storage, iso, bridge, start)vm_clone(full clone to new VMID)
Hermes / Claude registration
Point any stdio MCP client at server.py. Config goes in the client's env,
or in proxmox-mcp.yaml next to the repo. Hermes example
(~/.hermes/config.yaml):
mcp_servers:
proxmox:
command: "/path/to/proxmox-mcp/.venv/bin/python"
args: ["/path/to/proxmox-mcp/server.py"]
env:
PVE_URL: "https://pve.yourhost:8006"
PVE_TOKEN: "user@realm!tokenid=secret"
timeout: 60
connect_timeout: 30Requires a Hermes gateway restart to pick up new MCP servers (no hot-reload).
Claude Desktop: claude_desktop_config.json, same command/args shape.
Install
python -m venv .venv && .venv/bin/pip install -r requirements.txtTest
.venv/bin/python test_client.py # stdio: lists tools, calls cluster_status/list_vms/list_storage/list_nodesHTTP mode (StreamableHTTP — for ngrok / LAN / remote clients)
Configure via proxmox-mcp.yaml (recommended) or env vars:
# proxmox-mcp.yaml
pve_url: "https://pve.yourhost:8006"
pve_token: "root@pam!mcp=<secret>"
pve_readonly: true # public endpoint = inspection only
mcp_http_host: "127.0.0.1"
mcp_http_port: 8766
mcp_http_token: "change-me" # static bearer token for /health# start the local HTTP endpoint
.venv/bin/python http_entry.py
# → http://127.0.0.1:8766/mcp (health: /health, bearer-token protected)
# tunnel it publicly
/snap/bin/ngrok http 8766 --log stdout > ngrok.log 2>&1
# public URL: https://<random>.ngrok-free.app (MCP endpoint: /mcp)
# IMPORTANT: restart http_entry.py AFTER the tunnel is up so the OAuth issuer
# and DNS-rebinding allowlist use the public host (env example):
MCP_ALLOWED_HOSTS=<ngrok-host> MCP_OAUTH=1 MCP_PUBLIC_URL=https://<ngrok-host>.ngrok-free.app \
.venv/bin/python http_entry.pySecurity model:
pve_readonly: truedisables all 12 mutating tools server-side — the public URL can only read. Flip tofalseonly if you truly want remote control.mcp_http_tokenguards/health(ops-only, not the MCP endpoint).The Proxmox token itself never crosses the tunnel (lives server-side only).
Test:
MCP_HTTP_TOKEN=<token> .venv/bin/python http_test.py https://<host>.ngrok-free.app/mcpOAuth mode (required for Gemini)
Gemini only connects to MCP servers that support standard OAuth. Enable it
in the config file (mcp_oauth: true, mcp_public_url, mcp_allowed_hosts)
or with env vars:
# tunnel must already be up
MCP_OAUTH=1 \
MCP_PUBLIC_URL=https://<ngrok-host>.ngrok-free.app \
MCP_ALLOWED_HOSTS=<ngrok-host> \
PVE_READONLY=1 MCP_HTTP_TOKEN=<token> \
.venv/bin/python http_entry.pyThe OAuth Authorization Server then serves:
/.well-known/oauth-authorization-server(RFC 8414 metadata)/authorize— interactive HTML consent page for browsers (Google's account-linking UI requires a rendered grant page)/token(authorization-code + PKCE, refresh tokens 30d, rotated)/register(dynamic client registration — Gemini registers itself, no client id/secret to provision by hand)/revoke
Test the whole flow (register → PKCE authorize → token → MCP call → refresh):
.venv/bin/python oauth_test.py https://<host>.ngrok-free.app # SDK-style client
.venv/bin/python google_flow_test.py https://<host>.ngrok-free.app # Google/OpenAuth-exact (Basic-only auth)
.venv/bin/python browser_flow_test.py https://<host>.ngrok-free.app # browser consent-page flowNotes:
Everything is in-memory — server restart invalidates clients/tokens; clients re-register automatically.
/healthstays behind the staticmcp_http_token(ops-only, not MCP).Non-browser authorize requests still get a plain 302 (no consent HTML).
The OAuth metadata is served by
http_entry.py, not the SDK, because the SDK hardcodestoken_endpoint_auth_methods_supportedwithout"none"(public client / PKCE) — Gemini validates that list before registering.Consent is effectively auto-approve (no login); anyone who can reach /authorize with a registered client_id gets a code, but codes require the PKCE verifier. Fine for a personal tunnel; reconsider if shared.
Pitfalls (for future edits)
mcpSDK v2.x removedmcp.server.fastmcp— pinmcp<2(venv has 1.29.0).proxmoxer2.x usesservice="PVE"(not"proxmox"), needsrequestsinstalled.Token format for proxmoxer:
user='moritz@pve',token_name='moritz',token_value=secret.mcp 1.29 HTTP client yields a 3-tuple
(read, write, get_session_id)— unpack 3, not 2.streamable_http_app()must be the top-level ASGI app — Mounting it inside another Starlette app skips its lifespan and every request fails with "Task group is not initialized".Behind ngrok you get 421 "Invalid Host header" unless you pass
MCP_ALLOWED_HOSTS=<host>(DNS-rebinding protection).ngrok on this box is a snap: use the absolute path
/snap/bin/ngrokin background shells (PATH differs), and free-tier may needngrok-skip-browser-warning: true+http2=Falseon the client./auth/password-login-style auth is NOT used here; this is bearer-token auth.OAuth
expires_atmust be an int (int(time.time())) — pydantic rejects floats.authorize()must never passscopes=NoneintoAuthorizationCode— Gemini sends noscopeparam; fall back to the client's registered scope. Missing this caused a 500 at account-link time (fixed inoauth_provider.py).Google/OpenAuth sends
client_idONLY in theAuthorization: Basicheader at/token(RFC 6749 §2.3.1). The mcp SDK'sClientAuthenticatorAND itsTokenHandlerboth requireclient_idin the form body → every Gemini token exchange failed withunauthorized_client: Missing client_id(laterinvalid_request: authorization_code.client_id: Field required). Fixed inhttp_entry.py: patchedClientAuthenticator.authenticate_requestwith a Basic-header fallback + custom/tokenendpoint (our Router handles it; injects the resolved client_id into the form before model validation). Re-verify both against the SDK on upgrade.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityDmaintenanceEnables comprehensive management of Proxmox Virtual Environment through 35 tools for VMs, containers, storage, backups, snapshots, and cluster operations via the Proxmox VE API.3722MIT
- AlicenseCqualityDmaintenanceEnables management of Proxmox VE infrastructure through natural language, providing 120+ tools to control virtual machines, containers, storage, cluster resources, users, and network configurations via the Proxmox API.10037MIT
- Alicense-qualityDmaintenanceEnables comprehensive management of Proxmox virtualization environments, including VM and container lifecycle, snapshots, backups, monitoring, and OpenAPI integration.14MIT
- Flicense-qualityDmaintenanceEnables management of Proxmox VE environments via the Proxmox API, including node listing, guest management, and VM/LXC creation with Cloud-Init.
Related MCP Connectors
Official Sevalla MCP — full PaaS API access through just 2 tools.
Tailscale device, route, DNS, key, user, and ACL management over MCP and CLI.
Production-grade cryptography toolkit with 31 MCP tools for classical, PQC, and KMS workflows.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/mr-mister007/proxmox-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server