rag-mcp について
rag-mcpは、Agent向けのローカルコーパス検索MCPサーバーです。Markdown文書をローカル埋め込みとベクトル検索で調べ、出典付きの文章を返します。増分インデックス更新とコーパスルートの検証にも対応します。
主な機能
- 01ローカル埋め込みとベクトル検索
- 02出典・見出し・チャンク番号の引用
- 03増分更新と古いチャンクの削除
- 04noteまたはhandoffによる絞り込み
- 05コーパス範囲の制限と構造化エラー
活用シーン
- 01ローカルMarkdown文書から根拠箇所を検索
- 02検索結果を出典や見出しまで追跡
- 03文書の変更に合わせてインデックスを維持
MCP 対応
stdio MCPサーバーとして `search_knowledge(query, k, doc_class=None)` ツールを公開し、設定済みのローカルベクトルストアを検索して引用付きの文章を返します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
rag-mcp
最小限で誠実なコーパスを対象とする RAG MCP 検索ツールです。クエリを埋め込み、ローカルコーパスをベクトル検索して、回答の出典をたどれるように、引用(ソース+見出し+チャンクインデックス)付きの抜粋を返す単一のツール
search_knowledge(query, k, doc_class=None)を提供します。
mcp-factory のマニフェストモデルに組み込めるように設計されています。完全ローカルで、$0(有料の埋め込み API は不要)です。
実際のコーパスの前に安心して配置できる理由
- 引用付き — すべての検索結果に
source+heading+chunk_indexが含まれます。 - 認証スコープ内 — 結果は設定されたコーパスルート内に限定されます。ルート外に出るソース(絶対パス、
..による移動)は拒否されます。 - フェイルソフト — ストアが停止している、または空の場合は構造化エラーを返し、呼び出し元のエージェントをクラッシュさせる例外は発生しません。
- 上限あり —
kは[1, 20]の範囲に制限され、空のクエリは拒否されます。 - バージョン固定の依存関係(
requirements.txt)。
スタック
| レイヤー | 選択肢 |
|---|---|
| 埋め込み | ローカル ONNX all-MiniLM-L6-v2(384 次元、CPU、$0)— デフォルト。bge-large-en-v1.5(1024 次元、512 トークンのコンテキスト)も RAG_MCP_EMBEDDER=bge でオプトインできます。CUTOVER.md を参照してください。 |
| ベクトルストア | ChromaDB 組み込みの PersistentClient(インフラ不要) |
| サーバー | mcp Python SDK 2.x、stdio トランスポート、プロトコルリビジョン 2026-07-28 |
プロトコルリビジョン
最初に MCP プロトコルリビジョン 2026-07-28 を実装した SDK リリースである mcp==2.0.0 に固定されています。サーバーは同じ stdio 接続で両方の世代に対応し、クライアントの最初のフレームに応じて選択されます。
| クライアントが開始する方法 | ネゴシエートされたリビジョン | 備考 |
|---|---|---|
リクエストごとの _meta エンベロープ(または server/discover プローブ) | 2026-07-28 | リクエストごとのステートレスなエンベロープ。initialize は不要です |
従来の initialize ハンドシェイク | 2025-11-25 | ハンドシェイク世代で対応する上限です。ダウングレードではなく、想定どおりです |
2026-07-28 は initialize ハンドシェイクでは利用できません。これは server/discover またはインラインの _meta バージョンスタンプを通じて到達する「モダン」なリビジョンです。世代の選択は接続ごとに自動で行われ、サーバー側のフラグはありません。
tests/test_protocol_version.py では両方の経路をエンドツーエンドで検証しているため、依存関係をロールバックしてサーバーが古いリビジョンにひそかに戻ると、CI はそれを黙って通さず失敗します。
クイックスタート
python -m venv .venv && .venv/Scripts/python -m pip install -r requirements.txt
# Ingest a corpus (markdown). Incremental by default: only files whose content
# changed since the last run are re-embedded.
python -m rag_mcp.cli ingest path/to/docs --db ./store.chroma
# Force a rebuild in place (ignore the manifest, re-embed everything)
python -m rag_mcp.cli ingest path/to/docs --db ./store.chroma --full
# One-off query (corpus root = the auth scope)
python -m rag_mcp.cli query "your question" --db ./store.chroma --corpus path/to/docs -k 5
# Same, restricted to one doc_class ("note" or "handoff" -- see "Filtering by
# document class" below)
python -m rag_mcp.cli query "your question" --db ./store.chroma --corpus path/to/docs --doc-class note
# Run as an MCP server (stdio); configure via env first
# RAG_MCP_CORPUS_ROOT, RAG_MCP_DB_PATH, RAG_MCP_COLLECTION, RAG_MCP_EMBEDDER
python run_server.py # operational entrypoint (referenced by mcp.yaml)
python -m rag_mcp # same server, via the packaged console entry point
rag-mcp # after `pip install jaimenbell-rag-mcp` -- console script
インデックスを最新に保つ(増分取り込み)
取り込みはデフォルトで増分処理です。ストアディレクトリ内のマニフェストに各ファイルのデコード済みテキストの SHA-256 が記録されます。実行時には実際に変更されたファイルだけを再埋め込みし、upsert だけでは処理できない削除・名前変更されたノートのチャンクや、短くなったノートの末尾のチャンクも削除します。
稼働中の 2,808 ファイル/26.6 MiB のコーパス(bge、CPU)での測定値:
| 実行 | 所要時間 |
|---|---|
| 変更なしの実行 | 約 0.7 秒(すべてのファイルの走査+読み込み+ハッシュ計算) |
| 全件再埋め込み | 約 2 時間 33 分(50,109 チャンク、約 5.5 チャンク/秒) |
これにより、高頻度のスケジュール実行が現実的になります。reingest.bat は、毎日 03:00 に 1 回実行するのではなく、15 分ごとに実行することを想定しています。以前の方式では、03:05 に書かれたノートが search_knowledge からほぼ 24 時間見えない状態になっていました。
マニフェストが信頼されるのは、実行 ID(埋め込みモデル、埋め込み次元、コレクション、チャンク分割パラメーター)が一致する場合のみです。いずれかを変更するとすべてのファイルが再埋め込みされるため、埋め込みモデルの切り替えが気付かないうちに一部だけ適用されることはありません。マニフェストがない、破損している、一致しない、または空のストアに対するものである場合は、いずれも全件再構築に切り替わります。誤ってスキップすることはありません。
スナップショットの重複排除
マニフェストによるスキップはファイル全体のハッシュに基づくため、検索を実際に妨げる重複は検出できません。日次スナップショットの系列(fleet-health-2026-07-23.md など)では、ファイルのハッシュ自体は変わっていても、前日の段落がそのまま繰り返されます。実際のボルトで測定したところ、## RED Bots のステータス行1つは、連続する15ファイルの中で5つの異なる値を取り、バイト単位で同一のコピーが検索結果の上位10件を埋め尽くして、その行の説明が書かれたドキュメントを16位に押し下げていました。
そのため、取り込みではチャンク単位でも重複排除を行います。ただし、日付付きの系列内に限り、直前のスナップショットとの間だけで行います。最初に出現したものは必ず埋め込み、独自の日付を source として保持します。それ以降の完全一致する繰り返しは埋め込まず、代わりに残されたチャンクの repeat_dates メタデータを拡張します。これは search_knowledge が snapshot_date/also_unchanged_on/snapshots_covered として返します。つまり、「日付 X にこれは何と書かれていたか」という質問には引き続き回答できます。そのため、系列を除外せずに重複排除します。値が変わった後で元に戻った場合は、繰り返しではなく新しい事実なので保持されます。
対象範囲は限定されており、ルールは rag_mcp/snapshots.py に記載されています。ファイル名が -YYYY-MM-DD で終わり、同じディレクトリと語幹を共有するファイルが3つ以上あり、見出しが同一であることが条件です。実際のコーパスでは、2,814ファイル中316ファイルが対象となり、50,428チャンクのうち842チャンクが削減されます(系列内チャンクの17.5%、コーパス全体の1.67%)。通常のノートには一切影響しません。--no-snapshot-dedupe で無効にできます。
--full はインプレースで再構築します(マニフェストは無視しますが、ストアは保持します)。--clean は最初にストアを削除します。どちらも引き続きマニフェストを書き込むため、次回の実行は短時間で済みます。reingest-clean.bat(週次)は、引き続き念には念を入れるためのリセットとして使います。
MCP サーバーとして
mcp.yaml を使って登録します(mcp-factory の Manifest ローダーで検証されます)。ツールは search_knowledge(query, k, doc_class=None) です。RAG_MCP_* 環境変数で設定されたストアを読み込みます。
ドキュメントクラスによるフィルタリング
各チャンクのメタデータには、取り込み時に設定される doc_class が含まれます。ドキュメントの YAML フロントマターに type: handoff または handoff のタグ項目がある場合(大文字と小文字は区別しません)、値は "handoff" になります。また、ドキュメントのフロントマターは省略可能であり、セッションミラーには、目印とすることが多いこの情報が含まれないため、ファイルが handoff_mirror_dir(デフォルトは context/。ingest() の handoff_mirror_dir=/handoff_mirror_basenames= パラメーター、または CLI の --handoff-mirror-dir/--handoff-mirror-basename フラグで設定可能)の直下にあり、ファイル名が handoff.md/active.md/resume.md である場合、あるいは先頭に固定された「handoff」ファイル名トークンに一致する場合(例:handoff-2026-09-03.md、morning-dispatch-handoff.md)も該当します。単なる部分文字列の一致は対象外です。そのため、タイトルにこの単語が含まれているだけのファイル(handoff-skill-redesign-spec.md)は "note" のままです。それ以外はすべてデフォルトで "note" になります。クエリに doc_class を渡すと、クラスを指定して検索できます。たとえば、ナレッジ検索でエージェント自身のセッション/ハンドオフ関連の記録を除外するには、次のようにします。
from rag_mcp.search import search_knowledge
search_knowledge(
"what did we decide about X", k=5, store=store, corpus_root=root, doc_class="note",
)
doc_class は検証済みの大文字と小文字を区別する列挙値で、"note" または "handoff"(rag_mcp.search.ALLOWED_DOC_CLASSES を参照)を指定するか、フィルターなしの場合は省略します。この集合に含まれない値(大文字と小文字の誤り、タイプミス、その他の型)は、invalid_query と同じ形式の構造化エラー invalid_doc_class を返します。構文上有効な doc_class でも、現在のストアに一致するものがない場合は、エラーにせず空の ok: true 結果を返します。
増分取り込みの実行時には、すでに埋め込み済みで内容に変更のないチャンクに対して、再埋め込みを行わずに doc_class(およびその他のメタデータスキーマの変更)が反映されます。詳しくは ingest.CURRENT_METADATA_VERSION を参照してください。この機能が追加される前のストアでも、フルの --clean 再構築は不要で、増分実行を1回行うだけで全チャンクに正しい doc_class が設定されます。
テスト
python -m pytest # 272 passed
この数は収集されたテストの総数であり、実行中の pytest のサマリーに対して CI でチェックされています
(scripts/check_readme_counts.py。.github/workflows/ci.yml に組み込まれています)。テストのうち
1 件はプラットフォームに依存します。tests/test_reindex_handle_release.py の
WINDOWS_ONLY は Windows でのみ実行されるため、Linux CI では同じ 272 件のテストスイートで
271 件が成功、1 件がスキップと報告されます。このゲートでは成功数だけではなく、成功数とスキップ数の合計をこの記載と比較するため、想定どおりのスキップが差異として検出されることはありません。
構成
rag_mcp/
chunking.py heading-scoped, overlapping markdown chunks
store.py VectorStore (Chroma) + Embedder protocol (MiniLM default + BgeEmbedder opt-in + offline HashEmbedder)
ingest.py idempotent ingest pipeline with source/heading/chunk-index metadata; incremental by default
manifest.py per-file content hashes -> skip unchanged files, prune stale chunks
search.py search_knowledge: cited, auth-scoped, fail-soft, bounded
server.py MCP stdio server exposing search_knowledge
config.py env-driven Config
cli.py ingest + query CLI
__main__.py console entrypoint (`python -m rag_mcp` / `rag-mcp` script); fails loud on missing config
run_server.py operational MCP entrypoint (referenced by mcp.yaml)
mcp.yaml manifest (mcp-factory model)
商用サポート
jaimen Bell が保守しています。本番環境向けの MCP 連携、カスタムサーバー、またはエージェントの信頼性向上に関するご相談は、 jaimenbell.dev をご覧ください。
独自の MCP サーバーを構築していますか?MCP Starter Kit には、テンプレート、構築ガイド、そしてこのサーバーの公開で得たパッケージングの実践知が含まれています。
mcp-name: io.github.jaimenbell/rag-mcp
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。