O que é um servidor MCP? Três que coloquei em produção
O que é MCP (Model Context Protocol), como um servidor MCP difere de uma API REST e o que aprendi com três servidores MCP em produção com o Claude.
Um servidor MCP é um programa que expõe ferramentas e dados para um cliente de IA, como o Claude, num formato que o modelo entende sozinho. Eu tenho três servidores MCP em produção: um num SaaS para empreiteiras, um sobre alvarás de obra de Massachusetts e um sobre leilões oficiais no Brasil. Aqui explico o que é MCP e o que cada um desses servidores me ensinou.
O que é MCP (Model Context Protocol)
MCP significa Model Context Protocol. É um protocolo aberto, criado pela Anthropic, que define como um cliente de IA descobre e chama recursos de um sistema externo. O cliente pode ser o Claude Desktop, o Claude Code, o Codex, o Cursor ou um agente seu.
Um servidor MCP oferece três tipos de coisa:
- Ferramentas (tools): funções que o modelo pode chamar, como
search_permitsoucreate_quote. - Recursos (resources): documentos que o cliente pode ler, como um guia do dataset.
- Prompts: modelos de instrução prontos.
A conversa acontece por stdio (processo local) ou por HTTP. Os meus três servidores usam Streamable HTTP sem estado: cada requisição cria um servidor e um transporte, responde e fecha.
Servidor MCP não é só uma API REST com outro nome
Por baixo, um servidor MCP muitas vezes chama o mesmo código de uma API. A diferença está em quem consome.
Uma API REST é lida por um programador. Ele lê a documentação, escreve o código e decide quando chamar cada rota. Num servidor MCP, quem decide é o modelo, em tempo de execução. Por isso três coisas mudam:
- A descrição da ferramenta vira instrução. O modelo lê o texto de cada tool para escolher qual usar e como. Descrição vaga gera chamada errada.
- O schema de entrada é o contrato. Cada tool declara seus parâmetros (JSON Schema, que eu escrevo em Zod ou com type hints em Python). O cliente valida e o modelo sabe o que mandar.
- O cliente é intercambiável. O mesmo servidor atende Claude, Codex ou qualquer cliente que fale o protocolo. Não escrevo integração por cliente.
O protocolo também tem anotações como readOnlyHint e destructiveHint. O cliente usa isso para decidir o que roda sem pedir permissão.
LedgerJob: uma lista de ferramentas para o agente e para o MCP
O LedgerJob é um SaaS multi-tenant para empreiteiras nos EUA. O dono da empresa opera leads, orçamentos, projetos e faturas conversando com um agente dentro do app. As mesmas 66 ferramentas desse agente ficam disponíveis por um servidor MCP.
O ponto principal é que existe uma lista só. O servidor MCP não tem ferramentas próprias: ele mapeia a lista do agente. Ferramenta nova aparece nos dois lugares no mesmo deploy, sem código duplicado.
export const mcpToolList = assistantTools.map((t) => ({
name: t.name,
description: t.requiresConfirmation
? `IRREVERSIBLE: sends a message, spends money or permanently changes data. Ask the user before calling. ${t.description}`
: t.description,
inputSchema: t.parameters,
annotations: {
title: t.label,
readOnlyHint: Boolean(t.readOnly),
destructiveHint: Boolean(t.requiresConfirmation),
},
}));
Dentro do app, uma ação irreversível (mandar e-mail ou SMS, gastar com terceiros, apagar dados) não roda no loop do agente: vira um card que o usuário confirma. No MCP não existe esse card. O comentário no código diz por quê: lá a aprovação humana é responsabilidade do cliente, como o pedido de permissão do Claude Code. Então essas ferramentas levam o prefixo IRREVERSIBLE na descrição e a anotação destructiveHint.
A identidade vem da API key, nunca de um argumento que o modelo preenche. Cada usuário cria sua chave, que fica guardada só como hash SHA-256 e carrega tenant, usuário e papel. O MCP age como aquele usuário, com as mesmas permissões, rate limit e checagem de plano.
Isso tem uma consequência: o agente e o MCP não passam pelo validador HTTP das rotas. Por isso regra de negócio fica no service. Um bug real, em que o assistente conseguia agendar "das 18h às 17h", levou essa regra para o service, com teste.
O servidor também manda instruções gerais que o cliente lê antes de escolher ferramenta: como lead vira cliente, que orçamento aprovado vira projeto, e "busque antes de agir".
Cape Cod Permits: somente leitura, auditoria e acesso por chave
O Cape Cod Permits junta alvarás de obra de Massachusetts, espalhados em seis sistemas municipais, numa base PostgreSQL. O servidor MCP tem 7 ferramentas, todas somente leitura, e um guia do dataset.
Somente leitura aqui é declarado em cada ferramenta:
const READ_ONLY = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
Cada chave MCP tem nome próprio. Esse nome aparece no log de auditoria, e toda leitura de contato pessoal é registrada com os filtros usados. Cada chave também tem seu limite de requisições por minuto. O comentário no código explica a escolha: um agente não tem ritmo humano, e se você pede "todos os proprietários" ele pagina até acabar. O limite é por credencial, não por IP, porque o orçamento pertence a quem recebeu a chave.
Guiar o modelo pela descrição
Dado público de prefeitura tem armadilhas. Cada município tem um intervalo de datas diferente, o vocabulário de tipo e status muda de um para outro e o custo da obra é autodeclarado. Um modelo sem contexto compara dois municípios e conclui que um teve menos obra, quando na verdade a base dele só vai até julho.
Então as descrições fazem o trabalho de um analista cuidadoso:
list_facetsdiz para ser chamada antes de comparar municípios, porque devolve o intervalo coberto por cada um.permit_statsdiz para responder "quantos" e "quanto" com agregados em vez de paginarsearch_permits, para preferir mediana à média e para comparar quantos registros tinham custo antes de citar a soma.get_permitdiz para ser usada para aprofundar um caso, "nunca em laço sobre uma lista".
O guia do dataset sai como recurso MCP e também como ferramenta (dataset_docs), porque nem todo cliente lê recursos.
No Achado de Leilão, busca única de leilões oficiais no Brasil, o servidor MCP tem 7 ferramentas: busca de lotes, detalhe, agenda, resumo, resultados de arremate, histórico do lote e SQL. A ferramenta de SQL é a mais perigosa e a mais útil. A proteção não depende do modelo: a sessão do banco já abre só leitura e com timeout.
def _query(sql: str, params=()) -> list[dict]:
# sessão só leitura + timeout: nem a consulta_sql consegue escrever ou travar o banco
opts = "-c default_transaction_read_only=on -c statement_timeout=15000"
with psycopg.connect(DATABASE_URL, options=opts, row_factory=dict_row) as conn:
return [_json(r) for r in conn.execute(sql, params or None).fetchmany(MAX_ROWS)]
O limite é de 200 linhas e o token é comparado em tempo constante. A descrição da ferramenta lista as colunas da view, explica que um lote com dup_of preenchido é o mesmo imóvel já contado em outro lote, e pede para incluir o link para o usuário abrir o lote.
Checklist para o seu primeiro servidor MCP
- Comece pelo que já existe. Se você já tem um agente ou uma camada de serviço, exponha essa mesma lista. Não escreva um segundo conjunto de ferramentas.
- Escreva descrições para o modelo. Diga quando usar, quando não usar e qual ferramenta preferir para cada pergunta.
- Mande instruções do servidor. Um parágrafo sobre o domínio evita que o modelo adivinhe como os dados se ligam.
- Identidade vem da credencial. Tenant e usuário nunca são parâmetros que o modelo preenche. Guarde a chave como hash.
- Marque o que é destrutivo. Use
readOnlyHintedestructiveHinte deixe claro na descrição o que exige aprovação. - Proteja no banco, não no prompt. Sessão só leitura, timeout e limite de linhas valem mesmo se o modelo errar.
- Limite e registre por chave. Rate limit por credencial e log de auditoria para dado sensível.
- Corte respostas grandes. Devolva total, pagine e trunque com uma dica de filtro.
Eu desenvolvo esses servidores com o Claude Code. Se você quer um servidor MCP sobre os dados do seu sistema, fale comigo.
Todos os artigos