# MCP-Host-Policy — wo Hosts herkommen > **Grundsatz:** MCP-Endpoints (URLs, Hosts, IPs) werden **nicht** in `~/.claude.json` oder `~/.config/opencode/opencode.jsonc` hartcodiert. Sie kommen aus dem **Repo** (FQDN→Backend im `caddy/Caddyfile`, IP→Stack in `docs/nas-vlan20/stacks/*.yml`) oder werden **live über Portainer-API** verifiziert (`/api/endpoints//docker/containers/json?all=1`). In der Übergangszeit (GC rendert noch keine MCP-Liste) werden Hosts **manuell** in den genannten Configs gepflegt — aber **immer** mit Verweis auf diese Datei und das `bf-infra`-Repo. ## Hosts, die als "localhost" laufen Bestimmte MCP-Dienste werden auf mehreren Hosts **gleichzeitig** lokal betrieben. In der MCP-Config steht dann **nicht** eine spezifische IP, sondern `localhost` (127.0.0.1) — und jeder Host, auf dem der MCP läuft, hat seine **eigene** Config. | Dienst | Hosts | Begründung | |---|---|---| | **ollama** | **macmini** (`10.40.20.167`, BF-LAN), **meine Kiste / MacBook** (lokal) | Ollama läuft **auf jedem Host, der sie braucht** — der MCP-Client ist auch auf diesem Host, also reicht `http://localhost:11434`. Wer BF-LAN-Remote will: `export OLLAMA_HOST=http://10.40.20.167:11434` in der Shell. **Niemals** in der MCP-Config hartcodieren — der zweite Host (z.B. ein LXC, ein dritter Mac) wäre dann ausgeschlossen. | ## Hosts, die aus dem Repo (FQDN→Backend) kommen `caddy/Caddyfile` ist SSoT für die **FQDN→Backend-IP-Port**-Tabelle (Stand 2026-08-28): | Dienst | FQDN | Backend (`caddy/Caddyfile`) | Container (Portainer Endpoint 20 = macmini) | |---|---|---|---| | gitea-mcp | `gitea-mcp.emsenhuber.net` | `10.40.20.167:3000` | `gitea-mcp` (Image `docker.io/node:22-alpine`, eigenbau) — ⚠️ **sollte auf DS923 umziehen** (Performance-Restriktion, macmini ist User-Workstation) | | n8n-mcp | `n8n-mcp.emsenhuber.net` | `10.40.20.167:3001` | `n8n-mcp` (Image `ghcr.io/czlonkowski/n8n-mcp:latest`) — bleibt auf macmini: n8n-Workflows lösen Tool-Calls gegen diesen MCP aus, **lokale Performance-Kopplung gewollt** | | ha-mcp | `ha-mcp.emsenhuber.net` | `10.40.20.105:9583` | nicht im Bestand (Portainer) — externer Dienst | **Mac mini ist der n8n-Host, NICHT der zentrale MCP-Host** (User-Direktive 2026-08-28, Performance-Überlegung: User-Workstation soll nicht durch MCP-Clients zusätzlich belastet werden). Der zentrale MCP-Host ist die **DS923** (`nas-bf`, Portainer Endpoint 14). gitea-mcp läuft dort nur historisch; Umzug ist separater Punkt. **Container-Definitionen leben auf dem macmini** (Docker Desktop), **nicht** im `bf-infra`-Repo. Repo-Quelle ist nur das **FQDN-Mapping im Caddyfile**. Wer den Container umziehen will, ändert das Mapping, nicht die MCP-Config. ## Hosts, die aus dem Repo (IP→Stack) kommen `docs/nas-vlan20/stacks/*.yml` ist SSoT für Container auf der DS923 (Portainer Endpoint 14) — **dem zentralen MCP-Host**: | Dienst | Stack-YML | IP | Status | |---|---|---|---| | pve-mcp | `pve-mcp.yml` (gitignored) | `10.40.20.173:3000/mcp` | 🟢 deployed | | shelly-mcp | `mcp/shelly/{Dockerfile,BUILD.md}` im Repo, Stack bewusst nicht im Repo (würde nur User-Direktive "kein MCP-Client" widersprechen). Deploy via Portainer-API direkt. | `10.40.20.180:3030/mcp` (geplant, Container **kein** MCP-Client) | 🟡 staged (Build + Deploy ausstehend) | | komodo-mcp | — | `10.40.20.176:3000/sse` | 🟡 geplant | | gitea-mcp | (geplant) | `10.40.20.x:3000/mcp` | 🟡 Umzug macmini → DS923 (separater Punkt, Performance) | | n8n-mcp | bleibt macmini | `10.40.20.167:3001/mcp` | 🟢 live, **bewusst** nicht umziehen (n8n-Workflow-Kopplung) | ## Hosts, die NICHT als MCP-Client exponiert werden (User-Direktive 2026-08-28) | Dienst | Warum nicht als MCP-Client | Alternative | |---|---|---| | **portainer-mcp** | MCP-Client bietet keinen Mehrwert gegenüber dem Direkt-API-Zugriff (HTTP-Curl / Skript). | Portainer-API direkt: `mcp/portainer/.env` hat `PORTAINER_URL` + `PORTAINER_API_KEY`. Skripte machen die Calls, kein Container-Layer nötig. | | **shelly-mcp** | Container-Deploy geht via Portainer-API (s.o.). | Genau dasselbe: deployen via Portainer-API, Shelly-Verwaltung via Direkt-Aufrufe gegen die FastMCP-HTTP-Schnittstelle. | **Entscheid gilt ab sofort.** Wer in `~/.claude.json` oder `~/.config/opencode/opencode.jsonc` einen Block für portainer-mcp oder shelly-mcp anlegt, **macht etwas verkehrt** — `mcp/INVENTORY.md` aktualisieren, kein neuer MCP-Client. ## Drift-Detection Eine Änderung an einer IP im `caddy/Caddyfile` oder in `docs/nas-vlan20/stacks/*.yml` muss sich in `~/.claude.json` / `~/.config/opencode/opencode.jsonc` widerspiegeln — sonst zeigt der MCP-Client auf eine Adresse, an der der Dienst nicht (mehr) läuft. **Aktuell** ist dieser Sync manuell. Sobald TP34 (Caddyfile via Nautobot/GC) läuft, ist das die Vorlage für eine **MCP-Endpoint-ComplianceRule**: jede MCP-URL in den globalen Configs muss eine IP haben, die im Nautobot-Backend-Set existiert. ## Lehren aus den Backups vom 2026-08-28 Die `.bak-pre-mcp-cleanup-*`-Dateien in `~/.claude.json.*` und `~/.config/opencode/opencode.jsonc.*` zeigen, was **wirklich da war**. Hätte ich sie vor dieser Session gelesen, wäre mir aufgefallen: 1. **`gitea-mcp` + `n8n-mcp` waren in `~/.claude.json` nie eingetragen**, obwohl die Container laufen. Drift seit unknown. (In `opencode.jsonc` waren sie.) 2. **`shelly-mcp` war in beiden Configs nie aktiv** (immer `enabled=false`), und der Portainer-Stand war "kein Container" — der "folgt noch"-Pfad war doppelt blockiert. 3. **Backups lesen ist Pflicht**, nicht Kür. Wer eine Vorgängersession zusammenfasst, schreibt die Backups auf die Liste — nicht nur das, was im Working-Tree des Repos liegt. ## Stand - 2026-08-28 (zweite Fassung, nach User-Korrektur "pfui, schäm dich"): Symlink `~/.config/opencode/skills/projektstatus` wurde wieder entfernt (opencode-Verzeichnis leer, kommt später neu). - Mac mini als zentraler MCP-Host bestätigt: gitea-mcp `:3000` + n8n-mcp `:3001` (Portainer Endpoint 20). - `portainer-mcp` + `shelly-mcp` sind **keine** MCP-Clients (Direkt-API-Wrapper reicht).