Il 28 luglio 2026 il Model Context Protocol ha pubblicato una nuova revisione della sua specifica. Il changelog si apre con due voci che cambiano il funzionamento di ogni server remoto: le sessioni a livello di protocollo non ci sono più, e nemmeno l'handshake initialize. Ora ogni richiesta porta con sé tutto ciò che serve al server per rispondere.
Per molti server si tratta di una migrazione. Per un server che legge documenti d'identità è più che altro una conferma, perché quel tipo di server aveva già buoni motivi per essere senza stato. Questo articolo passa in rassegna la revisione dal punto di vista di un tool server: quali cambiamenti può ignorare, quali deve implementare e quali vale la pena adottare presto. Gli esempi vengono da un server che gestiamo noi, quindi le lacune indicate qui sotto sono anche nostre.
Questo è il blog di doc.cheap, un'API OCR per passaporti e documenti d'identità con un server MCP davanti. Leggete le parti sul prodotto tenendone conto.
In breve
| Cambiamento nel 2026-07-28 | Cosa fa un tool server senza stato |
|---|---|
Niente Mcp-Session-Id, niente sessioni |
Nulla, se non ne ha mai tenute |
Niente handshake initialize; versione e capacità nel _meta di ogni richiesta |
Leggerle a ogni richiesta, confrontarle con l'header MCP-Protocol-Version |
server/discover |
Implementarlo: è un MUST |
Header Mcp-Method e Mcp-Name obbligatori |
Verificare che corrispondano al corpo |
resultType su ogni risultato |
Inviare "complete" |
ttlMs e cacheScope sui risultati di elenco e lettura |
Scegliere una durata della cache e un ambito |
| Tool in ordine deterministico | Mantenere stabile l'ordine di registrazione |
| Stream HTTP GET e ripresa rimossi | Rispondere 405 a GET e DELETE |
| Roots, Sampling e Logging deprecati | Ricevere i file come argomenti dei tool, scrivere i log su stderr o OpenTelemetry |
Il resto dell'articolo spiega ogni riga e perché conta di più per un server che tratta documenti.
Le sessioni non ci sono più, e ai server di documenti va bene così
La prima voce del changelog rimuove "protocol-level sessions and the Mcp-Session-Id header from the Streamable HTTP transport", cioè le sessioni a livello di protocollo e quell'header dal trasporto Streamable HTTP. Gli endpoint di elenco come tools/list "no longer vary per-connection", non cambiano più da una connessione all'altra, e i server che hanno bisogno di stato tra una chiamata e l'altra "use explicit, server-minted handles passed as ordinary tool arguments": usano handle espliciti, generati dal server e passati come normali argomenti dei tool.
Un server di documenti raramente vuole una sessione. Pensate a cosa conterrebbe: una chiave API, forse un'immagine in attesa di essere letta, forse l'ultimo risultato. Sono tutti dati che preferireste non tenere nella memoria di un processo tra una chiamata e l'altra, dove un bug può consegnarli al chiamante successivo.
Il nostro server ospitato su https://mcp.doc.cheap/mcp era costruito così già prima di questa revisione. Il codice dice che è "stateless on purpose", senza stato di proposito: ogni POST riceve un server nuovo e un trasporto nuovo, risponde e viene buttato via, così "no session lives in memory, a restart loses nothing, and one caller's key can never be seen by another caller's request": nessuna sessione vive in memoria, un riavvio non fa perdere nulla e la chiave di un chiamante non può mai essere vista dalla richiesta di un altro. Il trasporto viene creato senza un generatore di id di sessione, ed è questo che nel TypeScript SDK lo rende senza stato; risponde con JSON semplice invece che con uno stream.
Anche lo schema degli handle descritto dalla revisione c'è già. Quando un agent vuole riavere un risultato salvato, non conta su una sessione che se lo ricordi. scan_document restituisce un id di scansione, e i tool get_scan e delete_scan prendono quell'id come normale argomento scan_id. L'id è l'handle. Qualsiasi istanza del server può rispondere, e lo stesso vale per un client che si riconnette domani.
Se il vostro server tiene qualcosa per sessione, la domanda da porsi è la stessa: può diventare un id che il server restituisce e il client rimanda indietro? Per il lavoro sui documenti la risposta di solito è sì, e di solito è anche un guadagno per la privacy.
Niente handshake: la versione viaggia con ogni richiesta
La seconda voce rende MCP senza stato a livello di protocollo. Lo scambio initialize e notifications/initialized è stato rimosso, e "every request now carries its protocol version and client capabilities in _meta": ogni richiesta porta la versione del protocollo e le capacità del client in _meta. Su HTTP la versione va anche in un header. La pagina su Streamable HTTP dice che ogni POST "MUST include an MCP-Protocol-Version header" e che il valore deve corrispondere a quello in _meta; altrimenti il server risponde 400 con un errore HeaderMismatch.
Ecco come appare nella nuova forma una chiamata a un tool per documenti. Gli header e le chiavi di _meta seguono l'esempio della specifica; il tool e i suoi argomenti sono i nostri, con dei segnaposto:
POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: scan_document
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "scan_document",
"arguments": { "image_base64": "<base64 image>", "retain_hours": 0 },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": { "name": "<client>", "version": "<version>" },
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}
Per il server questo significa tre controlli su ogni richiesta al posto di un solo handshake:
- La versione. Se il server non supporta la versione richiesta, risponde 400 con un
UnsupportedProtocolVersionErrorche elenca le versioni che supporta. - Gli header rispetto al corpo.
Mcp-Methodè obbligatorio su ogni richiesta,Mcp-Namesutools/call,resources/readeprompts/get. Riflettono il metodo e il nome del tool, e una discordanza è un erroreHeaderMismatch. - Le capacità. Tutto ciò che il server prima ricordava da
initializeora lo legge dalla richiesta che ha in mano.
Per un server di documenti gli header contano più di quanto sembri. Una chiamata a scan_document trasporta un'intera immagine in base64; il nostro server ospitato accetta corpi di richiesta fino a 36 MiB. Con Mcp-Method e Mcp-Name negli header, un gateway può instradare, contare o limitare il tool costoso separatamente da quelli economici senza leggere quel corpo. La specifica aggiunge un avvertimento per questi gateway: "SHOULD verify", dovrebbero verificare che l'header della versione indichi una versione che richiede la validazione di header e corpo, e altrimenti rifiutare la richiesta, invece di fidarsi di un header che nessuno ha controllato.
server/discover è obbligatorio
La terza voce aggiunge server/discover, e "servers MUST implement this RPC to advertise their supported protocol versions, capabilities, and identity": i server devono implementare questa RPC per dichiarare le versioni del protocollo supportate, le capacità e la propria identità. I client possono chiamarla per prima per scegliere una versione, oppure usarla come sonda di compatibilità su stdio.
È l'unico cambiamento che un server senza stato non può saltare. Il nostro server espone ancora un piccolo documento di discovery su GET / (nome, versione, trasporto ed endpoint), ma è una semplice pagina web, non il metodo del protocollo. Un server che vuole parlare 2026-07-28 risponde da sé a server/discover.
Risultati: resultType, cache e ordine
Tre voci minori cambiano l'aspetto di un risultato.
resultType. Ora ogni risultato lo porta:"complete"per una risposta normale,"input_required"per la risposta intermedia di una richiesta a più round-trip. I client "MUST treat results from earlier-protocol servers that omit the field as"complete"", cioè devono trattare come"complete"i risultati dei server con protocollo precedente che omettono il campo; così un server più vecchio continua a funzionare, ma uno nuovo lo invia.ttlMsecacheScope. I risultati ditools/list,prompts/list,resources/list,resources/readeresources/templates/listdevono portare un'indicazione di freschezza in millisecondi e un ambito,"public"o"private". Per un server il cui elenco di tool cambia solo al deploy e le cui risorse sono documentazione inclusa nel pacchetto, unttlMslungo con"public"è una scelta naturale. Una risorsa che dipende dalla chiave del chiamante sarebbe"private".- Ordine deterministico dei tool. I server "SHOULD return tools from
tools/listin a deterministic order", per aiutare la cache del client e i cache hit dei prompt. Se i tool sono registrati nel codice in un ordine fisso, lasciateli così; il nostro ne registra sei sempre nello stesso ordine.
Chiedere di più all'utente: richieste a più round-trip
Le revisioni precedenti permettevano a un server di inviare richieste proprie al client nel mezzo di una chiamata: elicitation/create per chiedere qualcosa all'utente, sampling/createMessage per prendere in prestito il modello del client, roots/list per conoscere le cartelle consentite. La revisione sostituisce tutto questo con lo schema Multi Round-Trip Requests. Il server risponde con resultType: "input_required" e un elenco di inputRequests, e il client ripete la richiesta originale con inputResponses.
Per un tool per documenti è il posto giusto per una domanda come "la foto è tagliata a sinistra, ne mandi un'altra?". Siccome il nuovo tentativo è di nuovo la stessa richiesta, sul server non deve restare nulla tra le due chiamate. Un server che non usava nessuna delle richieste avviate dal server, come il nostro, qui non ha nulla da migrare.
Cosa è deprecato e cosa usare al suo posto
Roots, Sampling e Logging sono deprecati. Continuano a funzionare durante un periodo di deprecazione di almeno dodici mesi, ma "new implementations should not add support for them": le nuove implementazioni non dovrebbero aggiungerne il supporto. Le alternative suggerite si adattano bene a un server di documenti:
- Al posto di Roots, passate i file come argomenti dei tool o tramite configurazione. Il nostro server locale legge un percorso di file solo quando nella sua configurazione è impostata una cartella di immagini, e solo dentro quella cartella; un percorso al di fuori viene rifiutato. Il modello sceglie l'argomento; l'operatore sceglie il recinto.
- Al posto di Logging, scrivete i log su stderr o OpenTelemetry. La revisione rimuove anche
logging/setLevel; il livello di log ora viaggia con ogni richiesta in_meta, e i server non devono emettere messaggi di log per richieste che non li hanno chiesti. Anche il contesto di tracciamento (traceparent) può viaggiare in_meta.
I vecchi client continueranno a chiamare
Non tutti i client passano alla nuova versione lo stesso giorno, quindi la pagina sul trasporto descrive cosa deve fare con il traffico più vecchio un server che supporta solo 2026-07-28:
- GET o DELETE sull'endpoint MCP: 405 Method Not Allowed.
- Un header
Mcp-Session-Id: ignorarlo, e non generarne né restituirne mai uno. - Un header
Last-Event-ID: ignorarlo, perché gli stream non si possono più riprendere.
Il nostro server ospitato risponde già a GET e DELETE sul suo endpoint con 405 e un header Allow: POST, OPTIONS, e non genera alcun id di sessione. Un client che parla ancora una revisione precedente lo scopre dal corpo dell'errore: la specifica gli dice di tornare a initialize solo quando il corpo del 400 non è un errore moderno riconosciuto.
A che punto è il nostro server
Per essere precisi: il server ospitato si basa sulla versione 1.30.0 del TypeScript SDK, e il nostro codice non nomina ancora da nessuna parte 2026-07-28. Le parti qui sopra che per lui valgono già sono quelle che derivano dal suo design: niente sessioni, id come handle, 405 per GET e DELETE, file limitati dalla configurazione. server/discover, i controlli sugli header, resultType e i campi della cache sono il lavoro che resta. La pagina del server MCP per l'OCR dei passaporti e la guida al server MCP descrivono ciò a cui i client si collegano oggi.
Se state scegliendo un'API per documenti destinata agli agent, il confronto tra API OCR per passaporti elenca i servizi; il lato MCP merita una domanda a parte a ciascuno di loro.
Una checklist per il vostro tool server
- Nessuno stato vive tra una richiesta e l'altra; tutto ciò che serve a una chiamata successiva è un id restituito dal server.
- La versione di ogni richiesta viene confrontata con l'header
MCP-Protocol-Version, conUnsupportedProtocolVersionErrorper una versione che non supportate. Mcp-MethodeMcp-Namevengono confrontati con il corpo.server/discoverrisponde con versioni, capacità e identità.- I risultati portano
resultType; i risultati di elenco e lettura portanottlMsecacheScope. tools/listrestituisce sempre lo stesso ordine.- GET e DELETE ricevono 405; gli header di sessione e di id evento vengono ignorati.
- I file arrivano come argomenti dentro un recinto configurato, non tramite Roots.
- I log vanno su stderr o OpenTelemetry, non al client.
Il testo completo è su modelcontextprotocol.io, e il post di rilascio ne racconta il contesto. Altro sul nostro server si trova nella pagina del server MCP per l'OCR dei passaporti.
Una domanda per voi: il vostro server MCP teneva in una sessione qualcosa che ora dovete trasformare in un handle? Raccontatecelo nei commenti qui sotto.