Qual a diferença entre stdio e HTTP no MCP?
O MCP define dois transportes. No stdio, o cliente sobe o servidor como subprocesso e os dois trocam mensagens JSON-RPC delimitadas por linha pelo stdin e pelo stdout. No Streamable HTTP, o servidor expõe um endpoint único e cada mensagem vira um POST, cuja resposta é um objeto JSON ou um stream SSE preso àquela requisição. A semântica do protocolo é a mesma nos dois1.
Essa última frase é a que orienta tudo o que vem depois. A especificação chama o transporte de binding: ele define como a mensagem é enquadrada e entregue, como o metadado da requisição viaja e como cancelamento e término são sinalizados. Ele não define o que as mensagens significam1.
O transporte é a camada de baixo dos três papéis descritos na arquitetura do MCP. A escolha entre os dois quase sempre é a mesma decisão de servidor local ou remoto, vista de outro ângulo, e as consequências de operação aparecem em MCP em produção.
stdio, uma linha por mensagem
O cliente lança o servidor como subprocesso. O servidor lê JSON-RPC do stdin e
escreve JSON-RPC no stdout, uma mensagem por linha, e as mensagens não podem
conter quebra de linha embutida2.
Três regras dessa página explicam quase todo bug de servidor stdio que você vai encontrar.
O servidor não pode escrever no stdout nada que não seja mensagem MCP válida.
Um print() de depuração esquecido no meio do código corrompe o enquadramento e o
cliente para de entender o canal. É a causa número um de “meu servidor não conecta”
e não aparece em nenhum log, porque o log é justamente o que quebrou.
O servidor pode escrever strings UTF-8 no stderr para qualquer finalidade de
log, e o cliente não deve presumir que saída em stderr indica erro2. É
para lá que vai o seu print().
Todas as mensagens compartilham um canal só: não existe stream por requisição. Isso
tem consequência no cancelamento. Como não há stream para fechar, o cliente
cancela mandando uma notificação notifications/cancelled com o ID da requisição,
e o servidor não pode mandar mais nenhuma mensagem daquela requisição depois
disso2.
O desligamento tem uma sequência definida: o cliente fecha o stream de entrada do
filho, espera o servidor sair e, se ele não sair em tempo razoável, termina o
processo à força — em POSIX escalando de SIGTERM para SIGKILL, no Windows via
TerminateProcess ou Job Objects2. O lado do servidor tem uma obrigação
correspondente: sair rápido quando o stdin fecha ou quando a leitura retorna
fim de arquivo. Esse é o sinal de encerramento gracioso portável, e servidor que o
ignora é servidor que só morre no SIGKILL.
Se o processo cai sozinho, o cliente deve reiniciá-lo. E aqui aparece um efeito
direto do protocolo ter virado stateless: como não há sessão, as requisições em voo
simplesmente se perdem e o cliente pode reemitir contra o processo novo. O que
precisa ser refeito são os streams de subscriptions/listen ativos2.
Uma observação que costuma passar batido: o enquadramento do stdio não depende das streams padrão. Uma mensagem JSON-RPC por linha sobre um fluxo de bytes bidirecional confiável funciona igual em socket Unix ou TCP, e a especificação recomenda que transportes customizados sobre esse tipo de canal reaproveitem esse enquadramento em vez de inventar outro1.
Streamable HTTP, um POST por mensagem
O servidor precisa expor um caminho HTTP único, o endpoint MCP, que aceite
POST. Toda mensagem JSON-RPC enviada pelo cliente precisa ser um novo POST para
esse endpoint, e o cliente precisa incluir um cabeçalho Accept listando
application/json e text/event-stream3.
O corpo é uma requisição ou uma notificação, nunca uma resposta. Se for
notificação e o servidor aceitar, a resposta é 202 Accepted sem corpo. Se for
requisição, o servidor devolve Content-Type: application/json com um objeto
único ou Content-Type: text/event-stream com um stream, e o cliente precisa
suportar os dois3.
O stream SSE é o ponto que mais confunde quem vem da revisão antiga. Ele é preso à
requisição: carrega notificações relacionadas àquela chamada, como
notifications/progress, e termina na resposta final. O servidor não pode
mandar requisições JSON-RPC independentes por ali. Streams longos existem, mas por
um caminho explícito: o cliente manda subscriptions/listen e a resposta daquela
requisição é o stream que fica aberto entregando as notificações que ele
assinou3.
Duas recomendações operacionais que evitam dor de cabeça: incluir
X-Accel-Buffering: no na resposta SSE, para proxies reversos não bufferizarem os
eventos, e emitir periodicamente uma linha de comentário SSE como keep-alive nos
streams longos, para intermediários não derrubarem a conexão em períodos de
silêncio3.
O cancelamento aqui é o oposto do stdio: fechar o stream de resposta precisa ser tratado pelo servidor como cancelamento daquela requisição. Como cada requisição tem seu próprio stream, a desconexão é inequívoca3.
Os cabeçalhos que o servidor cobra
A revisão 2026-07-28 espelha campos do corpo em cabeçalhos HTTP para que
balanceadores e gateways possam rotear sem abrir o JSON. Isso é obrigatório, não
opcional.
Todo POST precisa levar MCP-Protocol-Version, e o valor precisa bater com
io.modelcontextprotocol/protocolVersion no _meta do corpo. Se não bater, o
servidor precisa recusar com 400 Bad Request e o erro HeaderMismatch, de
código -32020. Também são obrigatórios Mcp-Method, com o nome do método, e
Mcp-Name, com params.name ou params.uri, nas chamadas de tools/call,
resources/read e prompts/get3.
Existe ainda uma extensão que espelha parâmetro de tool em cabeçalho: uma
propriedade x-mcp-header no schema do argumento faz o cliente enviar
Mcp-Param-{Nome} com aquele valor. Serve para roteamento por região ou por
tenant sem inspeção de corpo. A spec vem com o aviso óbvio junto: não marque
parâmetro sensível assim, porque o valor fica visível para qualquer intermediário
da rede3.
Do lado de segurança, três exigências. O servidor precisa validar o cabeçalho
Origin em toda conexão e responder 403 Forbidden se ele estiver presente e
inválido; deve escutar apenas em 127.0.0.1 quando roda local, em vez de
0.0.0.0; e deve implementar autenticação em todas as conexões. Sem isso, um
atacante consegue usar DNS rebinding para interagir com o seu servidor MCP local a
partir de um site remoto3.
O que mudou em 28 de julho de 2026
A revisão 2026-07-28 mudou a forma do Streamable HTTP e quebra cliente antigo. As
três remoções que importam3:
- O endpoint GET de stream saiu. Não existe mais stream autônomo aberto por GET para receber mensagens iniciadas pelo servidor.
- As sessões de protocolo saíram. O cabeçalho
Mcp-Session-Id, e o DELETE que encerrava a sessão, não fazem parte desta revisão. - A retomada por
Last-Event-IDnão é suportada. Se a conexão cai no meio de uma chamada, a requisição se perde.
Um servidor que implementa só esta revisão e recebe tráfego antigo deve responder
405 Method Not Allowed a GET e DELETE no endpoint, ignorar Mcp-Session-Id sem
cunhar nem ecoar identificador, e ignorar Last-Event-ID3.
O SSE não morreu, o transporte com esse nome está de saída
Aqui é onde quase todo texto sobre MCP erra, e a afirmação precisa de data.
Em 30 de julho de 2026, o registro de features depreciadas lista o transporte
HTTP+SSE da revisão 2024-11-05 — aquele de dois endpoints, com um GET que abria
o stream e um POST separado para as mensagens — como depreciado desde a revisão
2025-03-26, reclassificado sob a política formal de ciclo de vida pela SEP-2596,
com remoção mais cedo possível três meses depois de essa política atingir o estado
Final4. Novas implementações não devem adotá-lo, e as existentes devem
migrar.
O SSE como tecnologia continua no centro do transporte atual: sempre que uma
requisição precisa emitir progresso antes do resultado, ou quando o cliente abre
um subscriptions/listen, a resposta é um stream SSE3. Dizer “o MCP abandonou
o SSE” está errado. Dizer “o transporte HTTP+SSE de dois endpoints está
depreciado” está certo.
O mesmo registro traz o resto do que está de saída em 30 de julho de 2026: roots,
sampling, logging e o registro dinâmico de cliente, todos depreciados na
2026-07-28, com remoção possível na primeira revisão publicada em 28 de julho de
2027 ou depois4.
Qual escolher
Servidor na máquina do usuário, servindo essa pessoa: stdio. Sem rede, sem autenticação, sem porta aberta, e o ciclo de vida do processo é do cliente. É o que acontece quando você acrescenta um servidor ao arquivo de configuração do seu editor.
Servidor local mas acessado por HTTP, o que é legítimo em alguns setups:
Streamable HTTP, escutando em 127.0.0.1 e validando Origin. Sem essas duas
coisas, qualquer página aberta no browser do usuário alcança o servidor.
Servidor em outra máquina, com um ou muitos usuários: Streamable HTTP com autenticação. Como não existe sessão de protocolo, cada requisição carrega a própria credencial e qualquer réplica atende qualquer requisição. Isso é o que torna o escalonamento horizontal trivial e o que obriga o servidor a não guardar estado na instância.
Onde cada um falha
stdio não tem isolamento. O servidor roda com as permissões do usuário, na máquina do usuário, com acesso ao mesmo sistema de arquivos e às mesmas variáveis de ambiente. Não há sandbox no protocolo. Instalar um servidor stdio de terceiro é executar código de terceiro.
stdio não escala para além de uma pessoa. Um processo por cliente, na máquina de cada um. Dez pessoas usando o mesmo servidor são dez instalações para atualizar.
HTTP transformou perda de conexão em perda de trabalho. Sem Last-Event-ID, um
POST de tool que estava rodando há quarenta segundos e perde a conexão perde tudo.
O cliente precisa reemitir com um id novo, e o servidor precisa que a operação
seja idempotente ou reversível — coisa que a maior parte dos servidores não é.
HTTP local sem Origin é uma porta aberta. É o único modo de falha desta lista
que a especificação nomeia explicitamente, com o vetor descrito: DNS rebinding a
partir de um site remoto.
O cancelamento tem semânticas diferentes e o código costuma ter uma só. No stdio o cliente manda uma notificação; no HTTP ele fecha o stream. Um servidor escrito assumindo um dos dois vai continuar trabalhando depois do cancelamento no outro. Um estudo de medição de 2025 sobre agentes com MCP chegou a recomendar mecanismos de aborto de tarefa entre as otimizações mais relevantes5.
Quanto custa
O custo do transporte em si é pequeno perto do custo do contexto, e é útil manter a proporção em mente. O mesmo estudo de 2025 mediu interações com MCP e mostrou que a inflação de tokens vem do conjunto de prompt de sistema, definições de tool e histórico, não do canal por onde as mensagens passam5.
O que o transporte cobra é operação. Em stdio, um processo por servidor por usuário, com memória e ciclo de vida para gerenciar, e atualização que depende da máquina de cada pessoa. Em HTTP, um serviço para hospedar, autenticar, monitorar e limitar, mais o custo fixo de TLS e de um round-trip de rede por mensagem em vez de uma escrita em pipe.
A conta que decide raramente é de latência. É de quem administra o processo.
Por onde começar
- Comece por stdio. Sem rede e sem autenticação, o ciclo de desenvolvimento é mais curto e os erros aparecem no lugar certo.
- Redirecione todo log para
stderrantes da primeira linha de lógica. Em Python,print()semfile=sys.stderré o bug clássico. - Escreva o encerramento no
stdinfechado. Servidor que só morre noSIGKILLdeixa processo órfão na máquina do usuário. - Ao migrar para HTTP, implemente os três cabeçalhos obrigatórios de uma vez.
MCP-Protocol-Version,Mcp-MethodeMcp-Name, com validação contra o corpo. - Valide
Origine escute em127.0.0.1mesmo em desenvolvimento. O hábito é o que evita subir para produção com0.0.0.0. - Assuma que qualquer requisição pode ser reemitida. Sem retomada de stream e sem sessão, idempotência deixou de ser refinamento e virou requisito.
O passo 6 é o que mais muda em relação ao que os tutoriais escritos antes de julho de 2026 ensinam.
Footnotes
-
A página de visão geral de transportes da revisão
2026-07-28é a fonte para a definição de transporte como binding, para a lista dos dois transportes padrão e para a recomendação de reaproveitar o enquadramento do stdio em transportes customizados. Verificada em 30 de julho de 2026. ↩ ↩2 ↩3 -
A página do transporte stdio da mesma revisão é a fonte para o enquadramento por linha, para as regras de
stdoutestderr, para o cancelamento pornotifications/cancelled, para a sequência de desligamento e para o comportamento em terminação inesperada. ↩ ↩2 ↩3 ↩4 ↩5 -
A página do Streamable HTTP da mesma revisão é a fonte para o endpoint único, para as regras de
Accepte de resposta, para o modelo de stream por requisição, para os cabeçalhos obrigatórios e o erroHeaderMismatch, para as exigências deOrigine bind local, e para as três remoções desta revisão. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 -
O registro de features depreciadas da revisão
2026-07-28é a fonte para o estado do transporte HTTP+SSE de 2024-11-05 e para as datas de remoção mais cedo possível de roots, sampling, logging e registro dinâmico de cliente. ↩ ↩2 -
Ding et al. (2025) mediram interações de agentes com MCP e atribuíram a inflação de tokens ao prompt de sistema, às definições de tool e ao histórico, recomendando chamadas de tool em paralelo e mecanismos de aborto de tarefa. ↩ ↩2
Perguntas frequentes
- Quais transportes o MCP define?
- Dois padrão, na revisão 2026-07-28: stdio, em que o cliente sobe o servidor como subprocesso e troca mensagens delimitadas por linha pelas streams padrão, e Streamable HTTP, em que cada mensagem é um POST para um endpoint único. A semântica do protocolo é idêntica nos dois; muda só o enquadramento.
- O que é Streamable HTTP no MCP?
- É o transporte de rede do MCP. O servidor expõe um endpoint único que aceita POST, cada requisição JSON-RPC vira um POST, e a resposta volta como um objeto JSON ou como um stream SSE preso àquela requisição. Ele substituiu o transporte HTTP+SSE de dois endpoints na revisão 2025-03-26.
- O SSE foi depreciado no MCP?
- O transporte HTTP+SSE de dois endpoints, da revisão 2024-11-05, está depreciado desde 2025-03-26 e consta no registro de features depreciadas em 30 de julho de 2026. O SSE em si continua em uso: o Streamable HTTP responde com stream SSE sempre que a requisição exige eventos antes do resultado.
- Qual transporte MCP devo usar?
- Se o servidor roda na máquina de quem usa e serve uma pessoa, stdio. Em qualquer outro caso, Streamable HTTP com autenticação. Rodar HTTP em localhost também é legítimo, mas exige validar o cabeçalho Origin e escutar em 127.0.0.1, senão qualquer página aberta no browser alcança o servidor.
- O cabeçalho Mcp-Session-Id ainda existe?
- Não na revisão 2026-07-28, que removeu as sessões de protocolo junto com o endpoint GET de stream e a retomada por Last-Event-ID. Um servidor que só implementa essa revisão deve responder 405 a GET e DELETE no endpoint, ignorar Mcp-Session-Id sem ecoar nada e ignorar Last-Event-ID.
- Por que o servidor stdio quebra quando eu imprimo algo?
- Porque o stdout é o canal do protocolo. A especificação proíbe o servidor de escrever no stdout qualquer coisa que não seja mensagem MCP válida, e uma linha solta de print corrompe o enquadramento. Log de servidor stdio vai para stderr, que o cliente pode capturar, encaminhar ou ignorar.
Referências
- Agentic AI Foundation. Model Context Protocol specification, revision 2026-07-28 — Transports (2026)
- Agentic AI Foundation. Model Context Protocol specification, revision 2026-07-28 — stdio transport (2026)
- Agentic AI Foundation. Model Context Protocol specification, revision 2026-07-28 — Streamable HTTP transport (2026)
- Agentic AI Foundation. Model Context Protocol — Deprecated features registry, revision 2026-07-28 (2026)
- Ding, Z., Zhu, M., Liu, Y.. Network and Systems Performance Characterization of MCP-Enabled LLM Agents (2025)arXiv:2511.07426