Há alguns anos, ajudei a tirar um aplicativo de sessões fixas para que pudéssemos escalar atrás de um balanceador de carga round-robin comum. No papel, foi uma mudança de infraestrutura. Todo serviço deveria ser sem estado, portanto, a remoção da afinidade da sessão não deveria ter mudado nada.

Nada caiu. CPU parecia normal. Cada exame de saúde permaneceu verde. Mas os usuários começaram a relatar fluxos de trabalho que retrocediam aleatoriamente. Uma solicitação continuaria exatamente de onde a anterior parou. O próximo se comportaria como se pertencesse a uma conversa totalmente diferente.

O bug não estava em nossa lógica de negócios. Foi uma suposição que herdamos sobre onde residia a correlação. Confiávamos silenciosamente na plataforma para lembrar quais solicitações pertenciam uma à outra. Depois que essa responsabilidade desapareceu, nosso aplicativo não falhou ruidosamente. Tornou-se simplesmente inconsistente de maneiras que eram muito difíceis de reproduzir.

Nós consertamos da maneira óbvia. Paramos de depender da plataforma e começamos a passar um identificador explícito em cada solicitação. Funcionou e continuou funcionando, porque a coisa que carregava aquele identificador fez exatamente o que mandamos.

Esse é o incidente em que fiquei pensando enquanto lia a nova especificação do Model Context Protocol.

Quando o MCP despachou seu núcleo sem estado em 28 de julho, fiz o que todo mundo fez. Abri o changelog, encontrei as notas de migração do SDK e analisei o trabalho. A diferença de código era menor do que eu esperava. Encarei isso como uma boa notícia por cerca de quatro horas.

Eu estava lendo isso como uma mudança de transporte. Sessões encerradas, solicitações independentes, sem roteamento fixo. Tudo verdade e tudo coberto em outro lugar. O que perdi foi o que os mantenedores fizeram com a correlação.

Eles não o apagaram.

Eles entregaram para a modelo.

O que a especificação realmente mudou

Enterrado na postagem de lançamento está a orientação para servidores que ainda precisam de estado nas chamadas: crie um identificador explícito de uma ferramenta, como um ID de cesta ou um ID de navegador, e faça com que o modelo o devolva como um argumento comum em chamadas posteriores. Os mantenedores são claros sobre o porquê. O modelo pode ver o identificador e encadeá-lo entre ferramentas, em vez de ocultar o estado em uma conexão que você não pode inspecionar.

Do ponto de vista da engenharia, acho que eles estão certos. Ele é escalável, sobrevive a um balanceador de carga e corresponde ao modo como as APIs HTTP funcionam há décadas.

Mas leia novamente como um operador. O identificador que seu servidor usa para decidir qual cesta, qual documento ou qual inquilino é agora transportado por um modelo de linguagem, mantido em sua janela de contexto e reemitido posteriormente.

Acabamos de incluir um LLM no caminho da solicitação. Essa não é uma frase que eu esperava escrever sobre uma especificação de transporte.

Isso significa que a correção do seu servidor agora depende, em parte, do modelo que carrega fielmente um identificador de um turno para o outro.

Há uma objeção que ouço sempre. O MCP sempre foi apátrida no que importa, dizem as pessoas. Nunca segurou seu carrinho de compras. Ele nunca armazenou sua solicitação pull. Conecte um calendário e a modelo não saberá sobre a reunião que você adicionou há dez minutos até que algo a busque. Tudo correto e verdadeiro desde o lançamento.

Mas o MCP sempre teve dois tipos de Estado. O primeiro são os dados do seu aplicativo, que residem no seu aplicativo, no GitHub ou no Outlook. O MCP nunca foi dono disso e ainda não é.

O segundo é o estado da sessão do protocolo: o handshake, os recursos negociados e o identificador da sessão que informava ao servidor que duas solicitações pertenciam à mesma conversa. O MCP era o dono daquele, e foi esse que desapareceu.

A confusão segue uma direção conveniente. Se você acredita que o MCP sempre foi sem estado, esta versão parece uma otimização de escalonamento sem consequências arquitetônicas.

Não é.

O protocolo não removeu o estado do seu aplicativo. Ele transferiu a correlação de uma sessão gerenciada por protocolo para um argumento que o modelo carregava de um turno para o outro.

Temos medição sobre isso

O que transforma isso de uma observação em algo que prejudica as equipes é que encadear um identificador entre turnos não é novidade. Tem um nome na literatura de pesquisa. A fusão de informações entre voltas está entre os modos de falha mais estudados no comportamento do modelo multivoltas.

“LLMs Get Lost in Multi-Turn Conversation”, um excelente artigo no ICLR 2026, executou mais de 200.000 conversas simuladas em quinze modelos líderes de peso aberto e fechado. Ele encontrou uma queda média de desempenho de 39% em configurações multivoltas em comparação com configurações de volta única com informações idênticas. A chamada de função foi uma das seis tarefas testadas.

Primeiro, como a degradação se decompõe. A aptidão cai cerca de 16%. A falta de confiabilidade aumenta 112%. Os modelos permanecem capazes e tornam-se dramaticamente menos consistentes. Se você enviou algo agente, sabe como é visto de fora: um sistema que funciona em todas as demonstrações, passa em todos os testes que você escreveu e produz um resultado inexplicável por semana que ninguém consegue reproduzir.

Em segundo lugar, nem tudo está degradado. A tradução, que pode ser feita frase por frase sem dependência entre turnos, não apresentou nenhuma degradação. O que desmoronou foram as tarefas que exigiam que as informações fossem transportadas e fundidas entre turnos.

Isso é exatamente o que é uma alça.

Portanto, o protocolo realocou o estado da sessão para o único lugar onde os modelos de evidências têm maior dificuldade: transportar informações de maneira confiável entre os turnos. Fê-lo por boas razões.

Este é um modo de falha que encontrei repetidamente em sistemas agentes e é por isso que me tornei insuportável em relação à observabilidade. Quando um modelo passa pelo identificador errado, seu servidor não recebe um erro. Obtém uma solicitação bem formada com um argumento de aparência válida. Se o identificador apontar para um objeto real, a chamada será bem-sucedida.

Nenhuma linha de log indica que o modelo perdeu o fio. Nenhuma métrica fica vermelha. Uma sessão que se divide silenciosamente em duas cestas meio preenchidas se parece exatamente com um usuário que mudou de ideia.

A Red Hat publicou recentemente um relatório sobre o incidente que mostra seu formato, sem nenhum MCP envolvido. Um agente precisava cobrar a Conta A. O identificador estava bem ali no prompt. O modelo selecionou a Conta B. A API de cobrança aceitou a chamada e quatro mil dólares foram para o cliente errado. Ele passou em todos os testes de preparação.

No software comum, uma chamada mal roteada é contida por duas coisas: o escopo da credencial limita o que uma carga de trabalho pode alcançar e a API downstream impõe quais parâmetros são válidos para esse chamador. Ninguém enviaria um aplicativo da Web onde um conjunto de credenciais pudesse gravar no registro de faturamento de cada cliente. Enviamos agentes assim o tempo todo.

Agora pegue essa falha e faça do identificador algo que o modelo carrega nas curvas, em vez de algo entregue a ele em um único prompt.

Há uma consequência de segunda ordem que não vi ser levantada. Siga a corrente. O comportamento do seu servidor depende do identificador. A alça é transportada pelo modelo. O modelo é escolhido pelo cliente e não por você.

Portanto, a confiabilidade do seu servidor MCP agora depende, em parte, do modelo que o cliente estiver usando. Isso varia entre fornecedores, atualizações de modelos e entre o mesmo cliente ao longo do tempo. O artigo do ICLR encontrou uma distribuição significativa entre os quinze modelos testados. Seu conjunto de integração é executado em um deles.

O que estou fazendo sobre isso

Nada exótico. Os controles são comuns. Apenas o motivo é novo.

Autorizo ​​todos os tratamentos contra o chamador autenticado em todas as solicitações, sem isenção para ferramentas internas. Dou-lhes entropia real e vidas curtas. Eu uso chaves de idempotência para que uma nova tentativa após um identificador perdido não crie silenciosamente um segundo objeto. E eu testo na profundidade da curva trinta, em vez da curva um, porque é na curva um que todos esses sistemas parecem bem.

A leitura adversa disso não é minha. A equipe de pesquisa de ameaças da Akamai chegou lá primeiro, e a SecurityWeek cobriu isso antes da especificação final: uma vez terminadas as sessões de protocolo, os identificadores que as substituem tornam-se a superfície de ataque, abrindo caminhos para o sequestro de fluxo de trabalho, leitura de dados de outro agente e ações entre locatários.

Eu acrescentaria uma ruga.

Um identificador de sessão residia em um cabeçalho de protocolo que o modelo nunca viu. Um identificador fica na janela de contexto, próximo ao que a última chamada de ferramenta retornou. Se alguma de suas ferramentas ingerir conteúdo não confiável, seja uma página da web, um corpo de ticket ou um e-mail de cliente, seu identificador de estado e o texto de um invasor agora ficam no mesmo contexto, interpretados pelo mesmo modelo.

Prefiro ter identificadores explícitos que possam auditar do que o estado da sessão oculto em uma conexão. Essa é a melhor arquitetura e eu teria votado nela.

Mas há uma diferença entre um estado que você pode ver e um estado em que você pode confiar.

Esta versão resolveu o primeiro problema introduzindo o segundo.

O que deixa a questão na qual não consigo parar de pensar.

Se o identificador agora é transportado pelo modelo, qual é exatamente o limite que decide se ele deveria ser confiável em primeiro lugar?

Essa não é mais uma questão de transporte.

É uma questão de identidade e a resposta não está nesta especificação.