Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -704,8 +704,8 @@ private static string[] GetConfiguredSupportedProtocolVersions(string? protocolV
}

/// <summary>
/// Validates that HTTP requests using per-request metadata declare the same protocol version in both
/// the <c>MCP-Protocol-Version</c> header and body <c>_meta</c> envelope.
/// Validates that HTTP requests declare matching protocol versions in the
/// <c>MCP-Protocol-Version</c> header and the corresponding body field.
/// </summary>
private static bool ValidateProtocolVersionEnvelope(
HttpContext context,
Expand All @@ -719,6 +719,18 @@ private static bool ValidateProtocolVersionEnvelope(
}

var protocolVersionHeader = context.Request.Headers[McpProtocolVersionHeaderName].ToString();

if (message is JsonRpcRequest { Method: RequestMethods.Initialize, Params: JsonObject initializeParams } &&
initializeParams["protocolVersion"] is JsonValue initializeProtocolVersionValue &&
initializeProtocolVersionValue.TryGetValue(out string? initializeProtocolVersion) &&
!string.IsNullOrEmpty(protocolVersionHeader) &&
!string.Equals(protocolVersionHeader, initializeProtocolVersion, StringComparison.Ordinal))
{
errorDetail = CreateHeaderMismatchError(
$"Bad Request: The {McpProtocolVersionHeaderName} header value '{protocolVersionHeader}' does not match body params.protocolVersion value '{initializeProtocolVersion}'.");
return false;
}

bool hasProtocolVersionMeta = TryGetProtocolVersionMeta(message, out var protocolVersionMeta);

if (!McpProtocolVersions.RequiresPerRequestMetadata(protocolVersionHeader) &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,31 @@ public async Task PostRequest_Succeeds_WithValidProtocolVersionHeader()
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

[Theory]
[InlineData(false, McpProtocolVersions.March2025ProtocolVersion, McpProtocolVersions.November2025ProtocolVersion)]
[InlineData(false, McpProtocolVersions.November2025ProtocolVersion, McpProtocolVersions.March2025ProtocolVersion)]
[InlineData(true, McpProtocolVersions.March2025ProtocolVersion, McpProtocolVersions.November2025ProtocolVersion)]
[InlineData(true, McpProtocolVersions.November2025ProtocolVersion, McpProtocolVersions.March2025ProtocolVersion)]
public async Task InitializeRequest_IsBadRequest_WhenProtocolVersionHeaderDoesNotMatchBody(
bool stateless,
string protocolVersionHeader,
string protocolVersionBody)
{
await StartAsync(stateless);

var body = $$$$"""
{"jsonrpc":"2.0","id":4242,"method":"initialize","params":{"protocolVersion":"{{{{protocolVersionBody}}}}","capabilities":{},"clientInfo":{"name":"IntegrationTestClient","version":"1.0.0"}}}
""";
using var request = new HttpRequestMessage(HttpMethod.Post, "") { Content = JsonContent(body) };
request.Headers.Add("MCP-Protocol-Version", protocolVersionHeader);
using var response = await HttpClient.SendAsync(request, TestContext.Current.CancellationToken);

Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
var json = JsonNode.Parse(await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken));
Assert.Equal(4242, json!["id"]!.GetValue<long>());
Assert.Equal((int)McpErrorCode.HeaderMismatch, json["error"]!["code"]!.GetValue<int>());
}

[Fact]
public async Task GetRequest_IsBadRequest_WithInvalidProtocolVersionHeader()
{
Expand Down