Skip to main content
Glama

sfrbox-toolkit

CLI, API REST et serveur MCP non officiels pour piloter la configuration d'une box SFR (firmware NB6VAC, ex. « SFR Box 7 » fibre) depuis un script, un agent IA, ou votre propre interface.

⚠️ Projet communautaire, non affilié à SFR. Basé sur une rétro-ingénierie de l'interface web publique de la box (aucun binaire ni firmware n'a été désassemblé : uniquement le HTML/JS servi par la box elle-même). Le firmware peut changer à tout moment et casser ce dépôt — voir Rétro-ingénierie pour comprendre comment diagnostiquer et corriger ce cas.

Pourquoi

L'interface web de cette box ne propose pas d'API JSON/REST documentée (contrairement par exemple à la Freebox) : chaque page de configuration est un formulaire HTML classique. Ce projet reproduit fidèlement le protocole d'authentification et le remplissage de ces formulaires pour exposer la configuration via trois interfaces :

  • CLI (sfrbox ...) pour scripter/automatiser depuis un terminal.

  • API REST (FastAPI) pour l'intégrer à votre propre outillage.

  • Serveur MCP (sfrbox-mcp) pour la piloter depuis un agent IA (Claude Code, Claude Desktop, tout client MCP).

Les trois s'appuient sur la même bibliothèque Python (src/sfrbox/), qui peut aussi être utilisée directement.

Related MCP server: io.github.antonio-mello-ai/mcp-pfsense

Modèle de box concerné

Testé et validé contre une box avec bandeau Version : NB6VAC-MAIN-R4.0.47hx (visible en bas de la page d'accueil http://192.168.1.1/, ou dans Etat > Général). Il s'agit de la box fibre couramment appelée « SFR Box 7 », basée sur une plateforme Sagemcom. Non testé sur la SFR Box 8 ni sur les anciennes box ADSL (NB4/NB6 non-VAC) : l'authentification par challenge est probablement proche, mais les formulaires de configuration peuvent différer. Contributions/rapports bienvenus (voir Contribuer).

Installation

Nécessite uv et Python ≥ 3.11.

git clone <ce-dépôt>
cd sfr-box-toolkit
uv sync
cp .env.example .env
# éditez .env : SFRBOX_HOST (192.168.1.1 par défaut), SFRBOX_LOGIN (admin
# par défaut), SFRBOX_PASSWORD (le mot de passe imprimé sous votre box,
# ou celui que vous avez personnalisé dans Maintenance > Administration).

.env est listé dans .gitignore : vos identifiants ne doivent jamais être commités. Ne les passez pas non plus en argument de ligne de commande (ils resteraient dans l'historique du shell) : toutes les interfaces les lisent depuis l'environnement/.env.

Utilisation

CLI

uv run sfrbox status wan
uv run sfrbox status devices
uv run sfrbox wifi status
uv run sfrbox wifi set-2g --ssid "MonReseau" --hidden false
uv run sfrbox wifi wpa-key-set 5ghz          # demande la nouvelle clé de façon masquée
uv run sfrbox dhcp status
uv run sfrbox nat portforward-add "Serveur web" --protocol tcp \
    --external-port 8080 --destination-ip-last-octet 42 --destination-port 80
uv run sfrbox --help                          # liste complète des commandes

API REST

uv run uvicorn sfrbox.api:app --host 0.0.0.0 --port 8000
curl http://localhost:8000/wifi/status
curl -X POST http://localhost:8000/wifi/2ghz -H 'content-type: application/json' \
    -d '{"ssid": "MonReseau"}'

Documentation interactive auto-générée sur http://localhost:8000/docs.

⚠️ Cette API donne un accès complet à la configuration de la box (y compris la lecture des clés Wifi en clair) à quiconque peut l'atteindre. Ne l'exposez pas au-delà de votre réseau local sans ajouter votre propre couche d'authentification.

Serveur MCP

uv run sfrbox-mcp

Exemple de configuration client MCP (~/.claude/mcp.json ou équivalent) :

{
  "mcpServers": {
    "sfrbox": {
      "command": "uv",
      "args": ["--directory", "/chemin/vers/sfr-box-toolkit", "run", "sfrbox-mcp"]
    }
  }
}

Le serveur lit .env depuis son répertoire de travail au démarrage. 21 outils sont exposés (statut, Wifi, DHCP, NAT/port forwarding, DMZ, UPnP, pare-feu, DDNS, redémarrage...).

Fonctionnalités couvertes

Domaine

Lecture

Écriture

Statut WAN / équipements connectés

✅

—

Wifi (2,4 GHz / 5 GHz / invité) : actif, SSID, masqué

✅

✅

Clé WPA (2,4 GHz / 5 GHz / invité)

✅

✅

WPS

—

✅

DHCP (plage, bail, réservations statiques)

✅

✅

Redirections de ports (NAT)

—

✅ (ajout/suppression)

DMZ

✅

✅

UPnP

✅

✅

Wake-on-LAN

✅

✅

Passthrough SIP ALG / PPTP / GRE

✅

✅

Pare-feu (règles simples)

✅

✅

DNS dynamique

✅

✅

Redémarrage / réinitialisation d'usine

—

✅ (confirmation requise)

Non couvert pour l'instant : téléphonie (VoIP), partage USB/Samba/UPnP-AV, IPv6, planification Wifi horaire, mode Eco, routes statiques, entrées DNS locales. Le code est structuré pour que l'ajout d'un module suive exactement le même schéma que les modules existants (voir Contribuer).

Rétro-ingénierie : comment ça marche

Toute la logique vient de la lecture du HTML/JS servis publiquement par la box elle-même (/js/global.js, /js/login.js, et les pages de configuration) — aucune inspection de binaire, aucun accès à du code propriétaire non exposé.

Authentification (src/sfrbox/auth.py)

  1. POST /login avec action=challenge (en-têtes X-Requested-With: XMLHttpRequest requis) renvoie un challenge aléatoire en XML : <rsp stat="ok"><challenge>...</challenge></rsp>.

  2. Le client calcule :

    hash = HMAC-SHA256(clé=challenge, message=SHA256_hex(login))
         + HMAC-SHA256(clé=challenge, message=SHA256_hex(password))

    Point non intuitif (source d'erreur n°1 si vous réimplémentez ceci) : c'est le challenge qui sert de clé HMAC, et le SHA-256 hexadécimal de l'identifiant/mot de passe qui sert de message — pas l'inverse. Le mot de passe en clair ne quitte jamais le client.

  3. POST /login avec method=passwd, zsid=<challenge>, hash=<hash>, et les champs login/password vides. En cas de succès, la box pose un cookie de session sid et redirige vers /index.

Pages de configuration (src/sfrbox/parsing.py, client.py)

Aucune API JSON : chaque page (/wifi/config, /network/dhcp, /network/nat, ...) est un formulaire HTML classique en Post/Redirect/Get. Ce toolkit :

  1. Récupère la page et parse tous ses <form> (valeurs par défaut : texte/hidden tels quels, checked pour les radios/checkbox, selected pour les select).

  2. Fusionne les champs demandés par l'appelant avec les valeurs par défaut (pour ne pas écraser involontairement un champ voisin, comme le ferait un navigateur qui ne soumet que les champs présents dans le formulaire affiché).

  3. Ajoute le nom du bouton de soumission cliqué (une page peut contenir plusieurs formulaires, ou un même formulaire plusieurs boutons — observé sur /wifi/config où les blocs 2,4 GHz / 5 GHz / invité partagent un seul <form> avec 3 boutons distincts).

  4. Soumet en POST application/x-www-form-urlencoded.

Si le firmware change

Si une commande échoue avec SFRBoxFormError, c'est probablement que la structure d'un formulaire a changé entre versions de firmware. Pour diagnostiquer :

uv run python -c "
from sfrbox.config import Settings
from sfrbox.parsing import parse_forms
c = Settings.from_env().client(); c.login()
html = c.get_html('/wifi/config')
for f in parse_forms(html, c.base_url, '/wifi/config'):
    print(f.element_id, f.fields, f.buttons)
"

et comparez avec le module concerné dans src/sfrbox/modules/.

Sécurité

  • Toutes les requêtes se font en HTTP non chiffré sur le réseau local (comme le fait l'interface web native de la box) : c'est le comportement natif de la box, pas une régression de ce projet. N'utilisez ce toolkit que depuis un réseau de confiance.

  • Ne commitez jamais .env, un export de configuration de la box, ou une capture HAR/PCAP : ces fichiers contiennent des identifiants et des clés Wifi en clair.

  • L'API REST et le serveur MCP donnent tous deux un accès complet (lecture et écriture, y compris les clés Wifi) à la configuration de la box. Ne les exposez pas au-delà de votre réseau local.

  • Les actions de redémarrage/réinitialisation d'usine exigent une confirmation explicite (--yes en CLI, confirm=true en API/MCP) pour limiter les déclenchements accidentels.

Contribuer

Pour ajouter un module (ex. VoIP, IPv6) :

  1. Authentifiez-vous sur l'interface web réelle et identifiez la page concernée.

  2. Utilisez le script de diagnostic ci-dessus (section Rétro-ingénierie) pour lister les formulaires, champs, boutons de soumission de la page.

  3. Créez src/sfrbox/modules/<domaine>.py sur le modèle des modules existants (get_config/set_config utilisant client.get_form/client.submit_form).

  4. Exposez les nouvelles fonctions dans cli.py, api.py et mcp_server.py.

  5. Ajoutez des tests sur la logique de parsing avec du HTML de test synthétique (voir tests/test_parsing.py) — ne commitez jamais de HTML exporté depuis une vraie box (il peut contenir des secrets).

Licence

MIT — voir LICENSE.

Available Tools

21 tools
connected_devicesA

Liste des équipements connectés au réseau local (MAC, nom d'hôte, IP, port).

Inputs: no named parameters. Returns: result

ddns_setD

Modifie la configuration DNS dynamique.

Inputs: active, service, hostname, password, +1 more.

ddns_statusC

Configuration DNS dynamique courante.

Inputs: no named parameters.

dhcp_setC

Modifie la configuration DHCP. Les bornes sont le dernier octet (192.168.1.X).

Inputs: active, lease_seconds, range_end_last_octet, range_start_last_octet.

dhcp_statusB

Configuration DHCP courante (actif, plage d'adresses, durée de bail).

Inputs: no named parameters.

firewall_setD

Modifie la configuration du pare-feu.

Inputs: active, block_smtp, block_icmp_ping, block_windows_sharing.

firewall_statusC

Configuration du pare-feu (actif, blocage partage Windows/SMTP/ping).

Inputs: no named parameters.

nat_add_port_forwardC

Ajoute une redirection de port. protocol vaut "tcp" ou "udp".

Inputs: name (required), protocol (required), external_port (required), destination_port (required), +1 more.

nat_get_dmzB

État de la DMZ (hôte exposé directement à Internet).

Inputs: no named parameters.

nat_get_upnpB

État d'UPnP (ouverture automatique de ports par les applications).

Inputs: no named parameters.

nat_remove_port_forwardC

Supprime une redirection de port par son numéro de ligne dans l'interface web.

Inputs: row_index (required).

nat_set_dmzC

Active/désactive la DMZ vers 192.168.1.<ip_last_octet>.

Inputs: active (required), ip_last_octet.

nat_set_upnpC

Active/désactive UPnP.

Inputs: enabled (required).

system_rebootA

Redémarre la box. confirm doit valoir true (coupe Internet/Wifi temporairement).

Inputs: confirm (required).

wan_statusA

Statut de la connexion Internet (état, profil FTTH/xDSL, temps de connexion).

Inputs: no named parameters.

wifi_get_wpa_keyB

Récupère la clé WPA en clair pour la bande donnée : "2.4ghz", "5ghz" ou "guest".

Inputs: band (required). Returns: result

wifi_set_2ghzB

Modifie le réseau Wifi 2,4 GHz. Ne fournir que les champs à changer.

Inputs: ssid, active, hidden.

wifi_set_5ghzC

Modifie le réseau Wifi 5 GHz. Ne fournir que les champs à changer.

Inputs: ssid, active, hidden.

wifi_set_guestB

Modifie le réseau Wifi invité. Ne fournir que les champs à changer.

Inputs: ssid, active, hidden.

wifi_set_wpa_keyB

Change la clé WPA (8-63 caractères) pour la bande "2.4ghz", "5ghz" ou "guest".

Inputs: band (required), new_key (required).

wifi_statusB

État des 3 réseaux Wifi (2,4 GHz, 5 GHz, invité) : actif, SSID, masqué.

Inputs: no named parameters.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3/5.0

    Scored across 21 tools

    Disambiguation4/5

    Most tools target distinct resources and actions (e.g., wifi_set_2ghz vs wifi_set_wpa_key, nat_get_dmz vs nat_set_dmz). A few adjacent wifi and NAT operations could be confused at first glance, but descriptions clarify their boundaries.

    Naming Consistency5/5

    All tool names use consistent snake_case with a clear domain prefix followed by an action or status suffix. The pattern is predictable across wan, wifi, dhcp, ddns, nat, firewall, and system tools.

    Tool Count4/5

    21 tools is somewhat heavy but reasonable for a router administration server covering Wi-Fi, DHCP, DDNS, NAT, firewall, status, and reboot. Each tool maps to a specific configuration or status operation.

    Completeness3/5

    Core router management areas are covered, but there is no tool to list existing NAT port forwards, despite nat_remove_port_forward requiring a line number. This creates a notable dead end for discovery and safe removal.

    Maintenance

    ActivitySlowing
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      Enables AI agents to manage OpenWRT routers remotely via SSH, supporting system monitoring, network management, OpenThread Border Router configuration, and package management through natural language commands.
      19
      17
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI assistants to interact with Xiaomi/Redmi routers, providing read-only queries and management capabilities such as device listing, port forwarding, DHCP reservations, and reboot control.
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI agents to manage Nokia G-240W-F fiber router settings, including router status, connected devices, optical power, Wi-Fi channel selection, Ethernet speeds, port forwards, DMZ, and gaming optimization through 12 tools.
      1
      MIT