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-obsidian.git
cd mcp-obsidian
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 |
|
| One-line description of vault scope; surfaced to connected agents via MCP instructions and tool descriptions |
|
|
|
|
| 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. |
|
| Set to |
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
The single most important thing: set VAULT_CONTEXT to describe what your vault contains.
{
"mcpServers": {
"markdown-vault": {
"command": "npx",
"args": ["-y", "@wirux/mcp-markdown-vault"],
"env": {
"VAULT_PATH": "/path/to/your/vault",
"VAULT_CONTEXT": "personal research notes on machine learning and distributed systems"
}
}
}
}This one-line description is injected into the MCP handshake, the view tool description, and resource headers. When a user asks the agent something related to "machine learning" or "distributed systems", the agent sees the scope match and queries the vault autonomously.
Default: general markdown notes vault (too generic for reliable routing — always set a specific value).
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) | Vault scope + tool dispatcher summary. Supported by Claude Desktop, Claude Code, Cursor. | |
MCP Resources | On-demand via |
|
First-call priming | First |
|
Tool listing | Includes the |
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/. Both are created once and never overwritten — they are fully yours to edit.
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 via vault://overview and vault://contract resources on each request.
Tip: Add a
## Scopesection for a richer, multi-line vault description. Agents see it when readingvault://overvieworvault://contractresources.
meta/overview.md — Vault narrative (all users)
Free-form description of what the vault currently contains. Created as an empty stub. Write here whatever helps an agent understand your vault's content: active projects, key topic areas, organizational philosophy.
If the body is non-empty, its content is appended to the vault://overview resource that agents can read on demand.
Hot reload behavior
What changed | Effect | Restart needed? |
| Reflected in | No |
| Reflected in | No |
| Reflected in | 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
516 tests across 46 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