vault-mcp
Turn any folder into a searchable knowledge base for AI, exposed via MCP.
README
vault-mcp
Turn any folder into a searchable knowledge base for AI, exposed via MCP.
Point it at a directory. It embeds everything — markdown, PDFs, images, audio, video. Search it from Claude Desktop, Claude Code, or any MCP client.
Why this exists
Existing RAG tools either want to be the source of truth (duplicating your files into their own store) or only handle markdown. vault-mcp treats your folder as the single source of truth. The embedding index is derived state — delete it and rebuild from scratch in seconds. Git handles versioning. Your AI assistant modifies files directly. vault-mcp just makes them searchable.
Built for personal knowledge vaults, project documentation, research archives — anywhere you have a folder of mixed-format files and want semantic search over all of it.
Features
- Multi-format extraction — Markdown, PDF, DOCX, PPTX, images (OCR), audio/video (Whisper transcription)
- Heading-aware markdown chunking — Splits by headings, preserves breadcrumb context (
# Strategy > ## Vision > ### North Star), merges small sections, splits large ones - Chunk-level deduplication — Only re-embeds chunks that actually changed. Edit one paragraph in a 40KB doc? One API call, not 80
- File rename detection — Moved a file? Zero re-embedding. Matched by content hash
- 3-tier skip hierarchy — mtime unchanged → skip entirely. Content unchanged → skip extraction. Chunk unchanged → skip embedding
- Live watching — Watchdog-based file monitor with 2s debounce. Changes appear in search within seconds
- Remote MCP transport — Streamable HTTP (default), SSE, or stdio. Connect from anywhere
- SQLite + sqlite-vec — Single-file database, no external services. ~50MB RAM footprint
Quickstart
pip install vault-mcp
Set your OpenAI API key (used for text-embedding-3-small embeddings):
export OPENAI_API_KEY=sk-...
Index a folder and start the server:
export VAULT_PATH=/path/to/your/folder
# First-time index
vault-mcp reindex
# Start MCP server (streamable-http on port 8100)
vault-mcp serve --watch
Connect from Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"vault": {
"url": "http://localhost:8100/mcp"
}
}
}
For a remote server with authentication (see Remote Deployment):
{
"mcpServers": {
"vault": {
"type": "http",
"url": "https://your-server/mcp",
"headers": {
"Authorization": "Bearer YOUR_TOKEN"
}
}
}
}
Connect from Claude Code
Add to .mcp.json or settings:
{
"mcpServers": {
"vault": {
"url": "http://your-server:8100/mcp"
}
}
}
Connect via stdio (local only)
{
"mcpServers": {
"vault": {
"command": "vault-mcp",
"args": ["serve", "--transport", "stdio", "--watch"],
"env": {
"VAULT_PATH": "/path/to/your/folder",
"OPENAI_API_KEY": "sk-..."
}
}
}
}
MCP Tools
| Tool | Purpose |
|---|---|
search |
Semantic search over the vault |
reindex |
Re-embed changed files |
stats |
Index statistics |
write |
Write a file and optionally git commit |
Webhooks
vault-mcp can receive webhooks from external services and write captures directly to the vault.
| Endpoint | Method | Description |
|---|---|---|
/webhook/voicenotes |
POST | Receive VoiceNotes transcriptions |
VoiceNotes webhook
Handles recording.created, recording.updated, and recording.deleted events. Writes transcriptions to captures/inbox/YYYY/MM/YYYY-MM-DD-{slug}.md with frontmatter. The file watcher auto-indexes new files within seconds.
Auth: Set WEBHOOK_SECRET env var. Pass as query param: /webhook/voicenotes?secret=xxx (VoiceNotes doesn't support custom headers). If WEBHOOK_SECRET is unset, all requests are accepted.
Example:
curl -X POST 'http://localhost:8100/webhook/voicenotes?secret=xxx' \
-H 'Content-Type: application/json' \
-d '{"event":"recording.created","timestamp":"2026-03-01T10:00:00Z","data":{"id":"abc-123","title":"Morning thoughts","transcript":"Need to ship the onboarding flow this week."}}'
search(query, top_k?, path_filter?)
Semantic search over your vault.
query: "north star vision"
top_k: 5
path_filter: "strategy/" # optional — scope to subdirectory
Returns matching chunks with text, file path, heading breadcrumb, and relevance score.
reindex(path?)
Re-embed changed files. Without a path, indexes the full vault (skipping unchanged files). With a path, force-reindexes that file or directory.
stats()
Returns indexed file count, chunk count, last index time, vault path, and embedding model.
write(path, content, commit_message?)
Write a file to the vault and optionally git commit. The caller handles classification and frontmatter — vault-mcp just writes bytes and commits.
path: "captures/saturn/2026/03/ai-architecture-insight.md"
content: "---\nplanet: saturn\nsource: slack\n---\n\nBreakthrough on embedding context..."
commit_message: "capture(saturn): AI architecture insight from Slack" # optional
Returns { status, path, relative_path, committed?, commit_hash? }. Path traversal outside the vault is rejected. No git push — that's handled separately.
CLI
# Index the vault (only changed files)
vault-mcp reindex
# Force re-embed everything
vault-mcp reindex --force
# Index a specific subdirectory
vault-mcp reindex captures/
# Start MCP server
vault-mcp serve # streamable-http on 0.0.0.0:8100
vault-mcp serve --transport stdio # stdio for local MCP
vault-mcp serve --port 9000 # custom port
vault-mcp serve --watch # watch for file changes
# Check index stats
vault-mcp stats
How it works
Your Folder (git-backed)
│
├── .md, .pdf, .docx, .png, .mp4, ...
│
▼
Extractor
│ Markdown → passthrough
│ PDF/DOCX → Kreuzberg
│ Images → Kreuzberg OCR (Tesseract)
│ Audio/Video → OpenAI Whisper
│
▼
Chunker
│ Markdown → heading-aware splitting (512 token chunks)
│ Everything else → paragraph grouping
│ Overlap: last 1-2 sentences between chunks
│
▼
Embedder
│ OpenAI text-embedding-3-small (1536 dims)
│ Batched, with retry/backoff
│ Chunk-level dedup via SHA-256 hash
│
▼
SQLite + sqlite-vec
│ chunks: text, embedding, file_path, heading_path
│ files: path, mtime, content_hash
│
▼
MCP Server (FastMCP)
search / reindex / stats
Remote Deployment
vault-mcp has no built-in authentication. For remote access, run it behind nginx with bearer token auth:
- Bind to localhost only and use streamable-http transport (the default):
vault-mcp serve --watch --host 127.0.0.1 --port 8100
- Add nginx reverse proxy with bearer token validation:
server {
listen 443 ssl;
server_name vault-mcp.example.com;
# SSL certs (e.g. Let's Encrypt)
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
if ($http_authorization != "Bearer YOUR_SECRET_TOKEN") {
return 401 "Unauthorized";
}
proxy_pass http://127.0.0.1:8100;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
- Optional: systemd user service for auto-restart:
# ~/.config/systemd/user/vault-mcp.service
[Unit]
Description=vault-mcp
After=network.target
[Service]
Type=simple
WorkingDirectory=/path/to/vault-mcp
Environment=OPENAI_API_KEY=sk-...
Environment=VAULT_PATH=/path/to/your/vault
ExecStart=/path/to/vault-mcp serve --watch --host 127.0.0.1 --port 8100
Restart=on-failure
[Install]
WantedBy=default.target
systemctl --user enable --now vault-mcp
Configuration
| Environment Variable | Default | Description |
|---|---|---|
OPENAI_API_KEY |
(required) | OpenAI API key for embeddings |
VAULT_PATH |
. (current dir) |
Path to the folder to index |
VAULT_DB_PATH |
~/.vault-mcp/index.db |
SQLite database location |
WEBHOOK_SECRET |
(optional) | Shared secret for webhook auth (query param) |
Supported Formats
| Format | Method | Notes |
|---|---|---|
.md, .txt, .yaml, .json, .toml |
Direct read | Markdown gets heading-aware chunking |
.pdf, .docx, .pptx, .xlsx, .odt, .rtf, .epub, .html |
Kreuzberg | Async extraction |
.png, .jpg, .gif, .bmp, .tiff, .webp |
Kreuzberg OCR | Tesseract backend |
.mp3, .mp4, .m4a, .wav, .ogg, .flac, .webm, .avi, .mkv, .mov |
OpenAI Whisper | Time-windowed chunks |
Dependencies
- FastMCP — MCP server framework
- Kreuzberg — Document/image text extraction
- OpenAI — Embeddings + Whisper transcription
- sqlite-vec — Vector search in SQLite
- watchdog — Filesystem monitoring
- tiktoken — Token counting
License
MIT
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
Neon Database
MCP server for interacting with Neon Management API and databases
E2B
Using MCP to run code via e2b.
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.