Zum Inhalt

Homelab MCP – Entwicklung

Stand 08.09.2026: Der neue generische MCP-Server wird parallel zum produktiven CT 108 entwickelt und läuft als eigener Testdienst auf CT 109. CT 108 bleibt unverändert und dient bis zur vollständigen Migration als produktive Referenz und Fallback.

Ziel

homelab-mcp soll als generische, plugin-basierte MCP-Plattform für private Homelabs funktionieren. Haus- und installationsspezifische Daten liegen ausschließlich lokal. Der gemeinsame Anwendungscode liegt im privaten GitHub-Repository HerrBausW/homelab-mcp und wird über versionierte Releases installiert.

Der neue Aufbau trennt bewusst:

  1. MCP-Core und Policy
  2. interne Capabilities wie SSH oder HTTP
  3. semantische Plugins wie Proxmox oder Home Assistant MCP
  4. lokale Konfiguration und Secrets
  5. Netzwerk-Authentifizierung und Trusted Proxy
  6. Admin- und Audit-Ebene
flowchart TD
    CLIENT["MCP-Client"] --> PROXY["Trusted Proxy"]
    PROXY --> AUTH["OAuth / JWT / JWKS"]
    AUTH --> MCP["MCP v2 / Streamable HTTP"]
    MCP --> POLICY["Policy Engine<br/>Default Deny"]
    POLICY --> TOOLS["Tool Registry"]
    TOOLS --> AUDIT["Tool-Audit"]
    AUDIT --> PLUGIN["Plugin Tool"]
    PLUGIN --> CAP["interne Capability"]
    CAP --> TARGET["Zielsystem"]

    UI["Admin UI"] --> API["Admin API"]
    API --> CFG["Konfiguration"]
    API --> SEC["Secret Store"]
    API --> PM["Plugin Manager"]
    API --> POLICY
    API --> AUDIT

Repository

Privates GitHub-Repository:

HerrBausW/homelab-mcp

Der schreibende Repository-Zugriff für die Entwicklung auf CT 108 erfolgt über einen eigenen Deploy Key nur für dieses Repository. Für die Selbstaktualisierung besitzt CT 109 zusätzlich einen eigenen, getrennten read-only Deploy Key. Der Public Key ist seit dem 09.09.2026 im privaten Repository autorisiert und der GitHub-Zugriff ist live bestätigt. Private Schlüssel werden weder zwischen den Hosts kopiert noch im Repository abgelegt.

Die README des Repositorys ist die allgemeine Installations- und Betriebsdokumentation für eine generische Installation. Private DieBrocks-Adressen und hausinterne Zugangsdaten gehören ausschließlich in dieses Wiki.

Sicherheitsprinzipien

  • Default Deny;
  • Hauptprozess unprivilegiert;
  • keine freie Shell im Core;
  • SSH, HTTP und WebSocket sind interne Capabilities und keine allgemeinen MCP-Tools;
  • Plugins deklarieren benötigte Capabilities und jedes angebotene Tool;
  • Tools werden als read, write oder destructive klassifiziert;
  • write und destructive sind unabhängig von Leserechten freizugeben;
  • Secrets bleiben außerhalb des Repositorys und werden nach dem Speichern nicht über normale Admin-Antworten im Klartext ausgegeben;
  • Konfiguration wird streng und fail closed validiert;
  • Remote-Betrieb erfordert OAuth, Trusted Proxy und persistentes Audit gleichzeitig;
  • Tool-Aufrufe werden ohne Argumente und Antworten minimiert auditiert;
  • Updates erfolgen über neue Release-Verzeichnisse mit atomarem current-Symlink und nicht durch git pull im laufenden System;
  • CT 109 bleibt bis zur konkreten produktiven Ingress-Konfiguration ausschließlich an Loopback gebunden.

CT 109 – Testsystem

Eigenschaft Wert
Container-ID 109
IP 10.10.10.34
OS Debian 13
CPU 1 vCPU
RAM 512 MiB
Disk ca. 8 GB
Node.js Debian-Paket, Node 20.19.2
Laufzeitbenutzer homelab-mcp
MCP-Port 127.0.0.1:3000
MCP-Endpunkt http://127.0.0.1:3000/mcp
MCP-Dienst homelab-mcp.service
Audit-Dienst homelab-mcp-audit-writer.service

CT 109 ist ausschließlich das parallele Testsystem. Der produktive CT 108 wurde für diese Entwicklung nicht umgebaut.

Bootstrap-Zugriff auf CT 109

Beim Anlegen des LXC war bereits ein SSH-Key für root hinterlegt, dieser passte jedoch nicht zu den auf CT 108 verfügbaren Schlüsseln. Deshalb wurde ein eigener Schlüssel nur für CT 109 erzeugt und dessen Public Key zusätzlich nach /root/.ssh/authorized_keys eingetragen.

Der Zugriff von CT 108 auf CT 109 verwendet strikte Host-Key-Prüfung. Der ED25519-Host-Key wurde vor dem ersten Login separat erfasst. StrictHostKeyChecking no wird nicht verwendet.

Root-Zugriff dient nur der Installation und Wartung des Testsystems. Der eigentliche MCP-Dienst läuft nicht als root.

Laufzeitbenutzer und Verzeichnisstruktur

Der MCP-Dienst läuft als eigener Systembenutzer ohne interaktive Shell:

homelab-mcp

Installationsstruktur:

/opt/homelab-mcp/
  releases/<commit>/
  current -> releases/<commit>/

/etc/homelab-mcp/
  config.json
  secrets/

/var/lib/homelab-mcp/
  state/
  backups/

/var/log/homelab-mcp/
/run/homelab-mcp/
/run/homelab-mcp-audit/

Wichtige Rechte:

  • Releases: root:homelab-mcp, Verzeichnisse 0750, Dateien 0640;
  • /etc/homelab-mcp/secrets: root-eigen, 0700;
  • /etc/homelab-mcp/config.json: root:homelab-mcp, 0640;
  • /var/lib/homelab-mcp/: für den Laufzeitbenutzer beschreibbar;
  • der Benutzer homelab-mcp kann den Anwendungscode unter /opt/homelab-mcp/current nicht verändern.

Installation auf CT 109

Installierte Debian-Pakete:

apt-get install -y nodejs npm git ca-certificates

Debian 13 liefert Node.js 20.19.2. Die offiziellen MCP-v2-Pakete benötigen Node.js >=20, daher ist kein zusätzliches NodeSource-Repository erforderlich.

Der Anwendungscode wird auf CT 108 aus dem geprüften Git-Stand als Release-Paket erzeugt, nach CT 109 übertragen und in ein neues Release-Verzeichnis entpackt. Im Zielrelease werden nur Produktionsabhängigkeiten installiert:

npm ci --omit=dev --no-audit --no-fund

Danach wird current atomar auf das neue Release umgestellt. Die allgemeine Anleitung befindet sich im README.md und in docs/installation.md des Repositorys.

Aktive Testkonfiguration

CT 109 startet bewusst mit minimalen Tool-Rechten:

{
  "plugins": {
    "system": { "enabled": true },
    "proxmox": { "enabled": false },
    "home-assistant-mcp": { "enabled": false }
  },
  "policy": {
    "grants": {
      "system": {
        "read": true,
        "write": false,
        "destructive": false
      }
    }
  }
}

Damit ist genau ein MCP-Tool sichtbar:

system_status

system_status ist rein lesend und liefert nur sichere Runtime-Metadaten wie Dienstname, Version, Node-Version und Uptime. Secrets oder lokale Konfigurationswerte werden nicht ausgegeben.

MCP v2

Der neue Core verwendet die offiziellen MCP-v2-Pakete:

@modelcontextprotocol/server
@modelcontextprotocol/node

Der Transport ist Streamable HTTP. Die Runtime erzeugt den Toolkatalog aus:

  1. installierten Plugin-Manifests;
  2. aktiviertem Plugin-Zustand;
  3. verfügbaren internen Capabilities;
  4. Default-Deny-Policy;
  5. Risiko- beziehungsweise Toolfreigaben.

Ein Tool, das nicht freigegeben ist, erscheint bereits nicht in tools/list.

Netzwerk-Authentifizierung

OAuth-Resource-Server und Trusted-Proxy-Modell sind implementiert und getestet. Details: Netzwerk-Authentifizierung.

Eine nichtlokale Bindung ist nur zulässig, wenn gleichzeitig vorhanden sind:

  1. vollständige OAuth-/JWT-/JWKS-Konfiguration;
  2. explizite Trusted-Proxy-Allowlist und geprüfte Forwarding-Header;
  3. persistentes MCP-Tool-Audit.

CT 109 verwendet diese Remote-Konfiguration noch nicht und bleibt deshalb auf Loopback.

Systemd-Härtung

homelab-mcp.service läuft als homelab-mcp und enthält unter anderem:

NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
ProtectClock=yes
ProtectHostname=yes
RestrictSUIDSGID=yes
RestrictRealtime=yes
RestrictNamespaces=yes
LockPersonality=yes
CapabilityBoundingSet=
AmbientCapabilities=

Der Dienst bindet derzeit ausschließlich an:

127.0.0.1:3000

Der Code erlaubt eine nichtlokale Bindung nur mit OAuth, Trusted Proxy und persistentem Audit. Die konkrete produktive Konfiguration ist noch nicht gesetzt.

End-to-End-Test

Der laufende Dienst auf CT 109 wurde mehrfach erfolgreich von CT 108 mit dem offiziellen MCP-v2-Client getestet.

Dafür wurde temporär ein SSH-Local-Forward aufgebaut:

CT 108:127.0.0.1:39000
  -> SSH
  -> CT 109:127.0.0.1:3000

Geprüft wurden:

  1. Verbindung zum Streamable-HTTP-Endpunkt;
  2. tools/list mit exakt system_status;
  3. tools/call für system_status;
  4. Antwort ohne Fehler;
  5. anschließend vorhandene attempt- und success-Audit-Ereignisse.

Admin-Ebene

Im Repository bereits implementiert und getestet:

  • scrypt-Passwort-Hashing;
  • begrenzte, ablaufende Sessions;
  • CSRF-Schutz;
  • Authentifizierung der Admin-API;
  • Plugin aktivieren/deaktivieren;
  • Policy-Verwaltung;
  • Secrets anlegen/ersetzen/löschen, ohne Klartext-Rückgabe;
  • Audit-Anzeige;
  • same-origin Admin-Weboberfläche;
  • Host-/Origin-Prüfung;
  • Request-Größenlimit;
  • Login-Rate-Limit;
  • HttpOnly-/SameSite-Cookie-Modell.

Der Admin-HTTP-Adapter ist noch nicht als eigener produktiver systemd-Dienst auf CT 109 installiert. Vor einer externen Freigabe werden der reale Authorization Server und der endgültige Ingress festgelegt.

Audit

Der Audit-Pfad ist auf CT 109 inzwischen live aktiv.

flowchart LR
    MCP["homelab-mcp<br/>User homelab-mcp"] --> SOCK["/run/homelab-mcp-audit/audit.sock"]
    SOCK --> WRITER["homelab-mcp-audit-writer<br/>root:homelab-mcp"]
    WRITER --> LOG["/var/log/homelab-mcp/YYYY-MM-DD.jsonl"]

Rechte auf CT 109:

/run/homelab-mcp-audit/audit.sock  root:homelab-mcp 0660
/var/log/homelab-mcp/             root:homelab-mcp 0750
/var/log/homelab-mcp/*.jsonl      root:homelab-mcp 0640

Der Laufzeitbenutzer kann den Socket benutzen, aber Audit-Dateien und Audit-Verzeichnis nicht direkt beschreiben oder löschen.

MCP-Tool-Aufrufe erzeugen minimierte Ereignisse:

attempt -> Tool-Ausführung -> success/error

Gespeichert werden Toolname, Plugin, Risikoklasse, Ergebnis und die geprüfte Identität. Tool-Argumente und Tool-Antworten werden nicht übernommen.

Ist bereits der Audit-Intent nicht schreibbar, wird das Tool nicht ausgeführt. Ist das Tool bereits gelaufen und nur die abschließende Audit-Bestätigung fällt aus, liefert MCP ausdrücklich den Hinweis, den Aufruf nicht automatisch zu wiederholen.

Der Live-Test auf CT 109 hat für system_status sowohl attempt als auch success bestätigt.

Plugin-Stand

Plugin Stand Zweck
system funktionsfähig read-only system_status
proxmox Manifest/Platzhalter später semantische Proxmox-Tools über interne SSH-Capability
home-assistant-mcp Manifest/Platzhalter später optionaler HA-MCP-Upstream

Es gibt bewusst kein allgemeines ssh_execute, shell_execute oder beliebiges http_request im neuen Core.

Automatisierte Tests

Die Tests decken inzwischen unter anderem ab:

  • Default-Deny-Policy;
  • Plugin-Manifest-Prüfung und Pfad-Traversal;
  • strikte Konfigurationsvalidierung;
  • atomare und serialisierte Konfigurationsschreibvorgänge;
  • Secret Store und Rechte;
  • Admin-Authentifizierung, Sessions und CSRF;
  • OAuth JWT/JWKS, Audience, Issuer, Ablauf und Scopes;
  • RFC-9728-Metadaten und Bearer-Challenges;
  • Trusted-Proxy-IP, HTTPS- und Host-Prüfung;
  • offizieller MCP-v2-Client über Trusted Proxy + OAuth;
  • minimiertes fail-closed Tool-Audit;
  • privileggetrennten Audit-Writer;
  • Policy-Verwaltung;
  • Admin-HTTP-Härtung;
  • Plugin-Manager;
  • systemd-Härtungsinvarianten.

Vor jedem Push werden zusätzlich git diff --check und ein Scan auf private Schlüssel, lokale 10.10.x.x-Adressen, HA-MCP-Secret-URLs und persönliche DieBrocks-Daten im generischen Repository ausgeführt.

Update- und Rollback-Modell

Aktive Releases werden nicht verändert.

/opt/homelab-mcp/releases/<alt>/
/opt/homelab-mcp/releases/<neu>/
/opt/homelab-mcp/current -> releases/<neu>/

Ablauf:

  1. neuen Stand separat holen;
  2. vollständige Tests im Staging;
  3. neues Release-Verzeichnis anlegen;
  4. Produktionsabhängigkeiten installieren;
  5. Eigentümer und Rechte setzen;
  6. current atomar umschalten;
  7. Audit-Writer und MCP-Dienst neu starten;
  8. MCP-Smoke-Test und Audit prüfen;
  9. bei Fehlern current auf das vorherige Release zurückstellen.

Konfiguration und Secrets liegen außerhalb des Release-Baums und bleiben beim Rollback unverändert.

Nächste Schritte

  1. tatsächlichen OAuth-/OIDC-Authorization-Server auswählen und anbinden;
  2. endgültigen Cloudflare-/Reverse-Proxy-Ingress für CT 109 konfigurieren;
  3. Host-Firewall für den späteren nichtlokalen MCP-Port ergänzen;
  4. Admin-HTTP-Dienst und Initialpasswort-Bootstrap fertigstellen;
  5. interne SSH-Capability implementieren;
  6. Proxmox als erstes reales Infrastruktur-Plugin anbinden;
  7. danach optional HA-MCP, MikroTik, Wiki und weitere Plugins ergänzen;
  8. den bereits live bestätigten Self-Update-Pfad nach dem Timeout-/Cleanup-Fix noch einmal vollständig über die Admin-Oberfläche auslösen und anschließend die explizite Aktivierung eines inaktiven Releases testen;
  9. Funktions- und Sicherheitsvergleich mit dem produktiven CT 108 durchführen;
  10. erst nach vollständiger Parität und Prüfung eine Migration des öffentlichen Endpunkts erwägen.

Der produktive MCP-Server bleibt bis dahin unverändert.