この記事について

familiar のオーケストレーションで使っている MCP ツールは、現時点で9本。全部 Rust、全部 stdio JSON-RPC 2.0。ほぼ1日に1-2本作っては改良するペースで、廃棄したものもいくつもある。この記事では現在生き残って稼働している9本の設計と目的を書く。

Zed の MCP サーバー一覧 -- 9本全てグリーン
Zed の MCP 設定画面 -- 全サーバーが接続済み。合計ツール数は 41

全体像

これらのツールが動いている homelab の全体構成:

homelab 4ノード構成図 -- edge, storage, desktop, compute
homelab の全体構成。edge (RouterOS), storage (Ubuntu 24/7), desktop (macOS), compute (EPYC + Blackwell x2)。MCP サーバー群は desktop 上で稼働し、compute の LLM バックエンドと storage のデータ基盤に接続する
MCP サーバーツール数一言
voracle4Obsidian vault セマンティック検索 + ウェブリサーチ
argus1クロスリポジトリ コードインテリジェンス
ctree8コードツリー構造解析 + チェックポイント
dgs17ひみつ
memento2ひみつ
okitegami1Discord 置き手紙
smng2homelab シークレット管理
sqlimit-15read-only DB 探索

resolve-inference(パス解決エンジン)は現在 familiar 本体に統合されたため MCP としては独立していないが、設計経緯は記録しておく。


voracle – 外部記憶装置

別記事で詳しく書いたので要点だけ。Obsidian vault 向けセマンティック検索 + ウェブリサーチ MCP。ONNX embedding + ColBERT reranking をローカルで動かす。research コマンドで Brave Search API 経由のウェブ知見が vault に自動蓄積される。

ツール: search, read, research, related


argus – クロスリポジトリ コードインテリジェンス

Gitea の全リポジトリをインデックスして、クロスリポジトリでシンボル検索するための MCP。tree-sitter でシンボルを抽出し、ColBERT MaxSim でリランキングする。

  CLI (index + embed)           MCP Server (query)
     |                              |
     v                              v
[Gitea API: repos/search]    [CWD auto-index on startup]
     |                              |
     v                              v
[git clone/fetch]             [argus.see tool]
     |                              |
     v                              v
[tree-sitter: symbols]        [adaptive lexical filter -> ColBERT rerank]
     |                              |
     v                              v
[ColBERT embed (batch)]       [Gemini summarize]
     |                              |
     v                              v
[redb: symbols+embeddings]    [hit tracking + NATS pulse]
  

embedding は3層構造:

  1. Batch: argus embed でインデックス時に全シンボルを事前計算
  2. Hot-hit: アクセス3回以上のシンボルはクエリ時に再 embed して鮮度を保つ
  3. On-demand: 未キャッシュのシンボルはクエリ時に embed して永続化

ツール: see(期待するシンボル・モジュール・仕様を指定してリポジトリ横断検索)


ctree – コードツリー構造解析

コードベースのシンボル構造・依存関係・リビジョン管理を行う MCP。.ctree.toml でスコープを定義し、check で構造を解析、checkpoint で作業の節目を記録する。

ツール: check, checkpoint, get_affected, get_depends, get_symbol, get_text, get_revs, get_baseline

familiar の開発ワークフローでは、コード変更のたびに checkpoint を呼んでリビジョンを残し、get_affected で影響範囲を把握してから実装に入る、という流れが定着している。実際、エージェントの作業ログを Obsidian で検索すると ctree__checkctree__get_symbol の呼び出しが至るところに出てくる。

呼び出し回数は多いが、返ってくるのはほとんど5tok程度のreplyかシンボル名とハッシュだけで、コード本体は含まない。エージェントはまずハッシュで構造を把握して、本当に必要なコードだけ get_text でオンデマンドにコンテキストへ注入する。これでコンテキストウィンドウの消費を最小限に抑えながら、コードベース全体の依存関係を見渡せる。これが安定したころからserenaが不要になった。以前はserenaと協調して使っていたのだけど、自前のgiteaとdistribution(registry)いれたあたりから、argusを使ってiac,僕のコードベースからアーキテクチャ・設計思想・ノウハウもよく理解してコーディングできている。

Obsidian の作業ログで ctree__check を検索した結果 -- エージェントが thinking で計画を立てた直後に ctree のツールを連続で呼び出している
エージェントの作業ログ。thinking の直後に ctree__get_symbol, ctree__get_affected, ctree__check, ctree__get_depends, ctree__get_text を連続で呼んでいる

dgs – 17 tools (non-public)

9本の中で最大のツール数を持つ MCP。設計と実装の詳細は非公開。


memento – 2 tools (non-public)

AI エージェントの管理に関わる MCP。設計と実装の詳細は非公開。


okitegami – Discord 置き手紙

LLM エージェントが作業の節目で Discord に通知する MCP。ツール1つだけ。

設計思想は「メッセージ」ではなく「置き手紙」。対話は望んでいない。ただ手紙を置いておいてほしい。自分も何かあれば手紙を置いておくから、置き手紙をしたときにあれば読んでくれたら – そういう距離感で作った。

  1. いまの作業結果を Discord に置き手紙する
2. そのついでに、過去の手紙に返信がついていれば読む
3. 返信がなければ何もしない
  

発火条件は「返信を見に行く」ではなく「新しい手紙を置く」。ポーリングループにならない設計。

okitegami の Discord スレッド -- tegami APP による置き手紙とksh3のリプライ
tegami が置いた手紙のスレッド。summary, worked, result, concerns, tests が構造化されている。ksh3 が数時間後にリプライしている

実際の使い方はこう。エージェントが開発中に #tegami チャンネルにスレッドを作って進捗を置いていく。自分はたまに見返して、思いついたことがあればリプライしておく。1ヶ月後にリプライしても問題ない。次にエージェントが tegami を呼んだとき、「1ヶ月前に実装した件、ksh3 が指摘していたけどどうする?」と聞いてきたり、勝手に直してくれたりする。

非同期のフィードバックループだが、チャットではない。手紙の時間軸で動く。急ぎの返事は期待しないし、返事がなくても正常。この距離感が、長時間の自律作業と人間の生活リズムをうまく噛み合わせてくれる。

ツール: tegami


smng – シークレット管理

homelab サービスのクレデンシャル・エンドポイント・接続情報を管理する MCP。*.home.arpa ネットワーク内のサービス認証情報を安全に取得する。

ツール: list_services, get_secret


sqlimit-1 – read-only DB 探索

familiar のメイン DB だけを対象にした、クエリ結果1行限定のコンテキストセーフな MCP。核心は「LLM に LIMIT 1 を書かせない」こと。ツールが無条件にサブクエリでラップする:

  SELECT * FROM (<user_sql>) AS _q LIMIT 1
  

read-only は接続レベルで保証。PostgreSQL の default_transaction_read_only = on を設定した専用ユーザで接続するので、ツール側でフィルタリングする必要がない。非 SELECT 文はそもそも拒否する。

接続情報は smng(シークレット管理)からも取れるが、sqlimit-1 が担うのは DB 接続だけではない。スキーマのバージョン管理も兼ねている。dump_schema でスキーマを取得し、変更があればスキーマバージョンを作ってくれる。少ないコンテキストでさっとデータ構造を理解して、変更のついでにバージョンを残す – そういう使い方に特化させた。

何でもできるツールより、一つのことを上手にやるツールのほうが好きだ。smng は認証情報を返す。sqlimit-1 はスキーマを理解する。それぞれの責務が明確なほうが、組み合わせたときに壊れにくい。

ツール: tables, describe, sample, query1, dump_schema


resolve-inference – ENOENT パス解決エンジン(現在は familiar に統合)

LLM がファイル操作でパスを推測して ENOENT エラーを起こす問題を解決するために作った。ファイルシステムインデックスと字句スコアリング + ColBERT MaxSim で正しいパスを返す。

精度のブレイクスルーはモデルではなくクエリ履歴だった。直近5件の解決パスをリングバッファに持ち、ディレクトリアフィニティとパッケージアフィニティで候補をブーストする二層構造:

  Go モノレポ(106ファイル、client.go が4-11箇所に存在):
  履歴あり: 85.0% (17/20)
  履歴なし: 35.0% (7/20)
  差分:     +50.0pp
  

3つの ColBERT モデルをアンサンブルで評価するフレームワークも作ったが、3モデルとも同一精度(87.3%)でレイテンシだけ3.7倍に。精度のボトルネックはモデルではなく字句アルゴリズムにあった。アンサンブルを廃棄してクエリ履歴相関に注力したのが正解だった。


廃棄したツールたち

9本が稼働している裏で、いくつかのツールを廃止している。ただ「捨てた」というよりは、ほとんどが今のツールに統合されたか、機能を借用して生まれ変わっている。resolve-inference が familiar 本体に入ったのもそうだし、初期の shelpa(ファイルシステム MCP)のアイデアは ctree と dgs に分散して吸収された。

なんといっても、作るのがすごく楽しい。Rust で MCP サーバーを書くのは1日あれば形になるし、stdio JSON-RPC 2.0 のプロトコルは単純だから、思いついた翌日には動くものが手元にある。使ってみて微妙なら捨てる。良ければ磨く。このサイクルが回っている限り、ツールは増え続ける。


共通する設計原則

全ツールに共通しているのは:

  • Rust + stdio JSON-RPC 2.0: プロセス起動が速く、メモリフットプリントが小さい
  • 単機能: 1つのツールが1つの問題を解決する。多機能にしない
  • LLM エージェントの実務から生まれた: 「こういうときに困る」から逆算して作っている
  • グレースフルデグラデーション: ツールが落ちても他のツールは動く。familiar 本体のフローが止まらない

41ツールが9プロセスで動いていて、全部合わせてもメモリ使用量は数十MB。Rust のバイナリサイズとランタイムオーバーヘッドの小ささが、こういう多数の小さなプロセスを並走させるアーキテクチャで効いてくる。