Vault RAG MCP

Vault RAG MCP

A read-only MCP server that enables semantic search and retrieval over a local Markdown knowledge base with heading-aware chunking and multilingual embeddings.

Category
Visit Server

README

Vault RAG MCP

Local multilingual retrieval for Markdown knowledge bases, exposed through a read-only Model Context Protocol server.

Vault RAG MCP retrieving a cited policy from an allowlisted Markdown vault

Point it at the smallest folder an assistant should be allowed to read. It chunks Markdown by heading, generates embeddings locally, stores a persistent SQLite index, and gives MCP clients four narrow tools that return evidence with paths and line numbers.

No API key. No write tool. No bulk upload of the corpus.

Privacy boundary: indexing and embeddings stay local. If the MCP host uses a cloud model, the passages it retrieves can still be sent to that model. This project reduces the data exposed per answer; it does not turn a cloud LLM into a local one.

Why this exists

Giving an assistant filesystem access is easy. Giving it the minimum useful access is the engineering problem.

Vault RAG MCP separates retrieval from generation:

ALLOWLISTED MARKDOWN ROOT
          │
          ▼
 heading-aware chunker ──▶ multilingual local embeddings
          │                         │
          └──────────────▶ SQLite vector index
                                      ▲
                                      │ cosine ranking
MCP HOST ──▶ search_vault(query) ─────┘
    │                 │
    │                 └── path + heading + exact line range
    └────▶ read_vault_note(path, lines)

Every operation ──▶ private JSONL audit trail

The server never calls an LLM. Claude, another MCP host, or a local model owns answer generation; this process only retrieves source evidence.

Verified sample run

The included five-note corpus produces 14 heading-aware chunks with the default model. A real offline stdio smoke run produced:

Check Result
Protocol discovery All four tools listed and callable
Incremental refresh with no changes 5 unchanged files, 0 rewritten chunks, 2 ms
English policy query Projects/Support-Playbook.md, lines 11–13, score 0.8703
English query over a Spanish note Projects/Asistente-Universidad.md, lines 5–7, score 0.8460

Timings depend on hardware. Similarity scores rank passages; they are not probabilities or guarantees of correctness.

What it exposes

Tool Purpose Source mutation
vault_status Show index readiness and the enforced boundary None
refresh_vault_index Incrementally embed changed Markdown notes None
search_vault Rank passages semantically, optionally under a path prefix None
read_vault_note Read a bounded line range from one validated note None

Tool descriptions tell the host to treat note content as untrusted data and to cite paths and line numbers.

Quickstart

Requirements: Python 3.11–3.13 and uv. CI tests all three supported Python versions on Linux; the release smoke run was also verified locally on macOS. The project defaults to Python 3.12 in .python-version, and uv can download it automatically.

git clone https://github.com/rickquant/vault-rag-mcp
cd vault-rag-mcp
uv sync

Build the included sample index:

uv run vault-rag-mcp index --vault sample_vault

The first run downloads intfloat/multilingual-e5-small. Source notes are embedded on the same machine.

Search it:

uv run vault-rag-mcp search \
  "When should a delayed package be investigated?" \
  --vault sample_vault \
  --limit 3

Read the exact source lines returned by search:

uv run vault-rag-mcp read \
  Projects/Support-Playbook.md \
  --vault sample_vault \
  --start-line 10 \
  --end-line 14

Connect it to Claude Desktop

Use absolute paths. Claude Desktop may launch stdio servers from an undefined working directory, and GUI applications often do not inherit the same PATH as a terminal.

Open Claude Desktop → Settings → Developer → Edit Config and add:

{
  "mcpServers": {
    "vault-rag": {
      "command": "/ABSOLUTE/PATH/TO/uv",
      "args": [
        "--directory",
        "/ABSOLUTE/PATH/TO/vault-rag-mcp",
        "run",
        "vault-rag-mcp",
        "serve"
      ],
      "env": {
        "VAULT_ROOT": "/ABSOLUTE/PATH/TO/YOUR/MARKDOWN/FOLDER",
        "VAULT_MODEL_LOCAL_FILES_ONLY": "true"
      }
    }
  }
}

Fully quit and reopen Claude Desktop after changing the config.

Example prompt:

Search my vault for the decision about local embeddings. Answer only from the retrieved evidence and cite the note plus line range.

Connect it to Claude Code

claude mcp add vault-rag \
  --scope user \
  --env VAULT_ROOT=/ABSOLUTE/PATH/TO/YOUR/MARKDOWN/FOLDER \
  --env VAULT_MODEL_LOCAL_FILES_ONLY=true \
  -- /ABSOLUTE/PATH/TO/uv \
  --directory /ABSOLUTE/PATH/TO/vault-rag-mcp \
  run vault-rag-mcp serve

claude mcp get vault-rag

Configuration

Environment variable Default Meaning
VAULT_ROOT required The single allowlisted Markdown root
VAULT_EMBEDDING_MODEL intfloat/multilingual-e5-small Local Sentence Transformers model
VAULT_EMBEDDING_DEVICE cpu Torch device used for embeddings
VAULT_MODEL_LOCAL_FILES_ONLY false Refuse model-network access; enable after the model is cached
VAULT_EXCLUDE_DIRS .git,.obsidian,.trash,.venv,__pycache__,node_modules Directory names skipped recursively
VAULT_MAX_FILE_BYTES 1000000 Per-note indexing and read limit
VAULT_MAX_READ_CHARS 20000 Maximum text returned by one read
VAULT_MAX_RESULTS 10 Hard cap on search results
VAULT_INDEX_PATH OS cache directory SQLite index location
VAULT_AUDIT_PATH OS state directory JSONL audit location
VAULT_AUDIT_QUERY_TEXT false Store raw searches instead of hashes

The default index and audit paths are namespaced by a hash of the resolved vault root. The server refuses to place either file inside VAULT_ROOT.

The first model load needs network access. After that succeeds once, set VAULT_MODEL_LOCAL_FILES_ONLY=true in the MCP server environment to make model loading fail closed instead of checking the network.

Security boundary

Enforced in code:

  • One resolved root is the filesystem allowlist.
  • Only .md files are scanned or read.
  • Absolute paths, .., null bytes, non-Markdown files, oversized files, and every symlinked path are rejected.
  • Hidden and configured directories are skipped.
  • There are no create, edit, move, delete, shell, network, or arbitrary-path tools.
  • Search returns at most 10 bounded excerpts; note reads are character-capped.
  • The SQLite index and JSONL audit log are created with owner-only permissions where the operating system supports them.
  • Audit records hash query text by default.

Not guaranteed:

  • Retrieved passages may leave the machine through the MCP host's model provider.
  • This server cannot constrain other tools enabled in the same host.
  • Local processes running as the same operating-system user may read the source folder, index, or audit log.
  • Semantic retrieval can miss relevant text. Source citations make results inspectable; they do not make ranking infallible.

See docs/SECURITY.md for the threat boundaries and design decisions.

How indexing works

  1. Scan Markdown files without following symlinks.
  2. Remove YAML frontmatter from retrieval text.
  3. Split each note by its Markdown heading hierarchy, then into overlapping bounded chunks.
  4. Prefix passages and queries according to the multilingual E5 retrieval model.
  5. Normalize vectors and store them as float32 blobs in SQLite.
  6. On refresh, re-embed only files whose size or nanosecond modification time changed; remove deleted files.
  7. Rank the in-memory matrix with cosine similarity and return inspectable source metadata.

This deliberate brute-force search is appropriate for personal and small-team knowledge bases. A distributed vector database would add machinery without improving this demo's real use case.

Development

Tests inject a deterministic network-free embedder. They exercise incremental indexing, retrieval, deletion, source reads, path attacks, audit behavior, and all four tools through an in-memory MCP client/server transport.

uv sync --group dev
uv run ruff check .
uv run ruff format --check .
uv run pytest

To verify the packaged command through a real stdio subprocess after the model is cached:

uv run python scripts/smoke_stdio.py

Current scope and non-goals live in docs/SCOPE.md.

Limitations

  • Manual or tool-invoked refresh; no filesystem watcher.
  • One local user and one configured root.
  • Markdown only.
  • Stdio transport only in the portfolio release.
  • No answer generator, UI, OAuth, cloud deployment, or write operations.
  • Intended for small knowledge bases, not millions of chunks.

License

MIT

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured