Em 28 de julho de 2026 o Model Context Protocol publicou uma nova revisão da sua especificação. O changelog começa com dois itens que mudam o funcionamento de todo servidor remoto: as sessões no nível do protocolo deixam de existir, e o handshake initialize também. Cada requisição agora leva tudo o que o servidor precisa para respondê-la.
Para muitos servidores isso é uma migração. Para um servidor que lê documentos de identidade é mais uma confirmação, porque esse tipo de servidor já tinha bons motivos para não guardar estado. Este post percorre a revisão do ponto de vista de um servidor de ferramentas: quais mudanças ele pode ignorar, quais precisa construir e quais vale a pena adotar cedo. Os exemplos vêm de um servidor que mantemos, então as lacunas citadas abaixo também são nossas.
Este é o blog do doc.cheap, uma API de OCR para passaportes e documentos de identidade com um servidor MCP na frente. Leia as partes sobre o produto com isso em mente.
A versão curta
| Mudança em 2026-07-28 | O que um servidor de ferramentas sem estado faz |
|---|---|
Sem Mcp-Session-Id, sem sessões |
Nada, se nunca guardou uma |
Sem handshake initialize; versão e capacidades no _meta de cada requisição |
Lê-las a cada requisição e conferir com o cabeçalho MCP-Protocol-Version |
server/discover |
Implementar: é um MUST |
Cabeçalhos Mcp-Method e Mcp-Name obrigatórios |
Conferir se batem com o corpo |
resultType em todo resultado |
Enviar "complete" |
ttlMs e cacheScope nos resultados de listagem e leitura |
Escolher um tempo de cache e um escopo |
| Ferramentas em ordem determinística | Manter estável a ordem de registro |
| Stream HTTP GET e retomada removidos | Responder GET e DELETE com 405 |
| Roots, Sampling e Logging descontinuados | Receber arquivos como argumentos da ferramenta, registrar logs em stderr ou OpenTelemetry |
O resto do post explica cada linha e por que ela pesa mais para um servidor que lida com documentos.
As sessões acabaram, e isso combina com servidores de documentos
O primeiro item do changelog remove "protocol-level sessions and the Mcp-Session-Id header from the Streamable HTTP transport". Endpoints de listagem como tools/list "no longer vary per-connection", e servidores que precisam de estado entre chamadas "use explicit, server-minted handles passed as ordinary tool arguments", ou seja, identificadores explícitos emitidos pelo servidor e passados como argumentos comuns da ferramenta.
Um servidor de documentos raramente quer uma sessão. Pense no que ela guardaria: uma chave de API, talvez uma imagem esperando para ser lida, talvez o último resultado. Tudo isso são dados que você prefere não manter na memória de um processo entre chamadas, onde um bug pode entregá-los ao próximo cliente.
Nosso servidor hospedado em https://mcp.doc.cheap/mcp já era construído assim antes desta revisão. O código diz que ele é "stateless on purpose": cada POST recebe um servidor novo e um transporte novo, responde e é descartado, de modo que "no session lives in memory, a restart loses nothing, and one caller's key can never be seen by another caller's request". O transporte é criado sem gerador de id de sessão, o que no SDK de TypeScript é o que o torna sem estado, e responde com JSON simples em vez de um stream.
O padrão de identificadores que a revisão descreve também já está lá. Quando um agente quer de volta um resultado guardado, ele não depende de uma sessão para lembrá-lo. scan_document retorna um id de scan, e as ferramentas get_scan e delete_scan recebem esse id como um argumento comum scan_id. O id é o identificador. Qualquer instância do servidor pode respondê-lo, e também um cliente que se reconecte amanhã.
Se o seu servidor guarda algo por sessão, a pergunta é a mesma: isso pode virar um id que o servidor retorna e o cliente envia de volta? Em trabalho com documentos a resposta costuma ser sim, e costuma ser também um ganho de privacidade.
Sem handshake: a versão viaja em cada requisição
O segundo item torna o MCP sem estado no nível do protocolo. A troca initialize e notifications/initialized é removida, e "every request now carries its protocol version and client capabilities in _meta". Em HTTP a versão também vai em um cabeçalho. A página do Streamable HTTP diz que todo POST "MUST include an MCP-Protocol-Version header", e que ele deve bater com o valor em _meta; caso contrário, o servidor responde 400 com um erro HeaderMismatch.
Veja como fica uma chamada a uma ferramenta de documentos no novo formato. Os cabeçalhos e as chaves de _meta seguem o exemplo da própria especificação; a ferramenta e seus argumentos são nossos, com marcadores:
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": {}
}
}
}
Para o servidor isso significa três verificações em cada requisição em vez de um único handshake:
- A versão. Se o servidor não suporta a versão pedida, responde 400 com um
UnsupportedProtocolVersionErrorque lista as versões que ele suporta. - Os cabeçalhos contra o corpo.
Mcp-Methodé obrigatório em toda requisição, eMcp-Nameemtools/call,resources/readeprompts/get. Eles espelham o método e o nome da ferramenta, e uma divergência é um erroHeaderMismatch. - As capacidades. O que o servidor antes lembrava do
initializeagora ele lê da requisição em mãos.
Os cabeçalhos importam mais do que parece para um servidor de documentos. Uma chamada a scan_document carrega uma imagem inteira em base64; nosso servidor hospedado aceita corpos de requisição de até 36 MiB. Com Mcp-Method e Mcp-Name nos cabeçalhos, um gateway pode rotear, contar ou limitar a ferramenta cara separadamente das baratas sem ler esse corpo. A especificação traz um aviso para esses gateways: eles "SHOULD verify" que o cabeçalho de versão indica uma versão que exige validação de cabeçalho e corpo, e rejeitar a requisição caso contrário, em vez de confiar em um cabeçalho que ninguém conferiu.
server/discover é obrigatório
O terceiro item adiciona server/discover, e "servers MUST implement this RPC to advertise their supported protocol versions, capabilities, and identity". Os clientes podem chamá-lo primeiro para escolher uma versão, ou usá-lo como teste de compatibilidade em stdio.
Esta é a única mudança que um servidor sem estado não pode pular. Nosso servidor ainda expõe um pequeno documento de descoberta em GET / (nome, versão, transporte e endpoint), mas é uma página web comum, não o método do protocolo. Um servidor que queira falar 2026-07-28 responde ele mesmo a server/discover.
Resultados: resultType, cache e ordem
Três itens menores mudam a cara de um resultado.
resultType. Agora todo resultado o traz:"complete"para uma resposta comum,"input_required"para a resposta intermediária de uma requisição com várias idas e voltas. Os clientes "MUST treat results from earlier-protocol servers that omit the field as"complete"", então um servidor antigo continua funcionando, mas um novo o envia.ttlMsecacheScope. Os resultados detools/list,prompts/list,resources/list,resources/readeresources/templates/listdevem trazer uma indicação de validade em milissegundos e um escopo,"public"ou"private". Para um servidor cuja lista de ferramentas só muda a cada deploy e cujos recursos são documentação empacotada, umttlMslongo com"public"é a escolha natural. Um recurso que depende da chave do cliente seria"private".- Ordem determinística das ferramentas. Os servidores "SHOULD return tools from
tools/listin a deterministic order", para ajudar o cache do cliente e os acertos de cache do prompt. Se as ferramentas são registradas no código em uma ordem fixa, mantenha assim; o nosso registra seis sempre na mesma ordem.
Pedir mais ao usuário: requisições com várias idas e voltas
As revisões anteriores deixavam o servidor enviar suas próprias requisições ao cliente no meio de uma chamada: elicitation/create para perguntar algo ao usuário, sampling/createMessage para usar o modelo do cliente, roots/list para conhecer as pastas permitidas. A revisão substitui isso pelo padrão Multi Round-Trip Requests. O servidor responde com resultType: "input_required" e uma lista de inputRequests, e o cliente repete a requisição original com inputResponses.
Para uma ferramenta de documentos, este é o lugar certo para uma pergunta como "a foto está cortada à esquerda, quer enviar outra?". Como a repetição é a mesma requisição de novo, nada precisa ficar guardado no servidor entre as duas chamadas. Um servidor que não usava nenhuma das requisições iniciadas pelo servidor, como o nosso, não tem nada a migrar aqui.
O que foi descontinuado e o que usar no lugar
Roots, Sampling e Logging foram descontinuados. Continuam funcionando durante um período de transição de pelo menos doze meses, mas "new implementations should not add support for them". As alternativas sugeridas combinam bem com um servidor de documentos:
- Em vez de Roots, passe arquivos como argumentos da ferramenta ou como configuração. Nosso servidor local só lê um caminho de arquivo quando um diretório de imagens está definido na sua configuração, e só dentro desse diretório; um caminho fora dele é recusado. O modelo escolhe o argumento; o operador define a cerca.
- Em vez de Logging, registre logs em stderr ou OpenTelemetry. A revisão também remove
logging/setLevel; o nível de log agora viaja por requisição em_meta, e os servidores não devem emitir mensagens de log para requisições que não pediram. O contexto de trace (traceparent) também pode ir em_meta.
Clientes antigos vão continuar chamando
Nem todos os clientes mudam no mesmo dia, então a página do transporte descreve o que um servidor que suporta apenas 2026-07-28 deve fazer com o tráfego antigo:
- GET ou DELETE no endpoint MCP: 405 Method Not Allowed.
- Um cabeçalho
Mcp-Session-Id: ignorar, e nunca gerar nem devolver um. - Um cabeçalho
Last-Event-ID: ignorar, porque os streams não podem mais ser retomados.
Nosso servidor hospedado já responde GET e DELETE no seu endpoint com 405 e um cabeçalho Allow: POST, OPTIONS, e não gera nenhum id de sessão. Um cliente que ainda fala uma revisão anterior descobre isso pelo corpo do erro: a especificação diz para ele voltar ao initialize somente quando o corpo do 400 não for um erro moderno reconhecido.
Onde nosso servidor está
Para ser exato: o servidor hospedado é construído sobre a versão 1.30.0 do SDK de TypeScript, e nosso próprio código ainda não menciona 2026-07-28 em lugar nenhum. O que acima já vale para ele é o que vem do seu design: sem sessões, ids como identificadores, 405 para GET e DELETE, arquivos limitados por configuração. server/discover, as verificações de cabeçalho, resultType e os campos de cache são o trabalho que falta. A página do servidor MCP de OCR de passaportes e o guia do servidor MCP descrevem ao que os clientes se conectam hoje.
Se você está escolhendo uma API de documentos para agentes, a comparação de APIs de OCR de passaportes lista os serviços; vale perguntar a cada um sobre o lado MCP separadamente.
Um checklist para o seu próprio servidor de ferramentas
- Nenhum estado vive entre requisições; o que uma chamada posterior precisar é um id que o servidor retorna.
- A versão de cada requisição é conferida com o cabeçalho
MCP-Protocol-Version, comUnsupportedProtocolVersionErrorpara uma versão que você não suporta. Mcp-MethodeMcp-Namesão conferidos com o corpo.server/discoverresponde com versões, capacidades e identidade.- Os resultados trazem
resultType; os de listagem e leitura trazemttlMsecacheScope. tools/listretorna sempre a mesma ordem.- GET e DELETE recebem 405; cabeçalhos de sessão e de id de evento são ignorados.
- Os arquivos chegam como argumentos dentro de uma cerca configurada, não pelo Roots.
- Os logs vão para stderr ou OpenTelemetry, não para o cliente.
O texto completo está em modelcontextprotocol.io, e o post de lançamento dá o contexto. Mais sobre o nosso servidor está na página do servidor MCP de OCR de passaportes.
Uma pergunta para você: o seu servidor MCP guardava algo em uma sessão que agora você precisa transformar em um identificador? Conte para a gente nos comentários abaixo.