@@ -307,4 +307,143 @@ public java.lang.reflect.Type getType() {
307307 assertThat (names ).containsExactlyInAnyOrder ("subtract" , "add" );
308308 }
309309
310+ @ Test
311+ void testListToolsWithCursorAndMeta () {
312+ McpSchema .Tool addTool = McpSchema .Tool .builder ().name ("add" ).description ("calculate add" ).build ();
313+ McpSchema .ListToolsResult mockToolsResult = new McpSchema .ListToolsResult (List .of (addTool ), null );
314+
315+ // Use array to capture from anonymous class
316+ McpSchema .PaginatedRequest [] capturedRequest = new McpSchema .PaginatedRequest [1 ];
317+
318+ McpClientTransport transport = new McpClientTransport () {
319+ Function <Mono <McpSchema .JSONRPCMessage >, Mono <McpSchema .JSONRPCMessage >> handler ;
320+
321+ @ Override
322+ public Mono <Void > connect (
323+ Function <Mono <McpSchema .JSONRPCMessage >, Mono <McpSchema .JSONRPCMessage >> handler ) {
324+ return Mono .deferContextual (ctx -> {
325+ this .handler = handler ;
326+ return Mono .empty ();
327+ });
328+ }
329+
330+ @ Override
331+ public Mono <Void > closeGracefully () {
332+ return Mono .empty ();
333+ }
334+
335+ @ Override
336+ public Mono <Void > sendMessage (McpSchema .JSONRPCMessage message ) {
337+ if (!(message instanceof McpSchema .JSONRPCRequest request )) {
338+ return Mono .empty ();
339+ }
340+
341+ McpSchema .JSONRPCResponse response ;
342+ if (McpSchema .METHOD_INITIALIZE .equals (request .method ())) {
343+ response = new McpSchema .JSONRPCResponse (McpSchema .JSONRPC_VERSION , request .id (), MOCK_INIT_RESULT ,
344+ null );
345+ }
346+ else if (McpSchema .METHOD_TOOLS_LIST .equals (request .method ())) {
347+ capturedRequest [0 ] = JSON_MAPPER .convertValue (request .params (), McpSchema .PaginatedRequest .class );
348+ response = new McpSchema .JSONRPCResponse (McpSchema .JSONRPC_VERSION , request .id (), mockToolsResult ,
349+ null );
350+ }
351+ else {
352+ return Mono .empty ();
353+ }
354+
355+ return handler .apply (Mono .just (response )).then ();
356+ }
357+
358+ @ Override
359+ public <T > T unmarshalFrom (Object data , TypeRef <T > typeRef ) {
360+ return JSON_MAPPER .convertValue (data , new TypeRef <>() {
361+ @ Override
362+ public java .lang .reflect .Type getType () {
363+ return typeRef .getType ();
364+ }
365+ });
366+ }
367+ };
368+
369+ McpAsyncClient client = McpClient .async (transport ).build ();
370+
371+ Map <String , Object > meta = Map .of ("customKey" , "customValue" );
372+ McpSchema .ListToolsResult toolsResult = client .listTools ("cursor-1" , meta ).block ();
373+ assertThat (toolsResult ).isNotNull ();
374+ assertThat (toolsResult .tools ()).hasSize (1 );
375+ assertThat (capturedRequest [0 ]).isNotNull ();
376+ assertThat (capturedRequest [0 ].cursor ()).isEqualTo ("cursor-1" );
377+ assertThat (capturedRequest [0 ].meta ()).containsEntry ("customKey" , "customValue" );
378+ }
379+
380+ @ Test
381+ void testSyncListToolsWithCursorAndMeta () {
382+ McpSchema .Tool addTool = McpSchema .Tool .builder ().name ("add" ).description ("calculate add" ).build ();
383+ McpSchema .ListToolsResult mockToolsResult = new McpSchema .ListToolsResult (List .of (addTool ), null );
384+
385+ McpSchema .PaginatedRequest [] capturedRequest = new McpSchema .PaginatedRequest [1 ];
386+
387+ McpClientTransport transport = new McpClientTransport () {
388+ Function <Mono <McpSchema .JSONRPCMessage >, Mono <McpSchema .JSONRPCMessage >> handler ;
389+
390+ @ Override
391+ public Mono <Void > connect (
392+ Function <Mono <McpSchema .JSONRPCMessage >, Mono <McpSchema .JSONRPCMessage >> handler ) {
393+ return Mono .deferContextual (ctx -> {
394+ this .handler = handler ;
395+ return Mono .empty ();
396+ });
397+ }
398+
399+ @ Override
400+ public Mono <Void > closeGracefully () {
401+ return Mono .empty ();
402+ }
403+
404+ @ Override
405+ public Mono <Void > sendMessage (McpSchema .JSONRPCMessage message ) {
406+ if (!(message instanceof McpSchema .JSONRPCRequest request )) {
407+ return Mono .empty ();
408+ }
409+
410+ McpSchema .JSONRPCResponse response ;
411+ if (McpSchema .METHOD_INITIALIZE .equals (request .method ())) {
412+ response = new McpSchema .JSONRPCResponse (McpSchema .JSONRPC_VERSION , request .id (), MOCK_INIT_RESULT ,
413+ null );
414+ }
415+ else if (McpSchema .METHOD_TOOLS_LIST .equals (request .method ())) {
416+ capturedRequest [0 ] = JSON_MAPPER .convertValue (request .params (), McpSchema .PaginatedRequest .class );
417+ response = new McpSchema .JSONRPCResponse (McpSchema .JSONRPC_VERSION , request .id (), mockToolsResult ,
418+ null );
419+ }
420+ else {
421+ return Mono .empty ();
422+ }
423+
424+ return handler .apply (Mono .just (response )).then ();
425+ }
426+
427+ @ Override
428+ public <T > T unmarshalFrom (Object data , TypeRef <T > typeRef ) {
429+ return JSON_MAPPER .convertValue (data , new TypeRef <>() {
430+ @ Override
431+ public java .lang .reflect .Type getType () {
432+ return typeRef .getType ();
433+ }
434+ });
435+ }
436+ };
437+
438+ McpSyncClient client = McpClient .sync (transport ).build ();
439+
440+ Map <String , Object > meta = Map .of ("requestId" , "test-123" );
441+ McpSchema .ListToolsResult toolsResult = client .listTools ("cursor-1" , meta );
442+ assertThat (toolsResult ).isNotNull ();
443+ assertThat (toolsResult .tools ()).hasSize (1 );
444+ assertThat (capturedRequest [0 ]).isNotNull ();
445+ assertThat (capturedRequest [0 ].cursor ()).isEqualTo ("cursor-1" );
446+ assertThat (capturedRequest [0 ].meta ()).containsEntry ("requestId" , "test-123" );
447+ }
448+
310449}
0 commit comments