Initial: Add all homelab manifests
This commit is contained in:
272
docs/08-omada-mcp.md
Normal file
272
docs/08-omada-mcp.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user