Skip to main content
Glama
README.md
# homelab-mcp

An [MCP](https://modelcontextprotocol.io) server that gives an assistant
(Claude Code, the Claude app, or any MCP client) operational access to a home
lab through **one authenticated HTTPS endpoint**:

- **Proxmox VE** — list nodes and every VM/LXC (discovered live, so new guests
  appear automatically), start/stop/reboot, edit guest config, snapshots,
  storage, run commands inside guests (`pct exec` / QEMU guest agent), and a
  raw API escape hatch.
- **Home Assistant** — read states, call services, drive automations, browse the
  area/device/entity registries (incl. ESPHome devices), history, logbook,
  error log, plus raw REST and WebSocket escape hatches.
- **ESPHome** — list nodes, read/edit YAML, validate and flash over OTA, via the
  dashboard add-on or over SSH.
- **Shell & Docker** — run commands, read/write files, manage systemd units and
  journals, and manage Docker/compose on any host over SSH.
- **Fully Kiosk** — control Fully Kiosk Browser tablets over the Remote Admin
  API: screenshot, load a URL, screen on/off, brightness, text-to-speech,
  restart, and a raw command escape hatch.
- **Network** — ping sweep, TCP port scan and ARP/neighbour tables to discover
  what is on the LAN.

Call `homelab_overview` first — it reports which backends are configured and
gives a live inventory of each.

## How it fits together

```
Claude app / Claude Code
        │  HTTPS + Bearer token
        ▼
  mcp.fabrici.xyz   (your reverse proxy: nginx / NPM / Traefik)
        │  http://127.0.0.1:8787/mcp
        ▼
   homelab-mcp   ──► Proxmox API (token)
   (LXC/VM/Docker)──► Home Assistant (REST + WS)
        │        ──► ESPHome dashboard
        └────────►  SSH to hosts, network scans
```

The server speaks **streamable HTTP MCP** and enforces a bearer token (and an
optional IP allowlist) itself, so it is safe to expose behind your existing
reverse proxy alongside `ha.fabrici.xyz`.

## Quick start

```bash
python -m venv .venv && . .venv/bin/activate
pip install -e .

cp config/homelab.example.yaml config/homelab.yaml
$EDITOR config/homelab.yaml            # fill in hosts, tokens, ssh key

export MCP_AUTH_TOKEN=$(openssl rand -hex 32)   # or put it in the file
homelab-mcp --check                    # validate config, list modules
homelab-mcp                            # serve on :8787/mcp
```

Every module is optional: leave a section blank and its tools report that they
are not configured instead of failing the whole server.

## Deployment

The intended target is a small LXC container on Proxmox. See
[`deploy/`](deploy/) for a systemd unit, an install script and reverse-proxy
snippets (nginx / Nginx Proxy Manager). [`docs/SETUP.md`](docs/SETUP.md) walks
through provisioning the Proxmox API token, the Home Assistant token, the SSH
key and the `mcp.fabrici.xyz` proxy entry end to end.
[`docs/RESILIENCE.md`](docs/RESILIENCE.md) documents what the access path
depends on, the safeguards on the container (protection flag, weekly vzdump)
and how to rebuild or re-mint credentials from scratch.

## Connecting a client

Add to the Claude app / Claude Code as a remote MCP server:

- **URL**: `https://mcp.fabrici.xyz/mcp`
- **Header**: `Authorization: Bearer <MCP_AUTH_TOKEN>`

## Security

The user of this repo chose **full access** (unrestricted shell, all write
tools). That is powerful: the bearer token is effectively root on the whole
network. Accordingly the server:

- requires a bearer token on every request (`hmac.compare_digest`);
- **fails closed**: refuses to start with neither a token nor an allowlist on a
  non-loopback bind, unless `server.insecure: true` is set explicitly;
- supports an IP allowlist; `X-Forwarded-For` is honoured only from trusted
  proxies, and then the *right-most untrusted* hop is used, so the allowlist
  cannot be spoofed by prepending a header (note: a LAN-only allowlist also
  blocks the Claude app connector — see `docs/CLIENT.md`);
- writes a redacted JSONL **audit log** (owner-only, `0600`) of every
  state-changing call, and scrubs registered secrets from error messages too;
- keeps timestamped backups when overwriting files;
- can optionally refuse a few catastrophic commands (`guard_destructive: true`,
  off by default) — a **best-effort** fat-finger backstop, not a security
  boundary: every configured client already holds full shell access.

Secrets never go in git: `config/homelab.yaml`, `.env` and SSH keys are
git-ignored, and config values can be pulled from environment variables.