Sandbox
@john-broadway/proximo

MCP server for Proxmox VE, PBS, PMG, and PDM

Proximo gives agents one controlled way to work with Proxmox infrastructure. It exposes tools for reading state, planning changes first, taking snapshots where possible, and recording tamper-evident audit entries after actions.

43 stars8 forksPythonUpdated 11d ago
Who it's for

Builders who want an agent to inspect and change Proxmox systems without handing over blind root access.

What it delivers

You can let an agent operate Proxmox with previews, receipts, and scoped credentials instead of raw shell access.

What it does

One control plane for four Proxmox products

Covers Proxmox VE, Backup Server, Mail Gateway, and Datacenter Manager through a single tool surface.

Plan before mutation

State-changing calls return a plan and blast radius first, then run only when confirmed.

Tamper-evident audit ledger

Records actions in a hash-chained ledger that can be verified later with `audit_verify`.

Undo where the platform supports it

Can return prior config, create snapshots before risky commands, and roll back guests when possible.

Read-only by default

Starts with inspection and diagnosis tools, so you can preflight access before granting write scope.

Multiple agent-facing transports

Works as MCP, and also offers A2A and HTTP/OpenAPI faces for other client styles.

How to get it

  1. 1Claude Code, one line
    claude mcp add proximo --env PROXIMO_API_BASE_URL=https://your-pve:8006/api2/json \
      --env PROXIMO_NODE=your-node --env PROXIMO_TOKEN_PATH=/path/to/token-file -- uvx proximo-proxmox
  2. 2Then preflight what your token can actually do (read-only)
    uvx proximo-proxmox doctor

README

ProximoProximo

CI CodeQL Release PyPI Python 3.12+ License Apache-2.0

OpenSSF Scorecard OpenSSF Best Practices Glama score LobeHub — grade, tools, prompts

Enter the ludus ↗ · Quickstart · Setup · Trust layer · Demo · Tools · Install · Security · Docs

Named for Proximo, the lanista of Gladiator. The story is the design, joint for joint.

He armed his fighter with exactly what he needed, never more. He answered for every move in the arena. A lanista, not a jailer. The Spaniard earns his name by conduct, on the record, and the helmet comes off: truth said plainly, at cost. His last act opened the cages, holding the wooden sword of his own freedom. A tool should hope to end that well.

"Win the crowd and you will win your freedom."

The others make you pick: a read-only toy, or full keys and pray. Proximo won't. Every dangerous move is planned: see the blast radius first. Every move is proven: a tamper-evident record. And undoable wherever the platform gives us a primitive: a config change hands back the exact prior state, and a risky in-container command can take a snapshot first, and refuses to run if it can't.

Trust built into the substrate, not bolted on after. Hand an AI agent the keys; keep the receipts.

Sovereign and agent-agnostic. Your metal, your token, a ledger you own. No cloud, no phone-home, no standing server unless you opt in.

What it does

Ask, in plain English: "why is ct 105 thrashing?" An AI agent pulls node and guest status, tails the logs, and runs a diagnostic inside the container to find out.

If there's a fix, it shows you the plan before it touches anything. Takes a snapshot first if you ask it to, and won't run if it can't. Applies. Hands you a signed receipt of exactly what changed.

That's the product: a hypervisor an AI can operate without being able to wreck it.

Read-only by default. No mutation runs on the first call: it returns its blast radius as a plan for you to see first. A tamper-evident receipt for every change.

The comparison isn't Proximo vs. the GUI. It's Proximo vs. handing an LLM your root token and hoping.

Don't take our word for any of it. Verify it yourself.

Verify in 60 seconds: three receipts, no trust required
# 1. The tool count is real. Ask the server itself, cold (=> 908).
#    (in a clone of this repo, after `uv sync`)
uv run python -c "import asyncio; from proximo import server; \
print(len(asyncio.run(server.mcp.list_tools())))"

# 2. The container image is what the repo built. Sigstore provenance (exit 0 = verified):
gh attestation verify oci://ghcr.io/john-broadway/proximo:latest --owner john-broadway

# 3. The security posture is graded by a third party, not by us:
#    https://scorecard.dev/viewer/?uri=github.com/john-broadway/proximo

The rest is in VERIFY.md: forge a ledger byte and watch verify() refuse, grep the outbound surface for phone-home (there is none). These checks work on any tool, from any vendor. Demand them everywhere.


Proximo architecture: MCP clients (stdio and Streamable HTTP), A2A, and HTTP/OpenAPI clients all land on one governed spine, pass the six-pillar trust spine (PLAN, PROVE, UNDO, DIAGNOSE standing by default; CONSENT and CONTAIN yours to raise), sit on the Proxmox-enforced token floor, and reach four products — PVE, PBS, PMG, PDMProximo architecture: MCP clients (stdio and Streamable HTTP), A2A, and HTTP/OpenAPI clients all land on one governed spine, pass the six-pillar trust spine (PLAN, PROVE, UNDO, DIAGNOSE standing by default; CONSENT and CONTAIN yours to raise), sit on the Proxmox-enforced token floor, and reach four products — PVE, PBS, PMG, PDM

Every transport enters one governed dispatch and crosses the same trust spine; the token floor beneath it all is enforced by Proxmox itself.
Watch it hold in the Demo.

Quickstart

// your MCP client config (Claude Desktop / Claude Code / Cursor / …)
{
  "mcpServers": {
    "proximo": {
      "command": "uvx",
      "args": ["proximo-proxmox"],
      "timeout": 60,                                     // startup is ~3.5s; a 3s client default drops the server silently
      "env": {
        "PROXIMO_API_BASE_URL": "https://your-pve:8006/api2/json",
        "PROXIMO_NODE": "your-node",
        "PROXIMO_TOKEN_PATH": "/path/to/token-file"   // USER@REALM!TOKENID=SECRET, by reference, never inlined
      }
    }
  }
}

Claude Code, one line:

claude mcp add proximo --env PROXIMO_API_BASE_URL=https://your-pve:8006/api2/json \
  --env PROXIMO_NODE=your-node --env PROXIMO_TOKEN_PATH=/path/to/token-file -- uvx proximo-proxmox

Or install with one click:

Install in VS Code Install in Cursor

Both prompt for the token file path; the secret never lands in client config. No token yet? uvx proximo-proxmox mint prints the least-privilege runbook.

Then preflight what your token can actually do (read-only):

uvx proximo-proxmox doctor

Start with a read-only token. Proximo is useful long before you grant it write. Full token-first walkthrough: docs/SETUP.md · more install paths: Install & run.

Why Proximo exists

The Proxmox MCP landscape is split. API-based servers manage nodes and VMs but structurally cannot run a command inside an LXC: the REST API has no exec endpoint. SSH-based servers can, through broad shell access with little scoping.

Proximo builds the principled whole. Both halves, one audited surface, least-privilege. Trust by construction:

Read-only inspectorFull-access executorProximo
Can mutateno, that's the safetyyesyes, plan recorded first, then confirm=true
Preview before a changen/ararelydefault: blast radius + live state, every mutation
Record of what happenednoapp logs, editablekeyed hash-chained ledger, tamper-evident, on by default
Undon/araresnapshot-first, wherever the platform can snapshot
Command inside an LXCnobroad SSHopt-in, fail-closed CTID allowlist
Products coveredusually PVEusually PVEPVE + PBS + PMG + PDM, one audited plane
Verify the artifact you runvariesvariessigned image · PyPI provenance · SBOM · Scorecard

(The archetype columns describe the split above, not any specific project. There is no official Proxmox MCP; Proximo is a community project, standing on its own.)

The trust layer: what makes Proximo different

The spine has six pillars. Four stand by default:

ControlWhat it does
PLANEvery mutation first returns a recorded preview: the exact change, live state, blast radius, an advisory risk rating. Nothing mutates without its plan recorded; one confirm=true call records and performs.
PROVEKeyed (HMAC-SHA256), hash-chained audit ledger; audit_verify catches edits, reordering, insertion. Pin the head off-box (expected_head) to catch truncation too: that's the strong guarantee, and it's opt-in.
UNDOWhere the platform has a primitive: a config change returns its prior_config automatically (revert with pve_guest_config_revert), ct_exec/ct_psql take snapshot=true for an auto-snapshot and then fail closed (if the snapshot can't be taken the command does not run) and pve_rollback restores a guest snapshot. The exec snapshot is per call, not automatic. Planes with no snapshot primitive (firewall/SDN/ACL) have no rollback, said plainly.
DIAGNOSERead-only evidence battery + node health → advisory flags that surface incompleteness too, so an empty list never reads as a false clean bill.

Two are yours to raise, by design, off until their state paths exist, because both are only worth having if those paths sit outside the agent's reach. A pillar Proximo raised for you would be a pillar the agent could lower for itself:

Pillar (off until configured)What it holds
CONSENTIndependent, out-of-band approval per plan: an agent (compromised, confused, or steered by injected text) cannot confirm its own mutation. Grants live in a directory only you write (PROXIMO_CONSENT_DIR), expire on a TTL, and never clear a taint.
CONTAINThe kill-switch: one trip file halts every mutation immediately, mid-incident, no redeploy and no restart. Checked fresh on every mutation; fails closed. Put the trip path where only you can write (PROXIMO_CONTAIN_TRIP_PATH).

proximo doctor reports the spine: which pillars stand, which sockets are empty, and exactly how to fill them. Five more controls ship off until configured: an arm-LEASE, an arm-time SCOPE, a FORBID/RATE ENVELOPE, TAINT (the prompt-injection mitigation), and PRINCIPAL (who-asked attribution). What each one defends against: SECURITY.md.

Honesty note (load-bearing): risk ratings are an advisory heuristic, not a sandbox — LOW means "no state change," not "safe," and the absence of a HIGH flag is not a safety signal. Review every change yourself. The floor beneath it all is the token you mint: Proxmox RBAC holds even if Proximo's process is fully compromised — a stronger guarantee than anything Proximo's own code provides. Scope it to exactly what you mean to grant: SECURITY.md.

Hold any tool to this, including this one: The Keys Test. Ten questions to ask before you hand an AI agent real infrastructure. Proximo's own scorecard published, partials included.

Demo

The record defends itself:

Hand-the-keys demo: three agent moves land in the keyed hash-chained ledger and audit_verify answers ok=True keyed=True; an in-place edit breaks the chain at the exact line (ok=False); a truncation that fools the forward walk is caught by the pinned head

Three agent moves land in the keyed ledger; one entry gets edited in place; audit_verify() breaks at the exact line, ok=False; the truncation a forward walk would miss is caught against the pinned head. Real code, real crypto, nothing staged, recorded on 0.30.0. Run it yourself anywhere: scripts/demo/hand_the_keys.py (needs only the pip package) · against your own host: --live · verify by hand: VERIFY.md.

Surfaces & tools: one control plane

SurfaceBackendFor
Proxmox VEREST API + scoped tokennode/guest lifecycle, storage, SDN, identity, HA, firewall
Proxmox Backup ServerREST API + scoped tokendatastores, namespaces, snapshots, sync, GC, verify, tape
Proxmox Mail GatewayTicket authmail flow, quarantine, filtering rules, domains, services
Proxmox Datacenter ManagerAPI tokenfederated fleet: reads plus governed control (power/snapshot/migrate, dry-run-first)
Container execsshpct execrun-command-in-container, psql, log tailing: what the API structurally can't do

Those backends are deliberately boring. Anyone can call them. The product is the trust layer over them.

908 tools is an estate, not a starting point, and you only carry the part you use. Since 0.30 the floor IS the default: a bare install serves the search-and-call facade (~1,740 tokens of context) with every tool this box serves still callable; one domain like pve.guests runs ~9,781, a whole plane ~101,398, PROXIMO_TOOLSETS=catalog the classic auto-scoped catalog. The estate is 908. The doorway is yours to size. Coverage and context stopped being the same number.

Where an operator actually starts:

You want to…Start withWorth knowing
See the whole cluster at oncepve_cluster_resources, pve_list_guestsone call, every node
Find out why a container is sickct_diagnose, ct_logs, pve_guest_statusread-only evidence battery
Preflight a token / configproximo doctor (CLI) or pve_doctor, pve_overbroad_grantsrun this before wiring an agent
Power / lifecyclepve_guest_powerreturns a PLAN first; nothing moves without confirm=true
Snapshot before touching anythingpve_snapshot_create, pve_rollbackUNDO's foundation
Check backups are actually freshpve_backup_freshness, pbs_snapshots_listwalks real archives; "task OK" is never evidence
Run a command in a containerct_execopt-in (PROXIMO_ENABLE_EXEC=1), fail-closed allowlist
Trace / release mailpmg_tracker_list, pmg_quarantine_spamfull PMG plane behind it
Operate the federated fleetpdm_resources_list, pdm_pve_lxc_listgoverned control, dry-run-first
Prove the record wasn't touchedaudit_verifyregistered on every surface, always

Every tool with typed inputs: docs/TOOLS.md · sizing the surface to your model: docs/SETUP.md.

Install & run

📦 0.40.0: on PyPI, GitHub, and GHCR (signed multi-arch image).

New in 0.40.0 (doctor says where near-root exec lands). ct_exec and the node shell ride an ssh target or run on the box itself, never the API, so the machine the API reads and the machine a near-root command lands on can differ, and nothing said so. proximo doctor now reports where exec lands, resolves the ssh target through ssh's own config, counts every name and address this machine goes by as one host, and flags a split target with a remedy you can follow as written. The shadow-key flag also compares every set-valued key the way its gate reads it, so a reordered allowlist is no longer a change, and the TLS warning counts a pinned fingerprint as verification.

Recent: 0.39.1 made every allowlist refusal name the store that fed it. See SECURITY.md for what each control honestly holds.

Proximo runs on your machine, on demand. No daemon, no open port.

uvx proximo-proxmox            # zero-install run (PyPI package: proximo-proxmox; command stays `proximo`)
# or: pip install proximo-proxmox            the MCP core
# or: pip install "proximo-proxmox[a2a]"     + the optional A2A face
# or: pip install "proximo-proxmox[http]"    + the optional HTTP/OpenAPI face
# or: pip install "proximo-proxmox[mcp-http]" + the optional MCP-over-streamable-HTTP face
# or, from source:  git clone https://github.com/john-broadway/proximo.git && cd proximo && uv pip install -e .

Wire it into your MCP client as the command proximo, with the PROXIMO_* env vars; see packaging/proximo.env.example.

Docker (GHCR): docker run -i --rm … ghcr.io/john-broadway/proximo:latest. Multi-arch, SBOM, sigstore-signed provenance (gh attestation verify oci://ghcr.io/john-broadway/proximo --owner john-broadway). Mirrored to Docker Hub (docker.io/jebroadway/proximo, identical digest); GHCR stays the signed primary.

Safe by default: API-only out of the box. The two near-root edges are opt-in and say so loudly: LXC exec (PROXIMO_ENABLE_EXEC=1, near-root on the host) and the qemu-guest-agent edge (PROXIMO_ENABLE_AGENT=1, near-root in a guest). Each is scoped by its own fail-closed allowlist.

Smallest footprint by design: you don't have to load the whole estate: what a box serves is autoscoped to what it configures. A PBS-only box gets that plane's tools plus the always-on audit trail; PROXIMO_SURFACES=pve,exec scopes the searchable catalog to that pair (318 tools); a typo'd surface refuses startup rather than serving a surprise. Surfaces choose which planes are searchable, never how many schemas load; the doorway stays the default unless you name another with PROXIMO_TOOLSETS. Scoping is context hygiene, not an authorization control: it changes what is advertised, never what a token is allowed to do. The default doorway (dynamic mode) keeps four search-and-call tools resident (proximo_read runs read-only tools with an enforced readOnlyHint; proximo_call runs anything) plus the two ledger tools (audit_verify proves the chain, audit_entries reads who did what) and proximo_recall while estate memory is on (the default; PROXIMO_MEMORY=0 opts out), with the full catalog reachable by name. That narrowing is guarded at every entry point (0.27.0 closed a path where an opt-in flag could silently cut the registry to 5 tools), and the gates don't shrink with the doorway: PLAN and PROVE apply however small the visible surface gets.

The network faces (experimental, opt-in): proximo-a2a speaks Agent2Agent. proximo-http serves plain HTTP + generated /openapi.json for no-code clients. proximo-mcp-http serves MCP itself over Streamable HTTP (the SDK's native transport) for networked MCP clients: no third-party stdio→HTTP bridge, so the perimeter stays Proximo's.

All three serve the full surface through the same spine as MCP. No second code path; trust spine and token scope inherited. Fail-closed perimeter: loopback, bearer-token required off-localhost, DNS-rebind and CSRF defended. Details: SECURITY.md.

At scale

One container is the demo. A cluster is the point.

  • The whole cluster in one call. pve_cluster_resources: every VM, node, storage pool, SDN object.
  • One tamper-evident record across every node. "Show me every state-changing action this month, and prove the log wasn't touched" becomes a query you can actually answer. No human at the CLI walks away with that.
  • Where the time comes back. On one node a senior at the CLI is faster, and that's fine. Across a dozen nodes and hundreds of guests, a bounded, audited agent earns its keep.

Many boxes, one Proximo: register remotes in a TOML file (secrets by reference, never inlined), point PROXIMO_TARGETS at it, aim any tool with proximo_target="edge-pve". The target travels with the call. PLAN and EXECUTE hit the same box, the ledger records which, cross-plane calls error. Config shape: packaging/targets.example.toml.

Status: the arena record

  • 🩸 0.40.0: doctor says where near-root exec lands. The shell lane rides an ssh target or the box itself, never the API, so the two halves of one config can name different machines and nothing said so. Doctor now reports the landing host and flags a split target with a remedy you can follow as written; shadow flags compare every set-valued key the way its gate reads it, so a reordered allowlist is no longer a change.

Every release before it (every pillar, every redteam, every fix) lives in CHANGELOG.md.

The numbers, honestly: 908 MCP tools, proved in two deliberate layers. 12,000+ in-process tests (ruff + pyright clean) pin every tool's shape. A separate live-smoke harness drives real Proxmox hardware: a 3-node PVE 9.2 cluster, PBS 4.2, PMG 9.1, PDM 1.1.4, a real cross-datacenter move. The two are kept apart on purpose: passing shape tests never gets to masquerade as "works on a real host." And this workspace administers its own Proxmox estate through Proximo daily (dogfood). The blast-radius engine carries the destructive surface: across eleven op-classes it names the specific guests, nodes, principals, or disks at risk. Nothing falls back to a bare confirm.

Proven live (not mocks): the trust spine end-to-end; identity/storage/SDN/firewall/HA create→read→delete with the ledger verified throughout; offline + online live-migration and HA fencing (softdog) on a real 3-node cluster; full PBS/PMG/PDM planes including a real cross-datacenter move. **Not yet proven — sai

Files in the repo

Repository payload24 top-level entries
  • .github
  • debian
  • docs
  • packaging
  • requirements
  • scripts
  • src
  • tests
  • .dockerignore
  • .gitignore
  • .gitleaks.toml
  • AGENTS.md
  • CHANGELOG.md
  • Dockerfile
  • glama.json
  • lhm.plugin.json
  • LICENSE
  • llms-install.md
  • pyproject.toml
  • README.md
  • SECURITY.md
  • server.json
  • uv.lock
  • VERIFY.md

Discussion (0)

Ask about usage, or say what you built with it

Sign in to join the discussion.

No comments yet. Be the first to say what this is good for.

More connectors

High-performance code intelligence MCP server. Indexes codebases into a persistent knowledge graph — average repo in milliseconds. 158 languages, sub-ms queries, 99% fewer tokens. Single static binary, zero dependencies.

43k

Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code

14k
okf-memory/
okf-agent-memory

Git-native persistent memory for AI coding agents. Implements Google OKF v0.2 with sub-300µs in-memory BM25 search, embedded MCP server, and progressive disclosure. Slashes token bloat by 80% with zero external databases or dependencies. Built in pure Go.

547
tirth8205/
code-review-graph

Local-first code intelligence graph for MCP and CLI. Builds a persistent map of your codebase so AI coding tools read only what matters, with benchmarked context reductions on reviews and large-repo workflows.

31k
2akouwu/
reverify

Stop your AI from making things up — it proposes, deterministic tools decide, every claim checked against ground truth with evidence. Grounded facts and context survive resets. Reverse engineering is the proving ground. MCP server + CLI.

1.1k
t8y2/dbxConnectors

20 MB lightweight cross-platform database client for 90+ databases, including MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, SQL Server, and Dameng. Built-in AI, MCP Server, CLI, desktop and Docker. | 轻量级跨平台数据库管理工具,支持 MySQL、PostgreSQL、SQLite、Redis、MongoDB、达梦等 90+ 数据库,提供桌面端、Docker、CLI、内置 AI 助手和 MCP Server。

19k