4
4
package io .modelcontextprotocol ;
5
5
6
6
import java .time .Duration ;
7
+ import java .util .ArrayList ;
7
8
import java .util .List ;
8
9
import java .util .Map ;
9
10
import java .util .concurrent .ConcurrentHashMap ;
12
13
13
14
import com .fasterxml .jackson .databind .ObjectMapper ;
14
15
import io .modelcontextprotocol .client .McpClient ;
16
+ import io .modelcontextprotocol .client .McpSyncClient ;
15
17
import io .modelcontextprotocol .client .transport .HttpClientSseClientTransport ;
16
18
import io .modelcontextprotocol .client .transport .WebFluxSseClientTransport ;
17
19
import io .modelcontextprotocol .server .McpServer ;
@@ -111,7 +113,7 @@ void testCreateMessageWithoutSamplingCapabilities(String clientType) {
111
113
return Mono .just (mock (CallToolResult .class ));
112
114
});
113
115
114
- McpServer .async (mcpServerTransportProvider ).serverInfo ("test-server" , "1.0.0" ).tools (tool ).build ();
116
+ var server = McpServer .async (mcpServerTransportProvider ).serverInfo ("test-server" , "1.0.0" ).tools (tool ).build ();
115
117
116
118
// Create client without sampling capabilities
117
119
var client = clientBuilder .clientInfo (new McpSchema .Implementation ("Sample " + "client" , "0.0.0" )).build ();
@@ -125,6 +127,9 @@ void testCreateMessageWithoutSamplingCapabilities(String clientType) {
125
127
assertThat (e ).isInstanceOf (McpError .class )
126
128
.hasMessage ("Client must be configured with sampling capabilities" );
127
129
}
130
+
131
+ server .close ();
132
+ client .close ();
128
133
}
129
134
130
135
@ ParameterizedTest (name = "{0} : {displayName} " )
@@ -293,8 +298,7 @@ void testRootsNotifciationWithEmptyRootsList(String clientType) {
293
298
.roots (List .of ()) // Empty roots list
294
299
.build ();
295
300
296
- InitializeResult initResult = mcpClient .initialize ();
297
- assertThat (initResult ).isNotNull ();
301
+ assertThat (mcpClient .initialize ()).isNotNull ();
298
302
299
303
mcpClient .rootsListChangedNotification ();
300
304
@@ -309,6 +313,7 @@ void testRootsNotifciationWithEmptyRootsList(String clientType) {
309
313
@ ParameterizedTest (name = "{0} : {displayName} " )
310
314
@ ValueSource (strings = { "httpclient" , "webflux" })
311
315
void testRootsWithMultipleHandlers (String clientType ) {
316
+
312
317
var clientBuilder = clientBuilders .get (clientType );
313
318
314
319
List <Root > roots = List .of (new Root ("uri1://" , "root1" ));
@@ -339,8 +344,8 @@ void testRootsWithMultipleHandlers(String clientType) {
339
344
mcpServer .close ();
340
345
}
341
346
342
- @ ParameterizedTest (name = "{0} : {displayName} " )
343
- @ ValueSource (strings = { "httpclient" , "webflux" })
347
+ // @ParameterizedTest(name = "{0} : {displayName} ")
348
+ // @ValueSource(strings = { "httpclient", "webflux" })
344
349
void testRootsServerCloseWithActiveSubscription (String clientType ) {
345
350
346
351
var clientBuilder = clientBuilders .get (clientType );
@@ -365,10 +370,7 @@ void testRootsServerCloseWithActiveSubscription(String clientType) {
365
370
assertThat (rootsRef .get ()).containsAll (roots );
366
371
});
367
372
368
- // Close server while subscription is active
369
373
mcpServer .close ();
370
-
371
- // Verify client can handle server closure gracefully
372
374
mcpClient .close ();
373
375
}
374
376
@@ -378,9 +380,9 @@ void testRootsServerCloseWithActiveSubscription(String clientType) {
378
380
379
381
String emptyJsonSchema = """
380
382
{
381
- "$schema ": "http://json-schema.org/draft-07/schema#",
382
- "type": "object",
383
- "properties": {}
383
+ " ": "http://json-schema.org/draft-07/schema#",
384
+ "type": "object",
385
+ "properties": {}
384
386
}
385
387
""" ;
386
388
@@ -396,7 +398,7 @@ void testToolCallSuccess(String clientType) {
396
398
// perform a blocking call to a remote service
397
399
String response = RestClient .create ()
398
400
.get ()
399
- .uri ("https://github. com/modelcontextprotocol/specification/blob /main/README.md" )
401
+ .uri ("https://raw.githubusercontent. com/modelcontextprotocol/modelcontextprotocol/refs/heads /main/README.md" )
400
402
.retrieve ()
401
403
.body (String .class );
402
404
assertThat (response ).isNotBlank ();
@@ -436,7 +438,7 @@ void testToolListChangeHandlingSuccess(String clientType) {
436
438
// perform a blocking call to a remote service
437
439
String response = RestClient .create ()
438
440
.get ()
439
- .uri ("https://github. com/modelcontextprotocol/specification/blob /main/README.md" )
441
+ .uri ("https://raw.githubusercontent. com/modelcontextprotocol/modelcontextprotocol/refs/heads /main/README.md" )
440
442
.retrieve ()
441
443
.body (String .class );
442
444
assertThat (response ).isNotBlank ();
@@ -453,7 +455,7 @@ void testToolListChangeHandlingSuccess(String clientType) {
453
455
// perform a blocking call to a remote service
454
456
String response = RestClient .create ()
455
457
.get ()
456
- .uri ("https://github. com/modelcontextprotocol/specification/blob /main/README.md" )
458
+ .uri ("https://raw.githubusercontent. com/modelcontextprotocol/modelcontextprotocol/refs/heads /main/README.md" )
457
459
.retrieve ()
458
460
.body (String .class );
459
461
assertThat (response ).isNotBlank ();
@@ -511,4 +513,108 @@ void testInitialize(String clientType) {
511
513
mcpServer .close ();
512
514
}
513
515
516
+ // ---------------------------------------
517
+ // Logging Tests
518
+ // ---------------------------------------
519
+
520
+ @ ParameterizedTest (name = "{0} : {displayName} " )
521
+ @ ValueSource (strings = { "httpclient" , "webflux" })
522
+ void testLoggingNotification (String clientType ) {
523
+ // Create a list to store received logging notifications
524
+ List <McpSchema .LoggingMessageNotification > receivedNotifications = new ArrayList <>();
525
+
526
+ var clientBuilder = clientBuilders .get (clientType );
527
+
528
+ // Create server with a tool that sends logging notifications
529
+ McpServerFeatures .AsyncToolSpecification tool = new McpServerFeatures .AsyncToolSpecification (
530
+ new McpSchema .Tool ("logging-test" , "Test logging notifications" , emptyJsonSchema ),
531
+ (exchange , request ) -> {
532
+
533
+ // Create and send notifications with different levels
534
+
535
+ //@formatter:off
536
+ return exchange // This should be filtered out (DEBUG < NOTICE)
537
+ .loggingNotification (McpSchema .LoggingMessageNotification .builder ()
538
+ .level (McpSchema .LoggingLevel .DEBUG )
539
+ .logger ("test-logger" )
540
+ .data ("Debug message" )
541
+ .build ())
542
+ .then (exchange // This should be sent (NOTICE >= NOTICE)
543
+ .loggingNotification (McpSchema .LoggingMessageNotification .builder ()
544
+ .level (McpSchema .LoggingLevel .NOTICE )
545
+ .logger ("test-logger" )
546
+ .data ("Notice message" )
547
+ .build ()))
548
+ .then (exchange // This should be sent (ERROR > NOTICE)
549
+ .loggingNotification (McpSchema .LoggingMessageNotification .builder ()
550
+ .level (McpSchema .LoggingLevel .ERROR )
551
+ .logger ("test-logger" )
552
+ .data ("Error message" )
553
+ .build ()))
554
+ .then (exchange // This should be filtered out (INFO < NOTICE)
555
+ .loggingNotification (McpSchema .LoggingMessageNotification .builder ()
556
+ .level (McpSchema .LoggingLevel .INFO )
557
+ .logger ("test-logger" )
558
+ .data ("Another info message" )
559
+ .build ()))
560
+ .then (exchange // This should be sent (ERROR >= NOTICE)
561
+ .loggingNotification (McpSchema .LoggingMessageNotification .builder ()
562
+ .level (McpSchema .LoggingLevel .ERROR )
563
+ .logger ("test-logger" )
564
+ .data ("Another error message" )
565
+ .build ()))
566
+ .thenReturn (new CallToolResult ("Logging test completed" , false ));
567
+ //@formatter:on
568
+ });
569
+
570
+ var mcpServer = McpServer .async (mcpServerTransportProvider )
571
+ .serverInfo ("test-server" , "1.0.0" )
572
+ .capabilities (ServerCapabilities .builder ().logging ().tools (true ).build ())
573
+ .tools (tool )
574
+ .build ();
575
+
576
+ // Create client with logging notification handler
577
+ McpSyncClient mcpClient = clientBuilder .loggingConsumer (notification -> {
578
+ receivedNotifications .add (notification );
579
+ }).build ();
580
+
581
+ // Initialize client
582
+ InitializeResult initResult = mcpClient .initialize ();
583
+ assertThat (initResult ).isNotNull ();
584
+
585
+ // Set minimum logging level to NOTICE
586
+ mcpClient .setLoggingLevel (McpSchema .LoggingLevel .NOTICE );
587
+
588
+ // Call the tool that sends logging notifications
589
+ CallToolResult result = mcpClient .callTool (new McpSchema .CallToolRequest ("logging-test" , Map .of ()));
590
+ assertThat (result ).isNotNull ();
591
+ assertThat (result .content ().get (0 )).isInstanceOf (McpSchema .TextContent .class );
592
+ assertThat (((McpSchema .TextContent ) result .content ().get (0 )).text ()).isEqualTo ("Logging test completed" );
593
+
594
+ // Wait for notifications to be processed
595
+ await ().atMost (Duration .ofSeconds (5 )).untilAsserted (() -> {
596
+
597
+ // Should have received 3 notifications (1 NOTICE and 2 ERROR)
598
+ assertThat (receivedNotifications ).hasSize (3 );
599
+
600
+ // First notification should be NOTICE level
601
+ assertThat (receivedNotifications .get (0 ).level ()).isEqualTo (McpSchema .LoggingLevel .NOTICE );
602
+ assertThat (receivedNotifications .get (0 ).logger ()).isEqualTo ("test-logger" );
603
+ assertThat (receivedNotifications .get (0 ).data ()).isEqualTo ("Notice message" );
604
+
605
+ // Second notification should be ERROR level
606
+ assertThat (receivedNotifications .get (1 ).level ()).isEqualTo (McpSchema .LoggingLevel .ERROR );
607
+ assertThat (receivedNotifications .get (1 ).logger ()).isEqualTo ("test-logger" );
608
+ assertThat (receivedNotifications .get (1 ).data ()).isEqualTo ("Error message" );
609
+
610
+ // Third notification should be ERROR level
611
+ assertThat (receivedNotifications .get (2 ).level ()).isEqualTo (McpSchema .LoggingLevel .ERROR );
612
+ assertThat (receivedNotifications .get (2 ).logger ()).isEqualTo ("test-logger" );
613
+ assertThat (receivedNotifications .get (2 ).data ()).isEqualTo ("Another error message" );
614
+ });
615
+
616
+ mcpClient .close ();
617
+ mcpServer .close ();
618
+ }
619
+
514
620
}
0 commit comments