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
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=0se 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
searchcom sua pergunta, lê as passagens retornadas e responde — citando, por exemplo,pcie-5.0.pdf, p.412.
Ferramentas
| Ferramenta | O 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ável | Padrão | Finalidade |
|---|---|---|
PDF_RAG_EMBED_MODEL | all-MiniLM-L6-v2 | Qualquer nome de modelo sentence-transformers. |
PDF_RAG_RERANK_MODEL | cross-encoder/ms-marco-MiniLM-L-6-v2 | Cross-encoder usado para reordenar resultados vetoriais. |
PDF_RAG_RERANK | 1 | Defina como 0 para pular a reordenação e usar a classificação vetorial bruta. |
PDF_RAG_TABLES | 0 | Defina como 1 para habilitar a extração ciente de tabelas (veja abaixo). |
PDF_RAG_DB_PATH | ~/.local_pdf_rag_mcp/chroma | Onde 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_lengthdo 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.5em 512), considere aumentartarget_tokensemchunk_pagespara 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:
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 — excluaPDF_RAG_EMBED_MODEL=BAAI/bge-large-en-v1.5 local-pdf-rag-mcp~/.local_pdf_rag_mcp/chromae 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 mensagemWarning: You are sending unauthenticated requests to the HF Hub— definaHF_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_lengthdo 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. Mantenhatarget_tokensalinhado 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.