Skip to content

Relax legacy request metadata validation - #1852

Open
halter73 wants to merge 2 commits into
mainfrom
halter73-fix-metadata-protocol-version
Open

Relax legacy request metadata validation#1852
halter73 wants to merge 2 commits into
mainfrom
halter73-fix-metadata-protocol-version

Conversation

@halter73

@halter73 halter73 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

ChatGPT currently negotiates 2025-11-25 but includes metadata introduced in 2026-07-28. Legacy _meta is extensible, so rejecting these requests is unnecessarily strict and prevents otherwise compatible clients from using the SDK.

This change:

  • Treats known legacy protocol-version metadata and auxiliary modern metadata as advisory under legacy semantics.
  • Keeps modern or unknown protocol claims strict, including required metadata, unsupported versions, header mismatches, and attempts to change an established session version.
  • Preserves identity and capabilities established by initialize for stateful legacy sessions while exposing well-formed auxiliary metadata to request filters and stateless handlers.
  • Ignores malformed future metadata only on legacy requests; modern envelopes still validate it strictly.
  • Omits deprecated logging from 2026-07-28 server/discover responses while retaining it for legacy initialize responses.

Added regression coverage for the ChatGPT request shape, advisory legacy version metadata, malformed metadata boundaries, initialized-session isolation, modern version switching, capability spoofing, and protocol-specific logging advertisement.

Fixes: #1783
Fixes: #1774

halter73 and others added 2 commits August 6, 2026 17:27
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.

Copilot review overview

🟡 Changes recommended

Legacy requests with a malformed _meta/io.modelcontextprotocol/protocolVersion still throw InvalidParams due to unconditional parsing, which contradicts the intended “ignore malformed future metadata on legacy” behavior.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Lite
Findings: 1 Medium severity

New issues introduced by this change (1)
Severity Finding
Medium severity src/​ModelContextProtocol.Core/​Server/​McpServerImpl.cs — ReadRequestMetadata calls GetProtocolVersionMeta unconditionally, which throws InvalidParams if…
What changed in this PR

This PR relaxes validation of legacy per-request _meta so legacy clients (notably ChatGPT negotiating 2025-11-25) aren’t rejected when they include auxiliary metadata introduced in later protocol eras, while keeping modern (2026-07-28+) protocol/version claims and required metadata strict. It also adjusts server/discover capability advertisement to avoid claiming deprecated logging on modern discover responses.

Changes:

  • Reworks server-side _meta parsing/projection and protocol boundary validation to treat known legacy-era metadata as advisory while enforcing strictness for modern envelopes and version switching.
  • Omits deprecated logging capability from 2026-07-28 server/discover responses while retaining it for legacy initialize.
  • Adds regression coverage across server/client behavior, including legacy session isolation vs per-request metadata and malformed metadata handling.
File Description
tests/​ModelContextProtocol.Tests/​Server/​TaskProtocolGatingTests.cs Updates legacy task-gating expectation to tolerate per-request capability metadata without enabling tasks.
tests/​ModelContextProtocol.Tests/​Server/​NegotiatedProtocolVersionTests.cs Expands coverage for advisory legacy version metadata, legacy session isolation, malformed metadata handling, and modern version switching.
tests/​ModelContextProtocol.Tests/​Server/​McpServerTests.cs Validates legacy transport header remains authoritative over advisory _meta protocol version; asserts legacy initialize includes logging capability.
tests/​ModelContextProtocol.Tests/​Client/​McpClientMetaTests.cs Adds coverage that legacy per-request client metadata does not override initialized session identity/capabilities.
tests/​ModelContextProtocol.AspNetCore.Tests/​RawHttpConformanceTests.cs Adds conformance checks for discover capability suppression and legacy raw POST tolerance of auxiliary per-request metadata.
src/​ModelContextProtocol.Core/​Server/​McpServerImpl.cs Implements the relaxed legacy _meta semantics, strict modern validation, and logging capability suppression on discover.
src/​ModelContextProtocol.Core/​Protocol/​JsonRpcMessageContext.cs Updates documentation to clarify authoritative protocol version semantics and advisory legacy _meta behavior.
src/​ModelContextProtocol.Core/​McpSessionHandler.cs Removes session-handler _meta projection now handled by server pipeline filter logic.
Suppressed comments (1)

src/ModelContextProtocol.Core/Server/McpServerImpl.cs:318

  • ProjectInitializeRequestMetadata also uses GetProtocolVersionMeta, so an initialize request carrying a malformed _meta/io.modelcontextprotocol/protocolVersion (or other invalid value type) will currently fail with InvalidParams. Since initialize is a legacy handshake path and _meta is meant to be extensible there, consider treating a malformed protocolVersion entry as advisory/absent rather than rejecting the request.
        JsonObject? meta = GetRequestMeta(request);
        string? metadataProtocolVersion = GetProtocolVersionMeta(meta, out bool hasProtocolVersionMeta);
        string? transportProtocolVersion = request.Context?.ProtocolVersion;

        ValidateProtocolVersionMatch(transportProtocolVersion, metadataProtocolVersion);

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +216 to +234
private void ReadRequestMetadata(JsonRpcRequest request)
{
JsonObject? meta = GetRequestMeta(request);
string? metadataProtocolVersion = GetProtocolVersionMeta(meta, out bool hasProtocolVersionMeta);
string? transportProtocolVersion = request.Context?.ProtocolVersion;

ValidateProtocolVersionMatch(transportProtocolVersion, metadataProtocolVersion);

bool establishedModernProtocol = McpProtocolVersions.RequiresPerRequestMetadata(_negotiatedProtocolVersion);
bool transportClaimsModernProtocol =
transportProtocolVersion is not null &&
!McpProtocolVersions.SupportsInitializeHandshake(transportProtocolVersion);
bool metadataClaimsModernProtocol =
hasProtocolVersionMeta &&
!McpProtocolVersions.SupportsInitializeHandshake(metadataProtocolVersion);
bool serverRequiresModernProtocol =
_initializeHandshakeProtocolVersions.Length == 0 &&
_perRequestMetadataProtocolVersions.Length > 0;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants