
Point this at any folder of Markdown files and you get a 30-tool MCP server that turns your notes into queryable context. It combines SQLite FTS5 full-text search with semantic vector search (FastEmbed, Ollama, or OpenAI), handles YAML frontmatter indexing, and exposes read, write, edit, delete, and rename operations with automatic incremental reindexing. The adaptive chunking splits long sections at progressively deeper heading levels until they fit a configurable word budget, and search returns sentence-scale snippets by default to keep context costs bounded. Ships with optional Git auto-commit, OIDC auth for HTTP deployments, and 6 prompt templates including one that scans recent notes and proposes missing wikilinks. Useful for Obsidian vaults, Zettelkasten collections, or any documentation folder you want Claude to search and modify in conversation.
Generic markdown vault MCP with hybrid search
Documentation | Config wizard | PyPI | Docker
Give Claude, or any MCP client, a folder of Markdown notes to search, read and write. An Obsidian vault works as it is.
read fetches the whole section. See Embeddings.write, edit, append, delete, rename, move_folder, fetch, git_sync, the okf_* tools, create_upload_link) are on by default and hidden when MARKDOWN_VAULT_MCP_READ_ONLY=true. Replacing a file takes the etag from reading it, and per-folder _conventions.md rules reach the client as it writes.MARKDOWN_VAULT_MCP_REPORT_UNRESOLVED_LINKS=true, write, edit and append name each link they added that doesn't resolve.The tools reference lists every tool. The same engine is a Python library: see the Vault API.
What the server can reach, what it changes and who gets in is set out in the security model; the block below says who it serves and where it stops.
It suits one person or a small team who keep notes as Markdown files and want Claude to search them, follow their links and write back into them, on their own machine or as a shared server. It reaches the vault folder, plus only what you configure: a git remote, an embedding or summarizing model, and the URLs a fetch call names.
What it assumes:
MARKDOWN_VAULT_MCP_SERVER_NAME.MARKDOWN_VAULT_MCP_INDEX_PATH, the index and that file stay in memory and are rebuilt at each start.Reach for something else for a corpus of hundreds of thousands of chunks (a vector database behind a retrieval pipeline), for mostly scanned or office documents (a document management system with text recognition), or for many users who must not see each other's notes (a multi-tenant knowledge platform): every caller the server admits gets every tool it exposes.
Pick the client you use. Each line installs the released version; the Get started tutorials carry on from there.
Claude Desktop. Download the .mcpb bundle from the releases page and open it with Claude Desktop (or Settings › Extensions › Advanced settings › Install Extension…). Claude Desktop asks for the required settings itself. Tutorial.
Claude Code. Two commands inside Claude Code; the second asks for a scope. Tutorial.
/plugin marketplace add pvliesdonk/claude-plugins
/plugin install markdown-vault-mcp@pvliesdonk
A client that runs a command (stdio). To register it in Claude Code, see the Claude Code tutorial.
uv tool install "markdown-vault-mcp"
markdown-vault-mcp serve
A server for remote clients (streamable HTTP). A remote server connects the clients.
docker run --rm -p 8000:8000 --env-file .env ghcr.io/pvliesdonk/markdown-vault-mcp:latest
A compose.yml ships at the repository root and runs as-is: copy .env.example to .env, then docker compose up -d. Deploy covers authentication, OIDC, a reverse proxy and system packages (.deb/.rpm on the releases page). The server answers /health and /health/ready outside the MCP mount, and its get_server_info tool reports the running version.
The plain package covers keyword search, the write tools and git. Search by meaning, the file watcher and the summarize tool need extras; [all] installs every one:
uv tool install "markdown-vault-mcp[all]"
The Docker image, the .mcpb bundle and the Claude Code plugin already include them. Installation lists each extra.
Everything is configured through environment variables with the MARKDOWN_VAULT_MCP_ prefix. The ones most installs set:
| Variable | Default | Required | Description |
|---|---|---|---|
MARKDOWN_VAULT_MCP_SOURCE_DIR | /data/vault | No | Path to the markdown vault directory. When it does not exist, or the server cannot access it, the server starts but every tool fails with a message saying which until it is fixed (managed git mode clones into it). Symbolic links inside the vault are followed on Python 3.13+. |
MARKDOWN_VAULT_MCP_READ_ONLY | false | No | Set to true to hide the write tools (write, edit, append, delete, rename, move_folder, fetch, git_sync, the okf_* tools, create_upload_link) and serve a search-only vault. |
MARKDOWN_VAULT_MCP_WRITE_PROTECT_EXISTING | true | No | Refuse a write that would overwrite an existing file when no if_match etag is supplied. Deliberate replacement still works: read the file first, then pass if_match. Unaffected: edit, append, delete, rename. Set to false to allow blind overwrites. |
MARKDOWN_VAULT_MCP_REPORT_UNRESOLVED_LINKS | false | No | Set to true to have write, edit and append list, under unresolved_links, the target of each link the call added that get_broken_links reports. Links the note already held are left out. Off by default, leaving those responses unchanged. |
MARKDOWN_VAULT_MCP_DEFAULT_SEARCH_MODE | auto | No | Mode used when a search call omits 'mode': auto, keyword, semantic, or hybrid. The default 'auto' picks hybrid when embeddings are configured and keyword when they are not. Pin 'keyword' to keep unqualified searches off the embedding provider (each hybrid or semantic search embeds the query, which costs an API call on a metered provider). A configured semantic/hybrid default also degrades to keyword without embeddings, so no setting can make a vault unsearchable; an explicit mode= argument is never downgraded. |
MARKDOWN_VAULT_MCP_EMBEDDING_PROVIDER | (none) | No | Embedding provider: openai, voyage, ollama, or fastembed; setting it turns on search by meaning. Unset auto-detects from the environment (never voyage), but only when EMBEDDINGS_PATH is set. Any OpenAI-compatible endpoint works with openai plus OPENAI_BASE_URL; see the embeddings guide. |
MARKDOWN_VAULT_MCP_GIT_REPO_URL | (none) | No | HTTPS remote URL for managed git mode: the server clones into an empty SOURCE_DIR on startup (or validates an existing origin) and enables the pull loop, auto-commit, and deferred push. |
MARKDOWN_VAULT_MCP_FILE_WATCHER | true | No | Watch the vault for external filesystem changes; auto-disabled when git pull is active or a webhook can deliver (HTTP/SSE transports only). Requires the file-watcher extra. |
MARKDOWN_VAULT_MCP_SUMMARIZE_OPENAI_BASE_URL | (none) | No | OpenAI-compatible endpoint base URL for the summarize tool; setting it enables the tool even without an API key. The bare OPENAI_BASE_URL routes traffic only when a key already enables the feature. |
Every variable the server reads, the shared ones included, is in the configuration reference; .env.example lists the same surface in copy-paste form, and the config wizard writes one for your deployment.
CONTRIBUTING.md and SECURITY.md at the root..md extension; frontmatter is optional by default (REQUIRED_FIELDS opts into enforcement).asyncio.to_thread(). File writes return after saving, while index updates run in the background. Index-dependent mutations wait for prior writes; see index freshness.INDEX_SEMANTICS_VERSION so deployed vaults rebuild themselves once on upgrade.The full decision log lives in the design document.
MARKDOWN_VAULT_MCP_SOURCE_DIR*Absolute path to the markdown vault directory
MARKDOWN_VAULT_MCP_READ_ONLYdefault: trueDisable write tools
FASTMCP_LOG_LEVELdefault: INFOLog level for FastMCP internals; app loggers default to INFO, -v overrides both to DEBUG
MARKDOWN_VAULT_MCP_EVENT_STORE_URLdefault: file:///data/state/eventsEvent store backend for HTTP session persistence (file:///path or memory://)
MARKDOWN_VAULT_MCP_SERVER_NAMEdefault: markdown-vault-mcpMCP server name shown to clients
MARKDOWN_VAULT_MCP_STATE_PATHDirectory for index and embeddings state files
MARKDOWN_VAULT_MCP_INDEX_PATHPath to the FTS5 SQLite index file
MARKDOWN_VAULT_MCP_EMBEDDINGS_PATHPath to the numpy embeddings file
MARKDOWN_VAULT_MCP_INDEXED_FIELDSComma-separated frontmatter fields to index for search
MARKDOWN_VAULT_MCP_REQUIRED_FIELDSComma-separated frontmatter fields required on every document
MARKDOWN_VAULT_MCP_EXCLUDEComma-separated glob patterns to exclude from indexing
MARKDOWN_VAULT_MCP_EMBEDDING_PROVIDEREmbedding provider to use
OPENAI_API_KEYsecretOpenAI API key (required when MARKDOWN_VAULT_MCP_EMBEDDING_PROVIDER=openai)
MARKDOWN_VAULT_MCP_OLLAMA_MODELdefault: nomic-embed-textOllama embedding model name
MARKDOWN_VAULT_MCP_OLLAMA_CPU_ONLYdefault: falseForce CPU-only inference for Ollama
OLLAMA_HOSTdefault: http://localhost:11434Ollama server base URL
MARKDOWN_VAULT_MCP_GIT_TOKENsecretGit authentication token for push/pull
MARKDOWN_VAULT_MCP_GIT_REPO_URLRemote git repository URL for managed mode
MARKDOWN_VAULT_MCP_GIT_USERNAMEdefault: x-access-tokenGit username for token auth
MARKDOWN_VAULT_MCP_GIT_COMMIT_NAMEdefault: markdown-vault-mcpGit committer name
MARKDOWN_VAULT_MCP_GIT_COMMIT_EMAILdefault: noreply@markdown-vault-mcpGit committer email
MARKDOWN_VAULT_MCP_GIT_PUSH_DELAY_Sdefault: 30Seconds to wait before pushing (batches writes)
MARKDOWN_VAULT_MCP_GIT_LFSdefault: trueEnable Git LFS support
MARKDOWN_VAULT_MCP_GIT_PULL_INTERVAL_Sdefault: 600Seconds between periodic git pulls (0 to disable)