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

homelab-mcp-server

v0.2.0

Published

Tiered MCP server for Proxmox — least-privilege by default, every write backed up and revertible, fully audited

Downloads

145

Readme

homelab-mcp-server

Tiered MCP server for Proxmox — least-privilege by default, every write backed up and revertible, fully audited.

Designed by a human through a documented architecture-first process. Implemented by Claude against those specifications. See Design process.

CI

homelab MCP server demo

Why this exists

Managing a homelab has always meant SSH sessions full of the same ceremony: ls your way to the right directory, cat the config to understand what's there, open nano to make a two-line change, echo something into a file you could've just edited directly. Every task requires you to be the relay — gathering context, executing instructions, pasting output back, iterating.

From the operator's perspective, the immediate improvement is that the relay role mostly disappears. You describe what you want done. The tools handle the context-gathering, the execution, and the verification. The filesystem crawling and nano sessions that used to fill an SSH session are replaced by a single conversation turn.

From the AI's perspective, the improvement is precision. Without tools, suggesting a config change means giving instructions that might be applied to the wrong file, typed incorrectly, or made without knowing what was already there. With tools, the read-before-write cycle is automatic: check the current state, make the targeted change, read it back to confirm. Tasks that used to require ten back-and-forth exchanges — "run this, paste the output, now try this" — collapse into one. The feedback loop that makes useful assistance possible closes entirely on the AI side rather than depending on the operator to close it manually.

The tier model addresses the trust side of this. Giving an AI assistant access to a production-ish server is a reasonable thing to be cautious about. The answer isn't to refuse the capability — it's to calibrate it. Start at observe, where Proxmox itself enforces the limits and no software bug or misbehaving prompt can exceed the token's privileges. Escalate to companion only when you need it. The audit log records every write regardless of tier, and the backup pipeline makes every change reversible. The design invites you to trust incrementally rather than all at once.

Tier model

The server runs at one of four tiers. Choose the lowest tier that covers what you need.

| Tier | Credentials | Enforced by | What it adds | |------|-------------|-------------|--------------| | observe (default) | API token (PVEAuditor role) | Proxmox RBAC | Read-only tools | | operate | API token (MCPOperate role) | Proxmox RBAC | Guest start / stop / restart | | companion | + root SSH key | MCP server | In-guest exec, file I/O, snapshots, log tailing, config history | | root | + acknowledgment flag | MCP server | Host shell, host file read/write |

observe and operate are enforced by Proxmox itself — a bug or injected prompt cannot exceed the token's privileges. companion and above are enforced by the MCP server's guardrails (registration filtering + denylist + confirm gate), which are tripwires rather than a sandbox.

Tools

observe

| Tool | Description | |------|-------------| | describe_homelab | Secret-redacted census of the node: guests, storage, network, services, Tailscale | | health_check | Fixed-probe node health → ok / warn / crit per section (node, storage, guests, units, updates) | | pct_list | List LXC containers and status | | qm_list | List QEMU/KVM VMs and status | | query_audit | Filter and summarize the local audit log |

operate (adds to observe)

| Tool | Description | |------|-------------| | guest_start | Start a VM or container | | guest_stop | Stop a VM or container (confirm-gated) | | guest_restart | Restart a VM or container |

companion (adds to operate)

| Tool | Description | |------|-------------| | pct_exec | Run a command inside an LXC container | | pct_read_file / pct_write_file | Container file I/O via pct pull / pct push | | qm_agent_ping | Check a VM's QEMU guest agent responsiveness | | qm_exec | Run a command in a VM via the guest agent | | qm_read_file / qm_write_file | VM file I/O via the guest agent | | tail_log | Bounded, always-redacted journal or file tail (host or container) | | diff_config | Preview a revert: current → backup diff | | revert_file / list_backups | Restore a file from a local backup; list versions | | snapshot_create / snapshot_list / snapshot_rollback / snapshot_delete | Server-managed (mcp-) guest snapshots | | config_sweep | Hash-compare sweep of watched paths into a local git mirror; captures out-of-band edits |

root (adds to companion)

| Tool | Description | |------|-------------| | execute | Run a shell command on the Proxmox host | | read_file | Read a file from the host filesystem (stat-gated, windowed) | | write_file | Write a file on the host — full backup pipeline + audit on every write | | list_directory | List a host directory |

Sample output

{
  "schemaVersion": 1,
  "ts": "2026-06-12T03:16:08.616Z",
  "host": "10.0.0.10",
  "depth": "summary",
  "sections": {
    "node": {
      "version": "9.2.3",
      "uptime": "up 1 week, 6 days, 23 hours, 42 minutes",
      "cpu": 8,
      "memBytes": 16413732864,
      "memUsedBytes": 3904413696,
      "load": [0.1, 0.07, 0.08],
      "zpool": { "healthy": true, "detail": "no pools" }
    },
    "storage": [
      { "name": "local",        "type": "dir",     "active": true, "totalBytes": 100861726720,   "usedBytes": 9079173120,   "availBytes": 86611816448   },
      { "name": "local-lvm",    "type": "lvmthin", "active": true, "totalBytes": 374538764288,   "usedBytes": 143373437952, "availBytes": 231165325312  },
      { "name": "media-backup", "type": "dir",     "active": true, "totalBytes": 3936820731904,  "usedBytes": 261289168896, "availBytes": 3475475435520 }
    ],
    "containers": [
      { "vmid": 100, "name": "adguard-dns",  "status": "running" },
      { "vmid": 101, "name": "dockerBoss",   "status": "running" }
    ],
    "vms": [],
    "services": [
      { "vmid": 101, "failedUnits": [], "docker": [
        { "name": "jellyfin",     "image": "jellyfin/jellyfin:latest",                    "status": "Up 12 days (healthy)" },
        { "name": "sonarr",       "image": "lscr.io/linuxserver/sonarr:latest",           "status": "Up 12 days"           },
        { "name": "radarr",       "image": "lscr.io/linuxserver/radarr:latest",           "status": "Up 12 days"           },
        { "name": "prowlarr",     "image": "lscr.io/linuxserver/prowlarr:latest",         "status": "Up 12 days"           },
        { "name": "qbittorrent",  "image": "lscr.io/linuxserver/qbittorrent:latest",      "status": "Up 12 days"           },
        { "name": "gluetun",      "image": "qmcgaw/gluetun:latest",                       "status": "Up 12 days (healthy)" },
        { "name": "jellyseerr",   "image": "fallenbagel/jellyseerr:latest",               "status": "Up 12 days"           },
        { "name": "tailscale",    "image": "tailscale/tailscale:latest",                  "status": "Up 12 days"           },
        { "name": "portainer",    "image": "portainer/portainer-ce:latest",               "status": "Up 12 days"           },
        { "name": "homepage",     "image": "ghcr.io/gethomepage/homepage:latest",         "status": "Up 22 hours (healthy)"},
        { "name": "watchtower",   "image": "containrrr/watchtower:latest",                "status": "Up 25 hours (healthy)"},
        { "name": "flaresolverr", "image": "ghcr.io/flaresolverr/flaresolverr:latest",    "status": "Up 12 days"           }
      ]}
    ]
  },
  "errors": [],
  "redactions": 0
}

Setup

Prerequisites: Node.js 20+, Claude Code, a Proxmox VE node on the same LAN.

git clone https://github.com/ethanblauw21/homelab-mcp-server
cd homelab-mcp-server
npm install && npm run build

Then run the setup ceremony for your platform:

Linux / macOS

./scripts/setup.sh

Windows

.\scripts\setup.ps1

Both wrappers call the same Node.js setup script (scripts/setup.mjs). The ceremony walks you through the setup interactively — choose a tier, enter your node's address, pick a bootstrap mode:

  • auto — one SSH root password prompt, then fully automated
  • paste — prints a script to run in the Proxmox web shell (no root password touches your machine)

The ceremony then:

  1. Provisions mcp@pve with a tier-appropriate token on the node (idempotent — re-running changes the tier)
  2. Captures both trust anchors: the API TLS certificate fingerprint and (at companion) the SSH host key fingerprint
  3. Runs a 403 negative test to confirm privilege separation is actually enforcing, not just configured
  4. Calls claude mcp add to register the homelab server with Claude Code

Restart Claude Code when it completes.

All parameters can also be passed as flags for automated or repeated runs:

# Linux / macOS
./scripts/setup.sh --tier=observe   --node-host=192.168.1.100
./scripts/setup.sh --tier=companion --node-host=192.168.1.100 --bootstrap-mode=paste
./scripts/setup.sh --tier=observe   --node-host=192.168.1.100 --dry-run
# Windows
.\scripts\setup.ps1 -Tier observe   -NodeHost 192.168.1.100
.\scripts\setup.ps1 -Tier companion -NodeHost 192.168.1.100 -BootstrapMode paste
.\scripts\setup.ps1 -Tier observe   -NodeHost 192.168.1.100 -DryRun

Claude Desktop

Add to your claude_desktop_config.json:

{
  "mcpServers": {
    "homelab": {
      "command": "node",
      "args": ["/absolute/path/to/homelab-mcp-server/dist/index.js"],
      "env": {
        "MCP_TIER": "observe",
        "PVE_API_BASE_URL": "https://192.168.1.100:8006/api2/json",
        "PVE_API_TOKEN_ID": "mcp@pve!mcp-observe",
        "PVE_API_TOKEN_SECRET": "<secret from setup>",
        "PVE_API_NODE": "proxmox",
        "PVE_API_TLS_FINGERPRINT": "SHA256:<fingerprint from setup>"
      }
    }
  }
}

Upgrading tiers

Re-run the setup script at the new tier — it is idempotent. Downgrading from companion removes the SSH key from authorized_keys and deletes the local private key.

Root tier

Root is not selectable from the setup script by design. After a companion install, opt in by setting this exact env var in the registered MCP server configuration and restarting Claude Code:

MCP_HOST_ROOT_ENABLED=I-understand-Claude-gets-root-and-can-break-this-node

Any other value (including true) is treated as disabled. There is no runtime escalation path.

Troubleshooting

If the server isn't connecting, run the pre-flight checker first:

npm run doctor

It checks Node version, the claude CLI, the built artifact, all required env vars, API reachability, and SSH connectivity. Most first-time issues surface here in under two seconds.

Architecture

The server uses a pure-core, injected-I/O design: guardrails, backup policy, audit record construction, and tier registry are all pure functions. Tool handlers depend on injected SshTransport and NodeOps interfaces and never touch the concrete SSH or HTTP clients directly. The API backend runs at every tier; the SSH backend handles companion+ operations. Both implement the same interface — the transport follows the tool, not the tier.

See ARCHITECTURE.md for the full design: hybrid transport, trust model, backup pipeline, audit log, config history, guardrails, and source layout.

Development

npm run build       # tsc compile
npm run dev         # tsx watch
npm test            # all tests
npm run test:unit   # unit tests only (fast, no Docker required)
npm run test:int    # integration tests (requires Docker — Linux/CI only)
npm run lint
npm run typecheck

Integration tests spin up a Dockerized SSH container automatically and are skipped gracefully if Docker is absent. On Windows, unit tests are the local feedback loop.

See CONTRIBUTING.md for the ADR-first process, testing requirements, and what contributions are welcome.

Storage

All persistent data lives on the machine running the MCP server, not the Proxmox node:

| Data | Default location | |------|-----------------| | Backups | %LOCALAPPDATA%\claude-mcp\backups\ (Windows) / ~/.local/share/claude-mcp/backups/ | | Audit log | %LOCALAPPDATA%\claude-mcp\audit.jsonl | | Config history | %LOCALAPPDATA%\claude-mcp\config-history\ |

Point MCP_BACKUP_DIR at a synced folder or NAS for extra durability.

Security

The tier model is the primary defense: observe and operate are enforced by Proxmox RBAC, so no bug or injected prompt can exceed the token's privileges. At companion and above, the MCP server's guardrails take over — denylist, confirm gate, protected set, pinned trust, and an immutable audit trail.

See SECURITY.md for the full threat model, each layer of defense, and root tier guidance.

Design process

This project was built architecture-first. The design was specified through seven Architecture Decision Records before implementation began. Each ADR documents the decisions, constraints, and rationale for a slice of the system — the code was written to satisfy the spec, not the other way around.

| ADR | Scope | |-----|-------| | ADR-001 | SSH transport, initial tool surface, guardrails, testing strategy | | ADR-002 | Census tool: redaction, drift detection, tier-aware sections | | ADR-003 | Container file I/O, backup pipeline, snapshot guard | | ADR-004 | Transport hardening: pinned trust, timeout enforcement, denylist, audit | | ADR-005 | VM parity: qm_* tools, health check, log tailing, audit forensics | | ADR-006 | Git-backed config history: mutation commits, config sweep, push modes | | ADR-007 | Permission tiers, hybrid transport, root flag, protected set |

The ADRs are the authoritative specification — CLAUDE.md and the testing strategy reference them, not the reverse. The tier model, trust model, backup pipeline design, and guardrail architecture were all reasoned through in writing before a line of implementation existed.

Ethan designed and architected the system. Claude implemented the code against those specifications. The co-author attribution on commits reflects who wrote the implementation; the ADR corpus reflects who made the design decisions. These are different contributions, and the distinction is intentional.

See ROADMAP.md for planned work and known deferred items from the ADR series.