docs-rag-mcp
docs-rag-mcp
No es un mejor buscador sobre tus documentos. Es un filtro sobre lo que el agente puede creer: las decisiones obsoletas desaparecen, el umbral dice «no lo sé» en lugar de inventar, y siempre ves por qué ha entrado un resultado.
Un servidor MCP que expone una carpeta de documentos markdown a cualquier cliente MCP (Claude Code, Claude Desktop, Codex, Cursor, Zed…) como una única herramienta de búsqueda, search_notes. Todo se ejecuta en tu máquina: los embeddings pasan por Ollama y nada sale de tu ordenador ni en el indexado ni en la consulta.
npx -y docs-rag-mcp init # guided questions -> config.json
npx -y docs-rag-mcp index # builds the index¿Escribes la documentación que vas a buscar? La forma en que estructuras un archivo Markdown decide lo bien que se puede encontrar. Consulta AUTHORING.md — una guía breve para redactar documentación que se recupere bien. Cinco minutos allí mejoran cada búsqueda.
Qué es lo que realmente lo hace distinto
La mayor parte de lo que hace esta herramienta también lo hacen otros servidores RAG locales. La parte que es genuinamente rara es el ciclo de vida del documento: el índice sabe que un documento ha sido sustituido y actúa en consecuencia.
Un documento marcado con
status: supersededen su frontmatter deja de devolverse por defecto. Cuándo sí vuelve — porque pediste explícitamente el historial — llegua etiquetado como[superseded → reference/auth.md; 2026-03-01], de modo que el sucesor viaja con él.Por debajo de
minScore, la herramienta dice "No relevant results (best score 0.41, threshold 0.55)" en lugar de entregar el ruido más cerano que haya encontrado. Un agente que recibe una coincidencia mala la trata como verdad absoluta; un humano habría dudado. El umbral es donde vive esa dudo.Cada result ado muestra por qué está ahí:
[semantic 0.712],[both 0.712],[exact match]. No es una caja negra en la que tengas que confiar.
Todos los demás hacen frescura de archivos — resincronizar, reindizar, vigilar cambios. Nadie hace frescura de la verdad. Ese es el sentido de esta herramienta.
approach | handles document lifecycle? | |
docs-rag-mcp | denso + léxico, SQLite, Ollama | sí — |
| híbrido BM25+denso, fragmentación por AST, Milvus | no |
| semántico + palabras claves, LanceDB, PDF/DOCX/MD | no |
| markdown, fragmentación por encabezados, Milvus | no |
| sqlite-vec + Ollama, consciente del grafo | no |
| pgvector | no |
El resto — solo local, fragmentación por encabezados, almacenamiento en SQLite, indexación incremental — son los requisitos mínimos en este campo, no un diferenciador. Está en la tabla de característias de abajo, no en la propuesta.
Related MCP server: recall-mcp
Cuándo no necesitas esto
Versión honesta, porque el campo ha evolucionado:
Para coincidencias exactas de símbolos, tu agente ya tiene grep, y grep es más rápido y no necesita índice. Nombres de funciones, códigos de error, claves de config: no construyas un índice vectorial para eso.
Con menos de unas pocas docenas de documentos, la búsqueda agéntica es suficiente. El agente lee el árbol de archivos, usa grep, abre lo que parece relevante. Eso funciona.
Anthropic lanzó RAG con una base de datos vectorial dentro de Claude Code y luego lo retiró (mayo de 2025), porque la búsqueda agéntica lo superaba. Cursor, Windsurf, Cline y otros siguieron el mismo camino. Fingir lo contrario sería deshonesto.
Lo que sobrevive a eso, y es la razón de que esto exista: grep encuentra lo que tú nombras. Cuándo el documento lo llama «token refresh window» y tú lo llamas «session expiry», grep no devuelve nada y la búsqueda semántica devuelve el documento. Y en un corpus largo y en capas, la recuperación semántica cuesta menos viajes de ida y vuelta y menos tokens que hacer que el agente recorra el árbol — no «mejores resultados», resultados más baratos.
Así que el encaje es estrecho y específico: un corpus largo y en capas de decisiones, especificaciones y ADR en markdown, con material sustituido, donde necesites que el agente no rescucite una decisión muerta.
Prerrequisitos
Node.js ≥ 22.5 — el índice usa el módulo incorporado
node:sqlite, así que no hay dependencias nativas que compilar. Según tu versión de Node, puede que veas una advertencia de una línea en stderr (ExperimentalWarning: SQLite is an experimental feature); es inofensiva.Ollama con un modelo de embeddings. El modelo es necesario tanto para construir el índice como en cada búsqueda — la consulta se incrusta sobre la marcha, así que Ollama debe estar en ejecución siempre que el servidor MCP esté en uso, no solo al indexar.
# install Ollama from https://ollama.com, then: ollama pull bge-m3
Instalación
El paquete publicado no necesita clonado ni compilación:
npx -y docs-rag-mcp init # guided questions -> writes config.json
npx -y docs-rag-mcp index # builds the indexSi tienes previsto usar el hook de reindexado automático, instálalo globalmente en su lugar:
npm i -g docs-rag-mcpnpx vuelve a resolver el paquete en cada invocación y lo descarga el primer uso. Eso es irrelevante para el servidor MCP, que se inicia una vez por sesión, pero el hook está diseñado para salir en cerco segundos y se ejecuta después de cada edición de archivo — una instalación global elimina esa sobrecarga por completo. docs-rag scaffold detecta una instalación global y escribe por sí solo la forma más corta del comando.
¿Prefieres editar una configuración a mano? Copia config.example.json a config.json y establece vaultPath. Todo lo demás tiene valores predeterminados sensatos.
git clone https://github.com/andreaselmi/docs-rag-mcp
cd docs-rag-mcp
yarn install
yarn setup # -> config.json
yarn index
yarn build # compiles to dist/Los scripts de yarn se corresponden uno a uno con los subcomandos (setup→init, serve, index, search, scaffold). Al generar un proyecto a partir de un checkout del código fuente, pasa --local: escribe node /abs/path/dist/server.js en los archivos generados en lugar de un comando npx que resolvería al paquete publicado en lugar de tu árbol de trabajo.
Los comandos
docs-rag init interactive wizard, writes config.json
docs-rag scaffold <dir> give a project its own scoped instance
docs-rag index build or update the index
docs-rag search "question" query the index from the terminal
docs-rag serve run the MCP server on stdio
docs-rag hook Claude Code hook entry point (auto re-index)Todos los comandos aceptan --config <path>.
Prueba la recuperación desde la terminal
docs-rag search "how do we handle authentication"Verás las secciones coincidentes, cada una con el motivo por el que se devolvió:
[semantic 0.712] reference/auth.md › Auth > How the client refreshes the token
[both 0.688] decisions/2026-01-session-length.md › Session length [2026-01-14]
[exact match] reference/errors.md › Error codes > ERR_TOKEN_EXPIREDEsta es exactamente la recuperación que usará el cliente MCP — verifícala aquí primero.
Para omitir carpetas en una sola consulta, pasa --exclude (fragmentos de ruta separados por comas, sin distinguir mayúsculas):
docs-rag search "how do we handle auth" --exclude archive,draftsLa herramienta MCP expone lo mismo como un array opcional exclude en search_notes, de modo que puedes pedir «busca, pero ignora la carpeta de archivo» durante la conversación.
Algunas carpetas (archive, plans por defecto — consulta defaultExclude) se omiten en todas las consultas, no solo cuando pasas --exclude. Para buscarlas de todos modos en una consulta, pasa --all (CLI) o searchAll: true (el parámetro de la herramienta). El mismo flag vuelve a incluir los documentos marcados como superseded/archived en el frontmatter, que también están ocultos en la búsqueda predeterminada.
Dos vías de recuperación, y las etiquetas
Los embeddings densos son débiles precisamente en las cosas de las que está llena una especificación: acrónimos, códigos de error, nombres de funciones, números de versión. Así que cada consulta ejecuta dos vías y las fusiona.
La vía semántica clasifica cada fragmento por similitud coseno y aplica los umbrales.
La vía léxica es una búsqueda de texto completo (FTS5) restringida a los términos raros de tu consulta. «Raro» se mide contra tu propio índice: un término califica si aparece en como máximo
max(5, lexicalMaxDocFreq × total chunks)fragmentos, y en no más de la mitad de elos. Ejecutar FTS sobre cada palabra inundaría los result ados con coincidencias de términos comunes; la puerta de rareza es lo que la mantiene precisa.
La etiqueta en cada result ado te dice qué vía lo puso ahí:
label | meaning |
| encontrado por significado, puntuación coseno |
| encontrado por ambas vías — la señal más fuerte |
| solo léxico. Sin puntuación a propósito: el valor del coseno no es la razón de que este resultado esté aquí, e imprimirlo sugeriría lo contrario |
Los result ados solo léxicos tienen un tope (2 huecos) y siempre van después de los semánticos, de modo que una coincidencia de término raro puede añadir a la respuesta pero nunca dominarla.
Modelos de embedding
Cualquier modelo de embeddings disponible en Ollama funciona — establece embedModel en la configuración. bge-m3 es el predeterminado y los umbrales vienen ajustados para él.
Cambiar embedModel ahora obliga a una reconstrucción completa. El índice registra qué modelo lo construyó; abrirlo con uno diferente se rechaza en lugar de puntuarse silenciosamente contra vectores incompatibles. Versiones anteriores los habrían mezclado en silencio y devuelto resultados incorrectos sin ningún error.
Algunos modelos necesitan un prefijo de tarea en la entrada (nomic-embed-text quiere search_query: / search_document:). Esos viven en un pequeño registro, que se aplica de forma automática. Si eliges un modelo que el registro no conoce, docs-rag index lo dice — la búsqueda sigue funcionando, pero nadie ha verific ado la convención de prefijo ni los umbrales para ese modelo.
El informe de calibración
Al final de cada ejecución del índice obtienes una línea como esta:
Calibration: background noise p99 = 0.421 over 500 random pairs -> suggested minScore 0.45 (in use: 0.55, from the model registry).Toma muestras de pares de fragmentos aleatorios de tu propio corpus, que por definición no están relacionados, e informa de la puntuación de similitud que alcanzan de todos modos. Ese es el piso de ruido del modelo: cualquier cosa que puntúe por debajo es indistinguible de dos documentos que no tienen nada que ver entre sí.
Úsalo como límite inferior, no como un valor a copiar. Si el valor sugerido está muy por encima de tu minScore configurado, tu umbral está admitiendo ruido. Si está muy por debajo, eres libre de ser más estricto. Un valor configurado siempre gana — el informe nunca sobrescribe tu elección, solo te dice lo que midió.
Una instancia por proyecto (recomendado)
Normalmente querrás una base de conocimiento separada por proyecto. No necesitas una copia de esta herramienta por proyecto — instálala una vez, y luego dale a cada proyecto su propia configuración, registrada a nivel de proyecto:
docs-rag scaffold /path/to/some-project # asks a few questions (or pass flags)Para ese proyecto escribe:
some-project/.rag/config.json— su configuración (vaultPathes la raíz del proyecto; el índice ateriza en.rag/index.db, junto a ella). Volver a ejecutar scaffold sobre un proyecto ya scaffoldeado cons erva este archivo: lospathBoostsajustados y los umbrales sobreviven, y solo se sobreescriben las claves que pasaste explícitamente como flags en esa ejecución.some-project/.mcp.json— un registro MCP a nivel de proyecto. Los servidores existentes en él se conservan. Como el comando no lleva ninguna ruta específica de la máquina, este archivo se puede commitear: quien clone el repositorio obtiene búsqueda sobre sus documentos sin instalar nada manualmente.añade
.rag/index.db*y el legado.rag/index.json*al.gitignoredel proyecto.con
--hook:some-project/.claude/settings.local.json, un hook de Claude Code que reindiza en segundo plano después de cada edición de markdown (ver más abajo).
Luego:
docs-rag index --config /path/to/some-project/.rag/config.jsonNo interactivo, automatizable en muchos repositorios:
docs-rag scaffold /path/to/proj --name proj-docs --include "**/docs/**/*.md" \
--desc "What's in this project's docs" --hook --yesCómo se resuelve --config
Todos los comandos aceptan --config. Un archivo de configuración «lleva» su propio índice (un indexPath relativo se resuelve junto al archivo de configuración), así que las instancias nunca interfieren. Orden de resolución:
Una ruta absoluta siempre prevalece.
CLAUDE_PROJECT_DIR, que Claude Code establece como raíz del proyecto en el entorno de los servidores y hooks que lanza.Subir desde el directorio de trabajo hasta el primer ancestro donde exista la ruta. Esto es lo que hace que
--config .rag/config.jsonfuncione en clientes que no establecen ninguna variable de entorno propia.De lo contrario, el directorio de trabajo.
Sin --config en absoluto, la misma subida busca .rag/config.json, por lo
que ejecutar docs-rag search "…" en cualquier lugar dentro de un proyecto
generado con scaffold funciona sin más.
No escriba ${CLAUDE_PROJECT_DIR} en los args de .mcp.json usted mismo:
Claude Code no expande variables ahí, por lo que se pasaría literalmente.
Integración en un cliente MCP
Claude Code
El ámbito de proyecto es lo que configura docs-rag scaffold. Para una base de
conocimiento que quiera en todas las sesiones en su lugar, regístrela en el
ámbito de usuario:
claude mcp add work-docs -s user -- npx -y docs-rag-mcp serve --config ~/vaults/work.jsonCompruébe que está conectado con claude mcp list y luego pregunte algo como
"search_notes: ¿por qué elegimos X?".
Codex CLI, Cursor, Zed y otros clientes MCP
El servidor es MCP stdio simple, por lo que cualquier cliente que hable el
protocolo puede ejecutarlo. En el ~/.codex/config.toml de Codex CLI:
[mcp_servers.docs-search]
command = "npx"
args = ["-y", "docs-rag-mcp", "serve", "--config", "/absolute/path/to/.rag/config.json"]Cursor y Zed usan el mismo comando y los mismos args en sus propios ajustes de MCP.
La forma de usarlo aquí es un --config absoluto, porque no depende de
nada. Uno relativo también funciona mediante la subida por directorios descrita
anteriormente — pero esa ruta está verificada por construcción y pruebas
unitarias, no probada contra esos clientes. Si lo ejecuta en alguno de
ellos, se agradece un informe.
Asigne a cada instancia su propio serverName y toolDescription: la
descripción es lo que el modelo lee para decidir si llamar siquiera a la
herramienta, por lo que describa qué hay en esta base de conocimiento, no lo
que hace la herramienta.
Mantener el índice al día
El índice es un artefacto de compilación: index.db, una base de datos SQLite
en modo WAL, con sus archivos satélite -wal y -shm. Después de editar
documentos, vuelva a ejecutar docs-rag index — es incremental, solo vuelve a
incrustar los archivos cuyo mtime ha cambiado. Un servidor MCP en ejecución
detecta cada reindexado automáticamente, sin necesidad de reiniciar. Un índice
obsoleto responde con contenido obsoleto — peor que no encontrar nada.
Reindexado automático (solo Claude Code, opt-in)
Si se hace scaffold con --hook, un PostToolUse hook
de Claude Code vuelve a ejecutar el indexador incremental en segundo plano cada
vez que Claude escribe o edita un archivo markdown en ese proyecto — combinado
para que se ejecute como máximo una vez cada 30 segundos, sin dejar ninguna
edición atrás. Las ediciones realizadas fuera de Claude Code aún necesitan una
ejecución manual.
El hook se encuentra en el .claude/settings.local.json del proyecto (personal,
no se incluye en el repositorio); elimine la entrada PostToolUse para
desactivarlo. Si una ejecución en segundo plano no puede funcionar — casi
siempre porque Ollama no está en ejecución — recibe un aviso por episodio en la
sesión, no uno por guardado, y los detalles van a parar a .rag/hook.log.
Otros clientes MCP no ejecutan los hooks de Claude Code: en esos casos, reindice
manualmente.
Actualización de un índice existente
El primer docs-rag index después de actualizar es una reincrustación
completa de una sola vez, no la habitual operación nula de menos de un
segundo: el esquema ha ganado la tabla de identidad del modelo y la tabla FTS5,
y los vectores antiguos no se pueden transferir. Con el hook activado, comienza
en segundo plano en su primera edición, así que espere que esa primera
ejecución tarde minutos en lugar de un segundo.
Si se actualiza desde el index.json original: se renombra a index.json.bak
y se reconstruye desde cero. Elimine el .bak una vez que esté conforme con el
resultado.
Referencia de configuración
Campo | Por defecto | Notas |
| — (obligatorio) | carpeta a indexar; absoluta o relativa al archivo de configuración |
|
| |
|
| |
|
| |
|
| cualquier modelo de embedding de Ollama; cambiarlo fuerza una reconstrucción |
|
| relativo al archivo de configuación; SQLite (modo WAL) |
|
| número de resultados por defecto |
|
| nombre del servidor MCP que se muestra en el cliente |
| genérica | indique al modelo qué contiene esta base de conocimiento |
Ajuste de la recuperación
Cinco campos opcionales controlan lo que se devuelve (por defecto entre paréntesis):
defaultExclude(["archive", "plans"]) — subcadenas de ruta que se omiten en cada consulta. Quienes llaman pueden volver a incluirlas por consulta consearchAll: true(CLI:--all); la mismabandera vuelve a incluir los document os marcados comosuperseded/archiveden el frontmatter.minScore(0.55) — umbral mínino absoluto de coseno. Por debajo de él, los result ados se descartan y la herramienta responde "No relevant results" con la mejor puntuación rechazada, en lugar de devolver ruido. Consulte el informe de calibración anterior para saber cómo elegirlo.relativeCutoff(0.88) — descarta los resultados que puntúan por debajo de esta fracción de la mejor puntuación que queda.lexicalMaxDocFreq(0.01) — cuán raro debe ser un término de la consulta para activar la vía léxica: califica si aparece en como máximomax(5, ratio × total chunks)fragmentos, y nunca si aparece en más de la mitad del corpus. Súbalo para dejar pasar más términos (más coincidencias exactas, más ruido); bájelo para reservar la vía para identificadores realmente inusuales.pathBoosts({}) — multiplicadores que solo afectan al orden, por subcadenas de ruta, p.e.j.{"reference/": 1.15, "decisions/": 1.1}para preferir documentos curados sobre notas. Los boosts nunca anulan los umbrales ni cambian la puntuación reportada.
minScore y relativeCutoff vienen ajustados para bge-m3. Con otro modelo,
ejecute docs-rag index y lea la línea de calibración antes de confiar en los
valores por defecto.
Cómo funciona
Aproximadamente 2.000 líneas de TypeScript, sin magia de compilación, sin framework. La parte de recuperación propiamente dicha — segmentación, incrustación, ranking — son unas 500 de ellas, y se puede leer de principio a fin.
src/chunk.ts— divide el markdown por encabezados, conserva el rastro de encabezados, saca las cercas de código del texto incrustado y divide las secciones demasiado grandes.src/frontmatter.ts— analizastatus/date/superseded_byy los elimina del texto indexado.src/embed.ts+src/models.ts— el/api/embedde Ollama, más el registro de prefijos por modelo.src/store.ts— esquema SQLite, versionado, identidad del modelo, espejo FTS5.src/index-docs.ts— recorre la bóveda, segmenta, incrusta y escribe de forma incrimental.src/calibrate.ts— la medición del suelo de ruido detrás delminScoresugerido.src/lexical.ts— el filtro de rareza: qué términos de la consulta merecen una búsqueda exacta.src/search.ts— incrusta la consulta, ordena por coseno, aplica umbrales, excluye y aplica los boosts, fusiona la vía léxica.src/server.ts— el servidor MCP que exponesearch_notessobre stdio.src/scaffold.ts— generador de instancias por proyecto.src/hook.ts— el hook de reindexado automático (debounce, bloqueo, avisos de fallo).src/cli.ts— el ejecutabledocs-rag; una tabla de subcomandos y nada más.
Deliberadamente fuera del alcance
La fusión de rangos recíprocos (RRF) para las dos vías. RRF descarta la puntuación absoluta, y la puntuación absoluta es sobre la que se construye
minScore— el umbral del «no lo sé». Mantener separadas las vías mantiene intacta esa promesa.Varias bóvedas en una sola instancia (use en su lugar varias configuraciones).
Proveedores de embedding conectables — solo Ollama, para mantener la promesa de que todo sea local.
Una interfaz de usuario para humanos, colaboración, síntesis multdocumento. Esto existe para que el agente que trabaja en su código conozca sus decisiones, no como un lugar donde su equipo lea documentación.
Licencia
MIT — consulte LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables managing and searching markdown notes with semantic search, question answering, and note generation, and provides an MCP server for GitHub Copilot integration.4
- AlicenseAqualityDmaintenanceTurns a local folder of notes and documents into a searchable knowledge base for AI assistants via MCP, enabling semantic search, reading, and adding notes entirely on-device.49MIT
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to list, search, read, and append to Markdown notes through MCP tool calls, making it easy to interact with a second brain folder.
- AlicenseNot gradedqualityBmaintenanceEnables Claude Code to search and retrieve from a local knowledge base of markdown notes using hybrid semantic+keyword search, keeping data entirely offline.13MIT
Related MCP Connectors
Serve a folder of Markdown notes as an MCP server: hybrid search, reading, and sourced answers.
Search, read, and write your Apple Notes from ChatGPT/Claude via a local Mac agent + MCP relay.
Search and reason over your Obsidian-style Markdown vault, right from ChatGPT.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/andreaselmi/docs-rag-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server