@@ -854,4 +854,99 @@ public async Task CallToolAsync_WithAnonymousTypeArguments_Works()
854854 var textBlock = Assert . IsType < TextContentBlock > ( result . Content [ 0 ] ) ;
855855 Assert . Contains ( "coordinates" , textBlock . Text ) ;
856856 }
857+
858+ [ Theory ]
859+ [ InlineData ( false ) ]
860+ [ InlineData ( true ) ]
861+ public async Task WithProgress_ProgressTokenInMeta ( bool useInvokeAsync )
862+ {
863+ await using McpClient client = await CreateMcpClientForServer ( ) ;
864+
865+ var tools = await client . ListToolsAsync ( cancellationToken : TestContext . Current . CancellationToken ) ;
866+ var tool = tools . Single ( t => t . Name == "metadata_echo_tool" ) ;
867+
868+ var receivedMetadata = await CallMetadataEchoToolWithProgressAsync ( tool , useInvokeAsync ) ;
869+ Assert . NotNull ( receivedMetadata ) ;
870+ Assert . NotNull ( receivedMetadata [ "progressToken" ] ? . GetValue < string > ( ) ) ;
871+ }
872+
873+ [ Theory ]
874+ [ InlineData ( false ) ]
875+ [ InlineData ( true ) ]
876+ public async Task WithMeta_WithProgress_BothMetaAndProgressTokenPresent ( bool useInvokeAsync )
877+ {
878+ await using McpClient client = await CreateMcpClientForServer ( ) ;
879+
880+ var tools = await client . ListToolsAsync ( cancellationToken : TestContext . Current . CancellationToken ) ;
881+ var tool = tools . Single ( t => t . Name == "metadata_echo_tool" )
882+ . WithMeta ( new ( ) { [ "traceId" ] = "trace-123" } ) ;
883+
884+ var receivedMetadata = await CallMetadataEchoToolWithProgressAsync ( tool , useInvokeAsync ) ;
885+ Assert . NotNull ( receivedMetadata ) ;
886+ Assert . Equal ( "trace-123" , receivedMetadata [ "traceId" ] ? . GetValue < string > ( ) ) ;
887+ Assert . NotNull ( receivedMetadata [ "progressToken" ] ? . GetValue < string > ( ) ) ;
888+ }
889+
890+ [ Theory ]
891+ [ InlineData ( false ) ]
892+ [ InlineData ( true ) ]
893+ public async Task WithMeta_WithProgress_DoesNotMutateOriginalMeta ( bool useInvokeAsync )
894+ {
895+ await using McpClient client = await CreateMcpClientForServer ( ) ;
896+
897+ var tools = await client . ListToolsAsync ( cancellationToken : TestContext . Current . CancellationToken ) ;
898+ var tool = tools . Single ( t => t . Name == "metadata_echo_tool" ) ;
899+
900+ JsonObject originalMeta = new ( ) { [ "traceId" ] = "trace-789" } ;
901+ var toolWithMeta = tool . WithMeta ( originalMeta ) ;
902+
903+ await CallMetadataEchoToolWithProgressAsync ( toolWithMeta , useInvokeAsync ) ;
904+ await CallMetadataEchoToolWithProgressAsync ( toolWithMeta , useInvokeAsync ) ;
905+
906+ Assert . Single ( originalMeta ) ;
907+ Assert . Equal ( "trace-789" , originalMeta [ "traceId" ] ? . GetValue < string > ( ) ) ;
908+ Assert . False ( originalMeta . ContainsKey ( "progressToken" ) ) ;
909+ }
910+
911+ [ Fact ]
912+ public async Task WithMeta_WithProgress_WithRequestOptionsMeta_AllMerged ( )
913+ {
914+ await using McpClient client = await CreateMcpClientForServer ( ) ;
915+
916+ var tools = await client . ListToolsAsync ( cancellationToken : TestContext . Current . CancellationToken ) ;
917+ var tool = tools . Single ( t => t . Name == "metadata_echo_tool" )
918+ . WithMeta ( new ( ) { [ "toolKey" ] = "toolValue" } ) ;
919+
920+ RequestOptions requestOptions = new ( )
921+ {
922+ Meta = new ( ) { [ "requestKey" ] = "requestValue" }
923+ } ;
924+
925+ var receivedMetadata = await CallMetadataEchoToolWithProgressAsync ( tool , useInvokeAsync : false , requestOptions ) ;
926+ Assert . NotNull ( receivedMetadata ) ;
927+ Assert . Equal ( "toolValue" , receivedMetadata [ "toolKey" ] ? . GetValue < string > ( ) ) ;
928+ Assert . Equal ( "requestValue" , receivedMetadata [ "requestKey" ] ? . GetValue < string > ( ) ) ;
929+ Assert . NotNull ( receivedMetadata [ "progressToken" ] ? . GetValue < string > ( ) ) ;
930+ }
931+
932+ private static async Task < JsonObject ? > CallMetadataEchoToolWithProgressAsync (
933+ McpClientTool tool , bool useInvokeAsync , RequestOptions ? options = null )
934+ {
935+ var progress = new Progress < ProgressNotificationValue > ( ) ;
936+ string text ;
937+
938+ if ( useInvokeAsync )
939+ {
940+ tool = tool . WithProgress ( progress ) ;
941+ var result = await tool . InvokeAsync ( cancellationToken : TestContext . Current . CancellationToken ) ;
942+ text = Assert . IsType < TextContent > ( result ) . Text ;
943+ }
944+ else
945+ {
946+ var result = await tool . CallAsync ( progress : progress , options : options , cancellationToken : TestContext . Current . CancellationToken ) ;
947+ text = Assert . IsType < TextContentBlock > ( result . Content . Single ( ) ) . Text ;
948+ }
949+
950+ return JsonNode . Parse ( text ) ? . AsObject ( ) ;
951+ }
857952}
0 commit comments