Projeto IA.med Licença MIT Resumo local

radar-papers-mcp

Servidor MCP que monitora PubMed e medRxiv por tópicos de pesquisa e resume os papers com um LLM local, numa base local deduplicada por DOI.

O que a ferramenta faz e não faz

  • O resumo sai do abstract, não do texto completo. Serve para triagem, decidir se vale abrir o paper; não substitui a leitura.
  • O resumo é gerado por LLM e pode errar. Confira os números no abstract original: o link vem em toda resposta.
  • Aqui não há conferência de citação literal. O campo achado_principal pode conter um número que o modelo interpretou errado.
  • A cobertura é a dos tópicos configurados. Não é varredura da literatura: o que não casa com a query configurada, na janela pedida, não aparece; ausência de resultado não significa ausência de literatura.
  • Falha de fonte não passa em silêncio. Se o PubMed ou o medRxiv falhar, o sync grava o que as outras fontes trouxeram, lista as falhas e sai com código 1.

O problema

Por que este projeto existe

Literatura demais para acompanhar à mão

PubMed e medRxiv publicam sem parar. Acompanhar um punhado de tópicos de pesquisa manualmente, todo dia, não escala.

Triar abstract a abstract cansa

A maior parte do tempo é decidir se vale abrir o paper. Um resumo curto e estruturado do abstract acelera essa triagem.

O mesmo paper aparece duas vezes

Um preprint do medRxiv sai depois num periódico do PubMed com o mesmo DOI, e um paper casa com mais de um tópico. Sem deduplicação, a lista vira ruído.

Como funciona

Da fonte pública ao resumo de triagem

  1. 01

    Fontes

    PubMed (E-utilities) e medRxiv (details), por tópicos configuráveis. O sync pagina até o fim da janela e respeita o rate limit da NCBI; o medRxiv não busca por termo, então o filtro é local.

  2. 02

    Deduplicação

    A chave é o DOI. Um paper já conhecido ganha o novo tópico na lista, em vez de virar uma segunda linha.

  3. 03

    Armazenamento

    Guarda em DuckDB local, com a data de entrada na fonte, que é o que define "novo". O resumo fica cacheado: o mesmo paper não é resumido duas vezes.

  4. 04

    Resumo por LLM local

    Do abstract, um resumo estruturado (problema, método, achado principal, relevância), exposto como tools MCP.

Um paper sem abstract devolve resumo nulo com o motivo no aviso, não um resumo inventado a partir do título. E o resumo não é conferido contra o abstract: o link original vem em toda resposta justamente para você checar os números.

As ferramentas

Duas tools MCP expostas ao modelo

O servidor roda no stdio e só lê a base local; quem a alimenta é o papers-cli sync. Toda resposta traz o link original do paper. Os exemplos abaixo são retornos reais do projeto.

buscar_papers_novos

Papers que entraram no PubMed ou no medRxiv nos últimos dias, lidos da base local, com a chave para usar no resumo e o link original de cada um.

buscar_papers_novos(topico: str = "", dias: int = 7, limite: int = 50)
# topico: nome de um tópico configurado, sem caixa e sem acento,
#         basta um trecho com palavras inteiras ("fairness"); vazio = todos
# dias:   janela de 1 a 365, a contar de hoje
# limite: de 1 a 200, os mais recentes primeiro
"Novo" é pela data de entrada na fonte (data_entrada: Entrez no PubMed, postagem no medRxiv), não pela data da edição da revista (data_publicacao, só para exibição). O campo chave de cada resultado é o paper_id do resumir_paper. total conta todos os papers do período; se passar do limite, o aviso diz quantos ficaram de fora. Tópico que não corresponde a um único configurado devolve a lista dos válidos no aviso.
{
  "topico": "calibração de modelos clínicos",
  "dias": 7,
  "total": 59,
  "resultados": [
    {
      "chave": "10.1016/j.ijantimicag.2026.108012",
      "doi": "10.1016/j.ijantimicag.2026.108012",
      "fonte": "pubmed",
      "titulo": "A clinical prediction model to support risk-adapted fibrinogen monitoring during tigecycline therapy: multicenter development and validation.",
      "autores": "Guo J, Cai X, Huo Y, Wen H, Li J, Guo S, Hao L, Wu X, Hou W, Li S, Jia H, Hou R",
      "veiculo": "International journal of antimicrobial agents",
      "data_publicacao": "2026-09-23",
      "data_entrada": "2026-09-24",
      "url": "https://pubmed.ncbi.nlm.nih.gov/42777972/",
      "topicos": ["calibração de modelos clínicos"],
      "tem_abstract": true
    }
  ],
  "aviso": "Mostrando os 1 mais recentes de 59 papers do período. Reduza 'dias', filtre por tópico ou aumente 'limite' (até 200)."
}
Saída real com limite=1, numa cópia da base local

resumir_paper

Resumo estruturado do abstract (problema, método, achado principal, relevância), gerado pelo LLM local. Fica cacheado; o mesmo paper não é resumido duas vezes. Sem abstract, ou com o LLM fora do ar, resumo vem nulo, origem vem indisponivel e o aviso diz o motivo.

resumir_paper(paper_id: str)
# paper_id: a chave devolvida por buscar_papers_novos (o DOI, em geral)
{
  "chave": "pubmed:42761253",
  "titulo": "Mortality risk ranking after medical emergency team review...",
  "url": "https://pubmed.ncbi.nlm.nih.gov/42761253/",
  "origem": "llm",
  "resumo": {
    "problema": "Validação externa e redesenvolvimento de modelos preditivos de mortalidade após revisão da equipe de emergência.",
    "metodo": "Coorte multicêntrica em quatro hospitais, 1.937 adultos.",
    "achado_principal": "O modelo original discriminou bem (AUC 0,80) mas com estimativas variáveis entre hospitais; o novo modelo chegou a AUC 0,84 com menos variáveis.",
    "relevancia": "Mostra que a discriminação pode ser robusta mesmo quando a calibração absoluta varia entre instituições."
  },
  "aviso": "O resumo é gerado por LLM local a partir do abstract, não do texto completo. Sempre confira no link original antes de citar."
}
Resumo real do projeto; a chave é ilustrativa

Como rodar

Roda na sua máquina

Não é um serviço hospedado. Você define os tópicos, sincroniza das fontes públicas e o servidor MCP roda ali, no seu computador, pelo stdio do seu cliente, com o resumo pelo seu LLM local (qualquer API OpenAI-compatible: llama.cpp, Ollama, LM Studio). Distribuição por clone e uv, com Python 3.12+ e o SDK mcp 2.x.

Só local, por stdio: não há conector hospedado, e o resumo usa modelo local. A coleta (papers-cli sync --dias 30) leva minutos. Licença MIT, a única da família: o ans-mcp e o raciocinio-br-mcp são privados.

Estado conferido em 12/10/2026

Na base do autor: 280 papers e 1 resumo de triagem já gerado, em 6 MB, e 100 testes automatizados. São números de uma leitura em 12/10/2026 e envelhecem; a sua base terá o tamanho dos seus tópicos.

# 1. clonar e instalar
git clone https://github.com/fabianofilho/radar-papers-mcp.git
cd radar-papers-mcp
uv sync
cp .env.example .env
# opcional: PUBMED_API_KEY eleva o rate limit da NCBI de 3 para 10 req/s

# 2. definir os tópicos em config/topicos.yaml e sincronizar (leva minutos)
uv run papers-cli topicos
uv run papers-cli sync --dias 30

# 3. buscar e resumir
uv run papers-cli buscar "multicalibração"
uv run papers-cli resumir "10.1016/j.exemplo.2026.100217"

# 4. registrar o servidor MCP (stdio) no Claude Code
claude mcp add radar-papers --scope user \
  -e DUCKDB_PATH=/caminho/para/radar-papers-mcp/data/papers.duckdb \
  -e TOPICOS_PATH=/caminho/para/radar-papers-mcp/config/topicos.yaml \
  -e QWEN_ENDPOINT=http://127.0.0.1:8080/v1 \
  -e QWEN_MODEL=local-model \
  -- uv --directory /caminho/para/radar-papers-mcp run radar-papers-mcp
Confira variáveis e opções no README do projeto.

Um tópico em config/topicos.yaml

topicos:
  - nome: multicalibração
    pubmed: multicalibration[All Fields]   # sintaxe das E-utilities
    medrxiv:                                # palavras simples, filtradas localmente
      - multicalibration
      - multi-calibration
Cada tópico tem duas configs porque as fontes funcionam diferente.

Sync agendado

O servidor não agenda nada sozinho. O repositório traz em deploy/ um timer systemd de usuário para o sync diário. Falha não passa em silêncio: se o PubMed (em qualquer tópico) ou o medRxiv falhar, papers-cli sync grava o que as outras fontes trouxeram, escreve uma linha por falha no stderr (falhou: pubmed/<tópico>: <erro> ou falhou: medrxiv: <erro>) e sai com código 1. Sem falha, sai com 0. O OnFailure da unit do sync dispara radar-papers-sync-falha.service, que grava uma linha err no journal e, se houver notify-send, mostra uma notificação.

# sync diário às 04:10 (atraso aleatório de até 30 min)
cp deploy/radar-papers-sync.service deploy/radar-papers-sync.timer \
   deploy/radar-papers-sync-falha.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now radar-papers-sync.timer

# falhas recentes
systemctl --user status radar-papers-sync.service
journalctl --user -t radar-papers-sync -p err
As units supõem o repo em ~/radar-papers-mcp e o uv em ~/.local/bin/uv; ajuste se for diferente.

O que sai da máquina: as requisições ao PubMed e ao medRxiv (serviços públicos, não rode em loop nem contorne o limitador). Os tópicos ficam no arquivo local, e o abstract vai para o seu LLM local no resumo.