@@ -29,7 +29,6 @@ protected override void ConfigureServices(ServiceCollection services, IMcpServer
2929 }
3030 services . AddSingleton ( McpServerTool . Create ( [ McpServerTool ( Destructive = false , OpenWorld = true ) ] ( string i ) => $ "{ i } Result", new ( ) { Name = "ValuesSetViaAttr" } ) ) ;
3131 services . AddSingleton ( McpServerTool . Create ( [ McpServerTool ( Destructive = false , OpenWorld = true ) ] ( string i ) => $ "{ i } Result", new ( ) { Name = "ValuesSetViaOptions" , Destructive = true , OpenWorld = false , ReadOnly = true } ) ) ;
32-
3332 }
3433
3534 [ Theory ]
@@ -209,7 +208,7 @@ public async Task CreateSamplingHandler_ShouldHandleResourceMessages()
209208 [ Fact ]
210209 public async Task ListToolsAsync_AllToolsReturned ( )
211210 {
212- IMcpClient client = await CreateMcpClientForServer ( ) ;
211+ await using IMcpClient client = await CreateMcpClientForServer ( ) ;
213212
214213 var tools = await client . ListToolsAsync ( cancellationToken : TestContext . Current . CancellationToken ) ;
215214 Assert . Equal ( 12 , tools . Count ) ;
@@ -235,7 +234,7 @@ public async Task ListToolsAsync_AllToolsReturned()
235234 [ Fact ]
236235 public async Task EnumerateToolsAsync_AllToolsReturned ( )
237236 {
238- IMcpClient client = await CreateMcpClientForServer ( ) ;
237+ await using IMcpClient client = await CreateMcpClientForServer ( ) ;
239238
240239 await foreach ( var tool in client . EnumerateToolsAsync ( cancellationToken : TestContext . Current . CancellationToken ) )
241240 {
@@ -254,7 +253,7 @@ public async Task EnumerateToolsAsync_AllToolsReturned()
254253 public async Task EnumerateToolsAsync_FlowsJsonSerializerOptions ( )
255254 {
256255 JsonSerializerOptions options = new ( JsonSerializerOptions . Default ) ;
257- IMcpClient client = await CreateMcpClientForServer ( ) ;
256+ await using IMcpClient client = await CreateMcpClientForServer ( ) ;
258257 bool hasTools = false ;
259258
260259 await foreach ( var tool in client . EnumerateToolsAsync ( options , TestContext . Current . CancellationToken ) )
@@ -275,7 +274,7 @@ public async Task EnumerateToolsAsync_FlowsJsonSerializerOptions()
275274 public async Task EnumerateToolsAsync_HonorsJsonSerializerOptions ( )
276275 {
277276 JsonSerializerOptions emptyOptions = new ( ) { TypeInfoResolver = JsonTypeInfoResolver . Combine ( ) } ;
278- IMcpClient client = await CreateMcpClientForServer ( ) ;
277+ await using IMcpClient client = await CreateMcpClientForServer ( ) ;
279278
280279 var tool = ( await client . ListToolsAsync ( emptyOptions , TestContext . Current . CancellationToken ) ) . First ( ) ;
281280 await Assert . ThrowsAsync < NotSupportedException > ( async ( ) => await tool . InvokeAsync ( new ( ) { [ "i" ] = 42 } , TestContext . Current . CancellationToken ) ) ;
@@ -285,7 +284,7 @@ public async Task EnumerateToolsAsync_HonorsJsonSerializerOptions()
285284 public async Task SendRequestAsync_HonorsJsonSerializerOptions ( )
286285 {
287286 JsonSerializerOptions emptyOptions = new ( ) { TypeInfoResolver = JsonTypeInfoResolver . Combine ( ) } ;
288- IMcpClient client = await CreateMcpClientForServer ( ) ;
287+ await using IMcpClient client = await CreateMcpClientForServer ( ) ;
289288
290289 await Assert . ThrowsAsync < NotSupportedException > ( ( ) => client . SendRequestAsync < CallToolRequestParams , CallToolResponse > ( "Method4" , new ( ) { Name = "tool" } , emptyOptions , cancellationToken : TestContext . Current . CancellationToken ) ) ;
291290 }
@@ -294,7 +293,7 @@ public async Task SendRequestAsync_HonorsJsonSerializerOptions()
294293 public async Task SendNotificationAsync_HonorsJsonSerializerOptions ( )
295294 {
296295 JsonSerializerOptions emptyOptions = new ( ) { TypeInfoResolver = JsonTypeInfoResolver . Combine ( ) } ;
297- IMcpClient client = await CreateMcpClientForServer ( ) ;
296+ await using IMcpClient client = await CreateMcpClientForServer ( ) ;
298297
299298 await Assert . ThrowsAsync < NotSupportedException > ( ( ) => client . SendNotificationAsync ( "Method4" , new { Value = 42 } , emptyOptions , cancellationToken : TestContext . Current . CancellationToken ) ) ;
300299 }
@@ -303,7 +302,7 @@ public async Task SendNotificationAsync_HonorsJsonSerializerOptions()
303302 public async Task GetPromptsAsync_HonorsJsonSerializerOptions ( )
304303 {
305304 JsonSerializerOptions emptyOptions = new ( ) { TypeInfoResolver = JsonTypeInfoResolver . Combine ( ) } ;
306- IMcpClient client = await CreateMcpClientForServer ( ) ;
305+ await using IMcpClient client = await CreateMcpClientForServer ( ) ;
307306
308307 await Assert . ThrowsAsync < NotSupportedException > ( ( ) => client . GetPromptAsync ( "Prompt" , new Dictionary < string , object ? > { [ "i" ] = 42 } , emptyOptions , cancellationToken : TestContext . Current . CancellationToken ) ) ;
309308 }
@@ -312,7 +311,7 @@ public async Task GetPromptsAsync_HonorsJsonSerializerOptions()
312311 public async Task WithName_ChangesToolName ( )
313312 {
314313 JsonSerializerOptions options = new ( JsonSerializerOptions . Default ) ;
315- IMcpClient client = await CreateMcpClientForServer ( ) ;
314+ await using IMcpClient client = await CreateMcpClientForServer ( ) ;
316315
317316 var tool = ( await client . ListToolsAsync ( options , TestContext . Current . CancellationToken ) ) . First ( ) ;
318317 var originalName = tool . Name ;
@@ -327,7 +326,7 @@ public async Task WithName_ChangesToolName()
327326 public async Task WithDescription_ChangesToolDescription ( )
328327 {
329328 JsonSerializerOptions options = new ( JsonSerializerOptions . Default ) ;
330- IMcpClient client = await CreateMcpClientForServer ( ) ;
329+ await using IMcpClient client = await CreateMcpClientForServer ( ) ;
331330 var tool = ( await client . ListToolsAsync ( options , TestContext . Current . CancellationToken ) ) . FirstOrDefault ( ) ;
332331 var originalDescription = tool ? . Description ;
333332 var redescribedTool = tool ? . WithDescription ( "ToolWithNewDescription" ) ;
@@ -336,10 +335,55 @@ public async Task WithDescription_ChangesToolDescription()
336335 Assert . Equal ( originalDescription , tool ? . Description ) ;
337336 }
338337
338+ [ Fact ]
339+ public async Task WithProgress_ProgressReported ( )
340+ {
341+ const int TotalNotifications = 3 ;
342+ int remainingProgress = TotalNotifications ;
343+ TaskCompletionSource < bool > allProgressReceived = new ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
344+
345+ Server . ServerOptions . Capabilities ? . Tools ? . ToolCollection ? . Add ( McpServerTool . Create ( async ( IProgress < ProgressNotificationValue > progress ) =>
346+ {
347+ for ( int i = 0 ; i < TotalNotifications ; i ++ )
348+ {
349+ progress . Report ( new ProgressNotificationValue { Progress = i * 10 , Message = "making progress" } ) ;
350+ await Task . Delay ( 1 ) ;
351+ }
352+
353+ await allProgressReceived . Task ;
354+
355+ return 42 ;
356+ } , new ( ) { Name = "ProgressReporter" } ) ) ;
357+
358+ await using IMcpClient client = await CreateMcpClientForServer ( ) ;
359+
360+ var tool = ( await client . ListToolsAsync ( cancellationToken : TestContext . Current . CancellationToken ) ) . First ( t => t . Name == "ProgressReporter" ) ;
361+
362+ IProgress < ProgressNotificationValue > progress = new SynchronousProgress ( value =>
363+ {
364+ Assert . True ( value . Progress >= 0 && value . Progress <= 100 ) ;
365+ Assert . Equal ( "making progress" , value . Message ) ;
366+ if ( Interlocked . Decrement ( ref remainingProgress ) == 0 )
367+ {
368+ allProgressReceived . SetResult ( true ) ;
369+ }
370+ } ) ;
371+
372+ Assert . Throws < ArgumentNullException > ( "progress" , ( ) => tool . WithProgress ( null ! ) ) ;
373+
374+ var result = await tool . WithProgress ( progress ) . InvokeAsync ( cancellationToken : TestContext . Current . CancellationToken ) ;
375+ Assert . Contains ( "42" , result ? . ToString ( ) ) ;
376+ }
377+
378+ private sealed class SynchronousProgress ( Action < ProgressNotificationValue > callback ) : IProgress < ProgressNotificationValue >
379+ {
380+ public void Report ( ProgressNotificationValue value ) => callback ( value ) ;
381+ }
382+
339383 [ Fact ]
340384 public async Task AsClientLoggerProvider_MessagesSentToClient ( )
341385 {
342- IMcpClient client = await CreateMcpClientForServer ( ) ;
386+ await using IMcpClient client = await CreateMcpClientForServer ( ) ;
343387
344388 ILoggerProvider loggerProvider = Server . AsClientLoggerProvider ( ) ;
345389 Assert . Throws < ArgumentNullException > ( "categoryName" , ( ) => loggerProvider . CreateLogger ( null ! ) ) ;
0 commit comments