Skip to main content
Glama
tiancode
by tiancode
README.md
# pve-mcp

An MCP server for managing Proxmox VE (single node or cluster): query resources,
manage VM/CT lifecycle, snapshots, backups, clones — from Claude Code or any MCP
host.

- Works with both **QEMU VMs and LXC containers**, on single nodes and clusters.
- Tools that target one guest only need the **`vmid`** — the node and guest type
  are resolved automatically.
- **Safety first**: an optional read-only mode, `confirm=true` required for
  destructive operations, and guest command execution disabled by default.

## Requirements

- Python >= 3.11 (a pinned 3.12 is used via `uv`)
- A Proxmox VE 7/8 host with an API token (see below)

## Installation

```bash
git clone <this-repo> pve-mcp
cd pve-mcp
uv sync            # installs runtime + dev dependencies into .venv
uv run pytest      # optional: run the test suite
```

Or run it directly with `uvx` (no install):

```bash
uvx --from /path/to/pve-mcp pve-mcp
```

The console entry point is `pve-mcp` (stdio transport).

## Creating a PVE API token

Authentication uses API tokens only (`Authorization: PVEAPIToken=<id>=<secret>`),
never username/password tickets. Create a dedicated user and token on the PVE
host (as root):

```bash
# 1. Dedicated user
pveum user add mcp@pve --comment "MCP server"

# 2a. Read-only usage: PVEAuditor on the whole tree
pveum acl modify / --users mcp@pve --roles PVEAuditor

# 2b. Management usage: PVEVMAdmin (VM/CT lifecycle, snapshots, backups)
pveum acl modify / --users mcp@pve --roles PVEVMAdmin
# For backup/restore you may also need PVEDatastoreUser on the backup storage:
pveum acl modify /storage/<backup-storage> --users mcp@pve --roles PVEDatastoreUser

# 3. Token
pveum user token add mcp@pve mcp --privsep 1
```

Note the **`privsep`** setting: with `--privsep 1` (recommended) the token has
its *own* ACLs — grant the roles above to the token too
(`pveum acl modify / --tokens 'mcp@pve!mcp' --roles PVEVMAdmin`), or the token
ends up with no permissions. With `--privsep 0` the token inherits all
permissions of the user.

The command prints the token secret **once** — store it safely.

## Configuration (environment variables)

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `PVE_HOST` | yes | — | PVE API URL, e.g. `https://192.168.1.10:8006` |
| `PVE_TOKEN_ID` | yes | — | Token ID: `user@realm!tokenname`, e.g. `mcp@pve!mcp` |
| `PVE_TOKEN_SECRET` | yes | — | Token secret (UUID) |
| `PVE_VERIFY_SSL` | no | `true` | Set `false` for self-signed certificates (home labs) |
| `PVE_TIMEOUT` | no | `30` | HTTP timeout in seconds |
| `PVE_MCP_READ_ONLY` | no | `false` | `true` registers only the 12 read-only tools |
| `PVE_MCP_ENABLE_EXEC` | no | `false` | `true` registers `pve_vm_exec` (guest agent commands) |
| `PVE_MCP_TASK_WAIT` | no | `30` | Seconds to wait for PVE tasks; on timeout the UPID is returned |

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

## Registering with Claude Code

```bash
claude mcp add pve -e PVE_HOST=https://192.168.1.10:8006 \
  -e PVE_TOKEN_ID='root@pam!mcp' -e PVE_TOKEN_SECRET=xxx \
  -e PVE_VERIFY_SSL=false -- uvx --from /path/to/pve-mcp pve-mcp
```

Add `-e PVE_MCP_READ_ONLY=true` for a safe, audit-only setup.

## Tools

### Read-only (12, always registered)

| Tool | Purpose |
|------|---------|
| `pve_cluster_status` | Cluster/node health, quorum (works on single nodes too) |
| `pve_list_nodes` | Nodes with CPU/memory/disk usage and online status |
| `pve_node_status` | One node in detail (load, kernel, PVE version) |
| `pve_list_vms` | All VMs + CTs; filter by node / type / status |
| `pve_vm_status` | Live status of one guest (CPU/mem, agent, uptime) |
| `pve_vm_config` | Full config of one guest (cores, memory, disks, NICs) |
| `pve_list_storage` | Storage usage (cluster-wide or per node) |
| `pve_storage_content` | Contents of a storage (iso/backup/vztmpl/images) |
| `pve_list_backups` | Backups across storages; filter by vmid / storage |
| `pve_list_snapshots` | Snapshot tree of one guest |
| `pve_list_tasks` | Recent tasks (all nodes or one), errors-only option |
| `pve_task_status` | One task's status; failed tasks include a log tail |

### Write (11, skipped when `PVE_MCP_READ_ONLY=true`)

| Tool | Purpose | Destructive |
|------|---------|-------------|
| `pve_vm_power` | start / shutdown / stop / reboot / suspend / resume | no (`stop` is a hard power-off — prefer `shutdown`) |
| `pve_vm_migrate` | Move a guest to another node (QEMU live / CT restart) | no |
| `pve_vm_set_config` | Change config keys (cores, memory, onboot, ...) | no (most changes need a guest restart) |
| `pve_vm_resize_disk` | Grow a disk (`+10G` or absolute); shrinking unsupported | no |
| `pve_vm_clone` | Clone to a new vmid (full or linked) | no |
| `pve_snapshot_create` | Snapshot a guest (`vmstate` = include RAM, QEMU only) | no |
| `pve_backup_create` | vzdump backup (snapshot/suspend/stop mode) | no |
| `pve_snapshot_rollback` | Roll back to a snapshot | **yes** — requires `confirm=true` |
| `pve_snapshot_delete` | Delete a snapshot | **yes** — requires `confirm=true` |
| `pve_vm_delete` | Delete a guest and all its disks | **yes** — requires `confirm=true` |
| `pve_backup_restore` | Restore an archive to a vmid | new vmid: no; overwrite: **yes** — requires `force=true` + `confirm=true` |

### Exec (1, requires `PVE_MCP_ENABLE_EXEC=true`)

| Tool | Purpose |
|------|---------|
| `pve_vm_exec` | Run a shell command in a QEMU VM via the guest agent (`/bin/sh -c`); never registered in read-only mode |

## Security model

1. **Read-only mode** — with `PVE_MCP_READ_ONLY=true` the write tools are *not
   registered at all* (they don't exist for the model), leaving exactly the 12
   read-only tools.
2. **Tool annotations** — read-only tools carry `readOnlyHint`, the four
   destructive tools carry `destructiveHint`, so MCP hosts (e.g. Claude Code)
   can prompt appropriately.
3. **`confirm` parameter** — destructive tools refuse to run without
   `confirm=true` and explain the consequences first. Nothing is sent to PVE
   until confirmed.
4. **Exec opt-in** — `pve_vm_exec` (arbitrary command execution in guests) is
   only registered with `PVE_MCP_ENABLE_EXEC=true`, and never in read-only mode.

Combine with a least-privilege token: `PVEAuditor` for read-only setups,
`PVEVMAdmin` for management.

## Behavior notes

- **Async tasks**: write operations wait up to `PVE_MCP_TASK_WAIT` seconds for
  the PVE task to finish. Long operations (backups, clones, migrations) return
  `{"status": "running", "upid": ...}` — follow up with `pve_task_status`.
- **Output**: responses are JSON with a per-tool field whitelist; byte values
  keep the raw number and gain a `*_human` companion (e.g. `"31.25 GiB"`).
- **Errors**: HTTP/auth/SSL errors are translated into actionable messages
  (e.g. 403 suggests the missing role; unknown vmids list the existing ones).

## Development

```bash
uv sync
uv run pytest        # unit tests (PVE API mocked with respx)
```

See `DESIGN.md` for the full specification.

TDQS

A4.4/5.0

Scored across 23 tools

Disambiguation5/5

Each tool targets a distinct resource and action, with clear boundaries between backup, snapshot, VM, node, and storage operations. Even tools with similar names like pve_vm_config and pve_vm_set_config are clearly differentiated as get vs set.

Naming Consistency5/5

All tools follow the consistent pattern pve_<resource>_<action>, using snake_case. Actions include create, delete, list, set, get, etc., and resources are always in the plural or singular as appropriate. No mixing of styles.

Tool Count5/5

23 tools is appropriate for a Proxmox VE management server, covering essential operations on VMs, containers, snapshots, backups, nodes, storage, and tasks. Each tool serves a distinct purpose without redundancy.

Completeness4/5

The tool surface covers most lifecycle operations for VMs/containers, snapshots, and backups. A minor gap is the lack of a tool to create a new VM from scratch (e.g., from an ISO), but cloning and configuration tools cover common provisioning workflows.

Maintenance

ActivityInactive
ResponsivenessNo issues