Skip to content

feat(mcp): add vector_ids to search for stored-content retrieval [mcp, bridge, docs, tests] - #243

Merged
smileygames merged 1 commit into
mainfrom
239-featmcp-add-vector_ids-to-search-for-stored-content-retrieval
Aug 14, 2026
Merged

feat(mcp): add vector_ids to search for stored-content retrieval [mcp, bridge, docs, tests]#243
smileygames merged 1 commit into
mainfrom
239-featmcp-add-vector_ids-to-search-for-stored-content-retrieval

Conversation

@smileygames

Copy link
Copy Markdown
Member

概要

searchvector_ids パラメータを追加し、索引が既に保持している本文を返せるようにした。従来は doc / wiki_doc 以外の型に本文を返す手段が無く、search であたりを付けたあと gh や grep で取り直す一往復が必要だった。

Closes #239

設計判断

専用 tool は追加していない。 issue の「過去の判断」節どおり、#104 / #105 で 4 tool を search 1 本へ統合した判断を維持する。本文取得の専用 tool は削除された get_doc_content の復活そのものになるため、統合時の設計意図(モードはパラメータ集合で表現する)に乗せた。

優先順位は vector_ids -> query 空 -> hybrid search。fetch 呼び出しは query を持たないため、空クエリ判定より前に分岐しないと scan mode に落ちる。fetch mode では metadata filter を適用しない(行はサーバが選ぶのではなく呼び出し側が名指しするため)。

include_content とは統合していない。取得元(GitHub contents API / D1)も上限の根拠(API fan-out / 呼び出し側が挙げた id 数)も違うため、1 つのフラグに載せると 1 つの保証で 2 つの挙動を覆うことになる。既存挙動は無変更。

実装

  • src/fetch.ts(新規) — D1 search_docs の保存済み content を 1 回のバッチクエリ(既存の getDocsByVectorIds)で読む。GitHub API は呼ばず subrequest も増えない。scan.ts と同じ理由でモジュールを分けた(MCP server を立てずに実 D1 に対して検証できる)。
  • 索引由来 / truncate 済みの明示 — 応答の top-level に content_source: "index"content_max_chars、各行に content_charscontent_truncated。フラグは取り込み時の記録ではなく長さから導いているため、自然長がちょうど 8000 文字の本文も truncate 済みとして報告する。この向きの誤りは不要な読み直し 1 回で済み、逆向きは断片を完全な本文として通してしまう。
  • 部分成功 — 未知・失効 id は not_found に載せ、残りの行は返す。vector_id は永続識別子として約束しない(pipeline/legacy-vector-id.ts に採番移行の履歴がある)。D1 read 自体が失敗した場合は「全件 not_found」ではなく isError を返す。読み切れていない read に「行が存在しない」と主張させないため。
  • vector_id の露出search の全 result 行に加え、same_entity.others の各要素にも載せた。others まで載せたのは、実体集約が comment / review / 旧 diff をそこへ畳むため、無いと本モードが本来返すべき行だけが到達不能になるからである(issue の acceptance は result 行のみを挙げているが、機能の目的に対して穴になる)。
  • bridge 同期mcp-server/server/tools.js の静的 schema mirror と manifest.json を同期。scripts/check-schema-drift.mjs が CI gate として効いている。

テスト

  • src/fetch.test.ts(node pool)— truncate 判定の境界、文字数(バイトではない)、型ごとの identity カラム付与。
  • src/fetch.workers.test.ts(workers pool / 実 D1)— 実 ingest writer upsertFtsRow で 9 surface を書き、実 read で本文が返ることを型ごとに確認。truncate フラグ、部分成功、重複除去、空リスト。
  • src/mcp-stateless-contract.test.ts — 配信される schema に vector_ids が載ること、description が index 由来 / 8000 / content_truncated / not_found / 非永続識別子を述べていること。
  • mcp-server/test/search-tool-schema.test.js — client が実際に読む proxy 側 schema に同じ記述があること。

npm test(270 + 70 pass)/ mcp-servernpm test(12 pass)/ drift-check / tsc --noEmit / wrangler deploy --dry-run いずれもローカルで green。

acceptance 対応

条件 状態
search の結果各行に vector_id が載る 済(same_entity.others にも)
vector_ids で 7 型の本文が返る 済(doc / wiki_doc を含む 9 surface を D1 テストで確認)
GitHub API 呼び出し数が増えていない 済(fetch 分岐は GitHub 経路の手前で返す。inlineDocContent は無変更)
索引由来・truncate 済みが判別できる
未知 vector_id が混ざっても残りは返る
include_content の既存挙動が変わっていない 済(コード未変更、テスト green)
MCP tool 数が 1 のまま 済(tools/list["search"] であることをテストで確認)

非スコープ

  • 保存済み content が embedding input と同一値であるため 8000 文字上限が embedding と保存の判断を兼ねている件は、既存行の再 index が必要なので issue どおり別範囲とした。要件記述にも非スコープとして明記した。
  • scan mode の行は structured store 由来で索引キーを持たないため vector_id が載らない。tool description と要件記述の双方に明記した。

version type

minor と判断。client が読む schema(proxy mirror)に新パラメータが載り、全 result 行に新フィールドが増えるため user/system observable であり、Worker と bridge が揃って出る必要がある。内部構造だけの変更ではない。

🤖 Generated with Claude Code

…, bridge, docs, tests]

`search` に `vector_ids` パラメータを追加し、索引が既に保持している本文を
返せるようにした。従来は `doc` / `wiki_doc` 以外の型に本文を返す手段が無く、
`search` であたりを付けたあと `gh` や grep で取り直す一往復が必要だった。

設計は既存の導出作法に合わせた。モードは列挙パラメータで選ばせず、
パラメータ集合から導く。優先順位は `vector_ids` -> `query` 空 -> hybrid search。
専用 tool は追加しない。#104 / #105 で 4 tool を `search` へ統合した判断を維持する
(本文取得の専用 tool は削除された `get_doc_content` の復活そのものになる)。

- `src/fetch.ts` (new): D1 `search_docs` の保存済み content を 1 回のバッチ
  クエリで読み出す。GitHub API は呼ばない。`scan.ts` と同じ理由でモジュールを
  分けている(MCP server を立てずに実 D1 に対して検証できる)。
- 返す文字列が索引由来かつ 8000 文字 truncate 済みであることを応答が示す:
  top-level に `content_source: "index"` / `content_max_chars`、各行に
  `content_chars` / `content_truncated`。フラグは長さから導くため、自然長が
  ちょうど上限の本文も truncate 済みとして報告する(安全側)。
- 未知・失効 id は `not_found` に載せ、残りは返す。`vector_id` は永続識別子
  として約束しない(`legacy-vector-id.ts` の移行履歴があるため)。
- `search` の全 result 行と `same_entity.others` の各要素に `vector_id` を追加。
  others に載せるのは、実体集約が comment / review / 旧 diff をそこへ畳むため、
  無いと本モードが本来返すべき行だけ到達不能になるからである。
- `include_content` の既存挙動(contents API から完全なファイル、5 件上限)は
  変更しない。取得元も上限の根拠も違うため統合しない。
- bridge 側の静的 schema mirror (`mcp-server/server/tools.js`) と manifest も
  同期。drift-check が CI gate として効いている。

Closes #239
@smileygames smileygames linked an issue Aug 14, 2026 that may be closed by this pull request
@smileygames smileygames self-assigned this Aug 14, 2026
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
github-rag-mcp edb9c09 Aug 14 2026, 12:30 PM

@smileygames smileygames left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI self-review (auto mode, parent)

検証したもの

tool 数 — branch の src/mcp.ts で tool 登録は 1 件#104 / #105 の統合判断は維持されている。これが本 issue の load-bearing な制約だったので、最初に確認した。

モードの導出vector_ids の有無で分岐しており、列挙パラメータを新設していない。query が空なら scan という既存の作法と同じ形。commit 6acdbd3 の "three modes via its parameter set" の並びに 4 つめとして入っている。

上限の由来FETCH_CONTENT_MAX_CHARS = MAX_EMBEDDING_INPUT_CHARS。リテラルの複製ではなく導出。ingest 側の truncate 値が変われば追随する。

D1 失敗と not_found の切り分け — ここが最も評価できる判断。読み取り失敗を「全件 not_found」で返さず error にしている。コメントの理由づけがそのまま正しい。

an empty results with every id in not_found would assert the rows do not exist, which a failed read gives no ground to claim.

証拠の不在と不在の証拠を混同しないという区別が実装に落ちている。issue の制約は「部分成功」としか書いていなかったので、これは仕様を超えて正した側。

provenancecontent_source: "index"content_truncatedcontent_max_chars が wire に載る。truncated 判定を長さから導いているため、実長がちょうど 8000 の本文は truncated と報告される。誤る向きが「余計な再読み込み」側であり、prefix を全体と誤認する側ではない。向きが正しい。

same_entity.others への vector_id 付与 — issue の受け入れ条件は「結果各行」までしか書いていない。ただし entity 集約が comment / review / 旧 diff を others に畳む以上、それらを付けなければ本文取得の対象になる行だけが引けないことになる。範囲外だが、範囲の書き漏らしを正した側と判断する。受け入れる。

受け入れ条件

criterion 結果
search 結果各行に vector_id passothers にも)
vector_ids で各型の本文が返る pass
GitHub API 呼び出しが増えていない pass — D1 のみ
索引由来・truncate 済みが判別できる pass
未知 id が混ざっても残りが返る pass
include_content の既存挙動が不変 pass
MCP tool 数が 1 のまま pass

scope 逸脱

mcp-server/server/tools.js と manifest / README の同期。bridge が tools/list を手書きの静的ミラーから返しており、scripts/check-schema-drift.mjs が CI ゲートになっているため、Worker 側だけ足すとクライアントからパラメータが消える。同一 PR で直すのが正しい。受け入れる。

申し送り

scan モードの行は vector_id を持たない(構造化ストア由来で索引キーが無い)。実装せず、tool description と要件仕様の両方に記載する形で処理されている。制限として明示されているので可。

next step

auto mode のため human check なし。self-review pass をもって AI が直接 merge する。

@smileygames
smileygames merged commit bbef84e into main Aug 14, 2026
3 checks passed
@smileygames
smileygames deleted the 239-featmcp-add-vector_ids-to-search-for-stored-content-retrieval branch August 14, 2026 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(mcp): add vector_ids to search for stored-content retrieval

1 participant