# 08 · TPLink Omada MCP Server **Erstellt:** 2026-03-18 **Aktualisiert:** 2026-03-18 — Credentials rotiert (CLIENT_ID/SECRET), Service auf NodePort 31777 umgestellt **Namespace:** `omada-mcp` **Image:** `jmtvms/tplink-omada-mcp:latest` **Status:** Running · Pod `omada-mcp-dfdbfbcf8-x6t5k` · NodePort `31777` → Container `3000` --- ## Übersicht Der [tplink-omada-mcp](https://github.com/jmtvms/tplink-omada-mcp) Server stellt eine MCP-Schnittstelle (Model Context Protocol) für den TPLink Omada Controller bereit. Claude und andere KI-Assistenten können darüber das Netzwerk verwalten (Clients, SSIDs, VLANs, Geräte usw.). Der Server läuft im HTTP-Modus auf Port **3000** (`MCP_SERVER_USE_HTTP=true`). --- ## Dateien ``` homelab/k8s/omada-mcp/ ├── kustomization.yaml # Kustomize-Einstiegspunkt ├── namespace.yaml # Namespace omada-mcp ├── secret.yaml # Credentials (Platzhalter – vor Anwenden befüllen!) ├── deployment.yaml # Deployment (1 Replica) └── service.yaml # NodePort Service Port 3000 → NodePort 31777 ``` --- ## Credentials (Secret) Das Secret `omada-mcp-credentials` enthält folgende Schlüssel: | Key | Beschreibung | |-----|-------------| | `OMADA_BASE_URL` | URL des Omada Controllers, z. B. `https://192.168.11.29` | | `OMADA_CLIENT_ID` | OAuth Client-ID aus dem Omada Controller | | `OMADA_CLIENT_SECRET` | OAuth Client-Secret | | `OMADA_OMADAC_ID` | Omada-Controller-ID (zu finden in den Controller-Einstellungen) | | `OMADA_STRICT_SSL` | `false` – deaktiviert SSL-Verifikation (Self-signed Certs) | | `MCP_SERVER_USE_HTTP` | `true` – aktiviert HTTP-Modus | | `MCP_HTTP_BIND_ADDR` | `0.0.0.0` – lauscht auf allen Interfaces | ### Secret anlegen (empfohlen – kein base64 nötig) ```bash kubectl create secret generic omada-mcp-credentials \ --namespace omada-mcp \ --from-literal=OMADA_BASE_URL='https://192.168.11.29' \ --from-literal=OMADA_CLIENT_ID='dein-client-id' \ --from-literal=OMADA_CLIENT_SECRET='dein-client-secret' \ --from-literal=OMADA_OMADAC_ID='dein-omadac-id' \ --from-literal=OMADA_STRICT_SSL='false' \ --from-literal=MCP_SERVER_USE_HTTP='true' \ --from-literal=MCP_HTTP_BIND_ADDR='0.0.0.0' ``` Danach `secret.yaml` **nicht** anwenden (das Secret existiert bereits). ### Alternativ: secret.yaml befüllen ```bash # Werte base64-kodieren echo -n 'https://omada.example.com' | base64 echo -n 'dein-client-id' | base64 echo -n 'dein-client-secret' | base64 echo -n 'dein-omadac-id' | base64 ``` Die Ausgaben in `secret.yaml` bei den `REPLACE_WITH_*`-Platzhaltern eintragen. --- ## Deployment ### Voraussetzungen - Namespace existiert (wird durch `namespace.yaml` erstellt) - Secret ist befüllt (siehe oben) ### Anwenden mit Kustomize ```bash # Dry-run – Manifeste prüfen kubectl apply -k homelab/k8s/omada-mcp/ --dry-run=client # Anwenden kubectl apply -k homelab/k8s/omada-mcp/ ``` ### Einzelne Manifeste anwenden ```bash kubectl apply -f homelab/k8s/omada-mcp/namespace.yaml kubectl apply -f homelab/k8s/omada-mcp/secret.yaml # nur wenn nicht per kubectl create kubectl apply -f homelab/k8s/omada-mcp/deployment.yaml kubectl apply -f homelab/k8s/omada-mcp/service.yaml ``` --- ## Umgebungsvariablen im Container | Variable | Wert | Quelle | |----------|------|--------| | `OMADA_BASE_URL` | `https://192.168.11.29` | Secret `omada-mcp-credentials` | | `OMADA_CLIENT_ID` | aus Secret | `omada-mcp-credentials` | | `OMADA_CLIENT_SECRET` | aus Secret | `omada-mcp-credentials` | | `OMADA_OMADAC_ID` | aus Secret | `omada-mcp-credentials` | | `OMADA_STRICT_SSL` | `false` | `omada-mcp-credentials` | | `MCP_SERVER_USE_HTTP` | `true` | `omada-mcp-credentials` | | `MCP_HTTP_BIND_ADDR` | `0.0.0.0` | `omada-mcp-credentials` | --- ## Verifikation ```bash # Pod-Status prüfen kubectl get pods -n omada-mcp # Logs anzeigen kubectl logs -n omada-mcp deployment/omada-mcp # Service prüfen kubectl get svc -n omada-mcp # Direkter Test via Port-Forward kubectl port-forward -n omada-mcp svc/omada-mcp 3000:3000 curl http://localhost:3000/ ``` ### Erwartete Ausgabe (Pod läuft) ``` NAME READY STATUS RESTARTS AGE omada-mcp-xxxxxxxxx-xxxxx 1/1 Running 0 1m ``` --- ## MCP-Endpunkt in Claude Code einbinden Sobald der Pod läuft, kann der MCP-Server per Port-Forward oder Ingress angebunden werden. ### Option A: NodePort (dauerhaft, direkt erreichbar) Der Service ist auf NodePort **31777** auf allen Nodes erreichbar: | Node | URL | |------|-----| | rnk-cp01 | `http://192.168.11.170:31777` | | rnk-wrk01 | `http://192.168.11.171:31777` | | rnk-wrk02 | `http://192.168.11.172:31777` | Claude Code `.mcp.json`: ```json { "mcpServers": { "omada": { "type": "http", "url": "http://192.168.11.171:31777/mcp" } } } ``` ### Option B: Port-Forward (lokal/temporär) ```bash kubectl port-forward -n omada-mcp svc/omada-mcp 3000:3000 & ``` ### Option B: Ingress (dauerhaft, mit Traefik) Ingress-Manifest erstellen (optional): ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: omada-mcp namespace: omada-mcp annotations: traefik.ingress.kubernetes.io/router.entrypoints: websecure cert-manager.io/cluster-issuer: letsencrypt-production spec: ingressClassName: traefik tls: - hosts: - omada-mcp.homelab.local secretName: omada-mcp-tls rules: - host: omada-mcp.homelab.local http: paths: - path: / pathType: Prefix backend: service: name: omada-mcp port: number: 3000 ``` --- ## Ressourcen - CPU: 50m Request / 200m Limit - Memory: 64Mi Request / 256Mi Limit --- ## Troubleshooting ```bash # Pod-Events anzeigen kubectl describe pod -n omada-mcp -l app.kubernetes.io/name=omada-mcp # Secret-Inhalte prüfen (base64-dekodiert) kubectl get secret omada-mcp-credentials -n omada-mcp -o jsonpath='{.data.OMADA_BASE_URL}' | base64 -d # Pod neu starten kubectl rollout restart deployment/omada-mcp -n omada-mcp ``` ### Häufige Fehler | Fehler | Ursache | Lösung | |--------|---------|--------| | `ImagePullBackOff` | Image nicht erreichbar | Internet-Zugang der Nodes prüfen | | `CrashLoopBackOff` | Falsche Credentials | Logs prüfen, Secret aktualisieren | | `Pending` | Ressourcen fehlen | `kubectl describe pod` → Events | | SSL-Fehler | Self-signed Cert | `OMADA_STRICT_SSL=false` bestätigen | --- ## Deployment-Geschichte | Datum | Aktion | Ergebnis | |-------|--------|---------| | 2026-03-18 | Namespace, Secret, Deployment, Service erstellt | Pod in CrashLoopBackOff wegen falscher Probe-Pfade | | 2026-03-18 | Liveness/Readiness Probe auf `/healthz` geändert | Pod `1/1 Running`, `/healthz` → `{"status":"ok"}` | | 2026-03-18 | MCP Initialize-Request getestet | Server antwortet korrekt, Version 0.1.0 | | 2026-03-18 | Service von ClusterIP auf NodePort 31777 umgestellt | Extern erreichbar auf allen Nodes | | 2026-03-18 | Credentials rotiert (CLIENT_ID + CLIENT_SECRET) | Pod-Rollout erfolgreich, `1/1 Running` | ### Bekannte Besonderheit: Probe-Pfad Der MCP-Server gibt auf `GET /` HTTP 404 zurück (MCP-Protokoll beantwortet nur POST mit korrekten Headers). Der Health-Endpoint ist `/healthz` → `{"status":"ok"}`. ### MCP-Endpunkt aufrufen ```bash curl -s -X POST http://192.168.11.171:31777/mcp \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' ``` ## Nächste Schritte - [x] Credentials im Secret befüllen und anwenden - [x] Pod-Status und Logs nach Deployment prüfen - [ ] MCP-Server in Claude Code `.mcp.json` eintragen - [ ] Optional: Ingress für dauerhaften Zugriff erstellen - [ ] Optional: Horizontal Pod Autoscaler bei Bedarf hinzufügen