Skip to main content
Glama

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.

Related MCP server: repocks

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

A
license - permissive license
-
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.

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/rickquant/vault-rag-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server