garmin-mcp
Provides access to Garmin account data through the Garmin MCP server, including OAuth authentication, token management, and APIs for interacting with Garmin-linked accounts.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@garmin-mcpWhat was my average heart rate on yesterday's run?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Gamin-mcp-webui
Le serveur MCP garmin-mcp et une interface web de consultation des comptes, dans un seul dépôt et une seule image Docker.
Le serveur amont, écrit en Go, authentifie plusieurs comptes via OAuth 2.1 en mode remote et
garde leur état dans une base SQLite. Il n'expose aucune page ni API d'administration : impossible
de savoir, depuis le serveur lui-même, qui a créé un compte ni quand ce compte s'est connecté pour
la dernière fois. Ce dépôt ajoute cette vue et fait tourner les deux ensemble.
Le serveur amont est vendorisé dans garmin-mcp/ par git subtree : c'est une copie versionnée
du dépôt public, mettable à jour d'une commande (voir « Suivre l'amont »), et non un simple
téléchargement au moment du build.

conteneur unique
┌───────────────────────────────────────────────┐
│ garmin-mcp serve ──écrit──► /data/garmin.db│ :8180 endpoint MCP + login OAuth
│ (Go, OAuth 2.1) (SQLite, WAL) │
│ ▲ ▲ │
│ interface web ────lit (RO)───────┘ │ │ :8080 interface + API JSON
│ (FastAPI) ────valide────────────►┘ │ (écrit account_approvals)
└───────────────────────────────────────────────┘
/data : base + clé maîtresse + TLS (volume)Les deux processus tournent sous le même compte de service, condition nécessaire pour qu'une base SQLite en mode WAL soit lisible par le second. Le conteneur s'arrête dès que l'un des deux s'arrête, pour que la politique de redémarrage de Docker s'applique à l'ensemble.
Ce que l'interface affiche
Colonne | Origine |
Compte |
|
Garmin lié | présence de |
Compte créé le |
|
Dernière connexion | dérivée (voir ci-dessous). |
Signal retenu | le fait horodaté qui porte la date affichée. |
Clients / familles |
|
Notice acceptée | dernière ligne de |
Validation |
|
Le détail d'un compte ajoute la liste complète des signaux, ses acceptations de la notice de confidentialité, les clients OAuth autorisés avec leurs portées, les familles de jetons et, si la table est alimentée, les événements d'audit.
Related MCP server: mcp-server-garmin
Comment la « dernière connexion » est calculée
Le schéma de garmin-mcp n'a pas de colonne « dernière connexion ». Il stocke des faits horodatés ; l'interface prend le plus récent d'entre eux et indique lequel a gagné.
Signal | Source | Ce qu'il prouve |
Rafraîchissement des jetons Garmin |
| usage réel : les jetons Garmin sont réécrits à chaque rafraîchissement. |
Émission d'un jeton MCP |
| le client MCP a obtenu un jeton d'accès ou l'a fait tourner. |
Autorisation dans le navigateur |
| passage complet par la page de login. |
Consentement accordé |
| consentement donné à un client MCP. |
Événement d'audit |
| table présente dans le schéma, sans écrivain dans les versions 0.0.x. |
Mise à jour du compte |
| repli, retenu seulement s'il est postérieur à |
Un compte dont aucun signal ne dépasse sa date de création est classé jamais connecté —
updated_at vaut alors created_at et ne prouve aucune connexion.
États dérivés (seuils configurables) : actif ≤ 7 j, inactif ≤ 30 j, dormant au-delà, jamais connecté sans aucun signal.
Limites à connaître
Rétention. La tâche de nettoyage de garmin-mcp supprime les codes et les jetons expirés. Un compte inactif depuis longtemps peut donc voir sa dernière connexion « reculer » sur un signal plus ancien mais persistant (consentement, jetons Garmin). Les consentements, eux, ne sont jamais supprimés par le nettoyage.
Rien n'est instrumenté côté requête. garmin-mcp n'écrit pas d'horodatage de dernier appel d'outil ; la granularité est donc celle de l'émission des jetons, pas celle de chaque requête MCP. Pour une vraie mesure d'usage, il faudrait un correctif amont alimentant
audit_events— cette interface l'affichera automatiquement si la table se remplit.Identité Garmin. Le nom Garmin du compte est chiffré dans la base (
garmin_identity_sealed) et n'est déchiffrable qu'avec la clé maître du serveur. L'interface ne la demande pas et n'affiche donc que l'e-mail.
Le guide destiné aux utilisateurs
docs/connecter-garmin-a-claude.md est la notice à donner aux personnes qu'on invite sur un déploiement : brancher le connecteur dans Claude, se connecter à Garmin, accepter la notice de confidentialité, puis attendre la validation de l'exploitant. Elle décrit aussi ce que le serveur enregistre et ce qu'il n'enregistre pas, en langage courant.
Elle est écrite pour ce déploiement : l'URL du serveur MCP et l'identifiant de client OAuth qu'elle donne sont ceux d'une installation particulière, à remplacer par les vôtres. Les captures des écrans de Claude sont à déposer dans docs/captures/, qui liste celles que le guide attend ; celles des deux pages servies par le serveur lui-même sont déjà là.
Le consentement de l'utilisateur
Ce dépôt ajoute au serveur amont une fenêtre de consentement : la page qui conclut le login dans le navigateur — celle qui porte Autoriser et Refuser — affiche désormais ce que le déploiement enregistre et ce qu'il n'enregistre pas, et l'acceptation est enregistrée en base.

Un résumé toujours visible : ce qui est enregistré, ce qui ne l'est jamais, ce que reçoit le client MCP, et comment arrêter le traitement.
Le texte intégral à la demande, dans un dépliant sur la même page — aucun second chargement, donc rien qui puisse échouer entre la lecture et l'acceptation.
Une case à cocher que le serveur vérifie. Accorder sans cocher est refusé côté serveur, pas seulement par le navigateur : la page revient, la session reste vivante, rien n'a été accordé. Refuser, en revanche, n'exige aucune acceptation.
On ne redemande pas. Qui a déjà accepté le texte servi voit la date de son acceptation à la place de la case.
Ce qui est enregistré, et pourquoi c'est l'empreinte qui compte
Une ligne par compte et par texte accepté, dans la table privacy_notice_consents
(migration 0003 du serveur) : l'identifiant interne du compte, l'empreinte SHA-256
du texte exact affiché, le libellé de version que portait ce texte, et l'instant de la
première acceptation.
C'est l'empreinte, et non le libellé, qui décide si l'on redemande. Un libellé s'oublie au moment d'éditer le texte ; une empreinte non. Modifier la notice — un mot, un titre, ou sa traduction complète en français — change l'empreinte, ne correspond plus à aucune ligne, et fait donc réaccepter tout le monde, sans qu'aucune action d'exploitation ne soit nécessaire.
Réaccepter le même texte garde le premier instant : c'est celui-là qui a eu lieu. Accepter un texte différent ajoute une ligne au lieu d'en remplacer une, donc l'historique de ce qui a été accepté survit.
Modifier le texte
Il est dans garmin-mcp/internal/loginweb/pages/remote/privacy.html, en deux blocs
(résumé et texte intégral), en français. Le modifier ou le traduire est une édition de
ce seul fichier ; l'empreinte changeant, chacun le réacceptera. Pensez à remonter
PrivacyNoticeVersion dans garmin-mcp/internal/loginweb/privacy.go pour que la ligne
enregistrée porte aussi un libellé lisible. Le détail est dans
garmin-mcp/docs/privacy-notice.md.
Tout le login distant est en français, pas seulement le bloc de notice : les sept
pages de garmin-mcp/internal/loginweb/pages/remote/ — annonce, identifiants Garmin,
code à usage unique, consentement, attente de validation, connexion expirée, page
terminale — et les messages d'erreur que ces pages affichent. Le profil « loopback », celui de la commande
garmin-mcp login que seul l'exploitant utilise depuis son terminal, reste en anglais
comme en amont. C'est le prix à payer : chaque fichier traduit est un conflit potentiel à
la prochaine mise à jour du subtree, ce qui est acceptable pour des pages que vos
utilisateurs lisent et ne l'était pas pour un outil d'exploitation.
Le texte livré décrit ce que ce build fait réellement, vérifié contre le schéma. Si vous changez ce que le serveur stocke, la notice fait partie du changement — et c'est l'opérateur du déploiement qui reste responsable du traitement et de ce que la notice promet.
La page qui clôt le login
Un login réussi se termine par une redirection vers le client : la dernière page que la personne voit est celle de Claude, pas celle du serveur. La transaction est alors close et son cookie effacé — c'est voulu, une transaction terminée ne doit plus être adressable.
Conséquence : toute requête ultérieure sur une route /login… tombe sur une page
terminale, et c'est le cas normal quand la fenêtre d'autorisation est rouverte,
rechargée, ou revisitée avec le bouton « précédent » après coup. Cette page dit
maintenant ce qui s'est passé — « Si vous venez d'autoriser l'accès, tout s'est bien
passé » — au lieu du Nothing here amont, qui se lisait comme une panne alors que le
connecteur venait de se connecter.
Seules /authorize et les quatre routes /login… servent cette page. Une adresse
inconnue du déploiement obtient le 404 nu du serveur HTTP, sans mise en forme : si
quelqu'un vous montre une page blanche portant 404 page not found, il n'était pas dans
le login.
Côté interface
L'interface montre, par compte, la date de la dernière acceptation et sa version, ou une pastille Aucun pour un compte qui n'a jamais accepté de notice — l'état normal d'un compte antérieur à la mise en place. Deux tuiles comptent les deux populations, un filtre isole les comptes sans consentement, le détail liste toutes les acceptations avec leur empreinte, et l'export CSV porte les mêmes colonnes.
Écrire les séances dans Garmin
Par défaut, un déploiement est en lecture seule. Pour que Claude puisse créer, envoyer et planifier des séances dans le compte Garmin de la personne, deux choses doivent être vraies en même temps :
GARMIN_MCP_ENABLE_WRITE_TOOLS=true— sinon les outils d'écriture ne sont pas servis du tout ;le client OAuth déclare la portée
garmin:write, et la personne l'accorde sur la page de consentement, qui la lui affiche nommément.
GARMIN_MCP_ENABLE_WRITE_TOOLS=true
GARMIN_MCP_OAUTH_CLIENTS=[{"id":"claude-web-desktop","name":"Claude",
"redirect-uris":["http://127.0.0.1:33418/callback"],
"scopes":["garmin:read","garmin:write"],
"resources":["https://mcp.exemple.fr/mcp"],"public":true}]Les deux conditions sont nécessaires, et c'est ce qui rend le réglage sûr : activer les outils ne donne rien tant que personne n'a consenti, et consentir ne donne rien si le serveur ne les sert pas.
Le nom de la portée est un piège. Seuls
garmin:writeetgarmin:destructivecommandent les paliers (internal/policy/tier.go). Le manifeste amontcompat/tools.json, lui, nomme la portée par outilgarmin:workouts:write— un nom plausible, accepté partout, et qui n'autorise rien : la configuration l'accepte, le serveur l'annonce dansscopes_supported,/authorizela délivre, le jeton la porte, et la politique la refuse en silence, outil par outil. Le symptôme est un déploiement oùserver_infoaffiche la portée accordée et un palier restéread-only.e2e/writescope_test.gocompare les deux jetons et fige le bon nom.
La portée est large. garmin:write ouvre le palier d'écriture entier — 37
outils, pas seulement les séances. Dix concernent les séances (create_run_workout,
create_strength_workout, create_walk_run_workout, create_z2_walk_workout,
upload_workout, upload_workouts, update_workout, schedule_workout,
schedule_workouts, schedule_week) ; les 27 autres touchent le poids, la nutrition,
l'hydratation, les métadonnées d'activité, le matériel et les zones cardiaques. Le
compte passe de 108 outils visibles à 145.
Pour n'ouvrir que les séances, la portée ne suffit pas — il faut retirer les autres
nommément, avec GARMIN_MCP_TOOL_DENYLIST (réglage amont ; voir
garmin-mcp/docs/configuration.md) :
GARMIN_MCP_TOOL_DENYLIST=add_body_composition,add_gear_to_activity,add_hydration_data,add_weigh_in,add_weigh_in_with_timestamps,create_custom_food,create_manual_activity,create_strength_training_activity,download_activity_file,download_course_gpx,log_custom_food,log_food,remove_gear_from_activity,request_reload,set_activity_description,set_activity_event_type,set_activity_feel,set_activity_name,set_activity_strength_exercise_sets,set_activity_type,set_blood_pressure,set_heart_rate_zones,set_nutrition_daily_settings,set_perceived_effort,update_custom_food,upload_course,upsert_and_logUne liste à tenir à jour : un outil d'écriture ajouté par l'amont y sera absent, donc autorisé. Le réglage par outil de claude.ai (« Nécessite une approbation », capture 07) est plus souple mais reste côté client — c'est un garde-fou, pas une barrière.
Vérifier les deux moitiés, sans deviner. doctor ne connaît que la première —
ce que l'exploitant a activé :
$ docker compose exec garmin-mcp garmin-mcp doctor | grep -A3 "tool tiers"
tool tiers:
write: enabled
destructive: disabledLa seconde — ce que le jeton de la personne autorise réellement — se lit dans l'outil
server_info, depuis Claude, et c'est le seul endroit où les deux moitiés se
rencontrent :
Champ | Lecture seule | Écriture effective |
Paliers activés |
|
|
Scopes accordés |
|
|
Outils visibles | 108 | 145 |
Un palier resté read-only alors que la portée d'écriture apparaît dans les scopes
accordés signale l'une des deux causes : GARMIN_MCP_ENABLE_WRITE_TOOLS n'est pas pris
en compte, ou la portée accordée n'est pas garmin:write.
Supprimer reste impossible. delete_workout et unschedule_workout sont classés
destructifs : ils demandent GARMIN_MCP_ENABLE_DESTRUCTIVE_TOOLS=true et la portée
garmin:destructive, ni l'un ni l'autre activés ici. Les deux portées sont
indépendantes : garmin:write n'implique pas garmin:destructive.
Changer les portées d'un client est une ré-autorisation : les jetons existants ont été émis pour les anciennes, et chaque personne devra repasser par la page de consentement. La notice de confidentialité, elle, n'a pas à changer — elle dit déjà que le client reçoit un jeton « limité aux permissions affichées sur cette page », ce qui reste exact quelles que soient ces permissions.
La validation des comptes
Par défaut, un nouveau compte n'est utilisable qu'une fois validé dans l'interface. Quelqu'un qui se connecte avec ses identifiants Garmin obtient un compte, voit une page qui le lui dit, et n'obtient rien d'autre : aucun consentement enregistré, aucun code d'autorisation, aucun jeton.

Où | Ce qui se passe |
Page de consentement | un compte non validé y accède normalement : un bandeau annonce l'attente, et il peut accepter la notice dès maintenant. Son acceptation est enregistrée. |
Au clic sur Autoriser | l'acceptation est écrite, puis la porte s'applique : compte non validé → page « en attente », transaction OAuth close, aucun jeton. |
À chaque requête MCP | la lecture du jeton d'accès refuse un compte non validé. Retirer une validation coupe l'accès à la requête suivante, pas au prochain login. |
Dans l'interface | colonne Validation, filtre, tuile, et les boutons Valider / Bloquer / Remettre en attente. |
L'ordre compte : accepter la notice est la décision de la personne sur ses propres données, et elle n'a pas à attendre la décision de l'exploitant sur son accès. Une fois validée, la personne relance l'autorisation depuis son client et la notice ne lui est pas redemandée.
Trois états : en attente (personne n'a décidé), validé, bloqué. « En attente »
n'est jamais stocké — c'est l'absence de ligne dans account_approvals, si bien qu'un
compte tout juste créé attend par construction, sans que rien n'ait eu à s'exécuter. La
migration 0004 valide en revanche tous les comptes qui existaient déjà : activer la
porte sur un déploiement en cours ne met personne dehors.
Le réglage GARMIN_MCP_REQUIRE_ACCOUNT_APPROVAL (défaut true) commande la porte. À
false, on retrouve le comportement amont : un compte est utilisable dès que son login
Garmin a réussi.
Être prévenu par e-mail
Rien n'annonce un compte retenu : sans notification, on l'apprend quand la personne se plaint. Renseignez un serveur SMTP et le déploiement envoie un message par compte mis en attente.
Variable | Rôle |
| le serveur. Vide n'envoie rien, c'est le défaut. |
|
|
| le compte d'envoi. Avec Gmail, l'adresse complète. |
| fichier contenant le secret, lisible par le seul propriétaire. Avec Gmail, un mot de passe d'application. |
| l'expéditeur. Vide reprend |
| les destinataires, séparés par des virgules. |
|
|
| lien vers l'interface, placé dans le message. |
Le secret n'est pas une variable d'environnement, et ce n'est pas un oubli : le serveur amont refuse par principe qu'un identifiant soit configurable — un test le vérifie — donc seul le chemin d'un fichier l'est, comme pour la clé maîtresse. Le fichier doit appartenir au compte de service et n'être lisible que par lui, sinon le démarrage échoue. Avec Gmail, générez un mot de passe d'application (la validation en deux étapes doit être active sur le compte) et écrivez-le seul dans ce fichier.
Trois garanties, chacune couverte par un test :
Un envoi ne retarde ni ne fait échouer un login. Il part en tâche de fond, sur un contexte détaché de la requête, et ses erreurs sont journalisées puis abandonnées.
Un compte n'est annoncé qu'une fois par intervalle (six heures). Trois tentatives de connexion font un seul message ; un retour une semaine plus tard fait un rappel. Un envoi qui échoue ne consomme pas l'intervalle.
Une configuration incomplète refuse de démarrer. Nommer un serveur et oublier les destinataires est une erreur qu'on ne découvrirait qu'en ne recevant rien.
Le message contient l'adresse e-mail du compte, son identifiant interne et l'heure — aucune donnée Garmin. La notice de confidentialité le dit, puisque cette adresse transite alors par un tiers que vous avez choisi.
L'interface écrit, mais une seule table
C'est la seule exception à la lecture seule, et elle n'est pas une convention de code :
la connexion d'écriture installe un autorisateur SQLite qui refuse, avant exécution,
toute écriture ailleurs que dans account_approvals et toute modification de schéma. Un
test le vérifie en essayant huit requêtes interdites.
La route d'écriture est POST /api/accounts/{id}/approval, protégée en plus contre une
écriture déclenchée depuis un autre site : elle n'accepte que du JSON et refuse un
en-tête Origin qui ne désigne pas l'interface — nécessaire parce que le navigateur
rejoue tout seul l'authentification HTTP Basic.
Si l'interface tourne séparément avec le volume monté en lecture seule, la validation est
impossible : la route répond 409 en le disant, et la consultation continue de marcher.
Sécurité
Ce qui suit concerne l'interface web. Le modèle de menace du serveur MCP lui-même — isolation des
comptes, chiffrement des jetons, gestion des clés — est celui du projet amont, décrit dans
garmin-mcp/docs/threat-model.md.
Lecture seule, sauf une table. Les connexions de consultation sont ouvertes en
mode=roavecPRAGMA query_only, et un test vérifie qu'unDELETEéchoue. La seule écriture de toute l'interface est la validation des comptes, bornée àaccount_approvalspar un autorisateur SQLite — voir « La validation des comptes ».Conteneur non privilégié. L'entrypoint n'est root que le temps d'ajuster l'appartenance de
/data, puis redescend sur un compte de service viasetpriv. La racine du système de fichiers peut rester en lecture seule (read_only: truedans le compose),/dataest en0700, et les deux ports servis sont publiés sur la boucle locale.Deux ports, deux publics.
8180est l'endpoint MCP et les pages de login OAuth, destinés aux utilisateurs ;8080est l'interface d'exploitation, qui affiche leurs adresses e-mail. Ne les exposez pas au même public.Aucun secret ne sort. Les colonnes
*_hash(empreintes) et*_sealed(enveloppes chiffrées) ne sont jamais sélectionnées ni renvoyées ; un test parcourt les réponses de l'API pour s'en assurer.Authentification obligatoire. Le service refuse de démarrer sans
WEBUI_PASSWORDniWEBUI_API_TOKEN, saufWEBUI_ALLOW_ANONYMOUS=1assumé explicitement. Les comparaisons de secrets passent parsecrets.compare_digest.Données personnelles. L'interface affiche des adresses e-mail : exposez-la sur une adresse privée, derrière un reverse proxy en TLS, et activez
WEBUI_MASK_EMAILS=1si un affichage partiel suffit. La page envoienoindex, nofollow.
Démarrage rapide
cp .env.example .env # renseignez au minimum WEBUI_PASSWORD et l'URL publique
docker compose up -d --buildL'image compile le serveur Go depuis garmin-mcp/ puis l'embarque avec l'interface web.
L'interface écoute sur 127.0.0.1:8080, l'endpoint MCP sur 127.0.0.1:8180. Au premier
démarrage, le serveur crée sa clé maîtresse, migre sa base et commence à servir ; l'interface
affiche « base introuvable » les quelques secondes qui précèdent.
Essai sur un poste, sans reverse proxy
Le serveur d'autorisation refuse de nommer un émetteur en clair : l'URL publique doit être
https, et aucun override ne change cela (allow-insecure-http ne lève que le contrôle sur
l'écoute et l'origine). Pour un essai local, laissez le conteneur terminer le TLS avec un
certificat auto-signé :
GARMIN_MCP_SELF_SIGNED_TLS=1 \
GARMIN_MCP_PUBLIC_URL=https://127.0.0.1:8180/mcp \
GARMIN_MCP_BIND_ADDRESS=127.0.0.1:8180 \
GARMIN_MCP_OAUTH_CLIENTS='[{"id":"claude-desktop","name":"Claude Desktop","redirect-uris":["http://127.0.0.1:33418/callback"],"scopes":["garmin:read"],"resources":["https://127.0.0.1:8180/mcp"],"public":true}]' \
WEBUI_PASSWORD=demo \
docker compose up --buildLe certificat est écrit une fois dans /data/tls et n'est jamais remplacé. Il est fait pour un
essai, pas pour une mise en production : un client MCP refusera une autorité inconnue.
En production
Mettez un reverse proxy TLS devant, et donnez au serveur l'URL publique de ce proxy :
Réglage | Valeur |
|
|
|
|
| le réseau du proxy, sans quoi aucun en-tête |
| au moins un client ; il n'y a pas d'enregistrement dynamique. |
Alternative sans proxy : montez vos propres GARMIN_MCP_TLS_CERT_FILE et
GARMIN_MCP_TLS_KEY_FILE, le serveur termine alors le TLS lui-même.
Sauvegarde. La base et la clé maîtresse sont les deux moitiés d'une même sauvegarde : une base
sans sa clé est illisible. Sauvegardez /data en entier, avec le processus arrêté ou via la
sauvegarde en ligne de SQLite. Voir garmin-mcp/docs/operations.md.
Ne lancer qu'un service
RUN_SERVICES vaut les-deux (défaut), mcp ou webui. Deux conteneurs issus de la même image,
l'un en mcp et l'autre en webui sur le même volume, sont une configuration valide — le second
bascule alors sur une copie temporaire de la base si le volume lui est monté en lecture seule, et
la validation des comptes n'est alors pas possible depuis ce conteneur.
En local, sans conteneur
python -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt
# Pour essayer l'interface sans base de production :
python scripts/demo_database.py /tmp/demo.db
WEBUI_DATABASE_PATH=/tmp/demo.db WEBUI_PASSWORD=demo python -m app
# http://127.0.0.1:8080 — identifiants : admin / demoLe serveur MCP seul se compile comme n'importe quel programme Go :
cd garmin-mcp && go build ./cmd/garmin-mcp && ./garmin-mcp tools list | grep ' tools:'Configuration
Serveur MCP
Chaque réglage de garmin-mcp a une variable d'environnement : la clé en majuscules, tirets
remplacés par des soulignés, préfixée GARMIN_MCP_. La liste complète est dans
garmin-mcp/docs/configuration.md. Les principales pour cette image :
Variable | Défaut dans l'image | Rôle |
| — | Obligatoire. URL publique de l'endpoint MCP. Doit être |
| — | Obligatoire. Registre des clients OAuth, en JSON. |
|
| écoute dans le conteneur. |
|
| autorise une écoute et une origine en clair hors boucle locale. Ne rend jamais un émetteur en clair acceptable. |
| vide | réseaux dont les en-têtes |
| — | le serveur termine le TLS lui-même. |
|
| ajout de cette image : fabrique un certificat auto-signé dans |
|
| base SQLite, partagée avec l'interface. |
|
| la valeur sélectionne le répertoire ; le nom de fichier appartient au serveur. |
|
| état hors base. |
|
| sert les outils d'écriture. Sans portée accordée, ils restent refusés — voir « Écrire les séances ». |
|
| sert les outils de suppression. Exige aussi |
|
| ajout de ce dépôt : un nouveau compte attend une validation dans l'interface. |
Conteneur
Variable | Défaut | Rôle |
|
|
|
|
| compte de service auquel l'entrypoint redescend après avoir ajusté |
Interface web
Toutes les variables sont facultatives sauf le secret d'accès.
Variable | Défaut | Rôle |
|
| base SQLite de garmin-mcp (sa clé |
|
| identifiant HTTP Basic. |
| — | mot de passe HTTP Basic. Obligatoire (voir |
| — | jeton porteur pour l'API JSON ( |
|
| ouvre l'interface sans authentification. À réserver à un proxy qui authentifie déjà. |
|
| masque les e-mails ( |
|
| seuil « actif », en jours. |
|
| seuil « inactif », en jours. Doit dépasser |
|
| durée de validité de la copie quand la lecture directe échoue. |
|
| titre affiché. |
|
| écoute HTTP. |
|
| niveau de log uvicorn. |
API
Toutes les routes /api sauf /api/health exigent une authentification.
Route | Description |
| l'interface. |
| sonde publique : |
| chemin, taille et mode d'accès de la base, seuils, libellés des signaux. |
| compteurs agrégés (total, liés à Garmin, actifs, vus sous 24 h…). |
| liste paginée. Paramètres : |
| même liste au format CSV, mêmes filtres. |
| détail d'un compte : signaux, acceptations de la notice, consentements, familles de jetons, audit. |
| valide, bloque ou remet en attente. Corps JSON |
| documentation OpenAPI générée. |
curl -u admin:motdepasse 'http://127.0.0.1:8080/api/accounts?status=dormant'
curl -H "Authorization: Bearer $WEBUI_API_TOKEN" http://127.0.0.1:8080/api/statsDéveloppement
pip install -r requirements-dev.txt
ruff check . && ruff format --check .
pytest -q # interface web
cd garmin-mcp && go test ./... # serveur MCP (suite amont)
shellcheck docker/entrypoint.sh
docker build -t gamin-mcp-webui:test .La CI fait les quatre : lint et tests Python sur 3.11 et 3.12, compilation du serveur Go,
shellcheck sur l'entrypoint, puis construction de l'image et démarrage réel du conteneur jusqu'à
ce que sa sonde de santé passe.
Les tests construisent une base SQLite au schéma de garmin-mcp (tests/schema.sql) peuplée de
cas limites : compte actif, inactif, dormant, jamais connecté, compte dont les jetons ont été
purgés. tests/schema.sql reproduit les tables et colonnes lues par app/queries.py ; si le
schéma amont évolue, c'est le fichier à mettre à jour — un test échouera alors immédiatement.
Suivre l'amont
garmin-mcp/ est un git subtree du dépôt public. Pour récupérer une version plus récente :
git remote add garmin-mcp-upstream https://github.com/tamcore/garmin-mcp.git # une seule fois
git fetch garmin-mcp-upstream master
git subtree pull --prefix=garmin-mcp garmin-mcp-upstream master --squashLe sous-répertoire reste modifiable comme le reste du dépôt ; git subtree push renvoie ces
modifications vers un fork amont si vous en tenez un. Après une mise à jour, vérifiez que
tests/schema.sql correspond toujours aux migrations amont — c'est ce que testent les tests de
l'interface.
Compatibilité
Vérifié contre le schéma de garmin-mcp après les migrations 0001_initial,
0002_oauth_contract, 0003_privacy_notice_consent et 0004_account_approval (les deux
dernières ajoutées par ce dépôt). L'interface ne dépend que des tables principals,
garmin_token_sets, consents, oauth_clients, auth_codes, token_families,
mcp_tokens, audit_events, privacy_notice_consents et account_approvals, et ne lit
aucune colonne chiffrée.
Licence
MIT pour ce dépôt. garmin-mcp/ est une copie du projet amont de Philipp Born, sous licence MIT
également : sa licence et ses notices d'origine voyagent avec le code, dans garmin-mcp/LICENSE et
garmin-mcp/THIRD_PARTY_NOTICES.md. Ce dépôt n'est pas un produit officiel du projet amont.
This server cannot be deployed
Maintenance
Related MCP Connectors
Remote MCP server for training, nutrition, wellness, and performance data with OAuth 2.0.
Hosted MCP server with managed OAuth for 15+ toolkits: Google Workspace, Fitbit, Oura, Kalshi, etc.
Multi-tenant hosted MCP server for Oura Ring — 21 read-only tools, OAuth per user.
OAuth MCP for Google, Meta, X, LinkedIn, Reddit, TikTok, GSC, GA4, WordPress and GHL.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceConnects MCP clients to Garmin Connect data, enabling queries about activities, sleep, heart rate, body battery, and training status.MIT
- AlicenseNot gradedqualityBmaintenanceMCP server for Garmin Connect that enables users to access and manage their personal health and fitness data, including daily summaries, heart rate, sleep, HRV, stress, body composition, activities, and training readiness, with secure per-user authentication.Apache 2.0
- FlicenseAqualityCmaintenanceProvides read-only access to your Garmin Connect health data, including sleep, HRV, body battery, stress, training readiness, and activities, through an MCP server.14-
- AlicenseNot gradedqualityBmaintenanceExposes Garmin Connect data through MCP, enabling retrieval of activities, health metrics, training status, and workouts, with OAuth authentication and confirmed write operations.MIT