Reject mismatched initialize protocol versions (#1724)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
4aade88558
commit
9df7640172
2 changed files with 39 additions and 2 deletions
|
|
@ -704,8 +704,8 @@ internal sealed class StreamableHttpHandler(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates that HTTP requests using per-request metadata declare the same protocol version in both
|
/// Validates that HTTP requests declare matching protocol versions in the
|
||||||
/// the <c>MCP-Protocol-Version</c> header and body <c>_meta</c> envelope.
|
/// <c>MCP-Protocol-Version</c> header and the corresponding body field.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static bool ValidateProtocolVersionEnvelope(
|
private static bool ValidateProtocolVersionEnvelope(
|
||||||
HttpContext context,
|
HttpContext context,
|
||||||
|
|
@ -719,6 +719,18 @@ internal sealed class StreamableHttpHandler(
|
||||||
}
|
}
|
||||||
|
|
||||||
var protocolVersionHeader = context.Request.Headers[McpProtocolVersionHeaderName].ToString();
|
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);
|
bool hasProtocolVersionMeta = TryGetProtocolVersionMeta(message, out var protocolVersionMeta);
|
||||||
|
|
||||||
if (!McpProtocolVersions.RequiresPerRequestMetadata(protocolVersionHeader) &&
|
if (!McpProtocolVersions.RequiresPerRequestMetadata(protocolVersionHeader) &&
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,31 @@ public class StreamableHttpServerConformanceTests(ITestOutputHelper outputHelper
|
||||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
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]
|
[Fact]
|
||||||
public async Task GetRequest_IsBadRequest_WithInvalidProtocolVersionHeader()
|
public async Task GetRequest_IsBadRequest_WithInvalidProtocolVersionHeader()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue