Skip to content
Go back

Proxmox MCP: Read-Only Beats Root

By KingPin 14 min read
Proxmox MCP: Read-Only Beats Root
Contents

Your Homelab Doesn’t Need A Chatbot With root@pam

You’ve got Claude or some other model wired into your tools already, and someone in a Discord you frequent mentioned they hooked it up to their Proxmox cluster. Now you’re picturing it: “hey, which VM is eating all my RAM” typed into a chat window instead of tabbing over to the dashboard for the tenth time today. That part is actually useful. The part where an agent that occasionally hallucinates a shell command also holds a token that can delete a VM is where you should slow down.

The setup that works: give the agent a Proxmox API token scoped to the PVEAuditor role and leave the write tools switched off in the MCP server’s config. That elevated-mode flag is real enforcement, the server rejects gated calls with a “Requires Elevated Permissions” error before they ever reach Proxmox. But it’s enforcement you own, sitting in a config file on your machine that a stray env var, a copy-pasted tutorial, or a version bump can flip. The token is the wall you don’t control. A PVEAuditor token physically cannot start, stop, delete, or exec into anything, no matter what the agent asks the Proxmox API to do. The API says no before your hypervisor even notices the request was rude.

So: two servers to choose between, one pveum sequence that does the real containment, and a verification step that tells you whether you got it right.

What A Query-Only Agent Is Actually Good For

Set expectations before the setup work. A read-only Proxmox agent is a faster way to ask questions you’d otherwise answer by clicking through the web UI or SSHing in to run pvesh. It is not monitoring. It will not page you at 3 AM when a node goes down, because nobody asked it anything at 3 AM. It’s a research assistant with API access, not a replacement for Prometheus and Alertmanager.

What it’s actually good for, prompts included:

All of that is read access dressed up as conversation. It doesn’t replace your alerting stack, and it doesn’t replace backups. Keep both.

Two Servers, One Lineage

Two Proxmox MCP servers are in active circulation, and they share a family tree.

canvrno/ProxmoxMCP is the original and the one most tutorials still link. It’s Python, MIT licensed, sitting at 292 stars and 70 forks with 15 open issues. It was built on proxmoxer, Pydantic, and the official MCP SDK, originally for Cline. Installation goes through the UV package manager on Python 3.10+: uv venv, then uv pip install -e ".[dev]". Config lives in a JSON file pointed at by the PROXMOX_MCP_CONFIG environment variable, with keys for the host, port, SSL verification, the target service (PVE, as opposed to PBS or PMG), and the token credentials. It exposes tools like get_nodes, get_node_status, and get_vms, plus VM console command execution. There’s no permission gating built into the tool set at all. Whatever tools it registers, it registers, and the token underneath is doing all the actual work of deciding what succeeds.

Now the part that decides which one you install: the last commit to that repo was a README edit on 2025-02-19. It’s been over 19 months since anything else changed. It isn’t archived and nobody’s declared it dead, but it’s dormant. That’s not a knock on the author, plenty of small tools reach a state where they just work and stop needing commits. It does mean you’re on your own for anything newer than early last year.

gilby125/mcp-proxmox picks up where that left off, in spirit if not in code. It’s a Node.js rewrite, MIT licensed, 52 stars, last pushed 2026-07-08, and its README credits canvrno’s Python project as the base it worked from. It needs Node.js 20+ and installs with git clone plus npm install. The README also advertises npx mcp-proxmox, but nothing is published under that name on npm (the package.json calls itself mcp-proxmox-server and both names 404 on the registry as of September 2026), so the clone is not optional. You can also run it in Docker with docker run -i --rm (the -i matters, MCP talks over stdio and a non-interactive container will just sit there).

Configuration is entirely environment variables, no JSON file:

PROXMOX_HOST=192.168.1.10 # required
PROXMOX_TOKEN_VALUE=<uuid> # required
PROXMOX_USER=root@pam # default, override this
PROXMOX_TOKEN_NAME=mcpserver # default
PROXMOX_PORT=8006 # default
PROXMOX_ALLOW_ELEVATED=false # default
PROXMOX_VERIFY_TLS=false # default, override in production
PROXMOX_NODE_ALLOWLIST= # optional
PROXMOX_VMID_ALLOWLIST= # optional

Two of those defaults deserve a second look before you copy-paste. PROXMOX_USER defaults to root@pam, which is exactly the account you should not be handing to an agent. And PROXMOX_VERIFY_TLS defaults to false, which is fine for a self-signed cert on your own LAN but not a setting to leave unexamined if this ever talks to Proxmox over anything less trusted. It also loads a .env from the directory above the cloned repo on purpose, so the file holding your token never ends up inside a git-tracked folder by accident.

The read-only tool set is long: node and VM status, storage, cluster status, templates, the next free VMID, VM configs, task status, proxmox_whoami, RRD history, pools, HA resources, firewall rules, and a Terraform generator (more on that below). All of it runs on a read-only token, with one wart in the README: proxmox_get_node_status sits in the read-only table while its own description says it needs elevated mode plus Sys.Audit. The elevated tools, gated behind PROXMOX_ALLOW_ELEVATED=true, cover creation, every lifecycle action (start, stop, reboot, shutdown, pause, resume), clone and resize and delete, snapshots, backups, disk and network changes, migration, template conversion, cloud-init, and guest command execution. That is the entire blast radius sitting behind one environment variable, which is exactly why the flag alone should not be the only thing standing between an agent and your cluster.

The Real Work: Scoping The Proxmox Token

This is the part that actually matters, and it happens entirely on the Proxmox side, before you’ve even installed an MCP server.

Proxmox VE (currently on release 9.2) has privilege separation on API tokens, and it’s the default for anything you create today. A privsep token starts with zero permissions of its own and needs an explicit ACL grant. A token’s permissions are also always a subset of its backing user’s: the API token literally cannot do anything the user behind it isn’t already allowed to do. So the plan is two layers of the same restriction, belt and suspenders.

Create a dedicated user, grant it the built-in PVEAuditor role (read-only, provided by Proxmox out of the box), then create a privsep token under that user and grant the token the same role:

Terminal window
pveum user add agent@pve --comment "MCP read-only agent"
pveum acl modify / -user agent@pve -role PVEAuditor
pveum user token add agent@pve mcp -privsep 1 -expire $(date -d '+90 days' +%s)
pveum acl modify / -token 'agent@pve!mcp' -role PVEAuditor
pveum user permissions agent@pve
pveum user token permissions agent@pve mcp

Walking through it: the first line creates the user (the ACL grant fails if the user doesn’t exist yet, and agent@pve uses the built-in pve realm so there’s no Linux account behind it). The second gives agent@pve read-only access at the root of the resource tree, which covers every node and guest in the cluster instead of just one VM group. The third line creates a token named mcp under that user with privilege separation on, and sets an expiry 90 days out using date so you’re not hand-calculating a Unix epoch. Set that expiry. Proxmox’s own docs frame it as protection against abandoned automation quietly working forever after you’ve forgotten it exists, which is a very specific and very believable failure mode for a homelab. The fourth line grants the token itself the PVEAuditor role (privsep tokens don’t inherit the user’s roles automatically, they need their own grant). The last two commands are your verification step, printing exactly what the user and the token can each do.

The token secret prints exactly once, at creation, and there’s no API call that retrieves it again later. Copy it into a password manager immediately, because if you lose it your only recourse is generating a new one.

When you call the API with this token, the auth header looks like:

Authorization: PVEAPIToken=agent@pve!mcp=1a2b3c4d-5e6f-7890-abcd-ef1234567890

The MCP server builds that header for you from the env vars, you never type it by hand, but that string is what a packet capture on port 8006 will show you.

Wiring It Into Your MCP Client

With the token in hand, point Claude Desktop (or any MCP-speaking client) at your local clone of gilby125/mcp-proxmox:

claude_desktop_config.json
{
"mcpServers": {
"proxmox": {
"command": "node",
"args": ["/home/user/mcp-proxmox/index.js"],
"env": {
"PROXMOX_HOST": "192.168.1.10",
"PROXMOX_USER": "agent@pve",
"PROXMOX_TOKEN_NAME": "mcp",
"PROXMOX_TOKEN_VALUE": "1a2b3c4d-5e6f-7890-abcd-ef1234567890",
"PROXMOX_ALLOW_ELEVATED": "false",
"PROXMOX_VERIFY_TLS": "false"
}
}
}
}

Notice PROXMOX_USER is set explicitly to agent@pve, not left on the root@pam default, and PROXMOX_ALLOW_ELEVATED is spelled out as "false" in the config rather than trusted to the server’s own default. Being explicit here costs you two lines and saves you from a future version of the server changing its defaults out from under you. PROXMOX_VERIFY_TLS stays false in this example because it’s a self-signed cert on a LAN box; if your Proxmox host has a real certificate, flip it to true and don’t leave TLS verification off just because it was already off.

Prove It: whoami And diagnose_permissions

Don’t take the config on faith. Once the server’s running, the first prompt to send is simply: “run whoami on the Proxmox connection.” That calls proxmox_whoami, which returns the identity the token authenticates as and its effective permissions, straight from the API. A correctly scoped token comes back looking like this:

userid: agent@pve
tokenid: agent@pve!mcp
privileges (on /): PVEAuditor
- Sys.Audit, VM.Audit, Datastore.Audit, ...

If instead you see root@pam as the userid, or a privilege list that includes VM.Allocate or Sys.PowerMgmt, stop and go fix the ACL before you send it a second prompt. That’s a token that can create and destroy things, not one that can only look at them.

The diagnose_permissions prompt is the other half of this: it’s built for exactly the “why can’t my token see X” question, walking through what’s granted versus what the agent’s tool calls are expecting. Run it any time a tool call comes back with a 403 you didn’t predict, it’s faster than reading the ACL tree by hand.

The Terraform Trick

The one feature in mcp-proxmox that isn’t just a read wrapper around the API is proxmox_generate_terraform. Point it at a node, a VM ID (or all guests on a node), and it emits HCL for the bpg/proxmox Terraform provider, complete with import blocks so terraform plan (or tofu plan) adopts your existing guests instead of trying to recreate them:

main.tf
import {
to = proxmox_virtual_environment_vm.web01
id = "pve1/201"
}
resource "proxmox_virtual_environment_vm" "web01" {
name = "web01"
node_name = "pve1"
vm_id = 201
# generated config continues...
}
resource "proxmox_virtual_environment_container" "app01" {
node_name = "pve1"
vm_id = 202
operating_system {
template_file_id = "local:vztmpl/placeholder.tar.zst"
}
# generated config continues...
lifecycle {
ignore_changes = [operating_system]
}
}

That ignore_changes = [operating_system] block on LXC resources isn’t decoration, it’s a workaround for a real gap: Proxmox doesn’t record which template an existing container was built from, so the provider has nothing reliable to diff against on that field. The bpg provider marks operating_system and its template_file_id as required, so the generator has to emit a placeholder. Without the lifecycle block, that placeholder forces replacement: every plan run proposes destroying and recreating a container that is already running fine. It’s a small thing, but it’s the kind of detail that tells you someone hit the error and fixed it rather than just wrapping the API.

One detail makes this more useful than it looks: generating Terraform is a read-only operation. It needs nothing beyond the same PVEAuditor token you already built.

When You’d Actually Flip Elevated Mode On

For most homelabs, don’t. If you catch yourself wanting the agent to restart a hung container, restart it yourself, it takes ten seconds and you get to actually see what happened.

If you’ve got a real case for it, for example an agent that manages ephemeral CI runners and needs to spin up and tear down LXC containers on a schedule, narrow it hard before you touch PROXMOX_ALLOW_ELEVATED. Use PROXMOX_NODE_ALLOWLIST and PROXMOX_VMID_ALLOWLIST so the agent can only touch a specific node and a specific range of VMIDs, never the whole cluster. And build a second token for that purpose alone on a custom role holding one privilege (pveum role add VMPowerOnly -privs "VM.PowerMgmt", because Proxmox cannot assign a bare privilege to a path, it only assigns roles), not PVEAdmin and not the full elevated role list the README documents (Sys.Audit, VM.Monitor, VM.Console, VM.Allocate, VM.PowerMgmt, VM.Snapshot, VM.Backup, VM.Config.*, Datastore.Audit, Datastore.Allocate). Grant the smallest slice that does the one job, and keep your everyday query token on PVEAuditor untouched.

The Verdict

Both servers work. canvrno/ProxmoxMCP is the one with the name recognition, but it’s been quiet for over a year and a half, and it has no permission model of its own. gilby125/mcp-proxmox is actively maintained, covers more of the API, and ships the allowlists and elevated-mode gate that make it the safer default for a new setup. Pick that one unless you’ve got a specific reason not to.

But the server you pick matters less than the token you hand it. Whichever one you run, it’s a program on your machine holding credentials, and an agent talking to it is one weird prompt away from asking it to do something you didn’t intend. PVEAuditor, privilege separation on, an expiry date, and the elevated flag left at false means the worst outcome of that weird prompt is a 403 in a log file instead of a missing VM.

Common Questions

Does a Proxmox MCP server need root access?

No. A Proxmox API token scoped to the built-in PVEAuditor role can read every node, VM, and storage target in the cluster without ever touching root@pam. Every API token’s rights are capped at whatever the backing user was granted, so a PVEAuditor user and token combination never has write access to begin with.

Can a Proxmox MCP agent see inside a VM’s disk?

No, not with the read-only tool set. Tools like proxmox_get_vm_config and proxmox_get_vm_status return metadata, not file contents. The one exception is proxmox_execute_vm_command, an elevated tool gated behind PROXMOX_ALLOW_ELEVATED=true that runs commands through the QEMU guest agent, which most homelabs should leave off.

What happens if a Proxmox MCP token leaks?

A leaked PVEAuditor token only exposes read access: cluster topology, VM names, resource usage, and configuration values, nothing it can modify or delete. Revoke it immediately with pveum user token remove agent@pve mcp and issue a new one. An elevated token leaking is a different and worse problem, which is the argument for never creating one you don’t need.

Do I need the QEMU guest agent installed for a Proxmox MCP server to work?

No, only for a narrow slice of features. Node status, VM listings, storage, RRD history, and the Terraform export all work through the Proxmox API directly. The guest agent is only required for proxmox_execute_vm_command and proxmox_get_guest_ips, both elevated tools most read-only setups never enable.

Can a Proxmox MCP server run against a single node instead of a whole cluster?

Yes. Both servers point at one Proxmox host via PROXMOX_HOST, and if that host is standalone rather than clustered, every read-only tool still works against it. mcp-proxmox also supports PROXMOX_NODE_ALLOWLIST to restrict a multi-node cluster connection down to specific nodes if you don’t want the agent seeing the rest.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Next Post
Home Assistant MCP: Server and Client

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts