mcp-markdown-vault
mcp-markdown-vault is a headless MCP server for reading, writing, searching, and managing markdown vaults (Obsidian, Logseq, Dendron, Foam, or any folder of .md files) — no app or API keys required.
Vault Management — Full CRUD on notes (list, read, create, update, delete, stat), plus scaffold new notes from templates with {{variable}} placeholder injection.
Surgical Editing — AST-based append/prepend/replace targeting specific headings or block IDs with fuzzy matching, freeform line-range and string replacements, YAML frontmatter updates, batch edits (up to 50 operations), and dry-run diff previews.
Search & Retrieval — Hybrid semantic search (vector + lexical, zero-setup local embeddings with optional Ollama), global keyword search, single-file fragment retrieval (TF-IDF + proximity), bulk read of multiple files/sections, heading-scoped section reads, note outline view, frontmatter reads, and backlinks (wikilinks + markdown links with line context) — all optionally scoped to a directory.
Workflow Tracking — Petri net state machine with status checks, transition firing, history view, and reset, plus contextual LLM hints for agent workflows.
System Administration — Server health/status, full vault reindexing, and structural vault overview (folder tree, file counts, modification dates).
Transport & Security — Dual transport modes (stdio for single client, SSE over HTTP for multi-client/Docker), path traversal protection, atomic file writes, and safe path validation.
Provides Docker container deployment option with multi-arch support via GitHub Container Registry, enabling SSE transport mode for multi-client setups and containerized vault operations.
Enables MCP server capabilities for Logseq graphs, offering markdown file operations, semantic search, fragment retrieval, and surgical editing for Logseq's knowledge base structure.
Provides headless semantic MCP server functionality for Obsidian vaults, enabling direct read/write operations, surgical AST-based editing, semantic search, and workflow tracking without requiring the Obsidian app or plugins.
Supports optional integration with Ollama for higher-quality embeddings in semantic search functionality, using models like nomic-embed-text for improved vector search results.
📁 Markdown Vault MCP Server
Headless semantic MCP server for Obsidian, Logseq, Dendron, Foam, and any folder of markdown files.
npm install and point it at a folder. Hybrid search, AST editing, zero-config embeddings. No app, no plugins, no API keys.

💡 Why this server?
TL;DR — One
npxcommand. No running app. No plugins. No vector DB. Semantic search works out of the box.
Differentiator | Details | |
🚫 | No app or plugins required | Most Obsidian MCP servers (mcp-obsidian, obsidian-mcp-server) need Obsidian running with the Local REST API plugin. This server reads and writes |
🧠 | Built-in semantic search, zero setup | Hybrid search: cosine-similarity vectors + TF-IDF + word proximity. Local embeddings ( |
🔬 | Surgical AST-based editing |
|
🔓 | Tool-agnostic | Obsidian vaults, Logseq graphs, Dendron workspaces, Foam, or any plain folder of |
📦 | Single package, no infrastructure | Unlike Python alternatives that need ChromaDB or other vector stores, everything runs in one Node.js process. |
💎 Obsidian · 📓 Logseq · 🌳 Dendron · 🫧 Foam · 📂 Any .md folder
✨ Features
Feature | Description | |
🗂️ | Headless vault ops | Read, create, update, edit, delete |
📑 | Read by heading | Read a single section by heading title — returns only content under that heading (up to the next same-level heading), saving context window space |
📦 | Bulk read | Read multiple files and/or heading-scoped sections in a single call — reduces MCP round-trips with per-item fault tolerance |
🔬 | Surgical editing | AST-based patching targets specific headings or block IDs — never overwrites the whole file |
🔍 | Fragment retrieval | Heading-aware chunking + TF-IDF + proximity scoring returns only relevant sections |
📂 | Scoped search | Optional directory filter for |
🧠 | Semantic search | Hybrid vector + lexical search with background auto-indexing |
⚡ | Zero-setup embeddings | Built-in local embeddings via |
🔄 | Workflow tracking | Petri net state machine with contextual LLM hints |
🌐 | Dual transport | Stdio (single client) or SSE over HTTP (multi-client, Docker-friendly) |
✏️ | Freeform editing | Line-range replacement and string find/replace as AST fallback |
🏷️ | Frontmatter management | AST-based read and update of YAML frontmatter — safely manage tags, statuses, and metadata without corrupting file structure |
👀 | Dry-run / diff preview | Preview any edit operation as a unified diff without saving — set |
📝 | Templating / scaffolding | Create new notes from template files with |
🗺️ | Vault overview | Structural map of the vault — total file count, recursive folder tree with file counts and last modification dates per folder |
📦 | Batch edit | Apply multiple edit operations in a single call — sequential execution, stops on first error, supports |
🔗 | Backlinks index | Find all notes linking to a given path — supports wikilinks and markdown links with line numbers and context snippets |
🎯 | Typo resilience | Levenshtein-based fuzzy matching for edit operations |
🛠️ MCP Tools
Tool | Actions | Description |
📁 vault |
| Full CRUD for vault notes + template scaffolding |
✏️ edit |
| AST-based patching + freeform fallback + frontmatter update + batch edit (supports |
👁️ view |
| Fragment retrieval, cross-vault search, hybrid semantic search, read by heading, frontmatter read, bulk read, backlinks |
🔄 workflow |
| Petri net state machine control |
⚙️ system |
| Server health, indexing info, vault structure overview |
All tool responses include contextual hints based on the current workflow state.
🚀 Quick Start
Prerequisites
📦 Install from NPM
npm install -g @wirux/mcp-markdown-vaultThen run directly:
VAULT_PATH=/path/to/your/vault markdown-vault-mcp🔌 MCP Client Configuration
Add to your MCP client config (e.g. Claude Desktop, Claude Code):
{
"mcpServers": {
"markdown-vault": {
"command": "npx",
"args": ["-y", "@wirux/mcp-markdown-vault"],
"env": {
"VAULT_PATH": "/path/to/your/vault"
}
}
}
}
npx -yauto-installs the package if not already present — no global install needed.
Try it in the browser: You can test this server directly at Glama Inspector — no local install required.
🐳 Docker
Pull the pre-built multi-arch image from GitHub Container Registry:
docker pull ghcr.io/wirux/mcp-markdown-vault:latestOr use Docker Compose:
docker compose upEdit docker-compose.yml to point at your markdown vault directory. The default compose file uses SSE transport on port 3000.
🛠️ Development (from source)
git clone https://github.com/wirux/mcp-markdown-vault.git
cd mcp-markdown-vault
npm install
npm run build
VAULT_PATH=/path/to/your/vault node dist/index.js🌐 Transport Modes
Mode | Use case | How it works |
📡 | Single-client desktop apps (Claude Desktop) | Reads/writes stdin/stdout; 1:1 connection |
🌊 | Multi-client setups (Docker, Claude Code) | HTTP server with SSE streams; one connection per client |
SSE starts an HTTP server on PORT (default 3000):
GET /sse— establishes an SSE stream (one per client)POST /messages?sessionId=...— receives JSON-RPC messages
MCP_TRANSPORT_TYPE=sse PORT=3000 VAULT_PATH=/path/to/vault npx @wirux/mcp-markdown-vaultEach SSE client gets its own workflow state. Shared resources (vault, vector index, embedder) are reused across all connections.
🧠 Embedding Providers
The server selects an embedding provider automatically:
| Ollama reachable? | Provider used |
❌ No | — | 🏠 Local ( |
✅ Yes | ✅ Yes | 🦙 Ollama ( |
✅ Yes | ❌ No | 🏠 Local (fallback with warning) |
No configuration needed for local embeddings — the model downloads on first use and is cached automatically.
⚙️ Configuration
Variable | Default | Description |
|
| Markdown vault directory |
|
| Vault orientation mode: |
| (deprecated) | Deprecated and ignored. Use |
|
|
|
|
| HTTP port (SSE mode only) |
| (unset) | Set to enable Ollama embeddings |
|
| Ollama embedding model name |
|
| Ollama embedding vector dimensions |
| (unset) | Set to use Qdrant (e.g. |
|
| Qdrant collection name when |
|
| Set to |
| (unset) | Bearer token for SSE transport auth. If set, all SSE endpoints require |
|
| Bind address for the SSE HTTP server. |
|
| Max JSON request body size for SSE |
Note: When using the default local vector store, a
.markdown_vault_mcpdirectory will be created in your vault. It's recommended to add this directory to your.gitignore.
🏗️ Architecture
Clean Architecture with strict layer separation:
src/
├── domain/ 🔷 Errors, interfaces (ports), value objects
├── use-cases/ 🔶 Business logic (AST, chunking, search, workflow)
├── infrastructure/ 🟢 Adapters (file system, Ollama, vector store)
└── presentation/ 🟣 MCP tool bindings, transport layer (stdio/SSE)See CLAUDE.md for detailed architecture docs and CHANGELOG.md for implementation history.
Self-Orienting Context Layer
When an MCP client connects, the server automatically provides vault context so connected agents can decide when to query this vault and how to use its tools — without explicit user instructions.
Making agents find your vault
In auto mode (the default), the server generates meta/overview.md automatically after startup using structural heuristics: top directories, file counts, tag frequency, and H1 headings. It also writes a one-line vault_scope field in the frontmatter, which is the host-visible description used for MCP handshake instructions, tool descriptions, and first-call priming.
In manual mode, you author meta/overview.md yourself. Set VAULT_CONTEXT_MODE=manual, edit the vault_scope frontmatter for the one-line host-visible description, and use the markdown body for richer narrative context:
{
"mcpServers": {
"markdown-vault": {
"command": "npx",
"args": ["-y", "@wirux/mcp-markdown-vault"],
"env": {
"VAULT_PATH": "/path/to/your/vault"
}
}
}
}In both modes, meta/overview.md is the canonical source of vault description for connected agents: vault_scope in frontmatter supplies the short description, and the markdown body feeds the richer vault://overview resource.
How context is delivered
The server uses four complementary mechanisms so behavior degrades gracefully across clients with different MCP feature support:
Mechanism | When | What the agent sees |
| MCP handshake (session start) | The current |
MCP Resources | On-demand via |
|
First-call priming | First |
|
| Tool listing | Includes the current |
Even if a client supports none of these (rare), the agent still discovers vault content through normal tool use.
Two files: contract vs overview
On first startup, the server creates two files in <VAULT_PATH>/meta/. In manual mode, both are created once and never overwritten — they are fully yours to edit. In auto mode, meta/overview.md is regenerated automatically after indexing completes and after every 5 meaningful file changes; its vault_scope frontmatter is refreshed at the same time. meta/contract.md is still created once and never overwritten.
meta/contract.md — Tool optimization (power users)
Tells agents how to use the vault's tools efficiently. Pre-filled with sensible defaults:
Section | Purpose | Drives which tool |
| YAML keys and types used across notes |
|
| Tag naming rules and hierarchies |
|
| Which search action fits which query type | All |
| File naming patterns |
|
| Default structure for new notes |
|
Most users don't need to edit this. Power users can customize it to match their vault's specific conventions — agents read it as part of the vault://overview resource.
Tip: Keep
vault_scopeshort and specific — it is the one-line summary exposed to MCP hosts. Put richer, multi-line context in the body ofmeta/overview.md.
meta/overview.md — Vault description (read-side)
In auto mode: auto-generated after startup and refreshed after every 5 meaningful file changes. The frontmatter contains vault_scope, a generated one-line summary capped to a fixed maximum length for host-visible context. The body contains top directories, file counts, tag frequency, and recent note titles.
In manual mode: created as an editable stub. Set vault_scope in frontmatter to control the one-line host-visible description, then write whatever helps an agent understand your vault's content in the body: active projects, key topic areas, organizational philosophy.
Its frontmatter vault_scope is the primary source for handshake-visible one-line context, and its body is the primary source for the vault://overview resource that agents read on demand.
Hot reload behavior
What changed | Effect | Restart needed? |
| Reflected in | No |
| Reflected in | No |
Auto-generated | Picked up automatically when auto mode rewrites | No |
Manual edits to | Reflected in | Usually yes |
| Changes auto vs manual behavior | Yes |
Migration notes
On first run after upgrade, meta/contract.md and meta/overview.md are auto-created if missing. Existing files are never overwritten. No breaking changes to tool APIs.
If you have a meta/contract.md from other tooling with a different schema, review compatibility — the server-generated contract is dedicated to mcp-markdown-vault navigation hints.
🚢 CI/CD & Release
Fully automated via GitHub Actions and Semantic Release:
Workflow | Trigger | What it does |
PR Check | Pull request to | Lint → Build → Test |
Release | Push to | Lint → Test → Semantic Release (NPM + GitHub Release) → Docker build & push to |
Versioning follows Conventional Commits —
feat:= minor,fix:= patch,feat!:/BREAKING CHANGE:= majorDocker images are built for
linux/amd64andlinux/arm64via QEMUNPM package published as
@wirux/mcp-markdown-vaultDocker image available at
ghcr.io/wirux/mcp-markdown-vault
🧪 Testing
568 tests across 49 files, written test-first (TDD).
npm test # Run all tests
npx vitest run src/use-cases/ast-patcher.test.ts # Single file
npm run test:watch # Watch mode
npm run test:coverage # Coverage reportTests use real temp directories for file system operations and in-memory MCP transport for integration tests. No external services required.
🔒 Security
🛡️ All file paths validated through
SafePathvalue object before any I/O🚫 Blocks path traversal:
../, URL-encoded (%2e%2e), double-encoded (%252e), backslash, null bytes✍️ Atomic file writes (temp file + rename) prevent partial writes
👤 Docker container runs as non-root user
📄 License
Maintenance
Appeared in Searches
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/wirux/mcp-markdown-vault'
If you have feedback or need assistance with the MCP directory API, please join our Discord server