44
55package  io .modelcontextprotocol .client ;
66
7+ import  java .util .List ;
8+ import  java .util .Map ;
9+ import  java .util .Objects ;
10+ import  java .util .Set ;
11+ import  java .util .concurrent .atomic .AtomicReference ;
12+ import  java .util .function .Function ;
13+ import  java .util .stream .Collectors ;
14+ 
715import  io .modelcontextprotocol .json .TypeRef ;
816import  io .modelcontextprotocol .spec .McpClientTransport ;
917import  io .modelcontextprotocol .spec .McpSchema ;
1018import  io .modelcontextprotocol .spec .ProtocolVersions ;
11- 
1219import  org .junit .jupiter .api .Test ;
13- 
14- import  com .fasterxml .jackson .core .JsonProcessingException ;
15- 
1620import  reactor .core .publisher .Mono ;
1721import  reactor .test .StepVerifier ;
1822
19- import  java .util .List ;
20- import  java .util .Map ;
21- import  java .util .Objects ;
22- import  java .util .concurrent .atomic .AtomicReference ;
23- import  java .util .function .Function ;
24- 
2523import  static  io .modelcontextprotocol .util .McpJsonMapperUtils .JSON_MAPPER ;
2624import  static  org .assertj .core .api .Assertions .assertThat ;
2725import  static  org .assertj .core .api .Assertions .assertThatCode ;
@@ -40,8 +38,7 @@ class McpAsyncClientTests {
4038
4139	private  static  final  String  CONTEXT_KEY  = "context.key" ;
4240
43- 	private  McpClientTransport  createMockTransportForToolValidation (boolean  hasOutputSchema , boolean  invalidOutput )
44- 			throws  JsonProcessingException  {
41+ 	private  McpClientTransport  createMockTransportForToolValidation (boolean  hasOutputSchema , boolean  invalidOutput ) {
4542
4643		// Create tool with or without output schema 
4744		Map <String , Object > inputSchemaMap  = Map .of ("type" , "object" , "properties" ,
@@ -182,7 +179,7 @@ public java.lang.reflect.Type getType() {
182179	}
183180
184181	@ Test 
185- 	void  testCallToolWithOutputSchemaValidationSuccess () throws   JsonProcessingException   {
182+ 	void  testCallToolWithOutputSchemaValidationSuccess () {
186183		McpClientTransport  transport  = createMockTransportForToolValidation (true , false );
187184
188185		McpAsyncClient  client  = McpClient .async (transport ).enableCallToolSchemaCaching (true ).build ();
@@ -204,7 +201,7 @@ void testCallToolWithOutputSchemaValidationSuccess() throws JsonProcessingExcept
204201	}
205202
206203	@ Test 
207- 	void  testCallToolWithNoOutputSchemaSuccess () throws   JsonProcessingException   {
204+ 	void  testCallToolWithNoOutputSchemaSuccess () {
208205		McpClientTransport  transport  = createMockTransportForToolValidation (false , false );
209206
210207		McpAsyncClient  client  = McpClient .async (transport ).enableCallToolSchemaCaching (true ).build ();
@@ -226,7 +223,7 @@ void testCallToolWithNoOutputSchemaSuccess() throws JsonProcessingException {
226223	}
227224
228225	@ Test 
229- 	void  testCallToolWithOutputSchemaValidationFailure () throws   JsonProcessingException   {
226+ 	void  testCallToolWithOutputSchemaValidationFailure () {
230227		McpClientTransport  transport  = createMockTransportForToolValidation (true , true );
231228
232229		McpAsyncClient  client  = McpClient .async (transport ).enableCallToolSchemaCaching (true ).build ();
@@ -241,4 +238,73 @@ void testCallToolWithOutputSchemaValidationFailure() throws JsonProcessingExcept
241238		StepVerifier .create (client .closeGracefully ()).verifyComplete ();
242239	}
243240
241+ 	@ Test 
242+ 	void  testListToolsWithEmptyCursor () {
243+ 		McpSchema .Tool  addTool  = McpSchema .Tool .builder ().name ("add" ).description ("calculate add" ).build ();
244+ 		McpSchema .Tool  subtractTool  = McpSchema .Tool .builder ()
245+ 			.name ("subtract" )
246+ 			.description ("calculate subtract" )
247+ 			.build ();
248+ 		McpSchema .ListToolsResult  mockToolsResult  = new  McpSchema .ListToolsResult (List .of (addTool , subtractTool ), "" );
249+ 
250+ 		McpClientTransport  transport  = new  McpClientTransport () {
251+ 			Function <Mono <McpSchema .JSONRPCMessage >, Mono <McpSchema .JSONRPCMessage >> handler ;
252+ 
253+ 			@ Override 
254+ 			public  Mono <Void > connect (
255+ 					Function <Mono <McpSchema .JSONRPCMessage >, Mono <McpSchema .JSONRPCMessage >> handler ) {
256+ 				return  Mono .deferContextual (ctx  -> {
257+ 					this .handler  = handler ;
258+ 					return  Mono .empty ();
259+ 				});
260+ 			}
261+ 
262+ 			@ Override 
263+ 			public  Mono <Void > closeGracefully () {
264+ 				return  Mono .empty ();
265+ 			}
266+ 
267+ 			@ Override 
268+ 			public  Mono <Void > sendMessage (McpSchema .JSONRPCMessage  message ) {
269+ 				if  (!(message  instanceof  McpSchema .JSONRPCRequest  request )) {
270+ 					return  Mono .empty ();
271+ 				}
272+ 
273+ 				McpSchema .JSONRPCResponse  response ;
274+ 				if  (McpSchema .METHOD_INITIALIZE .equals (request .method ())) {
275+ 					response  = new  McpSchema .JSONRPCResponse (McpSchema .JSONRPC_VERSION , request .id (), MOCK_INIT_RESULT ,
276+ 							null );
277+ 				}
278+ 				else  if  (McpSchema .METHOD_TOOLS_LIST .equals (request .method ())) {
279+ 					response  = new  McpSchema .JSONRPCResponse (McpSchema .JSONRPC_VERSION , request .id (), mockToolsResult ,
280+ 							null );
281+ 				}
282+ 				else  {
283+ 					return  Mono .empty ();
284+ 				}
285+ 
286+ 				return  handler .apply (Mono .just (response )).then ();
287+ 			}
288+ 
289+ 			@ Override 
290+ 			public  <T > T  unmarshalFrom (Object  data , TypeRef <T > typeRef ) {
291+ 				return  JSON_MAPPER .convertValue (data , new  TypeRef <>() {
292+ 					@ Override 
293+ 					public  java .lang .reflect .Type  getType () {
294+ 						return  typeRef .getType ();
295+ 					}
296+ 				});
297+ 			}
298+ 		};
299+ 
300+ 		McpAsyncClient  client  = McpClient .async (transport ).enableCallToolSchemaCaching (true ).build ();
301+ 
302+ 		Mono <McpSchema .ListToolsResult > mono  = client .listTools ();
303+ 		McpSchema .ListToolsResult  toolsResult  = mono .block ();
304+ 		assertThat (toolsResult ).isNotNull ();
305+ 
306+ 		Set <String > names  = toolsResult .tools ().stream ().map (McpSchema .Tool ::name ).collect (Collectors .toSet ());
307+ 		assertThat (names ).containsExactlyInAnyOrder ("subtract" , "add" );
308+ 	}
309+ 
244310}
0 commit comments