proxmox-ve-mcp
Provides tools to manage Proxmox VE hosts, including inventory, reading status, and creating, cloning, starting, stopping, and deleting VMs and containers.
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-ve-mcplist all VMs on host server1"
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-ve-mcp
An MCP server that exposes one or more Proxmox VE hosts as tools an LLM client can call — inventory nodes, guests, storage and network bridges, read live status, and create, clone, start, stop and delete VMs and containers.
It speaks MCP over Streamable HTTP, so it runs as its own service on the network rather than as a local subprocess of one client.
Built for universal-network-director,
a chat-driven multi-vendor network manager with a human approval gate on every
write — but it is a standalone MCP server and works with any MCP client.
Not affiliated with, endorsed by, or supported by Proxmox Server Solutions GmbH. "Proxmox" and "Proxmox VE" are trademarks of their respective owners and are used here only to describe what this software talks to.
Read this before you point it at production
Twelve of the twenty-four tools change state, and this server does not ask before executing them. There is no confirmation and no dry-run. If a model decides to call one, it happens.
Tool | What it does | Risk |
| Sets a guest's notes field | Cosmetic. Reversible. |
| Powers a guest on | Low. |
| ACPI shutdown — the guest OS shuts itself down | Takes a workload offline. Clean. |
| Clean guest restart | Takes a workload offline briefly. |
| Immediate power-off, like pulling the cord | Takes a workload offline, uncleanly. Risks filesystem damage. |
| Clones a guest to a new vmid | Consumes storage. Source untouched. |
| Creates a VM from a staged disk image | Consumes storage and a vmid. |
| Creates a VM with an empty disk booting an installer ISO | Consumes storage and a vmid. |
| Pulls a disk image from a URL into | Consumes storage and egress bandwidth. |
| Deletes a staged image, ISO or template | Destructive. Refuses if a guest still has it attached. |
| Patches a guest NIC into a bridge, or unpatches it | Can move a running guest onto the wrong segment — or off the network. |
| Permanently deletes a guest and its disks | Destructive and irreversible. No snapshot, no undo. |
Three ways to handle that, in the order they actually help:
Scope the Proxmox API token read-only. This is the real control, and it lives on Proxmox, not in this code. Give the token the built-in
PVEAuditorrole at path/and every write tool fails at the API no matter what any model decides. Do this unless you specifically intend the writes to work.Use the protected-guest denylist.
config/protected-vms.jsonlists guests the write tools refuse to touch, checked locally before any backend call — so it holds even if a human approves something by accident. A missing or unparseable file refuses every guest write rather than silently protecting nothing. See below.Gate the writes in your client. Every state-changing tool is prefixed
write_. That prefix is a convention of this codebase precisely so a client can match on it and route those calls through a human-approval step before execution. This server deliberately doesn't do that itself — it has no user to ask.
The MCP endpoint has no authentication
This server exposes its tools to anyone who can reach its port. There is no token, no client auth, no TLS on the MCP side.
MCP_HOST defaults to 127.0.0.1 for that reason. The container image sets
0.0.0.0 because it has to, which means publishing the container's port puts
an unauthenticated control plane for your hypervisors on that interface. Keep
it on an internal network with the client, or terminate TLS and authentication
in front of it.
Multi-host by design
Proxmox clusters share one API, but plenty of setups run several standalone hosts on different subnets with no cluster between them. This server holds one connection per host, keyed by a short freeform label, and every tool takes that label to pick which host to talk to.
A host is defined by a pair of environment variables:
PROXMOX_SERVER1_URL=https://pve1.example.com:8006
PROXMOX_SERVER1_TOKEN='automation@pve!mcp=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'The <LABEL> in PROXMOX_<LABEL>_URL, lowercased, becomes the host value the
tools take (server1 above). Add a third host by adding a third pair — no code
change. Name them for the site so the model and the logs read clearly.
The token is the whole user@realm!tokenid=secret string, shown once at
creation under Datacenter → Permissions → API Tokens. Auth is stateless: every
request carries an Authorization: PVEAPIToken=... header. There is no login
call and no CSRF token — that's the username/password session path, which this
deliberately does not use.
Set PROXMOX_VERIFY_TLS=false for hosts with self-signed certificates. It
defaults to on.
The protected-guest denylist
config/protected-vms.json is mounted read-only into the container and holds
the guests write tools must never touch:
{
"protected_vms": [
{
"host": "server1",
"vmid": 100,
"name": "example-mcp-host",
"reason": "EXAMPLE -- the VM this MCP server itself runs in"
}
]
}host is the label from list_hosts, not the Proxmox node name. reason
is shown verbatim in the refusal, so write it for whoever hits it.
This file is meant to be tracked in git. It started life as an environment
variable in an untracked .env, which meant the protection did not survive a
fresh clone and an empty list looked exactly like a populated one. Now a missing
or unparseable file refuses every guest write; an empty list is allowed but
logs a loud warning at startup.
The entries shipped here are examples. Replace them before pointing this at anything you care about.
Running it
docker build -t proxmox-ve-mcp .
docker run --rm \
-e PROXMOX_SERVER1_URL=https://pve1.example.com:8006 \
-e PROXMOX_SERVER1_TOKEN='automation@pve!mcp=...' \
-e PROXMOX_VERIFY_TLS=false \
-v "$PWD/config/protected-vms.json:/app/config/protected-vms.json:ro" \
-p 127.0.0.1:8002:8002 \
proxmox-ve-mcpOr point pip install -r requirements.txt at a virtualenv and run
python server.py directly.
Variable | Default | Meaning |
| — | API root of a host, e.g. |
| — | The full |
|
| Set |
| unset | Escape hatch ( |
|
| Denylist path |
|
| Bind address (the image sets |
|
| Bind port |
Tests
Standalone scripts, no pytest. Run them in the container so they have the
PROXMOX_* environment the client needs:
docker run --rm proxmox-ve-mcp python test_network_bridges.py
docker run --rm proxmox-ve-mcp python test_media_in_use.py
docker run --rm proxmox-ve-mcp python test_client.pyThe offline sections use fabricated interface and guest lists and pass with no
hosts configured. The live sections read whatever your PROXMOX_* variables
point at, and skip cleanly when nothing is configured — point them at a real
host to exercise the one distinction that can't be faked: whether a bridge is
uplinked or isolated.
Design notes
/cluster/resourcesis the inventory backbone. One call returns every VM, container, node and storage already tagged with its node, vmid and type. It works on a standalone host too (it reports that one node), so this is used instead of walking/nodes→/nodes/{node}/qemuper node.list_network_bridgesexists because a NIC on the wrong bridge is a guest you can't reach. It reports, per bridge, whether it has a member port (a way off the box) or is an isolated segment — the distinction that decides whether a new VM comes up reachable.VLAN tags fail closed. A
tag=on a bridge that isn'tbridge_vlan_awareis accepted by Proxmox and then silently not carried — untagged traffic where isolation was asked for. The write paths refuse that rather than warn, and refuse if they can't read the bridge list to check.Writes are asynchronous. Most return a Proxmox UPID; poll it with
get_task_statusrather than assuming completion.Guest writes run behind one guard. A single wrapper does the protected-VM check and the vmid→node/kind resolution, so an individual tool cannot forget the guard and cannot reach the backend for a protected guest.
License
Apache-2.0. See LICENSE.
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 Connectors
MCP server for the FFmpeg Micro video transcoding API — create, monitor, download transcodes.
An MCP server that let you interact with Cycloid.io Internal Development Portal and Platform
Streamable HTTP MCP server for Google Calendar and Sheets with OAuth login.
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/anderson-jason573/proxmox-ve-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server