Tighten draft protocol negotiation boundaries#1692
Open
halter73 wants to merge 2 commits into
Open
Conversation
Rename protocol version groups around their wire mechanisms and enforce stricter initialize-vs-per-request metadata boundaries across client, server, and HTTP transports. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR tightens protocol negotiation so the SDK cleanly separates initialize-handshake protocol revisions (≤ 2025-11-25) from the per-request-metadata revision (2026-07-28), preventing “mixed-era” negotiation paths and aligning HTTP session behavior with the draft spec.
Changes:
- Split supported protocol versions into initialize-handshake vs per-request-metadata sets and enforce those boundaries in server/client negotiation.
- Update Streamable HTTP semantics for
2026-07-28: no sessions, noMcp-Session-Idmint/echo, and stricter header/body protocol envelope validation. - Refresh tests and docs to use clearer initialize-handshake/per-request-metadata terminology and to assert the exact supported protocol strings and gating behavior.
Show a summary per file
| File | Description |
|---|---|
| tests/ModelContextProtocol.Tests/Server/TaskProtocolGatingTests.cs | Updates legacy-task gating expectation to reject reserved per-request metadata on legacy traffic. |
| tests/ModelContextProtocol.Tests/Server/RawStreamConformanceTests.cs | Renames/clarifies dual-path behavior and mixed discover→initialize sequencing expectations. |
| tests/ModelContextProtocol.Tests/Server/NegotiatedProtocolVersionTests.cs | Adds extensive tests for boundary enforcement: missing required per-request metadata, rejecting initialize on 2026-07-28, method availability gates, etc. |
| tests/ModelContextProtocol.Tests/Server/McpServerTests.cs | Updates pinned protocol string from 2024 to 2024-11-05 in tests. |
| tests/ModelContextProtocol.Tests/Protocol/CacheableResultWarningTests.cs | Renames “modern lifecycle” terminology to “per-request metadata lifecycle” in test helpers. |
| tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsRequestFilterTests.cs | Pins protocol version for logging-level tests to an initialize-handshake revision. |
| tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs | Pins protocol version for logging-level integration test to an initialize-handshake revision. |
| tests/ModelContextProtocol.Tests/Client/McpRequestHeadersTests.cs | Renames header gating helper expectation to RequiresStandardHeaders. |
| tests/ModelContextProtocol.Tests/Client/McpClientTests.cs | Pins protocol version for logging-level related tests to initialize-handshake revisions. |
| tests/ModelContextProtocol.Tests/Client/July2026ProtocolFallbackTests.cs | Renames legacy transport/test terminology; adds assertions about initialize fallback protocol version selection and mismatch handling. |
| tests/ModelContextProtocol.Tests/Client/July2026ProtocolConnectionTests.cs | Tightens discover supportedVersions expectation and asserts initialize-handshake clients cannot call server/discover post-negotiation. |
| tests/ModelContextProtocol.AspNetCore.Tests/StreamableHttpServerConformanceTests.cs | Switches 2026-07-28 bootstrap from initialize to discover + per-request metadata in conformance tests. |
| tests/ModelContextProtocol.AspNetCore.Tests/RawHttpConformanceTests.cs | Adds coverage for configured server protocol pinning and additional per-request-metadata error shapes. |
| tests/ModelContextProtocol.AspNetCore.Tests/MrtrProtocolTests.cs | Makes per-request metadata injection explicit and controllable per request in test JSON builders. |
| tests/ModelContextProtocol.AspNetCore.Tests/July2026ProtocolStatefulFallbackTests.cs | Renames “legacy” wording to “initialize” and clarifies downgrade semantics on stateful servers. |
| tests/ModelContextProtocol.AspNetCore.Tests/July2026ProtocolHttpHandlerTests.cs | Updates 2026-07-28 HTTP tests to use discover request JSON and new method/endpoint behaviors. |
| tests/ModelContextProtocol.AspNetCore.Tests/July2026ProtocolHttpFallbackTests.cs | Extends HTTP fallback tests; adds session-id ignore assertions for per-request metadata responses/requests. |
| tests/ModelContextProtocol.AspNetCore.Tests/HttpHeaderConformanceTests.cs | Switches 2026-07-28 bootstrap to probe via discover; adds optional per-request metadata inclusion to request builders. |
| tests/Common/Utils/NodeHelpers.cs | Minor comment wording update for net472 timeout polyfill path. |
| src/ModelContextProtocol.Core/Server/McpServerOptions.cs | Updates option docs to reflect discovery vs initialization eras and explicitly lists supported protocol strings. |
| src/ModelContextProtocol.Core/Server/McpServerImpl.cs | Core server enforcement: separates supported version sets, validates per-request metadata requirements, rejects initialize on 2026-07-28, and tightens method boundary gates. |
| src/ModelContextProtocol.Core/Protocol/JsonRpcMessage.cs | Clarifies comment wording around non-SEP-2575 error envelopes. |
| src/ModelContextProtocol.Core/Protocol/InitializeResult.cs | Clarifies that initialize negotiates a legacy/initialize-handshake protocol version. |
| src/ModelContextProtocol.Core/Protocol/InitializeRequestParams.cs | Clarifies initialize is for legacy/initialize-handshake protocol versions and points 2026-07-28 to discover + per-request metadata. |
| src/ModelContextProtocol.Core/Protocol/DiscoverResult.cs | Documents that discover advertises per-request-metadata versions only. |
| src/ModelContextProtocol.Core/McpSessionHandler.cs | Centralizes supported protocol list in McpHttpHeaders and updates gates to use RequiresPerRequestMetadata. |
| src/ModelContextProtocol.Core/Client/StreamableHttpClientSessionTransport.cs | Prevents sending Mcp-Session-Id when protocol has no sessions; renames “modern” error-code helper. |
| src/ModelContextProtocol.Core/Client/McpClientOptions.cs | Documents supported protocol strings and clarifies default preference + initialize fallback semantics. |
| src/ModelContextProtocol.Core/Client/McpClientImpl.cs | Updates connect-time logic to treat 2026-07-28 as per-request metadata, and tightens initialize fallback semantics/version validation. |
| src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs | Uses centralized supported-version list; adds header/body protocol envelope validation; adjusts 2026-era GET/DELETE responses; refines session handling for per-request metadata. |
| src/ModelContextProtocol.AspNetCore/HttpServerTransportOptions.cs | Updates docs: per-request-metadata requests ignore Mcp-Session-Id and must not mint/echo sessions. |
| src/Common/McpHttpHeaders.cs | Introduces explicit initialize-handshake vs per-request-metadata version lists and new helper gates (supported/init/per-request/sessions). |
| docs/concepts/transports/transports.md | Updates HTTP transport docs to reflect stateless-by-default behavior and when to opt into sessions. |
| docs/concepts/stateless/stateless.md | Updates forward/backward compatibility guidance and clarifies stateful server behavior under 2026-07-28. |
| docs/concepts/sampling/sampling.md | Updates stateless/2026-era guidance to reflect initialize-handshake vs per-request metadata realities. |
| docs/concepts/roots/roots.md | Updates stateless/2026-era guidance similarly for roots. |
| docs/concepts/mrtr/mrtr.md | Updates MRTR opt-in text and clarifies HTTP stateful refusal of 2026-07-28. |
| docs/concepts/elicitation/elicitation.md | Updates stateless/2026-era guidance similarly for elicitation. |
Review details
- Files reviewed: 38/38 changed files
- Comments generated: 2
- Review effort level: Low
Comment on lines
+226
to
+231
| if (hasReservedPerRequestMeta) | ||
| { | ||
| SetNegotiatedProtocolVersion(protocolVersion); | ||
| protocolVersionRecorded = true; | ||
| ThrowReservedPerRequestMetadata(requestedProtocolVersion: protocolVersion, reservedPerRequestMetaKey); | ||
| } |
Comment on lines
+681
to
+691
| private static string[] GetConfiguredSupportedProtocolVersions(string? protocolVersion) | ||
| { | ||
| if (protocolVersion is null) | ||
| { | ||
| return s_supportedProtocolVersions; | ||
| } | ||
|
|
||
| return McpHttpHeaders.IsSupportedProtocolVersion(protocolVersion) ? | ||
| [protocolVersion] : | ||
| s_supportedProtocolVersions; | ||
| } |
Contributor
|
A couple of non-blocking nits, feel free to ignore:
Both are minor and non-blocking. |
tarekgh
approved these changes
Jul 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The draft
2026-07-28protocol revision moves bootstrapping frominitializetoserver/discoverplus per-request metadata, and it removes Streamable HTTP sessions. This PR makes the SDK enforce that split instead of allowing the new revision to leak through old negotiation paths or older revisions to leak through the metadata path.Key changes:
server/discoveradvertise only per-request metadata versions, currently2026-07-28.initializefor2026-07-28and reject older versions when sent through per-request metadata.2026-07-28without minting or echoingMcp-Session-Id.Mcp-Session-Idfor2026-07-28, while ignoring unexpected response session IDs for compatibility.Fixes: #1680