mcp-markdown-vault
📁 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.
🐳 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 |
|
|
|
|
| HTTP port (SSE mode only) |
| (unset) | Set to enable Ollama embeddings |
|
| Ollama embedding model name |
|
| Ollama embedding vector dimensions |
🏗️ 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.
🚢 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
318 tests across 31 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
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