MCP Hub
Back to servers

Markdown Vault MCP

Markdown vault MCP server with FTS5 + semantic search and frontmatter indexing

Registry
Stars
1
Forks
1
Updated
Mar 18, 2026
Validated
May 4, 2026

Quick Install

uvx markdown-vault-mcp

markdown-vault-mcp

CI codecov PyPI Python License Docker Docs llms.txt Ask DeepWiki

A generic markdown collection MCP server with FTS5 full-text search, semantic vector search, frontmatter-aware indexing, incremental reindexing, and non-markdown attachment support.

Documentation | PyPI | Docker

Point it at a directory of Markdown files (an Obsidian vault, a docs folder, a Zettelkasten) and it exposes search, read, write, and edit tools over the Model Context Protocol.

Features

  • Full-text search — SQLite FTS5 with BM25 scoring, porter stemming
  • Semantic search — cosine similarity over embedding vectors (FastEmbed, Ollama, or OpenAI)
  • Hybrid search — Reciprocal Rank Fusion combining FTS5 and vector results
  • Frontmatter-aware — indexes YAML frontmatter fields, supports required field enforcement
  • Incremental reindexing — hash-based change detection, only re-processes modified files
  • Write operations — create, edit, delete, rename documents with automatic index updates
  • Attachment support — read, write, delete, and list non-markdown files (PDFs, images, etc.)
  • Git integration — optional auto-commit and push on every write via GIT_ASKPASS
  • OIDC authentication — optional token-based auth for HTTP deployments (Authelia, Keycloak, etc.)
  • MCP tools — 13 tools including search, read, write, edit, delete, rename, and admin operations
  • MCP resources — 6 resources exposing vault configuration, statistics, tags, folders, and document outlines
  • MCP prompts — 6 prompt templates including template-driven note creation

Installation

From PyPI

pip install markdown-vault-mcp

With optional dependencies:

pip install markdown-vault-mcp[mcp]            # FastMCP server
pip install markdown-vault-mcp[embeddings-api]  # Ollama/OpenAI embeddings via HTTP
pip install markdown-vault-mcp[embeddings]      # FastEmbed local embeddings
pip install markdown-vault-mcp[all]             # MCP + FastEmbed + API embeddings

From source

git clone https://github.com/pvliesdonk/markdown-vault-mcp.git
cd markdown-vault-mcp
pip install -e ".[all,dev]"

Docker

docker pull ghcr.io/pvliesdonk/markdown-vault-mcp:latest

The Docker image uses [all] (MCP + FastEmbed + API embeddings). By default, semantic search works locally with FastEmbed and can switch to Ollama/OpenAI when configured.

Quick Start

As a library

from pathlib import Path
from markdown_vault_mcp import Collection

collection = Collection(source_dir=Path("/path/to/vault"))
results = collection.search("query text", limit=10)

As an MCP server

export MARKDOWN_VAULT_MCP_SOURCE_DIR=/path/to/vault
markdown-vault-mcp serve

With Docker Compose

  1. Copy an example env file:

    cp examples/obsidian-readonly.env .env
    
  2. Edit .env to set MARKDOWN_VAULT_MCP_SOURCE_DIR to the absolute path of your vault on the host.

  3. Start the service:

    docker compose up -d
    
  4. Check the logs:

    docker compose logs -f markdown-vault-mcp
    

Example env files

FileDescription
examples/obsidian-readonly.envObsidian vault, read-only, Ollama embeddings
examples/obsidian-readwrite.envObsidian vault, read-write with git auto-commit
examples/obsidian-oidc.envObsidian vault, read-only, OIDC authentication (Authelia)
examples/ifcraftcorpus.envStrict frontmatter enforcement, read-only corpus

For reverse proxy (Traefik) and deployment setup, see docs/deployment.md.

Configuration

All configuration is via environment variables with the MARKDOWN_VAULT_MCP_ prefix (except embedding provider settings, which use their own conventions).

Core

VariableDefaultRequiredDescription
MARKDOWN_VAULT_MCP_SOURCE_DIRYesPath to the markdown vault directory
MARKDOWN_VAULT_MCP_READ_ONLYtrueNoSet to false to enable write operations
MARKDOWN_VAULT_MCP_INDEX_PATHin-memoryNoPath to the SQLite FTS5 index file; set for persistence across restarts
MARKDOWN_VAULT_MCP_EMBEDDINGS_PATHdisabledNoPath to the numpy embeddings file; required to enable semantic search
MARKDOWN_VAULT_MCP_STATE_PATH{SOURCE_DIR}/.markdown_vault_mcp/state.jsonNoPath to the change-tracking state file
MARKDOWN_VAULT_MCP_INDEXED_FIELDSNoComma-separated frontmatter fields to promote to the tag index for structured filtering
MARKDOWN_VAULT_MCP_REQUIRED_FIELDSNoComma-separated frontmatter fields required on every document; documents missing any are excluded from the index
MARKDOWN_VAULT_MCP_EXCLUDENoComma-separated glob patterns to exclude from scanning (e.g. .obsidian/**,.trash/**)
MARKDOWN_VAULT_MCP_TEMPLATES_FOLDER_templatesNoRelative folder path where note templates live (used by the create_from_template prompt)
MARKDOWN_VAULT_MCP_PROMPTS_FOLDERNoPath to a directory of .md prompt files that extend or override built-in prompts (see User-defined prompts)

Server identity

VariableDefaultDescription
MARKDOWN_VAULT_MCP_SERVER_NAMEmarkdown-vault-mcpMCP server name shown to clients; useful for multi-instance setups
MARKDOWN_VAULT_MCP_INSTRUCTIONS(auto)System-level instructions injected into LLM context; defaults to a description that reflects read-only vs read-write state
MARKDOWN_VAULT_MCP_HTTP_PATH/mcpHTTP endpoint path for streamable HTTP transport (used by serve --transport http)
MARKDOWN_VAULT_MCP_LOG_LEVELINFOLog level: DEBUG, INFO, WARNING, or ERROR. CLI -v flag overrides to DEBUG.

Search and embeddings

VariableDefaultDescription
EMBEDDING_PROVIDERauto-detectEmbedding provider: openai, ollama, or fastembed (not MARKDOWN_VAULT_MCP_-prefixed)
OLLAMA_HOSThttp://localhost:11434Ollama server URL (not MARKDOWN_VAULT_MCP_-prefixed)
OPENAI_API_KEYOpenAI API key for the OpenAI embedding provider (not MARKDOWN_VAULT_MCP_-prefixed)
MARKDOWN_VAULT_MCP_OLLAMA_MODELnomic-embed-textOllama embedding model name
MARKDOWN_VAULT_MCP_OLLAMA_CPU_ONLYfalseForce Ollama to use CPU only
MARKDOWN_VAULT_MCP_FASTEMBED_MODELnomic-ai/nomic-embed-text-v1.5FastEmbed model name
MARKDOWN_VAULT_MCP_FASTEMBED_CACHE_DIRFastEmbed defaultFastEmbed model cache directory (in Docker, stored under /data/state/fastembed)

Git integration

Git integration has three modes:

  • Managed mode (MARKDOWN_VAULT_MCP_GIT_REPO_URL set): server owns repo setup. On startup it clones into SOURCE_DIR when empty, or validates existing origin. Pull loop + auto-commit + deferred push are enabled.
  • Unmanaged / commit-only mode (no GIT_REPO_URL): writes are committed to a local git repo if SOURCE_DIR is already a git checkout. No pull, no push.
  • No-git mode: if SOURCE_DIR is not a git repo, git callbacks are no-ops.

When token auth is used (MARKDOWN_VAULT_MCP_GIT_TOKEN), remotes must be HTTPS. SSH remotes (for example git@github.com:owner/repo.git) are rejected with a startup error. Fix with: git -C /path/to/vault remote set-url origin https://github.com/owner/repo.git

Backward compatibility: MARKDOWN_VAULT_MCP_GIT_TOKEN without GIT_REPO_URL still works (legacy mode) but logs a deprecation warning.

VariableDefaultDescription
MARKDOWN_VAULT_MCP_GIT_REPO_URLHTTPS remote URL for managed mode; enables clone/remote validation on startup
MARKDOWN_VAULT_MCP_GIT_USERNAMEx-access-tokenUsername for HTTPS auth prompts (x-access-token for GitHub, oauth2 for GitLab, account name for Bitbucket)
MARKDOWN_VAULT_MCP_GIT_TOKENToken/password for HTTPS auth (GIT_ASKPASS)
MARKDOWN_VAULT_MCP_GIT_PULL_INTERVAL_S600Seconds between git fetch + ff-only update attempts; 0 disables periodic pull
MARKDOWN_VAULT_MCP_GIT_PUSH_DELAY_S30Seconds of write-idle time before pushing; 0 = push only on shutdown
MARKDOWN_VAULT_MCP_GIT_COMMIT_NAMEmarkdown-vault-mcpGit committer name for auto-commits; set this in Docker where git config user.name is empty
MARKDOWN_VAULT_MCP_GIT_COMMIT_EMAILnoreply@markdown-vault-mcpGit committer email for auto-commits
MARKDOWN_VAULT_MCP_GIT_LFStrueEnable Git LFS — runs git lfs pull on startup to fetch LFS-tracked attachments (PDFs, images). Set to false for repos without LFS.

Attachments

Non-markdown file support. See Attachments for details.

VariableDefaultDescription
MARKDOWN_VAULT_MCP_ATTACHMENT_EXTENSIONS(built-in list)Comma-separated allowed extensions without dot (e.g. pdf,png,jpg); use * to allow all non-.md files
MARKDOWN_VAULT_MCP_MAX_ATTACHMENT_SIZE_MB10.0Maximum attachment size in MB for reads and writes; 0 disables the limit

Bearer token authentication

Simple static token auth for HTTP deployments. Set a single env var — clients must send Authorization: Bearer <token>.

VariableRequiredDescription
MARKDOWN_VAULT_MCP_BEARER_TOKENYesStatic bearer token; any non-empty string enables auth

OIDC authentication

Full OAuth 2.1 authentication for HTTP deployments. OIDC activates when all four required variables are set. See Authentication for setup details.

Multi-auth: If both BEARER_TOKEN and all OIDC variables are set, the server accepts either credential — a valid bearer token or a valid OIDC session. This is useful when different clients use different auth flows (e.g. Claude web via OIDC and Claude Code via bearer token).

VariableRequiredDescription
MARKDOWN_VAULT_MCP_BASE_URLYesPublic base URL of the server (e.g. https://mcp.example.com; include prefix if mounted under subpath, e.g. https://mcp.example.com/vault)
MARKDOWN_VAULT_MCP_OIDC_CONFIG_URLYesOIDC discovery endpoint (e.g. https://auth.example.com/.well-known/openid-configuration)
MARKDOWN_VAULT_MCP_OIDC_CLIENT_IDYesOIDC client ID registered with your provider
MARKDOWN_VAULT_MCP_OIDC_CLIENT_SECRETYesOIDC client secret
MARKDOWN_VAULT_MCP_OIDC_JWT_SIGNING_KEYNoJWT signing key; required on Linux/Docker — the default is ephemeral and invalidates tokens on restart. Generate with openssl rand -hex 32
MARKDOWN_VAULT_MCP_OIDC_AUDIENCENoExpected JWT audience claim; leave unset if your provider does not set one
MARKDOWN_VAULT_MCP_OIDC_REQUIRED_SCOPESNoComma-separated required scopes; default openid
MARKDOWN_VAULT_MCP_OIDC_VERIFY_ACCESS_TOKENNoSet true to verify the upstream access token as a JWT instead of the id token. Only needed when your provider issues JWT access tokens and you require audience-claim validation on that token. Default: verify the id token (works with all providers, including opaque-token issuers like Authelia)

CLI Reference

markdown-vault-mcp <command> [options]

serve

Start the MCP server.

markdown-vault-mcp serve [--transport {stdio|sse|http}] [--host HOST] [--port PORT] [--path PATH]
FlagDefaultDescription
--transportstdioMCP transport: stdio (stdin/stdout, default), sse (Server-Sent Events), http (streamable-HTTP). Use http for Docker with a reverse proxy or when OIDC is enabled.
--host0.0.0.0Bind host for the http transport (ignored for stdio and sse)
--port8000Port for the http transport (ignored for stdio and sse)
--pathenv MARKDOWN_VAULT_MCP_HTTP_PATH or /mcpMCP HTTP path for http transport; useful for reverse-proxy subpath mounting (e.g. /vault/mcp)

Reverse Proxy Subpath Mounts

By default, HTTP transport serves MCP on /mcp. You can run it under a subpath:

markdown-vault-mcp serve --transport http --path /vault/mcp

Equivalent env-based config:

MARKDOWN_VAULT_MCP_HTTP_PATH=/vault/mcp

For reverse proxies, you can either:

  • Keep app path at /mcp and use proxy rewrite/strip-prefix middleware.
  • Set app path directly to the public path (/vault/mcp) and route without rewrite.

When OIDC is enabled under a subpath, the configuration is different: the subpath goes in BASE_URL only, and HTTP_PATH stays at /mcp. See OIDC subpath deployments.

Then your redirect URI is:

https://mcp.example.com/vault/auth/callback

index

Build the full-text search index.

markdown-vault-mcp index [--source-dir PATH] [--index-path PATH] [--force]

search

Search the collection from the CLI.

markdown-vault-mcp search <query> [-n LIMIT] [-m {keyword|semantic|hybrid}] [--folder PATH] [--json]

reindex

Incrementally reindex the vault (only processes changed files).

markdown-vault-mcp reindex [--source-dir PATH] [--index-path PATH]

MCP Tools

ToolDescription
searchHybrid full-text + semantic search with optional frontmatter filters
readRead a document or attachment by relative path
writeCreate or overwrite a document or attachment
editReplace a unique text span in a document (notes only)
deleteDelete a document or attachment and its index entries
renameRename/move a document or attachment, updating all index entries; pass update_links=true to also rewrite backlinks in other notes
list_documentsList indexed documents; pass include_attachments=true to also list non-markdown files
list_foldersList all folder paths in the vault
list_tagsList all unique frontmatter tag values
reindexForce a full reindex of the vault
statsGet collection statistics (document count, chunk count, link health metrics, etc.)
build_embeddingsBuild or rebuild vector embeddings for semantic search
embeddings_statusCheck embedding provider and index status
get_backlinksFind all documents that link to a given document
get_outlinksFind all links from a document, with existence check
get_broken_linksFind all links pointing to non-existent documents
get_similarFind semantically similar notes by document path
get_recentGet the most recently modified notes
get_contextGet a consolidated context dossier for a note (backlinks, outlinks, similar, folder peers, tags, modified time)
get_orphan_notesFind all notes with no inbound or outbound links
get_most_linkedFind the most-linked-to notes ranked by backlink count
get_connection_pathFind the shortest path between two notes via BFS on the undirected link graph (max 10 hops)

Write tools (write, edit, delete, rename) are only available when MARKDOWN_VAULT_MCP_READ_ONLY=false.

Resources

MCP resources expose vault metadata as structured JSON that clients can read directly without invoking tools.

URIDescription
config://vaultCurrent collection configuration (source dir, indexed fields, read-only state, etc.)
stats://vaultCollection statistics (document count, chunk count, embedding count, etc.)
tags://vaultAll frontmatter tag values grouped by indexed field
tags://vault/{field}Tag values for a specific indexed frontmatter field (template)
folders://vaultAll folder paths in the vault
toc://vault/{path}Table of contents (heading outline) for a specific document (template)
similar://vault/{path}Top 10 semantically similar notes for a document (template)
recent://vault20 most recently modified notes with ISO timestamps

Prompts

Prompt templates guide the LLM through multi-step workflows using the vault tools.

PromptParametersDescription
summarizepathRead a document and produce a structured summary with key themes and takeaways
researchtopicSearch for a topic, synthesize findings, and create a new note at research/{topic}.md
discusspathAnalyze a document and suggest improvements using edit (not write)
create_from_templatetemplate_name (optional)Discover templates (if needed), read a template, gather user values, and write a new note
relatedpathFind related notes via search and suggest cross-references as markdown links
comparepath1, path2Read two documents and produce a side-by-side comparison

Write prompts (research, discuss, create_from_template) are only available when MARKDOWN_VAULT_MCP_READ_ONLY=false.

Templates are regular markdown files. If placeholder template text pollutes search results, add your templates folder to MARKDOWN_VAULT_MCP_EXCLUDE (for example _templates/**).

User-defined prompts

Mount a directory of .md prompt files to override or extend the built-in prompts. Set MARKDOWN_VAULT_MCP_PROMPTS_FOLDER to the path. Each file's frontmatter defines description, arguments (a list of objects, each with name, description, and required fields), and optional tags. A user prompt with the same name as a built-in replaces it.

For a complete example — including Zettelkasten capture, development, and review prompts — see the Zettelkasten guide.

Attachments

In addition to Markdown notes, the server can read, write, delete, rename, and list non-markdown files (PDFs, images, spreadsheets, etc.). All existing tools are overloaded — no new tool names.

How it works

Path dispatch is extension-based: a path ending in .md is treated as a note; any other path is treated as an attachment if the extension is in the allowlist. The kind field on returned objects distinguishes the two: "note" or "attachment".

Reading attachments

read returns base64-encoded content for binary attachments:

{
  "path": "assets/diagram.pdf",
  "mime_type": "application/pdf",
  "size_bytes": 12345,
  "content_base64": "<base64 string>",
  "modified_at": 1741564800.0
}

Writing attachments

write accepts a content_base64 parameter for binary content:

{ "path": "assets/diagram.pdf", "content_base64": "<base64 string>" }

Listing attachments

list_documents with include_attachments=true returns both notes and attachments:

[
  { "path": "notes/intro.md", "kind": "note", "title": "Intro", "folder": "notes", "frontmatter": {}, "modified_at": 1741564800.0 },
  { "path": "assets/diagram.pdf", "kind": "attachment", "folder": "assets", "mime_type": "application/pdf", "size_bytes": 12345, "modified_at": 1741564800.0 }
]

Default allowed extensions

pdf, docx, xlsx, pptx, odt, ods, odp, png, jpg, jpeg, gif, webp, svg, bmp, tiff, zip, tar, gz, mp3, mp4, wav, ogg, txt, csv, tsv, json, yaml, toml, xml, html, css, js, ts

Override with MARKDOWN_VAULT_MCP_ATTACHMENT_EXTENSIONS. Use * to allow all non-.md files.

Hidden directories: Attachments inside hidden directories (.git/, .obsidian/, .markdown_vault_mcp/, etc.) are never listed, regardless of extension settings. MARKDOWN_VAULT_MCP_EXCLUDE patterns are also applied to attachments.

Authentication

The server supports four auth modes:

  1. Multi-auth — both bearer token and OIDC configured; either credential accepted (e.g. Claude web via OIDC + Claude Code via bearer token on the same instance)
  2. Bearer token — set MARKDOWN_VAULT_MCP_BEARER_TOKEN to a secret string
  3. OIDC — full OAuth 2.1 flow via OIDC_CONFIG_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, and BASE_URL
  4. No auth — server accepts all connections (default)

Auth requires --transport http (or sse). It has no effect with --transport stdio.

For setup instructions, troubleshooting, and provider-specific guides, see the Authentication guide.

Development

git clone https://github.com/pvliesdonk/markdown-vault-mcp.git
cd markdown-vault-mcp
uv pip install -e ".[all,dev]"

# Run tests
uv run python -m pytest tests/ -x -q

# Lint and format
ruff check src/ tests/
ruff format src/ tests/

# Type check
mypy src/

License

MIT

Reviews

No reviews yet

Sign in to write a review