Accueil / Articles

MCP 2026-07-28 : Comment la spécification sans état modifie la conception des serveurs distants

La spécification MCP 2026-07-28 devient sans état : pas de sessions ni de handshake, ainsi que l’absence de MRTR, de tâches et d’indicateurs de cache. Quelles sont les modifications pour les serveurs distants et comment les migrer ?

Éditeur : REACTAPP.TOP23 min de lecture5381 mots

Sources: MCP Just Changed Its Architecture: Why the 2026 Specification Makes MCP Truly Cloud-Native

Un tournant pour MCP

La révision du 2026-07-28 du Model Context Protocol constitue un événement majeur, bien au-delà d’une simple mise à jour de version. Elle remet en question l’une des hypothèses de base de MCP, à savoir que un serveur distant est organisé autour d’une session protocolaire liée à une connexion. MCP s’est éloigné d’un modèle bidirectionnel à état pour adopter un modèle de requêtes et de réponses sans état, fonctionnant comme une infrastructure HTTP distribuée ordinaire. L’annonce de la version candidate l’a qualifiée de plus grande révision depuis le lancement du protocole, et la spécification finale a été publiée le 28 juillet 2026.

MCP à état n’était pas incapable de fonctionner. Le problème résidait dans le fait que l’état au niveau du protocole créait des contraintes opérationnelles qui posaient des difficultés sur les plateformes cloud modernes. Un déploiement à distance pouvait devoir mémoriser un identifiant de session, lier cette session à un seul backend, coordonner l’état entre les répliques et maintenir en fonctionnement des mécanismes de communication à long terme pour des interactions qui n’étaient en réalité que des demandes et des réponses simples. Cela est gérable à petite échelle. À grande échelle, cela entraîne les mêmes problèmes que ceux avec lesquels les systèmes web à sessions persistantes luttent depuis des années.

Dans la nouvelle conception, la session de protocole est supprimée ainsi que la séquence de connexion initialize et initialized. Chaque requête intègre les métadonnées du client dont elle a besoin, et les serveurs deviennent accessibles grâce à une découverte explicite. Les interactions serveur-client sont réorganisées sous forme de requêtes à plusieurs allers-retours, les en-têtes HTTP transportent des métadonnées de routage, les clients reçoivent des indications de mise en cache pour les listes et les réponses de ressources, les tâches longues sont transférées dans l’extension Tasks, la gestion du cycle de vie des extensions est formalisée et une politique de dépréciation plus stricte est mise en place. Le résultat est un noyau MCP qui s’intègre facilement derrière des balanceurs de charge classiques, des passerelles, des pare-feu d’applications web ainsi que des déploiements sans serveur ou auto-échelonnés.

Un modèle mental est utile. L’ancienne version à distance de MCP fonctionnait comme un protocole de connexion conversationnelle distribuée, tandis que la nouvelle version agit comme une API HTTP dont le comportement à état est exprimé explicitement dans la couche d’application ou d’extension.

Pourquoi MCP basé sur des sessions s’est avéré inadapté

L’expression « MCP à état qui a cessé de fonctionner » doit être lue avec prudence, car le MCP à état n’a pas soudainement cessé de fonctionner. Ce qui s’est passé, c’est que l’état géré par le protocole est devenu de moins en moins adapté à l’infrastructure sur laquelle les serveurs MCP étaient déployés.

Dans le modèle précédent, un client lançait la communication par une requête initialize, et le serveur répondait par un Mcp-Session-Id que toute requête ultérieure devait inclure. Cela liait le client logique à une session côté serveur. L’exemple donné par les mainteneurs montrant la situation avant et après illustre clairement cette différence : le flux traditionnel nécessitait une requête d’initialisation suivie de l’appel réel à l’outil, tandis que le protocole 2026 permet d’envoyer directement l’appel à l’outil en tant que requête autonome.

La conséquence opérationnelle est plus importante que la requête HTTP économisée. Imaginez un service MCP disposant de huit réplicas derrière un équilibreur de charge. Avec les sessions, la première requête crée une session sur l’instance A, et la deuxième doit à nouveau atteindre l’instance A à moins que l’état de la session ne soit partagé. Cela oblige à choisir entre deux stratégies : un routage collant, qui fait en sorte que le même client continue d’appeler toujours la même instance, ou un stockage de sessions partagé que toutes les instances peuvent lire. Dans les deux cas, le comportement du protocole dépend de la topologie de déploiement.

Le routage collant nuit à la flexibilité, car l’équilibreur de charge ne peut plus envoyer chaque requête à l’instance disponible qui a de la capacité. Un nœud peut être submergé tandis qu’un autre reste inactif, car les sessions sont fixées à un nœud en particulier, ce qui rend la bascule plus difficile lorsque l’état initial se trouve sur un nœud disparu.

Conserver les sessions dans un stock partagé résout une partie du problème tout en en ajoutant un lié aux systèmes distribués. Chaque session devient alors un enregistrement qui doit être créé, répliqué, périmé, purgé et maintenu disponible en permanence. À ce stade, le serveur MCP est une application accompagnée d’un sous-système de gestion de l’état des sessions réparti sur plusieurs machines. La version candidate indique que les sessions persistantes et les stocks partagés constituent une infrastructure dont on n’a plus besoin au niveau du protocole une fois que les sessions disparaissent.

Kubernetes, les plateformes de conteneurs à auto-échelle, les fonctions cloud et les environnements serverless rendent ce problème encore plus évident. Une requête peut arriver sur le pod A, ce dernier pouvant disparaître lors d’une opération d’échelle, et la requête suivante arriver sur le pod F. Pour un service HTTP sans état, c’est tout à fait normal, mais cela nuit à un protocole dont la validité suppose que l’état associé à la connexion reste disponible.

Le vieux protocole présentait une deuxième incohérence conceptuelle. Il utilisait un modèle bidirectionnel dans lequel les serveurs pouvaient envoyer des requêtes vers le client. L’induction, l’échantillonnage et la liste des racines reposaient tous sur ce schéma, qui était logique tant qu’un canal persistant existait. Les flux bidirectionnels persistants sollicitent fortement l’infrastructure, car les pare-feu, les proxies, les passerelles, les outils d’auto-échelle, les environnements sans serveur et les pools de connexions possèdent chacun leurs propres délais de timeout et cycles de vie. Maintenir un flux ouvert uniquement pour permettre au serveur de poser une question ultérieurement est fondamentalement différent du HTTP classique basé sur des requêtes et des réponses ordinaires.

Un développeur qui l’a testé de manière indépendante donne un exemple concret des avantages. Il a déclaré que le nouveau protocole sans état était beaucoup plus facile à mettre en œuvre tant du côté client que du côté serveur, et a souligné que les applications n’avaient plus besoin de suivre des identifiants de session côté serveur ni de rediriger une session vers une machine spécifique. Il a ensuite créé un explorateur MCP sans état et publié une nouvelle intégration Datasette MCP.

Ainsi, l’idée n’est pas que l’état soit mauvais. L’état caché du protocole est coûteux, tandis que l’état explicite des applications est gérable.

La session et la séquence de connexion ont disparu

Le premier changement que les utilisateurs remarquent est la disparition de la session de protocole. À la date 2026-07-28, l’échange entre initialize et notifications/initialized a été supprimé, tout comme l’en-tête Mcp-Session-Id. Chaque requête inclut désormais la version du protocole ainsi que les capacités du client à l’intérieur de _meta. Un client souhaitant en savoir plus sur un serveur à l’avance peut appeler server/discover. Le journal des modifications final classe ces deux suppressions, celle de la session et celle de la procédure de connexion, comme des changements majeurs.

Auparavant, le flux se déroulait par une procédure de connexion suivie de l’appel réel :

Client
  |
  | initialize
  v
MCP Server
  |
  | Mcp-Session-Id = ABC
  v
Client
  |
  | tools/call + Mcp-Session-Id: ABC
  v
Same logical server session

La communication peut maintenant avoir lieu directement :

Client
  |
  | tools/call
  | protocol version
  | capabilities
  | client metadata
  v
Load Balancer
  |
  +----> MCP Instance A
  |
  +----> MCP Instance B
  |
  +----> MCP Instance C

Une requête contient désormais tout le contexte dont la couche de protocole a besoin pour la comprendre. De plus, Streamable HTTP copie les métadonnées clés dans les en-têtes, comme dans cette requête :

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

Le corps du message reste la source de vérité, tandis que l’enveloppe HTTP fournit des métadonnées que les intermédiaires peuvent utiliser. La spécification exige que les intermédiaires et les protocoles de transport rejetent, le cas échéant, les combinaisons incohérentes de en-têtes et de corps.

La suppression de la session ne signifie pas que les applications ne peuvent pas avoir d’état. L’état de l’application devient simplement une partie explicite du modèle de données. Un flux de travail de shopping pourrait, par exemple, retourner un identifiant pour le panier :

{
  "basket_id": "bsk_8f2a..."
}

et la prochaine appel de l’outil restituerait cette poignée ainsi que l’objet :

{
  "basket_id": "bsk_8f2a...",
  "item_id": "SKU-123"
}

Du côté serveur, l’identifiant est recherché dans Redis, PostgreSQL, DynamoDB, Cosmos DB, un stockage d’objets ou tout système adapté. Le transport MCP cesse alors de gérer cet état. Les mainteneurs recommandent des identifiants explicites tels que basket_id et browser_id comme moyen de remplacer l’état de protocole caché.

Cela correspond à une séparation architecturale, présentée ici :

MCP protocol state
        |
        v
       NONEApplication state
        |
        v
Explicit IDs + external state store


Long-running task state
        |
        v
Tasks extension + durable task store

C’est sans doute l’idée la plus importante de toute cette version.

Métadonnées et découverte par requête

Si la phase d’échange initiale est absente, comment un serveur peut-il savoir quelle version de protocole et quels fonctionnalités un client prend en charge ? La réponse réside dans les métadonnées par requête. Les champs réservés _meta se trouvent dans l’espace de noms io.modelcontextprotocol/*, et la version ainsi que les capacités du client accompagnent chaque requête, sans négociation unique que la connexion se souviendrait discrètement. Lorsqu’un client choisit de s’identifier, son identité est également transmise sous forme de métadonnées par requête, et les serveurs peuvent inclure les leurs dans les métadonnées de résultat. Conceptuellement, une requête ressemble à ceci :

{
  "jsonrpc": "2.0",
  "id": 42,
  "method": "tools/call",
  "params": {
    "name": "search",
    "arguments": {
      "query": "MCP stateless architecture"
    },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": {
        "extensions": {
          "io.modelcontextprotocol/tasks": {}
        }
      },
      "io.modelcontextprotocol/clientInfo": {
        "name": "my-agent",
        "version": "3.2.0"
      }
    }
  }
}

Lorsque des informations préalables sont utiles, le serveur met en œuvre server/discover. Le journal des modifications final en fait une requête RPC obligatoire pour les serveurs, permettant ainsi aux clients de consulter les versions de protocole prises en charge, leurs capacités ainsi que l’identité du serveur avant de procéder à quoi que ce soit d’autre. Cela est pratique lorsque un client doit choisir son comportement en fonction de ce que le serveur prend en charge.

Il existe ici une distinction subtile : les clients peuvent choisir de ne pas appeler server/discover, mais les serveurs doivent le fournir. MCP n’a donc plus besoin de cette fonctionnalité pour établir une session, tout en offrant néanmoins un moyen formel d’examiner les capacités de manière proactive. Sa conception ressemble à celle des API HTTP matures, où chaque requête est interprétable de manière indépendante et où des points de terminaison de découverte ou des métadonnées optionnels fournissent des détails plus complets.

Routeur round-robin et mise à l’échelle à zéro

Ici, la conception est particulièrement avantageuse pour l’architecture cloud. Sans sessions de protocole, un point de terminaison MCP peut fonctionner derrière un balanceur de charge ordinaire avec un routage simple par rotation. L’annonce officielle présente cela comme l’objectif visé : plusieurs instances MCP, un routage par rotation, et aucune base de données commune pour les sessions de protocole.

Prenons trois répliques derrière un balanceur de charge :

                  +------------------+
                  |  Load Balancer   |
                  +---------+--------+
                            |
               +------------+------------+
               |            |            |
               v            v            v
           MCP Pod A    MCP Pod B    MCP Pod C

La demande 1 peut être envoyée à A, la demande 2 à C et la demande 3 à B. Aucune règle de protocole ne exige que les trois atteignent la même instance, ce qui correspond précisément à la propriété souhaitée par les systèmes cloud-native.

Supposons qu’un horizontal pod autoscaler décide qu’une seule réplique est nécessaire pendant la nuit. Deux instances peuvent disparaître. Lorsqu’une demande arrive par la suite, une nouvelle instance peut être lancée et la traiter immédiatement, car elle n’a pas besoin de reconstruire d’abord une session de protocole. L’architecture devient alors la suivante :

Le service MCP lui-même est sans état, et l’état métier reste persistant en dehors de lui.

Cependant, il y a une réservation. Un MCP sans état ne fait pas automatiquement en sorte que chaque application MCP s’échelle à zéro. Si une application conserve un panier, un flux de travail, une conversation, une transaction ou un job uniquement en mémoire de processus, elle a simplement recréé le problème de l’état caché à un niveau supérieur.

Une véritable mise à zéro à l’échelle exige que tout ce qui doit survivre à l’arrêt d’une instance soit hébergé dans une infrastructure durable. Une configuration de production plausible utilise Redis pour une coordination à court terme, PostgreSQL ou une base de données NoSQL gérée pour conserver l’état de l’application sur le long terme, ainsi qu’un stockage d’objets pour les fichiers volumineux. L’authentification reste également en dehors du processus, gérée par le système d’autorisation OAuth ou OIDC plutôt que déduite de la mémoire locale.

Le compromis est clair : le protocole permet une infrastructure sans état, et c’est désormais au concepteur de l’application de décider quel état doit être conservé, où il sera stocké, comment il sera autorisé, pendant combien de temps il restera en place et comment il sera nettoyé. Cela constitue une frontière plus saine dans la conception des systèmes.

Gérer l’état soi-même

Les identifiants explicites pourraient être le résultat le plus significatif de ce modèle. Prenons un agent d’achat basé sur l’IA chargé de commander 100 GPU, d’obtenir l’approbation et de soumettre la commande. Une conception basée sur des sessions pourrait facilement cacher ce flux de travail dans l’état de session MCP. La nouvelle architecture favorise plutôt une approche visible, où la création de la demande renvoie un identifiant :

create_purchase_request()
        |
        v
purchase_request_id = pr_123
        |
        v
request_approval(pr_123)
        |
        v
approval_id = appr_789
        |
        v
submit_purchase(pr_123, appr_789)

Le statut n’est plus implicite dans une session HTTP. Il s’agit de données de domaine, ce qui est avantageux car ces données peuvent être sécurisées, auditées, versionnées, répliquées et consultées de manière indépendante. Un enregistrement de statut pourrait ressembler conceptuellement à ceci :

{
  "purchase_request_id": "pr_123",
  "user_id": "user_42",
  "status": "awaiting_approval",
  "items": [
    {
      "sku": "GPU-H100",
      "quantity": 100
    }
  ],
  "created_at": "2026-08-21T08:00:00Z"
}

Le serveur MCP se contente de résoudre l’identifiant et de valider l’autorisation. L’observabilité s’améliore également. Au lieu de « la session ABC a effectué une action », un système peut indiquer que la demande d’achat pr_123 a été déplacée de draft à approved, ce qui constitue une trace ayant une réelle signification commerciale.

La sécurité devient aussi plus facile à gérer. Un identifiant opaque ne constitue jamais en soi une autorisation suffisante, et le serveur doit toujours confirmer que l’utilisateur authentifié a le droit d’accéder à l’état associé à cet identifiant. L’extension Tasks traite les identifiants de tâches de la même manière : les serveurs doivent générer des identifiants difficiles à deviner, et l’omission de tasks/list relève d’un choix délibéré en matière de confidentialité, puisque personne ne peut énumérer les identifiants de tâche des autres utilisateurs.

Demandes multi-allers-retours

La suppression des requêtes persistantes du serveur vers le client crée un autre problème de base. Prenons l’exemple d’une telle appel d’outil :

delete_customer_data(customer_id=123)

Le serveur ne peut supprimer rien en toute sécurité avant que l’utilisateur ne confirme, par une demande du type « Supprimer 48 enregistrements ? ». Le modèle traditionnel peut maintenir un canal ouvert et demander davantage d’informations au client. Une conception sans état ne peut pas dépendre d’une connexion permanente ouverte, et les demandes multi-allers-retours, ou MRTR, existent pour combler cette lacune.

MRTR couvre une seule opération logique qui nécessite plusieurs échanges sans connexion persistante ni session côté serveur. Pendant l’attente de confirmation, de précisions ou de prélèvement d’échantillons, rien ne maintient une connexion HTTP ouverte ni ne s’appuie sur un état de connexion persistante. Au lieu de cela, l’interaction devient une chaîne de requêtes HTTP distinctes reliées par un état de continuation. Le client demande d’abord une action, comme tools/call pour un outil de suppression de données. Le serveur évite d’envoyer sa propre requête RPC vers le client tant que l’opération initiale est en attente, et répond par un résultat intermédiaire indiquant qu’il a besoin de plus d’informations :

{
  "resultType": "input_required",
  "inputRequests": {
    "confirm": {
      "type": "elicitation",
      "message": "Delete 48 records?",
      "schema": {
        "type": "boolean"
      }
    }
  },
  "requestState": "..."
}

Le client collecte les réponses et réessaie la demande initiale en les y joignant :

Client
   |
   | tools/call
   v
Server
   |
   | input_required
   | requestState
   v
Client
   |
   | user confirmation
   v
Client
   |
   | same operation + inputResponses + requestState
   v
Any MCP instance

L’opération est donc divisée en plusieurs allers-retours HTTP, sans nécessiter de maintenir la connexion ouverte. La version officielle décrit MRTR comme le remplaçant des schémas elicitation/create, sampling/createMessage et roots/list initiés par le serveur.

Le champ clé est requestState. Tout ce dont le serveur a besoin pour reprendre est placé à l’intérieur, sous forme d’un token opaque qui est signé, chiffré ou protégé d’une autre manière contre toute modification. La spécification ne stocke pas tout l’état de l’application dans un seul stockage de sessions MCP centralisé ; les informations nécessaires pour poursuivre une interaction sont simplement transmises dans la demande suivante. Cela ressemble à un token de continuation, et l’avantage majeur est qu’une interaction logique longue ne signifie plus une connexion physique longue.

En-têtes HTTP et indications de cache

Le passage à un état sans état améliorerait déjà la scalabilité, mais le protocole deviendrait également plus facile à gérer pour les infrastructures HTTP. Les requêtes HTTP streamables comportent des en-têtes standardisés tels que Mcp-Method et Mcp-Name, ce qui permet aux passerelles et aux équilibreurs de charge de déterminer le routage, l’autorisation, les limites de vitesse et les données de telemétrie sans avoir à ouvrir les corps JSON-RPC. Le corps reste la source d’autorité, et tout désaccord entre un en-tête reflété et le champ correspondant dans le corps entraîne un rejet.

Une passerelle pourrait lire ces deux valeurs :

Mcp-Method = tools/call
Mcp-Name   = search

et appliquer des vérifications de politique de ce type :

tools/call + search  -> route/search-cluster
tools/call + execute -> route/execution-cluster
resources/read       -> route/resource-cluster

Cela permet une séparation claire entre le plan de contrôle et le plan de données, car un intermédiaire peut identifier la catégorie opérationnelle d’une requête sans avoir à comprendre le schéma JSON de l’application.

Cette version ajoute également des métadonnées de mise en cache via ttlMs et cacheScope. Une réponse listée peut les contenir de la manière suivante :

{
  "result": {
    "tools": [
      ...
    ],
    "ttlMs": 60000,
    "cacheScope": "public"
  }
}

La sémantique imite délibérément celle de HTTP cache-control. ttlMs indique au client pendant combien de temps un résultat est considéré comme frais, tandis que cacheScope précise si le résultat peut être partagé en toute sécurité. La spécification finale distingue "public" de "private" et met en garde les concepteurs contre l’usage du champ de portée du cache comme substitut aux vérifications d’autorisation. Le critère de fraîcheur fonctionne comme suit :

fresh_until = response_received_time + ttlMs

Un client devrait normalement récupérer à nouveau les données lorsque cela est nécessaire et que celles-ci sont devenues obsolètes, plutôt que de considérer la durée TTL comme un intervalle de sondage obligatoire. Pour les implémentations qui effectuent bien des sondages, la spécification recommande l’utilisation de variations aléatoires et de stratégies de recul.

Les catalogues d’outils rendent cette valeur évidente. Si un serveur liste 250 outils et que 500 agents se connectent, des appels constants à tools/list de la part de chacun génèrent un trafic important sur le plan de contrôle. Une durée TTL raisonnable permet aux clients de réutiliser le catalogue, et un ordre déterministe empêche des mises à jour inutiles dans l’ensemble d’outils serialisés, ce qui peut maintenir stables les caches de prompts des modèles LLM en amont. Le journal des modifications recommande justement un ordre déterministe pour cette raison. Un autre avantage : les connexions SSE restées ouvertes ne sont plus le seul moyen de savoir qu’une liste a changé, ce qui permet à la fois de garantir la fraîcheur des caches et d’envoyer des notifications de modification.

Tâches à long terme

Les API de requête et de réponse sans état doivent encore gérer des tâches qui durent des minutes ou des heures. Prenons un appel comme celui-ci :

run_ml_training_job()

Cela pourrait soumettre une tâche d’entraînement distribuée, attendre les GPU, exécuter plusieurs étapes, générer des points de contrôle et télécharger des artefacts. Maintenir une connexion HTTP ouverte tout au long de ce processus est fragile. L’architecture 2026-07-28 gère cela via l’extension Tasks, identifiée sous le nom de io.modelcontextprotocol/tasks.

Pour une tools/call prise en charge, le serveur peut répondre en fournissant un identifiant de tâche à la place du résultat final :

{
  "resultType": "task",
  "task": {
    "taskId": "task_7b93...",
    "status": "working"
  }
}

Le client peut ensuite demander le statut :

tasks/get(taskId)

Et continuez à effectuer des requêtes jusqu’à ce que la tâche affiche completed, failed ou cancelled. L’extension propose également tasks/update pour des données supplémentaires et tasks/cancel pour annuler la tâche.

Une distinction est cruciale : le protocole de base est sans état, tandis que l’extension Tasks représente délibérément un état persistant :

Core MCP
    |
    +---- Stateless request/response
Tasks extension
    |
    +---- Durable asynchronous state machine

Ce n’est pas une contradiction. C’est une séparation des préoccupations. Une tâche peut être associée à un système de travail externe, comme dans ce flux :

MCP tools/call
      |
      v
Create task
      |
      v
Queue / workflow engine
      |
      +--> Kubernetes Job
      |
      +--> Azure Batch
      |
      +--> AWS Step Functions
      |
      +--> Databricks Job
      |
      +--> CI/CD pipeline

Les clients utilisent l’ID de tâche MCP comme identifiant fiable pour accéder à l’état de ce flux de travail externe. Les configurations de production ne doivent pas conserver l’état des tâches uniquement dans un dictionnaire au sein du processus. L’extension décrit les tâches comme des machines à états durables et recommande de stocker de manière fiable les IDs de tâche, afin que les consultations puissent se poursuivre en cas de panne du client ou de redémarrage du serveur.

Une subtilité d’implémentation reste à résoudre. L’extension Tasks peut encore avoir besoin que les requêtes soient acheminées vers le système qui gère l’état de la tâche. La proposition finale permet à Mcp-Name de contenir l’ID de tâche pour les méthodes tasks/get, tasks/update et tasks/cancel, ce qui autorise les intermédiaires à envoyer des demandes de suivi au gestionnaire de l’état lorsque c’est nécessaire. L’échange complet entre le client, le serveur MCP et le moteur de tâches est présenté dans ce diagramme de séquence :

Client                  MCP Server               Task DB / Engine
  |                         |                           |
  |--- 1. tools/call ------>|                           |
  |                         |--- 2. Register Task ----->| (Status: working)
  |<-- 3. Return taskId ----|                           |
  |                         |                           |
  |--- 4. tasks/get ------->|                           |
  |                           \--- 5. Query state ----->| (Status: working)
  |<-- 6. Status: working --/                           |
  |                         |                           |
  |--- 7. tasks/get ------->|                           |
  |                           \--- 8. Query state ----->| (Status: completed)
  |<-- 9. Final Result -----/                           |

Dans le schéma typique, un client commence le travail en utilisant tools/call, et le serveur enregistre une entrée de tâche persistante dans un système backend partagé tel que Redis, PostgreSQL ou un moteur de workflow comme AWS Step Functions ou Kubernetes. Il renvoie alors immédiatement un taskId ainsi qu’un statut initial working. La première connexion HTTP se ferme proprement, libérant ainsi les ressources réseau, et le client vérifie périodiquement l’avancement en utilisant tasks/get.

L’affirmation architecturale exacte n’est donc pas que tout dans MCP est sans état. Le noyau de MCP est sans état, tandis que le comportement à état est modélisé explicitement à travers des données d’application et des extensions telles que Tasks.

Un agent pour les opérations de données et d’IA en production

Imaginez une plateforme d’IA en production qui expose des outils MCP à un agent d’entreprise. Cet agent dispose d’outils tels que ceux-ci :

search_dataset()
inspect_schema()
run_sql()
start_training_job()
get_training_metrics()
deploy_model()
rollback_deployment()

Un utilisateur demande alors ce qui suit : trouver le plus récent ensemble de données sur le désabonnement des clients, en faire le profilage, entraîner un modèle candidat, comparer les métriques avec celles du modèle en production et ne le déployer que si l’amélioration est d’au moins 5 %.

Au sein de l’ancienne architecture à état, le système pouvait conserver une session de protocole à long terme pour suivre l’interaction, ce qui liait le flux de travail logique de l’utilisateur à une instance serveur unique. Avec la nouvelle architecture, le processus peut être conçu de manière explicite. La première requête pourrait être une recherche :

search_dataset("customer churn latest")

et le serveur renvoie un identifiant de jeu de données :

{
  "dataset_id": "ds_2026_08_20_0042"
}

La prochaine requête examine le schéma à l’aide de ce handle :

inspect_schema("ds_2026_08_20_0042")

Le schéma provient du système de métadonnées. La demande suivante démarre l’entraînement :

start_training_job("ds_2026_08_20_0042")

Le serveur MCP crée une tâche durable :

task_id = "task_train_98af..."

La formation elle-même s’exécute en dehors du processus MCP. Le client la consulte régulièrement :

tasks/get("task_train_98af...")

Et la tâche peut passer par une série d’états :

QUEUED
   |
RUNNING
   |
EVALUATING
   |
INPUT_REQUIRED
   |
RUNNING
   |
COMPLETED

Dans l’état INPUT_REQUIRED, le système peut demander l’approbation pour le déploiement, en indiquant que la solution candidate est 7,2 % meilleure mais augmenterait les dépenses en GPU de 18 %. MRTR peut poser cette question sans maintenir la première connexion ouverte. Le client répond comme suit :

inputResponses = {
    "approve": true
}

et la demande reprend.

Imaginons maintenant que le serveur MCP fonctionne sur six réplicas Kubernetes :

                  Ingress
                      |
              Load Balancer
                      |
        +------+------+------+------+
        |      |      |      |      |
       MCP1   MCP2   MCP3   MCP4   MCP5
                      |
                      v
               Task Store
                      |
              +-------+-------+
              |               |
           Redis          PostgreSQL
              |
              v
        Workflow Engine
              |
        +-----+------+
        |            |
     GPU Job     Model Registry

Ici, la première requête tools/call est acheminée vers MCP2, la requête suivante vers MCP5, la vérification de l’état de la tâche vers MCP1 et la tentative de validation supplémentaire vers MCP3. Rien ne tombe en panne, car le protocole ne suppose plus qu’une session soit liée à un seul serveur. Un MCP3 détruit est remplacé par Kubernetes. Si le déploiement passe de six réplicas à deux pendant la nuit, le flux de travail continue de fonctionner, car l’état des tâches et des applications se trouve en dehors du processus. C’est là que la spécification démontre sa valeur pratique.

Authentification et renforcement de la sécurité

Parallèlement aux modifications sans impact sur l’état du système, des améliorations de sécurité significatives sont apportées. La version finale exige un comportement plus strict pour OAuth et OIDC : les serveurs d’autorisation doivent fournir un paramètre iss conformément à la description de la RFC 9207, et les clients MCP doivent vérifier cet émetteur avant d’échanger un code d’autorisation, ce qui empêche les attaques de confusion entre serveurs d’autorisation. Les identifiants des clients sont également liés à l’émetteur qui les a créés, ce qui interdit leur réutilisation avec un autre serveur d’autorisation. L’enregistrement dynamique des clients reste disponible pour compatibilité, mais est officiellement déprécié, les documents de métadonnées d’ID de client servant de remplacement.

Le modèle de menace évolue de manière utile avec l’absence d’état, bien qu’il implique des responsabilités. Un déploiement en production doit authentifier chaque requête et autoriser l’opération spécifique sur l’outil ou le ressource concerné. Il doit vérifier les identifiants d’état explicites par rapport au principal authentifié, empêcher l’énumération des identifiants de tâche, et chiffrer les données d’état sensibles, tant stockées que en transit. Prenons l’exemple du document suivant :

basket_id = bsk_123
user_id   = user_456

Nul ne doit en déduire que posséder bsk_123 suffit pour agir sur le panier. Avant toute autre chose, le serveur doit vérifier la propriété, de la manière suivante :

authenticated_subject == basket.owner

et ensuite appliquer des permissions spécifiques à l’outil.

La propagation du contexte de suivi W3C est également documentée, transmise via _meta à l’aide de traceparent, tracestate et baggage. Le suivi à travers l’hôte, le client MCP, le serveur MCP et les services en aval devient beaucoup plus simple, et les mainteneurs le relient directement aux traces compatibles avec OpenTelemetry. L’observabilité doit donc faire partie de la conception dès le premier jour. Un chemin de requête traçé pourrait ressembler à ceci :

User Request
    |
    v
Agent
    |
    | traceparent
    v
MCP Client
    |
    | traceparent
    v
MCP Gateway
    |
    v
MCP Server
    |
    v
Database / Queue / API

La trace résultante peut montrer toute la chaîne causale et non un ensemble de transactions HTTP non liées entre elles.

Les extensions deviennent de première classe

Les extensions obtiennent également un statut officiel. Elles existaient déjà, mais rien ne ressemblait à un cycle de vie mature équivalent à celui d’un protocole principal. Désormais, chaque extension dispose d’un identifiant reverse-DNS, d’une négociation explicite de capacités, de son propre répertoire, d’une versionning indépendante, ainsi que d’un suivi dédié au sein du processus SEP.

La raison en est que MCP évolue plus rapidement que ce à quoi une seule version de protocole monolithique peut se développer en toute sécurité. Les fonctions de soutien ne devraient pas obliger chaque implémentation à exécuter des tâches distribuées asynchrones, et un serveur en ligne de commande minimal ne devrait pas être contraint d’adopter les MCP Apps. Les écosystèmes de normes plus importants suivent le même principe : conserver le noyau petit et stable, tout en déplaçant les fonctionnalités spécialisées dans des modules qui évoluent selon leur propre calendrier.

Deux extensions bénéficient d’une attention particulière dans cette version. MCP Apps ajoute une interface utilisateur interactive générée par le serveur à l’intérieur d’un iframe isolé, tandis que Tasks permet l’exécution asynchrone. La hiérarchie qui en résulte est la suivante :

                    MCP
                     |
          +----------+----------+
          |                     |
       Core Protocol        Extensions
          |                     |
      Stateless HTTP      +-----+------+
                          |            |
                       Tasks       MCP Apps

Cela offre à MCP un moyen bien plus durable d’évoluer.

Dépréciation et mise à niveau

La version actuelle introduit délibérément un cycle de vie formel pour les fonctionnalités. Le journal des modifications définit les états Actif, Déprécié et Supprimé, et fixe une période de dépréciation minimale de douze mois, permettant ainsi aux développeurs d’appllications d’avoir une période de migration prévisible, sans avoir à procéder à des modifications d’urgence à chaque évolution du protocole.

Roots, Sampling et Logging figurent sur la liste des fonctionnalités dépréciées. Les conseils officiels de migration préconisent de remplacer Roots par des paramètres d’outil explicites, des URI de ressources ou une configuration serveur, de remplacer Sampling par des intégrations directes avec des fournisseurs de LLM, et de remplacer Logging par stderr ou OpenTelemetry. Le transport HTTP+SSE plus ancien est également déprécié, et Streamable HTTP constitue l’option préférée.

L’API expérimentale Tasks datant du 25-11-2025 marque une nouvelle frontière de migration. Elle se trouve désormais dans l’extension io.modelcontextprotocol/tasks, avec un cycle de vie révisé basé sur tasks/get, tasks/update et tasks/cancel. tasks/list a été supprimé car la liste des tâches sans session soulève des problèmes de portée et de confidentialité.

Pour une organisation qui migre une flotte MCP existante, l’ordre le plus sûr est le suivant :

  1. Mettre à jour le SDK, puis rechercher le code qui dépend de Mcp-Session-Id. Examiner attentivement les dictionnaires indexés par session, les registres de connexion ainsi que les règles de routage des sessions persistantes basées sur l’ancien modèle.
  2. Séparer l’état du protocole de l’état de l’application, en attribuant à tout élément représentant un véritable état métier un identifiant explicite et en le stockant dans un système de stockage suffisamment fiable.
  • Mettre à jour la configuration du gateway pour comprendre les nouveaux en-têtes MCP, et fournir aux réponses listées pouvant être mémorisées des politiques TTL et de portée explicites, d’autant plus que le stockage en mémoire public versus private a des conséquences sur la sécurité.
  • Évaluer les workflows à long terme pour l’adoption des tâches plutôt que de les implémenter sous forme de requêtes HTTP ouvertes indéfiniment.
  • La documentation actuelle sur la migration vers le SDK TypeScript montre également cette réalité transitoire. Les implémentations peuvent prendre en charge le nouveau protocole tout en conservant des mécanismes de compatibilité pour les clients de l’époque précédente, permettant ainsi une migration progressive sans nécessiter un changement complet de l’écosystème.

    Quelles en sont les conséquences pour l’architecture MCP

    La conséquence la plus importante est que MCP sépare désormais quatre domaines qui étaient auparavant mélangés :

    • La couche de protocole est responsable de la signification des messages.
    • La couche de transport transmet les requêtes via HTTP ou stdio.
    • La couche d’application gère l’état, le contexte d’autorisation, les workflows ainsi que ce qui identifie l’objet métier.
    • La couche d’extension fournit des fonctionnalités optionnelles telles que des tâches asynchrones et des applications MCP.

    La structure en couches est la suivante :

                     +----------------------------+
                     |        MCP Protocol        |
                     | JSON-RPC + request model   |
                     +-------------+--------------+
                                   |
                     +-------------v--------------+
                     |         Transport          |
                     | stdio / Streamable HTTP    |
                     +-------------+--------------+
                                   |
                     +-------------v--------------+
                     |      Application Layer     |
                     | explicit IDs + databases   |
                     +-------------+--------------+
                                   |
                     +-------------v--------------+
                     |         Extensions         |
                     | Tasks / MCP Apps / others   |
                     +----------------------------+
    

    Cela permet une scalabilité bien meilleure que de tout regrouper dans une abstraction de session limitée à une seule connexion, et cela met MCP en conformité avec les pratiques établies depuis longtemps dans les systèmes distribués : traitement des requêtes sans état, état externe durable, identifiants explicites, sémantique de cache, tentatives idempotentes, gestion asynchrone des tâches et traçage distribué.

    Les compromis

    Il y a des coûts. L’avantage principal est la scalabilité horizontale, mais le prix à payer est que la gestion de l’état devient la responsabilité de l’application. Les développeurs doivent déterminer comment les mécanismes de gestion de l’état sont créés, combien de temps ils restent en vigueur, où ils sont stockés, comment l’accès y est autorisé et comment les données obsolètes sont supprimées.

    Puisque les capacités du client et les métadonnées changent à chaque requête, les chargements de données augmentent légèrement. Les octets supplémentaires sont généralement faibles, mais les systèmes à très fort débit doivent éviter de répéter inutilement les chargements de données relatives aux capacités.

    MRTR rend possible l’HTTP sans état pour des échanges qui nécessitaient auparavant des requêtes initiées par le serveur, bien qu’il expose davantage la machine à états de l’interaction. Le client doit reconnaître l’état input_required, conserver l’état de la requête, rassembler les réponses et tenter à nouveau correctement.

    Les tâches gèrent l’exécution de longue durée, au prix d’un stockage des tâches, qui constitue un sous-système supplémentaire fiable. Ce n’est pas un défaut, mais plutôt un état explicite qui remplace l’état de protocole caché.

    Le cache améliore l’efficacité, mais des valeurs incorrectes de cacheScope peuvent créer des problèmes de sécurité. Une réponse mal étiquetée "public" pourrait devenir partageable entre différents contextes d’autorisation, et les directives relatives au cache avertissent que le champ de portée du cache ne remplace pas l’autorisation au niveau primitif.

    Le noyau sans état ne doit pas être interprété comme une architecture inférieure. Il est plus juste de le décrire comme une responsabilité distribuée de manière plus rationnelle.

    Le changement conceptuel

    Lorsqu’on se demandait où avait lieu l’interaction logique, l’ancien modèle indiquait implicitement qu’elle se produisait à l’intérieur de la session MCP liée à la connexion. Le nouveau modèle, en revanche, se demande quel état l’opération a réellement besoin, et la réponse est claire : un identifiant de domaine, un identifiant de tâche, un contexte d’autorisation ou un jeton d’état de requête nécessaire pour poursuivre. Cette abstraction s’adapte beaucoup mieux.

    Les transactions possèdent des IDs de transaction, les workflows Kubernetes ont des IDs de job, les paniers d’achat ont des IDs de panier, les pipelines CI ont des IDs d’exécution et les jobs d’entraînement ont des IDs d’exécution. Les applications MCP peuvent désormais utiliser le même vocabulaire.

    Ce que cette version apporte

    Cette version va au-delà d’une simple mise au point du protocole. Elle fait évoluer MCP d’une intégration d’agents orientés connexion vers des systèmes distribués natifs du cloud, et les améliorations sont les suivantes :

    • La suppression de Mcp-Session-Id met fin à l’affinité au niveau du protocole.
    • La suppression de la procédure de handshake fait de chaque requête un échange autonome.
    • server/discover permet de découvrir les capacités sans aucune sémantique de session associée.
    • Mcp-Method et Mcp-Name exposent le trafic MCP aux outils HTTP courants.
    • ttlMs et cacheScope fournissent aux clients et aux passerelles un vocabulaire commun pour gérer la fraîcheur des données et leur partage.
    • MRTR abandonne l’idée selon laquelle un serveur qui demande quelque chose à un client a besoin d’une connexion qui ne se ferme jamais.
    • Les tâches constituent une abstraction fiable pour des opérations qui dépassent la durée d’une seule requête HTTP.
    • Les identifiants explicites permettent de sortir l’état métier de la métadonnée de transport cachée pour l’intégrer dans le modèle de données propre à l’application.
    • Les extensions empêchent le noyau d’enfler tout en permettant à l’écosystème de fonctionner rapidement.
    • Une autorisation plus stricte correspond au comportement réel des implémentations OAuth et OIDC.
    • Les conventions de contexte de suivi d’OpenTelemetry facilitent l’observation des systèmes MCP distribués.
    • Une politique formelle de dépréciation offre à l’écosystème un moyen prévisible de s’adapter.

    Le modèle sans état rend l’implémentation de MCP considérablement plus simple, et les outils développés par l’indépendant, comme l’explorateur et Datasette, montrent que cette simplicité se transforme en outils utilisables. Sa description du protocole ancien, nécessitant une initialisation de session, et du nouveau, basé sur une seule requête autonome, correspond au changement opérationnel visé par les mainteneurs.

    La leçon à retenir pour les architectes est que MCP n’a pas supprimé l’état. Ce qu’il a supprimé, c’est l’état caché appartenant au protocole, le remplaçant par un état explicite et composable appartenant à l’application. Ce changement permet à un serveur MCP de passer de cette configuration :

    sticky sessions
          +
    shared session store
          +
    long-lived connections
          +
    connection affinity
    

    et vers celui-ci :

    stateless request handling
          +
    ordinary load balancing
          +
    external durable state
          +
    explicit handles
          +
    asynchronous task primitives
    

    Pour les déploiements d’entreprise, c’est un avantage majeur, car il convient bien mieux au Kubernetes, à l’auto-échellement, à l’exécution serverless, aux API gateways, aux mises à jour sans interruption, au routage multi-région, au suivi distribué et aux grandes plateformes multi-locataires. En termes simples, cette spécification transforme MCP d’un standard définissant la manière dont les agents appellent des outils en une infrastructure qui s’intègre naturellement dans les systèmes d’IA distribués modernes.