@@ -1016,6 +1016,113 @@ void testLoggingNotification(String clientType) throws InterruptedException {
10161016 mcpServer .close ();
10171017 }
10181018
1019+ // ---------------------------------------
1020+ // Progress Tests
1021+ // ---------------------------------------
1022+ @ ParameterizedTest (name = "{0} : {displayName} " )
1023+ @ ValueSource (strings = { "httpclient" , "webflux" })
1024+ void testProgressNotification (String clientType ) throws InterruptedException {
1025+ int expectedNotificationsCount = 4 ; // 3 notifications + 1 for another progress
1026+ // token
1027+ CountDownLatch latch = new CountDownLatch (expectedNotificationsCount );
1028+ // Create a list to store received logging notifications
1029+ List <McpSchema .ProgressNotification > receivedNotifications = new CopyOnWriteArrayList <>();
1030+
1031+ var clientBuilder = clientBuilders .get (clientType );
1032+
1033+ // Create server with a tool that sends logging notifications
1034+ McpServerFeatures .AsyncToolSpecification tool = McpServerFeatures .AsyncToolSpecification .builder ()
1035+ .tool (McpSchema .Tool .builder ()
1036+ .name ("progress-test" )
1037+ .description ("Test progress notifications" )
1038+ .inputSchema (emptyJsonSchema )
1039+ .build ())
1040+ .callHandler ((exchange , request ) -> {
1041+
1042+ // Create and send notifications
1043+ var progressToken = (String ) request .meta ().get ("progressToken" );
1044+
1045+ return exchange
1046+ .progressNotification (
1047+ new McpSchema .ProgressNotification (progressToken , 0.0 , 1.0 , "Processing started" ))
1048+ .then (exchange .progressNotification (
1049+ new McpSchema .ProgressNotification (progressToken , 0.5 , 1.0 , "Processing data" )))
1050+ .then (// Send a progress notification with another progress value
1051+ // should
1052+ exchange .progressNotification (new McpSchema .ProgressNotification ("another-progress-token" ,
1053+ 0.0 , 1.0 , "Another processing started" )))
1054+ .then (exchange .progressNotification (
1055+ new McpSchema .ProgressNotification (progressToken , 1.0 , 1.0 , "Processing completed" )))
1056+ .thenReturn (new CallToolResult (("Progress test completed" ), false ));
1057+ })
1058+ .build ();
1059+
1060+ var mcpServer = McpServer .async (mcpServerTransportProvider )
1061+ .serverInfo ("test-server" , "1.0.0" )
1062+ .capabilities (ServerCapabilities .builder ().tools (true ).build ())
1063+ .tools (tool )
1064+ .build ();
1065+
1066+ try (
1067+ // Create client with progress notification handler
1068+ var mcpClient = clientBuilder .progressConsumer (notification -> {
1069+ receivedNotifications .add (notification );
1070+ latch .countDown ();
1071+ }).build ()) {
1072+
1073+ // Initialize client
1074+ InitializeResult initResult = mcpClient .initialize ();
1075+ assertThat (initResult ).isNotNull ();
1076+
1077+ // Call the tool that sends progress notifications
1078+ McpSchema .CallToolRequest callToolRequest = McpSchema .CallToolRequest .builder ()
1079+ .name ("progress-test" )
1080+ .meta (Map .of ("progressToken" , "test-progress-token" ))
1081+ .build ();
1082+ CallToolResult result = mcpClient .callTool (callToolRequest );
1083+ assertThat (result ).isNotNull ();
1084+ assertThat (result .content ().get (0 )).isInstanceOf (McpSchema .TextContent .class );
1085+ assertThat (((McpSchema .TextContent ) result .content ().get (0 )).text ()).isEqualTo ("Progress test completed" );
1086+
1087+ assertThat (latch .await (5 , TimeUnit .SECONDS )).as ("Should receive notifications in reasonable time" ).isTrue ();
1088+
1089+ // Should have received 3 notifications
1090+ assertThat (receivedNotifications ).hasSize (expectedNotificationsCount );
1091+
1092+ Map <String , McpSchema .ProgressNotification > notificationMap = receivedNotifications .stream ()
1093+ .collect (Collectors .toMap (n -> n .message (), n -> n ));
1094+
1095+ // First notification should be 0.0/1.0 progress
1096+ assertThat (notificationMap .get ("Processing started" ).progressToken ()).isEqualTo ("test-progress-token" );
1097+ assertThat (notificationMap .get ("Processing started" ).progress ()).isEqualTo (0.0 );
1098+ assertThat (notificationMap .get ("Processing started" ).total ()).isEqualTo (1.0 );
1099+ assertThat (notificationMap .get ("Processing started" ).message ()).isEqualTo ("Processing started" );
1100+
1101+ // Second notification should be 0.5/1.0 progress
1102+ assertThat (notificationMap .get ("Processing data" ).progressToken ()).isEqualTo ("test-progress-token" );
1103+ assertThat (notificationMap .get ("Processing data" ).progress ()).isEqualTo (0.5 );
1104+ assertThat (notificationMap .get ("Processing data" ).total ()).isEqualTo (1.0 );
1105+ assertThat (notificationMap .get ("Processing data" ).message ()).isEqualTo ("Processing data" );
1106+
1107+ // Third notification should be another progress token with 0.0/1.0 progress
1108+ assertThat (notificationMap .get ("Another processing started" ).progressToken ())
1109+ .isEqualTo ("another-progress-token" );
1110+ assertThat (notificationMap .get ("Another processing started" ).progress ()).isEqualTo (0.0 );
1111+ assertThat (notificationMap .get ("Another processing started" ).total ()).isEqualTo (1.0 );
1112+ assertThat (notificationMap .get ("Another processing started" ).message ())
1113+ .isEqualTo ("Another processing started" );
1114+
1115+ // Fourth notification should be 1.0/1.0 progress
1116+ assertThat (notificationMap .get ("Processing completed" ).progressToken ()).isEqualTo ("test-progress-token" );
1117+ assertThat (notificationMap .get ("Processing completed" ).progress ()).isEqualTo (1.0 );
1118+ assertThat (notificationMap .get ("Processing completed" ).total ()).isEqualTo (1.0 );
1119+ assertThat (notificationMap .get ("Processing completed" ).message ()).isEqualTo ("Processing completed" );
1120+ }
1121+ finally {
1122+ mcpServer .close ();
1123+ }
1124+ }
1125+
10191126 // ---------------------------------------
10201127 // Completion Tests
10211128 // ---------------------------------------
0 commit comments