Skip to main content
Glama
README.md
# mcp-server-unifi

Ein MCP-Server (Model Context Protocol), der einer KI Lese- und Schreibzugriff
auf einen UniFi Network Controller (UniFi OS, z.B. UCG Ultra/UDM/UDM-Pro)
gibt. Gedacht fuer Heimnetz-/kleine MFH-Netzwerke mit VLANs, WLANs,
Port-Profilen, Zone-Based-Firewall und WireGuard-VPN.

## Funktionen

| Tool | Beschreibung |
|---|---|
| `discover_schema` | Liest die vom Controller selbst bereitgestellte OpenAPI-Spezifikation aus. Erstes Tool bei unerwarteten Fehlern. |
| `list_sites` | Listet alle Sites, die dieser Controller verwaltet (siehe "Mehrere Sites" unten). |
| `query_config` | Liest Netzwerke, WLANs, Port-Profile, Firewall-Gruppen/-Regeln oder Clients aus (WLAN-Passphrasen standardmaessig maskiert). |
| `find_client` | Sucht bekannte Clients nach MAC, IP, Name oder Netzwerk; liefert Online-Status und verbundenen AP. |
| `get_device` | Details zu einem Geraet (AP/Switch/Gateway): Adoptionsstatus, Firmware, Uptime, Port-Belegung inkl. verbundener Clients. |
| `apply_change` | Aendert ein bestehendes Konfigurationsobjekt (liest zuerst den aktuellen Stand, ueberlagert nur die angegebenen Felder). |
| `get_events` | Controller-Events/Alarme der letzten X Minuten, optional textgefiltert. |
| `run_diagnostic` | Speedtest oder Ping ueber den Controller anstossen (Start + Ergebnis-Polling gekapselt). |
| `manage_backup` | Controller-Backups erstellen, auflisten oder lokal herunterladen. |

Alle Tools geben strukturiertes JSON zurueck; Fehler (Verbindung, Auth,
Controller-Ablehnung) kommen als `{"error": "...", "hint": "..."}` zurueck,
nie als rohe Exception oder HTML.

## Installation

```bash
pip install --user /home/jms/develop/mcp_server_unifi
# oder
./pip_install.sh
```

Abhaengigkeiten: `mcp`, `pydantic`, `httpx`.

## Konfiguration

| CLI-Argument | Env-Variable | Pflicht | Default |
|---|---|---|---|
| `--api-key` | `UNIFI_API_KEY` | ja | - |
| `--controller-ip` | `UNIFI_CONTROLLER_IP` | ja | - |
| `--site` | `UNIFI_SITE` | nein | automatisch aufgeloest, siehe "Mehrere Sites" unten |
| `--verify-ssl` / `--no-verify-ssl` | `UNIFI_VERIFY_SSL` | nein | aus (UniFi-Controller nutzen i.d.R. selbstsignierte Zertifikate) |

CLI-Argumente haben Vorrang vor den Umgebungsvariablen. Der API-Key wird nur
maskiert geloggt (`API-Key: *******XXXX (letzte 4 Zeichen)`), niemals im
Klartext.

### Mehrere Sites

Verwaltet der Controller nur eine Site, ist keine Konfiguration noetig - der
Server loest sie automatisch auf. Verwaltet er **mehrere** Sites (z.B. Zuhause
+ Ferienhaus), gilt:

- Ist `--site`/`UNIFI_SITE` gesetzt, wird immer diese Site verwendet.
- Ist nichts gesetzt, geben alle Tools bei Mehrdeutigkeit einen Fehler mit den
  verfuegbaren Sites zurueck (`{"error": "Mehrere Sites vorhanden, keine
  ausgewaehlt", "hint": "... 'default' (Zuhause), 'site2' (Ferienhaus) ..."}`)
  statt stillschweigend eine davon zu waehlen. Die KI kann dann `list_sites()`
  aufrufen und die gewuenschte Site gezielt als `site=...`-Parameter an das
  jeweilige Tool uebergeben.

Die mitgelieferte `mcp.json` hat `--site` bereits als Platzhalter
(`"hier-site-name-eintragen-falls-mehrere-sites"`) drin, analog zum
API-Key-Platzhalter:

- **Mehrere Sites:** Platzhalter durch den `name`-Wert aus `list_sites()`
  ersetzen (z.B. `"default"` oder `"site2"` - nicht den `desc`-Anzeigenamen).
- **Nur eine Site:** die beiden `--site`-Argumente (Flag + Platzhalterwert)
  aus `args` entfernen - der Platzhaltertext ist kein gueltiger Site-Name und
  wuerde sonst mit einem `Controller-Fehler` fehlschlagen. Ohne `--site` loest
  der Server die einzige vorhandene Site automatisch auf.

Der API-Key wird als [UniFi-API-Key](https://help.ui.com) im Controller unter
Einstellungen -> Control Plane -> Integrations erzeugt (kein Login-Flow, kein
Session-Cookie noetig).

## Starten

```bash
python -m mcp_server_unifi --api-key sk-... --controller-ip 192.168.1.1
```

## Konfiguration in Claude Code / LM Studio

Siehe `mcp.json` in diesem Verzeichnis (API-Key dort ueber die `env`-Sektion
setzen, nicht im Klartext in `args`):

```json
{
  "mcpServers": {
    "unifi": {
      "command": "python",
      "args": ["-m", "mcp_server_unifi", "--controller-ip", "192.168.1.1"],
      "env": { "UNIFI_API_KEY": "sk-..." }
    }
  }
}
```

Danach den MCP-Client neu starten.

## Sicherheitshinweise

- Ausschliesslich API-Key-Authentifizierung, keine Session-/Cookie-basierte
  Anmeldung.
- `apply_change` aendert nur bestehende Objekte (liest zuerst den aktuellen
  Zustand und ueberlagert ihn mit den angegebenen Feldern) - es werden keine
  Objekte angelegt oder geloescht.
- Es gibt bewusst keine Tools zum Loeschen von Netzwerken oder Zuruecksetzen
  von Geraeten.

## Grenzen / unverifizierte Endpunkte

Gegen einen echten Controller (UDR Ultra, UniFi Network App 10.5.67)
verifiziert: `discover_schema`, `list_sites`, `query_config` (inkl.
WLAN-Passphrasen-Maskierung), `find_client`, `get_device`. Diese funktionieren
zuverlaessig ueber die klassische API (`/proxy/network/api/s/{site}/...`).

**Bestaetigt eingeschraenkt:**

- `get_events`: Auf UniFi Network App 10.5.67 liefert `stat/event` (und
  `stat/alarm`, `list/event`) durchgehend 404. Die neue offizielle v1
  Integration-API (`/proxy/network/integration/v1/...`, ebenfalls von diesem
  Controller bereitgestellt) hat **keine** Events/Alarme-Ressource - im
  gesamten OpenAPI-Spec taucht "event"/"alarm" nicht auf. Ein
  GraphQL-Endpunkt (`/proxy/network/api/graphql`) existiert, antwortet aber
  mit 401 auf API-Key-Auth (verlangt vermutlich Session-Cookie-Auth) und wird
  bewusst nicht unterstuetzt (siehe CLAUDE.md: nur API-Key). `get_events`
  erkennt den 404-Fall und gibt eine erklaerende Fehlermeldung statt eines
  nichtssagenden Controller-Fehlers zurueck. Auf aelteren
  Controller-Generationen kann der Endpunkt durchaus funktionieren.

**Weiterhin unverifiziert** (keine Seiteneffekte auf einem echten Controller
ausgeloest, um Bandbreite/Backup-Ressourcen nicht unnoetig zu beanspruchen):

- `run_diagnostic(test="ping")`: Endpunkt/Ergebnisformat unverifiziert;
  `test="speedtest"` (`cmd/devmgr` + Polling auf `/stat/device`) ist nach
  Code-Review der wahrscheinlichere korrekte Fall, aber nicht live getestet.
- `manage_backup`: Backup-Endpunkte (`cmd/backup`, `list-backups`,
  `/dl/backup/...`) sind die am wenigsten dokumentierten der klassischen
  UniFi-API und nicht live getestet.
- Zone-Based-Firewall (`v2/api/site/{site}/firewall-*`) wird bewusst nicht
  fest verdrahtet - `discover_schema` oder `run_overpass_query`-artiges
  Vorgehen (Rohdaten lesen, nicht Feldnamen raten) nutzen.

## Struktur

```
mcp_server_unifi/                ← Projekt-Root
├── pyproject.toml
├── README.md
├── CLAUDE.md
├── mcp.json
├── tests/
│   ├── test_server.py           ← Unit-Tests, gemockter httpx-Transport
│   └── integration/             ← manuelle Tests gegen echten Controller
└── mcp_server_unifi/            ← Python-Package
    ├── __init__.py              ← Einstiegspunkt & Argument-Parsing
    ├── __main__.py              ← Ermoeglicht python -m mcp_server_unifi
    ├── server.py                ← FastMCP-Instanz, alle Tools
    └── unifi_client.py          ← HTTP-Client, Config, Fehlerbehandlung
```

## Tests

```bash
pip install --user -e ".[test]"
pytest tests/
```

## Development

Publish to PyPI:
```bash
./push_pypi.sh
```

Push to GitHub:
```bash
./push_github.sh
```

## License

MIT — Martin Schlatter