|
21 | 21 | import java.util.List;
|
22 | 22 | import java.util.Map;
|
23 | 23 |
|
| 24 | +import io.modelcontextprotocol.client.McpSyncClient; |
24 | 25 | import io.modelcontextprotocol.server.McpAsyncServerExchange;
|
25 | 26 | import io.modelcontextprotocol.server.McpServerFeatures.AsyncToolSpecification;
|
26 | 27 | import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification;
|
27 | 28 | import io.modelcontextprotocol.server.McpSyncServerExchange;
|
28 | 29 | import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
|
| 30 | +import io.modelcontextprotocol.spec.McpSchema.Implementation; |
| 31 | +import io.modelcontextprotocol.spec.McpSchema.ListToolsResult; |
29 | 32 | import io.modelcontextprotocol.spec.McpSchema.TextContent;
|
| 33 | +import io.modelcontextprotocol.spec.McpSchema.Tool; |
30 | 34 | import org.junit.jupiter.api.Test;
|
31 | 35 | import reactor.test.StepVerifier;
|
32 | 36 |
|
@@ -211,4 +215,118 @@ private ToolCallback createMockToolCallback(String name, RuntimeException error)
|
211 | 215 | return callback;
|
212 | 216 | }
|
213 | 217 |
|
| 218 | + @Test |
| 219 | + void getToolCallbacksFromSyncClientsWithEmptyListShouldReturnEmptyList() { |
| 220 | + List<ToolCallback> result = McpToolUtils.getToolCallbacksFromSyncClients(List.of()); |
| 221 | + assertThat(result).isEmpty(); |
| 222 | + } |
| 223 | + |
| 224 | + @Test |
| 225 | + void getToolCallbacksFromSyncClientsWithSingleClientShouldReturnToolCallbacks() { |
| 226 | + McpSyncClient mockClient = mock(McpSyncClient.class); |
| 227 | + Implementation clientInfo = new Implementation("test-client", "1.0.0"); |
| 228 | + |
| 229 | + Tool tool1 = mock(Tool.class); |
| 230 | + when(tool1.name()).thenReturn("tool1"); |
| 231 | + when(tool1.description()).thenReturn("Test Tool 1"); |
| 232 | + |
| 233 | + Tool tool2 = mock(Tool.class); |
| 234 | + when(tool2.name()).thenReturn("tool2"); |
| 235 | + when(tool2.description()).thenReturn("Test Tool 2"); |
| 236 | + |
| 237 | + when(mockClient.getClientInfo()).thenReturn(clientInfo); |
| 238 | + |
| 239 | + ListToolsResult listToolsResult = mock(ListToolsResult.class); |
| 240 | + when(listToolsResult.tools()).thenReturn(List.of(tool1, tool2)); |
| 241 | + when(mockClient.listTools()).thenReturn(listToolsResult); |
| 242 | + |
| 243 | + List<ToolCallback> result = McpToolUtils.getToolCallbacksFromSyncClients(mockClient); |
| 244 | + |
| 245 | + assertThat(result).hasSize(2); |
| 246 | + assertThat(result.get(0).getToolDefinition().name()).isEqualTo("test_client_tool1"); |
| 247 | + assertThat(result.get(1).getToolDefinition().name()).isEqualTo("test_client_tool2"); |
| 248 | + |
| 249 | + List<ToolCallback> result2 = McpToolUtils.getToolCallbacksFromSyncClients(List.of(mockClient)); |
| 250 | + |
| 251 | + assertThat(result2).hasSize(2); |
| 252 | + assertThat(result2.get(0).getToolDefinition().name()).isEqualTo("test_client_tool1"); |
| 253 | + assertThat(result2.get(1).getToolDefinition().name()).isEqualTo("test_client_tool2"); |
| 254 | + } |
| 255 | + |
| 256 | + @Test |
| 257 | + void getToolCallbacksFromSyncClientsWithMultipleClientsShouldReturnCombinedToolCallbacks() { |
| 258 | + |
| 259 | + McpSyncClient mockClient1 = mock(McpSyncClient.class); |
| 260 | + Implementation clientInfo1 = new Implementation("client1", "1.0.0"); |
| 261 | + |
| 262 | + Tool tool1 = mock(Tool.class); |
| 263 | + when(tool1.name()).thenReturn("tool1"); |
| 264 | + when(tool1.description()).thenReturn("Test Tool 1"); |
| 265 | + |
| 266 | + McpSyncClient mockClient2 = mock(McpSyncClient.class); |
| 267 | + Implementation clientInfo2 = new Implementation("client2", "1.0.0"); |
| 268 | + |
| 269 | + Tool tool2 = mock(Tool.class); |
| 270 | + when(tool2.name()).thenReturn("tool2"); |
| 271 | + when(tool2.description()).thenReturn("Test Tool 2"); |
| 272 | + |
| 273 | + when(mockClient1.getClientInfo()).thenReturn(clientInfo1); |
| 274 | + |
| 275 | + ListToolsResult listToolsResult1 = mock(ListToolsResult.class); |
| 276 | + when(listToolsResult1.tools()).thenReturn(List.of(tool1)); |
| 277 | + when(mockClient1.listTools()).thenReturn(listToolsResult1); |
| 278 | + |
| 279 | + when(mockClient2.getClientInfo()).thenReturn(clientInfo2); |
| 280 | + |
| 281 | + ListToolsResult listToolsResult2 = mock(ListToolsResult.class); |
| 282 | + when(listToolsResult2.tools()).thenReturn(List.of(tool2)); |
| 283 | + when(mockClient2.listTools()).thenReturn(listToolsResult2); |
| 284 | + |
| 285 | + List<ToolCallback> result = McpToolUtils.getToolCallbacksFromSyncClients(mockClient1, mockClient2); |
| 286 | + |
| 287 | + assertThat(result).hasSize(2); |
| 288 | + assertThat(result.get(0).getToolDefinition().name()).isEqualTo("client1_tool1"); |
| 289 | + assertThat(result.get(1).getToolDefinition().name()).isEqualTo("client2_tool2"); |
| 290 | + |
| 291 | + List<ToolCallback> result2 = McpToolUtils.getToolCallbacksFromSyncClients(List.of(mockClient1, mockClient2)); |
| 292 | + |
| 293 | + assertThat(result2).hasSize(2); |
| 294 | + assertThat(result2.get(0).getToolDefinition().name()).isEqualTo("client1_tool1"); |
| 295 | + assertThat(result2.get(1).getToolDefinition().name()).isEqualTo("client2_tool2"); |
| 296 | + } |
| 297 | + |
| 298 | + @Test |
| 299 | + void getToolCallbacksFromSyncClientsShouldHandleDuplicateToolNames() { |
| 300 | + |
| 301 | + McpSyncClient mockClient1 = mock(McpSyncClient.class); |
| 302 | + Implementation clientInfo1 = new Implementation("client", "1.0.0"); |
| 303 | + |
| 304 | + Tool tool1 = mock(Tool.class); |
| 305 | + when(tool1.name()).thenReturn("tool"); |
| 306 | + when(tool1.description()).thenReturn("Test Tool 1"); |
| 307 | + |
| 308 | + McpSyncClient mockClient2 = mock(McpSyncClient.class); |
| 309 | + Implementation clientInfo2 = new Implementation("client", "1.0.0"); |
| 310 | + |
| 311 | + Tool tool2 = mock(Tool.class); |
| 312 | + when(tool2.name()).thenReturn("tool"); |
| 313 | + when(tool2.description()).thenReturn("Test Tool 2"); |
| 314 | + |
| 315 | + when(mockClient1.getClientInfo()).thenReturn(clientInfo1); |
| 316 | + |
| 317 | + ListToolsResult listToolsResult1 = mock(ListToolsResult.class); |
| 318 | + when(listToolsResult1.tools()).thenReturn(List.of(tool1)); |
| 319 | + when(mockClient1.listTools()).thenReturn(listToolsResult1); |
| 320 | + |
| 321 | + when(mockClient2.getClientInfo()).thenReturn(clientInfo2); |
| 322 | + |
| 323 | + ListToolsResult listToolsResult2 = mock(ListToolsResult.class); |
| 324 | + when(listToolsResult2.tools()).thenReturn(List.of(tool2)); |
| 325 | + when(mockClient2.listTools()).thenReturn(listToolsResult2); |
| 326 | + |
| 327 | + assertThatThrownBy(() -> McpToolUtils.getToolCallbacksFromSyncClients(mockClient1, mockClient2)) |
| 328 | + .isInstanceOf(IllegalStateException.class) |
| 329 | + .hasMessageContaining("Multiple tools with the same name"); |
| 330 | + } |
| 331 | + |
214 | 332 | }
|
0 commit comments