# 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.

2026-10-15 · Gustavo Karsten

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_permits` ou `create_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:

1. **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.
2. **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.
3. **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](https://gustavokarsten.com/projects/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.

```ts
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](https://gustavokarsten.com/projects/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:

```js
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_facets` diz para ser chamada antes de comparar municípios, porque devolve o intervalo coberto por cada um.
- `permit_stats` diz para responder "quantos" e "quanto" com agregados em vez de paginar `search_permits`, para preferir mediana à média e para comparar quantos registros tinham custo antes de citar a soma.
- `get_permit` diz 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](https://gustavokarsten.com/projects/achado-de-leilao), 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.

```python
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

1. **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.
2. **Escreva descrições para o modelo.** Diga quando usar, quando não usar e qual ferramenta preferir para cada pergunta.
3. **Mande instruções do servidor.** Um parágrafo sobre o domínio evita que o modelo adivinhe como os dados se ligam.
4. **Identidade vem da credencial.** Tenant e usuário nunca são parâmetros que o modelo preenche. Guarde a chave como hash.
5. **Marque o que é destrutivo.** Use `readOnlyHint` e `destructiveHint` e deixe claro na descrição o que exige aprovação.
6. **Proteja no banco, não no prompt.** Sessão só leitura, timeout e limite de linhas valem mesmo se o modelo errar.
7. **Limite e registre por chave.** Rate limit por credencial e log de auditoria para dado sensível.
8. **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](https://gustavokarsten.com/contact).

---
Página: https://gustavokarsten.com/articles/o-que-e-servidor-mcp
Contato: gustavo.karsten@outlook.com · https://www.linkedin.com/in/gustavo-karsten-88a04383/ · https://github.com/gustavo-karsten
