Skip to content

Quickstart: LLM agent / MCP server

You're wiring Claude, Cursor, or any MCP-compatible agent into a Proxmox cluster. You want a deterministic tool surface, audit- pinned via SHA-256, with a HITL gate on destructive ops. You do NOT want to write a custom integration.

This page gets you to a working proxxx mcp serve in ~5 min.

TIP

The MCP surface is compile-time fixed — the agent sees exactly 25 tools, no more, no less. Adding a tool requires a proxxx PR and a release. This is intentional: prompt-injection attacks can't extend the tool registry at runtime. The registry is append-only across SemVer releases.

proxxx MCP server — 25 typed tools, 10 flagged destructive; an MCP tools/call list_guests returns live guest data to the agent

1. Configure proxxx itself

You need a working config.toml first. Quickest path: run proxxx init --interactive against your cluster and validate with proxxx ls nodes. If that returns a table, you're ready.

2. Inspect the tool registry

bash
proxxx mcp tools

Returns the full registry as JSON: 25 tools across five clusters — inventory / reads (list_nodes, list_guests, get_guest_status, get_storage_pools, get_node_resources, list_snapshots, get_task_log, get_cluster_status, get_node_status, get_replication_status, list_tasks, list_backup_jobs, list_cluster_events), lifecycle (start_guest, stop_guest, restart_guest, suspend_guest, resume_guest, delete_guest), snapshots (create_snapshot, delete_snapshot), provisioning (clone_guest, clone_with_cloudinit, create_guest), and migration (migrate_guest). Each with parameters, descriptions, destructive flag, and per-tool execution timeout.

For supply-chain pinning:

bash
proxxx mcp tools --checksum
# → { "checksum": "8467de772787baa0" }

Pin this hash in your CI / agent config. If the deployed binary's registry hash drifts, the tool surface changed — review the diff before unpinning.

3. Configure HITL on destructive tools (required)

Destructive tools are fail-closed: a destructive call with no matching [[policies]] entry is refused before it ever reaches the PVE gateway — the agent gets a typed isError envelope, not an autonomous delete_guest. There is no ungated inline path. The only way to run a destructive tool over MCP is a matching [[policies]] entry that routes it through HITL approval:

toml
# config.toml
[telegram]
bot_token = "<bot-api-token>"
chat_id = "<your-chat-id>"

[[policies]]
when = { action = "delete" }
require = "telegram"
channel = "telegram"

[[policies]]
when = { action = "stop", tag = "prod" }
require = "telegram"
channel = "telegram"

The 120 s deny-on-timeout is hardcoded — if you don't approve, the op is rejected (NOT auto-approved). Replay protection is session-local: a stale callback from yesterday's chat history won't re-fire.

The tools flagged destructive — and therefore refused without a matching policy — are stop_guest, restart_guest, suspend_guest, delete_guest, create_snapshot, delete_snapshot, clone_guest, clone_with_cloudinit, create_guest, and migrate_guest. Everything else — start_guest, resume_guest, and every get_* / list_* read — runs inline, no approval required.

Test the round-trip:

bash
proxxx mcp serve &        # background-launch
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"delete_guest","arguments":{"guest_id":999}}}' \
  | nc -q1 - <(proxxx mcp serve)
# → Telegram message arrives → tap Approve → daemon runs → response

4. Wire up Claude Desktop

json
// ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)
// ~/.config/Claude/claude_desktop_config.json (Linux)
{
  "mcpServers": {
    "proxxx": {
      "command": "/usr/local/bin/proxxx",
      "args": ["mcp", "serve", "--profile", "homelab"]
    }
  }
}

Restart Claude Desktop. The agent now sees the 25-tool surface in any conversation. Prompt: "List the running VMs on my cluster and stop the one tagged 'staging'." — Claude calls list_guests, filters, calls stop_guest, the HITL gate fires on Telegram, you approve, op executes. Long-running operations (migration, restore) stream notifications/cluster-event so the agent sees started and completed without polling.

5. Wire up Cursor or another IDE

Same JSON shape, different config path. See your client's MCP docs for the exact location.

6. Operational notes

  • Per-tool timeouts (timeout_secs in mcp tools output) bound a single tool call. Default 30 s for read-ops, 60 s for start/stop/restart, 120 s for snapshots, 180 s for delete_guest (which has to wait through the 120 s HITL window). On expiry the call returns JSON-RPC error code -32001; the JSON-RPC loop continues — one slow tool can't brick subsequent calls.

  • Two transports at parityproxxx mcp serve (stdio JSON-RPC, agent runs as subprocess, no port) or proxxx mcp serve-http (Streamable HTTP/SSE per MCP 2025-03-26; defaults to --bind 127.0.0.1 --port 3000). Same tool registry, same HITL, same audit. HTTP is for hosted agents that can't spawn subprocesses; stdio for local agents (Claude Desktop, Cursor). Pick one per deployment.

  • serve-http is fail-closed on the network — on a non-loopback bind (e.g. --bind 0.0.0.0) the server refuses to start unless mcp_token is set. Configure it in the profile (mcp_token = "…") or pass --token; every POST /mcp and GET /mcp then demands a matching Authorization: Bearer header. An empty / whitespace token counts as absent — so a SIGHUP that clears mcp_token leaves the exposed server denying every request instead of silently opening up. To expose a public interface without a token (trusted network or an auth-enforcing reverse proxy only) you must consciously pass --insecure-bind. Loopback binds need no token.

  • Server-sent notifications — both transports stream notifications/cluster-event for task_state_change (started / completed / failed) and incident (frozen / thawed). Over stdio, these are JSON-RPC 2.0 notification lines interleaved with the request/response stream; over HTTP they arrive as SSE events on GET /mcp. Subscribe via notifications/subscribe (informational ack only — delivery flows automatically on the same channel).

  • Audit trail — every tool invocation logs to the proxxx audit log (same path as CLI / TUI). HITL callbacks log separately under crate::hitl::daemon.

  • Read-only mode? Comment out the delete_guest, stop_guest, restart_guest, create_snapshot, delete_snapshot entries from your agent's allowed-tools list. The proxxx server still exposes them; the agent just won't call them.

Common stumbles

SymptomFix
Agent: tool 'X' exceeded N s execution budgetCluster is hung on a lock. Check proxxx ls nodes directly; resolve upstream.
Agent: Method not foundTool name typo or registry-checksum mismatch. Run proxxx mcp tools --checksum to verify your pin.
HITL: replay rejected: <txn>The callback was already consumed in this session. Ask the agent to re-issue the operation.
Claude Desktop doesn't show proxxx toolsPath in command is wrong, or proxxx mcp serve exits immediately (config invalid → check proxxx ls nodes first).

Full troubleshooting at /guide/troubleshooting.

See also

Released under the MIT License. · Privacy & legal