Skip to main content
Glama

đź§ą Hausmeister

Ein MCP-Server, mit dem ein KI-Assistent (z. B. Claude Code) auf einem Unraid-Server nach dem Rechten sehen darf, ohne ihm die SchlĂĽssel zum ganzen Haus zu geben.

Der Hausmeister hat einen Schlüsselbund, geht durchs Haus und schaut, ob alles läuft. In den freigegebenen Räumen darf er das Licht an- und ausmachen, und im Notfall ruft er dich an. Umbauen, Wände einreißen oder neue Mieter einziehen lassen darf er nicht. Alles, was er tut, steht im Hausbuch.

Ăśbersetzt heiĂźt das: Container und Logs lesen, Server-Zustand prĂĽfen und nach RĂĽckfrage freigegebene Container starten, stoppen oder neu starten. Ohne SSH, ohne Root und ohne Docker-Socket.

KI-Client  ──HTTP + Bearer-Token (Port 8765)──▶ Hausmeister ──▶ Unraid GraphQL-API
Besitzer   ──Browser + Passwort (Port 8766)──▶ Weboberfläche: wer darf was, Not-Aus, Hausbuch

So sieht das aus

Freigaben je Container, gruppiert nach „freigegeben / läuft / gestoppt“:

Containerliste

Regeln, Passwortwechsel und das Hausbuch mit jeder Aktion und jeder Ablehnung:

Regeln und Hausbuch

Beim ersten Aufruf setzt der Besitzer das Passwort, mit dem Code aus dem Container-Log:

Erste Einrichtung

Heller Modus: docs/container-hell.png. Die Container in den Bildern sind erfunden.

Related MCP server: MCP SSH SRE

Was er darf

Tool

Art

Einschränkung

container_list

lesen

alle Container; manageable / logsReadable = was erlaubt ist

container_status(name)

lesen

nur freigegebene

container_logs(name, lines)

lesen

eigenes Recht je Container, Zeilenlimit, Secrets serverseitig geschwärzt

server_metrics

lesen

CPU, RAM, Temperaturen, Array-Zustand, Plattenfehler

container_start/stop/restart(name)

schreiben

nur freigegebene, Sperrzeit pro Container, Audit-Log

Was er bewusst nicht kann: Shell, exec, Container anlegen, löschen oder aktualisieren, Array, Shares oder Plugins verwalten.

Weboberfläche (Port 8766)

Die Rechte ändert der Besitzer im Browser, nicht der Assistent:

  • Container: je Container getrennt „Steuern“ (start/stop/restart) und „Logs“

  • Not-Aus: ein Schalter sperrt sofort alle Schreibaktionen, unabhängig von den Haken

  • Sperrzeit und Zeilenlimit einstellbar

  • Hausbuch: alle Aktionen inklusive abgelehnter Versuche

Änderungen wirken sofort, ohne Neustart (/data/settings.json, wird bei Änderung neu gelesen).

Erste Einrichtung

Beim ersten Start gibt es noch kein Passwort. Der Container schreibt dann einen Einrichtungscode in sein Log (in Unraid: Container-Symbol → Logs, oder docker logs hausmeister). Wer die Oberfläche öffnet, setzt damit das Passwort.

Der Code ist wichtig: Ohne ihn könnte der Assistent, der denselben Port erreicht, einfach zuerst da sein und sich selbst die Oberfläche einrichten. An das Container-Log kommt nur, wer Zugriff auf den Server hat. Das Fenster schließt nach 30 Minuten; danach erzeugt ein Neustart einen neuen Code. Wer lieber ohne Code arbeitet, setzt GUI_PASSWORD_HASH (aus python3 hashpw.py) – dann entfällt die Einrichtung. Das Passwort selbst lässt sich später in der Oberfläche ändern.

Getrennt vom Assistenten: eigener Port, eigene Anmeldung, Sitzung als signiertes Cookie (SameSite=Strict), CSRF-Header- und Origin-Prüfung, fünf Fehlversuche je IP pro fünf Minuten. Das MCP-Token gilt hier nicht, und die Sitzung der Oberfläche öffnet umgekehrt nicht den MCP-Port. Sonst könnte sich der Assistent selbst mehr Rechte geben. Das Passwort gehört deshalb nicht auf den Rechner des Assistenten.

Warum die Grenze hier liegt und nicht beim API-Key

Container starten und stoppen braucht in Unraid DOCKER:UPDATE_ANY (geprĂĽft gegen unraid/api v4.35.1). Dasselbe Recht erlaubt aber auch updateContainer und updateAllContainers. Der Unraid-Key darf den KI-Client deshalb nie erreichen: Er liegt nur in diesem Container, der Client kennt nur das MCP-Token. Whitelist, Sperrzeit und Audit-Log werden serverseitig erzwungen. Nachgebaute Aufrufe vom Client aus kommen daran nicht vorbei.

Weitere SchutzmaĂźnahmen:

  • Logs gehen ĂĽber die Unraid-API. Es gibt keinen Mount von /var/lib/docker und keinen Socket-Proxy.

  • Der Container läuft ohne Root (uid 10001), mit read_only, cap_drop: ALL und no-new-privileges.

  • Das Token wird in konstanter Zeit verglichen, auĂźerdem gibt es eine Host-Header-PrĂĽfung gegen DNS-Rebinding.

  • Bewusste Ablehnungen kommen im Klartext beim Client an, unerwartete Fehler bleiben verborgen.

  • Die Tool-Beschreibungen sagen dem Modell, dass Logzeilen Daten sind und keine Anweisungen.

Einrichtung

  1. Unraid-API-Key mit genau diesen Rechten anlegen. In Unraid 7.3.x zuverlässiger per CLI, weil die Weboberfläche teils Ressourcen mit auswählt. Ohne --roles "" bricht die CLI mit Invalid data structure ab:

    unraid-api apikey --create --name "Hausmeister" --roles "" \
      --permissions "DOCKER:READ_ANY,DOCKER:UPDATE_ANY,INFO:READ_ANY,ARRAY:READ_ANY" --json
  2. Projekt auf den Server kopieren. Daneben aus den Vorlagen anlegen:

    • .env (aus .env.example: IP, Key, Token)

    • config.json (aus config.example.json: Startwerte der Freigaben; danach zählt /data/settings.json aus der Oberfläche)

  3. Stack starten, z. B. in Compose.Manager mit Compose Up oder docker compose up -d --build.

  4. Ins Container-Log schauen, den Einrichtungscode kopieren, http://<unraid-ip>:8766 öffnen und das Passwort setzen.

  5. PrĂĽfen: curl -X POST http://<unraid-ip>:8765/mcp muss 401 liefern.

Absichern (wichtig)

Wenn der Client-Rechner den Deploy-Ordner per SMB erreicht (z. B. ein Share), könnte der Assistent dort die .env lesen oder die Whitelist ändern. Deshalb nach dem Deploy auf dem Server:

chown -R root:root /pfad/zu/hausmeister && chmod -R u=rwX,go=rX /pfad/zu/hausmeister && chmod 600 /pfad/zu/hausmeister/.env

Lag der Key vorher lesbar herum, einen neuen Key mit neuem Namen anlegen und den alten löschen. --overwrite behält den alten Key-Wert.

Claude Code anbinden

Das Token als Umgebungsvariable HAUSMEISTER_TOKEN setzen, dann:

claude mcp add --transport http --scope user hausmeister http://<unraid-ip>:8765/mcp --header 'Authorization: Bearer ${HAUSMEISTER_TOKEN}'

Rückfrage vor jeder Änderung, in ~/.claude/settings.json:

"permissions": {
  "allow": ["mcp__hausmeister__container_list", "mcp__hausmeister__container_status",
            "mcp__hausmeister__container_logs", "mcp__hausmeister__server_metrics"],
  "ask":   ["mcp__hausmeister__container_start", "mcp__hausmeister__container_stop",
            "mcp__hausmeister__container_restart"]
}

Logs: die stdout-Falle

Die Unraid-API ruft docker logs auf und liest davon nur stdout (geprüft in docker-log.service.ts, auch im aktuellen Upstream). Container, die nach stderr schreiben — bei Python die Voreinstellung von logging — sehen darüber leer aus, obwohl ihr Log voll ist.

Zwei Wege:

  1. In der eigenen Anwendung auf stdout loggen, z. B. logging.basicConfig(..., stream=sys.stdout). Das ist ohnehin die saubere Variante fĂĽr Container.

  2. Reserve-Logquelle einschalten (DOCKER_LOG_DIR + der :ro-Mount in der docker-compose.yml). Dann liest der Hausmeister /var/lib/docker/containers/<id>/<id>-json.log, sobald die API nichts liefert — dort stehen beide Ströme; Zeilen aus stderr sind mit ! markiert. Ohne Mount ändert sich nichts.

    Dafür braucht der Container user: "10001:0": Docker legt die Logordner als root:root mit drwx--x--- / -rw-r----- an, die Gruppe darf also lesen. Der Benutzer bleibt unprivilegiert, es läuft nichts als root.

    Abwägung: Der Prozess kann damit die Rohlogs aller Container lesen, gefiltert wird nur noch im Code (docker_logs.py: ausschließlich <64-hex-id>/<id>-json.log, nur letzte Bytes, keine anderen Dateien). Kein Socket, kein Schreibrecht. Wer das nicht will, lässt Mount, Variable und die user-Zeile weg.

Netz

Der Port ist nur an die LAN-IP gebunden. Keinen Reverse-Proxy-Eintrag, keine Cloudflare-Route und kein Port-Forward anlegen. Für unterwegs gehört er ins VPN (Tailscale, WireGuard), nicht ins Internet.

Das Hausbuch

docker exec hausmeister cat /data/audit.log

Eine JSON-Zeile pro Schreibaktion, inklusive abgelehnter Versuche (denied) und No-ops.

Entwicklung

python -m venv .venv && .venv/bin/pip install -r requirements.txt   # Windows: .venv\Scripts\...
python -m unittest discover -s tests -t .

tests/test_server.py testet über echtes HTTP mit dem offiziellen MCP-Client (401 ohne Token, Host-Header-Prüfung, genau die erlaubten Tools), tests/test_gui.py die Oberfläche inklusive der Trennung beider Zugänge: MCP-Token öffnet die GUI nicht, GUI-Sitzung öffnet den MCP-Port nicht. tests/test_auth.py deckt die Einrichtung ab (falscher Code, abgelaufenes Fenster, kein zweites Setup, Passwortwechsel), tests/test_docker_logs.py die Reserve-Logquelle inklusive Pfad-Ausbruchsversuchen. Gebaut auf mcp 2.x (MCPServer), getestet gegen Unraid 7.3.2 / API 4.35.1.

Entstehung

Dieses Projekt wurde mit Claude Code (Anthropic) gebaut — von der Recherche in der Unraid-API über Code und Tests bis zur Oberfläche. Entwurf, Entscheidungen und jeder Schritt auf dem Server liefen über den Besitzer des Homelabs, in dem es läuft.

Lizenz

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    A Python-based server that enables AI assistants to interact with an Unraid server through the official Unraid GraphQL API, providing read-only access to system information, Docker containers, VMs, storage, and more.
    131
    MIT
  • A
    license
    Not graded
    quality
    F
    maintenance
    Provides read-only server monitoring and diagnostic tools for AI assistants to manage Linux and Unraid systems via SSH. It enables natural language interactions for container management, storage health checks, and system log analysis while keeping credentials secure.
    17
    ISC
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables AI assistants to manage WHM hosting accounts and server administration tasks including account management, server stats, updates, SSL, backups, and email through a secure API.
    10
    -