Obsidian Hybrid Search

io.github.flowing-abyss由 GitHub 帳號 flowing-abyss 發布v0.17.4更新於 Nov 5, 2026

Search Obsidian vaults with hybrid full-text, fuzzy, semantic, and graph retrieval.

已驗證STDIO僅桌面檔案與儲存知識與記憶

概覽

AI 產生的概覽

為本機 Obsidian 儲存庫建立索引,讓 AI 助理依關鍵字、語意、標題或連結關係檢索筆記,並讀取筆記與其中繼資料。

可以做什麼
  • 以混合、語意、全文或模糊標題模式檢索筆記,並依資料夾、標籤與 frontmatter 篩選
  • 找出與某篇筆記語意相近的筆記,並依指定深度走訪出鏈與反向連結
  • 依儲存庫相對路徑讀取一篇或多篇筆記,回傳正文、標題、別名、標籤、連結與反向連結
  • 重建或更新索引,並查看索引狀態、筆記數量與最近索引時間
適用情境
適合筆記存放在 Obsidian 儲存庫、希望助理以自身筆記而非貼上的文字作為依據的情境,也適合需要沿筆記間連結查找相關內容、從原始筆記取得可引用脈絡的情況。
執行需求
透過 npx 以 stdio 在本機執行,或作為共用的本機 HTTP 服務執行於 127.0.0.1:3939。需要 Node.js,並在 OBSIDIAN_VAULT_PATH 提供儲存庫的絕對路徑。本機嵌入可離線使用,但首次執行會下載約 117 MB 模型;遠端嵌入需要 OPENAI_API_KEY,可選用 OPENAI_BASE_URL 與 OPENAI_EMBEDDING_MODEL。啟用重排序會額外下載模型。
安裝前請注意
此服務會索引並讀取整個儲存庫,連線前應以 OBSIDIAN_IGNORE_PATTERNS 或 OBSIDIAN_CONTENT_IGNORE_PATTERNS 排除私密資料夾。若設定 OPENAI_API_KEY 並使用遠端位址,筆記內容會傳送給該嵌入服務商。reindex 工具會寫入本機索引資料庫,忽略規則也會保存在該資料庫中。將 HTTP 服務綁定到 localhost 以外會把儲存庫暴露到網路。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Obsidian Hybrid Search,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

Obsidian Hybrid Search

[npm version] [Tests] [Total downloads]

[Obsidian Hybrid Search explains hybrid retrieval from Obsidian notes]

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)qmd
nDCG@50.7330.659
MRR0.7880.665
Hit@10.7240.500
Avg query time571 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.

MetricValue
nDCG@50.722
nDCG@100.753
MRR0.874
Hit@10.795
Hit@50.974
Recall@100.972
AllRel@100.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:

MetricValue
nDCG@50.895
MRR0.920
Hit@10.889
Hit@50.968
Recall@100.950
AllRel@100.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 →

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 --path to find semantically related notes using stored chunk embeddings, with a title + content fallback
  • Graph traversal
    • --path --related shows 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-length sets the context window; empty snippets always fall back to note content
  • Extended output
    • --extended adds 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" 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
  • Cross-encoder reranking
    • --rerank re-scores results with gte-multilingual-reranker-base (ONNX int8, ~341 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
    • read 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 suggestions
  • Ignore patterns
    • exclude folders, extensions, or specific files
  • Obsidian plugin

Installation

bash
npm install -g obsidian-hybrid-search

CLI 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.

bash
export OBSIDIAN_VAULT_PATH="/path/to/your/vault"

Open a new terminal and index the vault once.

bash
ohs reindex

You can now search from any directory.

bash
ohs "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.

bash
cd /path/to/your/vaultohs reindexohs "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.

bash
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.

bash
# Hybrid search (default)ohs "zettelkasten atomic notes"
# Fulltext BM25 searchohs "permanent notes" --mode fulltext
# Fuzzy title search (fast, typo-tolerant)ohs "zettleksten" --mode title
# Semantic / vector searchohs "how to build a knowledge graph" --mode semantic
# Limit results and set a score thresholdohs "productivity systems" --limit 5 --threshold 0.3
# Restrict to a subfolderohs "daily review" --scope notes/periodic/ohs "daily review" --folder notes/periodic/    # alias for --scope
# Restrict to multiple subfolders (OR)ohs "productivity" --scope notes/pkm/ --scope notes/2024/
# Exclude a subfolderohs "programming" --scope notes/ --scope -notes/archive/
# Filter by tagohs "productivity" --tag pkmohs "machine learning" --tag note/basic/primary
# Filter by multiple tags (AND include, exclude with -)ohs "learning" --tag pkm --tag work
# Filter by frontmatter / properties (exact match, case-insensitive)ohs "notes" --frontmatter status:todoohs "notes" --prop priority:high          # --prop is alias for --frontmatter
# Filter by multiple frontmatter fields (AND)ohs "notes" --frontmatter status:todo --frontmatter priority:high
# Exclude by frontmatter valueohs "notes" --frontmatter -status:done
# Filter-only mode: no query, just filters (returns all matching notes sorted by title)ohs --frontmatter status:todoohs --folder notes/2024/ohs --tag pkmohs --frontmatter status:done --tag archived
# Unlimited results in filter-only mode (default limit is 10)ohs --folder notes/ --limit 0
# Find semantically similar notesohs --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 linksohs --path notes/pkm/zettelkasten.md --relatedohs --path notes/pkm/zettelkasten.md --related --depth 2
# Only outgoing links (what this note references)ohs --path notes/pkm/zettelkasten.md --related --direction outgoing
# Only backlinks (who references this note)ohs --path notes/pkm/zettelkasten.md --related --direction backlinks
# Traverse standard Markdown note links instead of Obsidian wikilinksohs --path notes/pkm/zettelkasten.md --related --link-type markdown
# Traverse both wikilinks and standard Markdown note linksohs --path notes/pkm/zettelkasten.md --related --link-type all
# Longer context around each linkohs --path notes/pkm/zettelkasten.md --related --snippet-length 500
# Rerank results with a cross-encoder model (improves precision, ~1-3s extra latency)# Downloads gte-multilingual-reranker-base ONNX (~341 MB) on first use, cached in ~/.cache/huggingface/ohs "zettelkasten atomic notes" --rerank
# Show tags and aliases alongside resultsohs "zettelkasten" --extended
# JSON output (for scripting)ohs "spaced repetition" --json
# Output only paths (one per line) — useful for piping into readohs --frontmatter id:OHS-4 --only-pathsohs read ${(f)"$(ohs search --frontmatter status:todo --only-paths)"}  # zsh: read all matching notes
# Output absolute filesystem pathsohs "zettelkasten" --only-absolute-paths
# Open results in Obsidian (each in a new tab)ohs "zettelkasten" --open
# Reindex the vaultohs reindexohs reindex --threads 4  # Limit local CPU inference to 4 threads
# Force full reindexohs reindex --force
# Reindex a single fileohs reindex notes/pkm/zettelkasten.md
# Retry only the notes whose chunks failed to embedohs reindex --errors
# Show indexing statusohs status
# Show recent indexing activityohs status --recent
# Show chunks that failed to embedohs status --errors
# Read a note by path (outputs body content without frontmatter)ohs read notes/pkm/zettelkasten.md
# Read raw file from vault (with frontmatter, like cat)ohs read notes/pkm/zettelkasten.md --raw
# Read multiple notes (separator between each)ohs read notes/pkm/zettelkasten.md notes/pkm/evergreen-notes.md
# Cap content lengthohs read notes/pkm/zettelkasten.md --snippet-length 2000
# Structured output with all metadataohs read notes/pkm/zettelkasten.md --json

Shell aliases

Add to your ~/.zshrc or ~/.bashrc for quick access:

bash
alias ohss='ohs --mode semantic'alias ohst='ohs --mode title'alias ohsf='ohs --mode fulltext'alias ohsr='ohs read'alias ohsi='ohs reindex'alias ohsst='ohs status'

Then reload (source ~/.zshrc) and use:

bash
ohs "zettelkasten"                        # hybrid searchohss "how to build a knowledge graph"     # semanticohst "zettelkasten"                       # fuzzy title (typo-tolerant)ohsf "permanent notes"                    # fulltext BM25ohsr "notes/pkm/zettelkasten.md"          # read note by pathohsi                                      # reindex vaultohsst                                     # show statusohsst --recent                            # show recent indexing activityohsst --errors                            # show chunks that failed to embed

Output example

Hybrid search returns a table with scores and snippets. Scores are color-coded by relevance:

ScoreColorMeaning
0.8 – 1.0greenHighly relevant
0.5 – 0.8yellowModerately relevant
0.2 – 0.5plainSomewhat relevant
0.0 – 0.2dimLow 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.

json
{  "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)

json
{  "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, npx will 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:

bash
OBSIDIAN_VAULT_PATH="/path/to/your/vault" ohs serve

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):

json
{  "mcpServers": {    "obsidian-hybrid-search": {      "url": "http://127.0.0.1:3939/mcp"    }  }}

Manage the server:

bash
ohs serve statusohs serve stopohs serve --foregroundohs serve --http --foreground

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.

bash
ohs serve \  --host 0.0.0.0 \  --allowed-host 192.168.1.20:3939 \  --allowed-host notes.example.com:3939

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.

Available MCP tools

ToolDescription
searchSearch 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
readFetch 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
reindexReindex the vault or a specific file
statusShow 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 variableDefaultDescription
OBSIDIAN_VAULT_PATHRequired for MCP; CLI auto-detectsAbsolute path to your vault
OBSIDIAN_PREFIX""Optional MCP tool prefix, e.g. myvault_ → myvault_search, myvault_read
OBSIDIAN_IGNORE_PATTERNS.obsidian/**,templates/**,*.canvasComma-separated ignore patterns
OBSIDIAN_RESPECT_GITIGNOREtrueRead root and nested .gitignore files; set to false to disable
OBSIDIAN_INCLUDE_PATTERNS""Comma-separated patterns to re-include notes ignored only by .gitignore
OBSIDIAN_CONTENT_IGNORE_PATTERNS""Comma-separated regex rules for note body text to leave out of search, e.g. /%%[\s\S]*?%%/g. Original files and read stay unchanged.
OBSIDIAN_CHUNK_TOKENS512 for new indexesChunk size in estimated tokens: a positive integer capped by the model's context limit. Stored per index; changes require a full reindex --force. When unset, the saved size is retained.
OPENAI_API_KEYNoneAPI key; omit to use local model embeddings or keyless servers (Ollama, LM Studio)
OPENAI_BASE_URLhttps://api.openai.com/v1API base URL
OPENAI_EMBEDDING_MODELtext-embedding-3-smallEmbedding model name

Ignore patterns

  • Use folder/** to ignore a directory and all its contents.
  • Use *.canvas to ignore files by extension.
  • Use exact/path.md to ignore a specific file.
  • Use **/node_modules/** to ignore a directory at any depth.

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

  1. Indexing splits notes by headings with a sliding-window fallback, creates embeddings, and stores the results in SQLite with FTS5 and sqlite-vec.
  2. 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.
  3. Links are resolved from wikilinks such as [[note]], mapped to note paths, and stored. Every search result includes links and backlinks arrays.
  4. Watcher uses chokidar to detect file changes and update the index in the background.

License

MIT

來源:README.md,提交 a450eff

工具

0
SourceWeft 曾在隔離沙箱中以無憑證方式啟動該軟體包一次,以列出其工具,但未能完成:伺服器未能啟動。

版本歷史

13
  1. v0.17.4最新Nov 5, 2026
  2. v0.17.3Nov 4, 2026
  3. v0.17.2Oct 26, 2026
  4. v0.17.1Oct 25, 2026
  5. v0.16.4Oct 22, 2026
  6. v0.16.3Oct 19, 2026
  7. v0.16.2Oct 12, 2026
  8. v0.16.1Oct 5, 2026
  9. v0.16.0Sep 29, 2026
  10. v0.15.6Sep 21, 2026
  11. v0.15.5Sep 20, 2026
  12. v0.15.3Sep 17, 2026
  13. v0.15.2Sep 16, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分