Skip to main content
Glama

idf-mcp — « c'est quand le prochain RER ? »

Serveur MCP local qui branche Claude (Code et Desktop) sur le temps réel du RER B, via les API publiques d'Île-de-France Mobilités (plateforme PRIM).

> c'est quand le prochain RER pour Paris, et j'arrive à quelle heure ?

La Hacquinière → Denfert-Rochereau · lun. 7 sept. 20:38
• EKLI 20:42 → Denfert-Rochereau 21:19 · 37 min · direct · terminus Aéroport Charles de Gaulle 2 · dans 4 min
• DEFI 20:57 (+3) → Denfert-Rochereau 21:34 (+3) · 37 min · direct · terminus Mitry-Claye · dans 19 min ⚠ retards annoncés

Avec une correspondance ou une adresse, chaque étape est détaillée :

> je dois être au 43 rue Saint-Dominique à 10 h

La Hacquinière → 43 Rue Saint-Dominique 75007 Paris · mar. 8 sept. 08:50
• EPAF 08:59 → 43 Rue Saint-Dominique 75007 Paris 09:48 · 49 min · 1 corresp. · dans 9 min
    ↳ marche 2 min → La Hacquinière
    ↳ RER B (EPAF) · La Hacquinière 08:59 → Saint-Michel Notre-Dame 09:32 · 12 arrêts · dir. Aéroport CDG 2
    ↳ marche 4 min → Saint-Michel Notre-Dame
    ↳ attente 2 min
    ↳ RER C (ELBA) · Saint-Michel Notre-Dame 09:38 → Invalides 09:44 · 2 arrêts · dir. Versailles Rive Gauche
    ↳ marche 4 min → 43 Rue Saint-Dominique 75007 Paris

1. Obtenir une clé PRIM (5 min)

  1. Créer un compte gratuit sur https://prim.iledefrance-mobilites.fr.

  2. Générer une clé dans « Mes jetons ».

  3. Souscrire aux API — une clé ne donne accès qu'à ce à quoi on a souscrit, c'est la cause n°1 de 401 :

    • « Prochains passages (requête unitaire) » → departures_board

    • « Calculateur – accès générique v2 » → next_trains_to_paris, plan_journey

    • « Messages info trafic v2 » → line_status

Quota : environ 20 000 requêtes par jour et par API. Le serveur met en cache chaque URL 25 s, donc une rafale de questions ne coûte qu'un appel.

Related MCP server: openbusdata

2. Installer

npm install
npm run build     # optionnel : évite de dépendre de tsx au lancement
npm test

Claude Code

claude mcp add --scope user idfm \
  -e IDFM_API_KEY=ta_cle \
  -e IDFM_WORK_STOP=473890 \
  -- node /chemin/vers/idf-mcp/dist/index.js

Sans npm run build, remplacer la dernière ligne par -- npx tsx /chemin/vers/idf-mcp/src/index.ts.

Claude Desktop

Dans ~/Library/Application Support/Claude/claude_desktop_config.json :

{
  "mcpServers": {
    "idfm": {
      "command": "node",
      "args": ["/chemin/vers/idf-mcp/dist/index.js"],
      "env": {
        "IDFM_API_KEY": "ta_cle",
        "IDFM_WORK_STOP": "473890"
      }
    }
  }
}

Vérifier sans Claude

IDFM_API_KEY=ta_cle npm run inspector

3. Configuration

Variable

Défaut

Rôle

IDFM_API_KEY

—

Obligatoire. Clé PRIM.

IDFM_HOME_STOP

47046 (La Hacquinière)

Gare de départ par défaut.

IDFM_WORK_STOP

473890 (Denfert-Rochereau)

Ce que « Paris » veut dire.

IDFM_CACHE_TTL

25

Cache par URL, en secondes.

IDFM_TIMEOUT_MS

10000

Timeout HTTP.

Autres gares parisiennes de la B : 45102 Châtelet - Les Halles · 462394 Gare du Nord · 43833 Luxembourg · 44877 Saint-Michel Notre-Dame · 44500 Port Royal · 473843 Cité Universitaire.

Voir .env.example. Ces variables se passent au serveur via la config MCP (-e ou le bloc env), pas par un fichier .env lu au démarrage.

4. Les tools

Tool

Ce qu'il donne

Source

next_trains_to_paris

Le principal. Départ, retard, heure d'arrivée, durée, mission, terminus, perturbations.

Navitia journeys

departures_board

Tableau de gare : heure attendue, voie, train à quai, trains supprimés. Pas d'heure d'arrivée.

SIRI stop-monitoring

line_status

Perturbations en cours et à venir sur la B.

Navitia line_reports, repli SIRI general-message

plan_journey

Itinéraire quelconque, tous modes, avec arrive_by. Accepte une adresse postale.

Navitia journeys + BAN

find_stop

Recherche d'arrêt par nom → identifiant + lignes. Sans clé.

Open data IDFM

Prompt /prochain-rer : déclenche next_trains_to_paris avec les valeurs par défaut.

Les gares s'écrivent par identifiant (473890), par nom (Denfert-Rochereau), sans accent (hacquiniere) ou par alias (denfert, chatelet, cdg, st remy). plan_journey accepte en plus une adresse postale (43 rue Saint-Dominique, Paris) et des coordonnées (2.317444;48.859821) — inutile de chercher la station la plus proche à la main. Les horaires acceptent 18:30, 18h30, demain, demain 08:15 ou une date ISO.

Exemples de questions

  • « c'est quand le prochain RER pour Paris ? »

  • « je pars dans 20 min, j'arrive à quelle heure à Châtelet ? »

  • « il y a des retards sur la B ? »

  • « le train est à quai ? sur quelle voie ? »

  • « pour être à Denfert à 9 h, je pars à quelle heure ? » → plan_journey avec arrive_by

  • « je dois être au 43 rue Saint-Dominique demain à 9 h » → plan_journey, adresse telle quelle

  • « le dernier train pour Saint-Rémy ce soir ? » → departures_board sens saint-remy

5. Ce que fait le code

src/
  index.ts          bootstrap MCP (stdio), les 5 tools et le prompt
  config.ts         lecture de l'environnement
  time.ts           Europe/Paris : Navitia rend du local sans offset, SIRI de l'ISO
  format.ts         rendu texte compact (« EKLI 20:42 (+2) → … · dans 4 min »)
  idfm/client.ts    fetch + apikey + cache TTL + erreurs traduites (401/404/429/5xx/timeout)
  idfm/siri.ts      stop-monitoring, general-message
  idfm/navitia.ts   journeys, line_reports
  idfm/stops.ts     les 47 gares de la B en dur (ids + coordonnées) + ordre de la ligne
  idfm/geocode.ts   adresse → coordonnées (Base Adresse Nationale, sans clé)
tests/              99 tests, dont un test d'intégration qui démarre le serveur
scripts/            capture-fixtures.sh

Quelques décisions qui méritent une phrase :

  • L'heure d'arrivée vient de Navitia, pas de SIRI. SIRI ne sait dire que « ce train part d'ici à telle heure » ; toutes les missions de la B ne desservent pas les mêmes gares, donc SIRI seul ne permet pas de conclure.

  • Le sens « vers Paris » est calculé, pas deviné. Les 47 gares portent une position le long de la ligne (branches comprises), et Paris intra-muros est un intervalle sur le tronc commun. Depuis La Hacquinière « vers Paris » exclut Saint-Rémy ; depuis Le Bourget il exclut CDG et Mitry. Depuis une gare parisienne, la question n'a pas de sens et le tool le dit au lieu de filtrer.

  • Les trains supprimés sont affichés, marqués ⛔ SUPPRIMÉ. Les masquer serait le plus sûr moyen de rater une info importante.

  • L'heure d'arrivée est celle à destination, marche finale comprise, pas celle du dernier train : sur « je dois y être à 10 h », c'est l'écart qui compte. Le détail des étapes donne l'heure exacte de chaque train.

  • Le détail n'apparaît que s'il sert : correspondance à faire, ou marche d'au moins trois minutes. Un RER direct reste sur une seule ligne.

  • Une réponse vide n'est pas une erreur : la nuit, il n'y a simplement plus de train, et le message le dit.

  • Les identifiants et coordonnées des gares sont en dur (vérifiés contre l'open data IDFM le 2026-09-07) : pas d'appel réseau pour résoudre « denfert ».

  • Une station, une entrée. Le référentiel expose un identifiant par quai et par ligne : « Invalides » y figure 24 fois. Les résultats sont regroupés par nom et commune, ce qui évite de déclarer ambiguë chaque gare parisienne.

  • On tranche au lieu d'échouer. Quand plusieurs arrêts collent encore, le mieux classé est retenu (nom exact, puis mode lourd, puis nombre de lignes) et les autres sont affichés — refuser de choisir rendait le calcul inutilisable.

6. Développement

npm test              # suite complète
npm run test:watch
npm run typecheck
npm run dev           # lance le serveur en stdio (pour l'inspecteur)

Les fixtures livrées sont synthétiques : elles reproduisent la forme documentée des API, pas une capture réelle. Avec une clé en main :

IDFM_API_KEY=ta_cle ./scripts/capture-fixtures.sh
npm test

Le script écrase tests/fixtures/*.json avec de vraies réponses. Si un test casse à ce moment-là, c'est une bonne nouvelle : il a trouvé un écart entre la documentation PRIM et la réalité, et l'endroit exact à corriger.

Si le calculateur refuse les itinéraires :

IDFM_API_KEY=ta_cle ./scripts/diagnose.sh

Il teste une par une les causes possibles (filtre de ligne, identifiants d'arrêt, chemin appelé, souscription) et dit laquelle est la bonne.

7. Pièges connus

  • 401 sur Navitia alors que SIRI marche → l'API « Calculateur » n'est pas souscrite sur cette clé. Le message d'erreur du serveur le dit explicitement.

  • allowed_id[] est une liste blanche, pas un filtre. Tout ce qui n'y figure pas est interdit — y compris l'origine et la destination du trajet, ce qui fait répondre no_origin_nor_destination. Les deux gares sont donc toujours ajoutées à la liste ; et si le calculateur refuse malgré tout le filtre, il est abandonné et la réponse le signale plutôt que de ne rien rendre.

  • Le calculateur veut des coordonnées, pas des identifiants d'arrêt. PRIM n'expose que l'endpoint global de Navitia (/v2/navitia/journeys), et celui-ci ne sait résoudre que des lon;lat : lui passer un stop_area:IDFM:47046 répond invariablement no_origin_nor_destination, quel que soit le couple de gares. Les trajets sont donc demandés en coordonnées — embarquées pour les 47 gares de la B, cherchées dans l'open data sinon — tandis que les identifiants restent utilisés dans allowed_id[], où ils sont indispensables. En repli, le serveur retente en identifiants, puis sur /v2/navitia/coverage/{coverage}/journeys si cette coverage existe (/v2/navitia/coverage répond 404 sur au moins une clé PRIM, et l'ancienne fr-idf a été décommissionnée : elle est donc demandée, jamais devinée). La combinaison qui marche est mémorisée — les appels suivants coûtent une seule requête.

  • La Hacquinière a 3 points d'arrêt (ZDE) pour 1 zone d'arrêt (ZDA) — on travaille toujours au niveau ZDA (47046). Si SIRI ne renvoie rien pour une ZDA, departures_board bascule automatiquement sur les quais.

  • Changement d'heure : Navitia renvoie de l'heure locale sans offset. La conversion est testée sur la nuit du 25 octobre.

  • Un identifiant de quai n'est pas une zone d'arrêt. IDFM:22193 est un quai, IDFM:monomodalStopPlace:470540 une station : seul le second existe comme stop_area côté calculateur. Le premier donnait un no_origin_nor_destination très trompeur.

  • Le géocodage est bridé à l'Île-de-France. « Denfert » seul renvoie une avenue à La Rochelle avec un score honorable ; hors des départements 75 à 95, le résultat est rejeté.

  • Ne pas boucler depuis un hook : le cache de 25 s protège le quota d'une rafale, pas d'une boucle.

8. Sources

Available Tools

5 tools
departures_boardTableau de gare (temps réel)A

Tableau des prochains départs à un arrêt, comme les écrans sur le quai : heure attendue, retard, voie, train à quai ou non, trains supprimés. Ne donne PAS l'heure d'arrivée (utiliser next_trains_to_paris pour ça). Utile pour « il est où le train ? », « sur quelle voie ? », ou en repli si le calculateur est en panne. Défaut : La Hacquinière (47046), sens Paris.

Inputs: stop, count, direction.

find_stopRecherche d'arrêtA

Cherche un arrêt d'Île-de-France par nom : identifiant, lignes et modes desservis, regroupés par station. À utiliser quand un nom est inconnu ou pour lever un doute. Inutile pour une adresse postale : plan_journey la prend directement. N'utilise pas de clé API (données ouvertes).

Inputs: query (required), limit.

line_statusPerturbations du RER BA

État de la ligne : perturbations en cours et à venir (travaux, incidents, grèves, interruptions, suppressions). À utiliser quand l'utilisateur demande « il y a un problème sur la B ? » ou pour expliquer un trajet anormalement long.

Inputs: include_past.

next_trains_to_parisProchains RER vers ParisA

LE tool à utiliser pour « c'est quand le prochain RER pour Paris et j'arrive à quelle heure ? ». Donne, pour chaque prochain train La Hacquinière (47046) → Denfert-Rochereau (473890) : heure de départ réelle et retard, heure d'arrivée, durée, code mission, terminus et perturbations. Temps réel, RER B uniquement (pas de bus de rabattement). Les gares par défaut viennent de la configuration ; les préciser seulement si l'utilisateur parle d'une autre gare.

Inputs: to, from, when, count.

plan_journeyCalcul d'itinéraireA

Itinéraire temps réel en Île-de-France, tous modes et correspondances incluses. Les extrémités acceptent un nom d'arrêt MAIS AUSSI une adresse postale : passer « 43 rue Saint-Dominique, Paris » directement, sans chercher la station la plus proche. Pour « je dois être à X à 9 h », utiliser when avec arrive_by: true. Préférer next_trains_to_paris pour le trajet domicile → Paris habituel.

Inputs: to (required), from (required), when, count, +2 more.

Tool Schema Changelog

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

Loading tool schema changelog…

    TDQS

    A4.1/5.0

    Scored across 5 tools

    Disambiguation4/5

    Most tools have clearly distinct jobs: departures_board handles live platform/departure info, line_status handles disruptions, find_stop handles stop lookup, and plan_journey handles multimodal itineraries. next_trains_to_paris overlaps somewhat with plan_journey for the habitual commute, but the descriptions explicitly explain when to prefer each.

    Naming Consistency3/5

    All names are readable and use snake_case, but they mix noun-phrase resource names (departures_board, line_status, next_trains_to_paris) with verb-first command names (plan_journey, find_stop). There is no consistent verb_noun pattern across the set.

    Tool Count5/5

    Five tools is a well-scoped size for a transit information server. Each tool covers a distinct layer of functionality—next trains, live departures, line status, journey planning, and stop lookup—without unnecessary duplication.

    Completeness4/5

    The set covers the core transit domain well: departures, arrivals via next_trains_to_paris, disruptions, stop search, and full journey planning. Minor gaps exist, such as no dedicated tool for return-trip RER B status or line status for non-B lines, but plan_journey and line_status can work around these.

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      An MCP server for interacting with the Berlin Public Transport (BVG) API to search for locations and plan journeys. It provides real-time access to departures, arrivals, trip details, and vehicle tracking within Berlin's transit network.
      MIT
    • A
      license
      C
      quality
      C
      maintenance
      MCP server for the UK Bus Open Data Service, enabling timetable queries, stop search, route discovery, journey planning, and real-time bus tracking.
      16
      22 PyPI
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      MCP server for real-time German public transport data (HAFAS/VBN network). Enables querying departures, arrivals, journeys, and nearby stops using natural language.
      ISC