作成したツールと結果

ローカル LLM の tool_call で、誤ったパスから実在パスを探す pathfinder を Rust で作りました。32B以下のモデルで、typo、拡張子違い、階層の省略などを繰り返したためです。

MCP 側で候補を絞り、ColBERT(Late Interaction)で再順位付けします。INT8 は16MB、CPU-only、10ms未満で、GPU VRAM は使いません。

約4,000ファイルのモノレポで filesystem MCP と比較しました。精度は僅差で、context 消費は約10,000 tokens少なくなりました。list_directory の祖先チェックによる O(n) の出力を、候補の絞り込みで減らしています。

ModernBERT 推論を Rust に実装し、複数プロジェクトを検索する CLI も作りました。LLM導入の開発支援に加え、手元のコードを探す用途です。

Zed Editor の MCP Servers 画面: ctree, pathfinder, serena が並んでいる
Zed Editor の MCP Servers 画面 — ctree, pathfinder, serena が独立した MCP ツールとして動作する

ローカルモデルのパス失敗

手元では Claude / GPT-4より、7B〜32Bのローカルモデルでパスの失敗が多く見られました。

確認した失敗の例です。

失敗パターン例
ディレクトリ名の typosrc/componets/Button.tsx → components
拡張子の取り違えconfig.yaml → 実際は config.yml
階層の省略utils/helper.go → 実際は internal/pkg/utils/helper.go
類似ファイル名の混同auth/login.ts vs auth/login.test.ts

エラーと再試行で context が増えます。ツール側で解決し、再試行の回数と入力を減らす狙いです。

正確なパスを使う指示だけでは解消しなかったため、ツールで補う方針にしました。parameter 数だけを原因として切り分けた検証ではありません。


アーキテクチャ

stdin/stdout の JSON-RPC 2.0で、Claude Code / Zed Editor などと通信する Rust 製 MCP です。

三段階のパス解決

パスを3段階で絞り込みます。

  1. レキシカルスコアリング — 内部実装は非公開です。1ms未満です
  2. 履歴の相関 — 直近の結果を ring buffer に残し、作業に近い候補を優先します
  3. ColBERT再順位付け — 上位候補を MaxSim で並べ直します。約5–8msです

レキシカルスコアが十分高ければニューラル再順位付けを省きます。多くのクエリは1ms未満で返ります。

ColBERT モデル

ColBERT(Late Interaction)を再順位付けに使います。

項目値
モデルlateon-code-edge(コード特化)/ mxbai-edge-colbert(汎用)
パラメータ数約 17M
量子化INT8(ONNX Runtime)
モデルサイズ約 16MB
埋め込み次元128
推論CPU のみ(GPU 不要)

ONNX Runtime と HuggingFace Tokenizers を使い、Python には依存しません。

MCP ツール

主なツールです。

  • path_resolve — 失敗したパスから実在パスの候補を返します。intent_text(「Go config loader」などの短い目的記述)で候補を絞ります
  • tool_retry_with_resolve — パスを解決し、元の操作(read_file、list_dir 等)を自動リトライします
  • candidate_list — 上位候補をリストで返す。曖昧なケースのブラウジング用
  • roots_list / reindex_paths / server_version — 管理系ツール

read_file の ENOENT後に tool_retry_with_resolve を使うと、パス解決と操作の再試行を1回の tool_callで行えます。

パス解決の実例

ディレクトリ名を間違えた例です。

  path_resolve:
  check_path: "content/ja/docs/tech/infrastrcture/podman-quadlet-systemd-ubuntu.md"
                                      ^^^^^^^^^^^ typo
  →  resolved: "content/ja/docs/tech/infrastructure/podman-quadlet-systemd-ubuntu.md"
  
  $ pathfinder --help
pathfinder — semantic path finder & MCP resolution server

USAGE
    pathfinder [OPTIONS]          Interactive semantic directory finder (default).
    pathfinder --mcp [OPTIONS]    Start as an MCP server.

FINDER OPTIONS
    --include-builds    Include build/artifact dirs (target, dist, …).

MCP OPTIONS
    --root <PATH>       Add a project root directory to watch and index.
                        May be specified multiple times.  Defaults to $PWD.

GENERAL OPTIONS
    -h, --help          Print this help message and exit.
    -V, --version       Print version, model, and PCA config to stderr and exit.

MCP TOOLS
  1. path_resolve            Resolve a failed file path to the best match.
  2. tool_retry_with_resolve Resolve + retry the operation in one call.
  3. roots_list              Return configured root directories.
  4. reindex_paths           Force a full index rebuild.

ENVIRONMENT VARIABLES
    PF_MCP_INFERENCE    Inference mode: "general" (default) or "code".
    Models (both INT8 quantized):
      general → mxbai-edge-colbert (17M, 48-dim)
      code    → lateon-code-edge (17M, 48-dim)
  

CLIでのプロジェクト検索

同じ ColBERT 推論を使う対話 CLIです。domain / infra / presentation を含む複数プロジェクトを検索します。

操作体系

pf(コード特化)または pfg(汎用)で検索画面を開きます。

  • vim キーバインド で候補リストをナビゲート
  • ディレクトリ階層をたどり、最深部でファイル一覧を表示します
  • 右キー(→) で選択ファイルを less で開く
  • 結果を選択すると、そのディレクトリに cd します
  pf          # コード特化モード(lateon-code-edge)
pfg         # 汎用モード(mxbai-edge-colbert)
  

ModernBERT の Rust 実装で、体感ではほぼ即時に検索結果が返りました。

pathfinder CLI: pf コマンドでセマンティック検索し、ディレクトリを選択して cd する
pathfinder CLI — セマンティック検索でプロジェクトを横断的に俯瞰し、選択結果のディレクトリに cd する

ベンチマーク: filesystem MCP との比較

約4,000ファイルのサンプルで filesystem MCP と比べました。

パス解決精度

単体の精度テストは70ケース、12カテゴリです。

カテゴリ内容結果
正しいパスそのまま返る8/8
ディレクトリ typocomponets → components10/10
ファイル名 typo文字の入れ替え・欠落10/10
拡張子の取り違え.yaml → .yml7/7
階層の省略中間ディレクトリが欠落5/5
intent ベースクエリ目的記述からの推定4/5
リトライ操作resolve + retry の一体動作3/3
紛らわしいパスペア類似名ファイルの区別6/6
深いネスト8 階層以上4/4
言語横断クエリ「Go の設定ファイル」等4/6
テスト/設定ファイルtest, config の区別4/4
合計67/70 (95.7%)

コンテキスト消費量

精度差は僅差でしたが、context 消費は約10,000 tokens少なくなりました。

filesystem MCP の祖先確認は、list_directory の出力がファイル数とともに増えます。今回の4,000ファイルを超える規模は未検証です。

pathfinder は候補を絞って返します。8K〜32Kの window で不要な context を減らすための方式です。


リソースフットプリント

項目値
バイナリサイズシングルバイナリ(Rust)
モデルサイズ約 16MB(INT8)
メモリオーバーヘッド50MB 未満
GPU VRAM 消費ゼロ
典型的レイテンシ10ms 未満(レキシカルのみなら 1ms 未満)

GPU を使わないため、vLLM / llama.cpp の VRAM に追加の領域は必要ありません。CPU 資源は使います。


注意事項

  • レキシカルスコアリングは非公開です。ctree とともに自社の OSS LLM pipeline で使っています
  • 70ケースのうち、intent_text で言語を指定する2件が未解決です
  • 数万ファイルの規模は未検証です
  • モデルのコード特化 / 汎用は PF_MCP_INFERENCE で切り替えます
  • MCP 2024-11-05に対応し、Claude Code / Zed Editor などから使えます