obsidian-hybrid-search
Enables search and retrieval of Obsidian notes using hybrid search, similar note lookup, graph traversal, and note reading.
Supports using Ollama as an embedding API provider for local semantic search.
Supports integrating OpenAI-compatible embedding APIs for semantic search, including custom model and base URL configuration.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@obsidian-hybrid-searchsearch my vault for notes about machine learning"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Obsidian Hybrid Search
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.
Search quality
Evaluated on the Obsidian Help vault (171 notes, 58 queries, local model):
OHS (this project) | ||
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 →
Real knowledge-vault 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 →
Large memory benchmark
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 →
Related MCP server: Personal Notes Assistant
Features
Hybrid search
BM25 + fuzzy title + semantic embeddings, fused with RRF
Alias search
notes with
aliases:in frontmatter are indexed and searchable by any alias; alias matches are boosted in BM25 (weight 5×) and fuzzy title scoring
Four search modes
hybrid,semantic,fulltext,title(for text queries)
Similar note lookup
pass
--pathto find semantically related notes using stored chunk embeddings, with a title + content fallback
Graph traversal
--path --relatedshows linked notes at configurable depth; filter by--direction outgoing|backlinks|both
Links & backlinks
every result includes outgoing links and backlinks
Scope filtering
restrict to subfolder(s); supports multiple values and exclusions (
-notes/dev/)
Tag filtering
filter by tag(s); supports multiple values and exclusions (
-category/cs)
Snippet control
--snippet-lengthsets the context window; empty snippets always fall back to note content
Extended output
--extendedadds a TAGS/ALIASES column to the CLI table showing frontmatter tags (#tag) and aliases
Incremental indexing
only re-indexes changed files; watches for edits in real time
Multi-query fan-out
pass multiple queries at once (
ohs "q1" "q2"orqueries[]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
Cross-encoder reranking
--rerankre-scores results withbge-reranker-v2-m3(ONNX int8, ~570 MB download once); improves precision for conceptual and multilingual queries; applied after multi-query merge
Local embeddings
works offline via
@huggingface/transformers(no API key required); default model: Xenova/multilingual-e5-small, 100+ languages
Remote embeddings
OpenAI-compatible API (OpenRouter, Ollama, etc.)
Note reading
readfetches 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 suggestions
Ignore patterns
exclude folders, extensions, or specific files
Obsidian plugin
native search modal inside Obsidian powered by the same CLI; see obsidian-hybrid-search-plugin
Installation
npm install -g obsidian-hybrid-searchCLI usage
Quick start
The recommended setup is to set OBSIDIAN_VAULT_PATH once in ~/.zshrc or ~/.bashrc. This lets you run the CLI from any directory.
export OBSIDIAN_VAULT_PATH="/path/to/your/vault"Open a new terminal and index the vault once.
obsidian-hybrid-search reindexYou can now search from any directory.
obsidian-hybrid-search "zettelkasten"Run from a vault
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.
cd /path/to/your/vault
obsidian-hybrid-search reindex
obsidian-hybrid-search "zettelkasten"From outside the vault, set OBSIDIAN_VAULT_PATH or pass --db /path/to/vault/.obsidian-hybrid-search.db explicitly.
Optional remote embeddings
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.
export OPENAI_API_KEY="sk-..."
# Override the default API base for another provider
# export OPENAI_BASE_URL="https://openrouter.ai/api/v1" # OpenRouter
# export OPENAI_BASE_URL="http://localhost:11434/v1" # Ollama (no key needed)
# export OPENAI_BASE_URL="http://localhost:1234/v1" # LM Studio (no key needed)
# Override the default text-embedding-3-small model
# export OPENAI_EMBEDDING_MODEL="text-embedding-3-small"Search modes
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.
# Hybrid search (default)
obsidian-hybrid-search "zettelkasten atomic notes"
# Fulltext BM25 search
obsidian-hybrid-search "permanent notes" --mode fulltext
# Fuzzy title search (fast, typo-tolerant)
obsidian-hybrid-search "zettleksten" --mode title
# Semantic / vector search
obsidian-hybrid-search "how to build a knowledge graph" --mode semantic
# Limit results and set a score threshold
obsidian-hybrid-search "productivity systems" --limit 5 --threshold 0.3
# Restrict to a subfolder
obsidian-hybrid-search "daily review" --scope notes/periodic/
obsidian-hybrid-search "daily review" --folder notes/periodic/ # alias for --scope
# Restrict to multiple subfolders (OR)
obsidian-hybrid-search "productivity" --scope notes/pkm/ --scope notes/2024/
# Exclude a subfolder
obsidian-hybrid-search "programming" --scope notes/ --scope -notes/archive/
# Filter by tag
obsidian-hybrid-search "productivity" --tag pkm
obsidian-hybrid-search "machine learning" --tag note/basic/primary
# Filter by multiple tags (AND include, exclude with -)
obsidian-hybrid-search "learning" --tag pkm --tag work
# Filter by frontmatter / properties (exact match, case-insensitive)
obsidian-hybrid-search "notes" --frontmatter status:todo
obsidian-hybrid-search "notes" --prop priority:high # --prop is alias for --frontmatter
# Filter by multiple frontmatter fields (AND)
obsidian-hybrid-search "notes" --frontmatter status:todo --frontmatter priority:high
# Exclude by frontmatter value
obsidian-hybrid-search "notes" --frontmatter -status:done
# Filter-only mode: no query, just filters (returns all matching notes sorted by title)
obsidian-hybrid-search --frontmatter status:todo
obsidian-hybrid-search --folder notes/2024/
obsidian-hybrid-search --tag pkm
obsidian-hybrid-search --frontmatter status:done --tag archived
# Unlimited results in filter-only mode (default limit is 10)
obsidian-hybrid-search --folder notes/ --limit 0
# Find semantically similar notes
obsidian-hybrid-search --path notes/pkm/zettelkasten.md
# Graph traversal: show notes linked to/from this note
# Results show depth: -1/-2 = backlinks, 0 = source, +1/+2 = outgoing links
obsidian-hybrid-search --path notes/pkm/zettelkasten.md --related
obsidian-hybrid-search --path notes/pkm/zettelkasten.md --related --depth 2
# Only outgoing links (what this note references)
obsidian-hybrid-search --path notes/pkm/zettelkasten.md --related --direction outgoing
# Only backlinks (who references this note)
obsidian-hybrid-search --path notes/pkm/zettelkasten.md --related --direction backlinks
# Traverse standard Markdown note links instead of Obsidian wikilinks
obsidian-hybrid-search --path notes/pkm/zettelkasten.md --related --link-type markdown
# Traverse both wikilinks and standard Markdown note links
obsidian-hybrid-search --path notes/pkm/zettelkasten.md --related --link-type all
# Longer context around each link
obsidian-hybrid-search --path notes/pkm/zettelkasten.md --related --snippet-length 500
# Rerank results with a cross-encoder model (improves precision, ~1-3s extra latency)
# Downloads bge-reranker-v2-m3 ONNX (~570 MB) on first use, cached in ~/.cache/huggingface/
obsidian-hybrid-search "zettelkasten atomic notes" --rerank
# Show tags and aliases alongside results
obsidian-hybrid-search "zettelkasten" --extended
# JSON output (for scripting)
obsidian-hybrid-search "spaced repetition" --json
# Output only paths (one per line) — useful for piping into read
obsidian-hybrid-search --frontmatter id:OHS-4 --only-paths
ohs read ${(f)"$(ohs search --frontmatter status:todo --only-paths)"} # zsh: read all matching notes
# Output absolute filesystem paths
obsidian-hybrid-search "zettelkasten" --only-absolute-paths
# Open results in Obsidian (each in a new tab)
obsidian-hybrid-search "zettelkasten" --open
# Reindex the vault
obsidian-hybrid-search reindex
# Force full reindex
obsidian-hybrid-search reindex --force
# Reindex a single file
obsidian-hybrid-search reindex notes/pkm/zettelkasten.md
# Show indexing status
obsidian-hybrid-search status
# Show recent indexing activity
obsidian-hybrid-search status --recent
# Show chunks that failed to embed
obsidian-hybrid-search status --errors
# Read a note by path (outputs body content without frontmatter)
obsidian-hybrid-search read notes/pkm/zettelkasten.md
# Read raw file from vault (with frontmatter, like cat)
obsidian-hybrid-search read notes/pkm/zettelkasten.md --raw
# Read multiple notes (separator between each)
obsidian-hybrid-search read notes/pkm/zettelkasten.md notes/pkm/evergreen-notes.md
# Cap content length
obsidian-hybrid-search read notes/pkm/zettelkasten.md --snippet-length 2000
# Structured output with all metadata
obsidian-hybrid-search read notes/pkm/zettelkasten.md --jsonShell aliases
Add to your ~/.zshrc or ~/.bashrc for quick access:
alias ohs='obsidian-hybrid-search'
alias ohss='obsidian-hybrid-search --mode semantic'
alias ohst='obsidian-hybrid-search --mode title'
alias ohsf='obsidian-hybrid-search --mode fulltext'
alias ohsr='obsidian-hybrid-search read'
alias ohsi='obsidian-hybrid-search reindex'
alias ohsst='obsidian-hybrid-search status'Then reload (source ~/.zshrc) and use:
ohs "zettelkasten" # hybrid search
ohss "how to build a knowledge graph" # semantic
ohst "zettelkasten" # fuzzy title (typo-tolerant)
ohsf "permanent notes" # fulltext BM25
ohsr "notes/pkm/zettelkasten.md" # read note by path
ohsi # reindex vault
ohsst # show status
ohsst --recent # show recent indexing activity
ohsst --errors # show chunks that failed to embedOutput example
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 |
┌───────┬───────────────────────────────┬────────────────────────────────────────────┐
│ SCORE │ PATH │ SNIPPET │
├───────┼───────────────────────────────┼────────────────────────────────────────────┤
│ 0.98 │ notes/pkm/zettelkasten.md │ A note-taking method developed by Niklas │
│ │ │ Luhmann. Each note contains one atomic... │
├───────┼───────────────────────────────┼────────────────────────────────────────────┤
│ 0.72 │ notes/pkm/evergreen-notes.md │ Evergreen notes are written to evolve over │
│ │ │ time. Unlike fleeting notes, they are... │
└───────┴───────────────────────────────┴────────────────────────────────────────────┘With --extended, a TAGS/ALIASES column is added. Tags are prefixed with #, aliases are shown as-is:
┌───────┬───────────────────────────────┬──────────────────┬──────────────────────────────┐
│ SCORE │ PATH │ TAGS/ALIASES │ SNIPPET │
├───────┼───────────────────────────────┼──────────────────┼──────────────────────────────┤
│ 0.98 │ notes/pkm/zettelkasten.md │ #pkm │ A note-taking method... │
│ │ │ ЗК │ │
│ │ │ slip-box │ │
├───────┼───────────────────────────────┼──────────────────┼──────────────────────────────┤
│ 0.72 │ notes/pkm/evergreen-notes.md │ #pkm │ Evergreen notes are written │
│ │ │ #writing │ to evolve over time... │
└───────┴───────────────────────────────┴──────────────────┴──────────────────────────────┘Title mode omits the snippet column automatically.
MCP server
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).
Minimal config (local embeddings, no API key)
Uses the built-in Xenova/multilingual-e5-small model. It works fully offline and supports 100+ languages. Downloads ~117 MB on first run.
{
"mcpServers": {
"obsidian-hybrid-search": {
"command": "npx",
"args": ["-y", "-p", "obsidian-hybrid-search@latest", "obsidian-hybrid-search-mcp"],
"env": {
"OBSIDIAN_VAULT_PATH": "/path/to/your/vault"
}
}
}
}Full config (OpenRouter)
{
"mcpServers": {
"obsidian-hybrid-search": {
"command": "npx",
"args": ["-y", "-p", "obsidian-hybrid-search@latest", "obsidian-hybrid-search-mcp"],
"env": {
"OBSIDIAN_VAULT_PATH": "/path/to/your/vault",
"OBSIDIAN_PREFIX": "myvault_",
"OBSIDIAN_RESPECT_GITIGNORE": "true",
"OBSIDIAN_IGNORE_PATTERNS": ".obsidian/**,templates/**,*.canvas",
"OBSIDIAN_INCLUDE_PATTERNS": "private/notes/**",
"OPENAI_API_KEY": "sk-or-v1-...",
"OPENAI_BASE_URL": "https://openrouter.ai/api/v1",
"OPENAI_EMBEDDING_MODEL": "openai/text-embedding-3-small"
}
}
}
}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.
Shared HTTP server
Use this when multiple MCP clients should share one long-lived search/indexing process.
Start or reuse the background server:
OBSIDIAN_VAULT_PATH="/path/to/your/vault" obsidian-hybrid-search serveserve 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):
{
"mcpServers": {
"obsidian-hybrid-search": {
"url": "http://127.0.0.1:3939/mcp"
}
}
}Manage the server:
obsidian-hybrid-search serve status
obsidian-hybrid-search serve stop
obsidian-hybrid-search serve --foreground
obsidian-hybrid-search serve --http --foregroundHTTP 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.
obsidian-hybrid-search serve \
--host 0.0.0.0 \
--allowed-host 192.168.1.20:3939 \
--allowed-host notes.example.com:3939Repeat --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.
Available MCP tools
Tool | Description |
| Search the vault. Use |
| Fetch one or more notes by vault-relative path. Returns full content, title, aliases, tags, links, and backlinks. On path miss: returns |
| Reindex the vault or a specific file |
| 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.
Configuration
Environment variable | Default | Description |
| Required for MCP; CLI auto-detects | Absolute path to your vault |
|
| Optional MCP tool prefix, e.g. |
|
| Comma-separated ignore patterns |
|
| Read root and nested |
|
| Comma-separated patterns to re-include notes ignored only by |
| None | API key; omit to use local model embeddings or keyless servers (Ollama, LM Studio) |
|
| API base URL |
|
| Embedding model name |
Ignore patterns
Use
folder/**to ignore a directory and all its contents.Use
*.canvasto ignore files by extension.Use
exact/path.mdto 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.
How it works
Indexing splits notes by headings with a sliding-window fallback, creates embeddings, and stores the results in SQLite with FTS5 and
sqlite-vec.Search runs BM25, fuzzy trigram title and alias search, and vector KNN search in parallel. BM25 uses weights of 10× for titles, 5× for aliases, and 1× for content. RRF then gives semantic and BM25 results a weight of 1.5× each, exact alias matches 2×, and partial fuzzy matches 0.25×. The final scores range from 0 to 1, where higher scores mean greater relevance.
Links are resolved from wikilinks such as
[[note]], mapped to note paths, and stored. Every search result includeslinksandbacklinksarrays.Watcher uses
chokidarto detect file changes and update the index in the background.
Contributing
Issues and pull requests are welcome. See CONTRIBUTING.md for setup instructions and project checks.
License
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityBmaintenanceMCP server that exposes a local Obsidian vault as persistent, searchable memory for AI agents, with hybrid retrieval, reranker, and PDF support.3485129MIT
- Alicense-qualityBmaintenanceMCP server that indexes your Obsidian notes into a Milvus vector database and enables querying them via a local or OpenAI LLM, with real-time synchronization.3MIT
- Alicense-qualityDmaintenanceA lightweight MCP server that enables AI assistants to securely read, create, and modify notes in an Obsidian vault, with support for semantic search and web scraping.6,104MIT
- Flicense-qualityDmaintenanceEnables natural language interaction with Obsidian vaults through an MCP server, providing hybrid search, file management, and AI-powered analysis.2
Related MCP Connectors
Serve a folder of Markdown notes as an MCP server: hybrid search, reading, and sourced answers.
Search your AI chat history (ChatGPT, Claude, Codex) from any MCP client. Remote, private, read-only
An MCP server that gives your AI access to the source code and docs of all public github repos
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/flowing-abyss/obsidian-hybrid-search'
If you have feedback or need assistance with the MCP directory API, please join our Discord server