npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

proxmox-mcp-server

v0.2.0

Published

MCP server exposing Proxmox VE management as tools over stdio

Readme

proxmox-mcp-server

npm CI

An MCP server that exposes a Proxmox VE cluster as tools for an MCP client such as Claude. It talks to the Proxmox REST API (/api2/json) with an API token and communicates with the client over stdio.

Write operations in Proxmox are asynchronous: the API returns a task id (UPID) and the work continues in the background. This server polls each task to completion and reports the real exit status, so a tool only reports success once the operation has actually finished.

Requirements

  • Node.js 20 or newer
  • A Proxmox VE 7.2+ node or cluster reachable over HTTPS (the ISO/template download tool uses the download-url endpoint introduced in 7.2)
  • An API token (see below)

Install and build

npm install
npm run build

This produces dist/index.js, the entry point you point your MCP client at.

Creating an API token

Use a dedicated user and token with privilege separation enabled, not the root token. In the Proxmox web UI: Datacenter → Permissions → API Tokens → Add.

# create a user and a token with privilege separation on
pveum user add mcp@pve
pveum user token add mcp@pve mcp --privsep 1

The token secret (a UUID) is shown once. The token id is mcp@pve!mcp.

Grant only the privileges the tools you intend to use need. With privilege separation on, permissions must be assigned to the token itself, not just the user.

Read-only usage (everything in PVE_READONLY=true mode):

pveum acl modify / --tokens 'mcp@pve!mcp' --roles PVEAuditor

For write operations, grant the matching privileges per group. A practical setup grants PVEVMAdmin on the guests and audit/space roles on storage:

pveum acl modify /vms     --tokens 'mcp@pve!mcp' --roles PVEVMAdmin
pveum acl modify /storage --tokens 'mcp@pve!mcp' --roles PVEDatastoreAdmin

Privileges by tool group, if you prefer to build a custom role:

  • Read / cluster (pve_list_*, *_status, *_config, pve_cluster_resources): VM.Audit, Datastore.Audit, Sys.Audit.
  • VM/LXC lifecycle (start, stop, shutdown, reboot, reset): VM.PowerMgmt.
  • Create / set config / clone: VM.Allocate, VM.Config.Disk, VM.Config.CPU, VM.Config.Memory, VM.Config.Network, VM.Config.Options, VM.Clone, plus Datastore.AllocateSpace on the target storage.
  • Migrate: VM.Migrate.
  • Delete: VM.Allocate.
  • Snapshots (create/rollback/delete): VM.Snapshot (rollback also needs VM.Snapshot.Rollback).
  • Storage listing and ISO/template download: Datastore.Audit, Datastore.AllocateTemplate.
  • Network (create bridge, apply): Sys.Modify.
  • Backup / restore / schedule: VM.Backup, Datastore.AllocateSpace; restore also needs VM.Allocate; scheduling a cluster job also needs Sys.Modify.

A 403 from a tool almost always means a missing privilege on the token for that path.

Configuration

All configuration is via environment variables. Copy .env.example and fill it in, or set them directly in your MCP client config.

  • PROXMOX_HOST (required) — full base URL, e.g. https://pve.local:8006.
  • PROXMOX_TOKEN_ID (required) — USER@REALM!TOKENID, e.g. mcp@pve!mcp.
  • PROXMOX_TOKEN_SECRET (required) — the token UUID.
  • PROXMOX_INSECURE_TLStrue to accept self-signed certificates. Off by default; only for labs. See the security note below.
  • PVE_READONLYtrue (default) registers only read tools. Set to false to enable write and destructive tools.
  • PVE_NODE_ALLOWLIST — optional comma-separated node names; tools refuse to act on any node outside the list.
  • PVE_VMID_ALLOWLIST — optional comma-separated guest ids; same idea for VMs/CTs.
  • PROXMOX_REQUEST_TIMEOUT_MS — per-request timeout (default 30000).
  • PVE_TASK_TIMEOUT_MS — how long to wait for an async task (default 600000).
  • LOG_LEVELdebug | info | warn | error (default info). Logs go to stderr; stdout is reserved for the MCP protocol.

The auth header sent to Proxmox is Authorization: PVEAPIToken=USER@REALM!TOKENID=UUID (no Bearer prefix), as required for API tokens.

Running

Register the built server with your MCP client. For Claude Desktop, add to claude_desktop_config.json:

{
  "mcpServers": {
    "proxmox": {
      "command": "node",
      "args": ["/absolute/path/to/dist/index.js"],
      "env": {
        "PROXMOX_HOST": "https://pve.local:8006",
        "PROXMOX_TOKEN_ID": "mcp@pve!mcp",
        "PROXMOX_TOKEN_SECRET": "00000000-0000-0000-0000-000000000000",
        "PVE_READONLY": "true"
      }
    }
  }
}

To inspect the tools manually before wiring it into a client:

npx @modelcontextprotocol/inspector node dist/index.js

During development you can run from source with npm run dev.

Install as a Claude Code plugin

This repo doubles as a Claude Code plugin and its own marketplace, so it can be installed in two commands. From inside Claude Code:

/plugin marketplace add k-krawczyk/proxmox-mcp-server
/plugin install proxmox-mcp@proxmox

(The non-interactive equivalents are claude plugin marketplace add k-krawczyk/proxmox-mcp-server and claude plugin install proxmox-mcp@proxmox.)

Installing the plugin registers the proxmox MCP server automatically — it runs the prebuilt dist/ that ships in the repo, so no build step is needed on install. The plugin reads the connection details from your environment instead of storing them, so export them before launching Claude Code:

export PROXMOX_HOST=https://pve.local:8006
export PROXMOX_TOKEN_ID='mcp@pve!mcp'
export PROXMOX_TOKEN_SECRET=your-secret
export PVE_READONLY=true            # optional, defaults to true
export PROXMOX_INSECURE_TLS=false   # optional, defaults to false

When changing the source, rebuild and commit dist/ (npm run build) — marketplace installs do not run a build. The sections below cover wiring the server into other clients by hand.

Claude Desktop extension (.mcpb)

Claude Desktop installs this as a one-click extension. Download proxmox-mcp-server.mcpb from the GitHub Releases page and drag it onto the Claude Desktop window (Settings → Extensions). Claude Desktop asks for the host, token id and token secret in a form — no config file to edit, and the secret is stored in the OS keychain. Build the bundle yourself with npm run bundle:mcpb.

Adding to MCP clients

The package is on npm, so every client runs it the same way with no clone or build:

npx -y proxmox-mcp-server

Each client just needs that command plus the PROXMOX_* variables. The examples start in read-only mode; set PVE_READONLY=false to enable write tools. Keep the token secret out of version control — for shared/committed config files prefer ${PROXMOX_TOKEN_SECRET} expansion and export the value in your shell.

One-click install:

Add to Cursor Install in VS Code

The buttons register the server only; add your PROXMOX_* environment afterwards in the client's MCP settings.

Claude Code (CLI)

claude mcp add --transport stdio \
  --env PROXMOX_HOST=https://pve.local:8006 \
  --env PROXMOX_TOKEN_ID='mcp@pve!mcp' \
  --env PROXMOX_TOKEN_SECRET=your-secret \
  --env PVE_READONLY=true \
  proxmox -- npx -y proxmox-mcp-server

Place an option (here --transport) between the last --env and the server name, otherwise the name is parsed as another env pair. Use --scope user to make it available in every project, or --scope project to write a shared .mcp.json at the repo root. Manage with claude mcp list, claude mcp get proxmox, claude mcp remove proxmox, and /mcp inside a session.

A project .mcp.json looks like:

{
  "mcpServers": {
    "proxmox": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "proxmox-mcp-server"],
      "env": {
        "PROXMOX_HOST": "https://pve.local:8006",
        "PROXMOX_TOKEN_ID": "mcp@pve!mcp",
        "PROXMOX_TOKEN_SECRET": "${PROXMOX_TOKEN_SECRET}",
        "PVE_READONLY": "true"
      }
    }
  }
}

Codex CLI

Add to ~/.codex/config.toml:

[mcp_servers.proxmox]
command = "npx"
args = ["-y", "proxmox-mcp-server"]
env = { PROXMOX_HOST = "https://pve.local:8006", PROXMOX_TOKEN_ID = "mcp@pve!mcp", PROXMOX_TOKEN_SECRET = "your-secret", PVE_READONLY = "true" }

Cursor

~/.cursor/mcp.json (global) or .cursor/mcp.json (per project):

{
  "mcpServers": {
    "proxmox": {
      "command": "npx",
      "args": ["-y", "proxmox-mcp-server"],
      "env": {
        "PROXMOX_HOST": "https://pve.local:8006",
        "PROXMOX_TOKEN_ID": "mcp@pve!mcp",
        "PROXMOX_TOKEN_SECRET": "your-secret",
        "PVE_READONLY": "true"
      }
    }
  }
}

VS Code

.vscode/mcp.json (note the top-level key is servers):

{
  "servers": {
    "proxmox": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "proxmox-mcp-server"],
      "env": {
        "PROXMOX_HOST": "https://pve.local:8006",
        "PROXMOX_TOKEN_ID": "mcp@pve!mcp",
        "PROXMOX_TOKEN_SECRET": "${env:PROXMOX_TOKEN_SECRET}",
        "PVE_READONLY": "true"
      }
    }
  }
}

Other clients (Windsurf, Cline, Zed, …)

These read the same mcpServers JSON object as Claude Desktop and Cursor. Point command at npx with args ["-y", "proxmox-mcp-server"].

Tools

Read tools are always registered. Tools marked write are only registered when PVE_READONLY=false. Destructive tools additionally require confirm: true, and delete/restore/rollback require echoing the target id or name.

Cluster and nodes:

  • pve_version — Proxmox VE version of the connected node
  • pve_list_nodes, pve_node_status
  • pve_cluster_resources — VMs, containers, storage and nodes in one view
  • pve_list_tasks — recent tasks on a node, with UPID and exit status

The server logs the detected PVE version on startup. pve_download_iso needs the download-url endpoint and is gated to Proxmox VE 7.2+ (older nodes get a clear message); everything else works on 7.0, and most of it back to 6.2.

VMs (QEMU):

  • pve_list_vms, pve_vm_status, pve_vm_config
  • pve_vm_start, pve_vm_shutdown, pve_vm_reboot, pve_vm_stop (confirm), pve_vm_reset (confirm)
  • pve_vm_create, pve_vm_clone, pve_vm_migrate, pve_vm_set_config
  • pve_vm_delete (confirm + confirmVmid)

Containers (LXC):

  • pve_list_containers, pve_lxc_status, pve_lxc_config
  • pve_lxc_start, pve_lxc_shutdown, pve_lxc_reboot, pve_lxc_stop (confirm)
  • pve_lxc_create, pve_lxc_clone, pve_lxc_delete (confirm + confirmVmid)

Snapshots (qemu or lxc via the type argument):

  • pve_list_snapshots, pve_snapshot_create
  • pve_snapshot_rollback (confirm + confirmName), pve_snapshot_delete (confirm + confirmName)

Storage and images:

  • pve_list_storage, pve_storage_content, pve_download_iso

Network:

  • pve_list_network, pve_create_bridge, pve_apply_network (confirm)

Backup:

  • pve_list_backups, pve_backup_now, pve_restore (confirm + confirmVmid), pve_schedule_backup

Security notes

  • Start in PVE_READONLY=true. Write tools are not just hidden — they are never registered, so the client cannot call them at all.
  • Destructive tools require confirm: true. Deleting a guest, rolling back or deleting a snapshot, and restoring a backup also require repeating the target id or snapshot name, which guards against acting on the wrong target.
  • Use a token with the minimum privileges for your use case and keep privilege separation on.
  • The token secret is read only from the environment and is never written to logs.
  • PROXMOX_INSECURE_TLS=true disables certificate verification for the whole client and exposes the connection to man-in-the-middle attacks. Use it only against a lab with a self-signed certificate; for anything else, install a properly issued certificate or add the cluster CA to the host trust store.

Development

npm run dev          # run from source (tsx)
npm run lint         # eslint
npm run format       # prettier --write
npm test             # unit tests (vitest)
npm run build        # type-check and emit to dist/

The unit tests mock fetch and cover the auth header, UPID parsing and polling, error mapping, the confirmation gate and read-only registration. An optional live smoke test runs only when PVE_INTEGRATION=1 is set together with valid PROXMOX_* variables:

PVE_INTEGRATION=1 npm test

End-to-end sweep against a simulator

sim/ contains a small in-memory simulator of the Proxmox API. It is faithful to the request/response contract (token auth, the {data:...} envelope, UPID task ids and the task status lifecycle) and keeps state, so every tool can be exercised end to end — the full create → start → snapshot → clone → backup → delete → restore cycle for VMs and containers — without a hypervisor. It does not emulate virtualization; it proves the tool/client/polling/guard code paths.

Run the sweep against the simulator started with Node:

npm run build
npm run sim          # in one terminal
npm run sweep        # in another

Or run the simulator in a container and sweep against it:

npm run sweep:docker

This requires a running Docker engine. Building the real Proxmox VE inside Docker is not supported here: it is a hypervisor and needs /dev/kvm and the host LXC stack, which a container (especially on macOS, where Docker itself runs in a VM without nested virtualization) cannot provide. For real lifecycle testing point PVE_INTEGRATION=1 at an actual node instead.

End-to-end sweep against a real node

sim/live-real.mjs runs the same kind of sweep against an actual Proxmox node: it creates a VM (qcow2 disk) and an LXC container in a free VMID range and drives the full create → start → snapshot → clone → backup → delete → restore cycle, then cleans up every guest, the pending bridge, the downloaded template, backup archives and schedule jobs — even on failure. It deliberately skips pve_apply_network (reloading networking on a remote node can drop the connection) and treats a migration to the only node as the expected rejection. Point a .env at the node and:

npm run build
set -a; . ./.env; set +a   # load PROXMOX_* into the environment
npm run test:live

Guests use no NIC unless the node has a bridge, so a single-storage node with only local and no vmbr0 is enough.