drumfire-proxmox-mcp
by Drumfire01
README.md
# drumfire-proxmox-mcp
A remote MCP server that lets Claude manage the DRUMFIRE Proxmox cluster over chat —
inventory, power control, live config changes, creating/cloning VMs and containers,
snapshots, network/firewall/cluster visibility, running commands inside a guest, and
pushing a file straight onto a VM's filesystem via the QEMU Guest Agent (used here to
update the net worth calculator running on VM 107 with a fresh export from Firefly III).
It's a Cloudflare Worker, deployed the same way as the existing Firefly III MCP
server: click the button, set a few secrets, add it to Claude as a custom connector.
## What it deliberately does NOT do
There are no VM/container/disk/snapshot **delete** or snapshot **rollback** tools —
not "ask before," just **not present in the code**, so they can't happen through this
server even by mistake.
That said, read the security note below: because creating VMs/containers is in
scope, the underlying Proxmox token technically *can* delete them too (Proxmox ties
those permissions together) — the guarantee here is software-enforced (no such tool
exists, and Claude is instructed to always ask before anything destructive), not a
hard credential-level wall the way v1 of this server was. See "Security model" below.
## Tools this server exposes
| Tool | What it does |
|---|---|
| `list_nodes` | List cluster nodes and their status |
| `list_vms` / `list_containers` | List QEMU VMs / LXC containers on a node (or all nodes) |
| `get_guest_status` | Status of one VM/container |
| `start_guest` / `shutdown_guest` / `stop_guest` / `reboot_guest` | Power management (`shutdown` is graceful, `stop` is a hard power-off) |
| `guest_agent_ping` | Check the QEMU Guest Agent is alive inside a VM |
| `write_vm_file` | Write text content to an absolute path inside any VM via the guest agent |
| `update_networth_calculator` | Convenience wrapper around `write_vm_file` targeting the net worth calculator VM specifically |
| `get_cluster_status` | Cluster/node health and quorum |
| `get_node_network` | A node's bridges/VLANs/bonds |
| `get_firewall_rules` | Read-only firewall rules at datacenter/node/guest level |
| `get_vm_network_info` | A VM's *actual* live IPs/interfaces, reported from inside the guest |
| `run_guest_command` / `get_guest_command_result` | Run a command inside a VM (e.g. `ping`, `ipconfig`) via the guest agent — equivalent to shell access, see the caution note in the tool's own description |
| `get_next_vmid` | Next free VM/container ID |
| `update_vm_config` / `update_container_config` | Change CPU/memory/network/disk-attach/etc config |
| `resize_vm_disk` | Grow a disk (Proxmox only allows growing via this API, never shrinking) |
| `create_vm` / `create_container` | Create a new VM / LXC container |
| `clone_vm` | Clone an existing VM or template |
| `create_snapshot` / `list_snapshots` | Create and list snapshots (no rollback/delete tool) |
## Security model
- **Power/status/config/create/clone/snapshot-create tools**: treated as "fair game"
to run without asking first, per how you scoped this — nothing here is inherently
destructive.
- **`run_guest_command`**: runs whatever it's told inside the guest, equivalent to
SSH/RDP access. Fine for diagnostics and routine changes; Claude is instructed to
pause and confirm with you before anything that looks destructive or irreversible
inside the guest.
- **Nothing in this server deletes or rolls back a VM, container, disk, or
snapshot.** That's the actual backstop, not the Proxmox token's permissions —
see the role below, which does grant `VM.Allocate` (required for creation, and
which Proxmox also uses for deletion — there's no way to split those at the
permission level).
## 1. Create a scoped Proxmox API token
Don't reuse a root/admin token. In the Proxmox web UI:
1. **Datacenter → Permissions → Roles → Create**
Name: `ClaudeHomelabOps`. Privileges:
`VM.Audit`, `VM.Monitor`, `VM.PowerMgmt`, `Sys.Audit`,
`VM.Allocate`, `VM.Clone`, `VM.Snapshot`,
`VM.Config.CPU`, `VM.Config.Memory`, `VM.Config.Disk`, `VM.Config.Network`,
`VM.Config.Options`, `VM.Config.HWType`, `VM.Config.CDROM`, `VM.Config.Cloudinit`.
Deliberately **excludes** `VM.Snapshot.Rollback` (rollback discards current disk
state — genuinely destructive) and `VM.Backup`/`VM.Migrate`/`VM.Console`
(not needed for anything this server does). As covered above, `VM.Allocate` is
required for `create_vm`/`create_container`/`clone_vm` and Proxmox also uses it
for guest deletion — there's no finer-grained split available, so the "can't
delete" guarantee here comes from this server simply not having a delete tool,
not from the token's permissions.
If you'd rather keep the harder credential-level guarantee day-to-day and only
grant creation temporarily, you can instead create a *second*, more restricted
token (this same role minus `VM.Allocate`/`VM.Config.*`) and switch which
secret this Worker uses when you actually want to create/reconfigure something —
the tradeoff question earlier defaulted to one token for simplicity, but nothing
here requires that.
2. **Datacenter → Permissions → Users → Add** a dedicated user, e.g. `claude-agent@pve`
(no password needed, it'll only ever authenticate via API token).
3. **Datacenter → Permissions → Add** → Path `/`, User `claude-agent@pve`, Role `ClaudeHomelabOps`.
4. **Datacenter → Permissions → API Tokens → Add** → User `claude-agent@pve`, Token ID
e.g. `homelab-mcp`. Leave "Privilege Separation" checked and grant the same `/` →
`ClaudeHomelabOps` ACL to the token itself. Copy the **Token ID**
(`claude-agent@pve!homelab-mcp`) and the **secret** (shown once) — you'll need both below.
## 2. Make sure the calculator VM's guest agent is enabled
The `write_vm_file` / `update_networth_calculator` tools need the QEMU Guest Agent
running inside VM 107 (VM-OnPremTest):
1. In the VM's **Options** tab in Proxmox, confirm "QEMU Guest Agent" is enabled.
2. Inside the Windows VM, install the guest agent service if it isn't already
(it ships on the [virtio-win](https://github.com/virtio-win/virtio-win-pkg-scripts)
ISO as `qemu-ga-x86_64.msi`) and make sure the "QEMU Guest Agent" Windows service
is running.
3. Note which Windows user account the app's data folder lives under — the guest
agent service runs as SYSTEM, so file writes need a **full absolute path**, e.g.
`C:\Users\<username>\Documents\net-worth\data.json`, not a relative one.
## 3. Deploy
[](https://deploy.workers.cloudflare.com/?url=REPLACE_WITH_THIS_REPO_URL)
*(Push this repo to GitHub first, then replace `REPLACE_WITH_THIS_REPO_URL` above
with `https://github.com/<you>/<repo>` — GitHub doesn't let the button link to
itself before the repo exists.)*
Clicking the button forks/connects the repo into Cloudflare Workers Builds and runs
the deploy for you. It won't be usable yet, though — see step 4.
### Manual deploy (alternative to the button)
```bash
npm install
npx wrangler secret put PROXMOX_TOKEN_ID
npx wrangler secret put PROXMOX_TOKEN_SECRET
npx wrangler secret put MCP_BEARER_TOKEN # any long random string you generate
npx wrangler deploy
```
## 4. After deploying, set the secrets
Whether you used the button or the CLI, three **secrets** are required and are
intentionally *not* in this repo (they're not in `wrangler.jsonc`, which is fine to
be public):
| Secret | Value |
|---|---|
| `PROXMOX_TOKEN_ID` | `claude-agent@pve!homelab-mcp` (from step 1) |
| `PROXMOX_TOKEN_SECRET` | the token secret (from step 1) |
| `MCP_BEARER_TOKEN` | any long random string you generate yourself, e.g. `openssl rand -hex 32` |
Set them in the Cloudflare dashboard under your Worker → **Settings → Variables and
Secrets → Add → Encrypt**, or via `npx wrangler secret put <NAME>`.
Also double check the plain (non-secret) variable `NETWORTH_VM_PATH` in
`wrangler.jsonc` — it ships with a placeholder
(`C:\Users\CHANGE_ME\Documents\net-worth\data.json`) that needs the real Windows
username from step 2. Edit it in the Cloudflare dashboard under the same
**Variables and Secrets** page (as a plaintext var, not a secret), or edit
`wrangler.jsonc` and redeploy.
## 5. Add it to Claude as a custom connector
In Claude's connector settings, add a custom connector pointing at:
```
https://<your-worker-subdomain>.workers.dev/mcp
```
This Worker checks every request for `Authorization: Bearer <MCP_BEARER_TOKEN>`
before doing anything — without it, every tool call gets a 401. If your Claude
connector setup lets you attach a custom header/API key, set that header there. If
it doesn't, the two options are:
- Put a [Cloudflare Access](https://developers.cloudflare.com/cloudflare-one/policies/access/)
policy in front of the Worker's route instead (recommended — this is the standard
way to lock down a personal MCP endpoint when the client can't send a custom
header), or
- As a lower-security fallback, delete the bearer-token check in `src/index.ts`
(`fetch()`, top of the function) and rely on the Worker's URL itself being
unguessable. Not recommended for anything that can touch Proxmox.
## 6. Try it
Once connected, in a Claude chat: *"list the VMs on drumfire03"* or *"what's the
status of VM 107"* should return real data. For the net worth workflow, Claude
pulls data from Firefly III (already connected), builds the JSON the calculator
expects, and calls `update_networth_calculator` with that JSON as a string.
Once that's working, try the newer tools too — *"check the cluster's health"*,
*"what's VM 107's actual IP right now"*, *"clone VM-OnPremTest into a test copy"*,
*"bump that VM to 4 cores"*.
**Note on permissions:** Proxmox's exact privilege requirements for firewall-rule
and some network-read endpoints vary a bit by version, and the role above is a
best-effort mapping, not something tested against your specific Proxmox version. If
a tool call comes back with a 403, the error message includes Proxmox's actual
response — the fix is almost always adding one more privilege to `ClaudeHomelabOps`
(commonly just re-confirming `Sys.Audit` is applied at `/`, not just a sub-path).