MCP-Bestand korrigiert + Mac-mini-Performance-Restriktion

- gitea-mcp + n8n-mcp nachgetragen (waren real live, aber aus dem Inventar verschollen)
- pve-mcp: SSE-Doku -> StreamableHTTP-Realitaet
- shelly-mcp/portainer-mcp: bewusst KEIN MCP-Client (User-Direktive 2026-08-28)
- Mac mini = n8n-Host, NICHT zentraler MCP-Host (Performance-Restriktion)
- ~/.claude.json + opencode.jsonc: 9 MCPs identisch (vorher 7 vs. 11 Asymmetrie)
- docs/mcp-host-policy.md: SSoT fuer Host->FQDN/Stack-Mapping
- docs/handoff-20260828-mcp-bestand-und-performance.md: Folge-Sessions A/B/C

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-28 18:40:05 +02:00
parent dc38d821c2
commit 759491b512
5 changed files with 230 additions and 28 deletions

View File

@@ -0,0 +1,112 @@
# Handoff 2026-08-28 — MCP-Bestand + Performance-Restriktion + 3 offene Folgepunkte
> **Bezug:** Inventur 2026-08-27 (`handoff-20260827-inventur-und-architektur.md`) →
> MCP-Cleanup 2026-08-28 (`handoff-20260828-mcp-cleanup-und-shelly-tote.md`) →
> TP35-Diskussion (`session-20260828-decisions.md` + `teilprojekte.md` → TP35).
> Diese Datei: **was sich aus dieser Session ergeben hat** und **was drei getrennte Folge-Sessions braucht**.
## TL;DR
- **Mac mini ist NICHT der zentrale MCP-Host** (Performance-Restriktion). Mac mini = n8n-Host. Zentraler MCP-Host = **DS923** (`nas-bf`, Portainer Endpoint 14).
- **gitea-mcp + n8n-mcp liefen real, waren aber aus dem Inventar verschollen** — heute nachgetragen.
- **portainer-mcp + shelly-mcp werden NICHT als MCP-Client installiert** (User-Direktive) — Portainer-API direkt reicht.
- **9 MCPs** jetzt identisch in `~/.claude.json` und `~/.config/opencode/opencode.jsonc` (vorher Asymmetrie).
- **Globale Repo-Änderungen committed** (Commit-Hash siehe Ende).
## Bestand 2026-08-28 (Portainer-API, alle 4 Endpoints live)
| Endpoint | ID | Container mit "mcp" / "shelly" |
|---|---|---|
| pve | 2 | keine |
| **nas-bf (DS923)** | 14 | `pve-mcp` (`10.40.20.173:3000`, ghcr.io/samik081/mcp-pve) |
| **macmini** | 20 | `gitea-mcp` (`172.17.0.3:3000`, node:22-alpine) · `n8n-mcp` (`172.19.0.4`, ghcr.io/czlonkowski/n8n-mcp, PublicPort 3001) |
| nas-fm | 21 | keine |
**Mac mini hat 5 Container insgesamt** (`gitea`, `gitea-mcp`, `n8n`, `n8n-redis`, `n8n-mcp`). gitea-mcp und n8n-mcp sind die MCPs, die heute nachgetragen wurden — sie waren vorher **nicht** im Inventar (warum auch immer, hat Lücken gerissen über mehrere Cleanup-Sessions).
## Was sich geändert hat (in dieser Session)
### Repo
- `mcp/INVENTORY.md` — gitea-mcp + n8n-mcp neu, pve-mcp SSE→StreamableHTTP-Drift korrigiert, shelly-mcp/portainer-mcp mit User-Direktive, Pflicht-Verifikationsregel ergänzt (alle 4 Endpoints abfragen, nicht nur Stichproben)
- `mcp/portainer/README.md` — `10.40.41.x` → `10.40.20.x` (VLAN1 tot), Weg B neu auf DS923 ausgerichtet
- `CLAUDE.md` — Mac-mini-Block: "zentraler MCP-Host" → "n8n-Host", Performance-Begründung ergänzt
- `docs/mcp-host-policy.md` — **NEU** (Grundsatz: Hosts aus Repo, Ollama = localhost, portainer-mcp/shelly-mcp = keine MCP-Clients, Lehren aus den Backups)
### Globale Configs (Backups erstellt)
- `~/.claude.json` — portainer-mcp + shelly-mcp **raus**, gitea-mcp + n8n-mcp **rein** (n8n-mcp mit Bearer-Header aus `{env:N8N_MCP_BEARER}`)
- `~/.config/opencode/opencode.jsonc` — gleiche Korrekturen
- Resultat: 9 MCPs identisch in beiden Configs (vorher 7 vs. 11 — Asymmetrie war Drift)
### Sicherheits- und Aufräum-Aktionen
- `~/.config/opencode/skills/projektstatus` Symlink → **entfernt** (opencode ist die neue Umgebung, aber Symlinks waren verkehrt — opencode-Skill-Speicherort muss erst gefunden werden, **eigene Session**, siehe unten)
- `docs/nas-vlan20/stacks/{portainer-mcp,shelly-mcp}.yml` → **gelöscht** (User-Direktive: keine MCP-Clients für die beiden Dienste)
### Was bewusst NICHT getan wurde
- **Kein Portainer-Bump 2.40 → 2.42.** War eine **Halluzination meinerseits** in der vorigen Session — `portainer-mcp` ist nicht im Bestand und wird auch nicht installiert. Inventar-Eintrag jetzt ⛔ **bewusst NICHT**.
- **Kein shelly-mcp-Image-Build auf der DS923.** Gehört in eine eigene Session mit ssh-Zugang zur DS923.
- **Keine Backup-Guards.** Gehört zu TP35 Schicht 5, eigene Session.
## Drei getrennte Folge-Sessions
### Session A — `/projektstatus`-Skill globalisieren (TP35 Schicht 6)
**Zweck:** opencode soll den Skill wie Claude Code aus `~/.claude/skills/projektstatus/` finden. Wo genau opencode Skills speichert, ist hier **nicht** zuverlässig ermittelbar (Opencode-Doku oder Live-Test nötig).
**Vorbereitet, aber von hier aus nicht machbar:**
- Symlink unter `~/.config/opencode/skills/projektstatus` wurde **testweise** angelegt und wieder **entfernt** — Christoph hat klargestellt: opencode ist die neue Umgebung, soll eigene Skill-Pfade haben, keine Symlinks.
**Schritte in der neuen Session:**
1. Opencode-Doku nachschlagen, wo Skills erwartet werden (vermutlich `~/.config/opencode/skills/<name>/SKILL.md` oder ähnlich).
2. Entweder `~/.claude/skills/projektstatus/` dorthin **kopieren** (nicht symlinken) oder einen Wrapper anlegen, der das richtige Verzeichnis erzeugt.
3. Test: `opencode mcp list` o.ä. (oder einfach eine Session starten und `/projektstatus` triggern).
4. **Lehre**: opencode-CLI-Doku lesen, bevor geraten wird.
### Session B — shelly-mcp-Container auf DS923 deployen (live, ssh/sudo)
**Vorbereitet im Repo:**
- `mcp/shelly/Dockerfile` (python:3.12-slim, **kein** `.env`-COPY)
- `mcp/shelly/requirements.txt` (`mcp[cli]>=1.0`, `starlette>=0.36`)
- `mcp/shelly/BUILD.md` (vollständige Build-Anleitung: rsync → docker build → Portainer-Stack deploy → DHCP-Reservierung)
- `mcp/shelly/.env` ohne Klartext-PSKs (Schema-Vorlage)
- `~/.secrets/credentials.env` + `exports.zsh` mit `SHELLY_DEVICE_PW` + korrigierter `SHELLY_SWEEP=10.40.30.180-250`
**Live-Sweep bestätigt (2026-08-28):** 23 Shellys auf VLAN30 erreichbar (`.30.202-.226` + `.233`).
**Schritte in der neuen Session:**
1. `ssh adm.christoph@10.40.20.10` und dort:
- `mkdir -p /data/compose/shelly-mcp-build`
- `rsync` Source vom Mac (User-Pfad, nicht im Repo)
- `docker build -t bf/shelly-mcp:0.1 .`
- Portainer-API: Container erstellen (nicht als Stack, sondern direkt — siehe Dockerfile + StreamableHTTP `:3030` ohne Stack-YML, weil User-Direktive)
- **DHCP-Reservierung auf `crs326-core` `dhcp-v20`** für die MAC, die der Container bekommt (MikroMCP via `manage_dhcp_lease make-static` mit der MAC nach erstem Boot)
2. Container-Endpoints in keiner globalen MCP-Config eintragen (User-Direktive: kein MCP-Client für shelly-mcp) — Zugriff via Direkt-HTTP/Skript.
### Session C — Caddyfile↔CF-Ingress-Drift-Probe + Backup-Guard
**Zweck:**
1. `caddy/cf-sync.py` (im Repo) laufen lassen und Ausgabe prüfen — der Drift-Check aus dem Inventur-Handoff 2026-08-27 wurde noch nicht ausgeführt.
2. Backup-Guard-Skript `~/dev/.bin/backup-guard.sh` schreiben (TP35 Schicht 5) mit den Schwellen 5 Commits ODER 1000 LOC in 3 Tagen (`git log --since=<guard-date>` + `git diff --shortstat`).
**Schritte:**
1. `python3 caddy/cf-sync.py` → Drift-Report, dann ggf. CF-Ingress nachziehen.
2. Backup-Guard-Skript + Pre-Commit-Hook für `~/dev/<projekt>/.git/hooks/pre-commit`.
3. Pro Projekt einmal laufen lassen, um die `.guard`-Dateien zu initialisieren.
## Commit
Die 4 Repo-Änderungen dieser Session sind in einem Commit:
- `mcp/INVENTORY.md` (M)
- `mcp/portainer/README.md` (M)
- `CLAUDE.md` (M)
- `docs/mcp-host-policy.md` (NEU)
Commit-Message (vorgeschlagen): `MCP-Bestand korrigiert + Mac-mini-Restriktion dokumentiert`
**Commit-Hash:** siehe `git log --oneline -1` nach diesem Handoff.
## Was diese Session NICHT lösen konnte (keine Schuldzuweisung, nur Lücken)
1. **gitea-mcp von macmini auf DS923 umziehen.** War in dieser Session **kein** Auftrag. Lohnt sich, weil der macmini die User-Workstation zusätzlich belastet.
2. **Komodo-MCP deployen** (TP-Komodo, bisher nur `mcp/komodo/MIGRATION.md`).
3. **`OllAMA_HOST`-Default in den MCP-Configs explizit `http://localhost:11434`** statt hartkodiertem `127.0.0.1:11434`. Aktuell in `~/.claude.json` hartkodiert — gehört in den `{env:OLLAMA_HOST}`-Stil wie in opencode.jsonc.
## Stand
- 2026-08-28, 18:30
- Diese Session = Bestand-Korrektur + Policy-Doku + Config-Sync + Commit
- Drei Folge-Sessions oben priorisiert
- Backups in `~/.claude.json.bak-pre-mcp-cleanup-20260828-181052` + `~/.config/opencode/opencode.jsonc.bak-pre-mcp-cleanup-20260828-181101`

63
docs/mcp-host-policy.md Normal file
View File

@@ -0,0 +1,63 @@
# 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/<id>/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).