Give any local Markdown folder a semantic-search MCP server. Runs entirely offline.
Turn ~/Desktop/sales/, ~/Desktop/notes/, or any directory full of Markdown files into a searchable knowledge base that Claude Code, Cursor, Cline, and other MCP clients can query with natural-language questions.
- ๐ Multi-vault โ one MCP server manages many doc folders, each a separate "vault"
- ๐ฆ Self-contained โ each vault's vector DB lives inside the folder (
.mdrag/), move it anywhere - โก Incremental indexing โ only re-embed files whose
mtimechanged - ๐ Auto-reindex on save โ
mdrag servewatches every registered vault withwatchdog, 1.5s debounce; new/edited/deleted/moved.mdfiles are picked up with no manual reindex, no cron - ๐
.mdragignoreโ gitignore-style file at the vault root excludes drafts, archives, or whole directories from the index
- โ๏ธ Chunk-level retrieval โ long docs are split by headings (sliding-window fallback at 600 chars / 80 overlap) so mid-doc content stays findable; each doc also gets an "overview" chunk for broad queries
- ๐ Hybrid search โ dense vector retrieval fused with BM25 keyword matching via best-rank fusion, so specific terms and semantic intent both get through
- ๐ฏ Rare-term boost โ queries containing digit strings (e.g. "38 ็งๅญๆฎต") switch to a BM25-priority fusion so exact-match lookups aren't buried by vector results
- ๐ Cross-lingual query expansion โ comparison-style queries ("ๅบๅซ", "ๅฏนๆฏ", "compare", "vs") get auto-expanded with bilingual synonyms before embedding, improving recall on mixed-language corpora
- ๐ง Any embedding model โ default is multilingual
paraphrase-multilingual-MiniLM-L12-v2(handles Chinese + English + 50 more); swap in anysentence-transformersmodel
- ๐ File-locking โ concurrent CLI + watcher reindexes on the same vault are serialized via
filelock, preventing LanceDB corruption - ๐ Schema versioning โ
meta.jsonin each.mdrag/dir tracks schema version and model; mismatches are caught early with an actionable error - ๐ฉบ
mdrag doctorโ one command to check everything: Python, registry, per-vault health, model cache, disk usage, PATH; paste the output into bug reports - ๐ก Watcher health in MCP โ
list_vaultsshows aโ ๏ธ if a vault's auto-reindex is failing (consecutive errors + message), instead of silently serving stale data
- ๐ Fully local โ no API keys, no cloud; embeddings run on your machine
- ๐ MCP tools โ
list_vaults,search,get_doc,list_tagsexposed to Claude Code / Cursor / Cline over stdio - ๐ก Match explainability โ each search result includes
match_reason("vector+bm25", "bm25 (rare-term)", "bm25 only", "vector only") so AI clients can explain or re-rank - ๐ Quality eval harness โ
mdrag evalcompares any set of indexes on a YAML query suite; Recall@K, MRR, per-query ranking diff - ๐ท Frontmatter-aware โ
title,tags,summaryfrom YAML frontmatter are indexed and searchable
pip install mdragRequires Python โฅ 3.10.
Let's say Bob has a folder ~/Desktop/sales/ full of meeting notes, proposals, and competitor research in Markdown.
claude mcp add mdrag --scope user -- mdrag serveThis tells Claude Code "there's an MCP server called mdrag โ launch it with mdrag serve when needed". You'll only do this once per machine.
mdrag vault add sales ~/Desktop/salesThe first time you run this, a ~100MB embedding model downloads (once), then all .md files under ~/Desktop/sales/ get indexed. A .mdrag/ subfolder is created inside sales/ to hold the vector database.
Open Claude Code in any project. Ask:
"Use the mdrag MCP to search my sales vault for the Q4 pipeline review"
Claude will call mcp__mdrag__search(vault="sales", query="Q4 pipeline review") and return the top matching documents.
No new MCP config needed โ just register another vault:
mdrag vault add marketing ~/Desktop/marketing
mdrag vault add notes ~/Documents/notesAll vaults are visible through the same MCP server. Claude calls:
mcp__mdrag__list_vaults() โ see all vaults
mcp__mdrag__search(vault="marketing", query="...")
mcp__mdrag__search(vault="notes", query="...")
mdrag serve Start the MCP stdio server
mdrag vault add NAME PATH Register a directory and index it
mdrag vault list Show all vaults
mdrag vault info NAME Show vault details
mdrag vault reindex NAME [--full] Re-index (incremental or full)
mdrag vault remove NAME [--purge] Unregister (and optionally delete .mdrag/)
mdrag search VAULT QUERY [-k N] Search a vault from the shell (debugging)
mdrag eval QUERIES INDEX_SPECS... Compare retrieval quality across indexes
mdrag searchruns the same hybrid retrieval as the MCPsearchtool โ useful for verifying a reindex or debugging without an MCP client. Add--jsonfor machine-readable output,--tags a,bto filter.
Common options:
--model MODEL_NAMEonvault addโ pick a different embedding model--no-indexonvault addโ skip initial indexing (useful when first adding, want to index later)--fullonvault reindexโ rebuild from scratch (required after changing the model)
When mdrag serve is running, these tools are available to the AI client:
| Tool | Purpose |
|---|---|
list_vaults() |
List all registered vaults with their stats |
search(vault, query, top_k=5, tags="") |
Semantic search within a vault; returns the best-matching chunk per doc with heading_path and chunk_text |
get_doc(vault, path) |
Read the full content of a document |
list_tags(vault) |
List all frontmatter tags in a vault with counts |
If your Markdown files have YAML frontmatter, mdrag will use it:
---
title: Q4 Pipeline Review
tags: [sales, forecast, 2026-q4]
summary: Overview of deals in play for Q4 2026.
---
# Q4 Pipeline Review
...titleโ used as the result title (falls back to filename)tagsโ searchable via thetagsparameter ofsearchsummaryโ shown in search results
No frontmatter? It still works โ mdrag auto-generates a preview from the file body.
| Language | Recommended model | Notes |
|---|---|---|
| Multilingual (default) | paraphrase-multilingual-MiniLM-L12-v2 |
~120MB, handles Chinese + English + 50 more |
| Chinese-only | BAAI/bge-small-zh-v1.5 |
~100MB, higher recall on pure Chinese |
| English-only | BAAI/bge-small-en-v1.5 |
~100MB, higher recall on pure English |
| Higher accuracy | BAAI/bge-base-zh-v1.5 or -en |
~400MB, noticeably slower |
Change the model when registering a vault:
mdrag vault add notes ~/Documents/notes --model BAAI/bge-small-en-v1.5After changing the model on an existing vault (edit ~/.mdrag/vaults.yaml), run a full rebuild:
mdrag vault reindex notes --full โโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
โ ~/Desktop/sales/ โ โ ~/.mdrag/ โ
โ meeting-01.md โ โ vaults.yaml โ โ registry
โ proposal.md โ โโโโโโโโโโโโโโโโโโโโโโโโ
โ .mdrag/ โ โ LanceDB vector store (per-vault)
โ docs.lance/ โ
โโโโโโโโโโโโฌโโโโโโโโโโ
โ
โ mdrag serve
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ FastMCP stdio server โ
โ tools: โ
โ search / get_doc / โ
โ list_vaults / โ
โ list_tags โ
โโโโโโโโโโโโฌโโโโโโโโโโโโโโโโ
โ MCP protocol (stdio / JSON-RPC)
โผ
Claude Code / Cursor / Cline
- Vault registry is at
~/.mdrag/vaults.yaml - Each vault's vector database lives inside the vault directory at
.mdrag/โ self-contained, portable - Embeddings use
sentence-transformers, stored in LanceDB - MCP server is built on FastMCP
You don't have to. When mdrag serve is running (i.e. Claude Code / Cursor are connected), it watches every registered vault and auto-reindexes on save. A short debounce batches rapid edits.
If serve isn't running, run manual incremental:
mdrag vault reindex salesOnly files with changed mtime are re-embedded.
Put a .mdragignore file at the root of your vault, using gitignore syntax:
# Example: drafts, archives, big log exports
drafts/
archive/**
**/sales-log-*.md
Takes effect on the next index run (auto-watch picks up the change too).
Not directly โ mdrag only indexes .md. This is by design: conversion is a messy, format-specific
problem, and keeping the core focused on Markdown keeps the index predictable.
Use the companion tool mdpack to convert a directory of mixed-format docs to clean Markdown, then point mdrag at the output:
pip install mdpack
brew install pandoc # needed for DOCX
mdpack convert ~/Desktop/reports # writes ~/Desktop/reports/converted/
mdrag vault add reports ~/Desktop/reports/convertedmdpack mirrors the source directory, injects source/converter/converted_at frontmatter so
mdrag can trace results back to the original file, and strips inline base64 images (which would
otherwise inflate .md files to multi-MB and break chunking). Supports .docx, .xlsx, .csv
today; PDF / PPTX / HTML in the 0.2 roadmap.
For one-off conversions without installing mdpack, pandoc still works:
pandoc meeting.docx -o docs/meeting.md --wrap=noneIf you're in China, set a HuggingFace mirror:
export HF_ENDPOINT=https://hf-mirror.com
mdrag vault add sales ~/Desktop/sales- Vault registry:
~/.mdrag/vaults.yaml - Each vault's vectors:
<vault_path>/.mdrag/docs.lance/
Yes โ the .mdrag/ folder is self-contained. Sync the whole vault directory (via Dropbox, rsync, git-lfs, whatever) and mdrag vault add <name> <path> on the other machine. No re-indexing needed as long as the embedding model matches.
claude mcp add mdrag --scope user -- mdrag serveOr manually in ~/.mcp.json:
{
"mcpServers": {
"mdrag": {
"command": "mdrag",
"args": ["serve"]
}
}
}Add the same stdio command to your client's MCP configuration. The command is mdrag serve โ it communicates over stdio following the MCP protocol.
git clone https://github.com/andyleimc-source/mdrag
cd mdrag
python -m venv .venv
.venv/bin/pip install -e .[dev]
.venv/bin/pytestTry the example vault shipped in the repo:
mdrag vault add demo ./examples/sample-vault
mdrag vault listMIT โ do whatever you want with it.