ecoledirecte-mcp et ffe-mcp : deux serveurs MCP faits maison, sans API officielle

J'ai passé une soirée récente à comprendre pourquoi mon propre serveur MCP
refusait de se connecter à EcoleDirecte avec des identifiants pourtant
valides. Ce n'était pas un bug exotique, juste un protocole de login non
documenté qu'il fallait reproduire à l'octet près. Cette session est
l'occasion de revenir sur les deux serveurs MCP que j'ai construits pour mon
usage personnel : ecoledirecte-mcp pour les notes et devoirs de mes
enfants, ffe-mcp pour les tournois d'échecs homologués par la Fédération
Française des Échecs.
Le problème que ça résout#
Aucun des deux sites n'expose d'API publique. EcoleDirecte a une appli et un site web, point final : pour savoir si mes enfants ont un devoir pour le lendemain, il faut ouvrir le site, se connecter, naviguer jusqu'au cahier de texte. Rien qui s'intègre dans une conversation avec Claude où je suis déjà en train de regarder mon planning de la semaine.
Pour les échecs, j'avais déjà un skill qui cherche des tournois
via WebFetch directement sur le site de la FFE. Ça marchait, avec une
limite connue et documentée dans le skill lui-même : la pagination ASP.NET
et les filtres en postback JavaScript du site ne sont pas actionnables par un
simple fetch, donc seul le premier lot de résultats par département était
récupéré. Un serveur dédié, avec ses propres endpoints identifiés et ses
propres parseurs, ferme cette limite proprement au lieu de la contourner à
moitié à chaque appel.
Pourquoi un serveur MCP plutôt qu'un skill ou un script#
Un skill encode un comportement, un serveur MCP encode un accès à un système externe : je détaille cette distinction dans l'article sur MCP. Le sujet ici, c'est ce que ça donne concrètement quand on construit soi-même ce deuxième type de brique plutôt que de se contenter d'appeler une API publique bien documentée qui n'existe pas.
Ce que chaque serveur expose#

Capture réelle de l'interface, avec nom d'élève, d'établissement, de
parent et d'enseignant remplacés par des valeurs fictives avant capture —
c'est la page que consulter_devoirs lit à ma place.
ecoledirecte-mcp — nécessite les identifiants du compte (lus depuis un
.env local, jamais transmis ailleurs) :
| Outil | Description |
|---|---|
lister_eleves | Liste les élèves rattachés au compte |
consulter_notes | Notes d'un élève par matière et par période |
consulter_devoirs | Cahier de texte d'un élève |
consulter_absences | Absences, retards, sanctions d'un élève |
consulter_messages | Messages reçus dans la messagerie du compte |
ffe-mcp — données publiques, aucun identifiant requis :
| Outil | Description |
|---|---|
rechercher_joueur | Recherche un joueur par nom, retourne numéro FFE et Elo |
lister_tournois | Tournois homologués d'un département |
details_tournoi | Cadence, nombre de rondes, annonce d'un tournoi |
classement_tournoi | Classement général après la dernière ronde publiée |
resultats_joueur_tournoi | Résultats ronde par ronde d'un joueur dans un tournoi |

Données publiques cette fois, aucune anonymisation nécessaire : la page
que lister_tournois parse pour le département 92.
Comment ils sont construits#
Les deux projets partagent la même architecture, en trois couches :
src/domain/: types métier et interfaces (ports), zéro dépendance à l'API ou au site sourcesrc/infrastructure/: l'adaptateur qui parle le format brut de la source (requêtes HTTP pour l'un, parsing HTML pour l'autre), avec un cache fichier qui sert de filet de sécuritésrc/mcp/: l'exposition des outils à Claude, qui orchestre fetch live et repli sur le cache
Cette isolation a un but précis : le jour où EcoleDirecte change son protocole de login ou où la FFE modifie la mise en page d'une page de résultats, un seul fichier doit changer. Les outils exposés à Claude, eux, ne bougent pas.
Les deux tournent en local uniquement, via @modelcontextprotocol/sdk en
transport stdio : pas de serveur HTTP exposé, pas de déploiement, pas de
port réseau à sécuriser. Chaque famille qui veut l'utiliser clone le repo et
le fait tourner chez elle, avec ses propres identifiants pour
ecoledirecte-mcp, aucun identifiant du tout pour ffe-mcp.
Le vrai obstacle technique : deviner un protocole non documenté#
Le cas ecoledirecte-mcp est le plus instructif. login.awp rejetait mes
identifiants avec un message générique, « identifiant et/ou mot de passe
invalide », alors qu'ils fonctionnaient sans problème sur le site. Première
piste, plausible mais fausse : la présence de scripts anti-bot
(transparentedge.io, cookie TEDGEPT) observés en comparant une capture
réseau d'un vrai login navigateur avec ce que mon client Node envoyait. Ça
ressemblait à du fingerprinting bloqué côté serveur.
La vraie cause, trouvée en croisant la documentation communautaire du
protocole EcoleDirecte (le format n'est pas officiel, mais largement
rétro-documenté par plusieurs projets open source), était plus simple et
strictement protocolaire : login.awp attend un GET préalable
(?gtk=1) qui pose deux cookies, dont un GTK, à renvoyer sur le POST de
login à la fois comme header X-Gtk et comme header Cookie classique.
Mon client ne renvoyait ni l'un ni l'autre. Une fois les deux ajoutés, le
login est passé du premier coup.
La leçon qui compte n'est pas le detail du header manquant, elle est plus générale : un message d'erreur produit par une API non documentée n'est pas une preuve de sa cause. J'ai perdu du temps sur une hypothèse anti-bot plausible avant de vérifier le protocole réellement attendu par une capture réseau comparée point par point. Le même principe que je décris dans l'article sur le débogage avec un agent : citer une preuve brute avant de conclure, jamais un récit plausible.
Côté ffe-mcp, la fragilité est différente : pas de protocole à
reconstituer, mais des pages HTML publiques dont la structure peut changer
sans préavis. La parade n'est pas la même non plus : les tests vitest
tournent contre des fixtures HTML réellement capturées sur echecs.asso.fr,
et le README documente explicitement quels champs sont vérifiés par capture
réelle et lesquels restent à confirmer. Si la mise en page change, le test
casse fort et vite plutôt que de renvoyer silencieusement un classement
faux.
Comment les utiliser#
Pour ecoledirecte-mcp, un assistant en ligne de commande écrit le fichier
de configuration à l'écart du projet, pour rester stable même installé via
npx :
npx ecoledirecte-mcp-init
claude mcp add ecoledirecte -- npx -y ecoledirecte-mcpPour ffe-mcp, pas d'identifiants à fournir puisque les données sont
publiques :
git clone https://github.com/riadh-mnasri/ffe-mcp.git
cd ffe-mcp && npm install && npm run build
claude mcp add ffe -- node /chemin/vers/ffe-mcp/dist/index.jsLes deux fonctionnent aussi avec Claude Desktop, en ajoutant l'équivalent
dans claude_desktop_config.json.
Partager sans être un service#
Les deux projets sont sous licence MIT, sans compte à créer et sans base de
données centrale : ce n'est pas un service auquel on se connecte, c'est un
programme que chaque famille clone et fait tourner chez elle. Pour
ecoledirecte-mcp en particulier, ce choix n'est pas qu'une préférence
d'architecture, c'est ce qui évite d'avoir à porter la responsabilité du
stockage des données scolaires d'autrui : il n'y a pas de « autrui », chacun
héberge les siennes.
Ce que ça généralise#
Trois principes qui tiennent au-delà de ces deux projets, pour n'importe quel outil construit contre une source sans API officielle : partir d'une requête réellement capturée plutôt que d'une supposition sur le protocole, isoler l'adaptateur volatile du reste du code pour qu'une seule couche casse le jour où la source change, et préférer un repli sur un cache explicitement daté à un échec sec quand la source est hors de mon contrôle. Le code des deux serveurs est public : ecoledirecte-mcp et ffe-mcp.


