Skip to main content
Glama

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: superseded en 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

status/superseded_by, archivos excluidos por defecto, no-resultados honestos

zilliztech/claude-context

híbrido BM25+denso, fragmentación por AST, Milvus

no

shinpr/mcp-local-rag

semántico + palabras claves, LanceDB, PDF/DOCX/MD

no

Zackriya/MCP-Markdown-RAG

markdown, fragmentación por encabezados, Milvus

no

proofgeist/obsidian-notes-rag

sqlite-vec + Ollama, consciente del grafo

no

patakuti/local-knowledge-rag-mcp

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 index

Si tienes previsto usar el hook de reindexado automático, instálalo globalmente en su lugar:

npm i -g docs-rag-mcp

npx 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 (setupinit, 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_EXPIRED

Esta 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,drafts

La 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

[semantic 0.712]

encontrado por significado, puntuación coseno 0.712

[both 0.712]

encontrado por ambas vías — la señal más fuerte

[exact match]

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 (vaultPath es 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: los pathBoosts ajustados 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 .gitignore del 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.json

No 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 --yes

Có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:

  1. Una ruta absoluta siempre prevalece.

  2. CLAUDE_PROJECT_DIR, que Claude Code establece como raíz del proyecto en el entorno de los servidores y hooks que lanza.

  3. Subir desde el directorio de trabajo hasta el primer ancestro donde exista la ruta. Esto es lo que hace que --config .rag/config.json funcione en clientes que no establecen ninguna variable de entorno propia.

  4. 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.json

Comprué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

vaultPath

— (obligatorio)

carpeta a indexar; absoluta o relativa al archivo de configuración

includeGlobs

["**/*.md"]

excludeGlobs

["**/node_modules/**"]

ollamaUrl

http://localhost:11434

embedModel

bge-m3

cualquier modelo de embedding de Ollama; cambiarlo fuerza una reconstrucción

indexPath

index.db

relativo al archivo de configuación; SQLite (modo WAL)

topK

8

número de resultados por defecto

serverName

docs-search

nombre del servidor MCP que se muestra en el cliente

toolDescription

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 con searchAll: true (CLI: --all); la mismabandera vuelve a incluir los document os marcados como superseded/archived en 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áximo max(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 — analiza status / date / superseded_by y los elimina del texto indexado.

  • src/embed.ts + src/models.ts — el /api/embed de 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 del minScore sugerido.

  • 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 expone search_notes sobre 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 ejecutable docs-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.

A
license - permissive license
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables managing and searching markdown notes with semantic search, question answering, and note generation, and provides an MCP server for GitHub Copilot integration.
    4
  • A
    license
    A
    quality
    D
    maintenance
    Turns 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.
    4
    9
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables 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.

View all related MCP servers

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.

View all MCP Connectors

Latest Blog Posts

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