mcp-cnes
MCP server for loading and querying public data from the Brazilian National Registry of Health Establishments (CNES). It enables natural language searches for health facilities by municipality, CNES code, or state, along with statistics and data loading.
README
MCP CNES — Servidor de dados do CNES
Servidor MCP para carregar e consultar dados públicos do Cadastro Nacional de
Estabelecimentos de Saúde (CNES). O entrypoint padrão usa o SDK Python MCP v2
oficial com transporte stdio. mcp_server.py permanece temporariamente apenas
como baseline de paridade e não deve ser usado como rollback por clientes MCP.
Ferramentas disponíveis
| Ferramenta | Descrição |
|---|---|
cnes_load_data |
Carrega um CSV exportado do dashboard CNES |
cnes_search_municipio |
Busca estabelecimentos por município |
cnes_search_cnes |
Busca um estabelecimento pelo código CNES |
cnes_search_uf |
Busca estabelecimentos por UF |
cnes_statistics |
Retorna estatísticas dos dados carregados |
cnes_download_instructions |
Explica como obter o CSV manualmente |
Requisitos
- uv 0.12 ou superior.
- Python 3.11 ou superior. O arquivo
.python-versionfixa Python 3.14 para o ambiente de desenvolvimento reproduzível.
Não use o .venv de outra máquina. O uv cria ou recria o ambiente local a
partir de pyproject.toml e uv.lock.
Bootstrap reproduzível
uv sync --locked
pyproject.toml é a fonte canônica das dependências e uv.lock fixa as versões
resolvidas. O antigo requirements.txt permanece somente como aviso de migração.
Verificação local
# Suíte padrão: não acessa a internet nem requer Playwright
uv run pytest
# Cobertura do pacote que receberá as camadas modernizadas
uv run pytest --cov=mcp_cnes --cov-report=term-missing
# Qualidade estática
uv run ruff check src tests mcp_server.py benchmarks
uv run pyright
# Servidor MCP oficial via stdio
uv run mcp-cnes
# Validação interativa com MCP Inspector
uv run mcp dev src/mcp_cnes/mcp_app.py
# Paridade de cutover entre o fallback e o SDK oficial
uv run pytest tests/contract/test_cutover_parity.py
Os testes de contrato em tests/fixtures/contracts/ congelam os nomes, schemas e
exemplos de resposta das seis ferramentas existentes. Alterações nessas fixtures
devem ser revisadas como mudanças de contrato.
O benchmark de persistência pode ser reproduzido com
uv run python benchmarks/benchmark_sqlite_import.py --rows 400000; a última
medição registrada fica em benchmarks/results/sqlite-400k.json.
O snapshot sdk-tools.snapshot.json protege os schemas completos por SHA-256 e
mantém visíveis propriedades de entrada/saída e campos obrigatórios.
Contrato do SDK MCP
- Os seis nomes históricos permanecem estáveis.
- Inputs e outputs possuem JSON Schema gerado a partir de type hints/Pydantic.
- Parâmetros extras são rejeitados e anunciados com
additionalProperties: false. - CNES exige sete dígitos, UF exige duas letras e
limitaceita1–500. - Falhas recuperáveis retornam
isError: true, permitindo autocorreção pelo agente; elas não são retornadas como sucesso. - Sucessos incluem
structuredContente conteúdo textual JSON para clientes anteriores à saída estruturada. - O SDK negocia a revisão atual
2026-07-28e o modo legado suportado.
Essas são correções intencionais em relação ao envelope legado. O fallback
mcp_server.py não é um entrypoint suportado para novos consumidores e será
retirado após o gate operacional descrito em docs/cutover.md.
O teste tests/unit/test_architecture.py também funciona como gate de
dependências: domínio não pode importar MCP, banco, HTTP, Playwright ou pandas;
aplicação só pode depender do domínio e de suas próprias portas.
Configuração validada
O bootstrap lê settings somente quando o servidor ou um coletor é criado. Configurações inválidas interrompem a inicialização com mensagem explícita, sem iniciar rede, browser ou processamento de arquivos. Os principais nomes são:
| Variável | Padrão |
|---|---|
MCP_CNES_COMPETENCE |
202512 |
MCP_CNES_MIN_BEDS / MCP_CNES_MAX_BEDS |
50 / 150 |
MCP_CNES_TARGET_CITIES |
objeto JSON com regiões e cidades |
MCP_CNES_PRIVATE_NATURE_CODES |
códigos separados por vírgula |
MCP_CNES_DIRECTOR_CBO_CODES |
códigos separados por vírgula |
MCP_CNES_DATA_DIR / MCP_CNES_OUTPUT_DIR |
downloads / . |
MCP_CNES_DATABASE_PATH |
downloads/cnes.sqlite3 |
MCP_CNES_MAX_CSV_SIZE_BYTES |
104857600 |
MCP_CNES_ALLOWED_CSV_FILES |
vazio (todos os CSVs confinados ao diretório) |
MCP_CNES_BATCH_RETENTION_COUNT |
5 lotes concluídos |
MCP_CNES_BASE_URL, MCP_CNES_KIBANA_API, MCP_CNES_DASHBOARD_URL |
ElastiCNES |
MCP_CNES_KIBANA_INDEX |
padrão de índice; default cnes-leitos* |
MCP_CNES_REQUEST_TIMEOUT / MCP_CNES_BROWSER_TIMEOUT_MS |
60 / 60000 |
Delays e retries também podem ser definidos com MCP_CNES_MIN_DELAY,
MCP_CNES_MAX_DELAY, MCP_CNES_MAX_RETRIES e MCP_CNES_RETRY_DELAY.
A importação aceita somente arquivos .csv resolvidos dentro de
MCP_CNES_DATA_DIR. Quando MCP_CNES_ALLOWED_CSV_FILES é definido, os nomes
permitidos devem ser separados por vírgula. Travessia de diretório, escape por
link simbólico e arquivos acima do limite são rejeitados antes da leitura.
Durante o parsing, deduplicação e consolidação usam um SQLite temporário em disco,
removido ao final da carga. O catálogo retém somente a quantidade configurada de
lotes concluídos; o expurgo do histórico e de seu staging ocorre na mesma transação
que publica o lote atual.
Faixa de leitos configurável
As buscas MCP por município e UF aceitam limites inclusivos opcionais definidos em cada chamada pelo usuário ou agente:
{
"municipio": "Manaus",
"min_leitos": 50,
"max_leitos": 150,
"limit": 20
}
min_leitosemax_leitospodem ser usados juntos ou isoladamente.- Quando ambos são omitidos, a busca MCP mantém o comportamento anterior e não filtra por porte.
- A resposta diferencia
total_encontradosdetotal_retornadose informa os limites aplicados emfiltros_leitos. - Valores negativos ou intervalos invertidos retornam erro explícito.
O scraper direto mantém 50–150 como padrão por compatibilidade, mas também
aceita override por linha de comando:
uv run python cnes_scraper.py --min-beds 20 --max-beds 300
Dependência opcional de navegador
Os coletores externos implementam a porta CNESCollector em
mcp_cnes.infrastructure.collectors:
KibanaHttpCollectorrecebe uma sessão HTTP injetável, aplica timeout/retry e consultainternal/bsearch, distinguindohttp_timeout,http_rate_limited,http_server_errore erros de transporte.PlaywrightCNESCollectorcompõe oPlaywrightCsvDownloader, que identifica a etapa de falha, captura downloads porpage.expect_download, usa nomes únicos e remove o CSV temporário após a importação.
Falhas externas usam CollectorError, com code, stage, retryable e
status_code, permitindo diferenciá-las de regressões internas sem vazar respostas.
Playwright pertence ao grupo opcional browser e não é instalado por uv sync
nem necessário para a suíte padrão.
uv sync --locked --group browser
uv run playwright install chromium
uv run python cnes_playwright_collector.py
Testes externos
Testes marcados como live são separados da suíte padrão e só acessam a internet
quando a variável de autorização está definida:
$env:CNES_RUN_LIVE_TESTS = "1"
uv run pytest -m live
Remove-Item Env:CNES_RUN_LIVE_TESTS
O workflow Live smoke também pode ser iniciado manualmente no GitHub Actions.
Ele executa uma única consulta de contrato via Kibana, possui timeout de cinco minutos
e nunca é disparado em pull requests. A suíte padrão bloqueia conexões externas;
coletores HTTP devem usar respostas injetadas nos testes.
Os scripts históricos test_api.py, test_scraper.py e test_mcp_server.py são
diagnósticos manuais e não fazem parte da suíte automatizada em tests/.
Uso como servidor MCP
Exemplo de configuração local. Substitua o diretório pelo caminho real do seu checkout; nenhum caminho de usuário é codificado na aplicação.
{
"mcpServers": {
"cnes": {
"command": "uv",
"args": [
"--directory",
"C:/caminho/absoluto/mcp_cnes",
"run",
"mcp-cnes"
]
}
}
}
Cutover e rollback
O procedimento operacional completo está em docs/cutover.md.
Antes de alterar um cliente real, execute a paridade automatizada e gere um
manifesto do smoke stdio com uv run mcp-cnes-cutover-smoke --help. O manifesto
registra versão, revisão verificada, digest da fonte, protocolo, hashes de schema,
volume importado e a execução das seis ferramentas sem persistir conteúdo de
estabelecimentos ou caminhos absolutos. O comando exige banco novo e descartável,
recusa catálogos preexistentes e nunca sobrescreve um manifesto anterior.
O legado só pode ser removido depois da validação do cliente real, do ensaio de
rollback para um checkout oficial last-known-good, do inventário de consumidores
e da aprovação explícita do responsável.
Troubleshooting
uv sync --lockedfalha: confirme Python 3.11+ e uma versão recente douv; remova apenas a.venvlocal e repita o bootstrap.- o cliente não lista as tools: execute
uv run mcp-cnesno mesmo diretório e confiracommand,argse caminhos absolutos na configuração do cliente. - a carga rejeita o CSV: confirme que o arquivo está dentro de
MCP_CNES_DATA_DIR, possui extensão.csv, respeita o limite e, quando usada, consta emMCP_CNES_ALLOWED_CSV_FILES. - o smoke falha em uma busca: use município, UF e CNES que existam no mesmo CSV; uma resposta vazia é tratada como falha de validação, não como sucesso.
- suspeita de banco corrompido ou bloqueado: não apague o catálogo em uso; gere um banco separado para o smoke e preserve o anterior para rollback e diagnóstico.
Fonte dos dados
Os CSVs são obtidos dos dashboards do Ministério da Saúde:
- Leitos: https://elasticnes.saude.gov.br/leitos
- Geral: https://elasticnes.saude.gov.br/geral
- Profissionais: https://elasticnes.saude.gov.br/profissionais
Para download manual, acesse o dashboard de leitos, localize o painel “EXTRATO DOS LEITOS”, abra o menu do painel e escolha “Download CSV”. O dashboard informa limite de 400.000 registros por download.
Estrutura atual
mcp_cnes/
├── pyproject.toml
├── uv.lock
├── src/mcp_cnes/
│ ├── domain/ # modelos, regras puras e erros
│ ├── application/ # casos de uso e Protocols
│ ├── infrastructure/ # settings, importação segura, memória e SQLite
│ ├── interfaces/mcp/ # servidor, tools e schemas do SDK oficial
│ ├── mcp_app.py # objeto descoberto pelo MCP CLI/Inspector
│ └── __main__.py # entrypoint stdio
├── tests/ # suíte automatizada e fixtures de contrato
├── docs/cutover.md # runbook, inventário, smoke e rollback
├── mcp_server.py # baseline legado de paridade aguardando remoção
├── cnes_scraper.py # coletor HTTP experimental
├── cnes_playwright_collector.py
├── sample_data.csv
└── downloads/
Dados do CNES são públicos e disponibilizados pelo Ministério da Saúde/DATASUS.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
Neon Database
MCP server for interacting with Neon Management API and databases
E2B
Using MCP to run code via e2b.
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.