From 141063c99cfb081605cfae8e29060b32bf14ff68 Mon Sep 17 00:00:00 2001 From: Daniel Garnier-Moiroux Date: Thu, 9 Apr 2026 11:11:07 +0200 Subject: [PATCH] Polish gh-906 --- .../client/McpAsyncClient.java | 18 +- .../client/McpSyncClient.java | 10 +- .../client/AbstractMcpSyncClientTests.java | 8 +- .../client/McpAsyncClientTests.java | 678 +++--------------- 4 files changed, 121 insertions(+), 593 deletions(-) diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java index 8aac5ed..434c07a 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java @@ -654,12 +654,12 @@ public class McpAsyncClient { * @param meta Optional metadata to include in the request (_meta field) * @return A Mono that emits the list of tools result */ - public Mono listTools(String cursor, java.util.Map meta) { + public Mono listTools(String cursor, Map meta) { return this.initializer.withInitialization("listing tools", init -> this.listToolsInternal(init, cursor, meta)); } private Mono listToolsInternal(Initialization init, String cursor, - java.util.Map meta) { + Map meta) { if (init.initializeResult().capabilities().tools() == null) { return Mono.error(new IllegalStateException("Server does not provide tools capability")); @@ -749,12 +749,11 @@ public class McpAsyncClient { * @see McpSchema.ListResourcesResult * @see #readResource(McpSchema.Resource) */ - public Mono listResources(String cursor, java.util.Map meta) { + public Mono listResources(String cursor, Map meta) { return this.listResourcesInternal(cursor, meta); } - private Mono listResourcesInternal(String cursor, - java.util.Map meta) { + private Mono listResourcesInternal(String cursor, Map meta) { return this.initializer.withInitialization("listing resources", init -> { if (init.initializeResult().capabilities().resources() == null) { return Mono.error(new IllegalStateException("Server does not provide the resources capability")); @@ -837,13 +836,12 @@ public class McpAsyncClient { * @return A Mono that completes with the list of resource templates result. * @see McpSchema.ListResourceTemplatesResult */ - public Mono listResourceTemplates(String cursor, - java.util.Map meta) { + public Mono listResourceTemplates(String cursor, Map meta) { return this.listResourceTemplatesInternal(cursor, meta); } private Mono listResourceTemplatesInternal(String cursor, - java.util.Map meta) { + Map meta) { return this.initializer.withInitialization("listing resource templates", init -> { if (init.initializeResult().capabilities().resources() == null) { return Mono.error(new IllegalStateException("Server does not provide the resources capability")); @@ -955,11 +953,11 @@ public class McpAsyncClient { * @see McpSchema.ListPromptsResult * @see #getPrompt(GetPromptRequest) */ - public Mono listPrompts(String cursor, java.util.Map meta) { + public Mono listPrompts(String cursor, Map meta) { return this.listPromptsInternal(cursor, meta); } - private Mono listPromptsInternal(String cursor, java.util.Map meta) { + private Mono listPromptsInternal(String cursor, Map meta) { return this.initializer.withInitialization("listing prompts", init -> init.mcpSession() .sendRequest(McpSchema.METHOD_PROMPT_LIST, new PaginatedRequest(cursor, meta), diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpSyncClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpSyncClient.java index cd67b74..7e08f83 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpSyncClient.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpSyncClient.java @@ -5,6 +5,7 @@ package io.modelcontextprotocol.client; import java.time.Duration; +import java.util.Map; import java.util.function.Supplier; import org.slf4j.Logger; @@ -267,7 +268,7 @@ public class McpSyncClient implements AutoCloseable { * with a name, description, and input schema - nextCursor: Optional cursor for * pagination if more tools are available */ - public McpSchema.ListToolsResult listTools(String cursor, java.util.Map meta) { + public McpSchema.ListToolsResult listTools(String cursor, Map meta) { return withProvidedContext(this.delegate.listTools(cursor, meta)).block(); } @@ -300,7 +301,7 @@ public class McpSyncClient implements AutoCloseable { * @param meta Optional metadata to include in the request (_meta field) * @return The list of resources result */ - public McpSchema.ListResourcesResult listResources(String cursor, java.util.Map meta) { + public McpSchema.ListResourcesResult listResources(String cursor, Map meta) { return withProvidedContext(this.delegate.listResources(cursor, meta)).block(); } @@ -356,8 +357,7 @@ public class McpSyncClient implements AutoCloseable { * @param meta Optional metadata to include in the request (_meta field) * @return The list of resource templates result. */ - public McpSchema.ListResourceTemplatesResult listResourceTemplates(String cursor, - java.util.Map meta) { + public McpSchema.ListResourceTemplatesResult listResourceTemplates(String cursor, Map meta) { return withProvidedContext(this.delegate.listResourceTemplates(cursor, meta)).block(); } @@ -414,7 +414,7 @@ public class McpSyncClient implements AutoCloseable { * @param meta Optional metadata to include in the request (_meta field) * @return The list of prompts result. */ - public ListPromptsResult listPrompts(String cursor, java.util.Map meta) { + public ListPromptsResult listPrompts(String cursor, Map meta) { return withProvidedContext(this.delegate.listPrompts(cursor, meta)).block(); } diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java index 0c38dda..7fe7bd6 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java @@ -158,7 +158,7 @@ public abstract class AbstractMcpSyncClientTests { void testListToolsWithMeta() { withClient(createMcpTransport(), mcpSyncClient -> { mcpSyncClient.initialize(); - java.util.Map meta = java.util.Map.of("requestId", "test-123"); + Map meta = java.util.Map.of("requestId", "test-123"); ListToolsResult tools = mcpSyncClient.listTools(McpSchema.FIRST_PAGE, meta); assertThat(tools).isNotNull().satisfies(result -> { @@ -695,7 +695,7 @@ public abstract class AbstractMcpSyncClientTests { void testListResourcesWithMeta() { withClient(createMcpTransport(), mcpSyncClient -> { mcpSyncClient.initialize(); - java.util.Map meta = java.util.Map.of("requestId", "test-123"); + Map meta = java.util.Map.of("requestId", "test-123"); ListResourcesResult resources = mcpSyncClient.listResources(McpSchema.FIRST_PAGE, meta); assertThat(resources).isNotNull().satisfies(result -> { @@ -708,7 +708,7 @@ public abstract class AbstractMcpSyncClientTests { void testListResourceTemplatesWithMeta() { withClient(createMcpTransport(), mcpSyncClient -> { mcpSyncClient.initialize(); - java.util.Map meta = java.util.Map.of("requestId", "test-123"); + Map meta = java.util.Map.of("requestId", "test-123"); ListResourceTemplatesResult result = mcpSyncClient.listResourceTemplates(McpSchema.FIRST_PAGE, meta); assertThat(result).isNotNull().satisfies(r -> { @@ -721,7 +721,7 @@ public abstract class AbstractMcpSyncClientTests { void testListPromptsWithMeta() { withClient(createMcpTransport(), mcpSyncClient -> { mcpSyncClient.initialize(); - java.util.Map meta = java.util.Map.of("requestId", "test-123"); + Map meta = java.util.Map.of("requestId", "test-123"); McpSchema.ListPromptsResult result = mcpSyncClient.listPrompts(McpSchema.FIRST_PAGE, meta); assertThat(result).isNotNull().satisfies(r -> { diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java index bfe9d5d..a349fca 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java @@ -238,630 +238,160 @@ class McpAsyncClientTests { StepVerifier.create(client.closeGracefully()).verifyComplete(); } - @Test - void testListToolsWithEmptyCursor() { - McpSchema.Tool addTool = McpSchema.Tool.builder().name("add").description("calculate add").build(); - McpSchema.Tool subtractTool = McpSchema.Tool.builder() - .name("subtract") - .description("calculate subtract") - .build(); - McpSchema.ListToolsResult mockToolsResult = new McpSchema.ListToolsResult(List.of(addTool, subtractTool), ""); - - McpClientTransport transport = new McpClientTransport() { - Function, Mono> handler; - - @Override - public Mono connect( - Function, Mono> handler) { - return Mono.deferContextual(ctx -> { - this.handler = handler; - return Mono.empty(); - }); - } - - @Override - public Mono closeGracefully() { - return Mono.empty(); - } - - @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { - if (!(message instanceof McpSchema.JSONRPCRequest request)) { - return Mono.empty(); - } - - McpSchema.JSONRPCResponse response; - if (McpSchema.METHOD_INITIALIZE.equals(request.method())) { - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), MOCK_INIT_RESULT, - null); - } - else if (McpSchema.METHOD_TOOLS_LIST.equals(request.method())) { - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockToolsResult, - null); - } - else { - return Mono.empty(); - } - - return handler.apply(Mono.just(response)).then(); - } - - @Override - public T unmarshalFrom(Object data, TypeRef typeRef) { - return JSON_MAPPER.convertValue(data, new TypeRef<>() { - @Override - public java.lang.reflect.Type getType() { - return typeRef.getType(); - } - }); - } - }; - - McpAsyncClient client = McpClient.async(transport).enableCallToolSchemaCaching(true).build(); - - Mono mono = client.listTools(); - McpSchema.ListToolsResult toolsResult = mono.block(); - assertThat(toolsResult).isNotNull(); - - Set names = toolsResult.tools().stream().map(McpSchema.Tool::name).collect(Collectors.toSet()); - assertThat(names).containsExactlyInAnyOrder("subtract", "add"); - } - @Test void testListToolsWithCursorAndMeta() { - McpSchema.Tool addTool = McpSchema.Tool.builder().name("add").description("calculate add").build(); - McpSchema.ListToolsResult mockToolsResult = new McpSchema.ListToolsResult(List.of(addTool), null); - - // Use array to capture from anonymous class - McpSchema.PaginatedRequest[] capturedRequest = new McpSchema.PaginatedRequest[1]; - - McpClientTransport transport = new McpClientTransport() { - Function, Mono> handler; - - @Override - public Mono connect( - Function, Mono> handler) { - return Mono.deferContextual(ctx -> { - this.handler = handler; - return Mono.empty(); - }); - } - - @Override - public Mono closeGracefully() { - return Mono.empty(); - } - - @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { - if (!(message instanceof McpSchema.JSONRPCRequest request)) { - return Mono.empty(); - } - - McpSchema.JSONRPCResponse response; - if (McpSchema.METHOD_INITIALIZE.equals(request.method())) { - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), MOCK_INIT_RESULT, - null); - } - else if (McpSchema.METHOD_TOOLS_LIST.equals(request.method())) { - capturedRequest[0] = JSON_MAPPER.convertValue(request.params(), McpSchema.PaginatedRequest.class); - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockToolsResult, - null); - } - else { - return Mono.empty(); - } - - return handler.apply(Mono.just(response)).then(); - } - - @Override - public T unmarshalFrom(Object data, TypeRef typeRef) { - return JSON_MAPPER.convertValue(data, new TypeRef<>() { - @Override - public java.lang.reflect.Type getType() { - return typeRef.getType(); - } - }); - } - }; - + var transport = new TestMcpClientTransport(); McpAsyncClient client = McpClient.async(transport).build(); Map meta = Map.of("customKey", "customValue"); - McpSchema.ListToolsResult toolsResult = client.listTools("cursor-1", meta).block(); - assertThat(toolsResult).isNotNull(); - assertThat(toolsResult.tools()).hasSize(1); - assertThat(capturedRequest[0]).isNotNull(); - assertThat(capturedRequest[0].cursor()).isEqualTo("cursor-1"); - assertThat(capturedRequest[0].meta()).containsEntry("customKey", "customValue"); - } - - @Test - void testSyncListToolsWithCursorAndMeta() { - McpSchema.Tool addTool = McpSchema.Tool.builder().name("add").description("calculate add").build(); - McpSchema.ListToolsResult mockToolsResult = new McpSchema.ListToolsResult(List.of(addTool), null); - - McpSchema.PaginatedRequest[] capturedRequest = new McpSchema.PaginatedRequest[1]; - - McpClientTransport transport = new McpClientTransport() { - Function, Mono> handler; - - @Override - public Mono connect( - Function, Mono> handler) { - return Mono.deferContextual(ctx -> { - this.handler = handler; - return Mono.empty(); - }); - } - - @Override - public Mono closeGracefully() { - return Mono.empty(); - } - - @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { - if (!(message instanceof McpSchema.JSONRPCRequest request)) { - return Mono.empty(); - } - - McpSchema.JSONRPCResponse response; - if (McpSchema.METHOD_INITIALIZE.equals(request.method())) { - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), MOCK_INIT_RESULT, - null); - } - else if (McpSchema.METHOD_TOOLS_LIST.equals(request.method())) { - capturedRequest[0] = JSON_MAPPER.convertValue(request.params(), McpSchema.PaginatedRequest.class); - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockToolsResult, - null); - } - else { - return Mono.empty(); - } - - return handler.apply(Mono.just(response)).then(); - } - - @Override - public T unmarshalFrom(Object data, TypeRef typeRef) { - return JSON_MAPPER.convertValue(data, new TypeRef<>() { - @Override - public java.lang.reflect.Type getType() { - return typeRef.getType(); - } - }); - } - }; - - McpSyncClient client = McpClient.sync(transport).build(); - - Map meta = Map.of("requestId", "test-123"); - McpSchema.ListToolsResult toolsResult = client.listTools("cursor-1", meta); - assertThat(toolsResult).isNotNull(); - assertThat(toolsResult.tools()).hasSize(1); - assertThat(capturedRequest[0]).isNotNull(); - assertThat(capturedRequest[0].cursor()).isEqualTo("cursor-1"); - assertThat(capturedRequest[0].meta()).containsEntry("requestId", "test-123"); + McpSchema.ListToolsResult result = client.listTools("cursor-1", meta).block(); + assertThat(result).isNotNull(); + assertThat(result.tools()).hasSize(1); + assertThat(transport.getCapturedRequest()).isNotNull(); + assertThat(transport.getCapturedRequest().cursor()).isEqualTo("cursor-1"); + assertThat(transport.getCapturedRequest().meta()).containsEntry("customKey", "customValue"); } @Test void testListResourcesWithCursorAndMeta() { - McpSchema.Resource mockResource = McpSchema.Resource.builder().uri("file:///test.txt").name("test.txt").build(); - McpSchema.ListResourcesResult mockResult = new McpSchema.ListResourcesResult(List.of(mockResource), null); - - McpSchema.ServerCapabilities caps = McpSchema.ServerCapabilities.builder().resources(false, false).build(); - McpSchema.InitializeResult initResult = new McpSchema.InitializeResult(ProtocolVersions.MCP_2024_11_05, caps, - MOCK_SERVER_INFO, null); - - McpSchema.PaginatedRequest[] capturedRequest = new McpSchema.PaginatedRequest[1]; - - McpClientTransport transport = new McpClientTransport() { - Function, Mono> handler; - - @Override - public Mono connect( - Function, Mono> handler) { - return Mono.deferContextual(ctx -> { - this.handler = handler; - return Mono.empty(); - }); - } - - @Override - public Mono closeGracefully() { - return Mono.empty(); - } - - @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { - if (!(message instanceof McpSchema.JSONRPCRequest request)) { - return Mono.empty(); - } - McpSchema.JSONRPCResponse response; - if (McpSchema.METHOD_INITIALIZE.equals(request.method())) { - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), initResult, null); - } - else if (McpSchema.METHOD_RESOURCES_LIST.equals(request.method())) { - capturedRequest[0] = JSON_MAPPER.convertValue(request.params(), McpSchema.PaginatedRequest.class); - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockResult, null); - } - else { - return Mono.empty(); - } - return handler.apply(Mono.just(response)).then(); - } - - @Override - public T unmarshalFrom(Object data, TypeRef typeRef) { - return JSON_MAPPER.convertValue(data, new TypeRef<>() { - @Override - public java.lang.reflect.Type getType() { - return typeRef.getType(); - } - }); - } - }; - + var transport = new TestMcpClientTransport(); McpAsyncClient client = McpClient.async(transport).build(); Map meta = Map.of("customKey", "customValue"); McpSchema.ListResourcesResult result = client.listResources("cursor-1", meta).block(); assertThat(result).isNotNull(); assertThat(result.resources()).hasSize(1); - assertThat(capturedRequest[0]).isNotNull(); - assertThat(capturedRequest[0].cursor()).isEqualTo("cursor-1"); - assertThat(capturedRequest[0].meta()).containsEntry("customKey", "customValue"); - } - - @Test - void testSyncListResourcesWithCursorAndMeta() { - McpSchema.Resource mockResource = McpSchema.Resource.builder().uri("file:///test.txt").name("test.txt").build(); - McpSchema.ListResourcesResult mockResult = new McpSchema.ListResourcesResult(List.of(mockResource), null); - - McpSchema.ServerCapabilities caps = McpSchema.ServerCapabilities.builder().resources(false, false).build(); - McpSchema.InitializeResult initResult = new McpSchema.InitializeResult(ProtocolVersions.MCP_2024_11_05, caps, - MOCK_SERVER_INFO, null); - - McpSchema.PaginatedRequest[] capturedRequest = new McpSchema.PaginatedRequest[1]; - - McpClientTransport transport = new McpClientTransport() { - Function, Mono> handler; - - @Override - public Mono connect( - Function, Mono> handler) { - return Mono.deferContextual(ctx -> { - this.handler = handler; - return Mono.empty(); - }); - } - - @Override - public Mono closeGracefully() { - return Mono.empty(); - } - - @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { - if (!(message instanceof McpSchema.JSONRPCRequest request)) { - return Mono.empty(); - } - McpSchema.JSONRPCResponse response; - if (McpSchema.METHOD_INITIALIZE.equals(request.method())) { - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), initResult, null); - } - else if (McpSchema.METHOD_RESOURCES_LIST.equals(request.method())) { - capturedRequest[0] = JSON_MAPPER.convertValue(request.params(), McpSchema.PaginatedRequest.class); - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockResult, null); - } - else { - return Mono.empty(); - } - return handler.apply(Mono.just(response)).then(); - } - - @Override - public T unmarshalFrom(Object data, TypeRef typeRef) { - return JSON_MAPPER.convertValue(data, new TypeRef<>() { - @Override - public java.lang.reflect.Type getType() { - return typeRef.getType(); - } - }); - } - }; - - McpSyncClient client = McpClient.sync(transport).build(); - - Map meta = Map.of("customKey", "customValue"); - McpSchema.ListResourcesResult result = client.listResources("cursor-1", meta); - assertThat(result).isNotNull(); - assertThat(result.resources()).hasSize(1); - assertThat(capturedRequest[0]).isNotNull(); - assertThat(capturedRequest[0].cursor()).isEqualTo("cursor-1"); - assertThat(capturedRequest[0].meta()).containsEntry("customKey", "customValue"); + assertThat(transport.getCapturedRequest()).isNotNull(); + assertThat(transport.getCapturedRequest().cursor()).isEqualTo("cursor-1"); + assertThat(transport.getCapturedRequest().meta()).containsEntry("customKey", "customValue"); } @Test void testListResourceTemplatesWithCursorAndMeta() { - McpSchema.ResourceTemplate mockTemplate = new McpSchema.ResourceTemplate("file:///{name}", "template", null, - null, null); - McpSchema.ListResourceTemplatesResult mockResult = new McpSchema.ListResourceTemplatesResult( - List.of(mockTemplate), null); - - McpSchema.ServerCapabilities caps = McpSchema.ServerCapabilities.builder().resources(false, false).build(); - McpSchema.InitializeResult initResult = new McpSchema.InitializeResult(ProtocolVersions.MCP_2024_11_05, caps, - MOCK_SERVER_INFO, null); - - McpSchema.PaginatedRequest[] capturedRequest = new McpSchema.PaginatedRequest[1]; - - McpClientTransport transport = new McpClientTransport() { - Function, Mono> handler; - - @Override - public Mono connect( - Function, Mono> handler) { - return Mono.deferContextual(ctx -> { - this.handler = handler; - return Mono.empty(); - }); - } - - @Override - public Mono closeGracefully() { - return Mono.empty(); - } - - @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { - if (!(message instanceof McpSchema.JSONRPCRequest request)) { - return Mono.empty(); - } - McpSchema.JSONRPCResponse response; - if (McpSchema.METHOD_INITIALIZE.equals(request.method())) { - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), initResult, null); - } - else if (McpSchema.METHOD_RESOURCES_TEMPLATES_LIST.equals(request.method())) { - capturedRequest[0] = JSON_MAPPER.convertValue(request.params(), McpSchema.PaginatedRequest.class); - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockResult, null); - } - else { - return Mono.empty(); - } - return handler.apply(Mono.just(response)).then(); - } - - @Override - public T unmarshalFrom(Object data, TypeRef typeRef) { - return JSON_MAPPER.convertValue(data, new TypeRef<>() { - @Override - public java.lang.reflect.Type getType() { - return typeRef.getType(); - } - }); - } - }; - + var transport = new TestMcpClientTransport(); McpAsyncClient client = McpClient.async(transport).build(); Map meta = Map.of("customKey", "customValue"); McpSchema.ListResourceTemplatesResult result = client.listResourceTemplates("cursor-1", meta).block(); assertThat(result).isNotNull(); assertThat(result.resourceTemplates()).hasSize(1); - assertThat(capturedRequest[0]).isNotNull(); - assertThat(capturedRequest[0].cursor()).isEqualTo("cursor-1"); - assertThat(capturedRequest[0].meta()).containsEntry("customKey", "customValue"); - } - - @Test - void testSyncListResourceTemplatesWithCursorAndMeta() { - McpSchema.ResourceTemplate mockTemplate = new McpSchema.ResourceTemplate("file:///{name}", "template", null, - null, null); - McpSchema.ListResourceTemplatesResult mockResult = new McpSchema.ListResourceTemplatesResult( - List.of(mockTemplate), null); - - McpSchema.ServerCapabilities caps = McpSchema.ServerCapabilities.builder().resources(false, false).build(); - McpSchema.InitializeResult initResult = new McpSchema.InitializeResult(ProtocolVersions.MCP_2024_11_05, caps, - MOCK_SERVER_INFO, null); - - McpSchema.PaginatedRequest[] capturedRequest = new McpSchema.PaginatedRequest[1]; - - McpClientTransport transport = new McpClientTransport() { - Function, Mono> handler; - - @Override - public Mono connect( - Function, Mono> handler) { - return Mono.deferContextual(ctx -> { - this.handler = handler; - return Mono.empty(); - }); - } - - @Override - public Mono closeGracefully() { - return Mono.empty(); - } - - @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { - if (!(message instanceof McpSchema.JSONRPCRequest request)) { - return Mono.empty(); - } - McpSchema.JSONRPCResponse response; - if (McpSchema.METHOD_INITIALIZE.equals(request.method())) { - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), initResult, null); - } - else if (McpSchema.METHOD_RESOURCES_TEMPLATES_LIST.equals(request.method())) { - capturedRequest[0] = JSON_MAPPER.convertValue(request.params(), McpSchema.PaginatedRequest.class); - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockResult, null); - } - else { - return Mono.empty(); - } - return handler.apply(Mono.just(response)).then(); - } - - @Override - public T unmarshalFrom(Object data, TypeRef typeRef) { - return JSON_MAPPER.convertValue(data, new TypeRef<>() { - @Override - public java.lang.reflect.Type getType() { - return typeRef.getType(); - } - }); - } - }; - - McpSyncClient client = McpClient.sync(transport).build(); - - Map meta = Map.of("customKey", "customValue"); - McpSchema.ListResourceTemplatesResult result = client.listResourceTemplates("cursor-1", meta); - assertThat(result).isNotNull(); - assertThat(result.resourceTemplates()).hasSize(1); - assertThat(capturedRequest[0]).isNotNull(); - assertThat(capturedRequest[0].cursor()).isEqualTo("cursor-1"); - assertThat(capturedRequest[0].meta()).containsEntry("customKey", "customValue"); + assertThat(transport.getCapturedRequest()).isNotNull(); + assertThat(transport.getCapturedRequest().cursor()).isEqualTo("cursor-1"); + assertThat(transport.getCapturedRequest().meta()).containsEntry("customKey", "customValue"); } @Test void testListPromptsWithCursorAndMeta() { - McpSchema.Prompt mockPrompt = new McpSchema.Prompt("test-prompt", "A test prompt", List.of()); - McpSchema.ListPromptsResult mockResult = new McpSchema.ListPromptsResult(List.of(mockPrompt), null); - - McpSchema.ServerCapabilities caps = McpSchema.ServerCapabilities.builder().prompts(false).build(); - McpSchema.InitializeResult initResult = new McpSchema.InitializeResult(ProtocolVersions.MCP_2024_11_05, caps, - MOCK_SERVER_INFO, null); - - McpSchema.PaginatedRequest[] capturedRequest = new McpSchema.PaginatedRequest[1]; - - McpClientTransport transport = new McpClientTransport() { - Function, Mono> handler; - - @Override - public Mono connect( - Function, Mono> handler) { - return Mono.deferContextual(ctx -> { - this.handler = handler; - return Mono.empty(); - }); - } - - @Override - public Mono closeGracefully() { - return Mono.empty(); - } - - @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { - if (!(message instanceof McpSchema.JSONRPCRequest request)) { - return Mono.empty(); - } - McpSchema.JSONRPCResponse response; - if (McpSchema.METHOD_INITIALIZE.equals(request.method())) { - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), initResult, null); - } - else if (McpSchema.METHOD_PROMPT_LIST.equals(request.method())) { - capturedRequest[0] = JSON_MAPPER.convertValue(request.params(), McpSchema.PaginatedRequest.class); - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockResult, null); - } - else { - return Mono.empty(); - } - return handler.apply(Mono.just(response)).then(); - } - - @Override - public T unmarshalFrom(Object data, TypeRef typeRef) { - return JSON_MAPPER.convertValue(data, new TypeRef<>() { - @Override - public java.lang.reflect.Type getType() { - return typeRef.getType(); - } - }); - } - }; - + var transport = new TestMcpClientTransport(); McpAsyncClient client = McpClient.async(transport).build(); Map meta = Map.of("customKey", "customValue"); McpSchema.ListPromptsResult result = client.listPrompts("cursor-1", meta).block(); assertThat(result).isNotNull(); assertThat(result.prompts()).hasSize(1); - assertThat(capturedRequest[0]).isNotNull(); - assertThat(capturedRequest[0].cursor()).isEqualTo("cursor-1"); - assertThat(capturedRequest[0].meta()).containsEntry("customKey", "customValue"); + assertThat(transport.getCapturedRequest()).isNotNull(); + assertThat(transport.getCapturedRequest().cursor()).isEqualTo("cursor-1"); + assertThat(transport.getCapturedRequest().meta()).containsEntry("customKey", "customValue"); + } - @Test - void testSyncListPromptsWithCursorAndMeta() { - McpSchema.Prompt mockPrompt = new McpSchema.Prompt("test-prompt", "A test prompt", List.of()); - McpSchema.ListPromptsResult mockResult = new McpSchema.ListPromptsResult(List.of(mockPrompt), null); + static class TestMcpClientTransport implements McpClientTransport { - McpSchema.ServerCapabilities caps = McpSchema.ServerCapabilities.builder().prompts(false).build(); - McpSchema.InitializeResult initResult = new McpSchema.InitializeResult(ProtocolVersions.MCP_2024_11_05, caps, - MOCK_SERVER_INFO, null); + private Function, Mono> handler; - McpSchema.PaginatedRequest[] capturedRequest = new McpSchema.PaginatedRequest[1]; + private McpSchema.PaginatedRequest capturedRequest = null; - McpClientTransport transport = new McpClientTransport() { - Function, Mono> handler; + @Override + public Mono connect(Function, Mono> handler) { + return Mono.deferContextual(ctx -> { + this.handler = handler; + return Mono.empty(); + }); + } - @Override - public Mono connect( - Function, Mono> handler) { - return Mono.deferContextual(ctx -> { - this.handler = handler; - return Mono.empty(); - }); - } + @Override + public Mono closeGracefully() { + return Mono.empty(); + } - @Override - public Mono closeGracefully() { + @Override + public Mono sendMessage(McpSchema.JSONRPCMessage message) { + if (!(message instanceof McpSchema.JSONRPCRequest request)) { return Mono.empty(); } + McpSchema.JSONRPCResponse response; + if (McpSchema.METHOD_INITIALIZE.equals(request.method())) { + McpSchema.ServerCapabilities caps = McpSchema.ServerCapabilities.builder() + .prompts(false) + .resources(false, false) + .tools(false) + .build(); - @Override - public Mono sendMessage(McpSchema.JSONRPCMessage message) { - if (!(message instanceof McpSchema.JSONRPCRequest request)) { - return Mono.empty(); - } - McpSchema.JSONRPCResponse response; - if (McpSchema.METHOD_INITIALIZE.equals(request.method())) { - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), initResult, null); - } - else if (McpSchema.METHOD_PROMPT_LIST.equals(request.method())) { - capturedRequest[0] = JSON_MAPPER.convertValue(request.params(), McpSchema.PaginatedRequest.class); - response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockResult, null); - } - else { - return Mono.empty(); - } - return handler.apply(Mono.just(response)).then(); + McpSchema.InitializeResult initResult = new McpSchema.InitializeResult(ProtocolVersions.MCP_2024_11_05, + caps, MOCK_SERVER_INFO, null); + + response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), initResult, null); } + else if (McpSchema.METHOD_PROMPT_LIST.equals(request.method())) { + capturedRequest = JSON_MAPPER.convertValue(request.params(), McpSchema.PaginatedRequest.class); - @Override - public T unmarshalFrom(Object data, TypeRef typeRef) { - return JSON_MAPPER.convertValue(data, new TypeRef<>() { - @Override - public java.lang.reflect.Type getType() { - return typeRef.getType(); - } - }); + McpSchema.Prompt mockPrompt = new McpSchema.Prompt("test-prompt", "A test prompt", List.of()); + McpSchema.ListPromptsResult mockPromptResult = new McpSchema.ListPromptsResult(List.of(mockPrompt), + null); + response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockPromptResult, + null); } - }; + else if (McpSchema.METHOD_RESOURCES_TEMPLATES_LIST.equals(request.method())) { + capturedRequest = JSON_MAPPER.convertValue(request.params(), McpSchema.PaginatedRequest.class); - McpSyncClient client = McpClient.sync(transport).build(); + McpSchema.ResourceTemplate mockTemplate = new McpSchema.ResourceTemplate("file:///{name}", "template", + null, null, null); + McpSchema.ListResourceTemplatesResult mockResourceTemplateResult = new McpSchema.ListResourceTemplatesResult( + List.of(mockTemplate), null); + response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), + mockResourceTemplateResult, null); + } + else if (McpSchema.METHOD_RESOURCES_LIST.equals(request.method())) { + capturedRequest = JSON_MAPPER.convertValue(request.params(), McpSchema.PaginatedRequest.class); + + McpSchema.Resource mockResource = McpSchema.Resource.builder() + .uri("file:///test.txt") + .name("test.txt") + .build(); + McpSchema.ListResourcesResult mockResourceResult = new McpSchema.ListResourcesResult( + List.of(mockResource), null); + + response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockResourceResult, + null); + } + else if (McpSchema.METHOD_TOOLS_LIST.equals(request.method())) { + capturedRequest = JSON_MAPPER.convertValue(request.params(), McpSchema.PaginatedRequest.class); + + McpSchema.Tool addTool = McpSchema.Tool.builder().name("add").description("calculate add").build(); + McpSchema.ListToolsResult mockToolsResult = new McpSchema.ListToolsResult(List.of(addTool), null); + response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockToolsResult, + null); + } + else { + return Mono.empty(); + } + return handler.apply(Mono.just(response)).then(); + } + + @Override + public T unmarshalFrom(Object data, TypeRef typeRef) { + return JSON_MAPPER.convertValue(data, new TypeRef<>() { + @Override + public java.lang.reflect.Type getType() { + return typeRef.getType(); + } + }); + } + + public McpSchema.PaginatedRequest getCapturedRequest() { + return capturedRequest; + } - Map meta = Map.of("customKey", "customValue"); - McpSchema.ListPromptsResult result = client.listPrompts("cursor-1", meta); - assertThat(result).isNotNull(); - assertThat(result.prompts()).hasSize(1); - assertThat(capturedRequest[0]).isNotNull(); - assertThat(capturedRequest[0].cursor()).isEqualTo("cursor-1"); - assertThat(capturedRequest[0].meta()).containsEntry("customKey", "customValue"); } }