local-pdf-rag-mcp

Um servidor MCP totalmente local para perguntas e respostas sobre seus PDFs. Pergunte em linguagem natural; Claude recupera apenas as passagens relevantes com citações de página. Embeddings no dispositivo (sentence-transformers) + ChromaDB — sem chaves de API, nada sai da sua máquina.

Documentação

local-pdf-rag-mcp

CI

Um servidor MCP totalmente local que permite ao Claude (ou qualquer cliente MCP) responder perguntas sobre seus PDFs. Aponte-o para um PDF, faça perguntas em linguagem natural, e o modelo busca apenas as passagens relevantes — com citações em nível de página — em vez de processar o documento inteiro.

  • Totalmente local por padrão. Embeddings são executados no dispositivo (sentence-transformers) e os vetores são armazenados em disco (ChromaDB). Sem chaves de API, nada sai da sua máquina.
  • Econômico em tokens. Apenas um punhado de trechos relevantes é enviado ao modelo por pergunta, não o documento inteiro.
  • Respostas citadas. Cada trecho recuperado carrega o nome do arquivo de origem e o número da página.
  • Qualquer PDF, muitos PDFs. Indexe um único arquivo ou uma pasta inteira, organizados em coleções nomeadas.

Como funciona

A ingestão (uma vez por documento) extrai o texto página por página, divide-o em trechos sobrepostos de ~250 tokens que respeitam limites de parágrafos, gera embeddings de cada trecho localmente e os armazena no ChromaDB. No momento da consulta, o servidor gera o embedding da sua pergunta, busca os ~20 principais trechos na pesquisa vetorial e os reordena com um cross-encoder local para que os mais relevantes apareçam primeiro. O modelo lê esses trechos e escreve a resposta — o servidor deliberadamente não gera respostas por conta própria, o que o mantém simples e agnóstico em relação ao modelo.

Requisitos

  • Python 3.10+
  • ~170 MB de disco para os dois modelos padrão, baixados automaticamente e armazenados em cache: o modelo de embedding (~80 MB, baixado na primeira ingestão/pesquisa) e o cross-encoder de reordenação (~80 MB, baixado na primeira pesquisa). A reordenação pode ser desativada com PDF_RAG_RERANK=0 se você preferir pular o segundo download.

Instalação

git clone https://github.com/arjun7965/local-pdf-rag-mcp.git
cd local-pdf-rag-mcp
pip install -e .

Nix

O repositório inclui um flake uv2nix com um conjunto de dependências Python travado:

nix run github:arjun7965/local-pdf-rag-mcp

Para desenvolvimento local, entre no ambiente editável com nix develop. Ele fornece as dependências da aplicação e uv sem modificar o ambiente do projeto; use uv lock ao alterar dependências.

Registrar com Codex

Se você instalou com pip install -e ., registre o comando de console:

codex mcp add pdf-rag -- local-pdf-rag-mcp

Ou execute diretamente do GitHub sem clonar, usando o recurso uvx do uv:

codex mcp add pdf-rag -- uvx --from git+https://github.com/arjun7965/local-pdf-rag-mcp.git local-pdf-rag-mcp

Verifique o registro:

codex mcp get pdf-rag

O Codex CLI e a extensão Codex IDE compartilham a configuração do MCP. Para configurar o servidor manualmente, adicione isto a ~/.codex/config.toml, ou a .codex/config.toml para um projeto confiável:

[mcp_servers.pdf-rag]
command = "local-pdf-rag-mcp"

Inicie uma nova sessão do Codex após registrar o servidor. Na interface de terminal do Codex, use /mcp para verificar se ele está ativo. Consulte a documentação do MCP do Codex.

Registrar com Claude Code

Se você instalou (o pip install -e . acima), aponte o Claude Code para o comando de console:

claude mcp add pdf-rag -- local-pdf-rag-mcp

Ou execute diretamente do GitHub sem clonar, usando o recurso uvx do uv — ele busca e armazena em cache o pacote no primeiro lançamento:

claude mcp add pdf-rag -- uvx --from git+https://github.com/arjun7965/local-pdf-rag-mcp.git local-pdf-rag-mcp

Ou adicione manualmente à sua configuração MCP do Claude:

{
  "mcpServers": {
    "pdf-rag": {
      "command": "local-pdf-rag-mcp"
    }
  }
}

Reinicie o Claude Code para que ele reconheça o novo servidor.

Uso

O servidor expõe quatro ferramentas. Na prática, você apenas conversa com o Claude e ele as chama para você:

Você: Ingira a especificação em ~/docs/pcie-5.0.pdf em uma coleção chamada "pcie".

Claude chama ingest_pdf → "Ingerido na coleção 'pcie': pcie-5.0.pdf: 712 páginas, 2{,}480 trechos"

Você: Como funciona a equalização de link durante o treinamento?

Claude chama search com sua pergunta, lê as passagens retornadas e responde — citando, por exemplo, pcie-5.0.pdf, p.412.

Ferramentas

FerramentaO que faz
ingest_pdf(path, collection="default")Divide em trechos + gera embeddings de um arquivo PDF, ou de todos os PDFs em uma pasta, em uma coleção.
list_collections()Mostra coleções indexadas e suas contagens de trechos.
search(query, collection="default", top_k=8)Retorna os trechos mais relevantes com citações.
delete_collection(collection)Exclui uma coleção e todos os seus trechos (irreversível).

Configuração

Variáveis de ambiente:

VariávelPadrãoFinalidade
PDF_RAG_EMBED_MODELall-MiniLM-L6-v2Qualquer nome de modelo sentence-transformers.
PDF_RAG_RERANK_MODELcross-encoder/ms-marco-MiniLM-L-6-v2Cross-encoder usado para reordenar resultados vetoriais.
PDF_RAG_RERANK1Defina como 0 para pular a reordenação e usar a classificação vetorial bruta.
PDF_RAG_TABLES0Defina como 1 para habilitar a extração ciente de tabelas (veja abaixo).
PDF_RAG_DB_PATH~/.local_pdf_rag_mcp/chromaOnde o armazenamento vetorial reside em disco.

Extração ciente de tabelas (opt-in)

Por padrão, o texto é extraído linearmente — tabelas são achatadas em prosa, o que dispersa as células de uma linha e prejudica a recuperação em documentos técnicos densos. Defina PDF_RAG_TABLES=1 para detectar tabelas com linhas de grade e serializá-las como um registro por linha (Field: Foo; Bits: 0-3; Description: ...), de modo que uma consulta sobre uma única linha corresponda diretamente ao registro dessa linha. A detecção é conservadora (depende de linhas de grade, então prosa alinhada por espaços não é interpretada erroneamente como tabela), e qualquer página sem tabela detectada volta ao caminho normal de prosa. Reingira após habilitar, pois a mudança afeta apenas ingestões futuras.

Apenas tabelas com linhas de grade são detectadas. Como a detecção exige linhas de grade visíveis, tabelas sem bordas — colunas alinhadas por espaços sem linhas de grade — não são reconhecidas e voltam ao caminho de prosa, onde suas células são achatadas em texto linear. Este é um tradeoff deliberado: a detecção baseada em alinhamento capturaria tabelas sem bordas, mas também interpretaria erroneamente layouts de prosa comuns como tabelas, fragmentando-os em células inúteis. Se seus documentos dependem de tabelas sem bordas, PDF_RAG_TABLES não ajudará com elas.

Modelo de embedding

O padrão é all-MiniLM-L6-v2 e o restante do projeto é ajustado em torno dele:

  • Por que este modelo. Pequeno (~80 MB), rápido em CPU, sem necessidade de GPU, qualidade decente de recuperação em inglês geral, licenciado sob Apache-2.0. Padrão comum no ecossistema sentence-transformers.
  • Limite de entrada: 256 tokens. sentence-transformers trunca silenciosamente entradas acima do max_seq_length do modelo. Os trechos são dimensionados para permanecer dentro desta janela, de modo que o embedding reflita o trecho inteiro, não apenas seu início. Se você trocar por um modelo com limite diferente (por exemplo, BAAI/bge-large-en-v1.5 em 512), considere aumentar target_tokens em chunk_pages para corresponder — caso contrário, você paga por capacidade que não usa.
  • Saída: um vetor de 384 dimensões. Cada trecho é incorporado em um vetor fixo de 384 dimensões, independentemente do seu comprimento (o modelo produz um vetor, não texto, então não há limite de tokens de saída). O ChromaDB infere essa dimensionalidade automaticamente; um modelo diferente com tamanho diferente simplesmente funciona, mas misturar vetores de tamanhos diferentes em uma coleção não — reingira após trocar de modelo.
  • Troca. Qualquer modelo sentence-transformers do HuggingFace funciona:
    PDF_RAG_EMBED_MODEL=BAAI/bge-large-en-v1.5 local-pdf-rag-mcp
    
    Modelos maiores (BGE-large, E5-large) melhoram a qualidade da recuperação ao custo de mais disco, mais RAM e embedding mais lento. Trocar de modelo invalida quaisquer vetores existentes — exclua ~/.local_pdf_rag_mcp/chroma e reingira.
  • Operação offline / silenciosa. Ambos os modelos são armazenados em cache após o primeiro uso (em ~/.cache/huggingface). Para operar totalmente offline depois — e silenciar a mensagem Warning: You are sending unauthenticated requests to the HF Hub — defina HF_HUB_OFFLINE=1. Desative temporariamente se você trocar para um modelo que ainda não baixou, pois o modo offline bloqueia novos downloads.

Limitações

  • Sem OCR. PDFs escaneados ou apenas com imagens não têm texto extraível; o servidor detecta isso e retorna um erro claro em vez de indexar nada.
  • PDFs criptografados abrem apenas se usarem senha vazia.
  • Tabelas sem bordas. Mesmo com PDF_RAG_TABLES=1, apenas tabelas com linhas de grade/borda visíveis são detectadas. Tabelas alinhadas por espaços são achatadas em prosa como qualquer outro texto (veja Configuração → Extração ciente de tabelas).
  • Limite de entrada do embedding. Trechos mais longos que o max_seq_length do modelo de embedding (256 tokens para o MiniLM padrão) são truncados silenciosamente pelo sentence-transformers — o texto completo ainda é armazenado e retornado, mas o vetor reflete apenas o início. Mantenha target_tokens alinhado com o modelo que você usar.
  • Ajustado para um fluxo de trabalho de máquina única e usuário único. Para implantações multiusuário ou de escala muito grande, você trocaria o ChromaDB por um armazenamento vetorial hospedado.

Licença

MIT — veja LICENSE.