Skip to main content
Glama
fabianofilho

protocolos-pcdt-mcp

by fabianofilho
README.md
# protocolos-pcdt-mcp

Servidor MCP que consulta os PCDTs (Protocolos Clínicos e Diretrizes Terapêuticas) do
Ministério da Saúde/Conitec e resume a conduta recomendada para um contexto clínico
específico, usando um LLM local. Toda resposta traz o link do PDF oficial.

> ### ⚠️ Não substitui o protocolo nem o julgamento clínico
>
> - **Não é fonte oficial.** O projeto lê o que a Conitec publica e guarda uma cópia local,
>   que pode estar defasada em relação ao portal.
> - **O resumo é gerado por LLM e é uma ajuda de leitura.** Protocolos têm exceções,
>   populações específicas e notas de rodapé que um resumo de seis linhas não carrega.
> - **A citação literal é conferida, mas a interpretação não.** Ver
>   [Limitações conhecidas](#limitações-conhecidas) — há um exemplo real de citação correta
>   com conclusão clínica errada.
> - **Sem validação clínica.** Não foi avaliado por nenhum órgão e não é dispositivo médico.
> - Para conduta, leia o protocolo completo. O link vem em toda resposta.

## Requisitos

| O quê | Versão | Para quê |
| --- | --- | --- |
| Python | 3.12+ | runtime |
| [uv](https://docs.astral.sh/uv/) | recente | dependências e venv |
| Um LLM local com API OpenAI-compatible | — | resumo direcionado |
| Espaço em disco | ~1 GB | base DuckDB + PDFs cacheados (protocolos são grandes) |

## Instalação

```bash
git clone https://github.com/fabianofilho/protocolos-pcdt-mcp.git
cd protocolos-pcdt-mcp
uv sync
cp .env.example .env
```

## Configuração

| Variável | Padrão | Observação |
| --- | --- | --- |
| `QWEN_ENDPOINT` | `http://127.0.0.1:8080/v1` | llama.cpp. Ollama: `:11434/v1`. LM Studio: `:1234/v1` |
| `QWEN_MODEL` | `local-model` | llama.cpp e LM Studio aceitam qualquer nome |
| `DUCKDB_PATH` | `./data/pcdt.duckdb` | base local |
| `COLETA_DELAY_SEGUNDOS` | `1` | intervalo entre downloads de PDF |
| `SYNC_HORA_LOCAL` | `02:40` | horário fixo da coleta agendada |

```bash
uv run pcdt-cli llm                 # confirma o LLM local
uv run pcdt-cli sync --max-pdfs 5   # teste rápido
uv run pcdt-cli sync                # coleta (20 PDFs por execução, por padrão)
uv run pcdt-cli consultar asma
uv run pcdt-cli resumir "asma" "paciente gestante"
```

A coleta baixa no máximo `--max-pdfs` protocolos por execução: são ~130 PDFs grandes, e a
ideia é a base completar ao longo de algumas noites em vez de sobrecarregar o portal numa
única. O texto já coletado é preservado entre execuções.

### Ligando ao Claude Code

```bash
claude mcp add protocolos-pcdt --scope user \
  -e DUCKDB_PATH=/caminho/para/protocolos-pcdt-mcp/data/pcdt.duckdb \
  -e QWEN_ENDPOINT=http://127.0.0.1:8080/v1 \
  -e QWEN_MODEL=local-model \
  -- uv --directory /caminho/para/protocolos-pcdt-mcp run protocolos-pcdt-mcp
```

## Uso

### `consultar_protocolo(doenca_ou_condicao: str)`

Busca pelo nome da condição; se não achar, procura no texto completo — uma condição pode
ser tratada dentro do PCDT de outra. Devolve todos os protocolos relacionados.

```json
{
  "termo": "acidentes ofídicos",
  "total": 1,
  "resultados": [
    {
      "identificador": "acidentes ofídicos",
      "condicao": "Acidentes Ofídicos",
      "status": "Conitec",
      "portaria": "Portaria SECTICS/MS nº 83 - 07/10/2025",
      "url_pdf": "https://www.gov.br/conitec/pt-br/midias/protocolos/pcdt_acidentes_ofidicos_final.pdf/@@display-file/file",
      "secoes_disponiveis": ["introducao", "classificacao", "diagnostico", "tratamento", "monitoramento"],
      "texto_completo_disponivel": true
    }
  ]
}
```

### `resumir_conduta(pcdt_id: str, contexto_clinico: str)`

Extrai do protocolo a parte que responde ao contexto, em vez de devolver o documento
inteiro.

```json
{
  "condicao": "Acidentes Ofídicos",
  "resumo": {
    "resumo": "O paciente deve receber soroterapia antiveneno específica para o tipo de envenenamento.",
    "secao_origem": "1.3. Acesso a soroterapia antiveneno",
    "citacao_literal": "…é necessário utilizar a soroterapia antiveneno específica, correspondente ao tipo de envenenamento…",
    "citacao_confere": true,
    "fracao_citacao_verificada": 1.0
  },
  "url_pdf": "https://www.gov.br/conitec/..."
}
```

## A citação é conferida, não só pedida

O prompt exige a citação literal do trecho que sustenta o resumo. Um modelo pequeno às
vezes "cita" parafraseando — ou, pior, costura frases reais de partes diferentes do
documento num único bloco de aspas. Então o código confere:

| Campo | O que significa |
| --- | --- |
| `citacao_confere` | a citação existe inteira e **contígua** no protocolo |
| `fracao_citacao_verificada` | quanto dela existe, frase a frase (0 a 1) |

Fração alta com `citacao_confere: false` é o caso mais traiçoeiro: parece legítimo na
leitura e não é. Isso aconteceu no primeiro teste real deste projeto, com o PCDT de
Acidentes Ofídicos, e é por isso que os dois campos existem.

## Limitações conhecidas

**Conferir a citação não pega erro de interpretação.** Num teste com "paciente picado por
cascavel", o modelo local devolveu uma citação **real e contígua** do protocolo e mesmo
assim classificou o caso como envenenamento **botrópico**, quando cascavel é **crotálico**.
A citação estava certa; o raciocínio em cima dela, errado. Esta é a limitação mais
importante do projeto.

**O protocolo é truncado antes de ir para o modelo.** Protocolos passam de 200 mil
caracteres; o recorte prioriza a vizinhança das palavras do contexto perguntado, mas pode
cortar fora a parte relevante.

**A segmentação por seções é heurística.** A estrutura dos PCDTs varia entre protocolos
antigos e novos. Quando os títulos não são reconhecíveis, `secoes_disponiveis` vem vazio e
só o texto corrido fica disponível — de propósito, para não inventar estrutura.

**A base começa quase vazia.** Por causa do teto de PDFs por execução, os primeiros syncs
trazem a listagem completa mas pouco texto. `texto_completo_disponivel` diz quais já têm.

**PDFs digitalizados não têm camada de texto.** Nesses casos o registro fica com
`extracao_incompleta: true` e só os metadados.

**As URLs das fontes podem mudar.** Estão em `coleta/listagem.py`, confirmadas em
20/09/2026. Observação prática: os links `.csv` que o portal de dados abertos exibe estão
desatualizados e devolvem 403; os que funcionam terminam em `.zip`.

## Privacidade

- **Sai da máquina:** requisições ao `gov.br/conitec` e ao bucket de dados abertos do
  Ministério da Saúde, para a listagem e os PDFs públicos.
- **Não sai:** a condição e o contexto clínico que você consulta ficam entre a base local
  e o seu LLM local.
- Sem telemetria, sem analytics.

Atenção: o `contexto_clinico` que você digita vai para o seu LLM. Se ele estiver
hospedado fora da sua máquina, o texto vai junto — este projeto não impede isso, ao
contrário do `revisor-notas-mcp`.

## Contribuindo

Veja [CONTRIBUTING.md](CONTRIBUTING.md). Não rode a coleta em loop contra o portal.

## Licença e atribuição

[Apache License 2.0](LICENSE) — escolhida por o projeto tocar em conduta clínica.

Construído no contexto do [IA.med](https://iamed.cc).

TDQS

A3.9/5.0

Scored across 2 tools

Disambiguation5/5

The two tools serve clearly different functions: one searches and retrieves protocol information, the other summarizes clinical conduct based on a specific context. There is no overlap or ambiguity in their purposes.

Naming Consistency5/5

Both tool names follow the verb_noun pattern (consultar_protocolo, resumir_conduta) and are in Portuguese, consistent and predictable.

Tool Count3/5

With only 2 tools, the server feels thin, though the scope is narrow. It is bordering on inadequate but still minimally functional for its stated purpose of querying and summarizing PCDTs.

Completeness3/5

While the two tools cover querying and summarizing, there is no way to list all available protocols or discover them without knowing a specific disease. This is a notable gap for a medical reference server.

Maintenance

ActivityMaintained
ResponsivenessNo issues