@@ -245,34 +245,49 @@ public async Task LongRunningToolCall_DoesNotTimeout_WhenNoEventStreamStore()
245245 app . MapMcp ( ) ;
246246 await app . StartAsync ( TestContext . Current . CancellationToken ) ;
247247
248- // Create a custom HttpClient with a very short timeout (1 second)
249- // The tool will take 2 seconds to complete
250- using var shortTimeoutClient = new HttpClient ( SocketsHttpHandler )
248+ // Retry a couple of times to reduce occasional flakiness on low-resource machines.
249+ // If the server regresses to flushing only after tool completion, each attempt should still fail
250+ // because HttpClient timeout (1 second) is below the tool duration (2 seconds).
251+ for ( var attempt = 0 ; attempt < 3 ; attempt ++ )
251252 {
252- BaseAddress = new Uri ( "http://localhost:5000/" ) ,
253- Timeout = TimeSpan . FromSeconds ( 1 )
254- } ;
255-
256- var path = UseStreamableHttp ? "/" : "/sse" ;
257- var transportMode = UseStreamableHttp ? HttpTransportMode . StreamableHttp : HttpTransportMode . Sse ;
258-
259- await using var transport = new HttpClientTransport ( new ( )
260- {
261- Endpoint = new ( $ "http://localhost:5000{ path } ") ,
262- TransportMode = transportMode ,
263- } , shortTimeoutClient , LoggerFactory ) ;
253+ try
254+ {
255+ // Create a custom HttpClient with a very short timeout (1 second)
256+ // The tool will take 2 seconds to complete
257+ using var shortTimeoutClient = new HttpClient ( SocketsHttpHandler , disposeHandler : false )
258+ {
259+ BaseAddress = new Uri ( "http://localhost:5000/" ) ,
260+ Timeout = TimeSpan . FromSeconds ( 1 )
261+ } ;
264262
265- await using var mcpClient = await McpClient . CreateAsync ( transport , loggerFactory : LoggerFactory , cancellationToken : TestContext . Current . CancellationToken ) ;
263+ var path = UseStreamableHttp ? "/" : "/sse" ;
264+ var transportMode = UseStreamableHttp ? HttpTransportMode . StreamableHttp : HttpTransportMode . Sse ;
266265
267- // Call a tool that takes 2 seconds - this should succeed despite the 1 second HttpClient timeout
268- // because the response stream is flushed immediately after receiving the request
269- var response = await mcpClient . CallToolAsync (
270- "long_running_operation" ,
271- new Dictionary < string , object ? > ( ) { [ "durationMs" ] = 2000 } ,
272- cancellationToken : TestContext . Current . CancellationToken ) ;
266+ await using var transport = new HttpClientTransport ( new ( )
267+ {
268+ Endpoint = new ( $ "http://localhost:5000{ path } ") ,
269+ TransportMode = transportMode ,
270+ } , shortTimeoutClient , LoggerFactory ) ;
271+
272+ await using var mcpClient = await McpClient . CreateAsync ( transport , loggerFactory : LoggerFactory , cancellationToken : TestContext . Current . CancellationToken ) ;
273+
274+ // Call a tool that takes 2 seconds - this should succeed despite the 1 second HttpClient timeout
275+ // because the response stream is flushed immediately after receiving the request
276+ var response = await mcpClient . CallToolAsync (
277+ "long_running_operation" ,
278+ new Dictionary < string , object ? > ( ) { [ "durationMs" ] = 2000 } ,
279+ cancellationToken : TestContext . Current . CancellationToken ) ;
280+
281+ var content = Assert . Single ( response . Content . OfType < TextContentBlock > ( ) ) ;
282+ Assert . Equal ( "Operation completed after 2000ms" , content . Text ) ;
283+ return ;
284+ }
285+ catch ( OperationCanceledException ) when ( attempt < 2 )
286+ {
287+ // Retry intermittent timeout-related failures on slow CI machines.
288+ }
289+ }
273290
274- var content = Assert . Single ( response . Content . OfType < TextContentBlock > ( ) ) ;
275- Assert . Equal ( "Operation completed after 2000ms" , content . Text ) ;
276291 }
277292
278293 private ClaimsPrincipal CreateUser ( string name )
0 commit comments