Teilprojekte + MikroMCP + WLAN-Vorarbeit: Steuerungsdok, Research, MCP-Setup, CAPsMAN-Asbuilt, AP-Trunk-Guides + Port-Map
- docs/teilprojekte.md: kanonisches Projektsteuerungsdokument (TP1–TP13 + TP-R) - docs/research/: RouterOS-Capabilities-Sweep (TP-R) + VPN-Vergleich (TP10) - mcp/: MikroMCP + Portainer-MCP Docker-Compose, routers.yaml, READMEs, .gitignore - routeros/: ap1-manager.rsc, ap2-cap.rsc, CAPsMAN-Flat-Asbuilt, WLAN-Handover - switch/cli/03-trunk-ap-ports.cli + switch/ui-guides/01-trunk-ap-ports.md: AP-Trunk-Konfiguration - docs/allnet-ge8-trunk-klickanleitung.md: GE8 Hybrid→Trunk Schritt-für-Schritt - inventory/bf-core-ports: gi1/gi2 als TRUNK zu ap1/ap2 eingetragen (gesetzt 2026-06-09) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
5
mcp/.gitignore
vendored
Normal file
5
mcp/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# Secrets & runtime — niemals committen
|
||||
**/.env
|
||||
**/config/identities.yaml
|
||||
**/config/audit.ndjson
|
||||
# routers.yaml ist OK im Git (keine Secrets, nur Hosts); identities.yaml NICHT (Token-Hashes).
|
||||
32
mcp/INVENTORY.md
Normal file
32
mcp/INVENTORY.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# MCP-Inventar — BF (Single Source of Truth gegen "1000 MCP-Leichen")
|
||||
|
||||
> **Regel:** Jeder MCP-Server, der für BF läuft, steht **hier** — sonst existiert er nicht.
|
||||
> Kein lokaler Ad-hoc-`npx`-Wildwuchs auf Laptops. Zentral = Docker auf einem Host, FQDN-adressiert.
|
||||
> Wer einen MCP abschaltet, **streicht die Zeile** (oder setzt Status `retired` mit Datum).
|
||||
|
||||
## Aktive / geplante MCPs
|
||||
|
||||
| MCP | Zweck / Geräte | Host | Transport / Endpoint | Image | Config | Status |
|
||||
|---|---|---|---|---|---|---|
|
||||
| **MikroMCP** (AliKarami) | MikroTik hAP ax² ap1/ap2 (RouterOS 7) — WLAN/CAPsMAN, später Router-Cutover | **jetzt: lokal (dieser Mac)** / Ziel: Mac mini `.167` Docker | jetzt **stdio** (`npx mikromcp serve`) / Ziel HTTP `:3000` | `ghcr.io/alikarami/mikromcp:latest` (HTTP) bzw. npm `mikromcp` (stdio) | `mcp/mikromcp/` + Claude-Code user-config | 🟢 **live (stdio), doctor 12/12, in Claude Code ✓Connected** |
|
||||
| **omada-mcp** (realtydev) | TP-Link Omada Controller → ES208G (+ künftige Omada-Geräte) | tbd (Controller = pve-LXC `10.40.41.126`) | HTTP (stream) | `jmtvms/tplink-omada-mcp` (Fork prüfen) | `mcp/omada/` (tbd) | 🔴 wartet auf Controller+Adoption |
|
||||
| **portainer-mcp** (offiziell) | Docker-Überblick **5 Hosts** (pve/ds923/odroid/macmini/ds415) via Portainer | Portainer-Server **läuft** auf `.126:9443` **v2.40.0** | stdio (uvx) o. Docker-HTTP, **read-only** | `portainer/portainer-mcp` / `mcp-portainer` | `mcp/portainer/` (Token in .env ✓) | 🟠 **blockiert: Portainer 2.40 hat keinen passenden MCP (nur 2.41/2.42)** → Portainer bumpen; + lokal kein uvx/docker |
|
||||
| ~~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 Omada-Core-Tausch. |
|
||||
|
||||
Legende: 🟢 live · 🟡 staged (Config da, nicht deployed) · 🔴 blockiert · ⛔ bewusst nicht gemacht
|
||||
|
||||
## Anti-Sprawl-Regeln
|
||||
1. **Zentral statt lokal:** MCPs laufen als Container auf einem festen Host, nicht als `npx` pro Laptop.
|
||||
2. **Eine Zeile pro MCP** in dieser Tabelle. Kein Eintrag → abschalten.
|
||||
3. **Config im Git** (`mcp/<name>/`), Secrets nur in `.env` (gitignored).
|
||||
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).
|
||||
|
||||
## 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
|
||||
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).
|
||||
49
mcp/README.md
Normal file
49
mcp/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# MCP-Control-Plane — BF
|
||||
|
||||
Zentrale, versionierte MCP-Server für BF. Übersicht aller MCPs: **[INVENTORY.md](INVENTORY.md)**.
|
||||
Ziel: ein nachvollziehbarer Satz MCPs (gegen "1000 MCP-Leichen"), zentral als Container, FQDN-adressiert.
|
||||
|
||||
## MikroMCP (MikroTik ap1/ap2)
|
||||
|
||||
REST-primär + guarded SSH; RouterOS-7-nativ; Safety: dry-run, `plan_changes`/`apply_plan`/`rollback_change`,
|
||||
Confirmation-Tokens, RBAC, Audit-Log. Image: `ghcr.io/alikarami/mikromcp:latest`.
|
||||
|
||||
### Zwei Betriebsarten
|
||||
- **A — lokal stdio (schnell, kein Netz-Exposure):** läuft dort wo Claude läuft, RBAC = built-in superadmin,
|
||||
keine Tokens/Secret nötig. Gut für sofortige WLAN-Arbeit. Trotzdem in INVENTORY eintragen.
|
||||
- **B — Docker HTTP auf Mac mini (zentral, Ziel):** `docker-compose.yml`. Erfordert Confirmation-Secret +
|
||||
Bearer-Token-Identität. Endpoint später hinter NPM (FQDN+Auth).
|
||||
|
||||
### RouterOS-User (beide APs) — NICHT admin verwenden
|
||||
```
|
||||
/user group add name=mcp policy=read,write,api,rest-api,test,ssh,sniff,!ftp,!policy,!password
|
||||
/user add name=mcp-api group=mcp password=<stark>
|
||||
```
|
||||
(`ftp` nur falls `upload_file` gebraucht wird; `sniff` nur für `torch`. SSH-Dienst muss laufen für
|
||||
ping/traceroute/torch/run_command — REST blockt diese Befehle generell.)
|
||||
|
||||
### B deployen (Mac mini 10.40.41.167)
|
||||
```
|
||||
openssl rand -hex 32 # -> MIKROMCP_CONFIRMATION_SECRET
|
||||
# identities.yaml aus identities.example.yaml bauen (Token: openssl rand -hex 32, dann bcrypt-Hash):
|
||||
node -e "const b=require('bcryptjs');b.hash(process.argv[1],12).then(console.log)" <raw-token>
|
||||
|
||||
scp -r mcp/mikromcp 10.40.41.167:/opt/docker/mikromcp
|
||||
ssh 10.40.41.167 'cd /opt/docker/mikromcp && cp .env.example .env' # .env + config/identities.yaml füllen
|
||||
ssh 10.40.41.167 'cd /opt/docker/mikromcp && docker compose up -d && docker compose logs --tail=50'
|
||||
```
|
||||
Smoke-Test: `curl -s http://10.40.41.167:3000/health` bzw. MCP-Initialize mit Bearer-Token.
|
||||
|
||||
### In Claude Code einhängen
|
||||
- **A (stdio):** `claude mcp add mikromcp -- npx -y mikromcp` + ENV (`MIKROMCP_CONFIG_PATH`, `ROUTER_AP1_USER/PASS`, …).
|
||||
- **B (http):** `claude mcp add --transport http mikromcp http://10.40.41.167:3000` (Header `Authorization: Bearer <raw-token>`).
|
||||
Nach FQDN: URL auf `https://<fqdn>` umstellen.
|
||||
|
||||
## omada-mcp (TP-Link ES208G)
|
||||
Wartet auf: Omada-Controller (≥6.1.x, `../../omada/docker-compose.yml`, Ziel pve-LXC 10.40.41.126) **up** +
|
||||
ES208G **adoptiert**. Env: `OMADA_BASE_URL`, `OMADA_CLIENT_ID/SECRET`, `OMADA_OMADAC_ID`, `OMADA_SITE_ID`,
|
||||
`OMADA_STRICT_SSL=false` (self-signed). Tools: Switch-Ports/VLAN/Firewall CRUD. Details: INVENTORY.md.
|
||||
|
||||
## Sicherheit (wichtig)
|
||||
Ein MikroMCP/omada-mcp-HTTP-Endpoint kann Infrastruktur administrieren. Daher: nur auf LAN-IP binden,
|
||||
dedizierte Nicht-admin-User, Confirmation-Secret gesetzt, Audit-Log an, später NPM+Auth+TLS+FQDN davor.
|
||||
11
mcp/mikromcp/.env.example
Normal file
11
mcp/mikromcp/.env.example
Normal file
@@ -0,0 +1,11 @@
|
||||
# MikroMCP BF — Secrets. Kopiere zu .env (gitignored), fülle Werte.
|
||||
#
|
||||
# Confirmation-Secret (Pflicht im HTTP-Modus): openssl rand -hex 32
|
||||
MIKROMCP_CONFIRMATION_SECRET=
|
||||
|
||||
# Dedizierter RouterOS-User (NICHT admin) — anlegen lt. README.md §RouterOS-User.
|
||||
# Policies nötig: read,write,api,rest-api,test,ssh,sniff (ftp nur für upload_file).
|
||||
ROUTER_AP1_USER=mcp-api
|
||||
ROUTER_AP1_PASS=
|
||||
ROUTER_AP2_USER=mcp-api
|
||||
ROUTER_AP2_PASS=
|
||||
30
mcp/mikromcp/config/routers.yaml
Normal file
30
mcp/mikromcp/config/routers.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
# MikroMCP Router-Registry — BF hAP ax²
|
||||
# Credentials NICHT hier, sondern via ENV (envPrefix -> ROUTER_<PREFIX>_USER/PASS in .env).
|
||||
# APs sprechen REST über PLAIN HTTP auf Port 80 (verifiziert: curl ...:80/rest funktioniert).
|
||||
# -> tls.enabled: false (rest-client.ts: scheme = tls.enabled ? https : http)
|
||||
# Sobald www-ssl auf den APs aktiv ist, auf port 443 + tls.enabled:true + fingerprint umstellen.
|
||||
|
||||
routers:
|
||||
ap1:
|
||||
host: "10.40.41.1"
|
||||
port: 80
|
||||
tls:
|
||||
enabled: false
|
||||
rejectUnauthorized: true # Pflichtfeld lt. Schema; ohne Wirkung bei enabled:false
|
||||
credentials:
|
||||
source: "env"
|
||||
envPrefix: "ROUTER_AP1" # -> ROUTER_AP1_USER, ROUTER_AP1_PASS
|
||||
tags: ["bf", "wifi", "capsman-manager", "future-router"]
|
||||
rosVersion: "7.23"
|
||||
|
||||
ap2:
|
||||
host: "10.40.41.2"
|
||||
port: 80
|
||||
tls:
|
||||
enabled: false
|
||||
rejectUnauthorized: true # Pflichtfeld lt. Schema; ohne Wirkung bei enabled:false
|
||||
credentials:
|
||||
source: "env"
|
||||
envPrefix: "ROUTER_AP2" # -> ROUTER_AP2_USER, ROUTER_AP2_PASS
|
||||
tags: ["bf", "wifi", "cap"]
|
||||
rosVersion: "7.23"
|
||||
35
mcp/mikromcp/docker-compose.yml
Normal file
35
mcp/mikromcp/docker-compose.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
# MikroMCP — BF, Docker auf Mac mini (10.40.41.167)
|
||||
# Prebuilt-Image (kein Source-Build nötig). HTTP/streamable für Remote-Clients.
|
||||
#
|
||||
# SECURITY: Dieser Endpoint kann RouterOS administrieren. Er ist NUR durch Bearer-Token
|
||||
# (identities.yaml, bcrypt) + Confirmation-Secret geschützt. Daher:
|
||||
# - ports auf das Mgmt-/LAN-Interface binden, NICHT 0.0.0.0 ins Internet,
|
||||
# - später hinter NPM (FQDN + Auth/TLS) hängen,
|
||||
# - dedizierter RouterOS-User 'mcp-api' (NICHT admin) — siehe ../README.md.
|
||||
#
|
||||
# Deploy (auf dem mini):
|
||||
# scp -r mcp/mikromcp 10.40.41.167:/opt/docker/mikromcp
|
||||
# ssh 10.40.41.167 'cd /opt/docker/mikromcp && cp .env.example .env && $EDITOR .env'
|
||||
# ssh 10.40.41.167 'cd /opt/docker/mikromcp && docker compose up -d && docker compose logs -f'
|
||||
|
||||
services:
|
||||
mikromcp:
|
||||
image: ghcr.io/alikarami/mikromcp:latest
|
||||
container_name: mikromcp
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MIKROMCP_TRANSPORT: http
|
||||
MIKROMCP_PORT: "3000"
|
||||
MIKROMCP_BIND_HOST: "0.0.0.0" # im Container; Exposure via ports-Mapping unten begrenzen
|
||||
MIKROMCP_CONFIG_PATH: /app/config/routers.yaml
|
||||
MIKROMCP_IDENTITIES_PATH: /app/config/identities.yaml
|
||||
MIKROMCP_CONFIRMATION_SECRET: ${MIKROMCP_CONFIRMATION_SECRET}
|
||||
MIKROMCP_AUDIT_LOG_PATH: /app/config/audit.ndjson
|
||||
MIKROMCP_HTTP_RATE_LIMIT_RPM: "60"
|
||||
MIKROMCP_LOG_LEVEL: info
|
||||
env_file:
|
||||
- .env # ROUTER_AP1_USER/PASS, ROUTER_AP2_USER/PASS, secret
|
||||
volumes:
|
||||
- ./config:/app/config # routers.yaml + identities.yaml + audit.ndjson
|
||||
ports:
|
||||
- "10.40.41.167:3000:3000" # nur auf LAN-IP des mini binden (nicht überall)
|
||||
41
mcp/portainer/README.md
Normal file
41
mcp/portainer/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# portainer-mcp (offiziell) — BF
|
||||
|
||||
Ü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+)`.
|
||||
- **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).
|
||||
|
||||
## Weg A — lokal stdio via uvx (least-invasive, kein Portainer-Touch nötig zum Probieren)
|
||||
Voraussetzung: `uv` installieren (`brew install uv` oder `curl -LsSf https://astral.sh/uv/install.sh | sh`).
|
||||
```
|
||||
# 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_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)
|
||||
```
|
||||
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=<gate-secret> \
|
||||
-e PORTAINER_MCP_ALLOWED_HOSTS=<fqdn-or-167-ip>:17717 \
|
||||
portainer/portainer-mcp:2.42
|
||||
# Client:
|
||||
claude mcp add portainer --transport http http://<mini>:17717/mcp \
|
||||
--header "Authorization: Bearer <gate-secret>" \
|
||||
--header "X-Portainer-API-Key: ptr_..."
|
||||
```
|
||||
|
||||
## 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.
|
||||
TLS: self-signed → `PORTAINER_TLS_VERIFY=0`.
|
||||
Reference in New Issue
Block a user