Skip to main content
Glama
fabianofilho

protocolos-pcdt-mcp

by fabianofilho

protocolos-pcdt-mcp

Servidor MCP local (stdio) 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.

O servidor roda só por stdio, na sua máquina, sem porta de rede. Não há transporte HTTP nem connector remoto nesta versão, e expô-lo por túnel não é um uso suportado.

⚠️ 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: 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

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)

Related MCP server: Manual RAG — SIH/SUS Query System

Instalação

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

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.

Sync diário com systemd

O servidor MCP não agenda nada sozinho. O caminho oficial para manter a base atualizada é o timer systemd de usuário versionado em deploy/systemd/: ele roda pcdt-cli sync todo dia às 02:40, com até 30 min de atraso aleatório, e recupera o disparo perdido se a máquina estava desligada.

As units supõem o clone em ~/protocolos-pcdt-mcp e o uv em ~/.local/bin/uv. Se os seus caminhos forem outros, edite WorkingDirectory e ExecStart antes de copiar.

mkdir -p ~/.config/systemd/user
cp deploy/systemd/protocolos-pcdt-sync.service deploy/systemd/protocolos-pcdt-sync.timer \
  ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now protocolos-pcdt-sync.timer
systemctl --user list-timers protocolos-pcdt-sync.timer   # próximo disparo
journalctl --user -u protocolos-pcdt-sync.service         # saída dos syncs

Para o timer rodar sem sessão aberta, habilite loginctl enable-linger $USER.

Ligando ao Claude Code

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 primeiro no nome dos PCDTs vigentes:

  • sem acento e sem caixa, com as palavras em qualquer ordem, sempre no começo de uma palavra ("asma" não casa com "citoplasma");

  • trocando algumas siglas pelo nome por extenso (HAS, DPOC, DM1, DM2, TDAH, LES, ELA, IC, DRC, entre outras em nomes.py);

  • aceitando "diabetes"/"diabete" e "mellitus"/"melito", porque a Conitec escreve "Diabete Melito Tipo 1".

Devolve até 10 protocolos pelo nome, com origem: "nome". Se o nome não casar, procura o termo no texto completo e devolve até 5 com origem: "texto" e um aviso: esses só citam o termo e muitas vezes não são o PCDT da condição (por exemplo, "depressão" traz Alzheimer e Parkinson, que mencionam depressão).

O identificador é o nome da condição em minúsculas. Quando o portal pendura uma nota de revisão no nome, como "Asma (anexo alterado em 04/09/2026)", ela vai para nota_atualizacao e fica fora do identificador. Saída real, da base local em 24/09/2026:

{
  "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 (Publicada em 09/10/2025 )",
      "data_portaria": "2025-10-07",
      "nota_atualizacao": null,
      "url_pdf": "https://www.gov.br/conitec/pt-br/midias/protocolos/pcdt_acidentes_ofidicos_final.pdf/@@display-file/file",
      "url_resumido": "https://www.gov.br/conitec/pt-br/midias/protocolos/2026/pcdt-resumido/pcdt-resumido-acidentes-ofidicos/@@display-file/file",
      "vigente": true,
      "secoes_disponiveis": ["introducao", "diagnostico", "classificacao", "monitoramento", "tratamento"],
      "texto_completo_disponivel": true,
      "extracao_incompleta": false,
      "origem": "nome"
    }
  ],
  "aviso": null
}

status vem do CSV de dados abertos do Ministério da Saúde e é parcial: em 24/09/2026, só 43 dos 132 PCDTs da listagem tinham status, porque boa parte dos nomes do CSV não existe na tabela do portal. status: null não quer dizer que o protocolo não está aprovado; a listagem da Conitec só traz PCDTs vigentes.

resumir_conduta(pcdt_id: str, contexto_clinico: str)

Extrai do protocolo a parte que responde ao contexto, em vez de devolver o documento inteiro. Aceita o identificador de consultar_protocolo ou o nome da condição sem a nota de revisão (por exemplo, asma). Precisa do LLM local e do texto completo já coletado; sem um dos dois, devolve só o link do PDF e um aviso dizendo o que faltou.

{
  "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 é. Marcadores de omissão ("[...]", "(...)", reticências) nas pontas da citação são ignorados; no meio, significam que algo foi pulado, e a citação deixa de contar como contígua. 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. Na instância do mantenedor, em 24/09/2026, eram 64 de 132 com texto; no ritmo de 20 PDFs por noite, a base deve completar em cerca de 4 syncs (estimativa). Até lá, PCDTs como hipertensão arterial sistêmica e os de HIV aparecem na consulta, mas resumir_conduta responde que o texto ainda não foi coletado. pcdt-cli schema mostra a contagem.

Quando o PCDT muda, o texto antigo é descartado. Se a URL do PDF, a data da portaria ou a nota de revisão mudarem, o sync reextrai o texto, com prioridade na cota da noite. Se não couber na cota, ou se o download falhar, o protocolo fica sem texto até um sync conseguir baixar o PDF novo, em vez de resumir a versão velha com o link da nova. Quando só a nota ou a portaria mudam e a URL continua a mesma, o PDF velho sai do cache em disco na hora, para não ser reextraído depois.

O status é parcial. Ver a seção de consultar_protocolo.

A busca no texto é um último recurso. Ela acha protocolos que citam o termo, não o PCDT da condição. Siglas fora da lista de nomes.py também caem nela.

Listagem parcial não despromove ninguém. Se a listagem do portal trouxer menos de 80% dos PCDTs vigentes na base, o sync não tira ninguém de vigente e deixa um aviso no journal.

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.

Atualizando de uma base anterior

A versão 0.1.0 tira a nota de revisão do identificador e cria a coluna nota_atualizacao. A migração roda no próximo pcdt-cli sync (ou em qualquer abertura da base para escrita, como pcdt-cli schema), preserva o texto já extraído e refaz o índice FTS. Até lá, o servidor continua lendo a base antiga: os identificadores aparecem com a nota, e resumir_conduta aceita o nome sem ela.

Segurança

Ver SECURITY.md. Mudanças por versão em CHANGELOG.md.

Contribuindo

Veja CONTRIBUTING.md. Não rode a coleta em loop contra o portal.

Licença e atribuição

Apache License 2.0: escolhida por o projeto tocar em conduta clínica.

Construído no contexto do IA.med.

Available Tools

2 tools
consultar_protocoloA

Consulta o PCDT vigente do Ministério da Saúde para uma doença ou condição.

Procura primeiro no nome dos PCDTs, sem acento, com as palavras em qualquer ordem e aceitando algumas siglas ("HAS", "DPOC", "DM2") e a grafia "diabetes mellitus". Se o nome não casar, procura o termo no texto dos protocolos e devolve até 5, com origem="texto" e um aviso: esses só citam o termo e muitas vezes não são o PCDT da condição.

Cada resultado traz identificador (use em resumir_conduta), nome da condição, portaria, link do PDF completo e do PCDT resumido, seções extraídas e se o texto completo já foi coletado. O status vem do CSV de dados abertos e só existe para parte dos PCDTs; null não quer dizer que o protocolo não está aprovado. Se houver mais de um protocolo pelo nome, devolve todos (até 10), a escolha é de quem pergunta.

Args: doenca_ou_condicao: nome da doença ou condição, por exemplo "asma".

ParametersJSON Schema
NameRequiredDescriptionDefault
doenca_ou_condicaoYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
avisoNo
termoYes
totalYes
resultadosYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations at all, the description carries the full behavioral burden and does so thoroughly. It discloses the two-phase search strategy (name first then text), exact matching nuances (accents, word order, acronyms, 'diabetes mellitus'), the 5-result fallback with origin='texto', the status CSV null caveat, and the return of up to 10 protocols when multiple name matches exist.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Although the description is longer than typical, every sentence earns its place: it covers purpose, search behavior, result fields, an important null-status caveat, and parameter guidance. It is well-structured with the primary sentence firstched and the argument documented at the end.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter lookup tool with an output schema and no annotations, the description is complete. It tells the agent how the search works, what edge cases exist (text matches, multiple protocols, null status), and how to connect to the sibling tool, so there is no ambiguity about how to invoke it successfully.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% and there is only one parameter, but the description fully compensates. The 'Args' section defines doenca_ou_condicao with an example, and the main body adds substantial matching semantics that tell the model exactly what kind of input is acceptable and how it will be interpreted.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a precise verb and resource: 'Consulta o PCDT vigente do Ministério da Saúde para uma doença ou condição.' It clearly identifies the tool as a PCDT lookup for a disease/condition and adds the useful relationship to the sibling tool via the identifier comment.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description makes the usage context clear: use it to look up a PCDT by disease/condition)Skip; it even notes that the returned identifier is intended for use in resumir_conduta. It does not explicitly state when not to use it or name alternatives, but with only one sibling and such a distinct lookup purpose, that is not a significant gap.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

resumir_condutaA

Resume a conduta de um PCDT para um contexto clínico específico.

Em vez de devolver o protocolo inteiro, extrai a parte que responde ao contexto, por exemplo "paciente com contraindicação a metformina". A resposta traz sempre a citação literal do trecho que sustenta o resumo, e diz se essa citação foi de fato encontrada no protocolo.

É ajuda de leitura, não substitui o protocolo: o link do PDF vem junto.

Precisa de um LLM local configurado e do texto completo do protocolo já coletado; sem isso, devolve só o link do PDF e um aviso.

Args: pcdt_id: identificador devolvido por consultar_protocolo. O nome da condição sem a nota de revisão também é aceito, por exemplo "asma". contexto_clinico: a situação concreta sobre a qual se quer a conduta.

ParametersJSON Schema
NameRequiredDescriptionDefault
pcdt_idYes
contexto_clinicoYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
avisoNo
resumoNo
url_pdfNo
condicaoYes
identificadorYes
contexto_clinicoYes

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full responsibility for behavioral disclosure. It clearly states that the response always includes a literal citation, states whether the citation was actually found, includes the PDF link, and explains what happens when prerequisites are missing (returns only the link and a warning). This is unusually transparent about failure modes and return contents.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with a clear purpose sentence, then adds differentiation, return behavior, prerequisites, and parameter documentation. It is longer than a two-sentence ideal, but every sentence carries useful information and none is redundant. Minor extra length is justified by the tool's non-trivial behavior.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the tool's purpose, the exact inputs, the return characteristics, the prerequisites, and the fallback behavior. Since an output schema exists, return-value details are not required here. There is no missing information an agent would need to invoke the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the schema provides only titles. The description compensates fully by explaining both parameters: pcdt_id is the identifier returned by consultar_protocolo and may also be a condition name without the revision note (e.g., 'asma'), and contexto_clinico is the concrete clinical situation. This adds essential meaning beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description starts with a specific verb and resource: 'Resume a conduta de um PCDT para um contexto clínico específico.' It differentiates itself from the sibling tool by saying it does not return the whole protocol but extracts only the part relevant to the clinical context. This makes the tool easy to distinguish from consultar_protocolo.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description states the intended context ('para um contexto clínico específico') and contrasts with returning the full protocol ('Em vez de devolver o protocolo inteiro'), which implies when the sibling would be used. It also gives preconditions (local LLM and collected full protocol text) and the fallback behavior. However, it does not explicitly instruct to use consultar_protocolo when the full protocol is needed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 1 tool update
    • Changedconsultar_protocolo2 fields changed
      • addedOutput schema / $defs / Protocolo / properties / nota_atualizacao
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Nota de revisão que o portal põe ao lado do nome, ex. 'Anexo alterado em ...'",
        +  "title": "Nota Atualizacao"
        +}
      • addedOutput schema / $defs / Protocolo / properties / origem
        Added value: +{
        +  "default": "nome",
        +  "description": "'nome': o termo casou com o nome do PCDT. 'texto': o nome não casou e o protocolo só cita o termo em algum ponto do texto",
        +  "enum": [
        +    "nome",
        +    "texto"
        +  ],
        +  "title": "Origem",
        +  "type": "string"
        +}
  2. 2 tool updatesv0.1.0
    • First observedconsultar_protocolo
    • First observedresumir_conduta

TDQS

A4.7/5.0

Scored across 2 tools

Disambiguation5/5

The two tools have completely distinct purposes: one searches for PCDT documents, the other summarizes a specific clinical conduct from a selected PCDT. There is no overlap or ambiguity in their roles.

Naming Consistency5/5

Both tool names follow the same verb_noun pattern in Portuguese: 'consultar_protocolo' (consult protocol) and 'resumir_conduta' (summarize conduct). The naming is consistent and predictable.

Tool Count4/5

With only 2 tools, the server is slightly under the typical well-scoped range, but the narrow domain (PCDT consultation and summarization) justifies this minimal set. The tools cover the core workflow without unnecessary extras.

Completeness4/5

The server covers the essential operations: finding a protocol and summarizing a specific conduct. A minor gap exists in that there is no explicit tool to fetch the full protocol text, but the first tool provides links and sections, and the second tool gracefully degrades if full text is unavailable.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers