From 34fccd138c373d130b93f29141b1e7caafd382d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 21:09:58 +0000 Subject: [PATCH 1/3] Initial plan From 53606699aa318f1bfbbc893d60d6ba0faa69edd0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 21:20:29 +0000 Subject: [PATCH 2/3] Stabilize long-running timeout test with bounded retry Co-authored-by: halter73 <54385+halter73@users.noreply.github.com> --- .../MapMcpTests.cs | 65 ++++++++++++------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs b/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs index a03249496..cf8f198f9 100644 --- a/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs +++ b/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs @@ -245,34 +245,51 @@ public async Task LongRunningToolCall_DoesNotTimeout_WhenNoEventStreamStore() app.MapMcp(); await app.StartAsync(TestContext.Current.CancellationToken); - // Create a custom HttpClient with a very short timeout (1 second) - // The tool will take 2 seconds to complete - using var shortTimeoutClient = new HttpClient(SocketsHttpHandler) + // Retry a couple of times to reduce occasional flakiness on low-resource machines. + // If the server regresses to flushing only after tool completion, each attempt should still fail + // because HttpClient timeout (1 second) is below the tool duration (2 seconds). + for (var attempt = 0; attempt < 3; attempt++) { - BaseAddress = new Uri("http://localhost:5000/"), - Timeout = TimeSpan.FromSeconds(1) - }; - - var path = UseStreamableHttp ? "/" : "/sse"; - var transportMode = UseStreamableHttp ? HttpTransportMode.StreamableHttp : HttpTransportMode.Sse; - - await using var transport = new HttpClientTransport(new() - { - Endpoint = new($"http://localhost:5000{path}"), - TransportMode = transportMode, - }, shortTimeoutClient, LoggerFactory); + try + { + // Create a custom HttpClient with a very short timeout (1 second) + // The tool will take 2 seconds to complete + using var shortTimeoutClient = new HttpClient(SocketsHttpHandler) + { + BaseAddress = new Uri("http://localhost:5000/"), + Timeout = TimeSpan.FromSeconds(1) + }; - await using var mcpClient = await McpClient.CreateAsync(transport, loggerFactory: LoggerFactory, cancellationToken: TestContext.Current.CancellationToken); + var path = UseStreamableHttp ? "/" : "/sse"; + var transportMode = UseStreamableHttp ? HttpTransportMode.StreamableHttp : HttpTransportMode.Sse; - // Call a tool that takes 2 seconds - this should succeed despite the 1 second HttpClient timeout - // because the response stream is flushed immediately after receiving the request - var response = await mcpClient.CallToolAsync( - "long_running_operation", - new Dictionary() { ["durationMs"] = 2000 }, - cancellationToken: TestContext.Current.CancellationToken); + await using var transport = new HttpClientTransport(new() + { + Endpoint = new($"http://localhost:5000{path}"), + TransportMode = transportMode, + }, shortTimeoutClient, LoggerFactory); + + await using var mcpClient = await McpClient.CreateAsync(transport, loggerFactory: LoggerFactory, cancellationToken: TestContext.Current.CancellationToken); + + // Call a tool that takes 2 seconds - this should succeed despite the 1 second HttpClient timeout + // because the response stream is flushed immediately after receiving the request + var response = await mcpClient.CallToolAsync( + "long_running_operation", + new Dictionary() { ["durationMs"] = 2000 }, + cancellationToken: TestContext.Current.CancellationToken); + + var content = Assert.Single(response.Content.OfType()); + Assert.Equal("Operation completed after 2000ms", content.Text); + return; + } + catch (TaskCanceledException ex) when ( + attempt < 2 && + ex.ToString().Contains("HttpClient.Timeout", StringComparison.Ordinal)) + { + // Retry intermittent timeout-related failures on slow CI machines. + } + } - var content = Assert.Single(response.Content.OfType()); - Assert.Equal("Operation completed after 2000ms", content.Text); } private ClaimsPrincipal CreateUser(string name) From 4443f79974d5540e46e70b4cfe2b788fe5d22dc2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Feb 2026 01:20:55 +0000 Subject: [PATCH 3/3] Adjust retry catch to OperationCanceledException Co-authored-by: halter73 <54385+halter73@users.noreply.github.com> --- tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs b/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs index cf8f198f9..bee17be7c 100644 --- a/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs +++ b/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs @@ -254,7 +254,7 @@ public async Task LongRunningToolCall_DoesNotTimeout_WhenNoEventStreamStore() { // Create a custom HttpClient with a very short timeout (1 second) // The tool will take 2 seconds to complete - using var shortTimeoutClient = new HttpClient(SocketsHttpHandler) + using var shortTimeoutClient = new HttpClient(SocketsHttpHandler, disposeHandler: false) { BaseAddress = new Uri("http://localhost:5000/"), Timeout = TimeSpan.FromSeconds(1) @@ -282,9 +282,7 @@ public async Task LongRunningToolCall_DoesNotTimeout_WhenNoEventStreamStore() Assert.Equal("Operation completed after 2000ms", content.Text); return; } - catch (TaskCanceledException ex) when ( - attempt < 2 && - ex.ToString().Contains("HttpClient.Timeout", StringComparison.Ordinal)) + catch (OperationCanceledException) when (attempt < 2) { // Retry intermittent timeout-related failures on slow CI machines. }