Noa Matout Revenir à l’accueil English

01 / Mes articles / Publié

Ma montre parle à Claude, sous conditions

J'ai écrit un serveur MCP pour interroger mes données d'entraînement Garmin. Les vraies données ont cassé trois choses que mes tests ne voyaient pas.

Rédigé le

Garmin Connect garde des années de mes entraînements et ne sait répondre qu'aux questions que ses concepteurs ont prévues. Pour le reste, il faut exporter des fichiers et ouvrir un tableur.

J'ai écrit un serveur MCP qui récupère les fichiers FIT d'origine, les lit, les range dans une base DuckDB locale et expose 16 outils typés à Claude. Je peux lui demander de comparer mes deux dernières sorties, ou de vérifier si ma semaine a suivi le plan. Tout tourne chez moi : aucun service tiers ne garde mes données. Consulter le code de mon serveur pour les données sportives.

Ce qui m'intéresse ici n'est pas la fonction. C'est ce qu'il faut refuser à un modèle pour qu'il puisse toucher à des données personnelles, et ce que les vraies données m'ont appris que mes tests ignoraient.

Pas de SQL libre

Donner à un modèle un accès SQL arbitraire sur une base personnelle, c'est lui donner tout ce que le fichier contient. Il peut être convaincu de tout lire, et il écrira parfois une requête qui parcourt un million de lignes pour parler de mardi dernier.

Chaque requête du serveur est écrite à l'avance et paramétrée, dans un seul module. Les noms de champs qu'un modèle demande passent par une liste d'autorisation, parce qu'ils viennent du modèle. Je n'ai qu'un fichier à relire, plutôt qu'une promesse à croire.

Écrire sur ma montre demande deux fois mon accord

Lire un historique et modifier un compte sont deux actes différents. Le second arrive à mon poignet.

L'écriture est désactivée par défaut. Cloner le dépôt ne donne pas à un modèle le droit de modifier un compte Garmin. Une fois activée, aucune écriture ne se fait en un seul appel. Le premier appel valide la séance en local, n'envoie rien et la renvoie en entier. Seul un second appel, avec une confirmation explicite, la crée. Pendant l'aperçu, aucun identifiant n'est même chargé : c'est ce qui rend la confirmation réelle.

Chaque création a son annulation, et l'outil qui liste les séances existe pour que cette annulation soit atteignable. Une séance enregistrée n'est pas une séance planifiée : la mettre au calendrier est un autre outil, avec sa propre confirmation. C'est la règle que j'applique à mes agents, décrite dans l'article sur ce que les administrateurs acceptent : il propose, je décide.

Le serveur ne sait pas se connecter

Les composants qui parlent à Garmin sont construits sans identifiants. Ils ne peuvent que reprendre une session déjà ouverte. Seule une commande lancée à la main voit un mot de passe, et le mot de passe n'est jamais écrit sur le disque.

Conséquence voulue : quand la session Garmin expire, le serveur ne reste pas bloqué sur une demande de double authentification que personne ne verra. Mon historique s'arrête simplement à la dernière synchronisation, et le serveur continue de répondre.

Quand Garmin a fermé la porte

En mars 2026, Garmin s'est mis à filtrer les clients selon la forme de leur connexion TLS. Les clients HTTP ordinaires ont cessé de fonctionner, et la bibliothèque que je comptais utiliser a été abandonnée.

J'en ai tiré une règle : l'import manuel n'est pas un plan de secours, c'est un pilier. Je dépose des fichiers FIT exportés dans un dossier, une commande les importe, sans réseau ni identifiant. C'est le seul chemin dont je peux promettre qu'il marchera encore dans un an. Pour le reste, deux méthodes d'accès sont cachées derrière une interface étroite, et une troisième pourra s'y brancher le jour où Garmin rouvrira une API officielle.

Une moyenne qui mentait sur ma fréquence cardiaque

Une sortie de trois heures contient des milliers de points par mesure. Pour ne pas noyer le modèle, le serveur les regroupe et en fait la moyenne. Sur une sortie réelle de 1 513 échantillons, un aperçu à 10 points annonçait une fréquence cardiaque maximale de 160,2. La vraie valeur était 174. Le minimum était pire : 138 annoncé, 111 en réalité.

Un aperçu grossier est exactement ce qu'un modèle demande quand il veut une allure générale. Ce n'était donc pas un cas limite, c'était le chemin courant. Et « votre fréquence maximale était de 160,2 » est une réponse fausse, donnée avec assurance, sur un chiffre que les sportifs prennent au sérieux.

Chaque réponse porte désormais les vrais extrêmes, calculés sur tous les échantillons, à côté de la courbe lissée.

Une charge d'entraînement qui n'existait pas

Le résumé hebdomadaire additionnait un score Garmin qui décrit une seule séance, noté de un à cinq, et appelait le total « charge d'entraînement ». Sur une vraie semaine de six séances dans quatre sports, il affichait 17. Ce nombre n'a aucun sens, et il a l'air sérieux.

Un journal d'entraînement est précisément l'endroit où ce genre de chiffre fait des dégâts. Je l'ai supprimé plutôt que renommé.

Des titres effacés sans une erreur

En regardant les données plutôt que le code, j'ai vu 66 activités Garmin et un seul titre. La commande qui relit les fichiers stockés avait effacé les autres, sans aucune erreur.

Les titres se saisissent dans Garmin Connect et arrivent avec la liste des activités. Ils ne sont pas dans le fichier FIT. Reconstruire une ligne à partir du seul fichier perdait donc tout ce que le fichier ne porte pas. Une commande dont le rôle est de relire les fichiers stockés était certaine de les perdre.

La relecture conserve maintenant les titres existants, et chaque synchronisation les rafraîchit. La correction a rendu 91 titres et révélé 25 activités plus anciennes qui n'avaient jamais été importées, parce que la liste lue chez Garmin était tronquée.

Aucune de ces trois pannes n'était visible dans la suite de tests. Toutes sont apparues en regardant de vraies séances.

Ce que j'ai fait pour les voir plus tôt

Le lecteur vise le format FIT, pas une montre. Je le valide sur 42 enregistrements réels venant de 19 appareils et huit fabricants. Parmi eux, 30 sont lus comme des activités. Les 12 autres sont refusés à raison : réglages, pesées, suivi quotidien, ou un fichier tronqué avant sa première séance.

Les vrais appareils ont des manies qu'aucun fichier synthétique n'invente : une montre qui enregistre avant le départ, une fréquence cardiaque à zéro au lieu de « absente », une heure de départ illisible. Chaque valeur reconstituée porte une marque, pour qu'un chiffre déduit ne passe jamais pour un chiffre mesuré.

La suite hermétique, sans données ni réseau, compte 214 tests réussis. Je l'ai relancée avant d'écrire cet article.

Ce que je n'ai pas pu vérifier

L'accès automatique à Garmin repose sur des méthodes non officielles. Il a déjà cassé une fois, et il cassera encore. Seul l'import manuel est garanti.

Le corpus couvre 19 appareils, pas tous ceux du marché, et l'essentiel de mon usage porte sur mes propres données.

Enfin, j'avais soigné le code et oublié le système. Lancé sans aucun confinement, ce même serveur obtenait une note d'exposition de 9,2 sur 10. Je raconte comment je l'ai mesurée et corrigée dans l'article sur l'audit de mon propre serveur MCP.

Sources

Les mesures sont les miennes, tirées du dépôt, de son historique et de sa suite de tests.

Périmètre de la mesure

16 outils typés. Outils exposés par le serveur MCP, sans aucun accès SQL libre.

214 tests réussis. Suite hermétique, sans données réelles ni réseau, relancée le 2 octobre 2026.

42 enregistrements réels. Fichiers FIT réels utilisés pour valider le lecteur, hors de la suite hermétique.

19 appareils. Appareils différents représentés dans le corpus de validation.

8 fabricants. Fabricants représentés dans le corpus de validation.

30 fichiers lus. Fichiers du corpus lus comme des activités.

12 rejets corrects. Fichiers du corpus qui ne sont pas des activités ou sont tronqués, et que le lecteur refuse à raison.

1 513 échantillons. Points de fréquence cardiaque d'une sortie réelle, utilisés pour mesurer l'effet du sous-échantillonnage.

10 points. Résolution d'un aperçu grossier de la même sortie.

160,2 battements par minute. Fréquence cardiaque maximale lue dans l'aperçu à dix points, avant correction.

174 battements par minute. Fréquence cardiaque maximale réelle de la même sortie.

138 battements par minute. Fréquence cardiaque minimale lue dans l'aperçu à dix points, avant correction.

111 battements par minute. Fréquence cardiaque minimale réelle de la même sortie.

6 séances. Séances d'une semaine réelle additionnées par l'ancien résumé hebdomadaire.

4 sports. Sports représentés dans cette semaine.

17 points de charge. Somme sans signification produite par l'ancien résumé, affichée comme une charge d'entraînement.

66 activités. Activités Garmin présentes dans ma base au moment où j'ai remarqué la perte des titres.

1 titre. Titre encore présent après la relecture complète des fichiers.

91 titres. Titres récupérés après correction, en élargissant la liste lue chez Garmin.

25 activités. Activités plus anciennes jamais importées, découvertes pendant la même correction.

9,2 sur 10. Note d'exposition de mon serveur MCP avant durcissement, relevée par l'outil d'analyse de systemd. Plus elle est basse, mieux c'est.