結論

Obsidian vault 向けセマンティック検索ツールMCP、CLI ツール voracle を設計、実装した。最終的なツールは以下の構成になった。

  • MCP サーバー: stdio JSON-RPC 2.0、公開ツールは searchread の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 は Obsidian vault に対するセマンティック検索と reranking を提供する Rust 製バイナリで、ONNX Runtime による embedding(pplx-embed-v1-0.6b)と ColBERT reranking(mxbai-edge-colbert)をローカルで実行する構成になっていた MCP(stdio JSON-RPC 2.0)

同じ Rust MCP ツール群として pathfindershelpa(現 filesystem) があり、voracle はこれらと並ぶ vault 検索の専用ツールという位置付けにある。

設計のきっかけは、記事ファイル、設計メモ、ドメインファイルがobsidianに溜まってきて、記憶に留められなくなってきたので、なんとなくの「あれ、それ」で適当にchunkみつけてきてもらいたかったのと、LLMとの対話をdistil コマンドで吸い出ししていたので、どうせならまとめて実装、統合しようと思いついたことだった。


複数 vault root 対応

単一の vault_path: PathBuf から roots: Vec<PathBuf> に変更。--root フラグで複数 vault ルートを指定でき、VaultResolver は各 root を再帰的にスキャンしてノートを統合する。

  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 キャッシュ

初回 ingest は全ノートの全チャンクを ONNX モデルで embedding する必要があり、500 ノート規模の vault では重い処理になる。2 回目以降の高速化のために .embeddings.jsonl による差分キャッシュを実装した。

キャッシュは JSONL 形式で vault ルートごとに配置され、1 行が 1 ノートに対応する。各行に note_idcontent_hash(DefaultHasher)、mtime_secs、各チャンクの embedding ベクトルを含む。ファイル名はドットプレフィックスで Obsidian の UI から不可視にしている。

ルックアップは二段階で行う。まず mtime が一致すればキャッシュヒット。mtime が異なる場合はファイル内容のハッシュを比較し、ハッシュも不一致なら re-embed する。touch やコピーでタイムスタンプだけ変わったケースをカバーしつつ、大半のケースはメタデータだけで高速に判定できる。

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

-f フラグでキャッシュを無視した全 re-embed も可能。IngestResultcached_hits フィールドを追加して復元件数を報告する。

distil コマンドの統合

agent-gateway で運用していた distil シェルスクリプトの機能を voracle に統合した。外部 LLM 会話ログ(Claude CLI、Codex CLI、Gemini CLI)を vault に取り込むコマンドで、もともと agent-gateway の POST /v1/obsidian/ingest を叩いていたが、gateway 側のハンドラは既に削除済みだった。

入力は 3 ソースに対応。第 2 引数でインライン、stdin からパイプ、どちらもなければ pbpaste でクリップボード。モデルエイリアスは c/claudeclaude-opus-4-6x/codexcodex-5.3g/geminigemini-3.1-pro

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

書き込み先は第 1 vault root の _notes/llm-completions/{model_dir}/{correlation_id}.md。frontmatter には date、status: raw、correlation_id、model を含め、インデックスが構築済みであればセマンティック検索で関連ノートを related フィールドに記録する。


MCP ツール整理

MCP ingest の除去

MCP サーバーでは当初 searchreadingest の 3 ツールを公開していた。しかし ingest は CLI で事前実行する前提に変わったため MCP 経由で呼び出す必要がない。実際、handle_tool_call のルーティングには既に ingest へのディスパッチが含まれておらず、ツール定義だけが残っている不整合な状態だった。

tools.rs から ingest 関数本体を削除し、server.rstool_definitions() からもエントリを削除。MCP 公開ツールは searchread の 2 つになった。

各 search 結果には read_tool_call が含まれ、LLM がそのまま呼び出せる。ページは約 1000 文字ずつ分割されるため、コンテキスト消費を制御できる。

最終的な 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 をロードしてセマンティック検索を行うが、-list はファイルパスとスコアの一覧、-rg はマッチしたチャンクの前後コンテキスト付きで表示する。-read はノート全文を stdout に出力する CLI 専用コマンドで、MCP の read ツールとは別物。-embed-rerank は低レベルの推論ツールで、パイプラインのデバッグや他ツールとの連携に使う。

voracle への改名

コマンド名 valut-oracle はハイフン込みで長く、日常的に使うには煩わしかった。voracle に短縮する方針で、パッケージ名・バイナリ名・環境変数名をすべて変更した。

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

モデルディレクトリは ~/.local/share/voracle/models/ に標準化し、XDG_DATA_HOME が設定されていればそちらを尊重する。pathfinder のように 32MB 程度なら include_bytes! でバイナリに埋め込めるが、700MB では現実的でないため外部配置とした。リポジトリからは git filter-repo で全履歴から除去済み。