Validate MCP-Protocol-Version header in Streamable HTTP handler#1277
Validate MCP-Protocol-Version header in Streamable HTTP handler#1277
Conversation
If the server receives a request with an invalid or unsupported MCP-Protocol-Version header, respond with 400 Bad Request per the MCP spec. A missing header is allowed for backwards compatibility. Exposes McpSession.SupportedProtocolVersions as a public static property so the AspNetCore handler can validate against it. Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
tests/ModelContextProtocol.AspNetCore.Tests/StreamableHttpServerConformanceTests.cs
Show resolved
Hide resolved
…ns class, use nullable out param - Move SupportedProtocolVersions from McpSession to new ProtocolVersions static class in Protocol namespace (following RequestMethods/NotificationMethods pattern) - Revert McpSession.cs to original state - Change out string to out string? with null on success - Add ProtocolVersionsTests for the new public property Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
The SupportedProtocolVersions doesn't need to be exposed publicly. Instead, use InternalsVisibleTo to let ModelContextProtocol.AspNetCore access the internal McpSessionHandler.SupportedProtocolVersions directly. - Delete ProtocolVersions.cs public static class - Delete ProtocolVersionsTests.cs - Add InternalsVisibleTo in Core csproj for AspNetCore - Reference McpSessionHandler.SupportedProtocolVersions directly Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
…idation Define supported protocol versions as a private HashSet directly in StreamableHttpHandler instead of referencing McpSessionHandler internals or adding any cross-assembly visibility. No new public API surface needed. Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Add abstract SupportedProtocolVersions property to McpServer, implemented by McpServerImpl (delegating to McpSessionHandler.SupportedProtocolVersions) and DestinationBoundMcpServer. Protocol version validation in StreamableHttpHandler now occurs after session resolution, querying the server instance directly. Remove private HashSet from handler. Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Use TryGetValue to check version first, then TryRemove to actually delete. This prevents losing the session if validation fails. Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
…yList<string> Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
| if (sessionManager.TryGetValue(sessionId, out var session)) | ||
| { | ||
| await session.DisposeAsync(); | ||
| if (!ValidateProtocolVersionHeader(context, session.Server, out var errorMessage)) |
There was a problem hiding this comment.
I don't love the layering here. We shouldn't have to pull the McpServer instance out of the sessionManager to determine whether the protocol version is valid. This might make sense if the StreamableHttpHandler made the McpServer replaceable with a custom implementation, but that's not the case. Furthermore, is possible that we might have to put HTTP-specific protocol version dependent logic in StreamableHttpHandler that takes place before retrieving the session.
Why not just copy the internal static list from McpSessionHandler.SupportedProtocolVersions into StreamableHttpHandler with a note to keep them in sync? If we don't like that, we can make SupportedProtocolVersions public.
There was a problem hiding this comment.
Because it's up to the (abstract) McpSession implementation what versions are supported?
There was a problem hiding this comment.
But at least for now, StreamableHttpHandler doesn't support replacing the McpServer implementation, so it seems wrong to be making public API changes specifically so the StreamableHttpHandler can support alternative implementations right now.
Also, it seems wrong to me that the StreamableHttpHandler should have to create an McpServer instance to even start validating the MCP-Protocol-Version. So, in the future if/when we add support for extending StreamableHttpHandler with custom McpServer implementations, we'd probably want to offer a different public API for declaring with protocol versions you support anyway.
There was a problem hiding this comment.
I don't agree. But we can do the less maintainable thing for now and just maintain the supported list in two places.
@copilot please remove the new API and instead copy the supported protocol list into the other assembly as private/internal. Ensure there's a comment on each list about keeping them in sync.
There was a problem hiding this comment.
Reverted the SupportedProtocolVersions public API from McpServer, McpServerImpl, and DestinationBoundMcpServer. The supported protocol versions are now a private HashSet<string> directly in StreamableHttpHandler, with comments on both lists (in StreamableHttpHandler and McpSessionHandler) about keeping them in sync. Validation is back to before session resolution. a340156
…nc comments Remove the abstract SupportedProtocolVersions property from McpServer, McpServerImpl, and DestinationBoundMcpServer. Instead, keep a private HashSet in StreamableHttpHandler with the supported protocol versions, and add comments on both lists about keeping them in sync. Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Head branch was pushed to by a user without write access
Per the 2025-06-18 MCP spec: if the server receives a request with an invalid or unsupported
MCP-Protocol-Versionheader, it MUST respond with 400 Bad Request. A missing header is not an error (backwards compatibility).Changes
StreamableHttpHandler.cs— Added protocol version validation at the top of POST, GET, and DELETE handlers. Supported versions are defined as a privateHashSet<string>directly in the handler, with a comment to keep in sync with the canonical list. Present but unsupported values → 400; absent header → allowed.McpSessionHandler.cs— Added a sync comment on the internalSupportedProtocolVersionslist referencing the copy inStreamableHttpHandler.StreamableHttpServerConformanceTests.cs— Added tests for invalid versions (400), missing header (OK), and valid header (OK) on both POST and GET.Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.