The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Obsidian Hybrid Search listing page.
Your Obsidian vault already contains your best thinking. Obsidian Hybrid Search makes that thinking easier to find, reuse, and bring into AI-assisted work.
It gives your vault one retrieval engine and three practical ways to use it. The native Obsidian plugin gives you fast search, previews, similar notes, link discovery, and graph views while you write. The MCP server lets AI agents search and read your notes as tool calls. The CLI gives power users the same engine for indexing, filtering, reranking, reading, and scripting.
The search understands how real vaults are built. It combines semantic search, BM25 full text, fuzzy title and alias matching, tags, folders, frontmatter, wikilinks, backlinks, and similar-note lookup. You can search by idea, phrase, title, relationship, or metadata without remembering the exact words you wrote.
That turns Obsidian into a stronger personal knowledge system and a better starting point for AI work. Agents can begin from your own notes, pull cited context from source files, follow related material, and work with knowledge you already trust. OHS runs locally by default with SQLite, FTS5, sqlite-vec, RRF ranking, and optional OpenAI-compatible embedding APIs.
Evaluated on the Obsidian Help vault (171 notes, 58 queries, local model):
| OHS (this project) | qmd | |
|---|---|---|
| nDCG@5 | 0.733 | 0.659 |
| MRR | 0.788 | 0.665 |
| Hit@1 | 0.724 | 0.500 |
| Avg query time | 571 ms ¹ | 754 ms ² |
| Model download | ~117 MB | ~2.2 GB |
¹ CPU (Apple Silicon), hybrid mode, no rerank. ² GPU (Apple Silicon Metal), LLM query expansion + reranking.
OHS uses Xenova/multilingual-e5-small. How to reproduce → · Full benchmark →
OHS is also evaluated on Andy Matuschak’s public evergreen notes, converted into an Obsidian vault with title-based note filenames, source URLs in frontmatter, local attachments, and 5,000+ internal note links across 1,357 notes.
The curated golden set includes 78 hand-judged queries across known-item lookup, paraphrases, quote fragments, ambiguous topics, citation lookup, and multi-note evidence.
Using the default local embedding model, OHS performs strongly on this dense note network.
| Metric | Value |
|---|---|
| nDCG@5 | 0.722 |
| nDCG@10 | 0.753 |
| MRR | 0.874 |
| Hit@1 | 0.795 |
| Hit@5 | 0.974 |
| Recall@10 | 0.972 |
| AllRel@10 | 0.949 |
The benchmark exercises retrieval over a highly connected real-world knowledge vault, including queries that do not simply repeat note titles.
Result JSON · Reproduce and interpret →
To test retrieval on a larger public dataset,
LongMemEval-S
was converted into a 22,419-note Obsidian-style vault with 470 retrieval
queries. Using baai/bge-m3 embeddings, OHS ranked the answer-bearing notes
strongly:
| Metric | Value |
|---|---|
| nDCG@5 | 0.895 |
| MRR | 0.920 |
| Hit@1 | 0.889 |
| Hit@5 | 0.968 |
| Recall@10 | 0.950 |
| AllRel@10 | 0.904 |
For this benchmark, each query uses the LongMemEval-provided haystack as its search scope. That makes the result reproducible and easy to inspect query by query, while still exercising retrieval over a large generated memory vault.
Result JSON · Reproduce and interpret →
aliases: in frontmatter are indexed and searchable by any alias; alias matches are boosted in BM25 (weight 5×) and fuzzy title scoringhybrid, semantic, fulltext, title (for text queries)--path to find semantically related notes using stored chunk embeddings, with a title + content fallback--path --related shows linked notes at configurable depth; filter by --direction outgoing|backlinks|both-notes/dev/)-category/cs)--snippet-length sets the context window; empty snippets always fall back to note content--extended adds a TAGS/ALIASES column to the CLI table showing frontmatter tags (#tag) and aliasesohs "q1" "q2" or queries[] in MCP); results are merged via RRF, so a note that ranks well in any one query floats to the top; useful when the note may use different vocabulary than the query--rerank re-scores results with bge-reranker-v2-m3 (ONNX int8, ~570 MB download once); improves precision for conceptual and multilingual queries; applied after multi-query merge@huggingface/transformers (no API key required); default model: Xenova/multilingual-e5-small, 100+ languagesread fetches one or more notes by vault-relative path; returns full content with title, aliases, tags, links, and backlinks; on path miss returns top-3 fuzzy suggestionsThe recommended setup is to set OBSIDIAN_VAULT_PATH once in ~/.zshrc or ~/.bashrc. This lets you run the CLI from any directory.
Open a new terminal and index the vault once.
You can now search from any directory.
Alternatively, run the CLI without an environment variable from any directory inside your vault. It finds the vault root by walking up to the nearest .obsidian/ folder.
From outside the vault, set OBSIDIAN_VAULT_PATH or pass --db /path/to/vault/.obsidian-hybrid-search.db explicitly.
By default, the CLI uses the local Xenova/multilingual-e5-small model. It works offline without an API key, downloads about 117 MB on first use, and supports more than 100 languages.
To use a remote API, add its settings to your shell profile.
The CLI supports four search modes called hybrid, fulltext, semantic, and title, plus graph traversal for linked notes. The commands below show how to use them, apply filters, rerank results, and control the output.
Add to your ~/.zshrc or ~/.bashrc for quick access:
Then reload (source ~/.zshrc) and use:
Hybrid search returns a table with scores and snippets. Scores are color-coded by relevance:
| Score | Color | Meaning |
|---|---|---|
| 0.8 – 1.0 | green | Highly relevant |
| 0.5 – 0.8 | yellow | Moderately relevant |
| 0.2 – 0.5 | plain | Somewhat relevant |
| 0.0 – 0.2 | dim | Low relevance |
With --extended, a TAGS/ALIASES column is added. Tags are prefixed with #, aliases are shown as-is:
Title mode omits the snippet column automatically.
Most AI assistants operate without access to your personal knowledge and can only work with what you paste into the conversation. Adding this server gives any MCP-compatible assistant a persistent, searchable index of your entire vault. It becomes a tool call, not a copy-paste session: the assistant queries your notes the same way it calls any other tool, gets ranked results with snippets and links, and can navigate your knowledge graph on request.
Add to your MCP config (.mcp.json, claude_desktop_config.json, or equivalent for your client).
Uses the built-in Xenova/multilingual-e5-small model. It works fully offline and supports 100+ languages. Downloads ~117 MB on first run.
Note: On first run,
npxwill install the package automatically. Ignore patterns are persisted in the database and restored on every subsequent startup even if the env var is missing.
Use this when multiple MCP clients should share one long-lived search/indexing process.
Start or reuse the background server:
serve starts the MCP server over HTTP by default; serve --http is the explicit equivalent. The command prints the server URL, PID, log path, and a client config snippet. The default bind address is 127.0.0.1:3939.
Then add this to a URL-based MCP client config (.mcp.json, claude_desktop_config.json, or equivalent):
Manage the server:
HTTP mode uses stateless MCP Streamable HTTP. It does not issue or validate Mcp-Session-Id, so an MCP client can continue making tool calls after the daemon restarts even if it still sends a stale session header. This mode is intended for the server's request/response tools and does not provide persistent SSE streams, server-initiated notifications, or SSE resume.
If port 3939 is already in use, the command exits with an error instead of choosing another port automatically. Use --port for separate vaults.
When binding beyond localhost, allow every hostname or address that MCP clients will use.
Repeat --allowed-host for multiple values. You can also set a comma-separated list with OBSIDIAN_MCP_ALLOWED_HOSTS. The --allow-any-host option disables Host-header protection for trusted networks.
| Tool | Description |
|---|---|
search | Search the vault. Use query for text search (mode: hybrid/semantic/fulltext/title) or path for semantic similarity. Combine path with related: true for graph traversal. Pass queries[] for multi-query fan-out (parallel search, RRF merge). Supports scope, tag, limit, threshold, depth, direction, snippet_length, rerank |
read | Fetch one or more notes by vault-relative path. Returns full content, title, aliases, tags, links, and backlinks. On path miss: returns found: false with top-3 fuzzy suggestions. Accepts a single path or an array. Use snippet_length to cap content size |
reindex | Reindex the vault or a specific file |
status | Show total notes, indexed count, last indexed time |
Set OBSIDIAN_PREFIX to add a prefix to every tool name. For example, myvault_ produces myvault_search and myvault_read. The prefix is empty by default.
| Environment variable | Default | Description |
|---|---|---|
OBSIDIAN_VAULT_PATH | Required for MCP; CLI auto-detects | Absolute path to your vault |
OBSIDIAN_PREFIX | "" | Optional MCP tool prefix, e.g. myvault_ → myvault_search, myvault_read |
OBSIDIAN_IGNORE_PATTERNS | .obsidian/**,templates/**,*.canvas | Comma-separated ignore patterns |
OBSIDIAN_RESPECT_GITIGNORE | true | Read root and nested .gitignore files; set to false to disable |
OBSIDIAN_INCLUDE_PATTERNS | "" | Comma-separated patterns to re-include notes ignored only by .gitignore |
OPENAI_API_KEY | None | API key; omit to use local model embeddings or keyless servers (Ollama, LM Studio) |
OPENAI_BASE_URL | https://api.openai.com/v1 | API base URL |
OPENAI_EMBEDDING_MODEL | text-embedding-3-small | Embedding model name |
folder/** to ignore a directory and all its contents.*.canvas to ignore files by extension.exact/path.md to ignore a specific file.Root and nested .gitignore files are respected by default. Set OBSIDIAN_RESPECT_GITIGNORE=false to disable this behavior. Use OBSIDIAN_INCLUDE_PATTERNS to re-include Markdown notes that are ignored only by .gitignore. Include patterns do not override OBSIDIAN_IGNORE_PATTERNS or internal exclusions.
The database stores the ignore configuration and restores it when the server restarts, even if the environment variable is missing.
sqlite-vec.[[note]], mapped to note paths, and stored. Every search result includes links and backlinks arrays.chokidar to detect file changes and update the index in the background.Issues and pull requests are welcome. See CONTRIBUTING.md for setup instructions and project checks.
MIT