Epovest
← Todos os guias

Guia

MCP 2026-07-28: a revisão sem estado, e o que ela exige de seu servidor

Por Simon Vasconcelos Lee

Fundador do Epovest

Em 28 de julho de 2026, o Model Context Protocol recebeu sua maior revisão desde a chegada da autorização. Uma única frase carrega quase tudo: o protocolo não tem mais aperto de mãos. A versão, a identidade do cliente e suas capacidades passam a viajar em cada requisição, e o servidor aceita ou recusa cada requisição por conta própria.

Todo o resto decorre dessa frase. Segue o que a revisão retira, o que entra no lugar, e o punhado de exigências fáceis de perder porque nada quebra com barulho quando são esquecidas. Migramos nosso próprio servidor MCP para 2026-07-28 em 2 de agosto de 2026: a lista de verificação do final é a que seguimos.

O que a revisão retira

  • As sessões de protocolo e o cabeçalho Mcp-Session-Id. Os resultados de lista não variam mais de uma conexão para outra. Um servidor que precisa de estado entre chamadas cunha uma chave explícita e a faz circular como um argumento de ferramenta comum.
  • O aperto de mãos initialize e a confirmação notifications/initialized que o encerrava.
  • ping, logging/setLevel e notifications/roots/list_changed. O nível de registro passa a ser definido por requisição, dentro de _meta.
  • O fluxo GET independente. As notificações de mudança passam por uma única requisição subscriptions/listen de longa duração, e o cliente assina apenas os tipos que quer receber.
  • A retomada de um fluxo interrompido. Last-Event-ID e os identificadores de evento SSE desaparecem. Um fluxo cortado perde a requisição em curso, e o cliente a reemite com um identificador novo.

A razão declarada tem a ver com a forma de implantação. Um servidor que não guarda estado de sessão fica atrás de um balanceador de carga comum e escala como qualquer outro serviço HTTP, sem armazenamento compartilhado e sem roteamento fixo. Esse era o objetivo anunciado no plano de 2026, e esta revisão é onde ele chega.

O que entra no lugar do aperto de mãos

server/discover. Os servidores DEVEM implementá-la. Ela responde em uma única chamada com as versões de protocolo que o servidor atende, suas capacidades e sua identidade. Um cliente PODE chamá-la primeiro para escolher a versão de antemão, ou dispensá-la e tratar um erro de versão no caminho.

Os metadados por requisição. Cada requisição carrega seu próprio contexto:

"_meta": {
  "io.modelcontextprotocol/protocolVersion": "2026-07-28",
  "io.modelcontextprotocol/clientInfo": { "name": "example-client", "version": "1.0.0" },
  "io.modelcontextprotocol/clientCapabilities": {}
}

Em HTTP, a mesma versão se repete no cabeçalho MCP-Protocol-Version, e as duas precisam coincidir.

As trocas em várias rodadas. Um servidor que precisa de amostragem, de uma pergunta ao usuário ou da lista de raízes não envia mais uma requisição própria por um fluxo. Ele devolve um resultado com resultType: "input_required" e um campo inputRequests, e o cliente repete a requisição original com os inputResponses correspondentes. A requisição emitida pelo servidor, como forma, sai do protocolo.

As tarefas viram uma extensão. As tarefas experimentais deixam o núcleo e se tornam a extensão oficial io.modelcontextprotocol/tasks, com um tasks/get a consultar em vez de um tasks/result bloqueante.

Cinco exigências fáceis de perder

São as que passam por uma revisão de código e falham diante de um cliente rigoroso.

  1. resultType em todo resultado. Um resultado comum carrega "complete". O cliente deve ler a ausência do campo, num servidor que ficou em uma revisão anterior, como um "complete".
  2. As dicas de cache são exigidas, não opcionais. ttlMs e cacheScope pertencem aos resultados de server/discover, tools/list, prompts/list, resources/list, resources/templates/list e resources/read. ttlMs é uma duração de frescor em milissegundos; cacheScope vale "public" ou "private" e decide se um intermediário compartilhado pode guardar a resposta.
  3. Dois cabeçalhos de requisição, que o servidor precisa fiscalizar. Mcp-Method em cada requisição, Mcp-Name em tools/call, resources/read e prompts/get. Eles espelham valores do corpo para que um gateway roteie e autorize sem lê-lo, e é exatamente por isso que um servidor DEVE recusar com 400 qualquer divergência entre cabeçalho e corpo. Um valor que não cabe em um cabeçalho ASCII imprimível viaja codificado em base64 entre =?base64? e ?=, e o servidor o decodifica antes de comparar.
  4. Novos códigos de erro, em uma faixa reservada à especificação. -32020 para um cabeçalho que contradiz o corpo, -32021 para uma capacidade de cliente exigida e ausente, -32022 para uma versão de protocolo não atendida. O último pesa mais do que parece: ele CARREGA a lista de versões que o servidor atende, e é essa lista que permite ao cliente tentar de novo em vez de concluir que o servidor está quebrado. O recurso não encontrado também passa de -32002 para o -32602 padrão.
  5. Um método desconhecido responde 404, com o erro JSON-RPC no corpo. É esse par que diz a um cliente que sonda estar falando com um servidor atualizado, e não com um ponto de entrada que simplesmente não hospeda aquela rota.

A autorização

O registro dinâmico de clientes foi descontinuado em favor dos Client ID Metadata Documents. O identificador de cliente vira uma URL https com um caminho, apontando para um documento JSON que o próprio cliente hospeda e mantém atualizado. O servidor de autorização lê esse documento, confere que o client_id dentro dele é exatamente a URL de onde veio, e valida o endereço de retorno contra a lista que ele declara. Nada a registrar antes do primeiro fluxo, nada a limpar depois, e o mesmo identificador vale em qualquer servidor de autorização. Um servidor anuncia a capacidade com client_id_metadata_document_supported em seus metadados.

Uma consequência merece ser dita com clareza, já que a especificação a deixa como consideração e não como regra: esse mecanismo faz o servidor de autorização ler uma URL escolhida por quem chama, e num ponto de entrada de tokens público quem chama pode ser qualquer um. Resolva o endereço você mesmo, recuse tudo que não esteja na internet pública, e fixe o endereço resolvido na requisição de saída em vez de apenas conferi-lo. Conferir sem fixar deixa o nome ser resolvido uma segunda vez pelo cliente HTTP, e é nisso que consiste um ataque de reassociação de DNS. Recuse redirecionamentos, limite o tamanho do corpo, mantenha os tempos de espera curtos, e guarde o resultado em cache por uma duração fixa em vez da que o documento pede.

Dois acréscimos mais discretos no mesmo terreno: a resposta de autorização DEVERIA carregar o parâmetro iss da RFC 9207, que o cliente PRECISA validar quando estiver presente e que fecha o ataque de mistura contra um cliente ligado a vários servidores de autorização; e o cliente PRECISA enviar application_type no registro dinâmico, pois um servidor OIDC lê um valor ausente como "web" e passa a recusar os endereços de retorno de um aplicativo nativo.

O que está descontinuado, e por quanto tempo

A revisão também introduz um ciclo de vida das funcionalidades: ativa, descontinuada, removida, com no mínimo doze meses entre uma descontinuação e a primeira remoção possível, mais um registro público de tudo que está descontinuado. Raízes, amostragem, registro de eventos, transporte HTTP+SSE e registro dinâmico de clientes estão hoje nesse estado. Continuam funcionando. Uma implementação nova não os adota.

Atender as duas eras num só ponto de entrada

A maioria dos servidores vai falar a revisão nova e as antigas ao mesmo tempo por um tempo, e nada impede atendê-las no mesmo lugar. A regra: é a versão DECLARADA que escolhe a era, e mais nada. Nenhuma declaração equivale à era do aperto de mãos; uma requisição que declara 2026-07-28 recebe a era sem estado.

Dois detalhes decidem se isso se sustenta.

initialize nunca deve negociar 2026-07-28. Um cliente que envia initialize não fala uma revisão que não tem mais aperto de mãos, seja qual for a versão que peça nele. Concedê-la o deixaria esperando campos que jamais chegariam. Mantenha duas listas: as versões que você atende, anunciadas por server/discover, e as versões que initialize pode negociar, ou seja a primeira lista menos a versão sem estado.

Responda o erro de versão ANTES da checagem da chave. Quem chega com uma versão não atendida e sem autenticação precisa ler o erro de versão. Um 401 não lhe ensina nada acionável, e a lista de versões atendidas é pública de qualquer forma, já que server/discover a entrega sem autenticação.

A lista de verificação

Para passar em seu próprio servidor.

  1. server/discover responde, e sem chave se seus outros métodos públicos respondem sem ela?
  2. Cada resultado carrega seu resultType?
  3. server/discover e tools/list carregam ttlMs e cacheScope?
  4. Você valida MCP-Protocol-Version contra o corpo, e Mcp-Method e Mcp-Name contra seus valores no corpo, com -32020 em caso de divergência?
  5. Uma versão não atendida devolve 400 e -32022, carregando as versões que você atende?
  6. Um método desconhecido devolve 404 com -32601?
  7. initialize ainda responde aos clientes mais antigos, e nunca devolve 2026-07-28?
  8. Se você mantém o servidor de autorização: ele anuncia client_id_metadata_document_supported, e fixa o endereço resolvido antes de ir ler um documento de cliente?

O servidor MCP da Epovest responde em 2026-07-28 assim como em 2025-06-18, 2025-03-26 e 2024-11-05 no mesmo ponto de entrada, e a referência completa está em epovest.com/docs/api.md. Para ligar um assistente a ele, nosso guia para transformar o Claude em seu analista GEO pessoal descreve os dois caminhos de entrada.

Fontes