Skip to main content
Glama
Proxmox MCP Server

Proxmox MCP Server

by ry-ops

✨ Highlights

  • 🧑‍🚀 Ask for a machine, get a running one. "Spin up a Kali VM" runs deploy_kali_vm: official image, checksum checked, installed unattended, address reported. Ubuntu, Windows 11 (TPM, Secure Boot, VirtIO) and Datacenter Manager work the same way. More

  • 🛰️ Every cluster through one connection. Point it at Proxmox Datacenter Manager and 89 tools reach every Proxmox VE cluster and Backup Server it manages, including cross-cluster migration. More

  • 🧰 The whole API, not a demo. 346 tools across 14 areas: nodes, QEMU VMs, LXC containers, storage and backup, cluster and HA, access control, firewall, disks, Ceph, ACME, SDN, notifications, pools and appliances.

  • 🧭 Workflows built in. Five MCP prompts, such as a cluster health check and a snapshot-change-verify routine, plus live cluster state as MCP resources.

  • 🖥️ Inside your guests too. Run commands through the QEMU guest agent (vm_agent_exec → vm_agent_exec_status) and read back the exit code and output.

  • 🔒 Look-but-don't-touch with one switch. PROXMOX_READ_ONLY=true refuses every write inside the server, and the client sees only the 151 read-only tools.

  • 🧯 Safe writes, protected guests. PROXMOX_NO_DESTRUCTIVE=true hides the 73 tools that delete, stop, wipe or roll back. PROXMOX_PROTECTED_VMIDS names guests no tool may change.

  • ⏳ Waits for the slow stuff. Clone, migrate, backup and restore take wait: true, and wait_for_task follows any task to the end, with the log when it fails.

  • 🎯 Load only what you use. PROXMOX_TOOLSETS=nodes,qemu,lxc,storage cuts the tool list from 346 to 127, and the context it costs by more than half. Results come back as compact JSON.

  • 🚦 Labelled for safe approval. Every tool carries MCP readOnlyHint and destructiveHint annotations, so clients can auto-approve reads and ask before a delete, stop or rollback.

  • 🆕 Current protocol, old clients welcome. Built on MCP SDK 2: clients on the stateless 2026-07-28 protocol and on every handshake version back to 2024-11-05 connect. Failed calls are flagged as errors, so clients show them as failures.

  • 🔑 API tokens or passwords. Tokens are recommended; pair them with a PVEAuditor role for monitoring.

  • 🌐 Local or on the network. Clients spawn it over stdio, or proxmox-mcp-server-http serves it over streamable HTTP. Give each client its own token and a role: read, operator or admin.

  • 📜 Every change on the record. PROXMOX_AUDIT_LOG writes one JSON line per write call: who, what, the outcome and the task, with secrets redacted.

  • 🐳 Runs anywhere. Use uv from a clone, or the multi-arch image on ghcr.io.

  • ♻️ Long sessions just work. Password logins renew their Proxmox ticket before it expires.

  • 🤝 A2A-ready. agent-card.json lists every tool, Proxmox VE and Datacenter Manager, by skill category, for agent-to-agent discovery.

  • 🛡️ Appliances. deploy_fortigate_vm builds a FortiGate-VM from Fortinet's KVM image, with WAN/LAN NICs on the bridges and VLANs you choose. deploy_cloud_vms builds cluster-ready VMs from a cloud image, with your SSH key and a static IP each, ready for k3s-mcp-server's create_cluster.

Related MCP server: ProxmoxEmCP

💬 Talk to your cluster

Once it's connected, just ask:

"Can you list all VMs in my Proxmox cluster?" "What's the status of VM 100 on node pve1?" "Create a snapshot called 'backup-2025' for VM 100 on pve1." "Show me the storage usage on all nodes." "List all running tasks in the cluster." "Clone VM 9000 to a new VM called web-02 and start it." "Run df -h inside VM 120 using the guest agent and show me the output." "Make three Ubuntu VMs on pve1 at 192.168.1.181–183 with my SSH key, ready for a K3s cluster."

There are more worked examples in USAGE.md.

🧭 Built-in prompts and resources

The server ships MCP prompts, ready-made workflows your client lists by name. In Claude Code, type / to see them, for example /mcp__proxmox__cluster_health_check.

Prompt

What it does

cluster_health_check

Read-only check of quorum, nodes, guests, failed tasks, HA and Ceph, reported by severity

capacity_report

Read-only storage, CPU and memory report with the biggest consumers; takes threshold_percent

safe_change

Snapshot a guest, make your change after your go-ahead, verify it, and offer a rollback; takes vmid and change

investigate_failed_tasks

Read-only: failed tasks, their logs, grouped by cause with fixes; takes hours

decommission_guest

Back up, stop and delete a guest, asking before every change; takes vmid

Prompts only guide the model. Every step is still an ordinary tool call, so roles, read-only mode and protected guests apply as usual.

MCP resources let a client attach live cluster state as context without spending tool calls:

Resource

Contents

proxmox://cluster/status

Quorum and node membership

proxmox://cluster/resources

Every node, VM, container and storage with live usage

proxmox://cluster/tasks

Recent tasks, with status

proxmox://nodes

Nodes with CPU, memory and uptime

proxmox://storage

Storage definitions

proxmox://nodes/{node}/status

One node in detail

proxmox://vms/{node}/{vmid}/config, proxmox://cts/{node}/{vmid}/config

One guest's configuration

Each resource is a single read, so resources work in read-only mode and for every role.

⚙️ How it works

  • Your MCP client (Claude Desktop, Claude Code, or anything that speaks MCP) starts the server and talks to it over stdio. Or you run it as a service and clients connect over streamable HTTP.

  • PROXMOX_TOOLSETS decides which API areas the server loads, and so which tools the client sees. Each tool is labelled read-only or destructive.

  • The server (Python, httpx, the MCP SDK) turns each tool call into a request to the Proxmox VE REST API over HTTPS on port 8006, authenticated with an API token or a password.

  • Read-only mode hides the write tools and is also checked in the server's HTTP client, so a refused write never leaves your machine.

🧰 346 tools, 14 areas

Area

Tools

Highlights

Nodes

38

Status, config, DNS/hosts/time, network interfaces, services, APT updates, syslog, power

QEMU VMs

46

Lifecycle, create/clone/delete, config, snapshots, migration, disk resize/move, RRD metrics, guest agent (vm_agent_exec, vm_agent_exec_status), VNC proxy

LXC containers

28

Lifecycle, create/clone/delete, config, snapshots, migration, resize

Storage & backup

14

Storage definitions, content and volumes, URL downloads, vzdump backup and restore

Cluster

37

Status, resources, options, log, tasks, HA resources/groups, replication, backup jobs

Access control

33

Users, API tokens, groups, roles, ACLs, realms

Firewall

31

Cluster and node rules, security groups, aliases, IP sets, macros, logs (per-VM and per-container rules are in the guest modules)

Disks

17

SMART, wipe, GPT init, LVM, LVM-thin, ZFS, directory storage

Ceph

33

Status, OSDs, monitors, managers, MDS, pools, flags, CRUSH

ACME & certificates

17

ACME accounts and plugins, node certificate ordering, custom certs

SDN

16

Zones, VNets, subnets, apply

Notifications

23

Gotify, sendmail, SMTP and webhook endpoints, matchers

Pools

5

Resource pool CRUD

Appliances

2

deploy_fortigate_vm: FortiGate-VM from Fortinet's KVM image, WAN/LAN NICs on chosen bridges and VLANs, sized for the free evaluation license. deploy_cloud_vms: VMs from a cloud image (e.g. Ubuntu's) with cloud-init user, SSH key and a static IP or DHCP each, disk grown and started

One more tool, wait_for_task, is loaded with every toolset. It waits for any Proxmox task to finish; see Waiting for long tasks.

Tool names follow the Proxmox API: list_*, get_*, create_*, update_*/set_*, delete_*, plus actions such as start_vm, migrate_container or apply_sdn. Each area is one module in proxmox_mcp/tools/. Your MCP client shows every tool with its full input schema, or you can list them all locally:

uv run python -c "from proxmox_mcp.server import ALL_TOOLS; print('\n'.join(sorted(t.name for t in ALL_TOOLS)))"

Handy recipes

Task

Tools

Inventory

list_nodes, get_cluster_resources, list_vms, list_containers, list_storage

VM lifecycle

start_vm, shutdown_vm, stop_vm, reboot_vm, create_vm, clone_vm, migrate_vm

Disk images

download_url_to_storage with content=import, then create_vm with scsi0=local-lvm:0,import-from=local:import/<image>, or import_vm_disk

Snapshots

create_vm_snapshot, list_vm_snapshots, rollback_vm_snapshot, delete_vm_snapshot

Monitoring

get_node_status, get_vm_status, get_vm_rrddata, get_node_storage_status

Tasks

list_cluster_tasks, list_node_tasks, get_task_status, get_task_log

Guest agent

vm_agent_exec (returns a PID), then vm_agent_exec_status for the exit code and output

Cluster nodes

download_url_to_storage for a cloud image with content=import, then deploy_cloud_vms; hand the IPs to k3s-mcp-server's create_cluster

🚀 Quick start

You need Python 3.10+ with uv, or Docker, and Proxmox VE 6.0 or later.

1. Make an API token. In the Proxmox UI, go to Datacenter → Permissions → API Tokens → Add. For full access, uncheck Privilege Separation so the token inherits your user's permissions. Copy the secret; you won't see it again. The full token ID looks like root@pam!automation.

2. Run the server with one of these:

curl -LsSf https://astral.sh/uv/install.sh | sh     # if you don't have uv
git clone https://github.com/ry-ops/proxmox-mcp-server && cd proxmox-mcp-server
./setup.sh                                          # or: uv sync

export PROXMOX_HOST="192.168.1.100"
export PROXMOX_USER="root@pam"
export PROXMOX_TOKEN_NAME="automation"
export PROXMOX_TOKEN_VALUE="your-token-here"
./test-connection.sh                                # checks connection, auth and permissions

A .env file in the project directory is loaded automatically; see .env.example.

A multi-arch image is published to GitHub Container Registry on every push to main and for each release tag:

docker pull ghcr.io/ry-ops/proxmox-mcp-server:latest   # or a version tag, e.g. :2.14.0
docker run -i --rm --env-file .env ghcr.io/ry-ops/proxmox-mcp-server:latest

MCP clients talk over stdio, so run the container interactively (-i). docker-compose.yaml does the same using your .env. To serve it over HTTP instead, see Run it over HTTP.

3. Connect your MCP client. For Claude Desktop, edit ~/Library/Application Support/Claude/claude_desktop_config.json on macOS, or %APPDATA%/Claude/claude_desktop_config.json on Windows:

{
  "mcpServers": {
    "proxmox": {
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/proxmox-mcp-server", "run", "proxmox-mcp-server"],
      "env": {
        "PROXMOX_HOST": "192.168.1.100",
        "PROXMOX_USER": "root@pam",
        "PROXMOX_TOKEN_NAME": "automation",
        "PROXMOX_TOKEN_VALUE": "your-token-value-here"
      }
    }
  }
}

Use the absolute path to your clone.

{
  "mcpServers": {
    "proxmox": {
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/proxmox-mcp-server", "run", "proxmox-mcp-server"],
      "env": {
        "PROXMOX_HOST": "192.168.1.100",
        "PROXMOX_USER": "root@pam",
        "PROXMOX_PASSWORD": "your-password-here"
      }
    }
  }
}
{
  "mcpServers": {
    "proxmox": {
      "command": "docker",
      "args": ["run", "-i", "--rm",
        "-e", "PROXMOX_HOST", "-e", "PROXMOX_USER", "-e", "PROXMOX_TOKEN_NAME", "-e", "PROXMOX_TOKEN_VALUE",
        "ghcr.io/ry-ops/proxmox-mcp-server:latest"],
      "env": {
        "PROXMOX_HOST": "192.168.1.100",
        "PROXMOX_USER": "root@pam",
        "PROXMOX_TOKEN_NAME": "automation",
        "PROXMOX_TOKEN_VALUE": "your-token-value-here"
      }
    }
  }
}
claude mcp add proxmox \
  -e PROXMOX_HOST=192.168.1.100 -e PROXMOX_USER=root@pam \
  -e PROXMOX_TOKEN_NAME=automation -e PROXMOX_TOKEN_VALUE=your-token-value-here \
  -- uv --directory /absolute/path/to/proxmox-mcp-server run proxmox-mcp-server

Add -e PROXMOX_TOOLSETS=nodes,qemu,lxc,storage to keep the tool list small. For a server you run over HTTP, see Run it over HTTP.

4. Restart your client completely (quit it, don't just close the window), and ask it to list your VMs. More detail in QUICKSTART.md and SETUP.md.

🏢 Several clusters

Run one server per cluster, each under its own name. Every cluster then gets its own credentials, toolsets, safety settings and audit log, and the assistant always knows which cluster a tool acts on, because the tool names carry the server name (mcp__proxmox-lab__start_vm and mcp__proxmox-prod__start_vm).

{
  "mcpServers": {
    "proxmox-lab": {
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/proxmox-mcp-server", "run", "proxmox-mcp-server"],
      "env": { "PROXMOX_HOST": "10.0.10.5", "PROXMOX_USER": "root@pam",
               "PROXMOX_TOKEN_NAME": "automation", "PROXMOX_TOKEN_VALUE": "lab-token" }
    },
    "proxmox-prod": {
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/proxmox-mcp-server", "run", "proxmox-mcp-server"],
      "env": { "PROXMOX_HOST": "10.0.20.5", "PROXMOX_USER": "mcp@pve",
               "PROXMOX_TOKEN_NAME": "readonly", "PROXMOX_TOKEN_VALUE": "prod-token",
               "PROXMOX_NO_DESTRUCTIVE": "true", "PROXMOX_PROTECTED_VMIDS": "100-110",
               "PROXMOX_TOOLSETS": "nodes,qemu,lxc,storage" }
    }
  }
}

Give each extra cluster only the toolsets it needs, because every server's tools count against the client's context. Over HTTP, run one proxmox-mcp-server-http per cluster on its own port.

🧑‍🚀 Deploy an OS by asking

Ask for a VM by name and the server does the rest: fetches the official image (checksum verified, cached on storage and reused), builds a small seed ISO with the first-boot settings, creates and starts the VM, waits until its guest agent answers, and reports how to reach it.

Tool

What you get

deploy_ubuntu_vm

Ubuntu 24.04 LTS from the official cloud image

deploy_kali_vm

Kali Linux from its official cloud image. It ships as .tar.xz, which Proxmox can't import, so the server repacks its sparse 25 GiB disk as qcow2 and uploads only the ≈1 GiB of data

deploy_pdm_vm

Proxmox Datacenter Manager on a Debian 13 VM, installed by cloud-init from the no-subscription repository; reports https://<ip>:8443 and the root@pam login

deploy_windows11_vm

Windows 11 installed unattended: q35, UEFI, Secure Boot keys, TPM 2.0, VirtIO disk and network, VirtIO guest tools. Uses a Windows 11 ISO already on storage (Microsoft has no stable download link); installs without a product key, so activation is yours

list_os_images

What's cached on the node, and with check_upstream, the latest official versions

Give an SSH public key, or the tool generates a password and returns it once (the audit log redacts passwords). The seed ISO holds those credentials, so it's deleted as soon as the guest is up. Each tool takes the usual sizing and network inputs (cores, memory, disk_gb, bridge, vlan, a static ip or DHCP) and wait_until_ready / ready_timeout. Windows Setup takes 20 to 40 minutes; with a shorter timeout the tool returns installing and says what to clean up after.

🛰️ Proxmox Datacenter Manager

If you run Proxmox Datacenter Manager (PDM), point the server at it and the pdm toolset loads: 89 tools that reach every Proxmox VE cluster and Proxmox Backup Server PDM manages, through one connection. It can run alongside a direct Proxmox VE connection, or on its own.

  • Across everything: resources, top consumers, tasks, pending updates and subscriptions for all remotes at once.

  • Per remote: nodes, guests, configs, snapshots, storage, firewall, tasks and metrics on Proxmox VE remotes; datastores, namespaces, snapshots and tasks on Backup Server remotes; SDN and Ceph.

  • Actions: start, stop, shut down and resume guests; create, delete, roll back and describe snapshots; migrate within a cluster or to another cluster (pdm_pve_remote_migrate_guest); stop tasks; refresh updates, task caches and metrics. Actions take wait.

Tools that cover both VMs and containers take type (qemu or lxc). Every input is checked against PDM's own schema before a request is sent.

"env": {
  "PDM_HOST": "10.0.0.20",
  "PDM_TOKEN_ID": "root@pam!proxmox-mcp",
  "PDM_TOKEN_SECRET": "…",
  "PDM_CA_BUNDLE": "/path/to/pdm-api.pem",
  "PDM_TLS_NAME": "pdm",
  "PDM_PROTECTED_GUESTS": "pve01:104,pve01:105"
}

Create the token in PDM under Configuration → Access Control → API Tokens, then give it roles under Permissions. PDM tokens start with no permissions, and never get more than their user:

Path

Role

Covers

/resource

Auditor for read-only, Administrator for actions

Every remote: reads need Resource.Audit; actions need Resource.Manage (power, snapshots, stopping tasks), Resource.Modify (refreshing package lists) and Resource.Migrate

/system

Auditor

pdm_node_status (the PDM host's own usage)

Seven tools reach endpoints PDM 1.1 allows only for root@pam logged in with a password, so with an API token they return 403 whatever its roles: pdm_pve_list_remotes and pdm_pbs_list_remotes (use pdm_list_remotes instead), pdm_metric_collection_status, pdm_collect_metrics, pdm_get_remote_rrddata, and the two EVPN lookups. Their descriptions say so, and when PDM refuses one, the error explains the elevated permissions it needs and what to use instead.

PDM can also protect a guest itself: a NoAccess entry for the token on /resource/<remote>/guest/<vmid> (for example /resource/pve01/guest/104) makes PDM refuse every call about that guest, whatever this server does. For PDM_CA_BUNDLE, copy /etc/proxmox-datacenter-manager/auth/api.pem from the PDM host. Its self-signed certificate names pdm and localhost, not the host's IP, so when PDM_HOST is an IP also set PDM_TLS_NAME=pdm: the certificate is checked against that name while the server connects to the IP.

Protected guests. VMIDs are only unique within a cluster, so PDM tools use PDM_PROTECTED_GUESTS, with the remote in each entry: pve01:104, or *:9000-9010 for every remote. A migration is refused when its source or its target is protected. PROXMOX_PROTECTED_VMIDS doesn't apply to PDM tools. PROXMOX_READ_ONLY, PROXMOX_NO_DESTRUCTIVE, HTTP roles and the audit log apply as usual.

The tools are generated from PDM's published API schema; ROADMAP.md maps all 327 PDM endpoints and what's planned next.

🔧 Configuration

Variable

Required

Default

What it does

PROXMOX_HOST

✅¹

Hostname or IP, without scheme or port (192.168.1.100, not https://192.168.1.100:8006)

PROXMOX_USER

✅¹

e.g. root@pam, admin@pve

PROXMOX_TOKEN_NAME + PROXMOX_TOKEN_VALUE

one of

API token auth (recommended)

PROXMOX_PASSWORD

one of

Password auth

PROXMOX_PORT

8006

API port

PROXMOX_VERIFY_SSL

false

Verify the server's TLS certificate against the system's trusted CAs

PROXMOX_CA_BUNDLE

CA file to verify against instead, e.g. a copy of /etc/pve/pve-root-ca.pem; works with Proxmox's default self-signed certificates

PROXMOX_READ_ONLY

false

Refuse every write and advertise only read tools; see below

PROXMOX_TOOLSETS

all

Comma-separated API areas to load; see below

PROXMOX_NO_DESTRUCTIVE

false

Hide the 73 tools that delete, stop, wipe or roll back; see Safety

PROXMOX_PROTECTED_VMIDS

Guest IDs and ranges no tool may change, e.g. 100,101,9000-9010

PROXMOX_WAIT_TIMEOUT

900

Seconds a wait lasts before it returns with the task still running

MCP_HTTP_HOST

127.0.0.1

HTTP entrypoint only: bind address

MCP_HTTP_PORT

8811

HTTP entrypoint only: port

MCP_AUTH_TOKEN

HTTP entrypoint only: one admin bearer token; a token is required off loopback

MCP_AUTH_TOKENS

HTTP entrypoint only: several tokens with roles, name:role:token,…; see Roles

MCP_AUTH_TOKENS_FILE

HTTP entrypoint only: the same, one name role token per line

PROXMOX_AUDIT_LOG

File, or stderr, for a JSON-lines log of every write call; see Audit log

PROXMOX_AUDIT_READS

false

Log read calls too

MCP_HTTP_ALLOW_NO_AUTH

false

HTTP entrypoint only: allow a non-loopback address with no token

PDM_HOST

✅¹

Proxmox Datacenter Manager hostname or IP; loads the pdm toolset. See Datacenter Manager

PDM_PORT

8443

PDM API port

PDM_TOKEN_ID + PDM_TOKEN_SECRET

with PDM_HOST

PDM API token, user@realm!name, and its secret

PDM_CA_BUNDLE

PDM's certificate (or its CA) to verify against

PDM_TLS_NAME

Name to check PDM's certificate against when PDM_HOST is an IP it doesn't name, e.g. pdm

PDM_VERIFY_SSL

false

Verify PDM's certificate against the system's trusted CAs instead

PDM_PROTECTED_GUESTS

remote:ID entries PDM tools may not change, e.g. pve01:104,*:9000-9010

¹ Set PROXMOX_HOST (with PROXMOX_USER and a token or password) to talk to Proxmox VE directly, PDM_HOST to go through Datacenter Manager, or both.

🎯 Toolsets: load only what you use

Every tool the server advertises sits in the client's context on every turn, and all 346 schemas come to roughly 44k tokens. Set PROXMOX_TOOLSETS to the areas you need:

PROXMOX_TOOLSETS=nodes,qemu,lxc,storage

Setting

Tools

Approx. tokens

all (default)

346

44k

PROXMOX_NO_DESTRUCTIVE=true

273

35k

nodes,qemu,lxc,storage,cluster

164

23k

nodes,qemu,lxc,storage

127

19k

PROXMOX_READ_ONLY=true

151

13k

qemu

47

8k

Every count includes wait_for_task, which loads with any selection.

The names are nodes, qemu, lxc, storage, cluster, access, firewall, disks, ceph, acme, sdn, notifications, pools and appliances. vms and containers work as aliases for qemu and lxc. A misspelt name stops the server at startup with the list of valid ones. Toolsets combine with read-only mode.

🚦 Tool annotations

Each tool tells the client what it does, through standard MCP annotations:

  • readOnlyHint: true on the 151 tools that only read.

  • destructiveHint: true on the 73 that delete, stop, wipe, roll back, restore over, or run commands in a guest.

  • destructiveHint: false on the other writes, such as create, start or update.

A test sends every tool through a recording client and checks its read-only label against the HTTP methods it sends, so the labels can't drift from the code.

🌐 Run it over HTTP

proxmox-mcp-server-http serves the same tools over MCP streamable HTTP at /mcp, so the server can run as a network service instead of a process each client spawns. PROXMOX_TOOLSETS and PROXMOX_READ_ONLY apply as usual, and /healthz answers without auth for container health checks.

# Local only, no token needed:
uv run proxmox-mcp-server-http                       # http://127.0.0.1:8811/mcp

# On the network, a token is required:
MCP_HTTP_HOST=0.0.0.0 MCP_AUTH_TOKEN="$(openssl rand -hex 32)" uv run proxmox-mcp-server-http

The same image runs it in Docker:

docker run -p 8811:8811 --env-file .env -e MCP_HTTP_HOST=0.0.0.0 -e MCP_AUTH_TOKEN \
  ghcr.io/ry-ops/proxmox-mcp-server:latest proxmox-mcp-server-http

Clients send Authorization: Bearer <token>. For Claude Code:

claude mcp add --transport http proxmox http://pve-mcp.lan:8811/mcp \
  --header "Authorization: Bearer $MCP_AUTH_TOKEN"

Anyone who can reach this endpoint can use every loaded tool with your Proxmox credentials. So the server binds to 127.0.0.1 by default. It refuses any other address unless a token is set, or you opt out with MCP_HTTP_ALLOW_NO_AUTH=true because a proxy in front of it authenticates clients. The token check runs in constant time. On loopback, DNS-rebinding protection rejects requests whose Host header isn't a local address. Put TLS in front of it, for example with a reverse proxy, when the token crosses a network.

🎭 One token per client, each with a role

Give every client its own token, so you can tell them apart in the audit log and revoke one without the others. Each token carries a role:

Role

Can use

read

only tools that read, like a dashboard or a monitoring agent

operator

everything except destructive tools: start, create, clone, snapshot, migrate

admin

everything the server loads

MCP_AUTH_TOKENS="ops:admin:$(openssl rand -hex 32),grafana:read:$(openssl rand -hex 32)"
# or a file with one "name role token" per line, comments with #:
MCP_AUTH_TOKENS_FILE=/run/secrets/proxmox-mcp-tokens

Each client sees only the tools its role allows, and a call outside its role is refused before anything reaches Proxmox. Roles only narrow what the server already allows: PROXMOX_READ_ONLY, PROXMOX_NO_DESTRUCTIVE, toolsets and protected guests still apply to everyone. Tokens must be at least 16 characters, and names and tokens must be unique. MCP_AUTH_TOKEN still works, as an admin token named default.

⏳ Waiting for long tasks

Clone, migrate, backup, restore, disk moves, snapshots and power actions start a Proxmox task and return its ID, a UPID. Pass wait: true to any of these 30 tools and the result also carries the task's outcome:

{"data":"UPID:pve2:0003A1B2:…:qmclone:9000:root@pam:","task":{"status":"stopped","exitstatus":"OK","ok":true}}

wait_for_task does the same for any UPID, including ones from the cluster task list. A failed task includes the last 20 lines of its log. A wait never fails just because a task is slow. After PROXMOX_WAIT_TIMEOUT seconds, 900 by default, it returns status: "running", and calling wait_for_task again keeps waiting.

🔒 Safety: read-only mode, destructive tools and protected guests

PROXMOX_READ_ONLY=true makes the server look-but-don't-touch. Only GET requests are sent, and every create, update, delete or action tool is refused inside the server before it reaches the network. The client sees only the 151 read-only tools, so it never plans around a tool that would be refused. Three read-style tools send POST requests, so they're hidden too:

  • vm_agent_ping;

  • get_vm_vnc_proxy and get_vm_spice_proxy, which create console access tickets.

PROXMOX_NO_DESTRUCTIVE=true is the middle ground. It hides the 73 tools labelled destructive: deleting, stopping, wiping, rolling back, restoring over a guest, and running commands inside one. Safe writes such as start, create, clone, snapshot and config changes stay available, so 268 tools remain.

PROXMOX_PROTECTED_VMIDS=100,101,9000-9010 names guests no tool may change, such as your router, DNS or backup server. The server checks every write before sending it: power actions, config changes, snapshots, migration, disk moves, deletion, guest-agent commands, HA changes, and deleting the guest's volumes or backups. Reads always work. Cloning from a protected template, backing it up and opening its console stay allowed, because they leave it as it is. Node-wide and bulk start, stop and migrate actions need an explicit vms list while any guest is protected, since they would otherwise include it. Whole-node shutdown and reboot are not covered.

PROXMOX_AUDIT_LOG=/var/log/proxmox-mcp/audit.jsonl records every write call as one JSON line, whether it succeeded, failed or was refused. Set it to stderr to send the lines to your container logs instead. PROXMOX_AUDIT_READS=true logs reads too.

{"ts":"2026-10-30T21:04:12.418+00:00","who":"ci","via":"http","tool":"clone_vm","args":{"node":"pve2","vmid":9000,"newid":123},"outcome":"ok","upid":"UPID:pve2:…","task_ok":true,"ms":41873}
{"ts":"2026-10-30T21:05:02.007+00:00","who":"grafana","via":"http","tool":"stop_vm","args":{"node":"pve1","vmid":120},"outcome":"refused","error":"stop_vm is not allowed for grafana (role: read)","ms":0}

Arguments whose names look secret, such as passwords, tokens, private keys and cloud-init passwords, are written as ***. A failure to write the log is reported on stderr and never fails the tool call.

The rest of the security picture:

  • For monitoring-only use, combine both locks: PROXMOX_READ_ONLY=true and a token with the PVEAuditor role. The flag stops the server sending writes; the role makes Proxmox refuse them anyway.

  • The token's permissions are the real limit. The server exposes destructive operations: deleting VMs, wiping disks, and running commands inside guests. Whatever the token can do, a connected AI client can ask for. Grant the minimum.

  • Prefer API tokens over passwords. They can be revoked on their own.

  • Verify the TLS certificate. Without it, anyone on the network path can read the token. With Proxmox's default self-signed certificates, copy /etc/pve/pve-root-ca.pem from any node and set PROXMOX_CA_BUNDLE to it; with a publicly trusted certificate, set PROXMOX_VERIFY_SSL=true. The server prints a warning at startup when neither is set.

  • Keep credentials out of version control, and restrict network access to the API with firewall rules.

🤝 Agent-to-agent (A2A)

agent-card.json is this server's identity and capability manifest for other agents. It lists all 346 Proxmox VE tools in 14 skill categories, plus the 89 Datacenter Manager tools, each with its name, description, inputs, and read_only and destructive flags. A test fails if it drifts from the tool registry. It also covers authentication requirements and MCP protocol details.

Category

Tools

Category

Tools

node_management

38

disk_management

17

virtual_machine_management

46

ceph_management

33

container_management

28

certificate_management

17

storage_management

14

sdn_management

16

cluster_management

38

notification_management

23

access_control

33

pool_management

5

firewall_management

31

appliances

2

import json

with open('agent-card.json') as f:
    agent_card = json.load(f)

print(f"Agent: {agent_card['name']}  Version: {agent_card['version']}")
for skill in agent_card['skills']:
    print(f"\n{skill['category']}:")
    for capability in skill['capabilities']:
        print(f"  - {capability['name']}: {capability['description']}")
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

server_params = StdioServerParameters(
    command="uv",
    args=["--directory", "/path/to/proxmox-mcp-server", "run", "proxmox-mcp-server"],
    env={
        "PROXMOX_HOST": "192.168.1.100",
        "PROXMOX_USER": "root@pam",
        "PROXMOX_TOKEN_NAME": "automation",
        "PROXMOX_TOKEN_VALUE": "your-token-value",
    },
)

async with stdio_client(server_params) as (read, write):
    async with ClientSession(read, write) as session:
        await session.initialize()
        tools = await session.list_tools()
        result = await session.call_tool("list_vms", arguments={})
        print(result.content)
# Snapshot every VM, then hand off to other agents
async def backup_workflow():
    vms = await proxmox_agent.call_tool("list_vms", {})
    for vm in vms['data']:
        await proxmox_agent.call_tool("create_vm_snapshot", {
            "node": vm['node'],
            "vmid": vm['vmid'],
            "snapname": f"backup-{datetime.now():%Y%m%d-%H%M%S}",
        })
    await storage_agent.call_tool("check_capacity", {})
    await notification_agent.call_tool("send_alert", {"message": f"Backup completed: {len(vms['data'])} VMs"})

🩺 Troubleshooting

  • Double-check the credentials and that the user has the permissions you need.

  • For API tokens, make sure the token hasn't expired or been revoked.

  • If the token was created with Privilege Separation on, give the token its own permissions, or recreate it with that option off.

  • Check PROXMOX_HOST (no scheme, no port) and PROXMOX_PORT.

  • Check you can reach the host: curl -k https://YOUR_HOST:8006/api2/json/version

  • If PROXMOX_VERIFY_SSL=true or PROXMOX_CA_BUNDLE is set, the certificate must be valid for PROXMOX_HOST. Proxmox's node certificates name the node's hostname and IP addresses, so use one of those.

The user or token needs privileges for each operation. Common ones: VM.Monitor, VM.Audit, Datastore.Audit, Sys.Audit, VM.PowerMgmt, VM.Snapshot.

  • The path in the config must be absolute.

  • The config file must be valid JSON.

  • Quit Claude Desktop completely and start it again.

  • Check Claude Desktop's logs. The server logs its authentication method and connection status to stderr, which shows up there.

  • PROXMOX_TOOLSETS loads only the areas it names. Add the area, or remove the variable to load all of them.

  • PROXMOX_READ_ONLY=true hides every write tool. Set it to false to create, change or delete.

  • PROXMOX_NO_DESTRUCTIVE=true hides the delete, stop, wipe and rollback tools.

  • "guest … is protected" means PROXMOX_PROTECTED_VMIDS covers that guest.

  • The server logs how many tools it loaded, and from which toolsets, to stderr at startup.

  • 401 Unauthorized: the client must send Authorization: Bearer <token> with one of the configured tokens.

  • "… is not allowed for NAME (role: read)": that client's token has a role that excludes the tool. Use a token with a wider role.

  • 421 Misdirected Request: on 127.0.0.1 the server only accepts local Host headers. Connect to 127.0.0.1 or localhost, or bind to another address with a token.

  • "Refusing to serve on 0.0.0.0 without authentication": set MCP_AUTH_TOKEN or MCP_AUTH_TOKENS, or MCP_HTTP_ALLOW_NO_AUTH=true if a proxy in front authenticates clients.

./test-connection.sh

It checks the connection, the API, authentication, permission to list nodes, and access to cluster resources.

🛠️ Development

uv sync                          # dependencies, including dev tools
uv run proxmox-mcp-server        # run the server directly (stdio)
uv run proxmox-mcp-server-http   # or over streamable HTTP
uv run pytest                    # tests
uv run ruff check . && uv run black .
proxmox_mcp/
├── server.py        stdio entrypoint: tool calls, roles, guards, waits, audit
├── server_http.py   streamable-HTTP entrypoint: bearer tokens, loopback default
├── registry.py      toolsets, read-only and no-destructive filtering, annotations
├── auth.py          HTTP tokens and roles
├── audit.py         JSON-lines audit log
├── guards.py        protected guests
├── tasks.py         waiting for Proxmox tasks
├── prompts.py       MCP prompts: built-in workflows
├── resources.py     MCP resources: read-only cluster views
├── client.py        Proxmox API client: token/password auth, ticket renewal, read-only guard
└── tools/           one module per API area:
                     nodes, qemu, lxc, storage, cluster, access, firewall,
                     disks, ceph, acme, sdn, notifications, pools, appliances
tests/               pytest suite, run in CI on Python 3.10–3.13
docs/                the animations on this page; docs/gen/build.py regenerates four of them
agent-card.json      A2A agent card
Dockerfile, docker-compose.yaml, setup.sh, test-connection.sh
QUICKSTART.md · SETUP.md · USAGE.md · CHANGELOG.md

Dependencies:

  • mcp (>=2.3, <3): the MCP SDK. It serves both the handshake protocol (2024-11-05 to 2025-11-25) and the stateless 2026-07-28 protocol, so older and newer clients both connect.

  • httpx: the async HTTP client.

  • python-dotenv: loads .env.

  • starlette and uvicorn: serve the HTTP entrypoint. mcp already depends on both.

🗺️ Roadmap

  • One server for several clusters without Datacenter Manager, chosen per call (today: one server per cluster, or through PDM)

  • Progress notifications while waiting for long tasks

What has shipped is in CHANGELOG.md.

🙌 Contributing

Issues and pull requests are welcome: new tools, better error handling, performance, docs, tests and bug fixes.

📚 Learn more

License

MIT. See LICENSE.


Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers