Skip to main content
Glama
README.md
# MCP Brasil

Servidor [MCP](https://modelcontextprotocol.io) (Model Context Protocol) escrito em TypeScript que expõe dados públicos brasileiros — CEP, CNPJ, CPF, bancos, feriados nacionais, municípios e DDD — como _tools_ e _resources_ para qualquer cliente MCP (Claude Desktop, Claude Code, ou uma integração própria).

> ⚠️ **Licença proprietária.** Este código é disponibilizado publicamente apenas para consulta/avaliação (ex.: portfólio). Cópia, uso, execução, modificação ou distribuição não são permitidos sem autorização prévia e por escrito do autor. Veja [`LICENSE`](./LICENSE).

---

## Índice

- [Visão geral](#visão-geral)
- [Por que este projeto existe](#por-que-este-projeto-existe)
- [Tools disponíveis](#tools-disponíveis)
- [Resources disponíveis](#resources-disponíveis)
- [Transportes](#transportes)
- [Arquitetura](#arquitetura)
- [Estrutura de pastas](#estrutura-de-pastas)
- [Stack técnica](#stack-técnica)
- [Segurança e robustez](#segurança-e-robustez)
- [Estratégia de testes](#estratégia-de-testes)
- [Executando localmente](#executando-localmente)
- [Configuração (variáveis de ambiente)](#configuração-variáveis-de-ambiente)
- [Licença](#licença)

---

## Visão geral

LLMs não têm, nativamente, acesso a dados cadastrais e de referência do Brasil: não sabem o endereço de um CEP, se um CNPJ está ativo, quais são os feriados nacionais de um ano, ou qual estado corresponde a um DDD. Para que um agente de IA seja útil em fluxos de cadastro, cobrança, atendimento ou integração no contexto brasileiro, ele precisa de acesso confiável a esses dados — com validação de entrada, cache, tratamento de falhas e mensagens de erro que o próprio modelo consiga interpretar.

O **MCP Brasil** resolve isso implementando o protocolo MCP (o padrão aberto usado pelo Claude e por um número crescente de outros clientes de IA para conectar modelos a ferramentas e dados externos) sobre um conjunto de APIs públicas brasileiras: [BrasilAPI](https://brasilapi.com.br), [ViaCEP](https://viacep.com.br) e o [IBGE](https://servicodados.ibge.gov.br). O resultado é um binário que roda com `npx mcp-brasil` e se conecta a qualquer cliente MCP.

## Por que este projeto existe

Este projeto nasceu como peça de portfólio técnico, para demonstrar:

- Domínio do protocolo MCP e do SDK oficial (`@modelcontextprotocol/sdk`), incluindo _tools_, _resources_ e os dois transportes padrão (`stdio` e `streamable HTTP`).
- Engenharia de integração de verdade: múltiplos providers HTTP com _fallback_, cache com TTL, _rate limiting_ por provedor, retry com backoff, timeout, e tratamento cuidadoso de erros de API (inclusive comportamentos inconsistentes entre elas — ver [Segurança e robustez](#segurança-e-robustez)).
- TypeScript rigoroso, com validação de schema via Zod convertida automaticamente em JSON Schema para as _tools_.
- Uma suíte de testes em três camadas (unitária, integração com APIs mockadas de forma realista, e _end-to-end_ com o binário real), com cobertura de 100% nas partes testáveis.

## Tools disponíveis

| Tool                   | Entrada             | Descrição                                                                                                                                                                                       | Cache   |
| ---------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `validar_documento`    | `documento: string` | Valida CPF ou CNPJ (dígito verificador) e retorna o documento formatado. Detecta o tipo automaticamente pelo número de dígitos. **100% offline**, não faz nenhuma chamada de rede.              | —       |
| `consultar_cep`        | `cep: string`       | CEP → logradouro, bairro, cidade, UF (e coordenadas, quando disponíveis). Tenta o ViaCEP primeiro; se falhar por motivo de infraestrutura, cai para a BrasilAPI automaticamente.                | 24h     |
| `consultar_cnpj`       | `cnpj: string`      | CNPJ → razão social, nome fantasia, situação cadastral, atividade (CNAE), porte, endereço e sócios. Valida o dígito verificador _antes_ de consultar a API, evitando uma chamada desnecessária. | 1h      |
| `listar_bancos`        | —                   | Lista todos os bancos brasileiros registrados no Banco Central (código COMPE, nome, ISPB).                                                                                                      | 7 dias  |
| `consultar_banco`      | `codigo: string`    | Um banco específico pelo código COMPE (ex.: `"341"` para Itaú).                                                                                                                                 | 7 dias  |
| `feriados`             | `ano: number`       | Feriados nacionais de um ano (data, nome, tipo).                                                                                                                                                | 30 dias |
| `consultar_municipios` | `uf: string`        | Municípios de uma UF, com o respectivo código IBGE.                                                                                                                                             | 30 dias |
| `buscar_ddd`           | `ddd: string`       | Estado e lista de cidades atendidas por um DDD.                                                                                                                                                 | 30 dias |

Cada _tool_ retorna, ao mesmo tempo, um bloco de texto legível (para o modelo ler diretamente) e um bloco `structuredContent` em JSON (para o modelo — ou o código que orquestra o agente — consumir de forma programática).

## Resources disponíveis

| URI                   | Descrição                                                                                                                                 |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `brasil://uf/{sigla}` | Sigla, nome e região de uma unidade federativa, via IBGE. Ex.: `brasil://uf/SP` → `{"sigla":"SP","nome":"São Paulo","regiao":"Sudeste"}`. |

## Transportes

O servidor suporta os dois transportes definidos pela especificação MCP:

- **`stdio`** (padrão): usado por Claude Desktop e Claude Code, que gerenciam o processo filho e conversam com ele via stdin/stdout. Todo log vai para **stderr** — stdout é reservado exclusivamente para o canal JSON-RPC.
- **`HTTP` (streamable)**: para hospedar o servidor você mesmo. Ative com `MCP_TRANSPORT=http`; o servidor expõe `POST /mcp`. Roda em modo _stateless_ (cada requisição HTTP recebe sua própria instância de servidor/transporte MCP; os caches e o rate limiter, por serem módulos singleton, continuam compartilhados entre requisições).

## Arquitetura

```
Cliente MCP (Claude Desktop / Code / outro)
        │  JSON-RPC (stdio | HTTP)
┌───────▼────────────────────────────────┐
│ server/   McpServer (SDK oficial)      │
│   registra tools e resources           │
├─────────────────────────────────────────┤
│ tools/    uma pasta por tool           │
│   schema (Zod) + handler + format      │
├─────────────────────────────────────────┤
│ providers/ clients HTTP tipados        │
│   BrasilAPI, ViaCEP, IBGE + fallback   │
├─────────────────────────────────────────┤
│ infra/    cache, rate-limit, http,     │
│           logger, config               │
└─────────────────────────────────────────┘
```

Padrões usados:

- **Injeção de dependência via factory**: cada handler é criado por uma função `createXHandler(deps)` que recebe o provider e o cache como dependências. Isso permite testar a lógica de cada _tool_ com providers falsos, sem tocar a rede, e ainda assim conectar instâncias reais no `index.ts` de cada _tool_.
- **Provider com fallback**: `FallbackCepProvider` tenta o ViaCEP e, só em caso de falha de infraestrutura (não de "CEP não existe"), cai para a BrasilAPI. Um CEP genuinamente inexistente nunca é mascarado tentando o próximo provider.
- **Cache com deduplicação de chamadas concorrentes**: `Cache.getOrLoad` guarda a _promise_ em andamento por chave, então duas chamadas simultâneas para o mesmo CEP disparam **uma** requisição de rede, não duas.
- **Rate limiting por provedor**: um _token bucket_ por nome de provider (`viacep`, `brasilapi`, `ibge`), aplicado de forma transparente em toda chamada HTTP, para não abusar das APIs públicas gratuitas usadas pelo servidor.

## Estrutura de pastas

```
mcp-brasil/
├── bin/mcp-brasil.js            # entry point do npx
├── src/
│   ├── index.ts                 # bootstrap + escolha de transporte
│   ├── server/
│   │   ├── create-server.ts     # instancia o McpServer e registra tools/resources
│   │   └── transports/{stdio,http}.ts
│   ├── tools/                   # uma pasta por tool: schema.ts, handler.ts, format.ts, index.ts
│   ├── providers/                # clients HTTP tipados + fallback
│   ├── resources/                 # resources MCP (uf.resource.ts)
│   ├── infra/                    # cache, rate-limit, http (retry/timeout), logger, config
│   └── domain/                   # algoritmos de CPF/CNPJ, erros de domínio
├── tests/
│   ├── unit/                    # algoritmos, cache, rate limiter, handlers com providers fake
│   ├── integration/              # providers reais contra APIs mockadas (MSW), servidor via InMemoryTransport
│   └── e2e/                      # binário real via stdio e via HTTP
└── examples/                     # configs de exemplo para Claude Desktop e Claude Code
```

## Stack técnica

| Camada            | Tecnologia                            | Motivo                                                |
| ----------------- | ------------------------------------- | ----------------------------------------------------- |
| Protocolo         | `@modelcontextprotocol/sdk`           | SDK oficial do MCP                                    |
| Validação         | Zod                                   | Schemas viram JSON Schema das _tools_ automaticamente |
| HTTP              | `fetch` nativo (Node 20+) + `p-retry` | Sem dependências pesadas tipo axios                   |
| Cache             | `lru-cache`                           | TTL e limite de memória configuráveis                 |
| Build             | `tsup`                                | Gera um único bundle ESM executável via `npx`         |
| Testes            | Vitest + MSW                          | Determinismo — nenhum teste depende de rede real      |
| Lint / Formatação | ESLint + Prettier                     |                                                       |

## Segurança e robustez

Pontos que foram verificados e endurecidos deliberadamente, não só assumidos:

- **Rate limiting real**: cada chamada HTTP passa por um _token bucket_ por provedor antes de sair. Não é apenas uma classe isolada com testes — está de fato no caminho de toda requisição (`infra/http.ts`).
- **Validação de entrada em todas as _tools_**: schemas Zod com limites de tamanho (`.max()`) em todo campo de texto, evitando payloads arbitrariamente grandes. O _resource_ `brasil://uf/{sigla}` — que não passa pela validação automática do SDK, diferente das _tools_ — tem validação de formato própria antes de montar a URL de saída.
- **Limite de tamanho de corpo no transporte HTTP**: requisições acima de 1 MiB são rejeitadas com `413`, evitando um vetor de exaustão de memória.
- **Tratamento de erro 4xx sem retry inútil**: qualquer erro 4xx (não só 404) é tratado como "não encontrado", sem consumir tentativas de retry — importante porque as APIs públicas usadas aqui são inconsistentes entre si (a BrasilAPI responde `400` para um DDD malformado e `404` para um banco inexistente; o IBGE responde `200` com corpo vazio para uma UF inexistente). Cada uma dessas variações foi observada contra a API real e coberta por teste.
- **Nunca expõe stack trace ao cliente**: toda falha de provider vira uma mensagem de erro legível (`isError: true` no formato MCP), nunca um erro bruto do runtime.
- **Logs sempre em stderr**: nunca poluem o canal JSON-RPC do transporte `stdio`.
- **Sem segredos**: todas as APIs usadas são públicas e não exigem chave de API, token ou credencial de nenhum tipo.
- **`npm audit` limpo em produção**: `npm audit --omit=dev` não acusa nenhuma vulnerabilidade nas dependências que de fato vão para o pacote publicado (as únicas vulnerabilidades reportadas pelo `npm audit` padrão são em ferramentas de desenvolvimento — `vitest`/`vite`/`esbuild` — e não afetam o código em execução).

## Estratégia de testes

Três camadas, sem sobreposição de responsabilidade:

- **Unitários** (`tests/unit`): algoritmos de CPF/CNPJ contra casos válidos/inválidos conhecidos; cache (incluindo expiração por TTL e deduplicação de chamadas concorrentes); rate limiter (incluindo bloqueio, liberação por refill e reset entre testes); handler de cada _tool_, isoladamente, com um provider _fake_ injetado — sem tocar em rede.
- **Integração** (`tests/integration`): os providers HTTP _reais_ (`ViaCepProvider`, `BrasilApiClient`, `IbgeClient`) testados contra respostas mockadas com [MSW](https://mswjs.io/), com _fixtures_ fiéis ao formato exato das respostas reais dessas APIs (verificadas manualmente contra produção durante o desenvolvimento); o servidor MCP completo testado via `InMemoryTransport` do SDK oficial, cobrindo o fluxo real de `tools/list` e `tools/call`.
- **E2E** (`tests/e2e`): o binário publicado (`bin/mcp-brasil.js`) executado como processo real, com handshake MCP genuíno tanto via `stdio` quanto via HTTP (incluindo o limite de tamanho de corpo).

## Executando localmente

Pré-requisito: Node.js 20+.

```bash
npm install
npm run dev              # roda via stdio, com hot-reload via tsx
npm test                 # build + suíte completa (unit + integração + e2e)
npm run test:coverage    # com relatório de cobertura
npm run lint
npm run typecheck
npm run format:check     # verifica formatação (Prettier)
npm run build            # gera dist/ (ESM + tipos)
```

Para testar manualmente contra um cliente MCP local, aponte-o para `node bin/mcp-brasil.js` (transporte `stdio`) — exemplos prontos de configuração em [`examples/claude_desktop_config.json`](./examples/claude_desktop_config.json) e [`examples/claude-code.md`](./examples/claude-code.md).

## Configuração (variáveis de ambiente)

| Variável        | Padrão  | Descrição                                                  |
| --------------- | ------- | ---------------------------------------------------------- |
| `MCP_TRANSPORT` | `stdio` | `stdio` ou `http`.                                         |
| `MCP_PORT`      | `3000`  | Porta usada quando `MCP_TRANSPORT=http`.                   |
| `LOG_LEVEL`     | `info`  | `debug`, `info`, `warn` ou `error`. Logs sempre em stderr. |

## Licença

Este projeto **não é open source**. Todos os direitos são reservados ao autor — cópia, uso, execução, modificação e distribuição são proibidos sem autorização prévia e por escrito. O texto completo está em [`LICENSE`](./LICENSE).

TDQS

A4.1/5.0

Scored across 1 tool

Disambiguation5/5

With only one tool, there is no possibility of overlap or misselection. Its purpose (validate a CPF/CNPJ and return it formatted) is unambiguous and clearly stated.

Naming Consistency5/5

The single name 'validar_documento' uses a clean snake_case verb_noun pattern in Portuguese and there are no competing conventions to conflict with it. Consistency is trivially satisfied.

Tool Count2/5

A server named 'MCP Brasil' implies a broader suite of Brazilian utilities (CEP lookup, phone/currency formatting, state data, etc.), so a single validation tool is far too thin for the apparent scope. The tool itself is well-sized, but the surface overall is under-built.

Completeness4/5

For the narrow stated purpose of checking document validity before a cadastro, CPF and CNPJ are both covered with a self-contained offline implementation. Only minor adjacent operations are missing, such as format-only or generate-valid-document helpers.

Maintenance

ActivityMaintained
ResponsivenessNo issues