Fast DuckDB BM25 MCP search server for Obsidian vaults
Obsidian DuckDB MCP
Fast local search for Obsidian vaults, exposed as an MCP server for Claude Code, Codex, and other MCP clients.
The vault stays the source of truth. DuckDB is only a derived, rebuildable index.
Why
Large Obsidian vaults are useful, but plain filesystem search wastes context and returns noisy snippets. This project builds a local DuckDB index with BM25 full-text search, tags, links, backlinks, metadata, and vault statistics, then exposes that index through MCP tools.
Typical result:
rgover markdown: fast enough, but noisy and token-heavy.- DuckDB BM25 MCP: ranked note-level results, fewer tokens, better recall.
Features
- Index any local Obsidian vault into DuckDB.
- BM25 full-text search over note title and body.
- Parse frontmatter tags and inline
#tags. - Parse Obsidian wikilinks and Markdown links.
- Resolve backlinks and broken links.
- Expose MCP tools:
search(query, limit)get_note(path)backlinks(path)list_tags(prefix, limit)stats()rebuild_index()
- Works over stdio, so it fits Claude Code and Codex.
- No cloud service required.
Architecture
Obsidian vault
└─ Markdown files
│
▼
DuckDB index
├─ notes
├─ note_tags
├─ note_links
├─ note_meta
└─ build_log
│
▼
MCP stdio server
├─ Claude Code
├─ Codex
└─ any MCP client
Install
Option 1: use uvx from GitHub
uvx --from git+https://github.com/gemini-it/obsidian-duckdb-mcp obsidian-duckdb-index \
--vault "$HOME/Documents/Obsidian/MyVault" \
--db "$HOME/.cache/obsidian-duckdb/vault.duckdb"
Run MCP server:
uvx --from git+https://github.com/gemini-it/obsidian-duckdb-mcp obsidian-duckdb-mcp \
--vault "$HOME/Documents/Obsidian/MyVault" \
--db "$HOME/.cache/obsidian-duckdb/vault.duckdb"
Option 2: clone locally
git clone https://github.com/gemini-it/obsidian-duckdb-mcp.git
cd obsidian-duckdb-mcp
uv sync
uv run obsidian-duckdb-index --vault "$HOME/Documents/Obsidian/MyVault" --db ./vault.duckdb
uv run obsidian-duckdb-mcp --vault "$HOME/Documents/Obsidian/MyVault" --db ./vault.duckdb
Claude Code configuration
Add this to your Claude Code MCP config.
{
"mcpServers": {
"vault": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/gemini-it/obsidian-duckdb-mcp",
"obsidian-duckdb-mcp",
"--vault",
"/absolute/path/to/ObsidianVault",
"--db",
"/absolute/path/to/vault.duckdb"
]
}
}
}
Restart Claude Code after changing MCP configuration.
Codex configuration
Add this to ~/.codex/config.toml.
[mcp_servers.vault]
command = "uvx"
args = [
"--from",
"git+https://github.com/gemini-it/obsidian-duckdb-mcp",
"obsidian-duckdb-mcp",
"--vault",
"/absolute/path/to/ObsidianVault",
"--db",
"/absolute/path/to/vault.duckdb"
]
Restart Codex after changing MCP configuration.
Environment variables
Instead of CLI flags:
export OBSIDIAN_VAULT_PATH="$HOME/Documents/Obsidian/MyVault"
export OBSIDIAN_DUCKDB_PATH="$HOME/.cache/obsidian-duckdb/vault.duckdb"
uvx --from git+https://github.com/gemini-it/obsidian-duckdb-mcp obsidian-duckdb-mcp
MCP tools
search(query, limit=10)
BM25 search over note titles and content.
{
"query": "typebot delete resultIds",
"limit": 10
}
Returns:
[
{
"path": "04 - Incidents/2026-05-18 - Typebot.md",
"title": "2026-05-18 - Typebot DELETE resultIds",
"score": 10.0639
}
]
get_note(path)
Returns one full note with tags, metadata, incoming links, outgoing links, and content.
backlinks(path)
Returns notes linking to a path.
list_tags(prefix="", limit=50)
Returns tags and usage counts.
stats()
Returns note count, tag count, link count, broken links, and last build time.
rebuild_index()
Rebuilds the DuckDB index from the configured vault path.
Reindexing
Manual:
obsidian-duckdb-index --vault "/absolute/path/to/ObsidianVault" --db "/absolute/path/to/vault.duckdb"
Cron example:
*/5 * * * * uvx --from git+https://github.com/gemini-it/obsidian-duckdb-mcp obsidian-duckdb-index --vault "$HOME/Documents/Obsidian/MyVault" --db "$HOME/.cache/obsidian-duckdb/vault.duckdb"
Systemd examples are in examples/systemd/.
Recommended assistant instruction
Use vault search first:
Before answering questions about local technical, client, or operational context, use the vault MCP search tool. Prefer search/get_note/backlinks over filesystem grep/read. Use filesystem reads only when editing files or when MCP cannot answer the question.
What this is not
- Not an Obsidian plugin.
- Not a hosted search service.
- Not a vector database.
- Not a replacement for your Markdown files.
DuckDB is a derived index. Delete it any time and rebuild.
ChromaDB?
Not in v1. BM25 plus tags and backlinks are enough for most small and medium vaults. A vector sidecar can be added later for semantic search, but should not replace DuckDB as the canonical structured index.
Development
git clone https://github.com/gemini-it/obsidian-duckdb-mcp.git
cd obsidian-duckdb-mcp
uv sync
uv run obsidian-duckdb-index --vault ./tests/fixtures/vault --db ./tmp/test.duckdb
uv run obsidian-duckdb-mcp --vault ./tests/fixtures/vault --db ./tmp/test.duckdb
Security
The MCP server exposes whatever is in the configured vault. Run it only for trusted local MCP clients. Do not expose stdio wrappers or HTTP bridges to untrusted networks.
License
MIT