Skip to main content
Glama

proxmox-mcp

An MCP server that lets an AI assistant fully navigate a single-node Proxmox VE host — inspect, control, provision, and destroy — without exposing any new network service.

The server runs on the Proxmox node itself, launched over SSH and speaking stdio. Nothing listens. There is no port, no API token, no TLS, and no daemon. The node's existing sshd is the only entry point, and your existing SSH key is the only credential.

Claude Code (your PC)  ──ssh──►  proxmox-mcp (on the node)  ──►  pvesh / qm / pct

Install on the node

From the Proxmox host, as root:

apt-get install -y git          # not present on a stock PVE install
git clone https://github.com/ssan9876/proxmox-mcp /opt/proxmox-mcp-src
/opt/proxmox-mcp-src/install.sh

The installer creates a venv at /opt/proxmox-mcp, installs the package, drops a launcher at /opt/proxmox-mcp/bin/proxmox-mcp, and writes a starter config to /etc/proxmox-mcp/config.yaml if none exists.

Verify it:

/opt/proxmox-mcp/bin/proxmox-mcp --dry-run --log-level DEBUG

It should log a startup line and then wait on stdin. Ctrl-C to exit.

Related MCP server: ProxmoxEmCP

Connect from your PC

You need password-less SSH to the node first:

ssh-copy-id root@pve

Then add the server to your MCP client. For Claude Code:

claude mcp add proxmox -- ssh -o BatchMode=yes root@pve /opt/proxmox-mcp/bin/proxmox-mcp --mode safe

Or by hand, in your MCP config:

{
  "mcpServers": {
    "proxmox": {
      "command": "ssh",
      "args": [
        "-o", "BatchMode=yes",
        "root@pve",
        "/opt/proxmox-mcp/bin/proxmox-mcp", "--mode", "safe"
      ]
    }
  }
}

Replace pve with your node's hostname or IP.

Modes

The mode decides which tools are registered. Tools outside the active mode are never exposed — the model cannot see them or call them.

Mode

What it can do

readonly

Inspect everything. Zero mutation.

safe

+ start/stop/reboot, create VMs and containers, snapshot, back up.

full

+ destroy, restore, roll back, shrink disks, run commands in guests, root shell on the host.

safe is the default. Change it per-connection with --mode in the SSH args, so the power a session has is visible in your client config. Keeping a second entry named proxmox-full and only enabling it when you need it is a reasonable setup.

Guardrails

Four checks sit in front of anything that can lose data:

  1. Protected denylist — VMIDs, names, and pools listed in the config are refused by every destructive tool, including in full mode. Disruptive lifecycle actions (stop, reboot, shutdown) honour it too, and a disk whose volume ID belongs to a protected VMID is protected with it.

  2. Mode gate — the tool must be registered by the active mode.

  3. Confirm token — destructive tools require a confirm argument holding the target's exact token: vm:104, ct:110/snap:nightly, volume:local-lvm:vm-104-disk-0, node:pve, api:DELETE:/nodes/pve/.... A mismatch returns the target's real details — name, status, disks, uptime — so what was about to be destroyed is visible in the transcript before any retry.

  4. Protective snapshot or backup — rollback_snapshot and shrink_disk snapshot first. destroy_guest and restore_backup cannot be protected by a snapshot, so they run vzdump first and abort if the backup fails.

node_shell is deliberately not gated on every call — requiring a constant token to run ls would just train the model to paste it. Instead it demands the node token only when the command matches a destructive pattern (rm -rf, mkfs, dd, zfs destroy, lvremove, reboot, apt purge, and similar).

What the confirm token is and is not

The mismatch error includes the expected token, so a model can read it and retry. That is deliberate: a token the caller cannot learn makes the tool unusable. What it buys is a blocked first attempt on a hallucinated or stale VMID, a forced second round-trip, and the target's details in front of you. It is not an authorization boundary. The denylist and the mode gate are.

Configuration

/etc/proxmox-mcp/config.yaml:

mode: safe              # readonly | safe | full
node: null              # auto-detected when null
auto_snapshot: true     # protective snapshots before lossy operations
task_poll_seconds: 10   # how long tools wait on a task before returning a UPID
command_timeout: 60
shell_timeout: 600      # ceiling for node_shell
max_output_bytes: 100000
protected:
  vmids: [100]
  names: ["opnsense", "truenas"]
  pools: ["production"]

CLI flags --mode, --config, --node, and --dry-run override the file.

Tools

Inspect (all modes) — resource_summary, node_status, list_guests, guest_config, guest_status, list_storage, storage_content, list_networks, list_tasks, task_log, list_snapshots, list_backups, node_services, read_syslog, list_access, node_updates

Lifecycle (safe+) — start_guest, shutdown_guest, stop_guest, reboot_guest, suspend_guest, resume_guest, wait_for_task

Provision (safe+) — create_vm, create_container, set_guest_config, grow_disk, clone_guest, create_snapshot, create_backup, convert_to_template, download_to_storage

Destructive (full) — destroy_guest, delete_snapshot, delete_volume, rollback_snapshot, restore_backup, shrink_disk, guest_exec, node_shell, node_reboot

Passthrough — pve_get and pve_discover in all modes; pve_post, pve_put, pve_delete in full. These reach every endpoint the Proxmox web UI uses, so anything the curated tools miss is still reachable.

resource_summary is the intended starting point: node health, every guest, and every storage in one call.

Development

git clone https://github.com/ssan9876/proxmox-mcp && cd proxmox-mcp
uv venv && uv pip install -e ".[dev]"
uv run pytest

Tests inject a fake command runner, so the whole suite runs on any machine with no Proxmox host involved. Tools are verified by asserting the exact pvesh argv they build and feeding recorded JSON back.

Notes

  • Transport is stdio: stdout belongs to the MCP protocol. All logging goes to stderr. A stray print() corrupts the session.

  • The server holds root on your hypervisor. In full mode it can delete VMs and run arbitrary commands. Run it in safe unless you are actively doing something that needs more.

  • Tool results are capped at max_output_bytes so an unbounded journalctl or node_shell cannot exhaust the context window.

Available Tools

34 tools
clone_guestA

Clone a VM or container into a new guest.

Args: vmid: Source guest to clone. new_vmid: ID for the clone. Must not already exist. name: Name or hostname for the clone. full: Full independent copy. False makes a linked clone, which only works from a template and stays dependent on it. storage: Target storage for a full clone. Defaults to the source's.

Inputs: vmid (required), new_vmid (required), full, name, +1 more. Returns: object

convert_to_templateA

Convert a stopped VM or container into a template. This is one-way — the guest can no longer be started, only cloned from.

Inputs: vmid (required). Returns: object

create_backupA

Back up a VM or container with vzdump.

Args: vmid: Guest to back up. storage: Backup-capable storage to write to. mode: "snapshot" (no downtime), "suspend", or "stop". compress: "zstd", "gzip", "lzo", or "0" for none. notes: Optional note attached to the archive. wait: Seconds to wait before returning a UPID instead of a result.

Inputs: vmid (required), mode, wait, notes, +2 more. Returns: object

create_containerA

Create a new LXC container.

Args: vmid: New container ID. Must not already exist. hostname: Container hostname. ostemplate: Template volume, e.g. "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst". Find available templates with storage_content(storage="local", content="vztmpl"). storage: Storage for the root filesystem. rootfs_gb: Root filesystem size in gigabytes. memory_mb: RAM in megabytes. cores: CPU cores. bridge: Network bridge for net0. ip: Address for net0 — "dhcp" or CIDR like "192.168.1.50/24". unprivileged: Run unprivileged. Leave true unless you need host device access. ssh_public_keys: Public keys to install for root. Prefer this over a password. start: Start the container once created. extra: Any other Proxmox container config key, e.g. {"nameserver": "1.1.1.1"}.

Inputs: vmid (required), hostname (required), ostemplate (required), ip, +9 more. Returns: object

create_snapshotA

Take a snapshot of a VM or container.

Args: vmid: Guest to snapshot. snapname: Snapshot name. Letters, digits, hyphens and underscores. description: Optional note stored with the snapshot. include_ram: For VMs, also save RAM so the guest resumes mid-run.

Inputs: vmid (required), snapname (required), description, include_ram. Returns: object

create_vmA

Create a new QEMU virtual machine.

Args: vmid: New VM ID. Must not already exist — check list_guests first. name: VM name. memory_mb: RAM in megabytes. cores: CPU cores per socket. sockets: CPU sockets. disk: Boot disk spec, e.g. "local-lvm:32" for a 32 GB disk on local-lvm. iso: Installer ISO volume, e.g. "local:iso/debian-12.iso". Attached as ide2. bridge: Network bridge for net0. ostype: Proxmox OS type, e.g. "l26" (Linux 6.x), "win11". start: Start the VM once created. extra: Any other Proxmox VM config key, e.g. {"agent": "1", "bios": "ovmf"}.

Inputs: name (required), vmid (required), iso, disk, +7 more. Returns: object

download_to_storageA

Download an ISO or container template straight to a storage.

Args: storage: Target storage, usually "local". url: HTTP(S) URL to fetch. filename: Filename to save as, e.g. "debian-12.iso". content: "iso" or "vztmpl". checksum: Optional expected checksum. Supply it when the source publishes one. checksum_algorithm: "md5", "sha1", "sha224", "sha256", "sha384", or "sha512". wait: Seconds to wait before returning a UPID instead of a result.

Inputs: url (required), storage (required), filename (required), wait, +3 more. Returns: object

grow_diskA

Grow a guest disk. Growth only — shrinking is destructive and lives in shrink_disk.

Args: vmid: Guest whose disk to grow. disk: Disk key from guest_config, e.g. "scsi0", "virtio0", "rootfs". increment: Amount to add, e.g. "+10G" or "+512M". Must start with "+".

Inputs: disk (required), vmid (required), increment (required). Returns: object

guest_configA
Read-only

Get the full configuration of a VM or container: disks, network interfaces, memory, CPU, boot order, and every other config key.

Inputs: vmid (required). Returns: object

guest_statusA
Read-only

Get live runtime status for a VM or container: power state, uptime, CPU and memory usage, disk and network I/O counters.

Inputs: vmid (required). Returns: object

list_accessA
Read-only

List users, groups, roles, and resource pools configured on the host.

Inputs: no named parameters. Returns: object

list_backupsA
Read-only

List backup archives across every backup-capable storage, plus the configured scheduled backup jobs.

Args: vmid: Optional filter to one guest's backups.

Inputs: vmid. Returns: object

list_guestsA
Read-only

List every VM and LXC container with vmid, name, type, status, and the confirm token used by destructive tools.

Inputs: running_only, include_templates. Returns: object

list_networksA
Read-only

List network interfaces on the node: bridges, bonds, VLANs and physical NICs, with their configured and active state.

Inputs: no named parameters. Returns: object

list_snapshotsA
Read-only

List snapshots of a VM or container, including the synthetic "current" entry, with the confirm token needed to delete or roll back each one.

Inputs: vmid (required). Returns: object

list_storageA
Read-only

List configured storages with type, enabled state, content types allowed, and space used vs available.

Inputs: no named parameters. Returns: object

list_tasksA
Read-only

List recent Proxmox tasks (the job log behind every operation), newest first. Use this to find out what happened and why something failed.

Args: limit: Maximum tasks to return. errors_only: Only tasks that ended in an error. vmid: Only tasks for one guest. typefilter: Only one task type, e.g. "vzdump", "qmstart", "qmdestroy".

Inputs: vmid, limit, typefilter, errors_only. Returns: object

node_servicesA
Read-only

List Proxmox system services (pveproxy, pvedaemon, pve-cluster, ...) and whether each is running.

Inputs: no named parameters. Returns: object

node_statusA
Read-only

Get detailed live status for the Proxmox node: CPU, memory, swap, root filesystem, load average, uptime, kernel, and PVE version.

Inputs: no named parameters. Returns: object

node_updatesA
Read-only

List available package updates for the node, including which are security updates. Read-only — this does not install anything.

Inputs: no named parameters. Returns: object

pve_discoverA
Read-only

List the child endpoints of a Proxmox API path, so you can walk the API tree to anything the curated tools do not cover.

Args: path: API path to list, e.g. "/", "/nodes", "/nodes/pve", "/cluster".

Inputs: path. Returns: object

pve_getA
Read-only

Read any Proxmox API endpoint directly. Always safe — GET never mutates.

Use pve_discover to find paths. Example: pve_get("/nodes/pve/qemu/100/firewall/rules").

Args: path: Full API path, starting with "/". params: Optional query parameters.

Inputs: path (required), params. Returns: object

read_syslogA
Read-only

Read the node's system log, newest entries last.

Args: limit: Maximum lines to return. since: Optional start time, e.g. "2026-08-05" or "-1h". service: Optional systemd unit to filter to, e.g. "pveproxy".

Inputs: limit, since, service. Returns: object

reboot_guestA

Gracefully reboot a VM or container.

Args: vmid: Guest to reboot. timeout: Seconds to wait for the shutdown half of the reboot.

Inputs: vmid (required), timeout. Returns: object

resource_summaryA
Read-only

Get a one-shot overview of the whole Proxmox host: node health, every VM and container, and every storage. Start here — it orients you in a single call instead of a dozen.

Inputs: no named parameters. Returns: object

resume_guestB

Resume a suspended VM or container.

Inputs: vmid (required). Returns: object

set_guest_configA

Change configuration keys on an existing VM or container.

Args: vmid: Guest to reconfigure. settings: Config keys to set, e.g. {"memory": "4096", "cores": "4", "net0": "virtio,bridge=vmbr0", "onboot": "1", "tags": "web;prod"}. Read guest_config first to see current values.

Inputs: vmid (required), settings (required). Returns: object

shutdown_guestA

Gracefully shut down a VM or container, asking the guest OS to power off.

Args: vmid: Guest to shut down. timeout: Seconds to wait for the guest before giving up. force_stop: Hard-stop the guest if the graceful shutdown times out.

Inputs: vmid (required), timeout, force_stop. Returns: object

start_guestA

Start a stopped VM or container.

Inputs: vmid (required). Returns: object

stop_guestA

Immediately stop a VM or container, like pulling the power cord. The guest OS gets no chance to flush its disks — prefer shutdown_guest.

Inputs: vmid (required). Returns: object

storage_contentA
Read-only

List volumes in a storage: disk images, ISOs, container templates, and backup archives.

Args: storage: Storage ID, e.g. "local" or "local-lvm". content: Optional filter — "images", "iso", "vztmpl", "backup", "rootdir", "snippets". vmid: Optional filter to volumes belonging to one guest.

Inputs: storage (required), vmid, content. Returns: object

suspend_guestA

Suspend a running VM.

Args: vmid: Guest to suspend. to_disk: Write memory state to disk (hibernate) instead of holding it in RAM.

Inputs: vmid (required), to_disk. Returns: object

task_logA
Read-only

Get the status and full output log of one task, identified by its UPID.

Args: upid: Task ID, e.g. "UPID:pve:0000ABCD:...". limit: Maximum log lines.

Inputs: upid (required), limit. Returns: object

wait_for_taskA

Block until a Proxmox task finishes, then report how it ended. Use this when another tool returned a UPID because the job outlived its polling budget.

Args: upid: Task ID returned by an earlier tool. timeout: Maximum seconds to wait.

Inputs: upid (required), timeout. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.5/5.0

    Scored across 34 tools

    Disambiguation4/5

    While the tool set is large, each tool targets a distinct resource or action. Potential overlaps like list_backups vs storage_content with content='backup' are clarified by descriptions. A few tools such as resource_summary aggregate data from multiple list tools, but this is an intentional convenience rather than a source of ambiguity.

    Naming Consistency4/5

    Most tools follow a verb_noun pattern (list_guests, create_vm, start_guest), but some use noun-based names (guest_config, node_status) and the pve_* pair deviates. The naming is still predictable and consistent within categories, so the mix is a minor deviation.

    Tool Count2/5

    At 34 tools, this exceeds the threshold for a well-scoped server. While each tool has a clear purpose, the count is high enough to make it heavy, and many tools could be consolidated (e.g., lifecycle actions). The availability of pve_get/pve_discover adds redundancy.

    Completeness2/5

    There are significant gaps in the lifecycle surface: no delete/destroy for guests, no snapshot deletion or rollback, no backup restore, and no network/access management beyond listing. The generic pve_get tool is read-only, so these missing operations cannot be performed at all, causing dead ends.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers