Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.venv/
.env
.git/
.github/
.pytest_cache/
__pycache__/
*.pyc
tests/
docs/
.ruff_cache/
.mypy_cache/
htmlcov/
*.egg-info/
10 changes: 9 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Proxmox Connection
PROXMOX_HOST=192.168.1.100
PROXMOX_PORT=8006
# WARNING: Defaults to false (allows self-signed certs). Set true for production.
PROXMOX_VERIFY_SSL=false

# Auth Option 1: API Token (preferred)
Expand All @@ -15,7 +16,14 @@ PROXMOX_TOKEN_VALUE=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
PROXMOX_DRY_RUN=false
PROXMOX_ALLOWED_NODES=
PROXMOX_PROTECTED_VMIDS=
PROXMOX_MAX_CONCURRENT_TASKS=5

# SSH Configuration (for disk management tools)
PROXMOX_SSH_USER=root
PROXMOX_SSH_PORT=22
# PROXMOX_SSH_KEY_PATH=~/.ssh/id_rsa # Path to SSH private key (recommended)
# PROXMOX_SSH_PASSWORD= # SSH password. Falls back to PROXMOX_PASSWORD if empty.
PROXMOX_SSH_HOST_KEY_CHECKING=true
# PROXMOX_SSH_KNOWN_HOSTS= # Path to known_hosts file (default: ~/.ssh/known_hosts)

# Server
MCP_TRANSPORT=stdio
Expand Down
35 changes: 35 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- run: uv python install 3.12
- run: uv sync --dev
- run: uv run ruff check src/ tests/
- run: uv run ruff format --check src/ tests/

test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- run: uv python install ${{ matrix.python-version }}
- run: uv sync --dev
- name: Run tests
run: uv run pytest tests/ -v --cov=src/proxmox_mcp --cov-report=term-missing
env:
PROXMOX_HOST: "test"
PROXMOX_TOKEN_NAME: "test@pam!ci"
PROXMOX_TOKEN_VALUE: "00000000-0000-0000-0000-000000000000"
9 changes: 5 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Project Overview

MCP (Model Context Protocol) server for Proxmox VE infrastructure management. Exposes 58 tools, 10 resources, and 6 prompts via FastMCP. Python 3.11+, async-first design wrapping the synchronous `proxmoxer` library with `asyncio.to_thread()`.
MCP (Model Context Protocol) server for Proxmox VE infrastructure management. Exposes 91 tools, 10 resources, and 6 prompts via FastMCP. Python 3.11+, async-first design wrapping the synchronous `proxmoxer` library with `asyncio.to_thread()`.

## Commands

Expand Down Expand Up @@ -40,11 +40,12 @@ mypy src/proxmox_mcp # Type check

- **server.py** — Creates `mcp` (FastMCP) and `proxmox_client` (ProxmoxClient) at module level. Tool/resource/prompt modules import these and register via decorators. Side-effect imports at bottom of file register everything with mcp.
- **config.py** — `ProxmoxConfig(BaseSettings)` loads from `.env`. Uses `model_validator` to parse comma-separated env vars into typed lists (protected_vmids, allowed_nodes).
- **client.py** — `ProxmoxClient` wraps proxmoxer. Key safety methods: `check_protected()`, `validate_node()`, `resolve_node_for_vmid()`, `dry_run_response()`. All Proxmox API calls go through `api_call()` which runs sync proxmoxer in a thread.
- **tools/** — 8 domain modules (cluster, node, vm, container, storage, task, backup, network). Each has local `get_mcp()`/`get_client()` helpers that import from `server.py`.
- **client.py** — `ProxmoxClient` wraps proxmoxer. Key safety methods: `check_protected()`, `validate_node()`, `resolve_node()` (validates + auto-detects node for VMID), `dry_run_response()`. All Proxmox API calls go through `api_call()` which runs sync proxmoxer in a thread.
- **tools/** — 9 domain modules (cluster, node, vm, container, storage, task, backup, network, disk). Each has local `get_mcp()`/`get_client()` helpers that import from `server.py`.
- **resources/resources.py** — 10 read-only `proxmox://` URI resources returning JSON strings.
- **prompts/prompts.py** — 6 workflow prompt templates.
- **utils/** — `errors.py` (exception hierarchy), `validators.py` (validate_vmid, validate_node_name), `formatters.py` (format_vm_summary, format_bytes, etc.)
- **utils/** — `errors.py` (exception hierarchy), `validators.py` (validate_vmid, validate_node_name), `sanitizers.py` (input sanitization for disk/shell params), `formatters.py` (format_vm_summary, format_bytes, etc.)
- **ssh.py** — `SSHExecutor` for remote command execution via paramiko (used by disk tools).

### Conventions

Expand Down
23 changes: 23 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM python:3.12-slim AS builder

WORKDIR /app

COPY pyproject.toml .
COPY src/ src/

RUN pip install --no-cache-dir .

FROM python:3.12-slim

WORKDIR /app

COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=builder /usr/local/bin/proxmox-mcp /usr/local/bin/proxmox-mcp
COPY src/ src/

ENV MCP_TRANSPORT=streamable-http
ENV MCP_HTTP_PORT=3001

EXPOSE 3001

CMD ["proxmox-mcp"]
Loading
Loading