pve-mcp
by bigbatmanorg
README.md
# pve-mcp
MCP server for Proxmox VE. Curated tools for everyday work, plus schema-validated access to every API endpoint.
- **Schema:** `src/pve_mcp/data/pve-api.json.gz`, generated from a node's `/usr/share/pve-docs/api-viewer/apidoc.js`
(currently PVE 9.2.24, 680 endpoints). Regenerate after a PVE upgrade:
`scp root@<node>:/usr/share/pve-docs/api-viewer/apidoc.js schema-src/ && python scripts/gen_schema.py schema-src/apidoc.js <version>`
- **Generic:** `pve_api_search`, `pve_api_describe`, `pve_api` (validates params, waits for tasks, `dry_run`).
- **Curated:** cluster status/resources, guest status/power/config/snapshots, create/clone/migrate/destroy,
guest-agent exec, storage, vzdump backups, tasks, optional node SSH.
<!-- install:start -->
## Install in your agent
[](https://insiders.vscode.dev/redirect/mcp/install?name=proxmox&inputs=%5B%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22pve_host%22%2C%22description%22%3A%22Proxmox%20host%20or%20IP%22%2C%22password%22%3Afalse%7D%2C%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22pve_token_id%22%2C%22description%22%3A%22API%20token%20id%20%28user%40realm%21name%29%22%2C%22password%22%3Afalse%7D%2C%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22pve_token_secret%22%2C%22description%22%3A%22API%20token%20secret%22%2C%22password%22%3Atrue%7D%5D&config=%7B%22type%22%3A%22stdio%22%2C%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22git%2Bhttps%3A//github.com/bigbatmanorg/pve-mcp%40v0.1.0%22%2C%22pve-mcp%22%5D%2C%22env%22%3A%7B%22PVE_HOST%22%3A%22%24%7Binput%3Apve_host%7D%22%2C%22PVE_TOKEN_ID%22%3A%22%24%7Binput%3Apve_token_id%7D%22%2C%22PVE_TOKEN_SECRET%22%3A%22%24%7Binput%3Apve_token_secret%7D%22%7D%7D)
[](https://insiders.vscode.dev/redirect/mcp/install?name=proxmox&inputs=%5B%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22pve_host%22%2C%22description%22%3A%22Proxmox%20host%20or%20IP%22%2C%22password%22%3Afalse%7D%2C%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22pve_token_id%22%2C%22description%22%3A%22API%20token%20id%20%28user%40realm%21name%29%22%2C%22password%22%3Afalse%7D%2C%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22pve_token_secret%22%2C%22description%22%3A%22API%20token%20secret%22%2C%22password%22%3Atrue%7D%5D&config=%7B%22type%22%3A%22stdio%22%2C%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22git%2Bhttps%3A//github.com/bigbatmanorg/pve-mcp%40v0.1.0%22%2C%22pve-mcp%22%5D%2C%22env%22%3A%7B%22PVE_HOST%22%3A%22%24%7Binput%3Apve_host%7D%22%2C%22PVE_TOKEN_ID%22%3A%22%24%7Binput%3Apve_token_id%7D%22%2C%22PVE_TOKEN_SECRET%22%3A%22%24%7Binput%3Apve_token_secret%7D%22%7D%7D&quality=insiders)
[](https://cursor.com/en-US/install-mcp?name=proxmox&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJnaXQraHR0cHM6Ly9naXRodWIuY29tL2JpZ2JhdG1hbm9yZy9wdmUtbWNwQHYwLjEuMCIsInB2ZS1tY3AiXSwiZW52Ijp7IlBWRV9IT1NUIjoicHZlLmV4YW1wbGUubGFuIiwiUFZFX1RPS0VOX0lEIjoicm9vdEBwYW0hbWNwIiwiUFZFX1RPS0VOX1NFQ1JFVCI6Inh4eHh4eHh4LXh4eHgteHh4eC14eHh4LXh4eHh4eHh4eHh4eCJ9fQ%3D%3D)
It works with any MCP client that can start a stdio command. It needs [uv](https://docs.astral.sh/uv/). `uvx` fetches the pinned release, so no clone or virtualenv is needed. Pick your agent below.
| Variable | Meaning | Example |
|---|---|---|
| `PVE_HOST` | Proxmox host or IP | `pve.example.lan` |
| `PVE_TOKEN_ID` | API token id (user@realm!name) | `root@pam!mcp` |
| `PVE_TOKEN_SECRET` | API token secret | `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` |
Optional: `PVE_ALLOW_WRITES=true` (writes), `PVE_VERIFY_SSL` (`true` or a CA path), `PVE_SSH_ENABLED`. Without `PVE_ALLOW_WRITES` the server is **read-only**. Create the token under Datacenter → Permissions → API Tokens. For read-only use, give it the `PVEAuditor` role on `/`.
> A different project uses the name `pve-mcp` on PyPI. Install from this repository as shown, not with `pip install pve-mcp`.
The VS Code buttons ask for each value, keeping secrets out of files. The Cursor button writes placeholders: edit them in `~/.cursor/mcp.json` afterwards. Elsewhere, replace the example values.
<details>
<summary><b>Claude Code</b></summary>
```bash
claude mcp add proxmox -s user -e PVE_HOST=pve.example.lan -e PVE_TOKEN_ID=root@pam!mcp -e PVE_TOKEN_SECRET=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -- uvx --from git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0 pve-mcp
```
</details>
<details>
<summary><b>Claude Desktop</b></summary>
Settings → Developer → Edit Config (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"proxmox": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0",
"pve-mcp"
],
"env": {
"PVE_HOST": "pve.example.lan",
"PVE_TOKEN_ID": "root@pam!mcp",
"PVE_TOKEN_SECRET": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
}
}
}
```
</details>
<details>
<summary><b>VS Code / GitHub Copilot</b></summary>
Use the button above, or add this to `.vscode/mcp.json` (workspace) or to the user `mcp.json` (command
*MCP: Open User Configuration*):
```json
{
"inputs": [
{
"type": "promptString",
"id": "pve_host",
"description": "Proxmox host or IP",
"password": false
},
{
"type": "promptString",
"id": "pve_token_id",
"description": "API token id (user@realm!name)",
"password": false
},
{
"type": "promptString",
"id": "pve_token_secret",
"description": "API token secret",
"password": true
}
],
"servers": {
"proxmox": {
"type": "stdio",
"command": "uvx",
"args": [
"--from",
"git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0",
"pve-mcp"
],
"env": {
"PVE_HOST": "${input:pve_host}",
"PVE_TOKEN_ID": "${input:pve_token_id}",
"PVE_TOKEN_SECRET": "${input:pve_token_secret}"
}
}
}
}
```
</details>
<details>
<summary><b>Cursor</b></summary>
Use the button above, or add this to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project):
```json
{
"mcpServers": {
"proxmox": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0",
"pve-mcp"
],
"env": {
"PVE_HOST": "pve.example.lan",
"PVE_TOKEN_ID": "root@pam!mcp",
"PVE_TOKEN_SECRET": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
}
}
}
```
</details>
<details>
<summary><b>Windsurf</b></summary>
Add this to `~/.codeium/windsurf/mcp_config.json`:
```json
{
"mcpServers": {
"proxmox": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0",
"pve-mcp"
],
"env": {
"PVE_HOST": "pve.example.lan",
"PVE_TOKEN_ID": "root@pam!mcp",
"PVE_TOKEN_SECRET": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
}
}
}
```
</details>
<details>
<summary><b>OpenAI Codex CLI</b></summary>
```bash
codex mcp add proxmox --env PVE_HOST=pve.example.lan --env PVE_TOKEN_ID=root@pam!mcp --env PVE_TOKEN_SECRET=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -- uvx --from git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0 pve-mcp
```
Or add this to `~/.codex/config.toml`:
```toml
[mcp_servers.proxmox]
command = "uvx"
args = ["--from", "git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0", "pve-mcp"]
env = { PVE_HOST = "pve.example.lan", PVE_TOKEN_ID = "root@pam!mcp", PVE_TOKEN_SECRET = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }
```
</details>
<details>
<summary><b>Gemini CLI</b></summary>
Add this to `~/.gemini/settings.json` (or to `.gemini/settings.json` in a project):
```json
{
"mcpServers": {
"proxmox": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0",
"pve-mcp"
],
"env": {
"PVE_HOST": "pve.example.lan",
"PVE_TOKEN_ID": "root@pam!mcp",
"PVE_TOKEN_SECRET": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
}
}
}
```
</details>
<details>
<summary><b>DeepAgents</b></summary>
**Deep Agents CLI:** add this to `~/.deepagents/.mcp.json` (all projects) or to `.mcp.json` in a project. `${VAR}` values are read from your shell or `~/.deepagents/.env`, so secrets stay out of the file.
```json
{
"mcpServers": {
"proxmox": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0",
"pve-mcp"
],
"env": {
"PVE_HOST": "${PVE_HOST}",
"PVE_TOKEN_ID": "${PVE_TOKEN_ID}",
"PVE_TOKEN_SECRET": "${PVE_TOKEN_SECRET}"
}
}
}
}
```
**Python library** (`pip install deepagents langchain-mcp-adapters`):
```python
import asyncio
import os
from deepagents import create_deep_agent
from langchain_mcp_adapters.client import MultiServerMCPClient
client = MultiServerMCPClient({
"proxmox": {
"transport": "stdio",
"command": "uvx",
"args": ["--from", "git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0", "pve-mcp"],
"env": {
"PVE_HOST": os.environ["PVE_HOST"],
"PVE_TOKEN_ID": os.environ["PVE_TOKEN_ID"],
"PVE_TOKEN_SECRET": os.environ["PVE_TOKEN_SECRET"],
},
}
})
async def main():
tools = await client.get_tools()
agent = create_deep_agent(model="anthropic:claude-sonnet-5-5", tools=tools)
result = await agent.ainvoke({"messages": [{"role": "user", "content": "What can you do?"}]})
print(result["messages"][-1].content)
asyncio.run(main())
```
</details>
<details>
<summary><b>Docker Agent</b></summary>
Save this as `agent.yaml`, then run `docker agent run agent.yaml`:
```yaml
agents:
root:
model: anthropic/claude-sonnet-5-5
description: Assistant with the proxmox MCP tools
instruction: Use the proxmox tools to answer the user's requests.
toolsets:
- type: mcp
command: uvx
args:
- "--from"
- "git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0"
- "pve-mcp"
env:
PVE_HOST: "pve.example.lan"
PVE_TOKEN_ID: "root@pam!mcp"
PVE_TOKEN_SECRET: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
```
</details>
<details>
<summary><b>Hermes Agent</b></summary>
Add this to `~/.hermes/config.yaml`, then run `/reload-mcp` in a chat:
```yaml
mcp_servers:
proxmox:
command: uvx
args: ["--from", "git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0", "pve-mcp"]
env:
PVE_HOST: "pve.example.lan"
PVE_TOKEN_ID: "root@pam!mcp"
PVE_TOKEN_SECRET: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
```
</details>
<details>
<summary><b>Cline / Roo Code / Kilo Code</b></summary>
MCP Servers → Configure (`cline_mcp_settings.json` / `mcp_settings.json`):
```json
{
"mcpServers": {
"proxmox": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0",
"pve-mcp"
],
"env": {
"PVE_HOST": "pve.example.lan",
"PVE_TOKEN_ID": "root@pam!mcp",
"PVE_TOKEN_SECRET": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
}
}
}
```
</details>
<details>
<summary><b>Zed</b></summary>
Add this to `settings.json`:
```json
{
"context_servers": {
"proxmox": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0",
"pve-mcp"
],
"env": {
"PVE_HOST": "pve.example.lan",
"PVE_TOKEN_ID": "root@pam!mcp",
"PVE_TOKEN_SECRET": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
}
}
}
```
</details>
<details>
<summary><b>opencode</b></summary>
Add this to `opencode.json` (project) or `~/.config/opencode/opencode.json`:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"proxmox": {
"type": "local",
"command": [
"uvx",
"--from",
"git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0",
"pve-mcp"
],
"environment": {
"PVE_HOST": "pve.example.lan",
"PVE_TOKEN_ID": "root@pam!mcp",
"PVE_TOKEN_SECRET": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
"enabled": true
}
}
}
```
</details>
<details>
<summary><b>Goose</b></summary>
Run `goose configure` → Add Extension → Command-line Extension, with the command `uvx --from git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0 pve-mcp`. Or add this to
`~/.config/goose/config.yaml`:
```yaml
extensions:
proxmox:
type: stdio
enabled: true
cmd: uvx
args: ["--from", "git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0", "pve-mcp"]
envs:
PVE_HOST: "pve.example.lan"
PVE_TOKEN_ID: "root@pam!mcp"
PVE_TOKEN_SECRET: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
```
</details>
<details>
<summary><b>Other clients, a permanent install, a local checkout</b></summary>
Most clients take the standard `mcpServers` JSON shown for Claude Desktop. Some use `servers` instead.
To install once instead of fetching on each start:
```bash
uv tool install git+https://github.com/bigbatmanorg/pve-mcp@v0.1.0
```
then use `"command": "pve-mcp"`, `"args": []`.
From a checkout: `"command": "uv"`, `"args": ["run", "--directory", "/abs/path/pve-mcp", "pve-mcp"]`.
</details>
<!-- install:end -->
## Safety
| Setting | Effect |
|---|---|
| default | read-only: only GET calls are sent |
| `PVE_ALLOW_WRITES=true` | POST/PUT allowed |
| `confirm=true` (per call) | also required for DELETE, stop/reset, rollback, node power |
| `PVE_SSH_ENABLED=true` | enables `pve_node_ssh` (also needs writes) |
Task-returning calls wait for the task and fail with its log if it fails.
## Configuration
`PVE_HOST`, `PVE_PORT` (8006), `PVE_TOKEN_ID` (`user@realm!name`), `PVE_TOKEN_SECRET`,
`PVE_VERIFY_SSL` (`false`, `true`, or a CA bundle path such as a copy of `/etc/pve/pve-root-ca.pem`),
`PVE_TIMEOUT`, `PVE_SSH_USER`, `PVE_SSH_KEY`, `PVE_SSH_PORT`, `PVE_NODE_HOSTS` (`node:addr,...`).
## Tests
uv run pytest # unit tests (mocked HTTP)
PVE_LAB=1 uv run pytest -m lab # lifecycle tests on the lab only
Lab tests create and delete real objects; run them only against a disposable instance (PVE_LAB=1 with LABPVE_HOST, LABPVE_TOKEN_ID, LABPVE_TOKEN_SECRET).
## License
AGPL-3.0-or-later. The bundled schema (`src/pve_mcp/data/pve-api.json.gz`) is generated from Proxmox VE's `pve-docs` API viewer data, which is AGPL-3.0 licensed, Copyright Proxmox Server Solutions GmbH.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues