Skip to content

Support MCP protocol version 2026-07-28 (stateless, dual-era) - #409

Merged
t0k0sh1 merged 2 commits into
mainfrom
408-mcp-2026-07-28-support
Aug 4, 2026
Merged

Support MCP protocol version 2026-07-28 (stateless, dual-era)#409
t0k0sh1 merged 2 commits into
mainfrom
408-mcp-2026-07-28-support

Conversation

@t0k0sh1

@t0k0sh1 t0k0sh1 commented Aug 4, 2026

Copy link
Copy Markdown
Owner

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::Discoverdiscover_result
    • 全結果に resultType: "complete" と serverInfo _metatools/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 エコー回帰)
  • E2E: 実サーバーに対し modern の discover / tools/call / ヘッダー不一致 400+-32020 / 未知バージョン 400+-32022 / 未知メソッド 404、legacy の initialize(2026-07-28 を要求しても 2025-06-18 を応答)、stdio ブリッジの discover プローブと -32022 を確認

https://claude.ai/code/session_014RfogjbkTt5f14rz8fzYgP

Summary by CodeRabbit

  • 新機能

    • 現行MCPプロトコルに対応し、対応バージョンの確認とサーバー探索が可能になりました。
    • server/discover でサーバー情報や説明を取得できます。
    • レスポンスに処理結果種別、サーバー情報、キャッシュ情報を追加しました。
  • 改善

    • プロトコルバージョンやヘッダーの不整合を検証するようになりました。
    • 未対応バージョンや不正なリクエストには、詳細なエラー情報と適切なHTTPステータスを返します。
    • 従来のMCPプロトコルとの互換性を維持しています。

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
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f28b09fa-3581-4c0c-8de6-c2c6c017c6bb

📥 Commits

Reviewing files that changed from the base of the PR and between 43a412b and 202be3f.

📒 Files selected for processing (5)
  • src/bin/taguru-mcp.rs
  • src/mcp.rs
  • src/remote_mcp.rs
  • src/route/server.rs
  • tests/fixtures/wire/shapes.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/route/server.rs
  • src/bin/taguru-mcp.rs
  • src/mcp.rs

📝 Walkthrough

Walkthrough

MCP 2026-07-28のmodern契約を追加しました。server/discover、結果メタデータ、per-request protocol version検証、HTTPヘッダー検証を実装しました。legacy initialize経路と既存の結果内容を維持します。

Changes

MCP modern protocol support

Layer / File(s) Summary
プロトコル契約と結果形式
src/mcp/protocol.rs, src/mcp.rs, tests/fixtures/wire/mcp/*, tests/fixtures/wire/shapes.json
Modernバージョン、Call::Discover_metaからのバージョン取得、-32022エラーを追加しました。server/discovertools/list、tool応答にresultType、キャッシュ情報、サーバー情報を追加しました。
HTTP契約とリクエスト配線
src/main.rs, src/route/server.rs, src/remote_mcp.rs
POST /mcpのヘッダーをremote_mcp::serveへ渡します。modernリクエストのヘッダー、本文、ツール名、Base64 sentinel、protocol versionを検証します。modernの未知メソッドは404、legacyの未知メソッドは200を返します。
stdioディスパッチと探索応答
src/bin/taguru-mcp.rs
stdio経路で未対応のper-request protocol versionを拒否します。server/discoverは対応バージョン、instructions、completeを返します。

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
Loading

Possibly related PRs

  • t0k0sh1/taguru#327: MCPのプロトコルバージョン探索と互換性処理に関連します。
  • t0k0sh1/taguru#332: MCP wire契約のfixtureとassemble_evidence応答に関連します。
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed タイトルは、MCP 2026-07-28対応とステートレスなデュアル時代サーバーの主要変更を正確に示しています。
Linked Issues check ✅ Passed 実装は[#408]のmodern対応、legacy互換性、検証、エラー処理、discover、結果フィールド、テスト要件を満たしています。
Out of Scope Changes check ✅ Passed 変更は[#408]のプロトコル対応、互換性維持、検証、レスポンス更新、関連テストの範囲内です。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 408-mcp-2026-07-28-support

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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_rejectionclassify の結果が 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

📥 Commits

Reviewing files that changed from the base of the PR and between baaf981 and 43a412b.

📒 Files selected for processing (8)
  • src/bin/taguru-mcp.rs
  • src/main.rs
  • src/mcp.rs
  • src/mcp/protocol.rs
  • src/remote_mcp.rs
  • src/route/server.rs
  • tests/fixtures/wire/mcp/assemble_evidence_call.json
  • tests/fixtures/wire/mcp/assemble_evidence_tool_error.json

Comment thread src/remote_mcp.rs Outdated
Comment thread src/remote_mcp.rs
Comment thread src/route/server.rs
Comment thread tests/fixtures/wire/mcp/assemble_evidence_call.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
@t0k0sh1
t0k0sh1 merged commit 25ffe1d into main Aug 4, 2026
9 checks passed
@t0k0sh1
t0k0sh1 deleted the 408-mcp-2026-07-28-support branch August 4, 2026 23: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.

Support MCP protocol version 2026-07-28 (stateless, dual-era)

1 participant