Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2329,6 +2329,10 @@ Piping `{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}` into
restart-required error. Its optional `maxBytes` parameter (4,096–1,000,000; default
1,000,000) bounds the full JSON-RPC envelope, and bounded omission and
continuation diagnostics are returned under `_meta.response_controls`.
The `initialize` instructions advertise these template and list controls, and
every `resources/list` result publishes the accepted extension parameters and
their bounds under `_meta.discovery_contract`, so AI clients do not need to
infer non-standard protocol extensions.
`resources/read` accepts optional inclusive `startLine` / `endLine`
ranges and a `maxBytes` UTF-8 text budget (4-byte minimum, 64 KiB by
default, 128 KiB maximum). Every page is also capped at 1,000 logical lines. Successful
Expand Down Expand Up @@ -5090,6 +5094,7 @@ sequenceDiagram
- 独立した stdio request と HTTP POST は、設定された MCP request 上限まで並行実行する(#4536)。実行 slot が全て使用中でも read loop は cancellation/client-response frame を受け続ける。accepted-frame backlog は execution 上限 + 64 に別途制限し、超過 request には retry-safe な `-32003` / `server_busy` を返す。request id は protocol/gate 待機前に登録し、execution timeout は slot 取得後に開始し、timeout 後も cancellation を無視して動く action は実際に drain するまで slot を保持する。initialize など session mutation の受信順は protocol barrier で維持し、可変な request state は `AsyncLocal` または request-scoped snapshot に置き、shared writer tool は直列化する。JSON-RPC batch の各 item も同じ global execution slot を個別に消費する(#4545)。基本 `IMcpTransport` loop は outer frame slot を確保しないため、`maxConcurrency: 1` の single request は `_concurrencyGate` を1回だけ取得し、single request と batch item は dispatch 時だけ slot を消費する。
- advertised capability には `tools`、`resources`、`prompts`、`logging` が含まれる。`resources/list` はインデックス済みファイルを `cdidx://file/<path>` URI としてページングし、世代対応の不透明 keyset cursor を返す。ページ間でインデックス済みファイルが変わった場合は、再開必須の stale-index error を明示的に返す。任意の `maxBytes`(4,096〜1,000,000、既定 1,000,000)で JSON-RPC envelope 全体を制限し、省略件数と継続理由を `_meta.response_controls` に有界な形で返す。`resources/read` は inclusive な `startLine` / `endLine` と UTF-8 本文の `maxBytes`(最小 4 byte、既定 64 KiB、最大 128 KiB)を任意指定として受け付ける。各ページは論理行 1,000 行でも上限化される。成功レスポンスは標準の `contents` item を維持し、`result._meta` に実効範囲、返却 byte 数、切り詰め理由、不透明な `nextCursor` を追加する。継続時は行境界を再送せず、その cursor と任意の新しい `maxBytes` を渡す。cursor は index 済みファイル版に結び付くため、resource 変更後は stale として失敗する。database reader は長い単一行を含め、managed response string を構築する前に incremental SQLite BLOB read で範囲と byte 上限を適用する。server は MCP レスポンス上限と active transport のレスポンス上限のうち小さい方から実効本文 budget を算出し、JSON-RPC envelope と最悪ケースの JSON escape に必要な領域を確保する。1 つの JSON-RPC batch に複数の `resources/read` call がある場合は aggregate frame 上限を共有し、各 item を frame の残り領域に合わせて budget 化する。page 化できない item が割当内に収まらない場合は、元の request ID を保持した構造化 `batch_response_budget_too_small` error に置換する。file metadata の取得、cursor 検証、chunk BLOB 読み取りは単一の deferred SQLite read snapshot 内で実行するため、並行 reindex によって異なる resource 版が混在しない。実際に空の index 済みファイルは空の成功レスポンスを返すが、非空 resource の content 欠落、chunk coverage の不足、安全上限を超える chunk topology は部分的または空の成功として返さず、構造化された `index_missing`、`index_stale`、`index_corrupted` error として失敗する。専用の range partial index がない read-only または immutable な legacy database では、既存の `idx_chunks_file` index を使い、SQLite VM-step budget 内で metadata-only の predecessor / candidate query を実行する。budget 超過時は無制限に scan せず、構造化された `resource_bounded_read_index_unavailable` を返す。stable reason には `resource_content_unavailable`、`resource_bounded_read_index_unavailable`、`resource_chunk_coverage_incomplete`、`chunk_limit_exceeded`、`chunk_candidate_scan_limit_exceeded`、`resource_file_metadata_inconsistent`、`resource_chunk_topology_invalid`、`scan_limit_exceeded` がある。`logging` は MCP `notifications/message` を示し、`logging/setLevel` は `debug`、`info`、`notice`、`warning`、`error`、`critical`、`alert`、`emergency` を受け付ける。
- `resources/templates/list` は正確な既知 path の直接解決用に `cdidx://file-path/{path}` を公開し、成功した read は canonical な `cdidx://file/<path>` identity を返す。`resources/list` の `path`、`lang`、`includeGenerated` filter は server-side で有界に適用され、継続 cursor は canonical filter と generation の両方に結び付く。generated file は既定で list と read から除外され、明示的な `includeGenerated: true` が必要になる。
- `initialize` の `instructions` はこれらの resource template / list control を直接案内し、各 `resources/list` response は accepted extension parameter と上限を `_meta.discovery_contract` に公開する。これにより AI client は標準外の protocol extension を推測する必要がない。
- `protocolVersion` は**ハードコードではなく交渉**で決まる(#1554)。サーバーは
`McpServer.SupportedProtocolVersions`(新しい順: `2025-06-18`,
`2025-03-26`, `2024-11-05`)を保持し、`initialize` パラメータから
Expand Down
12 changes: 12 additions & 0 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2759,6 +2759,12 @@ cdidx exposes separate JSON-RPC integration surfaces with different framing and

MCP file-resource discovery can avoid a repository-wide inventory scan. `resources/templates/list` advertises `cdidx://file-path/{path}`; expand `path` as one repository-relative string, then pass that URI to `resources/read`. The template safely percent-encodes separators and reserved filename characters, while a successful read returns the canonical `cdidx://file/<path>` identity. For browsing, `resources/list` accepts one `path` string or an array of up to 100 path filters, a normalized `lang`, and `includeGenerated` (default `false`). Its continuation cursor is bound to both the index generation and the canonical filters, so changing filters requires restarting without the cursor. Generated resources also require `includeGenerated: true` when read directly.

The MCP `initialize` instructions advertise these template and browsing controls
in-band. Every `resources/list` result also publishes the accepted extension
parameters and bounds under `_meta.discovery_contract`, so AI clients can discover
`path`, `lang`, `includeGenerated`, `maxBytes`, and cursor semantics without
guessing beyond the standard protocol.

MCP `resources/read` is bounded too. Pass optional inclusive `startLine` / `endLine` values and `maxBytes` (the UTF-8 resource-text budget); the minimum is 4 bytes, omitted budgets default to 64 KiB, and requests cannot exceed 128 KiB. Each page is also capped at 1,000 logical lines. `result._meta.truncationReason` is `maxLines` when that cap is reached and `maxBytes` when the requested text budget is reached. If the configured MCP or HTTP response ceiling is tighter, `_meta.maxBytes` retains the requested budget, `_meta.effectiveMaxBytes` reports the reduced budget, and truncation uses `maxResponseBytes`. Multiple `resources/read` calls in one JSON-RPC batch share the aggregate frame ceiling, so each item also yields to the space remaining in that batch. A non-pageable item that cannot fit its allocation returns a structured `batch_response_budget_too_small` error while preserving the request ID. When `result._meta.truncated` is true, send the returned `nextCursor` with the same resource URI to continue. Do not combine a cursor with new line boundaries; you may change `maxBytes` for the next page. A cursor becomes stale if the indexed resource changes. Read-only or immutable legacy databases without the dedicated range indexes use the existing `idx_chunks_file` index for a metadata-only compatibility lookup under a SQLite VM-step budget; exceeding that budget returns a structured index-unavailable error instead of performing an unbounded scan.

### MCP Server (for Claude Code, Cursor, Windsurf, etc.)
Expand Down Expand Up @@ -5965,6 +5971,12 @@ cdidx は framing と制限が異なる JSON-RPC integration surface を分け

MCP file-resource discovery ではリポジトリ全体の inventory scan を回避できます。`resources/templates/list` は `cdidx://file-path/{path}` を公開します。`path` を 1 つのリポジトリ相対文字列として展開し、その URI を `resources/read` に渡してください。template は separator と予約 filename 文字を安全に percent encode し、成功した read は canonical な `cdidx://file/<path>` identity を返します。browse 用の `resources/list` は 1 つの `path` 文字列または最大 100 件の path filter 配列、正規化される `lang`、`includeGenerated`(既定 `false`)を受け付けます。継続 cursor は index generation と canonical filter の両方に結び付くため、filter を変更する場合は cursor を省略して再開してください。generated resource の direct read にも `includeGenerated: true` が必要です。

MCP `initialize` の `instructions` はこれらの template / browse control を
protocol 上で直接案内します。各 `resources/list` result も accepted extension
parameter と上限を `_meta.discovery_contract` に公開するため、AI client は標準
protocol の外側にある `path`、`lang`、`includeGenerated`、`maxBytes`、cursor
semantics を推測せず発見できます。

MCP `resources/read` にも上限があります。inclusive な `startLine` / `endLine` と、UTF-8 resource 本文の budget である `maxBytes` を任意指定できます。最小値は 4 byte、budget 省略時は 64 KiB、要求可能な最大値は 128 KiB です。各ページには論理行 1,000 行の上限もあります。この上限に達した場合、`result._meta.truncationReason` は `maxLines`、要求した本文 budget に達した場合は `maxBytes` になります。設定された MCP または HTTP のレスポンス上限の方が小さい場合、`_meta.maxBytes` は要求値を保持し、`_meta.effectiveMaxBytes` が縮小後の budget を示し、切り詰め理由は `maxResponseBytes` になります。1 つの JSON-RPC batch に複数の `resources/read` call がある場合は aggregate frame 上限を共有するため、各 item はその batch の残り領域にも従います。page 化できない item が割当内に収まらない場合は、request ID を保持した構造化 `batch_response_budget_too_small` error を返します。`result._meta.truncated` が true の場合、返された `nextCursor` を同じ resource URI とともに送って継続してください。cursor と新しい行境界は併用できませんが、次ページの `maxBytes` は変更できます。index 済み resource が変わると cursor は stale になります。専用の range index がない read-only または immutable な legacy database では、既存の `idx_chunks_file` index を使い、SQLite VM-step budget 内の metadata-only compatibility lookup を行います。この budget を超えた場合は無制限に scan せず、構造化された index-unavailable error を返します。

### MCP サーバー(Claude Code、Cursor、Windsurf 等に対応)
Expand Down
18 changes: 18 additions & 0 deletions changelog.d/unreleased/+mcp-resource-discovery-guidance.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
category: fixed
affected:
- src/CodeIndex/Mcp/McpToolHandlers.Instructions.cs
- src/CodeIndex/Mcp/McpServer.Resources.cs
- tests/CodeIndex.Tests/McpServerProtocolTests.cs
- tests/CodeIndex.Tests/McpServerTests.cs
- USER_GUIDE.md
- DEVELOPER_GUIDE.md
---

## English

- **MCP file-resource controls are now discoverable in-band** — `initialize` instructions now explain exact-path templates and filtered `resources/list` usage, while each list response publishes accepted parameters, bounds, and cursor semantics under `_meta.discovery_contract`, so AI clients can use current resource discovery without guessing non-standard protocol extensions.

## 日本語

- **MCP の file-resource control を protocol 上で発見できるようになりました** — `initialize` の instructions が exact-path template と filter 付き `resources/list` の使い方を案内し、各 list response が accepted parameter、上限、cursor semantics を `_meta.discovery_contract` に公開するため、AI client は標準外の protocol extension を推測せず最新の resource discovery を利用できます。
48 changes: 48 additions & 0 deletions src/CodeIndex/Mcp/McpServer.Resources.cs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ private static JsonObject CreateResourceListResponse(
["resources"] = resources,
["_meta"] = new JsonObject
{
["discovery_contract"] = CreateResourceListDiscoveryContract(),
["response_controls"] = CreateResourceListResponseControls(
requestedMaxBytes,
effectiveMaxBytes,
Expand All @@ -298,6 +299,53 @@ private static JsonObject CreateResourceListResponse(
return CreateSuccessResponse(true, id, result);
}

private static JsonObject CreateResourceListDiscoveryContract()
=> new()
{
["accepted_params"] = new JsonArray
{
"cursor",
"path",
"lang",
"includeGenerated",
"maxBytes",
},
["filter_params"] = new JsonArray
{
"path",
"lang",
"includeGenerated",
},
["path_filter"] = new JsonObject
{
["type"] = "string_or_array",
["max_items"] = MaxResourceListPathFilterCount,
["max_characters_per_item"] = MaxResourceListPathFilterChars,
["max_wildcards_per_item"] = MaxResourceListPathFilterWildcards,
},
["language_filter"] = new JsonObject
{
["type"] = "normalized_language_name_or_alias",
["max_characters"] = MaxResourceListLanguageFilterChars,
},
["generated_files_excluded_by_default"] = true,
["max_bytes"] = new JsonObject
{
["scope"] = "json_rpc_envelope",
["minimum"] = MinResourceListMaxBytes,
["default"] = DefaultResourceListMaxBytes,
["maximum"] = MaxResourceListMaxBytes,
},
["pagination"] = new JsonObject
{
["cursor_param"] = "params.cursor",
["next_cursor_field"] = "result.nextCursor",
["cursor_is_opaque"] = true,
["cursor_binds_index_generation"] = true,
["cursor_binds_filters"] = true,
},
};

private static JsonObject CreateResourceListResponseControls(
int requestedMaxBytes,
int effectiveMaxBytes,
Expand Down
2 changes: 2 additions & 0 deletions src/CodeIndex/Mcp/McpToolHandlers.Instructions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ bool All(params string[] names)
parts.Add("Investigation flow: search broadly, use definition for declarations, references for usage sites, callers/callees for call graph impact, outline/map for structure, then excerpt or resources/read for focused line ranges. Prefer pagination, path/lang filters, exactName/exactSubstring, and prefix over dumping large files. 調査順序: まず広く search し、宣言は definition、利用箇所は references、呼び出し影響は callers/callees、構造把握は outline/map、その後に excerpt または resources/read で必要な行範囲だけを読んでください。大きなファイルを丸ごと読む前に pagination、path/lang filter、exactName/exactSubstring、prefix で絞り込んでください。");
}

parts.Add("For an exact known repository-relative path, expand the cdidx://file-path/{path} template from resources/templates/list and pass that URI to resources/read. 正確なリポジトリ相対 path が分かっている場合は、resources/templates/list の cdidx://file-path/{path} template を展開し、その URI を resources/read に渡してください。");
parts.Add("For browsing, call resources/list with optional path, lang, includeGenerated, and maxBytes; continue with result.nextCursor and unchanged filters. Generated files are excluded by default. browse する場合は resources/list に任意の path、lang、includeGenerated、maxBytes を渡し、同じ filter のまま result.nextCursor で継続してください。generated file は既定で除外されます。");
parts.Add("For resources/read, use startLine/endLine and maxBytes, then continue truncated reads with result._meta.nextCursor. resources/read では startLine/endLine と maxBytes を使い、切り詰められた場合は result._meta.nextCursor で継続してください。");

if (On("analyze_symbol"))
Expand Down
6 changes: 6 additions & 0 deletions tests/CodeIndex.Tests/McpServerProtocolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,12 @@ public void Initialize_ReturnsInstructions()
Assert.Contains("search", instructions);
Assert.Contains("CodeIndex MCP tools", instructions);
Assert.Contains("grep/find/cat", instructions);
Assert.Contains("resources/templates/list", instructions);
Assert.Contains("cdidx://file-path/{path}", instructions);
Assert.Contains("resources/list", instructions);
Assert.Contains("optional path, lang, includeGenerated, and maxBytes", instructions);
Assert.Contains("result.nextCursor", instructions);
Assert.Contains("unchanged filters", instructions);
Assert.Contains("resources/read", instructions);
Assert.Contains("startLine/endLine", instructions);
Assert.Contains("maxBytes", instructions);
Expand Down
Loading
Loading