Skip to main content
Glama
README.md
# fritzbox-mcp

MCP-Server (stdio) für die AVM FRITZ!Box. Er spricht TR-064 über `fritzconnection` und liefert einem MCP-Client wie Claude Code Mesh, WLAN, Geräte, Protokoll, DSL, Anrufliste und Anrufbeantworter als lesbaren deutschen Text.

Standardmäßig ist er rein lesend. Schreibende Aktionen (z. B. Gast-WLAN, WLAN-Kanal, Neustart) gibt es nur, wenn du sie per Umgebungsvariable einschaltest, und jede läuft in zwei Schritten: planen, dann mit Token ausführen.

## Was er kann

| Werkzeug | Liefert |
|---|---|
| `zugang_pruefen` | Modell, FRITZ!OS und Rechte des Box-Benutzers |
| `mesh_uebersicht` | Alle Mesh-Zugangspunkte: Rolle, Uplink-Raten, Kanal/Bandbreite/Auslastung je Funkteil, Zahl der Clients |
| `geraet` | Wo hängt ein Gerät? Suche nach Name, IP oder MAC: Zugangspunkt, Band, Signal, Raten |
| `wlan_status` | WLAN-Funkteile (2,4 GHz, 5 GHz, Gast): an/aus, SSID, Kanal, Autokanal, Geräte |
| `protokoll` | Box-Protokoll, neueste zuerst; Filter `seit` (ISO-Datum/-Zeit), `stichwort`, `limit` |
| `dsl_status` | DSL-Leitung: Sync, Maximalrate, Marge, Dämpfung, CRC/FEC |
| `hosts` | Bekannte Geräte mit IP, MAC, aktiv/inaktiv, Anschlussart; Filter `nur_aktive`, `name`, `limit` |
| `anrufliste` | Anrufe; Filter `art` (eingehend, verpasst, ausgehend, blockiert, AB), `tage`, `limit` |
| `anrufbeantworter` | Status und Nachrichten (Zeit, Anrufer, Dauer, neu/gehört). Markiert nichts |
| `anrufbeantworter_audio` | Lädt eine AB-Nachricht als WAV nach `FRITZ_DOWNLOAD_DIR`. Markiert nichts als gehört |

## Installation

Voraussetzung: Python ab 3.11.

```bash
pipx install git+https://github.com/Schimmilab/fritzbox-mcp-server
# oder ohne Installation:
uvx --from git+https://github.com/Schimmilab/fritzbox-mcp-server fritzbox-mcp
```

In Claude Code registrieren:

```bash
claude mcp add fritzbox -s user \
  -e FRITZ_USER=mcp-lesen -e FRITZ_PASSWORD=... \
  -- fritzbox-mcp
```

Bei `uvx`: `-- uvx --from git+https://github.com/Schimmilab/fritzbox-mcp-server fritzbox-mcp`.

Anderer MCP-Client (JSON):

```json
{
  "mcpServers": {
    "fritzbox": {
      "command": "fritzbox-mcp",
      "env": { "FRITZ_USER": "mcp-lesen", "FRITZ_PASSWORD": "..." }
    }
  }
}
```

Fehlt `FRITZ_USER` oder das Passwort, beendet sich der Server mit einer deutschen Fehlermeldung auf stderr (Exit-Code 2).

## Box vorbereiten

1. In der Box einen eigenen Benutzer anlegen (System → FRITZ!Box-Benutzer), nicht dein Admin-Konto.
2. Heimnetz → Netzwerk → Netzwerkeinstellungen → „Zugriff für Anwendungen zulassen“ (TR-064) einschalten.

Rechte:

- **Nur lesen:** Empfohlen ist ein Benutzer **ohne** Schreibrechte. `zugang_pruefen` weist darauf hin, wenn der Benutzer volle Rechte hat, der Server aber nur liest.
- **Schreiben:** Der Benutzer braucht „FRITZ!Box Einstellungen“ (intern `BoxAdmin`). Mit `FRITZ_WRITE=1` ohne dieses Recht warnt `zugang_pruefen`.
- Welches einzelne Lesewerkzeug welches Recht minimal braucht (z. B. Telefonie für Anrufliste und Anrufbeantworter), ist nicht systematisch geprüft. Teste mit deinem Benutzer; ein fehlendes Recht zeigt sich als Fehlermeldung des Werkzeugs. Für Telefonbuch, Anrufbeantworter und Rufumleitung ist das Recht „Telefonie“ naheliegend, aber nicht gegengeprüft.

## Konfiguration

Alles über Umgebungsvariablen.

| Variable | Bedeutung | Standard |
|---|---|---|
| `FRITZ_ADDRESS` | Adresse der Box | `fritz.box` |
| `FRITZ_USER` | Box-Benutzer (Pflicht) | – |
| `FRITZ_PASSWORD` | Passwort des Benutzers (Pflicht, außer Keychain-Fallback) | – |
| `FRITZ_KEYCHAIN_SERVICE` | Nur macOS: Name eines Schlüsselbund-Eintrags (Account = `FRITZ_USER`), aus dem das Passwort gelesen wird, wenn `FRITZ_PASSWORD` leer ist | – |
| `FRITZ_DOWNLOAD_DIR` | Zielordner für `anrufbeantworter_audio`; ohne ihn kein Audio-Download | – |
| `FRITZ_WRITE` | Schaltet 🟢- und 🟡-Schreibaktionen frei | aus |
| `FRITZ_WRITE_CRITICAL` | Schaltet zusätzlich 🔴-Aktionen frei; wirkt nur zusammen mit `FRITZ_WRITE` | aus |

Als „an“ gelten `1`, `true`, `yes` (Groß-/Kleinschreibung egal); alles andere ist aus. Das Zeitlimit für Box-Aufrufe ist fest 10 s.

## Schreiben

Aus. Ohne `FRITZ_WRITE` existieren die Schreibwerkzeuge gar nicht erst.

Pro Aktion gibt es zwei Werkzeuge: `<aktion>_planen` und `<aktion>_ausfuehren`.

1. **Planen:** liest den echten Ist-Zustand der Box, zeigt Stufe, Folge, Vorher und Nachher und gibt ein Freigabe-Token aus. Geschrieben wird nichts.
2. **Ausführen:** mit Token und denselben Parametern. Das Token ist einmalig, 5 Minuten gültig und gebunden an Aktion, Parameter und die Identität des Ziels (z. B. die MAC hinter einer IP). Hat sich das Ziel seit dem Plan geändert, wird abgelehnt.
3. **Rücklesen:** Der Server meldet den Ist-Zustand aus der Box. Ist der nach dem Anwenden nicht lesbar (z. B. beim Neustart), steht dort „Ist-Zustand nicht lesbar“, kein erfundener Erfolg.

Weitere Schutzregeln im Code:

- Eine Internetsperre für die IP des Rechners, auf dem der Server läuft, wird abgelehnt.
- Telefonbuch: nur neue Einträge; bestehende werden nie überschrieben.
- Unbekannte Ziele (MAC, IP, Nachricht) lehnt schon der Plan ab, bevor ein Token entsteht.
- 🔴-Pläne beginnen mit einem Warnblock.

### Aktionen

🟢 harmlos (`FRITZ_WRITE`)

| Aktion | Parameter | Folge |
|---|---|---|
| `wake_on_lan` | `mac` | Das Gerät wird per Netzwerk eingeschaltet. |
| `gast_wlan` | `an` | Gäste-WLAN wird ein- bzw. ausgeschaltet; verbundene Gäste fliegen beim Ausschalten raus. |
| `ab_nachricht_gehoert` | `ab`, `nachricht`, `gehoert` | Die Nachricht wird als gehört/ungehört markiert (Anzeige am Telefon ändert sich). |

🟡 spürbar (`FRITZ_WRITE`)

| Aktion | Parameter | Folge |
|---|---|---|
| `wlan_kanal` | `band`, `kanal` | Alle WLAN-Geräte dieses Bands trennen sich kurz und verbinden neu; Mesh-Repeater folgen dem Kanal. |
| `wlan_funk_an` | `band` | Das Funkband wird eingeschaltet; Geräte dieses Bands können sich wieder verbinden. |
| `anrufbeantworter` | `ab`, `an` | Der Anrufbeantworter nimmt Anrufe an bzw. nicht mehr an. |
| `rufumleitung` | `umleitung`, `an` | Anrufe werden umgeleitet bzw. nicht mehr umgeleitet. |
| `internet_sperre` | `ip`, `sperren` | Das Gerät verliert bzw. erhält den Internetzugang (LAN bleibt). |
| `telefonbuch_eintrag` | `telefonbuch`, `name`, `nummer` | Ein neuer Telefonbuch-Eintrag wird angelegt. Bestehende Einträge werden nie überschrieben. Nicht umkehrbar per Server. |

🔴 kritisch (`FRITZ_WRITE` und `FRITZ_WRITE_CRITICAL`)

| Aktion | Parameter | Folge |
|---|---|---|
| `wlan_funk_aus` | `band` | Das Funkband wird ausgeschaltet; alle Geräte dieses Bands verlieren die Verbindung. Läuft der Server selbst über dieses Band, trennt er sich damit selbst (zurück nur an der Box oder per LAN). |
| `neustart` | – | Die FRITZ!Box startet neu: Internet, WLAN, Telefonie und Smart-Home-Geräte sind ca. 2–4 min weg, laufende Telefonate brechen ab. |
| `neu_verbinden` | – | Die Internetverbindung wird getrennt und neu aufgebaut: neue öffentliche IP, laufende Verbindungen, VPNs und Telefonate brechen ab. |
| `ab_nachricht_loeschen` | `ab`, `nachricht` | Die Anrufbeantworter-Nachricht wird gelöscht. Nicht umkehrbar. |

Als `band` gilt `"2,4"` oder `"5"`.

### ⛔ Das Token ist keine Freigabe durch einen Menschen

Das Token bindet nur Parameter und Ziel. Es beweist nicht, dass jemand zugestimmt hat; ein Modell kann Plan und Ausführung direkt hintereinander aufrufen. Die eigentliche Sperre ist die Werkzeug-Berechtigung deines MCP-Clients: **Setze `*_ausfuehren`-Werkzeuge nie auf eine Allowlist und aktiviere dafür kein Auto-Approve.** Der Client muss bei jedem Ausführen nachfragen.

## Reichweite: was jeder Schalter dem Modell erlaubt

| Schalter | Was das Modell kann | Schlimmster Fall |
|---|---|---|
| ohne `FRITZ_WRITE` (Standard) | nur lesen (Mesh, WLAN, Hosts, Protokoll, DSL, Anrufliste, AB-Liste; mit `FRITZ_DOWNLOAD_DIR` AB-Aufnahmen als WAV) | Offenlegung: wer wann angerufen hat, welche Geräte im Haus sind, Inhalt von Sprachnachrichten. Keine Änderung an der Box. |
| `FRITZ_WRITE=1` | 🟢 WoL, Gast-WLAN, AB-Nachricht gehört · 🟡 WLAN-Kanal, Funkband an, AB an/aus, Rufumleitung an/aus, Internetsperre je Gerät, neuer Telefonbucheintrag | Gerät ohne Internet, AB nimmt nicht mehr an, Anrufe umgeleitet, irreführender Telefonbucheintrag. Umkehrbar, heilt aber nicht von selbst. |
| `+ FRITZ_WRITE_CRITICAL=1` | 🔴 Funkband aus, Neustart, Neuverbinden, AB-Nachricht löschen | WLAN weg (ggf. auch für den Server), 2–4 min Totalausfall, neue öffentliche IP, unwiederbringlich gelöschte Sprachnachricht. |

Das Ausschalten eines Funkbands (`wlan_funk_aus`) ist bewusst 🔴: Es kann den Server selbst abhängen, und zurück geht es dann nur an der Box oder per LAN. Das Einschalten (`wlan_funk_an`) ist 🟡.

## Bewusste Entscheidungen

- **Private Adressen in den Fixtures:** `192.168.x.x` und andere private IPv4-Adressen (RFC 1918, nicht routbar) bleiben in `tests/fixtures` unverändert, damit die Netzstruktur testbar bleibt. Öffentliche Adressen, MACs, Rufnummern, Namen und Anbieterkennungen sind pseudonymisiert.
- **Kein TLS zur Box (noch):** `fritzconnection` schaltet die Zertifikatsprüfung ab. Session-ID und WAV-Download laufen im LAN als unverschlüsseltes HTTP. Betreibe den Server nur in einem Netz, dem du traust.
- **Box-Texte sind Daten:** Namen, SSIDs und Protokollzeilen werden vor der Ausgabe einzeilig gemacht, von Steuerzeichen und führenden Markdown-Markern befreit und gekürzt; die Server-Instruktionen nennen sie ausdrücklich Daten, keine Anweisungen. Das ist eine Schutzschicht gegen Prompt-Injection, keine Garantie.

## Datenschutz

Was das Modell zu sehen bekommt, ist das, was die Werkzeuge ausgeben:

- Anrufliste mit Namen und Rufnummern, Anrufbeantworter-Metadaten (Zeit, Anrufer, Dauer), Geräteliste mit Namen, IPs und MACs, Protokoll, WLAN-SSIDs.
- Das Passwort und Session-IDs (`sid`) werden aus jeder Ausgabe und jeder Fehlermeldung geschwärzt.
- AB-Audio wird nur lokal in `FRITZ_DOWNLOAD_DIR` gespeichert; das Modell bekommt den Dateipfad und die Größe, nicht den Inhalt. Der Ordner wird mit Modus 0700 angelegt, Dateien mit 0600. Dateinamen werden aus Datum, Nummer und Index gebildet, nie aus Pfaden der Box. Download-Links zu fremden Hosts werden abgelehnt, die Größe ist auf 20 MB begrenzt.
- Nachrichten werden dabei nicht als gehört markiert.

Der Server kontaktiert nur die konfigurierte Box.

## Entwicklung

```bash
python -m venv .venv && .venv/bin/pip install -e '.[dev]'
.venv/bin/pytest -q
```

- Die Tests laufen offline gegen anonymisierte Aufnahmen einer echten Box in `tests/fixtures`.
- Eigene Aufnahme: `FRITZ_USER=... FRITZ_PASSWORD=... python tools/record_fixtures.py` (nur lesend). `tools/anonymize.py` ersetzt MACs (HMAC-Pseudonyme mit Zufallsgeheimnis pro Lauf), Rufnummern, Namen, SSIDs, öffentliche IPs und Zugangsdaten.
- `tests/test_fixtures_clean.py` ist ein unabhängiger Wächter mit Allowlist. Er muss grün sein, bevor Fixtures committet werden.
- CI (GitHub Actions) läuft auf Ubuntu mit Python 3.11 und 3.13.

## Getestet mit

FRITZ!Box 7690, FRITZ!OS 8.50, fritzconnection 1.15.1, FastMCP 3.4. Andere Modelle und Versionen sind nicht geprüft.

## Lizenz

MIT, siehe [LICENSE](LICENSE).

TDQS

B3.2/5.0

Scored across 10 tools

Disambiguation4/5

Most tools have clearly distinct targets (DSL, WLAN, mesh, log, calls, answering machine). The main overlap is between 'geraet' (locate a single device) and 'hosts' (list all known devices with IP/MAC), and secondarily 'wlan_status' vs 'mesh_uebersicht' since mesh reports per-radio data too; descriptions mostly disambiguate these.

Naming Consistency3/5

Naming follows mainly German compound nouns (dsl_status, wlan_status, mesh_uebersicht, anrufliste) but mixes in a bare singular ('geraet') and a verb-first form ('zugang_pruefen'), plus an inflected compound ('anrufbeantworter_audio'). It is readable and semi-predictable but not a single consistent convention.

Tool Count5/5

10 tools is well-scoped for a FRITZ!Box diagnostic server; each tool maps to a distinct subsystem (access, DSL, WLAN, mesh, devices, log, telephony) and none feel redundant.

Completeness4/5

The surface covers the main diagnostic domains: connectivity/access, line and radio status, device inventory/lookup, log, and telephony including audio retrieval. Gaps are minor and mostly read-only limitations (e.g. no way to mark AB messages heard or perform actions like reboots/WLAN toggles), which are documented rather than dead ends.

Maintenance

ActivityMaintained
ResponsivenessNo issues