From 33f7076e24acd70de470fa0e2b967973d67ec371 Mon Sep 17 00:00:00 2001 From: Loi Nguyen Date: Sun, 19 Jul 2026 18:31:03 +0700 Subject: [PATCH] Fix invalid initialize request handling --- .../StreamableHttpHandler.cs | 23 +++++++++++++ .../StreamableHttpServerConformanceTests.cs | 34 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs b/src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs index 75281aa96..d5afbce41 100644 --- a/src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs +++ b/src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs @@ -45,6 +45,7 @@ internal sealed class StreamableHttpHandler( private static readonly JsonTypeInfo s_messageTypeInfo = GetRequiredJsonTypeInfo(); private static readonly JsonTypeInfo s_errorTypeInfo = GetRequiredJsonTypeInfo(); + private static readonly JsonTypeInfo s_initializeRequestParamsTypeInfo = GetRequiredJsonTypeInfo(); private static bool AllowNewSessionForNonInitializeRequests { get; } = AppContext.TryGetSwitch("ModelContextProtocol.AspNetCore.AllowNewSessionForNonInitializeRequests", out var enabled) && enabled; @@ -116,6 +117,28 @@ await WriteJsonRpcErrorAsync(context, return; } + if (message is JsonRpcRequest { Method: RequestMethods.Initialize } initializeRequest) + { + try + { + var initializeParams = JsonSerializer.Deserialize(initializeRequest.Params, s_initializeRequestParamsTypeInfo); + if (initializeParams is null) + { + await WriteJsonRpcErrorAsync(context, + "Bad Request: The initialize request parameters were invalid.", + StatusCodes.Status400BadRequest, (int)McpErrorCode.InvalidParams, requestId); + return; + } + } + catch (JsonException ex) + { + await WriteJsonRpcErrorAsync(context, + $"Bad Request: The initialize request parameters were invalid. {ex.Message}", + StatusCodes.Status400BadRequest, (int)McpErrorCode.InvalidParams, requestId); + return; + } + } + var session = await GetOrCreateSessionAsync(context, message, requestId); if (session is null) { diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/StreamableHttpServerConformanceTests.cs b/tests/ModelContextProtocol.AspNetCore.Tests/StreamableHttpServerConformanceTests.cs index 3422405a6..4b9ed0cef 100644 --- a/tests/ModelContextProtocol.AspNetCore.Tests/StreamableHttpServerConformanceTests.cs +++ b/tests/ModelContextProtocol.AspNetCore.Tests/StreamableHttpServerConformanceTests.cs @@ -318,6 +318,40 @@ public async Task PostMalformedJson_Returns400_InvalidRequest_WithNullId() Assert.Equal((int)McpErrorCode.InvalidRequest, doc.RootElement.GetProperty("error").GetProperty("code").GetInt32()); } + [Fact] + public async Task InitializeWithMissingClientVersion_Returns400_InvalidParams_WithRequestId() + { + await StartAsync(); + + const string request = """ + {"jsonrpc":"2.0","id":7,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"IntegrationTestClient"}}} + """; + + using var response = await HttpClient.PostAsync("", JsonContent(request), TestContext.Current.CancellationToken); + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + Assert.False(response.Headers.Contains("mcp-session-id")); + + using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); + Assert.Equal(7, doc.RootElement.GetProperty("id").GetInt64()); + Assert.Equal((int)McpErrorCode.InvalidParams, doc.RootElement.GetProperty("error").GetProperty("code").GetInt32()); + Assert.Contains("version", doc.RootElement.GetProperty("error").GetProperty("message").GetString(), StringComparison.OrdinalIgnoreCase); + } + + [Theory] + [InlineData("{\"jsonrpc\":\"2.0\",\"id\":8,\"method\":\"initialize\"}")] + [InlineData("{\"jsonrpc\":\"2.0\",\"id\":9,\"method\":\"initialize\",\"params\":null}")] + public async Task InitializeWithMissingOrNullParams_Returns400_InvalidParams_WithoutCreatingSession(string request) + { + await StartAsync(); + + using var response = await HttpClient.PostAsync("", JsonContent(request), TestContext.Current.CancellationToken); + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + Assert.False(response.Headers.Contains("mcp-session-id")); + + using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); + Assert.Equal((int)McpErrorCode.InvalidParams, doc.RootElement.GetProperty("error").GetProperty("code").GetInt32()); + } + [Fact] public async Task PostRequestWithExplicitNullId_Returns400_InvalidRequest_WithNullId() {