Skip to content

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

Description

@smileygames

purpose

searchvector_ids パラメータを追加し、索引が既に保持している本文を取り出せるようにする。現在 doc / wiki_doc 以外の型には本文を返す手段が一切なく、search であたりを付けたあと gh や grep で取り直す一往復が必要になっている。普段使いの retrieval コストの問題である。

過去の判断(先に確認済み)

専用 tool を追加する案を検討し、退けた#104 / #105 で 4 tool を search 1 本へ統合した判断があり、専用 tool はその逆行になる。

削除された 3 tool: get_issue_context / get_doc_content / list_recent_activity本文取得の専用 tool は get_doc_content の復活そのものである。

統合時の設計意図(commit 6acdbd3 body より):

search_issues now exposes three modes via its parameter set: hybrid semantic search (existing), time-ordered activity scan (empty query + sort=updated_desc/created_desc + optional since/until), and inline doc content fetch (include_content).

「パラメータ集合でモードを表現する」形は意図された設計であり、事故ではない。したがって本 issue もその作法に乗る。

なお #104 は起票者アカウントの凍結により GitHub 上で参照できない(gh issue view 104 は 404)。上記の意図はローカル clone の commit body から復元した。commit body に意図を書く規約が、issue が読めなくなっても判断を保存していた事例である。

premise

本文は既に読み込まれていて、返す直前に捨てられている

src/mcp.ts の search 経路は reranker に食わせるために D1 FTS 行の content を読み込む(L773-820)。dense 側だけでヒットした候補には search_docs からのバックフィルまで行う(L788-806)。つまり結果組み立て時点で content はメモリ上にある。

しかし出力に載るのは doc / wiki_doc だけ。inlineDocContent(L1222)が対象を絞っており、非 doc 行は素通りする(tool description L429: "Non-doc rows are unaffected")。

対照的に graph 軸の buildGraphItem(L258)は includeContentrow.content を型を問わずそのまま返している。同じ include_content フラグで keyword 軸と graph 軸の挙動が割れている。

索引が保持している content の実体

全 surface が FTS に書いているのは embedding input そのもの。

surface 書き込み箇所 中身
issue / PR embed-issue.ts L190, L285 prepareEmbeddingInput(title, body)
comment / review embed-comment.ts L134, L247, L372 embedding input
release embed-release.ts L125 embedding input
doc / wiki_doc embed-doc.ts L82, L186 embedding input
diff embed-diff.ts L249 embedding input

いずれも MAX_EMBEDDING_INPUT_CHARS = 8000 で truncate されている。索引に尾は存在しない。

実測(Liplus-Project/liplus-language の issue body 10 件): 1,175 / 1,319 / 1,400 / 1,640 / 1,749 / 1,975 / 2,226 / 2,715 / 3,193 / 3,696 文字。8000 超は 0 件。issue 本文では上限がほぼ効かない。一方 diff 行は patch なので日常的に上限に当たる。

doc 経路が GitHub から取り直している理由

embed-doc.ts も content を D1 に持っているのに、include_content は contents API を叩いている。これは冗長ではなく、doc は完全なファイルが必要で 8000 文字の頭では足りないため。INCLUDE_CONTENT_MAX_DOCS = 5 はその API fan-out を抑える上限である。

したがって include_content と本 issue の経路を統合してはならない。取得元と上限の根拠が違う。

設計

既存の導出作法に合わせる。モードは列挙パラメータで選ばせず、パラメータ集合から導出する。

query が空          -> scan       (既存、L481 の isScanMode)
include_content     -> doc 本文 inline(既存、contents API 経由)
vector_ids が存在   -> 本文取得   (本 issue)

vector_ids が渡されたら、その行の保存済み content を返す。呼び出し側が対象を名指しするため、サーバ側が合計バイト数上限を発明する必要がない。フラグ方式で必要になる「どこまで返せば安全かの推測」がここでは発生しない。

vector_idsearch の結果各行に追加する。内部では既に主キー(L663 の vector_id -> payload lookup)。

ただし永続識別子として約束しない。 この repo には legacy vector id の移行履歴(src/pipeline/legacy-vector-id.ts、pre-migration generation)がある。「検索結果からその場で引くための一時的な取っ手」として description に明記し、採番変更で縛られない形にする。

応答の mode フィールドが現在 "search" / "scan" の 2 値であるため、本経路をどう報告するかは実装判断。

constraints

target files

  • src/mcp.tsvector_ids パラメータ、search 結果への vector_id 追加、tool description
  • docs/0-requirements.md / docs/0-requirements.ja.md — MCP Tool Surface 節
  • テスト — 型ごとに content が返ること、未知 vector_id の部分成功、truncate 表示、search 結果に vector_id が載ること

acceptance

  • search の結果各行に vector_id が載る
  • vector_ids を渡すと issue / pull_request / issue_comment / pr_review / pr_review_comment / release / diff の本文が返る
  • GitHub API 呼び出し数が増えていない
  • 索引由来・truncate 済みであることが結果から判別できる
  • 未知の vector_id が混ざっても残りは返る
  • include_content の既存挙動が変わっていない
  • MCP tool 数が 1 のまま

補足(この issue のスコープ外)

保存済み content が embedding input と同一値であるため、8000 文字上限は embedding のための判断が保存の判断を兼ねている状態にある。FTS 側に truncate しない本文を持たせるのが根本の形だが、既存行の再 index が必要で、範囲が別。ここでは「既に持っているものを返す」に限定する。

Metadata

Metadata

Assignees

Labels

enhancement新機能・改善要望ready本文が実装開始できる形まで収束している状態。ただし更新は継続可能review-pending実装フェーズ完了。orchestration (brake eval / review / merge / close) 待ち

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions