Proxmox VE

io.github.akmalovaa由 GitHub 账号 akmalovaa 发布v2.3.0更新于 Oct 24, 2026

Manage Proxmox VE nodes, VMs, containers, storage and snapshots. Read-only by default.

概览

AI 生成的概览

让助手通过 50 个工具查看 Proxmox VE 节点、QEMU 虚拟机、LXC 容器、存储和快照,并在启用后执行控制操作。

可以做什么
把 Proxmox VE API 封装为 50 个工具,覆盖节点、QEMU 虚拟机、LXC 容器、存储、集群和快照。只读工具可列出节点、虚拟机、容器、存储池、集群健康、备份、HA 状态和任务日志,并可通过客户机代理获取虚拟机或容器的 IP 地址。更高风险级别会增加启动、停止、重启、克隆、迁移、创建快照等生命周期操作,以及删除和回滚快照。写操作返回 Proxmox 已接受的任务和可用 get_task_status 轮询的 UPID。
适用场景
适合让助手回答有关 Proxmox VE 集群的问题,或在聊天客户端中执行客户机生命周期操作。适用于家庭实验室和小型集群管理,默认只读,可由管理员主动提高风险级别。
运行要求
以 stdio(默认)或 streamable HTTP 在本地运行,来自 PyPI 包 proxmox-ve-mcp 或 OCI 镜像 ghcr.io/akmalovaa/proxmox-mcp。需要 PROXMOX_HOST,以及 PROXMOX_USER 加 PROXMOX_TOKEN_NAME 和 PROXMOX_TOKEN_VALUE,或 PROXMOX_PASSWORD。可选:PROXMOX_PORT、PROXMOX_VERIFY_SSL、PROXMOX_TIMEOUT、PROXMOX_RISK_LEVEL、PROXMOX_TOOLS_ALLOW、PROXMOX_REDACT_SECRETS、PROXMOX_MCP_* 传输设置、SENTRY_DSN。需要能访问 Proxmox API 的网络。
安装前请注意
凭据通过环境变量传入:PROXMOX_PASSWORD 或 PROXMOX_TOKEN_VALUE,以及可选的 SENTRY_DSN。默认风险级别 read 只注册只读工具;lifecycle 和 all 会增加启动、停止、重启、克隆、迁移、创建快照,以及删除或回滚快照,这些会更改或销毁客户机状态。HTTP 传输没有身份验证,任何能访问该端点的人都获得风险级别允许的权限;请绑定回环地址或置于需要认证的代理之后。PROXMOX_REDACT_SECRETS=false 会暴露 cipassword 和 sshkeys,SENTRY_SEND_DEFAULT_PII 会把工具参数和结果发送到 Sentry。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Proxmox VE,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

Proxmox MCP server

[proxmox-mcp — MCP server for Proxmox VE]

[CI] [Release] [License: MIT] [Python 3.14] [GHCR] [MCP]

Simple Proxmox MCP

[proxmox-mcp logo]

MCP server for managing Proxmox VE

50 tools — nodes, QEMU VMs, LXC containers, storage, cluster, snapshots.

Why this one?

  • One image, multi-arch — docker run ghcr.io/akmalovaa/proxmox-mcp:latest and 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
  • stdio or Streamable HTTP — one env var apart; stdio by default, HTTP binds loopback
  • Tiny codebase — a thin layer over Proxmoxer, no config files, no database, no state
  • 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

[proxmox-mcp MCP server]

Quick start

Image: ghcr.io/akmalovaa/proxmox-mcp:latest (multi-arch: amd64 + arm64).

1. Export credentials in your shell profile (~/.zprofile, ~/.zshrc or ~/.bashrc):

bash
# token auth (recommended — see "Least privilege" for the user and role to give it):export PROXMOX_HOST=192.168.1.100export PROXMOX_USER=mcp@pveexport PROXMOX_TOKEN_NAME=mcpexport PROXMOX_TOKEN_VALUE=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# or password auth:# export PROXMOX_USER=root@pam# export PROXMOX_PASSWORD=your-password
# 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):

json
{  "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:

json
{  "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 — 32 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.

VariableDefaultDescription
PROXMOX_HOST—Proxmox host (IP or hostname)
PROXMOX_USERroot@pamAPI user
Auth—token or password — see below
PROXMOX_PORT8006API port
PROXMOX_VERIFY_SSLfalseVerify TLS certificate
PROXMOX_TIMEOUT15Seconds to wait for each API request
PROXMOX_RISK_LEVELreadread / lifecycle / all — see Risk levels
PROXMOX_TOOLS_ALLOW—Comma-separated tool names to register, on top of the risk level
PROXMOX_REDACT_SECRETStrueMask cipassword / sshkeys in responses

Transport settings live under a PROXMOX_MCP_ prefix — see Streamable HTTP.

Authentication: token or password

Pick one. If both are set, the token wins.

Token (recommended):

bash
export PROXMOX_USER=mcp@pveexport PROXMOX_TOKEN_NAME=mcpexport PROXMOX_TOKEN_VALUE=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Setting only one half of the pair is a startup error rather than a silent fallback to the password — a typo in PROXMOX_TOKEN_NAME used to mean quietly running as whoever PROXMOX_USER is.

Password (fallback):

bash
export PROXMOX_PASSWORD=your-password

Least privilege

An MCP server is reachable by a model acting on text it did not write, so give it its own user and its own token rather than root@pam. On the Proxmox host:

bash
# a user that is not root, with a role that matches the risk level you plan to runpveum user add mcp@pvepveum acl modify / --users mcp@pve --roles PVEAuditor          # read# pveum acl modify / --users mcp@pve --roles PVEVMAdmin        # lifecycle / all
# a token for that user. Privilege Separation ON (the default) means the token starts# with no rights at all, so grant it the same role explicitly:pveum user token add mcp@pve mcp --privsep 1pveum acl modify / --tokens 'mcp@pve!mcp' --roles PVEAuditor

Check what the token actually ended up with:

bash
pveum user permissions mcp@pve --token mcp

PVEAuditor covers all 32 read tools. PVEVMAdmin on /vms adds guest lifecycle and snapshots; migrate_vm additionally needs VM.Migrate on the target node, and clone_vm needs Datastore.AllocateSpace on the target storage. Narrow the ACL path (/vms/101, /pool/homelab) if the server should only see part of the cluster.

root@pam still works and is the quickest thing for a local look around — it is just not what should be left running.

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:

LevelToolsAdds
read (default)32read-only tools
lifecycle46+ start / stop / reboot / suspend / clone / migrate / create-snapshot
all50+ delete-snapshot / rollback-snapshot

Each elevated call is also re-checked at call time and logged to stderr (ALLOW / DENY + tool + tier).

The active level is not otherwise observable from the client side — a tool that is missing looks the same as a tool that was never written — so get_server_info reports it, along with the versions and the tool count.

PROXMOX_TOOLS_ALLOW narrows further within the tier, for a deployment that serves one specific agent:

bash
export PROXMOX_TOOLS_ALLOW=list_nodes,list_containers,get_container_status,get_cluster_resources

Names that do not exist are a startup error, so a typo cannot silently amputate the tool list. Keep get_server_info on the list unless you have a reason not to — it is how a client learns what the rest of the list means.

Response shape

Inventory listings (list_nodes, list_vms, list_containers, get_cluster_resources) return a compact subset of each row: identity, status, uptime, CPU and memory. What is dropped is per-second IO counters and PSI pressure gauges — real data, but nothing a triage decision turns on, and several times the volume of what is kept. Pass verbose=true for the untouched rows.

Write tools answer with the task Proxmox accepted, not with a finished result:

json
{"state":"accepted","operation":"start_vm","node":"pve","vmid":101, "upid":"UPID:pve:...","poll_with":"get_task_status"}

cipassword and sshkeys are replaced with ***redacted*** everywhere, keeping the key so the model can still tell that cloud-init is configured. Set PROXMOX_REDACT_SECRETS=false to get the raw values.

Streamable HTTP

stdio stays the default and is what an MCP client on your own machine should use. For a shared deployment — a container in a cluster, several clients on one URL — set the transport and nothing else changes:

bash
docker run --rm -p 8000:8000 \  -e PROXMOX_HOST -e PROXMOX_TOKEN_NAME -e PROXMOX_TOKEN_VALUE \  -e PROXMOX_MCP_TRANSPORT=streamable-http \  -e PROXMOX_MCP_HOST=0.0.0.0 \  -e PROXMOX_MCP_ALLOWED_HOSTS=proxmox-mcp.example.com \  ghcr.io/akmalovaa/proxmox-mcp:latest
VariableDefaultDescription
PROXMOX_MCP_TRANSPORTstdiostdio or streamable-http
PROXMOX_MCP_HOST127.0.0.1Bind address — 0.0.0.0 in a container
PROXMOX_MCP_PORT8000Port
PROXMOX_MCP_PATH/mcpEndpoint path
PROXMOX_MCP_JSON_RESPONSEtrueJSON responses instead of an SSE stream
PROXMOX_MCP_ALLOWED_HOSTS—Host headers to accept; * disables the check
PROXMOX_MCP_ALLOWED_ORIGINS—Browser origins allowed; empty = same-origin only

The prefix is PROXMOX_MCP_, not PROXMOX_, because Kubernetes injects <SERVICE>_PORT for every linked Service — a Service named proxmox would otherwise redefine PROXMOX_PORT.

Sessions are not used (stateless_http): they were removed from the protocol in revision 2026-07-28, and without them several clients can share one URL and a rolling update does not cut anyone off.

Host and Origin validation. Binding anything other than loopback requires PROXMOX_MCP_ALLOWED_HOSTS; the server refuses to start otherwise. A foreign Origin gets 403 and an unexpected Host gets 421 — this is what stops a page in a browser on the same LAN from driving the server through DNS rebinding. A request with no Origin header (curl, MCP clients) always passes. Set PROXMOX_MCP_ALLOWED_HOSTS=* only when something in front already validates it.

There is no authentication. Anyone who can reach the endpoint gets whatever PROXMOX_RISK_LEVEL allows. Put it behind a gateway, an authenticating proxy or a network boundary you trust, and keep PROXMOX_RISK_LEVEL=read unless the path to it is authenticated.

Health endpoints are served alongside /mcp and need no auth:

PathMeaning
GET /healthzThe process is up. Never touches Proxmox — a liveness probe that fails when Proxmox is down would restart the server in a loop and fix nothing.
GET /readyzProxmox answered version.get(). This is the one that should gate traffic; 503 with a readable reason otherwise.
yaml
livenessProbe:  httpGet: { path: /healthz, port: 8000 }readinessProbe:  httpGet: { path: /readyz, port: 8000 }

Flags mirror the variables for interactive use: uvx proxmox-ve-mcp --transport streamable-http --port 8080.

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:

bash
uvx --from 'proxmox-ve-mcp[sentry]' proxmox-ve-mcp
VariableDefaultDescription
SENTRY_DSN—Set it to enable reporting
SENTRY_ENVIRONMENTproductionFree-form environment label
SENTRY_TRACES_SAMPLE_RATE1.0Share of tool calls traced
SENTRY_SEND_DEFAULT_PIIfalseSend 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)

ToolDescription
list_nodesList all cluster nodes with status, CPU, memory, uptime
get_node_statusDetailed node metrics (CPU, memory, disk, load, kernel)
get_node_networksNetwork interfaces on a node
get_node_disksPhysical disks on a node
get_node_servicesProxmox system services and their state
get_node_updatesPending APT package updates
get_node_rrd_dataHistorical CPU/memory/disk/network metrics (RRD)
get_node_tasksRecent tasks on a node, optionally errors only
get_task_statusStatus of a specific task by UPID
get_task_logLog output from a task

QEMU VMs (17)

ToolTierDescription
list_vmsreadList all VMs, optionally filter by node
get_vm_statusreadCurrent VM status (running/stopped, CPU, memory)
get_vm_configreadVM configuration (hardware, disks, network)
get_vm_network_interfacesreadIP addresses of a running VM (via QEMU guest agent)
get_vm_rrd_datareadHistorical CPU/memory/disk/network metrics (RRD)
list_vm_snapshotsreadList all snapshots of a VM
start_vmlifecycleStart a VM
stop_vmlifecycleForce-stop a VM — annotated destructive, unsaved guest state is lost
shutdown_vmlifecycleGraceful ACPI shutdown with timeout
reboot_vmlifecycleReboot via ACPI
suspend_vmlifecycleSuspend a VM
resume_vmlifecycleResume a suspended VM
clone_vmlifecycleFull or linked clone
migrate_vmlifecycleMove a VM to another node, online or offline
create_vm_snapshotlifecycleCreate a snapshot
delete_vm_snapshotallDelete a snapshot
rollback_vm_snapshotallRollback to a snapshot

LXC Containers (13)

ToolTierDescription
list_containersreadList all LXC containers, optionally filter by node
get_container_statusreadCurrent container status
get_container_configreadContainer configuration
get_container_interfacesreadIP addresses of a running container
get_container_rrd_datareadHistorical CPU/memory/disk/network metrics (RRD)
list_container_snapshotsreadList all snapshots
start_containerlifecycleStart a container
stop_containerlifecycleForce-stop a container — annotated destructive
shutdown_containerlifecycleGraceful shutdown with timeout
reboot_containerlifecycleReboot a container
create_container_snapshotlifecycleCreate a snapshot
delete_container_snapshotallDelete a snapshot
rollback_container_snapshotallRollback to a snapshot

Storage (2)

ToolDescription
list_storageStorage pools with usage, optionally filter by node
get_storage_contentContents of a storage pool (ISOs, backups, images, templates)

Cluster (7)

ToolDescription
get_cluster_statusCluster health, quorum, node membership
get_cluster_resourcesAll resources (VMs, containers, storage, nodes)
get_cluster_backupsConfigured backup jobs
get_ha_statusHigh-availability resources and their state
list_poolsResource pools
get_cluster_logCluster-wide event log, newest first
get_next_vmidNext available VM/container ID

Server (1)

ToolDescription
get_server_infoThis server's own risk level, tool count, versions and Proxmox reachability

Architecture

src/proxmox_mcp/├── server.py    # MCPServer instance, health routes, transport selection, entry point├── config.py    # Pydantic Settings (PROXMOX_) + transport config (PROXMOX_MCP_)├── client.py    # Proxmoxer connection, built once per process└── tools/       # nodes, vms, containers, storage, cluster, server_info
  • Read-only by default — elevated tools gated by PROXMOX_RISK_LEVEL
  • Gated at registration — a tool above the tier is absent from tools/list, not refused at call time: what the model cannot see, it cannot retry
  • Admission and annotations are separate — tier= is the operator's policy, annotations= is what the client is told a call does, so a force-stop can be destructiveHint: true and still live at the lifecycle tier
  • 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 requests traceback

Development

Run standalone (testing)

bash
export PROXMOX_HOST=192.168.1.100export PROXMOX_USER=root@pamexport PROXMOX_TOKEN_NAME=mcpexport 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)

bash
git clone https://github.com/akmalovaa/proxmox-mcp.git && cd proxmox-mcp && uv sync

MCP client config:

json
{  "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

bash
git clone https://github.com/akmalovaa/proxmox-mcp.gitcd proxmox-mcpdocker 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

bash
uv sync --locked --group devuv run ruff check .uv run mypy src/uv run pytest -v

License

MIT

来源:README.md,提交 ff252d4

工具

0
SourceWeft 尚未在沙箱中启动过该软件包,因此其工具未知。

版本历史

1
  1. v2.3.0最新Sep 16, 2026

评分

已满足适用检查项的 50%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分

    请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。

  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分