Skip to content

feat(proxmox-mcp): add Proxmox MCP — MCP server for Proxmox VE - #156

Open
pofallon wants to merge 4 commits into
mainfrom
feat/proxmox-mcp
Open

pofallon wants to merge 4 commits into
mainfrom
feat/proxmox-mcp

Conversation

@pofallon

@pofallon pofallon commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Packages RekklesNA/ProxmoxMCP-Plus (MIT) — an MCP server for Proxmox VE (nodes, VMs, containers, snapshots, backups, task logs) — so an AI agent can manage a Proxmox cluster through one authenticated endpoint at https://proxmox-mcp.<HOLA_BASE_DOMAIN>/mcp.

  • Image: ghcr.io/rekklesna/proxmoxmcp-plus:v0.5.15 (digest-pinned, linux/amd64 + linux/arm64, runs as an unprivileged user). One service, no host ports, no data volume — the optional job store is ephemeral inside the container, so accepts is intentionally empty.
  • Transport: the image defaults to its OpenAPI/REST bridge on 8811; this package runs it in native MCP Streamable HTTP mode on 8000, which is the transport try-hola/hola's aggregated-gateway spec (002) requires, so it joins that gateway later with a manifest mcp block rather than a repackage.
  • Config is env-only. The image's PROXMOX_MCP_CONFIG file is never mounted; the loader falls back to the wizard fields (host, API user, token name/secret required; port, TLS verification and the tool denylist as advanced fields). Verified against the loader source and by running the image.
  • Auth: forward-auth with bypassPaths: ["/mcp"] — the remo setup-API pattern. /mcp is exempted from the interactive Authentik login because the app protects it with its own generated bearer credential (MCP_API_KEY, generate: hex); MCP clients are not browsers, so a cookie gate would only lock them out. Everything else stays behind SSO.
  • Defensive defaults: MCP_TOOL_DENYLIST hides delete_vm, delete_container, delete_snapshot, rollback_snapshot (clearable in the wizard); guest command execution is additionally gated by upstream's deny-all command policy; DNS-rebinding protection allows only the public host plus loopback; the README tells operators to use a dedicated Proxmox user + token, not root@pam.
  • Healthcheck: the MCP transport has no unauthenticated health route, so the check accepts any HTTP answer on /mcp (a 401 without the key) rather than curl -f.
  • Icon: upstream's own 400px logo in an SVG wrapper (same approach as remo.svg), noted in icons/README.md.

One thing the smoke test caught

The MCP Python SDK's host allowlist matches a host:* pattern only when the Host header carries a port, and Traefik forwards the bare public host. An allowlist of just ${HOLA_APP_HOST}:* answered 421 to every authenticated request behind a proxy. The compose therefore lists each host both bare and with :*; with that, initialize returns 200, a foreign Host is rejected with 421, and the unauthenticated probe returns 401 (all verified by running the pinned image locally).

Checks

  • node bin/validate-manifest.mjs src/proxmox-mcp/src/manifest.json — OK
  • ./bin/build-catalog.sh builds cleanly (entry looks right; catalog.json left for CI to regenerate) and node bin/validate-catalog.mjs — OK
  • The real validateComposeDocument from @hola/shared/compose-validate against compose.yaml — 0 issues
  • CI's verify-packages layout checks replicated locally — pass
  • Ran the pinned image in mcp-http mode with env-only config: startup clean, POST /mcp initialize → 200 with the bearer key, 401 without, 421 for a wrong host

Not yet done: an install on a disposable Hola VM against a real Proxmox host (bin/lib/app-test.sh). The endpoint and auth path are exercised above; the Proxmox connection itself needs real credentials.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Vck5KSX2CLxhohx14nb5Sh

Update 2026-09-12 — 0.1.0-beta.1 on the fork image

The PR now publishes a beta-channel pre-release (0.1.0-beta.1, published from this PR as ghcr.io/try-hola/proxmox-mcp:0.1.0-beta.1) that runs ghcr.io/pofallon/proxmoxmcp-plus:v0.5.15-hola.1 — upstream v0.5.15 plus the VM provisioning tools proposed in RekklesNA/ProxmoxMCP-Plus#127 (update_vm_config, get_vm_ip_addresses, get_next_vmid). Also added: PROXMOX_DEV_MODE (upstream refuses unverified TLS without it) and the guest command-policy fields.

Installed and exercised on a real Hola host (Authentik mode, PVE 9.1 lab): hola install ghcr.io/try-hola/proxmox-mcp:0.1.0-beta.1 … → running; /mcp answers 401 without the key while / redirects to Authentik; a full get_next_vmid → clone_vm → update_vm_config → start_vm → get_vm_ip_addresses → ssh → stop_vm → delete_vm cycle succeeded through the public endpoint.

Once upstream ships the tools, the stable 0.1.0 returns to the upstream image and the beta retires per the release-channel retention rule.

Paul O'Fallon and others added 3 commits September 8, 2026 11:15
Packages RekklesNA/ProxmoxMCP-Plus (MIT) in its native MCP Streamable HTTP
mode so an AI agent can manage a Proxmox VE cluster through one authenticated
endpoint at https://proxmox-mcp.<HOLA_BASE_DOMAIN>/mcp.

- Image ghcr.io/rekklesna/proxmoxmcp-plus:v0.5.15, digest-pinned, multi-arch,
  unprivileged user. No host ports; one service; no data volume (the optional
  job store is ephemeral).
- Config is env-only: the image's PROXMOX_MCP_CONFIG file is never mounted, so
  the loader falls back to the wizard fields (host, user, token name/secret,
  port, TLS verification).
- Auth: forward-auth with bypassPaths ["/mcp"], the remo setup-API pattern —
  /mcp is protected by the app's own generated MCP_API_KEY bearer credential;
  everything else stays behind Authentik.
- Defensive defaults: MCP_TOOL_DENYLIST hides delete_vm, delete_container,
  delete_snapshot, rollback_snapshot (advanced field, clearable); DNS-rebinding
  protection allows only the public host plus loopback for the healthcheck.
- Healthcheck probes /mcp for any HTTP answer (a 401 without the key) since the
  MCP transport has no unauthenticated health route.
- Icon: upstream's own 400px logo in an SVG wrapper (remo precedent).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vck5KSX2CLxhohx14nb5Sh
… fields

An agent that provisions VMs through this server can clone/start/stop/delete
them, but the tool set has no cloud-init config write, so the one thing it
still needs is an allowlisted guest-agent command to inject an SSH key into a
fresh clone. COMMAND_POLICY_MODE (deny_all by default) and
COMMAND_POLICY_ALLOW_PATTERNS make that an explicit, narrow opt-in.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vck5KSX2CLxhohx14nb5Sh
…flag

Running the image against a lab host with a self-signed certificate showed
that PROXMOX_VERIFY_SSL=false alone makes the server exit at startup
("Insecure TLS configuration blocked … Only dev_mode=true can allow
verify_ssl=false"). Expose PROXMOX_DEV_MODE as an advanced boolean, wire it
in compose, and cross-reference the two fields in the wizard text and README.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vck5KSX2CLxhohx14nb5Sh
…oning tools

Publishes a beta-channel pre-release from this PR (per the release-channel
flow) that runs ghcr.io/pofallon/proxmoxmcp-plus:v0.5.15-hola.1 — upstream
v0.5.15 plus update_vm_config / get_vm_ip_addresses / get_next_vmid
(RekklesNA/ProxmoxMCP-Plus#127) — so an agent can provision disposable VMs
through the app today. Digest-pinned, multi-arch. The stable version goes
back to the upstream image once a release carries the tools.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vck5KSX2CLxhohx14nb5Sh
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant