Guide
MCP 2026-07-28 : la révision sans état, et ce qu’elle demande à votre serveur
Fondateur d'Epovest
Le 28 juillet 2026, le Model Context Protocol a reçu sa plus grosse révision depuis l'ajout de l'autorisation. Une phrase en porte presque tout : le protocole n'a plus de poignée de main. La version, l'identité du client et ses capacités voyagent désormais sur chaque requête, et le serveur accepte ou refuse chaque requête indépendamment.
Tout le reste découle de cette phrase. Voici ce que la révision retire, ce qui la remplace, et la poignée d'exigences faciles à manquer parce que rien ne casse bruyamment quand on les oublie. Nous avons fait passer notre propre serveur MCP à 2026-07-28 le 2 août 2026 : la liste de contrôle en fin d'article est celle que nous avons suivie.
Ce que la révision retire
- Les sessions de protocole et l'en-tête
Mcp-Session-Id. Les résultats de liste ne varient plus d'une connexion à l'autre. Un serveur qui a besoin d'un état entre deux appels frappe une clé explicite et la fait circuler comme un argument d'outil ordinaire. - La poignée de main
initializeet l'accusénotifications/initializedqui la refermait. ping,logging/setLeveletnotifications/roots/list_changed. Le niveau de journal se règle maintenant par requête, dans_meta.- Le flux GET indépendant. Les notifications de changement passent par une unique requête
subscriptions/listende longue durée, et le client s'abonne aux types qu'il veut recevoir. - La reprise d'un flux interrompu.
Last-Event-IDet les identifiants d'événement SSE disparaissent. Un flux coupé perd la requête en vol, et le client la réémet sous un nouvel identifiant.
La raison annoncée tient à la forme de déploiement. Un serveur qui ne garde aucun état de session se place derrière un répartiteur de charge ordinaire et se met à l'échelle comme n'importe quel service HTTP, sans stockage partagé ni routage collant. C'était l'objectif de la feuille de route 2026, et cette révision est le moment où il arrive.
Ce qui remplace la poignée de main
server/discover. Les serveurs DOIVENT l'implémenter. Elle répond en un seul appel avec les versions du protocole que le serveur sert, ses capacités et son identité. Un client PEUT l'appeler d'abord pour choisir sa version, ou s'en passer et traiter une erreur de version en cours de route.
Les métadonnées par requête. Chaque requête porte son propre contexte :
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": { "name": "example-client", "version": "1.0.0" },
"io.modelcontextprotocol/clientCapabilities": {}
}
En HTTP, la même version est répétée dans l'en-tête MCP-Protocol-Version, et les deux doivent concorder.
Les échanges en plusieurs tours. Un serveur qui a besoin d'un échantillonnage, d'une sollicitation de l'utilisateur ou de la liste des racines n'envoie plus sa propre requête sur un flux. Il rend un résultat portant resultType: "input_required" et un champ inputRequests, et le client rejoue la requête d'origine avec les inputResponses correspondantes. La requête émise par le serveur, en tant que forme, quitte le protocole.
Les tâches deviennent une extension. Les tâches expérimentales sortent du coeur pour devenir l'extension officielle io.modelcontextprotocol/tasks, avec un tasks/get à interroger au lieu d'un tasks/result bloquant.
Cinq exigences faciles à manquer
Ce sont celles qui passent une relecture de code et échouent devant un client strict.
resultTypesur tout résultat. Un résultat ordinaire porte"complete". Un client doit lire l'absence du champ, chez un serveur resté sur une révision antérieure, comme un"complete".- Les indices de cache sont exigés, pas optionnels.
ttlMsetcacheScopeappartiennent aux résultats deserver/discover,tools/list,prompts/list,resources/list,resources/templates/listetresources/read.ttlMsest une durée de fraîcheur en millisecondes ;cacheScopevaut"public"ou"private"et décide si un intermédiaire partagé a le droit de stocker la réponse. - Deux en-têtes de requête, que le serveur doit contrôler.
Mcp-Methodsur chaque requête,Mcp-Namesurtools/call,resources/readetprompts/get. Ils reflètent des valeurs du corps pour qu'une passerelle route et autorise sans le lire, et c'est exactement pourquoi un serveur DOIT refuser tout désaccord entre l'en-tête et le corps par un400. Une valeur qui ne tient pas dans un en-tête ASCII imprimable voyage encodée en base64 entre=?base64?et?=, et le serveur la décode avant de comparer. - De nouveaux codes d'erreur, dans une plage réservée à la spécification.
-32020pour un en-tête qui contredit le corps,-32021pour une capacité client requise et absente,-32022pour une version de protocole non servie. Le dernier compte plus qu'il n'en a l'air : il PORTE la liste des versions que le serveur sert, et c'est cette liste qui permet au client de réessayer au lieu de conclure que le serveur est cassé. La ressource introuvable passe par ailleurs de-32002au-32602standard. - Une méthode inconnue répond
404, l'erreur JSON-RPC dans le corps. C'est ce couple qui dit à un client qui sonde qu'il parle à un serveur à jour, et non à un point d'entrée qui n'héberge simplement pas cette route.
L'autorisation
L'enregistrement dynamique de client est déprécié au profit des Client ID Metadata Documents. L'identifiant de client devient une URL https avec un chemin, qui pointe vers un document JSON que le client héberge et tient à jour. Le serveur d'autorisation lit ce document, vérifie que le client_id qu'il contient est bien l'URL d'où il vient, et valide l'adresse de retour contre la liste qu'il déclare. Rien à enregistrer avant le premier flux, rien à purger après, et le même identifiant vaut chez tous les serveurs d'autorisation. Un serveur annonce la capacité par client_id_metadata_document_supported dans ses métadonnées.
Une conséquence mérite d'être dite franchement, la spécification la laissant à l'état de considération plutôt que de règle : ce mécanisme fait lire au serveur d'autorisation une URL choisie par l'appelant, et sur un point d'entrée de jetons public, cet appelant peut être n'importe qui. Résolvez l'adresse vous-même, refusez tout ce qui n'est pas sur l'internet public, et épinglez l'adresse résolue dans la requête sortante au lieu de vous contenter de la vérifier. Vérifier sans épingler laisse le nom se résoudre une seconde fois côté client HTTP, ce qui est tout le mécanisme d'une attaque par réassociation DNS. Refusez les redirections, plafonnez le corps, gardez des délais courts, et mettez le résultat en cache pour une durée fixe plutôt que pour celle que le document réclame.
Deux ajouts plus discrets dans le même domaine : la réponse d'autorisation DEVRAIT porter le paramètre iss de la RFC 9207, que le client DOIT valider quand il est présent et qui ferme l'attaque par mélange contre un client branché sur plusieurs serveurs d'autorisation ; et le client DOIT envoyer application_type à l'enregistrement dynamique, un serveur OIDC lisant une valeur absente comme "web" et refusant ensuite les adresses de retour d'application native.
Ce qui est déprécié, et pour combien de temps
La révision introduit aussi un cycle de vie des fonctionnalités : active, dépréciée, retirée, avec au moins douze mois entre une dépréciation et le premier retrait possible, plus un registre public de tout ce qui est déprécié. Les racines, l'échantillonnage, la journalisation, le transport HTTP+SSE et l'enregistrement dynamique de client sont dans cet état aujourd'hui. Ils continuent de fonctionner. Une implémentation neuve ne les adopte pas.
Servir les deux ères depuis un seul point d'entrée
La plupart des serveurs parleront la nouvelle révision et les anciennes en même temps pendant un moment, et rien n'interdit de les servir au même endroit. La règle : c'est la version DÉCLARÉE qui choisit l'ère, et rien d'autre. Aucune déclaration vaut l'ère de la poignée de main ; une requête qui déclare 2026-07-28 obtient l'ère sans état.
Deux détails décident si cela tient.
initialize ne doit JAMAIS négocier 2026-07-28. Un client qui envoie initialize ne parle pas une révision qui n'a plus de poignée de main, quelle que soit la version qu'il y demande. La lui accorder le laisserait attendre des champs qui ne viendraient jamais. Tenez deux listes : les versions que vous servez, celles qu'annonce server/discover, et les versions que initialize a le droit de négocier, c'est-à-dire la première liste moins la version sans état.
Répondez l'erreur de version AVANT le contrôle de la clé. Un appelant qui cumule une version non servie et aucune authentification doit lire l'erreur de version. Un 401 ne lui apprend rien d'actionnable, et la liste des versions servies est de toute façon publique, puisque server/discover la rend sans authentification.
La liste de contrôle
À passer sur votre propre serveur.
server/discoverrépond-elle, et sans clé si vos autres méthodes publiques répondent sans clé ?- Chaque résultat porte-t-il
resultType? server/discoverettools/listportent-ellesttlMsetcacheScope?- Validez-vous
MCP-Protocol-Versioncontre le corps, etMcp-MethodetMcp-Namecontre leurs valeurs du corps, avec-32020en cas de désaccord ? - Une version non servie rend-elle
400et-32022, en portant les versions que vous servez ? - Une méthode inconnue rend-elle
404avec-32601? initializerépond-elle toujours aux clients plus anciens, et ne rend-elle jamais2026-07-28?- Si vous tenez le serveur d'autorisation : annonce-t-il
client_id_metadata_document_supported, et épingle-t-il l'adresse résolue avant d'aller lire un document de client ?
Le serveur MCP d'Epovest répond en 2026-07-28 comme en 2025-06-18, 2025-03-26 et 2024-11-05 sur le même point d'entrée, et la référence complète est sur epovest.com/docs/api.md. Pour y brancher un assistant, notre guide pour faire de Claude votre analyste GEO personnel détaille les deux portes d’entrée.