radar-cfm-mcp
This server provides read-only search and monitoring of CFM (Brazilian Federal Council of Medicine) resolutions on AI, telemedicine, and electronic medical records, always returning the official source link.
Search resolutions by theme (
consultar_resolucao_cfm): query by topic (e.g. "inteligência artificial", "telemedicina") or by resolution number (e.g.2314/2022), ranked by relevance, with total count and truncation flag.Filter to active norms only:
apenas_vigentesomits revoked resolutions by default; revoked ones come withvigente=falseand the replacing resolution number.Monitor recent publications (
monitorar_novas_resolucoes): list resolutions published in the last N days (up to 3650), optionally filtered to configured keywords, with asem_data_publicacaocount to distinguish "no data" from "nothing published".Retrieve full resolution metadata: identifier, number, year, ementa, publication date,
trecho_relevanteexcerpt,url_origem, andurl_pdf.Vigência/suspension flags:
vigente,suspensa,suspensao_parcial,nota_vigencia,revogada_por, plusrevogada_por_confere/revogada_por_provavelto flag portal errors.Transparency flags:
texto_completo_disponivel,metadados_incompletos, andavisoto signal when text wasn't downloaded or extraction was incomplete.Pagination via
limite: 1–100 results per call in both tools, withtruncadoindicating more matches exist.No LLM, no telemetry: it is a local full-text search tool, not a legal interpretation source — always open the official PDF/URL before citing.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@radar-cfm-mcpquais resoluções do CFM sobre telemedicina estão vigentes?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
radar-cfm-mcp
Servidor MCP que consulta e monitora resoluções do CFM (Conselho Federal de Medicina) sobre inteligência artificial, telemedicina e prontuário eletrônico. Mantém uma cópia local pesquisável e sempre devolve o link do documento oficial.
⚠️ Não é fonte oficial
Não substitui a consulta ao portal do CFM. Este projeto lê o que o portal publica e guarda uma cópia local, que pode estar defasada.
Um trecho extraído não é "a posição do CFM". Toda resposta traz a ementa e a URL do PDF oficial justamente para você conferir antes de citar.
A extração de PDF pode falhar ou truncar. Quando isso acontece o registro é marcado com
metadados_incompletos, mas leia sempre o documento original.Sem validação jurídica. Isto é uma ferramenta de busca, não uma interpretação normativa.
Connector hospedado
Há uma instância pública deste servidor, mantida pelo autor, para quem quer usar sem instalar nada:
https://debian-f.tailf42a96.ts.net/cfm/mcpNo Claude (web ou desktop): Configurações, Conectores, adicionar conector personalizado, e colar a URL acima.
No Claude Code:
claude mcp add --transport http radar-cfm https://debian-f.tailf42a96.ts.net/cfm/mcp
Antes de usar, saiba o que ela é:
Os dados são públicos, do CFM. A base é uma cópia do que o portal de normas publica, atualizada uma vez por dia de madrugada. O aviso acima vale inteiro: 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 desligada sem aviso. Para uso de que você dependa, rode a sua (instruções abaixo).
Sem autenticação, com limites. Qualquer pessoa pode chamar. Por isso há teto de requisições (600 por minuto por origem e 1.200 por minuto no total) e teto nos parâmetros (
limiteaté 100,diasaté 3.650). Acima disso a chamada é recusada.O que você consulta passa por essa máquina. Veja Privacidade.
Related MCP server: cfm_estabelecimento
Requisitos
O quê | Versão | Para quê |
Python | 3.12+ | runtime |
recente | dependências e venv | |
Espaço em disco | ~200 MB | base DuckDB + PDFs cacheados |
Este projeto não usa LLM: a busca é full-text sobre a base local.
Instalação
git clone https://github.com/fabianofilho/radar-cfm-mcp.git
cd radar-cfm-mcp
uv sync
cp .env.example .envConfiguração
Variável | Padrão | Observação |
|
| intervalo entre requisições ao portal |
| inteligência artificial, telemedicina, prontuário eletrônico, algoritmo | separadas por vírgula; decidem quais PDFs o sync padrão baixa (pela ementa) e o filtro de |
|
| base local |
uv run cfm-cli sync --max-paginas 3 # teste rápido
uv run cfm-cli sync # varredura completa (~8 min com delay de 2s)
uv run cfm-cli consultar telemedicina
uv run cfm-cli novas --dias 90
uv run cfm-cli reextrair-datas # refaz as datas a partir do texto já gravado
uv run cfm-cli schemaLigando ao Claude Code
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-mcpUso
consultar_resolucao_cfm(tema: str, apenas_vigentes=True, limite=10)
limite vai de 1 a 100. Um tema que seja o número de uma resolução (2314/2022,
2.314) busca aquela norma diretamente, inclusive revogada.
{
"tema": "inteligência artificial",
"total": 1,
"retornados": 1,
"truncado": false,
"resultados": [
{
"identificador": "2454/2026",
"ementa": "Normatiza o uso da inteligência artificial na medicina.",
"vigente": true,
"revogada_por": null,
"trecho_relevante": "...Normatiza o uso da inteligência artificial na medicina. O CONSELHO FEDERAL DE MEDICINA...",
"url_origem": "https://sistemas.cfm.org.br/normas/visualizar/resolucoes/BR/2026/2454",
"texto_completo_disponivel": true
}
]
}Ordena por relevância (BM25 do FTS do DuckDB) e depois por data. Revogadas vêm com
vigente: false e o número da que substituiu. total é quantas casam na base
inteira; truncado: true quer dizer que há mais do que veio.
monitorar_novas_resolucoes(dias=30, filtrar_tema=True, limite=50)
O que foi publicado nos últimos dias (1 a 3.650), mais recentes primeiro, até
limite (1 a 100). Com filtrar_tema, só as que citam alguma das PALAVRAS_CHAVE
na ementa ou no texto; o filtro é aplicado antes do limite. A resposta traz total,
retornados, truncado e sem_data_publicacao: a janela usa a data extraída do
texto, e as resoluções sem data ficam de fora, então total zero com muitas sem data
quer dizer "não sei", não "nada foi publicado".
Como a fonte funciona
O portal de busca de normas renderiza os resultados no navegador a partir de uma variável
resultadoBuscaJson embutida no HTML. O crawler lê esse JSON em vez de raspar tabela:
é mais estável, e já vem com o campo de revogação.
Confirmado em 20/09/2026: 2.457 resoluções, 10 por página, 246 páginas.
Dois detalhes que moldaram o desenho:
A busca textual por URL é ignorada pelo portal (o total não muda com
busca=), então o filtro por palavra-chave é aplicado localmente sobre a ementa.A página de detalhe é um visualizador PDF.js, não texto. O PDF real segue o padrão
sistemas.cfm.org.br/normas/arquivos/resolucoes/BR/{ano}/{numero}_{ano}.pdf.
Respeito ao portal
É o site de um conselho profissional, não uma API feita para volume:
intervalo configurável entre requisições (padrão 2s);
o mesmo PDF nunca é baixado duas vezes (cache por hash da URL);
Por padrão, o PDF só é baixado quando a ementa casa com alguma palavra-chave. A instância hospedada baixou as 2.457 uma vez, e a coleta diária dela só baixa o que é novo (ver "Texto integral" abaixo).
Modo connector (servidor HTTP)
Por padrão o servidor fala stdio: o cliente sobe o processo na máquina de quem usa.
Com TRANSPORTE=streamable-http, ele vira um servidor alcançável pela rede, que é o que
o Claude aceita como custom connector.
TRANSPORTE=streamable-http HTTP_HOST=0.0.0.0 HTTP_PORTA=8000 uv run radar-cfm-mcpVariável | Padrão | Observação |
|
|
|
|
| |
|
| cada requisição independente; escala melhor |
|
| o teto que protege a máquina |
|
| por origem, contra chamada direta |
Por que dois limites. Quando o Claude chama um connector remoto, as requisições chegam dos IPs da Anthropic, não do usuário final. Limitar só por IP colocaria todos os usuários no mesmo balde: ou derruba todo mundo junto, ou não protege nada. O teto global é o que vale para esse tráfego; o por origem serve contra quem chama o servidor direto.
Atrás de um proxy ou túnel, declare o nome público
O SDK do MCP valida o cabeçalho Host e responde 421 Invalid Host ao que não
reconhece. É proteção contra DNS rebinding, um ataque em que um site qualquer faz o
navegador da vítima conversar com um servidor que só deveria ser local.
Quando o servidor fica atrás de um túnel, o Host que chega é o nome público, não
127.0.0.1, e toda requisição legítima leva 421. A saída certa é declarar o nome, não
desligar a checagem:
HTTP_HOSTS_PUBLICOS=mcp.exemplo.ts.netAceita vários separados por vírgula. O loopback continua valendo junto, porque é assim que se testa o servidor de dentro da máquina.
Texto integral: baixar o PDF de todas
Por padrão o sync baixa o PDF só das resoluções cuja ementa toca em IA ou
telemedicina, que é o escopo do projeto. São 5 das 2.457. Para as outras, a busca por
tema compara apenas a ementa, e trecho_relevante vem nulo: não porque a norma não
trate do assunto, mas porque o texto nunca foi lido. A resposta diz isso, em
texto_completo_disponivel e no aviso.
Para servir a base a mais gente, vale baixar tudo uma vez:
uv run cfm-cli sync --texto-integral --max-pdfs 0 --publicarCom o intervalo padrão de 2s entre requisições, a primeira execução leva perto de uma
hora e meia e ocupa cerca de 250 MB em data/pdfs. O cache em disco evita repetir, e
resolução que já tem texto na base nem volta ao parser: as execuções seguintes só
baixam o que é novo.
A coleta diária de deploy/radar-cfm-sync.service roda com --texto-integral --max-pdfs 25. Sem isso, uma resolução nova fora de IA e telemedicina entraria sem
texto e, portanto, sem data de publicação, e nunca apareceria no monitoramento.
A coleta seguinte não apaga o texto. Quem roda o sync diário sem --texto-integral
não traz PDF nenhum, e um upsert comum sobrescreveria as extrações com nulo. A coluna
só é substituída quando o novo valor tem conteúdo, porque texto ausente na coleta
significa "não busquei desta vez", nunca "a norma ficou sem texto".
Rodar como serviço
deploy/ tem as units de usuário do systemd que rodam a instância hospedada. Elas
assumem o repositório em ~/radar-cfm-mcp e o uv em ~/.local/bin/uv; ajuste os
caminhos se o seu for outro.
Unit | O que faz |
| o servidor HTTP (modo connector), só leitura |
| uma coleta com |
| dispara a coleta todo dia às 02:00, com até 30 min de espalhamento |
cp deploy/radar-cfm-connector.service deploy/radar-cfm-sync.service \
deploy/radar-cfm-sync.timer ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now radar-cfm-connector.service radar-cfm-sync.timerA unit do connector vem com HTTP_HOSTS_PUBLICOS= vazio. Preencha com o nome público
do túnel ou proxy antes de habilitar, senão tudo que chega de fora recebe 421. Para
atualizar só a coleta numa máquina que já roda o connector, copie apenas
radar-cfm-sync.service e radar-cfm-sync.timer e rode systemctl --user daemon-reload; assim o nome público já configurado no connector não se perde.
O timer do systemd é o único agendador: o projeto não tem agendador interno. Para
rodar uma coleta fora do horário, systemctl --user start radar-cfm-sync.service, e o
resultado sai em journalctl --user -u radar-cfm-sync.
Ela escuta só em 127.0.0.1. Expor para fora é uma camada à parte, um proxy reverso com TLS ou um túnel, que aponta para essa porta. Manter assim deixa a decisão de expor num lugar só, em vez de espalhada em variável de ambiente.
O serviço só lê. Quem escreve é a coleta, que roda separada e troca o arquivo por rename.
Se o processo morrer, o systemd sobe de novo em 5 segundos (Restart=always).
A base não vai junto, e o sync roda fora
O DuckDB recusa abrir para escrita enquanto houver um leitor, e no modo connector o servidor abre a base a cada requisição. Escrever direto no arquivo servido falharia sempre que a coleta caísse em cima de uma consulta.
Por isso o sync usa --publicar: constrói a base ao lado e troca por os.replace, que é
atômico no POSIX. Quem já abriu continua no arquivo antigo até fechar (o tempo de uma
requisição); quem abrir depois pega o novo.
uv run cfm-cli sync --publicar # constrói ao lado e troca no fim
uv run cfm-cli sync --publicar --forcar # aceita base menor que a servidaA base ao lado começa como cópia da servida, não vazia. Uma varredura que pare no meio
(rede ruim, portal fora) produziria uma base com só uma parte das resoluções, e publicá-la
tiraria as outras do ar. Com a cópia, a coleta faz upsert por cima do que já existe: o pior
caso é uma base desatualizada, nunca uma base menor. A cópia é feita pelo próprio DuckDB
(COPY FROM DATABASE), porque um cp pegaria o arquivo sem o WAL pendente.
A publicação é recusada quando a base nova encolhe mais de 10%. Com a cópia acima, uma
varredura interrompida já não produz base pequena: ela só deixa de atualizar. A checagem
fica como rede de segurança para o que a cópia não cobre, como um clone que falhou pela
metade. A versão trocada fica como .anterior, e
store.troca.reverter() volta atrás.
Hospedar reduz a carga no CFM
Hoje, cada pessoa que clona o repositório roda o próprio crawler nas 246 páginas. Com um connector, uma instância varre e todo mundo consulta a mesma base. Para um portal de conselho profissional, o connector é a opção mais respeitosa.
Limitações conhecidas
O corpus relevante é pequeno, e isso é do CFM, não do projeto. Das 2.457 resoluções, apenas 5 ementas mencionam os temas padrão: a 2.454/2026 (IA), a 2.314/2022, as 2.227/2018 e 2.228/2019 (revogadas) e a 1.643/2002. Se você espera dezenas de resultados, o problema é a expectativa.
O filtro age sobre a ementa, não sobre o texto completo. Uma resolução que trate de IA
no corpo sem dizer isso na ementa não terá o PDF baixado. Ajuste PALAVRAS_CHAVE se o seu
tema for outro.
A vigência vem do portal, não de análise jurídica. O campo reflete o que o CFM marca como revogado. Revogação parcial ou alteração por outra resolução não aparece como tal.
O parser depende do formato da página. Se o portal mudar, o crawler falha com uma
mensagem explícita (resultadoBuscaJson não encontrado) em vez de devolver vazio em
silêncio, mas vai falhar.
Nem toda resolução tem data de publicação. A data sai do cabeçalho do texto, e o
campo de data do portal não serve (é a data de carga no sistema deles). Com o extrator
desta versão, medido em 24/09/2026 sobre uma cópia da base, 343 das 2.457 continuam sem
data, quase todas antigas cujo PDF traz o cabeçalho sem a data ("Publicada no D.O. Seção
I, Parte II de", e mais nada). Essas não entram em monitorar_novas_resolucoes, que
informa quantas são em sem_data_publicacao.
O portal erra o ano em revogada_por. A Resolução 1.643/2002 vem como revogada pela
"2314/2024", e a 2.314 é de 2022. O valor fica como o CFM publica, mas a resposta marca
revogada_por_confere: false e sugere a provável em revogada_por_provavel, que é
inferência nossa.
A busca cai para LIKE sem o FTS. A extensão full-text do DuckDB é baixada na primeira
execução. Sem rede, a busca continua respondendo, mais lenta e sem ranking.
Privacidade
Rodando localmente (stdio):
Sai da máquina: requisições ao
portal.cfm.org.bre aosistemas.cfm.org.br, para a busca e os PDFs públicos, só durante o sync.Não sai: os temas que você consulta. A busca roda contra a base local.
Usando o connector hospedado:
Sai da sua máquina: os parâmetros de cada chamada (tema, dias, limite) vão para o servidor do autor, passando pelo Tailscale Funnel.
O que o servidor guarda: o código deste projeto não registra os temas consultados. O único registro por requisição é o IP de origem quando uma chamada é recusada por limite de taxa.
Nos dois modos: sem LLM, sem telemetria, sem analytics.
Contribuindo
Veja CONTRIBUTING.md. Não rode o crawler em loop nem reduza o delay.
Licença e atribuição
Apache License 2.0: escolhida por o projeto tocar em regulação de conduta médica.
Construído no contexto do IA.med.
Available Tools
2 toolsconsultar_resolucao_cfmA
Consulta resoluções do CFM relacionadas a um tema.
Devolve as resoluções mais relevantes, com número, ano, data, ementa, o trecho em volta do termo buscado e sempre a URL de origem. Resoluções revogadas vêm com vigente=false e o número da que as substituiu.
A resposta traz total (quantas casam na base) e retornados (quantas
vieram). Com truncado=true, não conclua "só existem N resoluções sobre
isso": aumente limite.
trecho_relevante=null significa que o termo não aparece no texto, e não
que a norma não trate do assunto. Nesse caso a resolução casou pela ementa
ou pelo índice, então abra a URL antes de afirmar qualquer coisa.
Nunca trate um trecho como "a posição do CFM" sem abrir a fonte: a ementa e o link vêm justamente para isso.
Args: tema: assunto a buscar, por exemplo "telemedicina" ou "inteligência artificial". apenas_vigentes: quando True, omite as resoluções já revogadas. limite: quantas resoluções trazer, no máximo 100. Aumente para ver além das mais relevantes.
| Name | Required | Description | Default |
|---|---|---|---|
| tema | Yes | ||
| limite | No | ||
| apenas_vigentes | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| tema | Yes | |
| aviso | No | |
| total | Yes | Quantas resoluções casam com o tema na base inteira, não quantas vieram nesta resposta. Temas amplos casam com dezenas. |
| truncado | No | True quando total > retornados. As que vieram são as mais relevantes; as demais existem e não estão aqui. Não conclua 'só existem N resoluções sobre isso' a partir da lista. |
| resultados | Yes | |
| retornados | No | Quantas vieram em 'resultados', no máximo 'limite' |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With zero annotations, the description carries the full burden and delivers richly: it discloses that revoked resolutions carry vigente=false plus the replacing number, explains the total vs retornados distinction, and defines the trecho_relevante=null semantics (no textual match, not irrelevance). It also warns against treating an excerpt as the CFM's official position without opening the source — a real hallucination risk the agent could not otherwise anticipate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Every sentence earns its place: purpose, return format, revoked-flag behavior, total/retornados, truncation caveat, null-excerpt caveat, and an anti-overclaiming warning, followed by a structured Args block. Critical interpretation warnings are front-loaded right after the core description, with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 3 params, no annotations, and 0% schema parameter coverage, the description covers invocation semantics, response interpretation, edge cases, and user-error prevention. The only omission is explicit sibling differentiation, which is minor and inferable from the sibling's name.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, and the Args section fully compensates: tema gets domain examples ('telemedicina', 'inteligência artificial'), apenas_vigentes gets behavioral meaning (omits revoked resolutions), and limite gets usage guidance ('Aumente para ver além das mais relevantes'). This adds genuine value beyond the schema's bare types, defaults, and bounds.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Consulta resoluções do CFM relacionadas a um tema' — search CFM resolutions by theme. The search-by-topic behavior distinguishes it from the sibling monitorar_novas_resolucoes, which by name concerns monitoring new resolutions rather than searching historical ones.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear operational context: search by theme, raise limite when truncado=true, and open the URL when trecho_relevante=null. However, it never explicitly names the sibling tool or the condition that would select monitorar_novas_resolucoes instead; that differentiation is left to inference from the sibling's name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
monitorar_novas_resolucoesA
Resoluções do CFM publicadas nos últimos dias, mais recentes primeiro.
A janela usa a data de publicação extraída do texto da resolução. As que
ficaram sem data não entram, e sem_data_publicacao diz quantas são: total
zero com esse número alto quer dizer "não sei", não "nada foi publicado".
A resposta traz total (quantas casam no período) e retornados (quantas
vieram). Com truncado=true, aumente limite ou encurte dias.
Args: dias: tamanho da janela, em dias, a contar de hoje (máximo 3650). filtrar_tema: quando True, devolve só as que mencionam, na ementa ou no texto, as palavras-chave configuradas no servidor (por padrão inteligência artificial, telemedicina, prontuário eletrônico e algoritmo). limite: quantas resoluções trazer, no máximo 100.
| Name | Required | Description | Default |
|---|---|---|---|
| dias | No | ||
| limite | No | ||
| filtrar_tema | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| dias | Yes | |
| aviso | No | |
| total | Yes | Quantas resoluções datadas no período casam com o filtro, na base inteira, não quantas vieram nesta resposta. |
| truncado | No | True quando total > retornados. As que vieram são as mais recentes; as demais existem e não estão aqui. Aumente 'limite' ou encurte 'dias'. |
| resultados | Yes | |
| retornados | No | Quantas vieram em 'resultados', no máximo 'limite' |
| sem_data_publicacao | No | Quantas resoluções da base não têm data e por isso ficam fora deste filtro, existindo ou não no período. Número alto com total=0 significa 'não sei', não 'nada foi publicado'. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so thoroughly: it discloses that the date window is based on the publication date extracted from the text, that undated resolutions are excluded, and that a high sem_data_publicacao with total zero means 'unknown' rather than 'nothing published'. It also explains the total/retornados response fields and the meaning of truncado=true, going well beyond a simple listing statement.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose and then adds only high-value caveats and parameter semantics. The Args block is compact, and each sentence earns its place without filler or repetition of schema defaults.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only monitoring tool with no annotations, the description covers the essential call semantics: window definition, filtering, limits, response fields, and the truncation recovery action. The presence of an output schema is supplemented by the description of total, retornados, sem_data_publicacao, and truncado, so an agent has enough to invoke and interpret the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must define the parameters, and it does: dias is the window in days from today (max 3650), filtrar_tema triggers server-side keyword filtering with default topics, and limite caps the number of returned resolutions (max 100). This adds real behavioral meaning that the bare schema cannot convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Resoluções do CFM publicadas nos últimos dias, mais recentes primeiro,' which names the exact resource (CFM resolutions) and the operation (monitor/list recent publications). It also specifies ordering and the time window, making it easy to distinguish from the sibling consultar_resolucao_cfm even without naming it.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains how to adjust the call via dias, filtrar_tema, and limite, and how to interpret truncation, but it never explicitly states when to prefer this tool over consultar_resolucao_cfm or when not to use it. Usage is implied by the monitoring purpose rather than contrasted with alternatives.
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.
2 tool updates
- Changed
consultar_resolucao_cfm12 fields changed- added
Input schema / properties / limiteAdded value: +{ + "default": 10, + "maximum": 100, + "minimum": 1, + "title": "Limite", + "type": "integer" +} - added
Output schema / $defs / ResolucaoCFM / properties / nota_vigenciaAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "O texto da marcação, como o CFM escreveu", + "title": "Nota Vigencia" +} - added
Output schema / $defs / ResolucaoCFM / properties / revogada_por / descriptionAdded value: +"Identificador da resolução que revogou esta, como o portal do CFM publica. Confira 'revogada_por_confere' antes de citar: o portal erra o ano em alguns casos." - added
Output schema / $defs / ResolucaoCFM / properties / revogada_por_confereAdded value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "False quando o identificador acima não existe na base, ou seja, o portal publicou um ano que não bate. Nesse caso veja 'revogada_por_provavel'.", + "title": "Revogada Por Confere" +} - added
Output schema / $defs / ResolucaoCFM / properties / revogada_por_provavelAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Única resolução com aquele número, quando o identificador publicado não existe. É inferência nossa a partir do número, não o que o CFM publicou: confirme na URL de origem antes de citar.", + "title": "Revogada Por Provavel" +} - added
Output schema / $defs / ResolucaoCFM / properties / suspensaAdded value: +{ + "default": false, + "description": "A ementa traz marcação de suspensão. Para quem vai aplicar a norma, suspensa tem o mesmo efeito prático de revogada.", + "title": "Suspensa", + "type": "boolean" +} - added
Output schema / $defs / ResolucaoCFM / properties / suspensao_parcialAdded value: +{ + "default": false, + "description": "Só alguns dispositivos foram suspensos; o resto da norma segue valendo", + "title": "Suspensao Parcial", + "type": "boolean" +} - changed
Output schema / $defs / ResolucaoCFM / properties / trecho_relevante / descriptionPrevious value: -"Trecho do texto completo em volta do termo buscado"New value: +"Trecho do texto integral em volta do termo buscado. None por dois motivos diferentes, distinguidos por 'texto_completo_disponivel': se for false, o texto integral não foi baixado e a busca viu só a ementa; se for true, o termo não aparece no texto. Em nenhum dos casos conclua que a norma não trata do assunto, abra a URL de origem." - added
Output schema / $defs / ResolucaoCFM / properties / vigente / descriptionAdded value: +"Só reflete revogação, que é o que o portal marca em campo próprio. Uma norma suspensa vem com vigente=true: veja 'suspensa' antes de concluir que ela está produzindo efeito." - added
Output schema / properties / retornadosAdded value: +{ + "default": 0, + "description": "Quantas vieram em 'resultados', no máximo 'limite'", + "title": "Retornados", + "type": "integer" +} - added
Output schema / properties / total / descriptionAdded value: +"Quantas resoluções casam com o tema na base inteira, não quantas vieram nesta resposta. Temas amplos casam com dezenas." - added
Output schema / properties / truncadoAdded value: +{ + "default": false, + "description": "True quando total > retornados. As que vieram são as mais relevantes; as demais existem e não estão aqui. Não conclua 'só existem N resoluções sobre isso' a partir da lista.", + "title": "Truncado", + "type": "boolean" +}
- Changed
monitorar_novas_resolucoes15 fields changed- added
Input schema / properties / dias / maximumAdded value: +3650 - added
Input schema / properties / dias / minimumAdded value: +1 - added
Input schema / properties / limiteAdded value: +{ + "default": 50, + "maximum": 100, + "minimum": 1, + "title": "Limite", + "type": "integer" +} - added
Output schema / $defs / ResolucaoCFM / properties / nota_vigenciaAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "O texto da marcação, como o CFM escreveu", + "title": "Nota Vigencia" +} - added
Output schema / $defs / ResolucaoCFM / properties / revogada_por / descriptionAdded value: +"Identificador da resolução que revogou esta, como o portal do CFM publica. Confira 'revogada_por_confere' antes de citar: o portal erra o ano em alguns casos." - added
Output schema / $defs / ResolucaoCFM / properties / revogada_por_confereAdded value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "False quando o identificador acima não existe na base, ou seja, o portal publicou um ano que não bate. Nesse caso veja 'revogada_por_provavel'.", + "title": "Revogada Por Confere" +} - added
Output schema / $defs / ResolucaoCFM / properties / revogada_por_provavelAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Única resolução com aquele número, quando o identificador publicado não existe. É inferência nossa a partir do número, não o que o CFM publicou: confirme na URL de origem antes de citar.", + "title": "Revogada Por Provavel" +} - added
Output schema / $defs / ResolucaoCFM / properties / suspensaAdded value: +{ + "default": false, + "description": "A ementa traz marcação de suspensão. Para quem vai aplicar a norma, suspensa tem o mesmo efeito prático de revogada.", + "title": "Suspensa", + "type": "boolean" +} - added
Output schema / $defs / ResolucaoCFM / properties / suspensao_parcialAdded value: +{ + "default": false, + "description": "Só alguns dispositivos foram suspensos; o resto da norma segue valendo", + "title": "Suspensao Parcial", + "type": "boolean" +} - changed
Output schema / $defs / ResolucaoCFM / properties / trecho_relevante / descriptionPrevious value: -"Trecho do texto completo em volta do termo buscado"New value: +"Trecho do texto integral em volta do termo buscado. None por dois motivos diferentes, distinguidos por 'texto_completo_disponivel': se for false, o texto integral não foi baixado e a busca viu só a ementa; se for true, o termo não aparece no texto. Em nenhum dos casos conclua que a norma não trata do assunto, abra a URL de origem." - added
Output schema / $defs / ResolucaoCFM / properties / vigente / descriptionAdded value: +"Só reflete revogação, que é o que o portal marca em campo próprio. Uma norma suspensa vem com vigente=true: veja 'suspensa' antes de concluir que ela está produzindo efeito." - added
Output schema / properties / retornadosAdded value: +{ + "default": 0, + "description": "Quantas vieram em 'resultados', no máximo 'limite'", + "title": "Retornados", + "type": "integer" +} - added
Output schema / properties / sem_data_publicacaoAdded value: +{ + "default": 0, + "description": "Quantas resoluções da base não têm data e por isso ficam fora deste filtro, existindo ou não no período. Número alto com total=0 significa 'não sei', não 'nada foi publicado'.", + "title": "Sem Data Publicacao", + "type": "integer" +} - added
Output schema / properties / total / descriptionAdded value: +"Quantas resoluções datadas no período casam com o filtro, na base inteira, não quantas vieram nesta resposta." - added
Output schema / properties / truncadoAdded value: +{ + "default": false, + "description": "True quando total > retornados. As que vieram são as mais recentes; as demais existem e não estão aqui. Aumente 'limite' ou encurte 'dias'.", + "title": "Truncado", + "type": "boolean" +}
2 tool updates
v0.1.0- First observed
consultar_resolucao_cfm - First observed
monitorar_novas_resolucoes
TDQS
Scored across 2 tools
The two tools have clearly distinct purposes: one searches resolutions by topic, the other monitors recently published resolutions. There is no overlap or ambiguity between them.
Both tool names follow the same verb_noun pattern in Portuguese: consultar_resolucao_cfm and monitorar_novas_resolucoes. The naming is consistent and predictable.
With only 2 tools, the server feels thin for a domain that could reasonably include fetching a specific resolution by number or listing by year. However, the narrow 'radar' purpose partially justifies the small count, making it borderline rather than extreme.
The tool surface covers the two core needs: finding resolutions by topic and tracking new publications. Obvious gaps like fetching a resolution by its number or browsing all resolutions are missing, but agents can work around them via the search tool, so the gaps are minor.
Maintenance
Related MCP Connectors
Conselho Federal de Medicina: Cadastro, official-source lookup. Platform-hosted, pay per query with
Conselho Federal de Medicina: Estabelecimentos de Saúde, official-source lookup. Platform-hosted, pa
Open-source alternative to Jusbrasil for AI: find lawsuits by name, CPF, CNPJ or case number and bui
Physician-reviewed medical opinions and prescriptions for AI agents.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceMCP server for querying Brazilian Federal Council of Medicine (CFM) registration data from official sources. It provides a read-only tool to consult medical registrations via natural language.MIT
- AlicenseNot gradedqualityCmaintenanceEnables querying health establishment data from the Brazilian Federal Council of Medicine (CFM), providing read-only access to official information.MIT
- AlicenseNot gradedqualityCmaintenanceEnables read-only consultation of official Conselho Regional de Farmácia GO (CRF-GO) registry data through an MCP server, allowing users to query professional registration information using natural language.MIT
- AlicenseNot gradedqualityCmaintenanceEnables querying official Brazilian Federal Accounting Council records for accounting professionals and companies, providing read-only consultation through natural language.MIT