Projeto IA.med Licença Apache-2.0 Local ou conector Sem LLM

radar-cfm-mcp

Servidor MCP para consultar as resoluções do CFM sobre inteligência artificial, telemedicina e prontuário eletrônico. Use pelo conector hospedado, sem instalar nada, ou rode na sua máquina e ligue ao cliente MCP que preferir.

Antes de usar, leia

  • Não é fonte oficial do CFM e não substitui a leitura da resolução original.
  • Não é aconselhamento jurídico nem ético-profissional, não é apoio à decisão clínica e não é dispositivo médico.
  • Resoluções podem ser revogadas ou substituídas. O projeto sinaliza isso quando consegue identificar no texto, mas não garante a detecção.
  • Todo resultado traz o link da fonte original, que deve ser conferida.

O problema

Por que este projeto existe

Espalhado em PDFs, sem dado aberto

As resoluções do CFM sobre IA, telemedicina e prontuário eletrônico ficam no portal de normas do conselho, sem API nem dado aberto estruturado. Cruzar tema, vigência e número dá trabalho manual.

Saber o que está vigente exige leitura

Uma resolução pode ter sido revogada ou substituída por outra. Descobrir o que ainda vale hoje significa abrir e comparar documentos um a um.

Um modelo de linguagem não alcança isso sozinho

Um modelo de linguagem não consegue consultar o portal e filtrar por tema de forma confiável. Falta a camada que exponha essa busca como ferramenta.

Como funciona

Do portal do conselho ao agente, em quatro camadas

  1. 01

    Crawler

    Varre o portal de normas do CFM com intervalo entre requisições. Um mesmo PDF nunca é baixado duas vezes. Por padrão, só é baixado quando a ementa casa com um tema; com --texto-integral, baixa todos (a instância hospedada roda assim).

  2. 02

    Extração

    Converte PDF e HTML em texto e metadados: número, ano, ementa, data de publicação (lida do cabeçalho do texto) e os sinais de revogação e suspensão.

  3. 03

    Armazenamento

    Guarda tudo localmente em DuckDB, com busca textual (FTS/BM25) e queda para LIKE quando o índice não está disponível.

  4. 04

    Ferramentas MCP

    Expõe a consulta por tema e o monitoramento de novas resoluções como tools, que o modelo chama pelo cliente MCP.

Toda resposta inclui a ementa e o link do PDF oficial. Um trecho extraído é ponto de partida para a leitura, não uma citação normativa.

As ferramentas

Duas tools MCP expostas ao modelo

O modelo chama estas funções durante a conversa. Cada resultado sempre traz a URL de origem para conferência. Acima dos tetos (limite até 100, dias até 3.650) a chamada é recusada. Os retornos abaixo mostram o formato; os campos são os do projeto e os valores, ilustrativos.

consultar_resolucao_cfm

Resoluções relacionadas ao tema, mais relevantes primeiro (BM25), até limite. Revogadas vêm com vigente: false e a que as substituiu. total conta a base inteira e truncado: true avisa que há mais do que veio. Um tema que seja o número da norma (2314/2022) busca aquela resolução direto, inclusive revogada.

consultar_resolucao_cfm(
    tema: str,
    apenas_vigentes: bool = True,
    limite: int = 10,   # 1 a 100
)
{
  "tema": "telemedicina",
  "total": 20,
  "retornados": 1,
  "truncado": true,
  "resultados": [
    {
      "identificador": "2314/2022",
      "numero": "2314",
      "ano": "2022",
      "data_publicacao": "2022-05-05",
      "ementa": "Define e regulamenta a telemedicina, como forma de serviços médicos mediados por tecnologias de comunicação.",
      "trecho_relevante": "...Define e regulamenta a telemedicina, como forma de serviços médicos...",
      "vigente": true,
      "suspensa": false,
      "revogada_por": null,
      "revogada_por_confere": null,
      "revogada_por_provavel": null,
      "url_origem": "https://sistemas.cfm.org.br/normas/visualizar/resolucoes/BR/2022/2314",
      "url_pdf": "https://sistemas.cfm.org.br/normas/arquivos/resolucoes/BR/2022/2314_2022.pdf",
      "texto_completo_disponivel": true,
      "metadados_incompletos": false
    }
  ],
  "aviso": "Cada resultado traz a ementa e a URL de origem: confira o texto oficial antes de tratar qualquer trecho como a posição do CFM. ..."
}
Exemplo do formato de retorno (valores ilustrativos)

monitorar_novas_resolucoes

O que foi publicado nos últimos dias, mais recente primeiro. Com filtrar_tema, só as que citam as palavras-chave do servidor (por padrão inteligência artificial, telemedicina, prontuário eletrônico e algoritmo), filtradas antes do limite. A janela usa a data extraída do texto: sem_data_publicacao conta as que ficam de fora, então total zero com esse número alto quer dizer "não sei", não "nada foi publicado".

monitorar_novas_resolucoes(
    dias: int = 30,          # 1 a 3650
    filtrar_tema: bool = True,
    limite: int = 50,        # 1 a 100
)
{
  "dias": 90,
  "total": 1,
  "retornados": 1,
  "truncado": false,
  "sem_data_publicacao": 343,
  "resultados": [
    {
      "identificador": "2471/2026",
      "data_publicacao": "2026-09-17",
      "vigente": true,
      "url_origem": "https://sistemas.cfm.org.br/normas/visualizar/resolucoes/BR/2026/2471",
      "url_pdf": "https://sistemas.cfm.org.br/normas/arquivos/resolucoes/BR/2026/2471_2026.pdf"
    }
  ],
  "aviso": "..."
}
Exemplo do formato de retorno (campos abreviados)

O que ele não diz

Os números, e onde a resposta pode enganar

Números lidos da base real e de uma execução em 12/10/2026; eles envelhecem. O servidor não usa modelo de linguagem: responde só o que está na base.

2.457
resoluções na base, todas com texto
53 MB
tamanho da base
93
testes automatizados
2
tools expostas

Lido em 12/10/2026.

  • A data de publicação é lida do texto, não de um campo

    O portal não publica a data em metadado. A extração cobre 97% das normas de 2020 em diante, e sem_data_publicacao conta quantas ficaram de fora da janela: total zero com esse número alto quer dizer "não sei", não "nada foi publicado".

  • A revogação marcada no portal está errada na origem

    O campo de ano de revogação do portal foi auditado página por página e não confere. O servidor usa os sinais que consegue ler do próprio texto e não garante a detecção, nem a vigência.

  • Suspensão só conta dentro de parênteses

    É o que separa estado da norma de assunto da norma. Uma resolução que fala sobre suspensão não é uma resolução suspensa.

  • O portal cai, e a coleta cai junto

    A fonte não tem API nem dado estruturado: raspar portal falha de vez em quando, e isso não vai mudar.

A falha de 12/10, e por que ela não apareceu para quem consulta

A coleta das 02:37 morreu no meio, com erro de leitura de rede contra o portal. A base servida não foi tocada: continuou com as 2.457 resoluções, todas com texto, e o conector respondeu 200 durante e depois. O arquivo em construção ficou em 20 MB e será apagado pela próxima coleta. É o que a publicação por troca atômica existe para dar: antes dela, uma coleta interrompida deixava a base servida pela metade.

O portal voltou a responder em seguida, então foi falha passageira, e o agendamento tenta de novo na madrugada seguinte. Isso descreve o que aconteceu, não é promessa de disponibilidade.

Dois jeitos de usar

Pelo conector hospedado, ou na sua máquina

O mesmo servidor fala stdio (local) e streamable-http (conector). Toda resposta traz o link do PDF oficial, nos dois casos.

Conector hospedado

Há uma instância pública, mantida pelo autor, para usar sem instalar nada. No Claude (web ou desktop): Configurações, Conectores, adicionar conector personalizado, e colar a URL abaixo.

https://debian-f.tailf42a96.ts.net/cfm/mcp
URL do conector
claude mcp add --transport http radar-cfm https://debian-f.tailf42a96.ts.net/cfm/mcp
No Claude Code

Antes de usar o conector

  • Dados públicos do CFM A base é uma cópia do que o portal de normas publica, atualizada uma vez por dia de madrugada. Não é fonte oficial.
  • Sem garantia de disponibilidade Roda numa máquina pessoal exposta pelo Tailscale Funnel. Pode ficar fora do ar, mudar de endereço ou ser desligado sem aviso. Se você depende dele, rode o seu.
  • Sem autenticação, com limites Qualquer pessoa pode chamar. Há teto de 600 requisições por minuto por origem e 1.200 no total, e teto nos parâmetros (limite até 100, dias até 3.650). Acima disso a chamada é recusada.
  • O que você consulta passa por lá Os parâmetros de cada chamada (tema, dias, limite) chegam ao servidor do autor. O código não registra os temas; só o IP de origem quando uma chamada é recusada por limite.

stdio (local)

O cliente sobe o processo na sua máquina, contra a sua cópia da base (passo a passo abaixo). Nada fica exposto na rede, e os temas consultados não saem do seu computador.

streamable-http (o seu conector)

Para hospedar a sua instância. O tráfego do Claude chega dos IPs da Anthropic, então há um teto global e um por origem. Uma instância varre o portal e todos consultam a mesma base: para um portal de conselho, é a opção mais respeitosa.

# expor a sua própria instância como conector HTTP
TRANSPORTE=streamable-http HTTP_HOST=127.0.0.1 HTTP_PORTA=8000 uv run radar-cfm-mcp

# atrás de túnel ou proxy, declare o nome público (senão responde 421)
HTTP_HOSTS_PUBLICOS=mcp.exemplo.ts.net

Como rodar

Na sua máquina

Além do conector hospedado, você pode rodar a sua instância: clona o repositório, sincroniza a base a partir do portal do CFM e o servidor MCP roda ali, no seu computador. O projeto não usa LLM: a busca é full-text sobre a base local.

# 1. clonar o repositório
git clone https://github.com/fabianofilho/radar-cfm-mcp.git
cd radar-cfm-mcp

# 2. instalar as dependências (Python 3.12+ e uv)
uv sync

# 3. configurar o ambiente
cp .env.example .env

# 4. rodar a coleta inicial
uv run cfm-cli sync --max-paginas 3   # teste rápido
uv run cfm-cli sync                    # varredura completa (~8 min)

# 5. subir o servidor MCP (stdio) e conectar ao seu cliente
uv run radar-cfm-mcp
Confira os comandos e opções no README do projeto.
claude mcp add radar-cfm --scope user \
  -e DUCKDB_PATH=/caminho/para/radar-cfm-mcp/data/cfm.duckdb \
  -- uv --directory /caminho/para/radar-cfm-mcp run radar-cfm-mcp
Ligando a instância local ao Claude Code
# baixa o PDF de todas as resoluções, não só das que tocam no tema
uv run cfm-cli sync --texto-integral --max-pdfs 0

# constrói a base ao lado e troca no fim (quando há um servidor HTTP lendo)
uv run cfm-cli sync --publicar --texto-integral --max-pdfs 25

# refaz as datas de publicação a partir do texto já gravado, sem tocar no portal
uv run cfm-cli reextrair-datas [--publicar]
Outras opções do cfm-cli

O crawler respeita o portal: intervalo entre requisições, cache por PDF e, por padrão, download só quando a ementa casa com um tema. No modo local, os temas consultados não saem da sua máquina; só o sync fala com o portal do CFM. No conector hospedado, os parâmetros de cada chamada passam pelo servidor do autor.