Skip to content

Tighten draft protocol negotiation boundaries#1692

Open
halter73 wants to merge 2 commits into
mainfrom
halter73-draft-protocol-initialize-modern-version
Open

Tighten draft protocol negotiation boundaries#1692
halter73 wants to merge 2 commits into
mainfrom
halter73-draft-protocol-initialize-modern-version

Conversation

@halter73

@halter73 halter73 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

The draft 2026-07-28 protocol revision moves bootstrapping from initialize to server/discover plus 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:

  • Split protocol helpers and server negotiation into initialize-handshake versions versus per-request-metadata versions.
  • Make server/discover advertise only per-request metadata versions, currently 2026-07-28.
  • Reject initialize for 2026-07-28 and reject older versions when sent through per-request metadata.
  • Keep Streamable HTTP stateful mode on initialize-capable revisions; stateless HTTP serves 2026-07-28 without minting or echoing Mcp-Session-Id.
  • Make the client never send Mcp-Session-Id for 2026-07-28, while ignoring unexpected response session IDs for compatibility.
  • Update public docs and tests to use clearer initialize-handshake/per-request-metadata terminology and document exact supported protocol strings.

Fixes: #1680

halter73 and others added 2 commits July 2, 2026 15:22
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>

Copilot AI 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.

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, no Mcp-Session-Id mint/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;
}
@tarekgh

tarekgh commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

A couple of non-blocking nits, feel free to ignore:

  • GetConfiguredSupportedProtocolVersions in StreamableHttpHandler silently returns all versions for an unsupported configured value, while the McpServerImpl twin throws. The throw runs at construction so this branch is effectively unreachable, but the two could match for consistency.
  • GET/DELETE on 2026-07-28 correctly return 405 now, but the responses do not set an Allow header (RFC 7231 recommends it for 405). Purely cosmetic.

Both are minor and non-blocking.

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.

Draft protocol: initialize can negotiate a modern protocol version

3 participants