Spec MCP stateless : serveurs stateless ou stateful expliqués
La spec MCP du 2026-07-28 a supprimé l’échange initialize et l’en-tête Mcp-Session-Id. Cet article compare les serveurs stateless et stateful, montre où va désormais l’état des outils avec les handles, les tâches et les requêtes en plusieurs allers-retours, et liste les étapes de migration.
Chaque serveur MCP que vous avez construit avant cet été commence probablement de la même façon : un client se connecte, envoie initialize, attend la réponse, envoie initialized, et seulement alors peut faire un vrai travail. La révision 2026-07-28 du Model Context Protocol supprime ce rituel. Il n’y a plus de poignée de main, plus d’en-tête Mcp-Session-Id, et plus de session au niveau du protocole rattachée à un seul processus. Si vous faites tourner un serveur MCP derrière un répartiteur de charge, ou si vous avez reporté ce déploiement parce que les sessions collantes vous semblaient un piège, c’est le changement que vous attendiez.
Cet article détaille la spec MCP stateless, ce que signifient serveurs stateless ou stateful en pratique et, surtout, ce qu’il advient de l’état dont vos outils ont encore besoin. Vous verrez les champs et en-têtes exacts qui ont changé, une checklist de migration, et une courte section sur la façon dont une connexion de génération d’images s’inscrit dans le même schéma.
Ce qui a changé dans la spec du 2026-07-28
L’Agentic AI Foundation, le projet de la Linux Foundation qui pilote désormais MCP, a résumé cette version dans son billet de migration. En bref, le protocole a cessé de supposer une longue conversation entre un client et un serveur, et traite désormais chaque appel comme une requête HTTP classique.
La poignée de main a disparu
Dans le protocole de l’époque 2025, la première chose que faisait un client était de négocier. La requête initialize transportait une version de protocole et une liste de capacités, le serveur répondait avec les siennes, et le client confirmait avec initialized. Tout le reste dépendait de ce qui avait été convenu pour cette connexion précise.
La nouvelle révision supprime entièrement cet échange. Le serveur ne construit plus de mémoire privée pour chaque client, de sorte que deux requêtes du même client peuvent être traitées par deux machines différentes sans que l’une ou l’autre s’en aperçoive.
Chaque requête porte son propre contexte
Comme rien n’est négocié à l’avance, chaque requête se présente elle-même. Un objet _meta dans l’enveloppe JSON-RPC transporte la version du protocole, l’identité du client et les indicateurs de capacités. Les informations du client ressemblent à ceci :
L’effet concret est un serveur qui lit tout ce dont il a besoin dans la requête qu’il a sous les yeux. Voici ce qui a bougé :
Sujet
Protocole de l’époque 2025
Protocole du 2026-07-28
Accord sur la version
Négocié une fois dans initialize
Envoyé dans _meta à chaque requête
Identité du client
Stockée dans la session
Envoyée dans _meta à chaque requête
Capacités
Négociées à la connexion
Envoyées par requête, avec un appel de consultation facultatif
Suivi de session
En-tête Mcp-Session-Id
Supprimé
Points de terminaison de liste
Pouvaient varier selon la connexion
Même réponse pour tous les appelants
💡 Astuce : Un client peut toujours récupérer à l’avance les capacités d’un serveur lorsqu’il le souhaite. C’est désormais un appel facultatif, et non plus une étape obligatoire au départ.
En-têtes de routage pour les passerelles
Sur Streamable HTTP, la spec définit aussi des en-têtes qui permettent à l’infrastructure de router le trafic sans analyser le corps JSON :
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
Une passerelle peut envoyer tools/call pour search vers un pool, et tout le reste vers un autre, uniquement grâce aux en-têtes. La limitation de débit et la journalisation deviennent plus simples pour la même raison.
Ce qui reste identique
Rien ne change dans la façon dont le modèle voit votre serveur. Les outils, les ressources et les prompts restent les trois primitives, les requêtes restent en JSON-RPC, et un outil reçoit toujours des arguments et renvoie un résultat. La différence se joue en coulisse : les points de terminaison de liste ne varient plus selon la connexion, de sorte que tools/list renvoie la même réponse à tous les appelants au lieu d’une variation par session. C’est cette règle qui rend sûre la mise en cache de la liste d’outils en périphérie.
Stateful ou stateless en termes simples
Ces termes sont employés à la légère, voici donc la définition de travail. Un serveur stateful conserve quelque chose entre les requêtes, et la requête suivante n’a de sens que si elle atteint le même endroit. Un serveur stateless ne conserve rien entre les requêtes, et chaque requête contient tout ce qu’il faut pour y répondre.
Le café qui vous reconnaît
Imaginez un café où la barista connaît votre commande, votre prénom et le fait que vous ne prenez pas de sucre. Commander prend trois mots, car le contexte est dans sa tête. C’est un serveur stateful. Il est rapide et sympathique, jusqu’au moment où elle part en pause et que son remplaçant ne sait pas qui vous êtes.
Le bureau de poste qui ne le fait pas
Une lettre fonctionne à l’inverse. L’adresse, l’expéditeur et le timbre figurent tous sur l’extérieur, de sorte que n’importe quel guichetier de n’importe quelle agence peut la trier sans appeler personne. C’est un serveur stateless, et c’est exactement le comportement d’une requête MCP du 2026-07-28 : la version du protocole, l’identité du client et les capacités voyagent toutes avec l’appel.
Le coût derrière un répartiteur de charge
Le transport Streamable HTTP introduit dans la révision 2025-03-26 permettait à un serveur d’émettre un Mcp-Session-Id lors de l’initialisation. Le client le renvoyait à chaque requête suivante, et le serveur s’en servait pour retrouver la bonne page de son registre : capacités négociées, contexte propre à chaque utilisateur, parfois abonnements ouverts. Les serveurs sur le transport stdio étaient stateful d’une manière encore plus simple, puisque le processus lui-même faisait office de session.
Dès que ce registre se trouve dans la mémoire d’un seul processus, votre répartiteur de charge doit continuer à envoyer le même client au même processus. Les équipes ont résolu ce problème de deux façons. Les sessions collantes déséquilibrent le trafic et cassent dès qu’un nœud redémarre. Un magasin partagé, comme Redis, ajoute de la latence et un nouveau point de défaillance unique. Aucune de ces solutions n’est gratuite.
Sans sessions, n’importe quelle requête peut atterrir sur n’importe quelle instance derrière un simple répartiteur en round-robin, comme des voitures qui remplissent des voies de péage identiques. Voici la comparaison côte à côte :
Question
Serveur stateful
Serveur stateless
Où se trouve la mémoire ?
Dans le processus ou un magasin de sessions
Dans la requête, ou dans votre propre base de données
Répartiteur de charge
Routage collant ou magasin partagé
Round-robin simple
Un nœud tombe en panne
Les sessions de ce nœud sont perdues
La requête suivante part ailleurs
Passage à l’échelle
Ajouter des nœuds, plus la gestion des sessions
Ajouter des nœuds
Débogage
Rejouer une session entière
Rejouer une seule requête
Adéquation au serverless
Peu adapté
Naturelle
Où va désormais votre état
Retirer les sessions du protocole ne rend pas votre application stateless. Un panier d’achat, un onglet de navigateur et un flux de travail à moitié terminé existent toujours. La différence, c’est que l’état se trouve désormais là où il doit être, dans votre propre stockage, et que le protocole ne le dissimule plus.
💡 Règle empirique : Si le modèle doit poursuivre quelque chose plus tard, donnez-lui un handle. Si l’utilisateur doit répondre à quelque chose en cours d’appel, utilisez une requête en plusieurs allers-retours. Si le travail est lent, utilisez une tâche.
Des handles explicites
Le schéma recommandé est celui que les API REST utilisent depuis des décennies. Un appel d’outil crée un identifiant et le renvoie, et le modèle le retransmet comme argument dans les appels suivants. L’étiquette en papier de ce panier joue le même rôle qu’un basket_id.
Le serveur recherche le panier dans une base de données à chaque appel. N’importe quelle instance peut traiter n’importe quelle étape, un redémarrage ne perd rien, et le modèle peut reprendre le travail dans une conversation toute neuve tant qu’il conserve l’ID.
Requêtes en plusieurs allers-retours
Parfois, un outil a besoin d’une confirmation en cours de route, par exemple « supprimer ces 40 fichiers ? ». Dans un monde de sessions, le serveur se mettrait en pause et attendrait sur une connexion ouverte. Avec SEP-2322, la réponse transporte à la place resultType: "input_required" et un jeton opaque requestState. Le client rejoue le même appel avec les réponses dans inputResponses.
Comme la progression voyage à l’intérieur de ce jeton, l’instance qui reçoit la nouvelle tentative peut reprendre exactement là où la précédente s’était arrêtée.
Des tâches pour les travaux lents
Les longs travaux suivent le modèle du ticket de dépôt. Avec l’extension Tasks (SEP-2663), le client reçoit immédiatement un taskId et interroge tasks/get jusqu’à ce que le travail soit terminé. L’appel est découplé de son exécution : un rendu de dix minutes ne garde donc jamais une connexion ouverte, et chaque interrogation peut atteindre n’importe quelle instance.
Situation
Schéma
Ce qui circule entre les appels
Travail qui continue plus tard
Handle explicite
Un ID tel que basket_id
Confirmation en cours d’appel
Requête en plusieurs allers-retours
requestState et inputResponses
Tâche qui prend des minutes
Extension Tasks
Un taskId à interroger
Migrer un serveur sans douleur
Auditer ce que vous stockez
Commencez par repérer chaque endroit où votre serveur retient quelque chose sur un client entre deux requêtes. Les coupables habituels :
Contexte d’authentification enregistré au moment de initialize
Caches de session ou compteurs de débit conservés en mémoire
Contrôles de capacités qui lisent les indicateurs négociés au lieu de _meta
Listes d’outils qui changent selon la personne connectée
Abonnements liés à une connexion ouverte
Chacun doit trouver un nouveau foyer : la requête elle-même, votre base de données ou un handle explicite.
Utiliser le codemod du SDK
Le SDK TypeScript v2 se découpe en paquets spécifiques à chaque côté et livre un codemod pour les changements mécaniques :
Les SDK v2 continuent de parler le protocole de l’époque 2025 par défaut, et servir le 2026-07-28 relève d’une activation explicite. Vous pouvez ainsi livrer d’abord le changement de code, puis basculer le protocole quand vos clients seront prêts. La branche v1.x continue de recevoir des corrections de bogues et de sécurité pendant au moins six mois après la v2.
Surveiller le calendrier des dépréciations
Les mainteneurs promettent au moins douze mois entre la dépréciation et la suppression, et la date de suppression la plus précoce pour les fonctionnalités dépréciées est le 28 juillet 2027. Les comptes rendus de migration citent Roots, Sampling et Logging parmi les fonctionnalités dépréciées (SEP-2577), les flux initiés par le serveur passant à des requêtes en plusieurs allers-retours. Prévoyez le travail, mais il n’y a pas d’urgence.
La sécurité se durcit
Une session permettait à un serveur de dire « ce client s’est connecté plus tôt ». Ce raccourci disparaît. Chaque requête doit porter des identifiants, et chaque requête doit être vérifiée. Si le coût de la validation vous inquiète, conservez le résultat en mémoire pendant quelques secondes, mais ne faites jamais confiance à une requête parce que la précédente semblait correcte.
Rendez les handles impossibles à deviner. Un handle n’est qu’un identifiant, et les identifiants sont des cibles d’attaque classiques. b_47f2 fonctionne sur le papier. En production, générez de longues valeurs aléatoires, stockez le propriétaire à côté de l’enregistrement et vérifiez à chaque appel que l’appelant possède bien le handle. Faites expirer ceux dont vous n’avez plus besoin.
Les nouveaux en-têtes de routage aident aussi ici. Comme Mcp-Method et Mcp-Name sont visibles sans ouvrir le corps, une passerelle peut appliquer une politique par outil, comme des limites de débit plus strictes sur un outil de paiement ou une liste d’autorisation pour un outil destructeur, avant même que la requête n’atteigne votre code. La défense en profondeur est plus facile lorsque la couche externe peut lire l’étiquette sur l’enveloppe.
💡 Règle empirique : Traitez chaque handle comme un paramètre d’URL public. Partez du principe que quelqu’un essaiera le suivant.
Faut-il passer en stateless ?
Pour la plupart des serveurs, oui. Les recherches en lecture seule, le CRUD sur une base de données, la recherche et tout ce qui encapsule une API REST n’ont rien à retenir : la migration consiste surtout à supprimer du code. Vous gagnez des déploiements plus simples, une adéquation naturelle avec les plateformes serverless, et des défaillances qui touchent une seule requête au lieu d’une conversation entière.
Certains outils détiennent quelque chose de vivant : une page de navigateur, un shell, un rendu en cours. Conservez cet état, mais placez-le derrière un handle avec une expiration, dans un magasin accessible à toutes les instances. Le protocole est stateless. Votre backend, lui, n’a pas à l’être.
Un test rapide indique à quel point un serveur est prêt. Choisissez une requête en cours, éteignez l’instance qui la traite, puis rejouez le même appel sur une autre instance. Si la réponse est identique, vous êtes stateless là où cela compte. Si la nouvelle tentative échoue, demande au client de recommencer, ou renvoie quelque chose de subtilement différent, c’est qu’un registre se cache encore en mémoire, et c’est ce code qu’il faut déplacer en premier dans une base de données ou derrière un handle.
Type de serveur
Meilleure option
Pourquoi
Recherches de données en lecture seule
Entièrement stateless
Rien à retenir
CRUD sur base de données
Stateless, identifiant d’enregistrement comme handle
La base de données détient déjà la vérité
Contrôle de navigateur ou de shell
Protocole stateless, backend stateful
La ressource vivante reste derrière un handle à expiration
Longs rendus et travaux par lots
Extension Tasks
L’interrogation remplace les connexions ouvertes
Générer des images via MCP
MCP est aussi le moyen par lequel les assistants atteignent les outils créatifs, et la génération d’images est un exemple net du schéma des handles. PicassoIA expose ses modèles via une API pour développeurs et une connexion MCP. L’API se trouve à https://api.picassoia.com/v1, utilise un jeton Bearer qui commence par pia_sk_, et suit une structure de type Replicate : POST /v1/models/{owner}/{name}/predictions pour lancer un travail, GET /v1/predictions/{id} pour en vérifier l’état et POST /v1/predictions/{id}/cancel pour l’arrêter.
Les identifiants de prédiction sont aussi des handles
La génération est asynchrone. Lancer un travail renvoie immédiatement un identifiant de prédiction, et l’assistant appelle l’outil d’état avec cet identifiant après le délai suggéré, encore et encore, jusqu’à ce que le statut indique succeeded ou failed. Aucune connexion ouverte ne reste inactive pendant que le GPU travaille, et l’identifiant porte toute la continuité. C’est le schéma basket_id appliqué aux pixels.
Quelques limites à connaître lorsque vous planifiez un flux de travail : 5 prédictions simultanées par compte, partagées entre les jetons et les connexions MCP, des prompts allant jusqu’à 4 000 caractères, et un délai d’expiration de 3 heures par travail.
Les modèles que vous pouvez appeler
Ces quatre modèles sont disponibles à la fois via l’API et la connexion MCP :
Les photos de cet article proviennent de P Image, l’un des nombreux modèles de texte vers image de la plateforme. Lorsque vous rédigez les descriptions d’outils et les schémas JSON que votre propre serveur MCP exposera, un grand modèle de langage vous fait gagner du temps. Claude Sonnet 5, GPT 5.6 Sol et Gemini 3.5 Flash sont tous disponibles pour rédiger et relire ce type de texte structuré.
À vous de jouer : créez des images avec Picasso IA
Vous avez maintenant vu le tableau complet : les sessions disparaissent, les handles entrent, n’importe quelle instance peut répondre à n’importe quelle requête. Le moyen le plus rapide de ressentir ce schéma est de l’utiliser. Ouvrez Picasso IA, choisissez un modèle de texte vers image et écrivez un prompt pour la scène que vous voudriez voir dans votre propre schéma d’architecture. Essayez un café douillet, un péage animé ou une rangée calme de serveurs, puis modifiez un détail à la fois et observez comment le résultat évolue.
Quand vous serez prêt à aller plus loin, parcourez tous les modèles sur la page de tous les modèles, transformez une image préférée en courte vidéo avec un modèle vidéo, et continuez d’expérimenter. Chaque prompt que vous écrivez est une petite requête qui porte tout ce dont elle a besoin, ce qui est exactement le propos de cette spec.