PVEHub-MCP
Integrates with Proxmox VE and Proxmox Backup Server APIs, providing curated and generic tools for managing nodes, VMs, containers, datastores, snapshots, backups, prune/GC/verify jobs, tasks, and related resources.
Provides QEMU VM guest-agent access, enabling shell command execution and file read/write operations inside QEMU virtual machines.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
PVEHub-MCP
One MCP server for Proxmox VE and Proxmox Backup Server, plus shell access to PVE hosts, LXC containers, QEMU VMs (guest agent) and the PBS host.
Status:
0.1.0— core server, policy engine, curated PVE/PBS/exec tools and packaging are implemented.docs/TOOLS.mdis generated from the code and shows exactly what exists today;docs/STATUS.mdseparates live-verified, mocked and untested behaviour.
Features
PVE REST API — curated tools plus a generic
pve_api_schema/pve_api_callbridge for every endpoint the token can reach.PBS REST API — datastores, namespaces, snapshots, prune, GC, verify, jobs, tasks, plus a generic bridge.
Shell — QEMU guest agent, LXC via
pct exec, host and PBS host via SSH (opt-in).Rights-aware — token permissions are discovered at startup; tools without the required privilege are not registered, and each call is pre-checked.
Blockable — every tool and category can be denied via YAML/env policy; denied tools do not appear in
tools/list.One container, one endpoint — Streamable HTTP at
/mcp,/health, optional stdio.
Related MCP server: ProxmoxMCP-Plus
Architecture
flowchart LR
client["MCP client<br/>Claude, Hermes, Open WebUI"]
subgraph hub["pvehub-mcp container (UID 10001)"]
server["server.py<br/>/mcp · /health<br/>bearer auth"]
policy["policy.py<br/>categories · allow/deny<br/>generic API gates"]
caps["capabilities.py<br/>token privileges"]
tools["tools/<br/>curated + generic"]
audit[("audit.jsonl<br/>/data")]
server --> policy --> caps --> tools
tools --> audit
end
client -->|"Streamable HTTP + Bearer"| server
client -->|"stdio (docker run -i)"| server
tools -->|"HTTPS · API token"| pve["Proxmox VE API"]
tools -->|"HTTPS · API token"| pbs["Proxmox Backup Server API"]
tools -->|"SSH · dedicated key"| nodes["PVE nodes / PBS host"]Quickstart
git clone https://git.mls4tech.net/admin/PVEHub-MCP.git
cd PVEHub-MCP
cp .env.example .env
# edit .env: PVE/PBS URLs, token IDs/secrets, PVEHUB_MCP_AUTH_TOKEN
mkdir -p config ssh
cp config/examples/operator.yaml config/policy.yaml
docker compose up -d --build
curl -s http://127.0.0.1:8080/healthdocker compose reads .env, mounts ./config and ./ssh read-only, keeps /data
(audit log, caches) in the named volume pvehub-data, runs the container read-only with
all capabilities dropped, and publishes 8080 on all interfaces. Bind it to LAN/VPN only:
put a TLS reverse proxy in front of it, or change the port mapping to
127.0.0.1:8080:8080 and proxy from the same host.
The API token needs the privileges of the tools you want to use. Start read-only:
PVE: Datastore.Audit, Sys.Audit, VM.Audit
PVE (exec/files): VM.GuestAgent.Unrestricted, VM.GuestAgent.FileRead, VM.GuestAgent.FileWrite,
VM.GuestAgent.Audit
PBS: Datastore.Audit, Sys.AuditThen widen the token ACL and restart the server. pvehub_capabilities shows what is
available, what policy blocks and what the token is missing.
Clients
The server speaks Streamable HTTP at http://<host>:8080/mcp with
Authorization: Bearer <PVEHUB_MCP_AUTH_TOKEN>; the http
transport refuses to start without PVEHUB_MCP_AUTH_TOKEN (any bind
address). PVEHUB_MCP_AUTH_REQUIRED=false is an explicit opt-out for loopback
development setups only (warning logged at startup). Or stdio when started
with --transport stdio / PVEHUB_TRANSPORT=stdio (no listener, no bearer
token).
Claude Code / generic HTTP client
claude mcp add --transport http pvehub http://127.0.0.1:8080/mcp \
--header "Authorization: Bearer $PVEHUB_MCP_AUTH_TOKEN"Generic client entry (Claude Code .mcp.json, Cursor and similar):
{
"mcpServers": {
"pvehub": {
"type": "http",
"url": "http://127.0.0.1:8080/mcp",
"headers": { "Authorization": "Bearer <token>" }
}
}
}Claude Desktop (stdio via Docker)
Claude Desktop launches stdio servers itself. Run the same image with -i and no port:
{
"mcpServers": {
"pvehub": {
"command": "/usr/local/bin/docker",
"args": [
"run", "-i", "--rm",
"--env-file", "/Users/you/pvehub/.env",
"-e", "PVEHUB_TRANSPORT=stdio",
"-v", "/Users/you/pvehub/config:/config:ro",
"-v", "/Users/you/pvehub/ssh:/ssh:ro",
"-v", "pvehub-data:/data",
"pvehub-mcp:0.1.0"
]
}
}
}Use the absolute path to docker (GUI apps often have a minimal PATH) and absolute host
paths for the mounts. Docker Desktop must be running. -v pvehub-data:/data creates a
volume of that name, separate from the compose volume (<project>_pvehub-data); point both
at the same host directory if they should share audit/cache state. The image's HTTP
healthcheck does not apply here; ignore the container health status in stdio mode.
Hermes
~/.hermes/config.yaml:
mcp_servers:
pvehub:
url: "http://127.0.0.1:8080/mcp"
headers:
Authorization: "Bearer ${PVEHUB_MCP_AUTH_TOKEN}"Hermes expands ${VAR} in url/headers; keep the token in the environment, not in the
file. For stdio, use command: docker with args: ["run", "-i", "--rm", ...] and
env: { PVEHUB_TRANSPORT: "stdio" } instead of url.
Open WebUI
Admin Settings → Integrations → External Tool Servers → + Add Connection:
Type: MCP (Streamable HTTP)
URL:
http://<docker-host>:8080/mcpAuth: bearer token (
PVEHUB_MCP_AUTH_TOKEN)
Open WebUI calls the tools through its backend, so the URL must be reachable from the
Open WebUI container, not just from your browser (use the LAN address or a shared Docker
network, not 127.0.0.1).
SSH access (optional)
Shell tools are the highest-risk part of the server. Host and PBS shells are disabled by
default (exec.host: false, exec.pbs_host: false); LXC and VM exec are enabled but
still require confirm=true and a VMID inside exec.allowed_vmids.
Minimum setup:
# 1. Dedicated key, no passphrase (it lives in the container)
ssh-keygen -t ed25519 -C pvehub-mcp -f ssh/id_ed25519 -N ''
# 2. Host keys, verified out of band before trusting them
ssh-keyscan -H 10.0.0.11 10.0.0.12 10.0.0.20 >> ssh/known_hosts
ssh-keygen -lf ssh/known_hosts # compare with the node consoles
# 3. UID 10001 must be able to read the key
chown -R 10001:10001 ssh && chmod 700 ssh && chmod 600 ssh/id_ed25519 ssh/known_hostsOn every node, create a dedicated user, install the public key and map the PVE node name
to its address in config/config.yaml:
ssh:
nodes:
pve1:
host: 10.0.0.11
pve2:
host: 10.0.0.12
sudo: true # pct exec via passwordless sudo
pbs:
host: 10.0.0.20Notes:
The
nodeskey must be the PVE node name, not the hostname.pct execneeds root or passwordless sudo for/usr/sbin/pct;sudo: truerunssudo -n pct exec ..., so a missing NOPASSWD rule fails immediately instead of hanging.Keep
strict_host_key: true.known_hostsmust exist or the shell tools stay hidden.authorized_keyscannot usecommand="..."restrictions: the MCP server sends arbitrary commands (host shell,pct execwith a variable VMID). A forced command would break every call. Restrict by key (from="10.0.0.20"), a dedicated user, firewall rules and the policy instead.restrict(no forwarding, no agent, no pty) is fine.Revoke by removing the key from
authorized_keys; the MCP server reconnects with the key on the next call.
Security (short version)
Bind the MCP port to LAN/VPN only; put TLS in front of it (reverse proxy).
The http transport refuses to start without
PVEHUB_MCP_AUTH_TOKEN(any bind address).PVEHUB_MCP_AUTH_REQUIRED=falseis an explicit, logged opt-out for loopback development setups only; a non-loopback bind without a token is refused even with the opt-out.Use a dedicated PVE/PBS API token with the minimum ACLs and no expiry surprises.
Start with
config/examples/readonly.yaml; widen tooperator.yamlwhen needed.admin.yaml(destructive + host shells) is for trusted single-operator setups only.Shell access: dedicated SSH key,
known_hostsverification,exec.hostandexec.pbs_hoststayfalseunless you really need them.Destructive and exec tools require
confirm=true; destructive tools supportdry_runwhere the API does.The generic API bridge always denies ticket/console endpoints (
/access/ticket, token endpoints,vncshell,spiceshell,termproxy, QEMUmonitor) and the guest-agentexec/file-writeendpoints; useexec_vm/file_write_guestfor those. Non-GET generic calls requireconfirm=true.Audit JSONL is written to
/data/audit.jsonlwith secret redaction; result bodies are opt-in (PVEHUB_AUDIT_INCLUDE_RESULTS=true).The container runs as UID 10001, read-only root filesystem,
cap_drop: ALL,no-new-privileges.
Troubleshooting
Symptom | Cause / fix |
| Set |
| Set |
| Unknown YAML key; compare with |
| Unknown category or key in |
| Missing/wrong |
| Set |
|
|
Tool missing from | Policy deny, category deny, missing token privilege, missing SSH target/key, or |
| Permission discovery failed for at least one instance; the server still serves tools per |
SSH | Wrong mount path, wrong UID/permissions, or missing |
|
|
Container | Expected: there is no HTTP listener, so the |
Changes to policy/token ACLs have no effect | Settings and policy are read at startup; restart the container |
Documentation
Document | Content |
| components, request pipeline, why SSH is needed |
| every environment variable and YAML key, precedence, TLS, policy, audit |
| generated tool catalog with categories and privileges |
| design decisions and trade-offs |
| phase plan |
| reference projects, licenses, what was reused |
Development
make install # uv sync --extra dev
make check # ruff + mypy + pytest
make docs-tools # regenerate docs/TOOLS.md from @hub.tool declarations
make docker-build # docker build -t pvehub-mcp:0.1.0 .The Docker image is built by CI (.gitea/workflows/ci.yml); the development sandbox has
no Docker daemon, so the image build is not executed there (see docs/DECISIONS.md D010).
License
MIT — see LICENSE. Third-party inspiration: THIRD_PARTY.md.
This server cannot be deployed
Maintenance
Related MCP Connectors
Manage remote servers, SSH terminals, files, databases and proxies with scoped access and approvals.
Scoped, audited SSH exec, sessions, and SFTP on your saved servers without exposing credentials
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
Hosted remote MCP for protected account access, permissions, activity history and supported undo.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables comprehensive management of Proxmox Virtual Environment through 35 tools for VMs, containers, storage, backups, snapshots, and cluster operations via the Proxmox VE API.25 npm22MIT
- AlicenseNot gradedqualityCmaintenanceEnables comprehensive management of Proxmox virtualization environments, including VM and container lifecycle, snapshots, backups, monitoring, and OpenAPI integration.12MIT
- AlicenseAqualityAmaintenanceEnables management of Proxmox VE infrastructure via its REST API and execution of commands inside LXC containers via SSH, with built-in trust layers for planning, audit, undo, and diagnosis.6740 PyPI85AGPL 3.0
- AlicenseNot gradedqualityAmaintenanceExposes 338 tools for the Proxmox Virtual Environment API, enabling users to inspect and control nodes, QEMU VMs, LXC containers, storage and backups, clustering and HA, users and permissions, firewall rules, disks, Ceph, ACME certificates, SDN, notifications, and resource pools. Includes lifecycle actions, snapshots, migration, metrics, task tracking, and guest-agent command execution, with an optional read-only mode that blocks all writes.MIT