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.

Spec MCP stateless : serveurs stateless ou stateful expliqués
Cristian Da Conceicao
Fondateur de Picasso IA

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.

Rangées de baies serveur identiques dans une allée lumineuse de centre de données

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 :

{
  "_meta": {
    "io.modelcontextprotocol/clientInfo": {
      "name": "my-app",
      "version": "1.0"
    }
  }
}

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é :

SujetProtocole de l’époque 2025Protocole du 2026-07-28
Accord sur la versionNégocié une fois dans initializeEnvoyé dans _meta à chaque requête
Identité du clientStockée dans la sessionEnvoyée dans _meta à chaque requête
CapacitésNégociées à la connexionEnvoyées par requête, avec un appel de consultation facultatif
Suivi de sessionEn-tête Mcp-Session-IdSupprimé
Points de terminaison de listePouvaient varier selon la connexionMê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

Un barista tendant un café à un client habitué souriant

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

Des mains triant des enveloppes portant leurs propres étiquettes d’adresse

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

Un concierge d’hôtel lisant dans un épais registre des clients

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.

Vue aérienne d’un péage autoroutier où la circulation se répartit uniformément sur des voies identiques

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 :

QuestionServeur statefulServeur stateless
Où se trouve la mémoire ?Dans le processus ou un magasin de sessionsDans la requête, ou dans votre propre base de données
Répartiteur de chargeRoutage collant ou magasin partagéRound-robin simple
Un nœud tombe en panneLes sessions de ce nœud sont perduesLa requête suivante part ailleurs
Passage à l’échelleAjouter des nœuds, plus la gestion des sessionsAjouter des nœuds
DébogageRejouer une session entièreRejouer une seule requête
Adéquation au serverlessPeu 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

Un panier en osier sur l’étal d’un marché, avec une étiquette en papier numérotée sur l’anse

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.

create_basket()                           -> {"basket_id": "b_47f2"}
add_item(basket_id="b_47f2", sku="widget-123")
checkout(basket_id="b_47f2")

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

Un employé de pressing tendant à un client un ticket de dépôt numéroté

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.

SituationSchémaCe qui circule entre les appels
Travail qui continue plus tardHandle expliciteUn ID tel que basket_id
Confirmation en cours d’appelRequête en plusieurs allers-retoursrequestState et inputResponses
Tâche qui prend des minutesExtension TasksUn taskId à interroger

Migrer un serveur sans douleur

Auditer ce que vous stockez

Un développeur à un bureau debout examinant le code d’un serveur

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 :

npm install @modelcontextprotocol/server
npx @modelcontextprotocol/codemod@latest v1-to-v2 .

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 ?

Deux ingénieurs dessinant des boîtes et des flèches sur un tableau blanc

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 serveurMeilleure optionPourquoi
Recherches de données en lecture seuleEntièrement statelessRien à retenir
CRUD sur base de donnéesStateless, identifiant d’enregistrement comme handleLa base de données détient déjà la vérité
Contrôle de navigateur ou de shellProtocole stateless, backend statefulLa ressource vivante reste derrière un handle à expiration
Longs rendus et travaux par lotsExtension TasksL’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.

Partager cet article

Choisissez votre langue