Support MCP protocol version 2026-07-28 (stateless, dual-era) - #409
Conversation
The 2026-07-28 revision removes the initialize handshake in favor of per-request _meta, makes server/discover mandatory, requires resultType on every result and ttlMs/cacheScope on tools/list, and mirrors request metadata into required HTTP headers (MCP-Protocol-Version, Mcp-Method, Mcp-Name) with HeaderMismatch (-32020) and UnsupportedProtocolVersion (-32022) refusals. Serve both eras on the same endpoints, per the spec's dual-era model: a request declaring the stateless era (via _meta or the mirrored header) is validated and answered under 2026-07-28 rules; everything else runs the legacy initialize contract untouched. The legacy and modern version lists stay disjoint so initialize can never echo a version under which initialize does not exist. - protocol.rs: MODERN_PROTOCOL_VERSIONS, Call::Discover, discover_result, the 2026-07-28 result envelope (resultType/serverInfo _meta, CacheableResult fields), and the -32022 error shared by both transports - taguru-mcp (stdio): answers the server/discover era probe and refuses unimplemented per-request versions before dispatch - remote_mcp (HTTP): the modern gate — header/body agreement incl. the Mcp-Name Base64 sentinel, 400 for -32020/-32022, 404 for an unknown method on the modern era only - wire fixtures regenerated: additive envelope fields only Closes #408 Claude-Session: https://claude.ai/code/session_014RfogjbkTt5f14rz8fzYgP
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughMCP 2026-07-28のmodern契約を追加しました。 ChangesMCP modern protocol support
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Client as MCP client
participant Endpoint as POST /mcp
participant Serve as remote_mcp::serve
participant Protocol as MCP protocol
Client->>Endpoint: headers and JSON-RPC body
Endpoint->>Serve: forward headers and body
Serve->>Protocol: validate version and classify method
Protocol-->>Serve: response envelope or JSON-RPC error
Serve-->>Client: HTTP response
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
src/bin/taguru-mcp.rs (1)
667-675: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
handleは本番ループのゲートを複製しており、テストの忠実性が下がります。
handleは Line 671-673 でバージョン拒否を行います。本番の stdio ループは Line 275-282 で同じ判定を独自に行い、handleを呼びません。新しいテスト(Line 878、Line 899)はhandleだけを通ります。一方だけを変更すると、テストが通ったまま本番ループの順序が食い違います。バッチ拒否とバージョン拒否の順序はすでに 2 箇所に重複しています。
本番ループから
handleを呼ぶか、handleの doc コメントに「本番ループの順序を写したものであり、両方を同時に更新する」と明記してください。🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/bin/taguru-mcp.rs` around lines 667 - 675, Unify the production stdio loop with handle so both use the same batch-rejection and modern-version-rejection ordering, preferably by routing production processing through handle. If duplication must remain, add a doc comment to handle explicitly identifying it as a mirror of the production loop and requiring both paths to be updated together.src/remote_mcp.rs (1)
1067-1105: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Mcp-Methodが存在するが本文と不一致のケースが未検証です。
disagreementsの配列はmcp-methodを削除したケース(Line 1078-1082)を含みます。しかしmcp-methodが存在し、値だけが本文のmethodと異なるケースがありません。modern_gateの Line 326-331 の arm は未実行のままです。Mcp-Nameは「不一致」と「欠落」の両方を検証しているので、Mcp-Methodも対称にしてください。💚 追加するケース
( { let mut headers = modern_headers("tools/list", None); headers.remove("mcp-method"); headers }, modern_body("tools/list", json!({})), ), + ( + // 存在するが本文の method と食い違う: 欠落とは別の arm。 + modern_headers("tools/call", Some("list_contexts")), + modern_body( + "tools/list", + json!({ "name": "list_contexts", "arguments": {} }), + ), + ),テストされていないエッジケース・失敗系の欠落を指摘する path instructions に従いました。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/remote_mcp.rs` around lines 1067 - 1105, Extend the disagreements cases in header_body_disagreements_are_refused_with_header_mismatch to include headers containing an mcp-method value that differs from the body’s method, while keeping the existing missing-header case. Use the same expected 400 status and -32020 error assertions so the modern_gate mismatch arm is exercised, matching the existing Mcp-Name mismatch and missing cases.Source: Path instructions
src/mcp.rs (1)
1144-1183: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win不正な型の
idを伴う未対応バージョンのケースが未検証です。
modern_version_rejectionはclassifyの結果がMessage::Requestでない場合にNoneを返します。現在このガードを踏むのは notification のケースだけです。idが object/array/bool の場合も同じelseに落ち、-32022 ではなく通常の -32600 経路に進みます。この分岐は「不正なidを -32022 のエラー本文へエコーしない」という性質を保証するので、テストで固定してください。💚 追加するアサーション
assert!( modern_version_rejection(&json!({ "jsonrpc": "2.0", "method": "notifications/whatever", "params": {"_meta": {META_PROTOCOL_VERSION: "2099-01-01"}}, })) .is_none() ); + // JSON-RPC が許さない型の id は -32022 にはならず、通常の + // InvalidId 経路に落ちる — 不正な id をエラー本文へエコーしない。 + assert!( + modern_version_rejection(&json!({ + "jsonrpc": "2.0", "id": [1], "method": "tools/list", + "params": {"_meta": {META_PROTOCOL_VERSION: "2099-01-01"}}, + })) + .is_none() + ); }テストコードのエッジケース・失敗系の欠落を指摘する path instructions に従いました。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/mcp.rs` around lines 1144 - 1183, Extend modern_version_rejection_refuses_only_unsupported_declared_versions with unsupported-version requests whose id is an object, array, or boolean. Assert modern_version_rejection returns None for each malformed id, ensuring these inputs are not converted into the -32022 response or echoed in its error data and remain on the normal invalid-request path.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/remote_mcp.rs`:
- Around line 278-281: Update the header-version handling in the request
processing flow around header_version and meta_version so a present but
non-UTF-8 mcp-protocol-version value is explicitly routed to header_mismatch.
Preserve None only for an actually absent header, ensuring malformed values
cannot fall through to the (None, None) legacy path.
- Around line 282-319: Update the version matching logic around the
`meta_version`/`header_version` match so a request without `_meta` returns
`Era::Legacy` for every header version that is not in
`mcp::MODERN_PROTOCOL_VERSIONS`, including unlisted legacy versions such as
`2025-11-25`; only modern header versions should produce `header_mismatch`. Add
the corresponding `roundtrip` test covering an unlisted legacy header and
asserting a successful response without an error.
In `@src/route/server.rs`:
- Around line 250-264: Update the comment near the
`wants_instructions`/`discover_result` flow that says only `initialize` reads
the manual, so it explicitly states that `server/discover` also waits for manual
retrieval during the era probe. Keep the implementation unchanged, including
`wants_instructions` returning true for both methods.
In `@tests/fixtures/wire/mcp/assemble_evidence_call.json`:
- Around line 11-23: Update the shape definitions used by complete_result:
register _meta.io.modelcontextprotocol/serverInfo.version as volatile and
register resultType as a closed enum, keeping enums and required_request_fields
aligned with the wire contract. Apply this to
tests/fixtures/wire/mcp/assemble_evidence_call.json lines 11-23 and
tests/fixtures/wire/mcp/assemble_evidence_tool_error.json lines 8-21; ensure the
error fixture records the compatible coexistence of isError: true and
resultType.
---
Nitpick comments:
In `@src/bin/taguru-mcp.rs`:
- Around line 667-675: Unify the production stdio loop with handle so both use
the same batch-rejection and modern-version-rejection ordering, preferably by
routing production processing through handle. If duplication must remain, add a
doc comment to handle explicitly identifying it as a mirror of the production
loop and requiring both paths to be updated together.
In `@src/mcp.rs`:
- Around line 1144-1183: Extend
modern_version_rejection_refuses_only_unsupported_declared_versions with
unsupported-version requests whose id is an object, array, or boolean. Assert
modern_version_rejection returns None for each malformed id, ensuring these
inputs are not converted into the -32022 response or echoed in its error data
and remain on the normal invalid-request path.
In `@src/remote_mcp.rs`:
- Around line 1067-1105: Extend the disagreements cases in
header_body_disagreements_are_refused_with_header_mismatch to include headers
containing an mcp-method value that differs from the body’s method, while
keeping the existing missing-header case. Use the same expected 400 status and
-32020 error assertions so the modern_gate mismatch arm is exercised, matching
the existing Mcp-Name mismatch and missing cases.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 57f6dba0-549c-4139-a7eb-625e73f7617c
📒 Files selected for processing (8)
src/bin/taguru-mcp.rssrc/main.rssrc/mcp.rssrc/mcp/protocol.rssrc/remote_mcp.rssrc/route/server.rstests/fixtures/wire/mcp/assemble_evidence_call.jsontests/fixtures/wire/mcp/assemble_evidence_tool_error.json
- modern_gate tolerates any non-modern MCP-Protocol-Version without _meta as legacy (2025-11-25 was never listed; refusing it would break clients this PR promised to leave untouched), while an unreadable header value is now -32020 instead of quietly reading as absent - route/server.rs: the manual-fetch comment names server/discover too - tests: Mcp-Method present-but-mismatched, unlisted legacy header, unreadable header, invalid-id never echoed into -32022; handle's doc marks it a mirror of the production loop's gate order - shapes.json: resultType registered as a closed enum (its serverInfo version was already covered by the existing volatile "version" entry) Claude-Session: https://claude.ai/code/session_014RfogjbkTt5f14rz8fzYgP
Closes #408
概要
MCP 仕様 2026-07-28(7/28 正式リリース、ステートレス化を軸とした最大改訂)に、仕様が定める dual-era サーバーとして対応する。modern な
_meta/ヘッダーを名乗るリクエストは 2026-07-28 の規則で検証・応答し、legacy(initialize世代)経路は無変更で併存する。変更点
src/mcp/protocol.rs(共有コア)MODERN_PROTOCOL_VERSIONS = ["2026-07-28"]を legacy 集合と分離して追加 —initializeが「initialize が存在しないバージョン」をエコーしないため(回帰テストあり)server/discover(仕様上 MUST)のCall::Discoverとdiscover_resultresultType: "complete"と serverInfo_meta、tools/list/discover にttlMs/cacheScope(CacheableResult)を無条件付与 — legacy クライアントには無害な追加フィールド-32022(UnsupportedProtocolVersion、data.supported付き)を両トランスポート共有で生成src/bin/taguru-mcp.rs(stdio): dual-era クライアントの era プローブであるserver/discoverに応答。未実装バージョンを名乗るリクエストはディスパッチ前に-32022で拒否src/remote_mcp.rs(HTTP): modern gate —MCP-Protocol-Versionとボディ_metaの一致、Mcp-Method/Mcp-Nameとボディの一致(=?base64?…?=センチネルのデコード込み)を検証し、不一致・欠落は 400 +-32020、未実装バージョンは 400 +-32022、未知メソッドは modern era のみ 404 +-32601(legacy は従来どおり 200)影響なし(確認済み)
taguru は元々セッションレス(
Mcp-Session-Idなし・SSE なし・POST のみ)かつツール専用のため、セッション廃止・SSE 再開削除・Roots/Sampling/Logging 非推奨・MRTR・Tasks 拡張の影響なし。検証
cargo fmt/cargo clippy(警告ゼロ)/cargo test全パス(新規テスト: discover の形、-32020/-32022、404/200 の era 分岐、base64 センチネル、legacy エコー回帰)-32020/ 未知バージョン 400+-32022/ 未知メソッド 404、legacy のinitialize(2026-07-28 を要求しても 2025-06-18 を応答)、stdio ブリッジの discover プローブと-32022を確認https://claude.ai/code/session_014RfogjbkTt5f14rz8fzYgP
Summary by CodeRabbit
新機能
server/discoverでサーバー情報や説明を取得できます。改善