Echo parsed request id in Streamable HTTP header/session error responses#1682
Open
anneheartrecord wants to merge 1 commit into
Open
Conversation
Header validation and session-gate errors in the stateless Streamable HTTP transport ran after the JSON-RPC body (and its id) had been parsed, but the error responses were written with no id, so they serialized as id:null. JSON-RPC 2.0 §5 reserves a null id for responses where the request id could not be read (parse/invalid-request errors). Once the body is parsed the id is known, so thread it through WriteJsonRpcErrorAsync for the header-mismatch and session-validation paths. Pre-parse and unparseable-body paths keep id:null.
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.
Summary
Header-validation and session-gate errors in the stateless Streamable HTTP transport were returning
id: nulleven though the JSON-RPC request body — and itsid— had already been parsed.In
StreamableHttpHandler.HandlePostRequestAsync, the body is parsed (ReadJsonRpcMessageAsync) beforeValidateMcpHeadersruns, butWriteJsonRpcErrorAsyncconstructed aJsonRpcErrorwithout anId, so it serialized asnull. The same applied to the post-parse session checks inGetOrCreateSessionAsync.Per JSON-RPC 2.0 §5, a
nullid is reserved for responses where the request id couldn't be read (parse error / invalid request). The MCP base protocol likewise requires error responses to carry the request's id except when it couldn't be read from a malformed request. Here the id was available, so it should be echoed.Change
WriteJsonRpcErrorAsynctakes an optionalRequestIdand sets it on the error.GetRequestId(JsonRpcMessage?)helper returns the parsed id for messages that carry one, ordefault(→null) otherwise.nullbody) and the body-less GET/DELETE paths keepid: null, which stays correct.Tests
Added to
HttpHeaderConformanceTests:Mcp-Nameheader echoes the numeric request id (the case in the issue)Mcp-Session-Idrejected under the 2026-07-28 revision echoes the idid: null(guards against over-correcting)Verified the three echo tests fail before the change and pass after; the malformed-body test passes in both. Full
HttpHeaderConformanceTestsand the Streamable HTTP / stateless / July-2026 suites pass (net10.0; net8/net9 runtimes weren't available locally but the logic is framework-independent).Fixes #1677