実装した検索ツール

Obsidianのノートを意味で検索する voracle を実装しました。Rust製のMCP / CLIで、embeddingとrerankingをローカル実行します。開発メモを検索し、LLMの会話も取り込めるツールです。

  • MCP サーバー: stdio JSON-RPC 2.0、公開ツールは search と read の2つ
  • CLI コマンド: -ingest(インデックス構築)、-list / -rg(セマンティック検索)、-distil(外部 LLM 会話取り込み)
  • 複数 vault root: --root フラグまたは VORACLE_PATH 環境変数(コロン区切り)で指定
  • embedding キャッシュ: .embeddings.jsonl による差分 ingest。変更のないノートはキャッシュから復元
  • モデル配置: ~/.local/share/voracle/models/ に ONNX モデルを配置
  # インデックス構築(初回は全チャンク embedding、2回目以降は差分のみ)
voracle -ingest

# セマンティック検索
voracle -list "kubernetes networking"

# MCP サーバー起動
voracle --server

# 外部 LLM 会話の取り込み(クリップボードから)
voracle -distil c
  

背景

前身のvalut-oracleは、ONNX Runtimeのpplx-embed-v1-0.6bとColBERTのmxbai-edge-colbertを使っていました。MCPはstdio JSON-RPC 2.0です。

pathfinder と shelpa(現filesystem) と並ぶRust製MCPで、vault検索を担当します。

記事、設計メモ、ドメインファイルが増え、曖昧な記憶から該当chunkを探したくなりました。LLM会話を取り出すdistilも、同じツールへ統合しました。


複数 vault root 対応

vault_path: PathBuf を roots: Vec<PathBuf> に変えました。--root で複数指定し、VaultResolver が再帰的に走査します。

  voracle --root /path/to/vault1 --root /path/to/vault2 --server
  

VORACLE_PATH はコロン区切りです。優先順位は --root > VORACLE_PATH > エラーです。

  export VORACLE_PATH=/Volumes/VALUT/obsidian/docs:/Volumes/VALUT/obsidian/articles
  

embedding キャッシュ

500ノート規模では全chunkのembeddingが重いため、.embeddings.jsonl に差分cacheを持たせました。

vaultごとのJSONLで、1行が1ノートです。note_id、content_hash(DefaultHasher)、mtime_secs、chunkごとのvectorを保存します。ドットで始め、Obsidian UIには表示しません。

mtimeが同じならcacheを使います。異なる場合は内容hashを比べ、hashも変わったノートだけ再計算します。touchやコピーで時刻だけ変わった場合も再利用できます。

voracle -ingest 実行中のターミナル出力
voracle -ingest — ノートごとに embedding を実行している様子

-f は全re-embedです。IngestResult の cached_hits で復元件数を返します。

distil コマンドの統合

agent-gatewayの distil shellを統合しました。Claude CLI、Codex CLI、Gemini CLIの会話を取り込む機能です。以前の POST /v1/obsidian/ingest handlerは削除済みでした。

入力は第2引数、stdin、どちらもなければ pbpaste です。aliasは c / claude → claude-opus-4-6、x / codex → codex-5.3、g / gemini → gemini-3.1-pro です。

  voracle -distil c              # クリップボードから
voracle -distil claude "text"  # インライン
cat file.md | voracle -distil x  # stdin
  

第1rootの _notes/llm-completions/{model_dir}/{correlation_id}.md へ書きます。frontmatterにdate、status: raw、correlation_id、modelを保存し、indexがあれば関連ノートを検索して related に入れます。


MCP ツール整理

MCP ingest の除去

MCPは当初 search、read、ingest でした。ingestはCLIで事前に行うため、handle_tool_call のdispatchからは既に外れていましたが、定義だけ残っていました。

tools.rs の ingest と server.rs の tool_definitions() のentryを削除し、公開ツールを search と read に絞りました。

検索結果に read_tool_call を付け、LLMから呼べるようにしました。約1000文字ごとのpageでcontext消費を抑えます。

CLIの使い方です。

  usage: voracle [--root <path>]... <command> [args]

options:
  --root <path>                   vault root directory (repeatable)
                                  falls back to VORACLE_PATH (colon-separated)

index:
  -ingest [-f]                    build index (required before search)

search (loads .index.bin):
  -list  <query> [-k N]           semantic file list, default top 3
  -rg    <query> [-k N]           grep-like semantic search with context
  -read  <note_id>                print note content to stdout

tools:
  -embed <text>                   print embedding vector (TSV)
  -rerank <query> <file>...       rerank files by ColBERT MaxSim score
  -status                         show index status
  -distil <model> [content]       ingest LLM conversation into vault
                                  model: c(laude), x(codex), g(emini)
                                  reads from: arg, stdin, or clipboard

server:
  --server                        stdio MCP server (JSON-RPC 2.0)
  

-list と -rg は .index.bin を読みます。前者はpathとscore、後者はchunkと前後contextを表示します。-read は全文をstdoutへ出すCLI専用機能で、MCPの read とは別です。-embed と -rerank はdebugや他ツール連携向けです。

voracle への改名

長い valut-oracle を voracle に短縮し、package、binary、環境変数も変えました。

  • Cargo.toml: パッケージ名を voracle に変更
  • src/cli/mod.rs: 環境変数を VALUT_ORACLE_PATH → VORACLE_PATH に変更、全 usage メッセージを更新
  • src/config.rs: モデルディレクトリを ~/.local/share/voracle/models/ に変更
  • src/main.rs: トレーシングフィルタを voracle=info に変更
  • src/mcp/server.rs: ServerInfo.name を voracle に変更
  • ドキュメント全体(README.md、docs/ 内 5 ファイル)を新名称に統一

モデルは ~/.local/share/voracle/models/ に置き、XDG_DATA_HOME があれば従います。pathfinderの約32MBは include_bytes! で埋め込めますが、700MBのため外部配置にしました。リポジトリ履歴からは git filter-repo で除去済みです。