Retour au thème
MCP2026-08-10 · 9 min de lecture

On a retiré FastMCP et les sessions de notre serveur MCP

La révision 2026-07-28 supprime le handshake et la session de protocole. Chronologie des trois transports, schéma avant/après, échanges HTTP réels, et les pièges que le sans état introduit.

Amine Harrak
Founder, Leebr Data Consulting · LinkedIn
TL;DR, l'essentiel en six lignes

Le serveur MCP d'Energy Data Platform a été migré : abandon de FastMCP pour le SDK Python officiel v2, et passage à la révision 2026-07-28 du protocole, qui supprime la session. Avant, un handshake initialize et un identifiant de session à porter à chaque appel compliquaient le passage à l'échelle et les redémarrages. Désormais chaque requête se suffit à elle-même et n'importe quelle instance peut répondre. Le retrait de FastMCP n'est pas une critique du projet : une contrainte de version bloquait sur le SDK v1, et le verrou de dépendances perd 337 lignes nettes. Le vrai piège du sans état, c'est le rejeu d'un appel qui duplique une écriture : les outils doivent être idempotents. L'authentification par requête existait déjà, et les données métier restent en base.

On vient de migrer le serveur MCP d'Energy Data Platform. Deux changements en un : on a retiré FastMCP au profit du SDK officiel version 2, et on est passé à la révision 2026-07-28 du protocole, qui supprime la session. Voici ce que ça veut dire concrètement, et ce que ça a coûté.

MCP en une phrase

Le Model Context Protocol est la façon standard de donner des outils à un modèle. Au lieu de laisser Claude inventer un tarif réglementé, on lui expose une fonction lookup_tarif ; il l'appelle, un serveur répond avec la vraie valeur, et le modèle rédige à partir de ce résultat. Le serveur MCP, c'est le programme qui héberge ces outils.

Chez nous, ce serveur expose les calculs métier de l'énergie : simulation tarifaire, lecture des grilles en vigueur, analyse de facture. Le chatbot ne calcule jamais lui-même, c'est la règle qui rend ses réponses auditables.

Trois transports en trois ans

Pour comprendre ce qui change, il faut distinguer trois étapes, souvent confondues.

  • 2024, HTTP+SSE. Deux points d'entrée : un GET /sse qui laissait un flux ouvert en permanence pour que le serveur puisse pousser des messages, et des POST /messages séparés pour envoyer les demandes.
  • 2025, Streamable HTTP. Un seul point d'entrée /mcp. Le client commence par un handshake initialize, le serveur peut lui attribuer un identifiant de session renvoyé dans l'en-tête Mcp-Session-Id, et chaque requête suivante le reporte. Voir la spécification des transports 2025.
  • 2026-07-28. Le handshake et la session de protocole disparaissent. Chaque requête est autonome. Voir l'annonce officielle.

D'où l'on vient

Notre serveur était sur la révision 2025, pas sur l'ancien transport 2024. Le schéma ci-dessous compare donc 2025 et 2026-07-28, ce qui est la vraie bascule que nous avons vécue.

Ce qu'était une session, et ce qu'elle coûtait

En 2025, le serveur pouvait se souvenir. Se souvenir de qui avait fait initialize, de l'état de négociation des capacités, des abonnements en cours. Trois conséquences, qu'il faut énoncer sans les exagérer :

  • Le passage à l'échelle se complique. Deux instances derrière un répartiteur, et un appel qui atterrit sur la mauvaise ne retrouve pas sa session. On s'en sort avec de l'affinité de session ou un magasin de sessions partagé, mais le basculement en cas de panne devient nettement moins simple.
  • Un redémarrage invalide les sessions MCP. Pas nécessairement la conversation métier, qui vit ailleurs, en base. Le client doit refaire initialize, ce qui est récupérable, mais qu'il faut avoir prévu.
  • Il y a un état à entretenir, donc à expirer, à nettoyer, et à surveiller quand des clients disparaissent sans fermer proprement.

Ce que change la révision 2026-07-28

Il n'y a plus de handshake ni d'identifiant de session. Chaque requête porte elle-même ce qu'il faut pour être traitée : la version du protocole, la méthode, le nom de l'outil, et les informations client dans _meta.

Avant · révision 2025, session négociéeClient MCPServeur MCPSession négociéeétat conservé côté serveurMcp-Session-Id: 8f3a1c7ePOST /mcp · initialize200 OK · Mcp-Session-IdPOST /mcp · puis chaque appel porte Mcp-Session-IdLe serveur retient une session : affinité ou magasin partagé, et un état à expirer.Après · révision 2026-07-28, requêtes autonomesClient MCPServeur MCPaucune session de protocoleMCP-Protocol-Version: 2026-07-28POST /mcp · appel 1 · Mcp-MethodPOST /mcp · appel 2 · Mcp-MethodPOST /mcp · appel 3 · Mcp-MethodChaque appel se suffit à lui-même : n'importe quelle instance peut répondre.

Sur le fil, avant

http
POST /mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{ ... }}

--- reponse ---
HTTP/1.1 200 OK
Mcp-Session-Id: 8f3a1c7e-4b21-4d9a-9f0c-2e7d5a1b3c44

--- puis chaque appel reporte la session ---
POST /mcp HTTP/1.1
Mcp-Session-Id: 8f3a1c7e-4b21-4d9a-9f0c-2e7d5a1b3c44

{"jsonrpc":"2.0","id":2,"method":"tools/call",
 "params":{"name":"lookup_tarif","arguments":{"puissance_kva":36}}}

Sur le fil, après

http
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: lookup_tarif
Content-Type: application/json

{"jsonrpc":"2.0","id":1,"method":"tools/call",
 "params":{"name":"lookup_tarif",
           "arguments":{"puissance_kva":36},
           "_meta":{"client":{"name":"edp-chatbot","version":"1.4.0"}}}}

Plus de handshake préalable, plus d'en-tête de session. La méthode et le nom de l'outil remontent dans les en-têtes, ce qui permet au passage de router ou de journaliser sans avoir à ouvrir le corps de la requête.

Ce que stateless_http=True fait vraiment

La révision 2026-07-28 est sans état par construction : ce n'est pas un drapeau qui la rend telle. Dans le SDK Python, stateless_http=True configure principalement le comportement servi aux clients restés en 2025 et avant, pour qu'ils soient traités sans que le serveur conserve leur session. Voir le guide de déploiement du SDK Python.

Le diff réel

diff
- """EDP MCP Server — FastMCP with tools, resources, auth, and audit logging."""
+ """EDP MCP Server — official MCP SDK with tools and resources."""

- from fastmcp import FastMCP
+ from mcp.server import MCPServer

- mcp = FastMCP("edp-mcp-server")
+ mcp = MCPServer("edp-mcp-server", version="0.2.0")

- mcp.run(transport="streamable-http", host=host, port=port)
+ mcp.run(transport="streamable-http", host=host, port=port, stateless_http=True)
diff
- "fastmcp>=0.3.0",
- "mcp>=0.10.0",
+ "mcp>=2,<3",

Pourquoi FastMCP est sorti, la vraie raison

Ce n'est pas un procès en obsolescence. La version que nous utilisions, FastMCP 3.2.4, imposait mcp>=1.24,<2. Elle nous bloquait donc sur le SDK v1, et empêchait mécaniquement le passage au SDK Python v2 dont nous avions besoin pour la révision 2026.

Depuis, FastMCP 4 prend en charge MCP 2026. Rester sur FastMCP était donc redevenu possible. Notre décision de le retirer est un choix de simplification architecturale : une couche de moins entre notre code et le protocole, une implémentation au lieu de deux. Ce n'est pas une critique du projet, qui a rendu de vrais services pendant un an.

Effet mesuré sur le verrou de dépendances : 401 lignes supprimées, 64 ajoutées, soit 337 lignes nettes en moins.

Ce qu'on gagne concrètement

  • N'importe quelle instance répond à n'importe quel appel. Plus besoin d'affinité ni de magasin de sessions partagé pour ajouter un serveur.
  • Le basculement en cas de panne devient trivial. Il n'y a rien à répliquer entre instances.
  • Un déploiement ne demande plus de renégociation. Attention toutefois : les appels en vol pendant le redémarrage échouent quand même, le client doit les rejouer. Ce qui disparaît, c'est l'étape de renégociation, pas l'échec transitoire.
  • Une dépendance de moins, donc une surface de mise à jour et de sécurité réduite d'autant.

Sur la rétrocompatibilité

Le mode servi aux anciens clients couvre les opérations principales, celles dont nous avons besoin : lister les outils, les appeler, lire des ressources. Certaines fonctions historiques peuvent en revanche être limitées, notamment le canal retour du serveur vers le client, la reprise d'un flux interrompu et les notifications. Si votre intégration en dépend, vérifiez avant de basculer.

Les pièges

Le rejeu, et les effets de bord en double

C'est le piège le plus sérieux, et il arrive avec le sans état. Puisqu'un appel peut échouer en vol et que le client est encouragé à le rejouer, un même appel peut être exécuté deux fois. Sur une lecture, c'est sans conséquence. Sur une écriture, ça l'est beaucoup moins.

La parade : rendre les outils idempotents, ou faire porter à l'appelant une clé d'idempotence que le serveur mémorise le temps nécessaire pour reconnaître un doublon. À surveiller en priorité sur les opérations de paiement, de suppression, d'envoi et de création.

L'état applicatif, à ne pas confondre avec la session

Ce qui disparaît, c'est la session de protocole. Les données métier restent en base : grilles tarifaires, dossiers clients, historique des conversations.

Le réflexe à éviter, c'est de compenser en envoyant tout l'historique de conversation à un outil. C'est coûteux, bruyant, et ça expose des données dont l'outil n'a pas besoin. La bonne pratique est de passer des identifiants explicites que le serveur sait résoudre : dossier_id, basket_id, browser_id. L'outil va chercher ce qu'il lui faut, et rien d'autre.

L'authentification

Une remarque qu'on lit souvent et qui est trompeuse : « il faut maintenant authentifier chaque requête ». C'était déjà le cas. En HTTP, chaque requête doit porter et faire vérifier ses justificatifs ; une session MCP n'a jamais été un cache d'authentification. Ce qui change, c'est qu'on n'a plus la tentation de s'appuyer dessus. Notre journal d'audit enregistre désormais l'appel plutôt que la session, ce qui est plus verbeux et nettement plus utile quand il faut prouver qui a demandé quoi.


Sources

Retour d'expérience de l'auteur, adossé à nos déploiements et à notre banc R&D. Les opinions et exemples sont les siens.