Proxmox MCP server
Simple Proxmox MCP
MCP server for managing Proxmox VE
49 tools — nodes, QEMU VMs, LXC containers, storage, cluster, snapshots.
Why this one?
- One image, multi-arch —
docker run ghcr.io/akmalovaa/proxmox-mcp:latestand you're done - Just env vars — no config files, no database, no state
- Read-only by default — destructive ops are gated behind an explicit
PROXMOX_RISK_LEVEL - Tiny codebase — pure stdio MCP over Proxmoxer, no HTTP server, no auth layer, no extras
- Raw JSON out — no formatting, no emoji; LLM gets clean data
- Readable failures — a 403, a dead host or a blocked tier come back as a sentence, not a stack trace
Quick start
Image: ghcr.io/akmalovaa/proxmox-mcp:latest (multi-arch: amd64 + arm64).
1. Export credentials in your shell profile (~/.zprofile, ~/.zshrc or ~/.bashrc):
# base environment:
export PROXMOX_HOST=192.168.1.100
export PROXMOX_USER=root@pam
export PROXMOX_PASSWORD=your-password
# or use token auth (recommended):
export PROXMOX_TOKEN_NAME=mcp
export PROXMOX_TOKEN_VALUE=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# optional:
export PROXMOX_RISK_LEVEL=read
Reload: source ~/.zprofile (or restart the shell).
2. Add to ~/.claude/settings.json (Claude Code) or claude_desktop_config.json (Claude Desktop):
{
"mcpServers": {
"proxmox": {
"command": "docker",
"args": ["run", "-i", "--rm",
"-e", "PROXMOX_HOST",
"-e", "PROXMOX_USER",
"-e", "PROXMOX_PASSWORD",
"ghcr.io/akmalovaa/proxmox-mcp:latest"]
}
}
}
or token auth:
{
"mcpServers": {
"proxmox": {
"command": "docker",
"args": ["run", "-i", "--rm",
"-e", "PROXMOX_HOST",
"-e", "PROXMOX_USER",
"-e", "PROXMOX_TOKEN_NAME",
"-e", "PROXMOX_TOKEN_VALUE",
"ghcr.io/akmalovaa/proxmox-mcp:latest"]
}
}
}
docker run -e VAR without a value passes the host variable through — no secrets in the config file. Restart the client — 31 read-only Proxmox tools become available (more if you raise PROXMOX_RISK_LEVEL).
For password auth, swap the token vars for PROXMOX_PASSWORD.
Note: Claude Desktop on macOS is launched via launchd and does not inherit
~/.zprofile/~/.zshrc. Either put the exports in~/.zshenv, or fall back to an inline"env": { ... }block in the config.
Configuration
All settings are environment variables — set them in your shell profile, pass them inline to docker run -e, or declare them in your MCP client's env block.
| Variable | Default | Description |
|---|---|---|
PROXMOX_HOST |
— | Proxmox host (IP or hostname) |
PROXMOX_USER |
root@pam |
API user |
| Auth | — | token or password — see below |
PROXMOX_PORT |
8006 |
API port |
PROXMOX_VERIFY_SSL |
false |
Verify TLS certificate |
PROXMOX_RISK_LEVEL |
read |
read / lifecycle / all |
Authentication: token or password
Pick one. If both are set, the token wins.
Token (recommended) — create in Proxmox UI: Datacenter → Permissions → API Tokens → Add (uncheck Privilege Separation). Then:
export PROXMOX_TOKEN_NAME=mcp
export PROXMOX_TOKEN_VALUE=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Password (fallback):
export PROXMOX_PASSWORD=your-password
Risk levels
PROXMOX_RISK_LEVEL controls which tools exist. Tools above the active level are not registered, so they never appear in the MCP client's tool list:
| Level | Tools | Adds |
|---|---|---|
read (default) |
31 | read-only tools |
lifecycle |
45 | + start / stop / reboot / suspend / clone / migrate / create-snapshot |
all |
49 | + delete-snapshot / rollback-snapshot |
Each elevated call is also re-checked at call time and logged to stderr (ALLOW / DENY + tool + tier).
Sentry (optional)
Tool calls and failures can be shipped to Sentry — every tools/call
becomes a span, every failing tool an issue. Nothing is sent, and the SDK is never even
imported, while SENTRY_DSN is unset.
The ghcr.io image already contains the SDK. From PyPI, install the extra:
uvx --from 'proxmox-ve-mcp[sentry]' proxmox-ve-mcp
| Variable | Default | Description |
|---|---|---|
SENTRY_DSN |
— | Set it to enable reporting |
SENTRY_ENVIRONMENT |
production |
Free-form environment label |
SENTRY_TRACES_SAMPLE_RATE |
1.0 |
Share of tool calls traced |
SENTRY_SEND_DEFAULT_PII |
false |
Send tool arguments and results as span data |
Leave SENTRY_SEND_DEFAULT_PII off unless you mean it: with it on, tool arguments and
results are attached to spans, and get_vm_config returns ssh keys and cipassword
hashes. A DSN set without the extra installed logs a warning and the server runs on.
Tools
Nodes (10)
| Tool | Description |
|---|---|
list_nodes |
List all cluster nodes with status, CPU, memory, uptime |
get_node_status |
Detailed node metrics (CPU, memory, disk, load, kernel) |
get_node_networks |
Network interfaces on a node |
get_node_disks |
Physical disks on a node |
get_node_services |
Proxmox system services and their state |
get_node_updates |
Pending APT package updates |
get_node_rrd_data |
Historical CPU/memory/disk/network metrics (RRD) |
get_node_tasks |
Recent tasks on a node, optionally errors only |
get_task_status |
Status of a specific task by UPID |
get_task_log |
Log output from a task |
QEMU VMs (17)
| Tool | Tier | Description |
|---|---|---|
list_vms |
read | List all VMs, optionally filter by node |
get_vm_status |
read | Current VM status (running/stopped, CPU, memory) |
get_vm_config |
read | VM configuration (hardware, disks, network) |
get_vm_network_interfaces |
read | IP addresses of a running VM (via QEMU guest agent) |
get_vm_rrd_data |
read | Historical CPU/memory/disk/network metrics (RRD) |
list_vm_snapshots |
read | List all snapshots of a VM |
start_vm |
lifecycle | Start a VM |
stop_vm |
lifecycle | Force-stop a VM |
shutdown_vm |
lifecycle | Graceful ACPI shutdown with timeout |
reboot_vm |
lifecycle | Reboot via ACPI |
suspend_vm |
lifecycle | Suspend a VM |
resume_vm |
lifecycle | Resume a suspended VM |
clone_vm |
lifecycle | Full or linked clone |
migrate_vm |
lifecycle | Move a VM to another node, online or offline |
create_vm_snapshot |
lifecycle | Create a snapshot |
delete_vm_snapshot |
all | Delete a snapshot |
rollback_vm_snapshot |
all | Rollback to a snapshot |
LXC Containers (13)
| Tool | Tier | Description |
|---|---|---|
list_containers |
read | List all LXC containers, optionally filter by node |
get_container_status |
read | Current container status |
get_container_config |
read | Container configuration |
get_container_interfaces |
read | IP addresses of a running container |
get_container_rrd_data |
read | Historical CPU/memory/disk/network metrics (RRD) |
list_container_snapshots |
read | List all snapshots |
start_container |
lifecycle | Start a container |
stop_container |
lifecycle | Force-stop a container |
shutdown_container |
lifecycle | Graceful shutdown with timeout |
reboot_container |
lifecycle | Reboot a container |
create_container_snapshot |
lifecycle | Create a snapshot |
delete_container_snapshot |
all | Delete a snapshot |
rollback_container_snapshot |
all | Rollback to a snapshot |
Storage (2)
| Tool | Description |
|---|---|
list_storage |
Storage pools with usage, optionally filter by node |
get_storage_content |
Contents of a storage pool (ISOs, backups, images, templates) |
Cluster (7)
| Tool | Description |
|---|---|
get_cluster_status |
Cluster health, quorum, node membership |
get_cluster_resources |
All resources (VMs, containers, storage, nodes) |
get_cluster_backups |
Configured backup jobs |
get_ha_status |
High-availability resources and their state |
list_pools |
Resource pools |
get_cluster_log |
Cluster-wide event log, newest first |
get_next_vmid |
Next available VM/container ID |
Architecture
src/proxmox_mcp/
├── server.py # MCPServer instance + entry point
├── config.py # Pydantic Settings (PROXMOX_ prefix)
├── client.py # Proxmoxer connection via lifespan
└── tools/ # nodes, vms, containers, storage, cluster
- Read-only by default — elevated tools gated by
PROXMOX_RISK_LEVEL - Lazy connection — the Proxmoxer client is built on first use, once, and shared; the server therefore starts cleanly even when Proxmox is unreachable
- Raw JSON output — compact, no formatting; LLM consumes data directly
- Normalized errors — Proxmox and network failures are translated into one
actionable sentence instead of a
requeststraceback
Development
Run standalone (testing)
export PROXMOX_HOST=192.168.1.100
export PROXMOX_USER=root@pam
export PROXMOX_TOKEN_NAME=mcp
export PROXMOX_TOKEN_VALUE=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
docker run -i --rm \
-e PROXMOX_HOST -e PROXMOX_USER \
-e PROXMOX_TOKEN_NAME -e PROXMOX_TOKEN_VALUE \
ghcr.io/akmalovaa/proxmox-mcp:latest
Without Docker (UV)
git clone https://github.com/akmalovaa/proxmox-mcp.git && cd proxmox-mcp && uv sync
MCP client config:
{
"mcpServers": {
"proxmox": {
"command": "uv",
"args": ["run", "--directory", "/path/to/proxmox-mcp", "python", "-m", "proxmox_mcp"],
"env": {
"PROXMOX_HOST": "192.168.1.100",
"PROXMOX_TOKEN_NAME": "mcp",
"PROXMOX_TOKEN_VALUE": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
}
}
}
Build from source
git clone https://github.com/akmalovaa/proxmox-mcp.git
cd proxmox-mcp
docker build -t proxmox-mcp .
The image is multi-stage: uv builds the virtualenv in a throwaway layer, and the
runtime stage carries only Python plus the venv and runs as the unprivileged mcp
user (uid 10001).
Lint, type-check, test
uv sync --locked --group dev
uv run ruff check .
uv run mypy src/
uv run pytest -v
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file proxmox_ve_mcp-2.2.0.tar.gz.
File metadata
- Download URL: proxmox_ve_mcp-2.2.0.tar.gz
- Upload date:
- Size: 17.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f4a1a01ea51ba7ce47a4ffeee8449eda80acd6e91daf1fb189590d0a4de9dad8
|
|
| MD5 |
9f2257446aefa8dde83f445c95033f74
|
|
| BLAKE2b-256 |
55663dc2dd38d35d9552cab049b4737aaf8588165b8ddfb6d4cd6da173d10325
|
File details
Details for the file proxmox_ve_mcp-2.2.0-py3-none-any.whl.
File metadata
- Download URL: proxmox_ve_mcp-2.2.0-py3-none-any.whl
- Upload date:
- Size: 24.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c3db9e55127b3d2170d77274d103854d32e7b6a757b04508ee5c7c156afb737f
|
|
| MD5 |
4acaf069872444e5674f8af39751b412
|
|
| BLAKE2b-256 |
359bef8073b05c079c7f2a79b040fc3c7d198abc7db6d3fd9a0664889cc6a23a
|