Escrever um servidor MCP que devolve a hora atual leva quinze minutos. É o "hello-world" do protocolo: registra uma tool, retorna uma string, o agente lê. Funciona na primeira tentativa e não serve para nada em produção. O MCP é só o encanamento - um jeito padrão de expor tools, resources e prompts para um agente via stdio ou HTTP. O trabalho de verdade é tudo o que o tutorial pula.
Quando um servidor MCP sai do brinquedo e encosta em dados reais, três problemas aparecem na mesma ordem, toda vez: quem pode chamar o quê, quanto cabe na resposta, e o que acontece quando dá errado. Auth, paginação e erros. É aí que mora o design de verdade.
Auth: nunca confie no agente
Um servidor de brinquedo roda em stdio na sua máquina e herda as suas permissões. Um servidor remoto exposto por HTTP é uma superfície de ataque. A spec trata o servidor MCP como um resource server OAuth 2.1: sem token válido, ele responde 401 e devolve onde está o authorization server. O cliente faz o fluxo, volta com um token, e só então a tool executa.
O ponto que mais gente erra é achar que o agente é o dono da autorização. Não é. O token tem
escopo, e o escopo é por requisição, não por conexão. Um agente com um token de leitura não
deveria nem ver a tool de escrita no tools/list - a
lista de tools pode variar conforme as permissões apresentadas. O servidor valida tudo de novo
do lado dele: identidade, escopo, e a autorização contra o recurso específico que está sendo tocado.
Confiar que o modelo "vai se comportar" não é uma estratégia de segurança.
"O agente é um cliente, não uma identidade. Quem decide o que ele pode fazer é o servidor - a cada chamada, contra o escopo do token."
Paginação: a janela de contexto é seu orçamento
A falha mais comum que vejo em servidor MCP é a tool que devolve "todos os registros". Numa tabela de três linhas, lindo. Em produção, o payload estoura a janela de contexto, custa uma fortuna em tokens e enterra a resposta relevante no meio de mil que não importam. Cada token que você empurra é um token de atenção que você gastou.
O protocolo já dá a ferramenta certa: paginação por cursor. O servidor devolve uma página
limitada e um nextCursor opaco; o cliente manda o cursor
de volta para a próxima página. O cursor é opaco de propósito - o cliente não sabe nem deve saber
se por trás dele tem um offset, um token codificado ou um ponteiro de banco. O servidor decide
o tamanho da página, e esse tamanho deveria ser pensado em orçamento de tokens, não em "quantas
linhas couberam na query".
Na prática, isso muda como você desenha a tool. Em vez de list_orders devolvendo tudo, você devolve as colunas que importam, um punhado de registros, e deixa explícito
na descrição que existe um cursor. O agente lê alguns resultados, decide se precisa de mais, e
pagina sob demanda. Quem controla o tamanho da resposta é o servidor, não a sorte.
Erros: transparentes, não opacos
Aqui mora a diferença entre um servidor que o agente consegue usar e um que trava nele. O
MCP separa dois tipos de erro, e essa separação é o detalhe que ninguém lê no tutorial. Erro
de protocolo - tool desconhecida, requisição malformada - vira um erro JSON-RPC e o modelo
quase nunca consegue se recuperar dele. Erro de execução da tool - validação que falhou, API
externa que caiu, regra de negócio violada - volta dentro do resultado, com a flag
isError: true e uma mensagem que o modelo lê.
A regra prática é simples: se o agente tem chance de consertar, o erro tem que ser legível. "Data de partida inválida: precisa ser no futuro, hoje é 02/04/2026" é uma mensagem da qual o modelo se recupera sozinho. Um 500 cru ou uma stack trace inteira é ruído que ele não consegue acionar. Mensagem de erro, num servidor MCP, é interface - escreva como se o leitor fosse o próximo passo do raciocínio do agente, porque é exatamente isso que ele é.
Trate cada tool como uma vaga numa lista de funções que o modelo lê antes de agir. Nome claro e descritivo, descrição que diz quando NÃO usar, e resposta moldada para a atenção do modelo - texto enxuto para ler, structuredContent quando precisa de tipo.
Nome de tool é o primeiro contexto que o modelo vê. getUser diz mais do que handler_3, e uma descrição que
explica os limites economiza uma chamada errada inteira. Servidor MCP bom não é o que tem
mais tools - é o que tem as tools certas, com auth que não confia cego, paginação que
respeita o orçamento, e erros que o agente consegue ler. O hello-world prova que o protocolo
funciona. Esses três detalhes provam que o servidor presta - e são exatamente o que venho
explorando no MCP server de pesquisa que mantenho.