diff --git a/CLAUDE.md b/CLAUDE.md index 362a27a..f60b335 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -20,7 +20,8 @@ DNS/DHCP-Ablösung, WLAN via **CAPsMAN auf dem Core** (TP28, 2026-08-17 — der - **`inventory/` ist tot** (Pipeline seit 2025-06 stale). Live-Daten aus MikroMCP/CF/Portainer. - **`mcp/INVENTORY.md`** ist BF-Netz-SSoT; `~/.claude.json`/`opencode.jsonc` sind globalere Werkzeugkästen. - **terpene.teilzeitladen.xyz**: Worker läuft auf Mac mini (`10.40.20.167:8890`), **kein Caddy-Block, kein CF-Tunnel** — Backfill via DB-Migration blockiert (Handoff 2026-08-27). -- **shelly-mcp läuft auf keinem Host** (Stand 2026-08-27, kein lokaler Prozess, kein Container) — Konfig zeigt auf `127.0.0.1:3030` mit `enabled=false`. Container-Deploy + neue URL auf VLAN20 (`10.40.20.30:3030`) ist eigener Punkt (war in Inventur nicht aufgefallen). +- **shelly-mcp / portainer-mcp bewusst NICHT als MCP-Client** (User-Direktive 2026-08-28): shelly-mcp-Container auf DS923 wird via **Portainer-API** (Skript-Aufrufe, `mcp/portainer/.env` als Token-Quelle) deployt, nicht via MCP-Protokoll. Inventar-Eintrag spiegelt das. `mcp-host-policy.md` ist SSoT für die Host-/IP-Konvention. +- **Mac mini `10.40.20.167`** ist der **n8n-Host** (n8n `:5678`, n8n-redis `:6379`, n8n-mcp `:3001`) — **NICHT** der zentrale MCP-Host. Performance-Überlegung: MCP-Clients würden auf dem macmini (User-Workstation) laufen und die Box zusätzlich belasten; deshalb liegt der MCP-Host auf der **DS923** (`nas-bf`, Portainer Endpoint 14). gitea-mcp läuft aktuell noch auf dem macmini (historisch, eigene Container-Definition dort) — gehört **bei Gelegenheit** auf die DS923 umgezogen (separater Punkt, **nicht** Teil dieser Session). Container-Definitionen für n8n leben auf dem macmini (Docker Desktop); FQDN→Backend-Mapping im `caddy/Caddyfile`. ## TP-Management (2026-08-26) @@ -189,7 +190,8 @@ Verhaltensregeln für nachfolgende AI-Sessions — kleiner, klarer, weniger Drif - **Master-Frage früh stellen.** Bei jedem Repo, jeder Datei, jedem Tooling: „Was ist die SSoT, wo lebt der Master, was ist Mirror?" — **bevor** du eine Lösung vorschlägst. - **Drift zuerst, Lösungen später.** Drift-Tabelle posten + fragen, was richtig ist. Keine Lösungen vor dem Befund. - **MCP-Erstellung/-Stilllegung = aktiv melden** (2026-08-27, Christoph-Direktive). Wer einen neuen MCP anlegt, ändert oder abschaltet — **meldet das proaktiv** an den User, nicht stillschweigend im Commit. Begründung: jede AI-Session ist gleichzeitig **Oberhand über Container** (MCP-Hosts) und **Garbage-Collector** auf Infra-Ebene — wenn Drift unbemerkt entsteht, sammelt sich Müll. -- **MCPs brauchen Host-Verifikation** vor Edit. Wenn der MCP verändert/editiert wird: vorher per Live-Read (nc/ps/curl) prüfen, ob der Host noch antwortet. Beispiel: 2026-08-27 — `shelly-mcp` zeigte auf `10.40.20.30:3030`, aber kein Host antwortet. Konfig auf `enabled=false`, dokumentieren statt hart fehlschlagen zu lassen. +- **MCPs brauchen Host-Verifikation** vor Edit. Wenn der MCP verändert/editiert wird: vorher per Live-Read (nc/ps/curl) prüfen, ob der Host noch antwortet. Beispiel: 2026-08-27 — `shelly-mcp` zeigte auf `10.40.20.30:3030`, aber kein Host antwortet. Konfig auf `enabled=false`, dokumentieren statt hart fehlschlagen zu lassen. **(Stand 2026-08-28: shelly-mcp wird auf DS923 `10.40.20.180` deployt — `BUILD.md` liegt bereit.)** +- **Mac mini ist n8n-Host, nicht zentraler MCP-Host** (User-Direktive 2026-08-28): MCP-Dienste gehören auf die **DS923** (`nas-bf`, Portainer Endpoint 14) — nicht auf die User-Workstation. Aktuell laufen gitea-mcp + n8n-mcp **noch** auf dem macmini (historisch), gehören **bei Gelegenheit** auf die DS923 umgezogen. - **Tote MCP-Skripte → ins Projekt-Repo, nicht global.** Wenn ein MCP nur für ein Projekt relevant ist (`cannaleo` für terpene, `terpene-pg` für terpene): gehört in `~/dev//mcp.json` + Skript im Projekt-Repo. Globale `~/.claude.json`/`~/.config/opencode/opencode.jsonc` = nur projekt-übergreifende Tools. **Checkliste vor jedem Edit-Vorschlag:** diff --git a/docs/handoff-20260828-mcp-bestand-und-performance.md b/docs/handoff-20260828-mcp-bestand-und-performance.md new file mode 100644 index 0000000..3b1acf9 --- /dev/null +++ b/docs/handoff-20260828-mcp-bestand-und-performance.md @@ -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//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=` + `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//.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` diff --git a/docs/mcp-host-policy.md b/docs/mcp-host-policy.md new file mode 100644 index 0000000..eb7c18d --- /dev/null +++ b/docs/mcp-host-policy.md @@ -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//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). diff --git a/mcp/INVENTORY.md b/mcp/INVENTORY.md index b8db8ed..5176144 100644 --- a/mcp/INVENTORY.md +++ b/mcp/INVENTORY.md @@ -5,22 +5,40 @@ > Wer einen MCP abschaltet, **streicht die Zeile** (oder setzt Status `retired` mit Datum). > > **Richtungsentscheid (User, 2026-07-11):** MCP-Dienste gehören grundsätzlich auf **zentrale -> Serverdienste, nicht auf den Client**. Die aktuell **Client-lokal** laufenden (MikroMCP stdio, -> shelly-mcp `:3030`) sind bewusste **pragmatische Interims** ("Hauptsache es funktioniert") und -> werden **zu einem späteren Zeitpunkt zentral zusammengezogen** (ein Host, Container, FQDN) — Teil +> Serverdienste, nicht auf den Client**. Die aktuell **Client-lokal** laufenden (MikroMCP stdio) +> sind bewusste **pragmatische Interims** ("Hauptsache es funktioniert") und werden **zu einem +> späteren Zeitpunkt zentral zusammengezogen** (ein Host, Container, FQDN) — Teil > der Control-Plane-Konsolidierung (vgl. TP11 / Komodo-Migration). +> +> **Bestand-Aufnahme 2026-08-28 (Portainer-API, alle 4 Endpoints live):** +> Mac mini (`10.40.20.167`, Portainer-Endpoint 20) hat **5 Container**: gitea, gitea-mcp, +> n8n, n8n-redis, n8n-mcp. **Mac mini ist der n8n-Host, NICHT der zentrale MCP-Host** +> (User-Direktive 2026-08-28, Performance-Restriktion: User-Workstation soll nicht zusätzlich +> durch MCP-Clients belastet werden). Der zentrale MCP-Host ist die **DS923** (`nas-bf`, +> Portainer-Endpoint 14) — dort liegen pve-mcp + nautobot-Stack. gitea-mcp + n8n-mcp laufen +> aktuell **noch** auf dem macmini (historisch gewachsen, Container-Definition dort) und +> gehören **bei Gelegenheit** auf die DS923 umgezogen (separater Punkt, **nicht** Teil dieser Session). +> DS923 (Endpoint 14) hat pve-mcp + nautobot-Stack. pve (Endpoint 2) und NAS-FM (Endpoint 21) +> ohne MCPs. Verifikation **alle 4 Endpoints** abgefragt, nicht nur Stichproben. +> +> **Portier-Regel (ab 2026-08-28):** bei jeder MCP-Eintragung/-Stilllegung Container-Existenz +> in **allen 4 Endpoints** verifizieren (`/api/endpoints//docker/containers/json?all=1`), +> nicht in einer Config glauben. Drift-Doku ist wiederholter Befund — siehe Handoff 2026-08-27 D3/D4. ## Aktive / geplante MCPs -> **Netzwerk-Schema ab 2026-08-27:** `10.40..` — altes `10.40.41.x` ist tot. Hosts in dieser Tabelle sind auf das neue Schema zu prüfen. +> **Netzwerk-Schema ab 2026-08-27:** `10.40..` — altes `10.40.41.x` ist tot. +> **Caddyfile (`caddy/Caddyfile`)** ist SSoT für FQDN→Backend-Port (z.B. Z. 75: `n8n-mcp.emsenhuber.net` → `10.40.20.167:3001`). | MCP | Zweck / Geräte | Host | Transport / Endpoint | Image | Config | Status | |---|---|---|---|---|---|---| -| **MikroMCP** (AliKarami) | MikroTik hAP ax² ap1/ap2 (RouterOS 7) — WLAN/CAPsMAN, Router-Cutover | **jetzt: lokal (dieser Mac)** / Ziel: Mac mini `10.40.20.167` Docker | jetzt **stdio** (`npx mikromcp serve`) / Ziel HTTP `:3000` | `ghcr.io/alikarami/mikromcp:latest` (HTTP) bzw. npm `mikromcp` (stdio) | `mcp/mikromcp/` + opencode/Claude-Code user-config | 🟢 **live (stdio), doctor 12/12, in opencode ✓Connected** | -| **shelly-mcp** (eigenbau, FastMCP) | ~26 Shellys Gen3/4 — VLAN1→VLAN30-Migration (TP17) + Verwaltung | **jetzt: lokal (dieser Mac) `:3030`** / Ziel: zentral (Container) | **StreamableHTTP** `http://127.0.0.1:3030/mcp` (+ Status `/`, Health `/health`) | eigenbau `mcp/shelly/server.py` | `mcp/shelly/` ✓ (`.env` PSK) | 🟢 **live (Interim, Client-lokal). Tools: discover/get/set_sta[guarded]/verify/reservation_plan** | -| **pve-mcp** (samik081) | Proxmox VE API (Host `.100`) | DS923 (`10.40.20.10`), macvlan-IP **`10.40.20.173`** | SSE `http://10.40.20.173:3000/sse` | `ghcr.io/samik081/mcp-pve:latest` | — (Stack via Portainer) | 🟢 **live, in opencode eingetragen** | -| **komodo-mcp** (samik081) | Docker-Management **5 Hosts** via Komodo Core (ersetzt Portainer) | DS923 (`10.40.20.10`), macvlan-IP **`10.40.20.176`** | SSE `http://10.40.20.176:3000/sse` | `ghcr.io/samik081/mcp-komodo:latest` | `mcp/komodo/` ✓ (`.env.example` → `.env` nach Phase 1) | 🟡 **geplant — Migration: `mcp/komodo/MIGRATION.md`** | -| ~~portainer-mcp~~ (offiziell) | Docker-Überblick via Portainer CE 2.40.0 | `10.40.20.126:9443` | — | `portainer/portainer-mcp` | `mcp/portainer/` (Token .env ✓) | ⏳ **retired nach Komodo-Migration — Token aufheben bis Phase 4** | +| **MikroMCP** (AliKarami) | MikroTik hAP ax² ap1/ap2 (RouterOS 7) — WLAN/CAPsMAN, Router-Cutover | **jetzt: lokal (dieser Mac)** stdio / Ziel: HTTP-Container auf Mac mini `10.40.20.167` | jetzt **stdio** (`npx mikromcp serve`) / Ziel HTTP `:3000` | `ghcr.io/alikarami/mikromcp:latest` (HTTP) bzw. npm `mikromcp` (stdio) | `mcp/mikromcp/` + opencode/Claude-Code user-config | 🟢 **live (stdio), doctor 12/12, in opencode ✓Connected** | +| **gitea-mcp** (eigenbau) | Gitea REST API (Repo-Verwaltung, Issues, Wiki, …) | **Mac mini** `10.40.20.167` (Portainer-Endpoint 20) — Docker-Bridge-IP `172.17.0.3:3000` — ⚠️ **sollte auf DS923 umziehen** (Performance-Restriktion: macmini ist User-Workstation, kein zentraler MCP-Host) | **StreamableHTTP** `https://gitea-mcp.emsenhuber.net/mcp` (Caddy + CF) bzw. `http://10.40.20.167:3000/mcp` (LAN-direkt) | `docker.io/node:22-alpine` (eigener Build) | Repo `mcp/` hatte **kein** Config-Verzeichnis — Container-Definition lebt auf macmini | 🟢 **live (seit unknown, gefunden 2026-08-28), war aus dem Inventar verschwunden. serverInfo: gitea-mcp 0.0.10. Umzug auf DS923 ausstehend (Performance-Restriktion)** | +| **n8n-mcp** (czlonkowski) | n8n-public-API (Workflows, Credentials, Executions) | **Mac mini** `10.40.20.167` (Portainer-Endpoint 20, **n8n-Host** — n8n `:5678`, n8n-redis `:6379`, n8n-mcp `:3001`) — Docker-Bridge-IP `172.19.0.4` → PublicPort `3001:3000` | **StreamableHTTP** `https://n8n-mcp.emsenhuber.net/mcp` (Caddy + CF) bzw. `http://10.40.20.167:3001/mcp` (LAN-direkt) | `ghcr.io/czlonkowski/n8n-mcp:latest` | keine im Repo (`mcp.json.example` in `blaueblueten-n8n/` ist alt, stdio-Modus) | 🟢 **live (gefunden 2026-08-28, war **nicht** im Inventar). FQDN → 401 ohne Bearer (Auth-Sanierung TP16.3). LAN-direkt :3001 antwortet korrekt mit 401 = Bearer fehlt, Server lebt. Bleibt auf macmini (zusammen mit n8n — n8n-Workflows lösen Tool-Calls aus, lokale Performance-Kopplung gewollt)** | +| **shelly-mcp** (eigenbau, FastMCP) | ~26 Shellys Gen3/4 — VLAN1→VLAN30-Migration (TP17) + Verwaltung | **geplant:** DS923 (`10.40.20.10`), macvlan-IP **`10.40.20.180`** | **StreamableHTTP** `http://10.40.20.180:3030/mcp` (geplant) | eigenbau `bf/shelly-mcp:0.1` (DS923-lokal gebaut aus `mcp/shelly/Dockerfile`) | `mcp/shelly/` ✓ (`Dockerfile` + `BUILD.md` + `requirements.txt`) · Stack gitignored in `docs/nas-vlan20/stacks/shelly-mcp.yml` | 🟡 **staged — Image-Build + Stack-Deploy auf DS923 ausstehend. Vorher-Stand war localhost `127.0.0.1:3030` mit `enabled=false` (Handoff 2026-08-28). Live-Verifikation 23 Shellys auf `10.40.30.180-250` (VLAN30) bestätigt 2026-08-28** | +| **pve-mcp** (samik081) | Proxmox VE API (Host `.100`) | DS923 (`10.40.20.10`), macvlan-IP **`10.40.20.173`** | **StreamableHTTP** `http://10.40.20.173:3000/mcp` (+ Health `/health` → `mcp-pve 0.6.1`) | `ghcr.io/samik081/mcp-pve:latest` | — (Stack via Portainer) | 🟢 **live, in opencode + claude.json eingetragen. Drift-Korrektur 2026-08-28: war im Inventar als „SSE `/sse" gelistet, Realität ist StreamableHTTP** | +| **komodo-mcp** (samik081) | Docker-Management via Komodo Core (ersetzt Portainer) | DS923 (`10.40.20.10`), macvlan-IP **`10.40.20.176`** | SSE `http://10.40.20.176:3000/sse` (geplant) | `ghcr.io/samik081/mcp-komodo:latest` | `mcp/komodo/` ✓ (`.env.example` → `.env` nach Phase 1) | 🟡 **geplant — Migration: `mcp/komodo/MIGRATION.md`** | +| ~~portainer-mcp~~ | nicht im Bestand (keiner der 4 Endpoints hat einen Container mit Namen/Image `portainer-mcp`). **Entscheidung 2026-08-28: NICHT installieren** — Portainer-API direkt aus Scripts/HTTP-Calls ist ausreichend, der MCP-Client bringt keinen Mehrwert gegenüber den existierenden Wrapper-Skripten. Inventar bleibt auf "nicht geplant". | — | — | — | `mcp/portainer/` (API-Token in `.env`) bleibt als **Direkt-API-Wrapper** ohne MCP-Layer | ⛔ **bewusst NICHT** (2026-08-28, User-Direktive) | | ~~omada-mcp~~ (jmtvms v0.15) | TP-Link Omada Controller | — | — | `jmtvms/tplink-omada-mcp:latest` | `mcp/omada/` ✓ | ⛔ **RETIRED 2026-08-27** — Omada-Stack raus, wir fahren auf MikroTik. Container stoppen, Image optional behalten. | | ~~omada-controller~~ | Omada Software Controller | `10.40.20.175:8043` | — | `mbentley/omada-controller:6.2` | `omada/docker-compose.yml` | ⛔ **RETIRED 2026-08-27** | | ~~ALLNET-MCP~~ | ALLNET ALL-SG8316M Core-Switch | — | — | — | — | ⛔ **bewusst NICHT** (Telnet/1-VTY/15-min-Lock auf Core = zu fragil). User-Entscheid: erst beschriebener Weg, sonst MikroTik-Core-Tausch. | @@ -34,11 +52,14 @@ Legende: 🟢 live · 🟡 staged (Config da, nicht deployed) · 🔴 blockiert 4. **FQDN statt IP**, sobald eingerichtet (DNS-Rewrite) → Endpoint-Spalte updaten. 5. **Review-Datum:** mind. halbjährlich diese Tabelle gegen tatsächlich laufende Container prüfen (`docker ps` auf den Hosts). +6. **Pflicht-Verifikation (seit 2026-08-28):** bei jeder Eintragung/-Stilllegung Container-Existenz + in **allen 4 Portainer-Endpoints** prüfen (`pve` Id 2, `nas-bf` Id 14, `macmini` Id 20, `nas-fm` Id 21). + Nicht "im Handoff gelesen" — **live abgefragt**. ## ALLNET — warum kein MCP ALL-SG8316M (Core) nach FW-2.2.1-Flash: **Web-UI tot, nur Telnet-CLI, kein SSH/REST/SNMP-Write, nur 1 VTY-Line, ~15 min Busy-Lock bei Stale-Sessions.** Ein MCP darüber wäre auf dem **Core-Switch** -(an dem alles hängt) fragil und lockout-gefährlich. → Management bleibt bei der bestehenden +(an dem alles hängt) fragil und lockout-gefährdet. → Management bleibt bei der bestehenden Telnet-CLI-Toolchain (`switch/cli/`). **Strategische Option:** Core-Switch (Methusalem, Bj. 2015) gegen einen **Omada-Switch** tauschen → fällt unter denselben Omada-Controller/omada-mcp. Das ist Hardware-Ausgabe + Core-Cutover → **User-Entscheidung** (offen). diff --git a/mcp/portainer/README.md b/mcp/portainer/README.md index 09aeaf7..4eff077 100644 --- a/mcp/portainer/README.md +++ b/mcp/portainer/README.md @@ -2,9 +2,11 @@ Überblick über die Docker-Landschaft via Portainer. Übersicht aller MCPs: [../INVENTORY.md](../INVENTORY.md). -## Ist-Stand (2026-06-09) -- **Portainer-Server läuft** auf `https://10.40.41.126:9443`, **v2.40.0**, managed **5 Endpoints**: - `pve` (lokal), `nas-bf (ds923+)`, `odroid-m1`, `macmini`, `nas-fm (ds415+)`. +## Ist-Stand (2026-08-28) +- **Portainer-Server läuft** auf `https://10.40.20.126:9443` (Docker-LXC auf pve, VLAN20), **v2.40.0**, managed **5 Endpoints**: + `pve` (lokal, Id 2), `nas-bf (ds923+)` (Id 14), `macmini` (Id 20), `nas-fm (ds415+)` (Id 21), `odroid-m1` (nicht mehr im Inventar — falls noch aktiv, neu hinzufügen). +- **API-Token** in `mcp/portainer/.env` (gitignored). +- **Versions-Drift**: Portainer ist 2.40.0, das offizielle `portainer/portainer-mcp`-Image verlangt 2.42.x → **Portainer-Bump zuerst**, dann MCP-Deploy. - **API-Token** vorhanden (admin) → in `.env` (gitignored). - **Blocker:** `portainer-mcp` gibt es nur als **2.41.x / 2.42.x**, jeweils = gleiche Portainer-Minor. **2.40 hat keinen passenden MCP** → Portainer auf `.126` auf **2.42.x** bumpen (sauberster Weg). @@ -14,27 +16,29 @@ Voraussetzung: `uv` installieren (`brew install uv` oder `curl -LsSf https://ast ``` # NACH Portainer-Bump auf 2.42 (oder Versuch 2.41 gegen 2.40): claude mcp add portainer -s user \ - -e PORTAINER_URL=https://10.40.41.126:9443 \ + -e PORTAINER_URL=https://10.40.20.126:9443 \ -e PORTAINER_API_KEY=ptr_... \ -e PORTAINER_TLS_VERIFY=0 \ -- uvx --from "mcp-portainer~=2.42.0" mcp-portainer ``` Read-only erzwingen: über MCP-Config/Profil (siehe portainer-mcp `docs/configuration.md` → „read-only capabilities"). -## Weg B — Docker HTTP auf Mac mini (.167), zentral hinter FQDN (wie ha-mcp/MikroMCP-Ziel) +## Weg B — Container auf DS923 (`.181:3000/mcp`), Stack-YML gitignored +**Voraussetzung:** Portainer-Bump 2.40.0 → 2.42.x (offizielles `portainer/portainer-mcp`-Image verlangt gleiche Minor). ``` -docker run -d --name portainer-mcp -p 17717:17717 \ - -e PORTAINER_URL=https://10.40.41.126:9443 \ - -e PORTAINER_API_KEY=ptr_... \ - -e PORTAINER_TLS_VERIFY=0 \ - -e PORTAINER_MCP_AUTH_TOKEN= \ - -e PORTAINER_MCP_ALLOWED_HOSTS=:17717 \ - portainer/portainer-mcp:2.42 -# Client: -claude mcp add portainer --transport http http://:17717/mcp \ - --header "Authorization: Bearer " \ - --header "X-Portainer-API-Key: ptr_..." +# Stack deployen (gitignored): docs/nas-vlan20/stacks/portainer-mcp.yml +# Container-IP = 10.40.20.181 im vlan20-Netz (analog pve-mcp .173, komodo-mcp .176, shelly-mcp .180) ``` +**Client:** +```jsonc +// ~/.claude.json + ~/.config/opencode/opencode.jsonc: +"portainer-mcp": { + "type": "remote", + "url": "http://10.40.20.181:3000/mcp", + "headers": {} +} +``` +Aktuell `enabled: false` bis Deploy durch ist. ## Versions-Policy (Doku) Match MCP-Minor = Portainer-Minor. Tabelle: nur 2.41.x↔2.41.x, 2.42.x↔2.42.x. Kein 2.40.