From 2865015b2c33085d3fc1a251ca96021810ebe707 Mon Sep 17 00:00:00 2001 From: codeboyzhou Date: Thu, 6 Nov 2025 00:31:53 +0800 Subject: [PATCH 1/3] refactor: use builder pattern for CallToolResult and simplify lambda expressions in tests --- ...stractMcpClientServerIntegrationTests.java | 2 +- .../server/AbstractMcpSyncServerTests.java | 120 +++++++++++------- .../AsyncToolSpecificationBuilderTest.java | 18 ++- 3 files changed, 85 insertions(+), 55 deletions(-) diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpClientServerIntegrationTests.java b/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpClientServerIntegrationTests.java index 603324631..ed9c6f84b 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpClientServerIntegrationTests.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpClientServerIntegrationTests.java @@ -851,7 +851,7 @@ void testThrowingToolCallIsCaughtBeforeTimeout(String clientType) { @ParameterizedTest(name = "{0} : {displayName} ") @MethodSource("clientsForTesting") - void testToolCallSuccessWithTranportContextExtraction(String clientType) { + void testToolCallSuccessWithTransportContextExtraction(String clientType) { var clientBuilder = clientBuilders.get(clientType); diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java b/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java index 976eb8c2c..d5f673a13 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java @@ -78,14 +78,14 @@ void testConstructorWithInvalidArguments() { void testGracefulShutdown() { var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test void testImmediateClose() { var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build(); - assertThatCode(() -> mcpSyncServer.close()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::close).doesNotThrowAnyException(); } @Test @@ -94,7 +94,7 @@ void testGetAsyncServer() { assertThat(mcpSyncServer.getAsyncServer()).isNotNull(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } // --------------------------------------- @@ -117,7 +117,7 @@ void testAddTool() { (exchange, args) -> new CallToolResult(List.of(), false)))) .doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -134,10 +134,10 @@ void testAddToolCall() { assertThatCode(() -> mcpSyncServer.addTool(McpServerFeatures.SyncToolSpecification.builder() .tool(newTool) - .callHandler((exchange, request) -> new CallToolResult(List.of(), false)) + .callHandler((exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build())).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -158,7 +158,7 @@ void testAddDuplicateTool() { (exchange, args) -> new CallToolResult(List.of(), false)))) .doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -171,15 +171,16 @@ void testAddDuplicateToolCall() { var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .toolCall(duplicateTool, (exchange, request) -> new CallToolResult(List.of(), false)) + .toolCall(duplicateTool, + (exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build(); assertThatCode(() -> mcpSyncServer.addTool(McpServerFeatures.SyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> new CallToolResult(List.of(), false)) + .callHandler((exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build())).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -192,8 +193,10 @@ void testDuplicateToolCallDuringBuilding() { assertThatThrownBy(() -> prepareSyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .toolCall(duplicateTool, (exchange, request) -> new CallToolResult(List.of(), false)) - .toolCall(duplicateTool, (exchange, request) -> new CallToolResult(List.of(), false)) // Duplicate! + .toolCall(duplicateTool, + (exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) + .toolCall(duplicateTool, + (exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) // Duplicate! .build()).isInstanceOf(IllegalArgumentException.class) .hasMessage("Tool with name 'duplicate-build-toolcall' is already registered."); } @@ -208,11 +211,13 @@ void testDuplicateToolsInBatchListRegistration() { List specs = List.of( McpServerFeatures.SyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> new CallToolResult(List.of(), false)) + .callHandler( + (exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build(), McpServerFeatures.SyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> new CallToolResult(List.of(), false)) + .callHandler( + (exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build() // Duplicate! ); @@ -235,11 +240,12 @@ void testDuplicateToolsInBatchVarargsRegistration() { .capabilities(ServerCapabilities.builder().tools(true).build()) .tools(McpServerFeatures.SyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> new CallToolResult(List.of(), false)) + .callHandler((exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build(), McpServerFeatures.SyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> new CallToolResult(List.of(), false)) + .callHandler((exchange, + request) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build() // Duplicate! ) .build()).isInstanceOf(IllegalArgumentException.class) @@ -256,12 +262,12 @@ void testRemoveTool() { var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .toolCall(tool, (exchange, args) -> new CallToolResult(List.of(), false)) + .toolCall(tool, (exchange, args) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build(); assertThatCode(() -> mcpSyncServer.removeTool(TEST_TOOL_NAME)).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -272,16 +278,16 @@ void testRemoveNonexistentTool() { assertThatCode(() -> mcpSyncServer.removeTool("nonexistent-tool")).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test void testNotifyToolsListChanged() { var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build(); - assertThatCode(() -> mcpSyncServer.notifyToolsListChanged()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::notifyToolsListChanged).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } // --------------------------------------- @@ -292,9 +298,9 @@ void testNotifyToolsListChanged() { void testNotifyResourcesListChanged() { var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build(); - assertThatCode(() -> mcpSyncServer.notifyResourcesListChanged()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::notifyResourcesListChanged).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -305,7 +311,7 @@ void testNotifyResourcesUpdated() { .notifyResourcesUpdated(new McpSchema.ResourcesUpdatedNotification(TEST_RESOURCE_URI))) .doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -314,14 +320,18 @@ void testAddResource() { .capabilities(ServerCapabilities.builder().resources(true, false).build()) .build(); - Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description", - null); + Resource resource = Resource.builder() + .uri(TEST_RESOURCE_URI) + .name("Test Resource") + .mimeType("text/plain") + .description("Test resource description") + .build(); McpServerFeatures.SyncResourceSpecification specification = new McpServerFeatures.SyncResourceSpecification( resource, (exchange, req) -> new ReadResourceResult(List.of())); assertThatCode(() -> mcpSyncServer.addResource(specification)).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -334,15 +344,19 @@ void testAddResourceWithNullSpecification() { .isInstanceOf(IllegalArgumentException.class) .hasMessage("Resource must not be null"); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test void testAddResourceWithoutCapability() { var serverWithoutResources = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build(); - Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description", - null); + Resource resource = Resource.builder() + .uri(TEST_RESOURCE_URI) + .name("Test Resource") + .mimeType("text/plain") + .description("Test resource description") + .build(); McpServerFeatures.SyncResourceSpecification specification = new McpServerFeatures.SyncResourceSpecification( resource, (exchange, req) -> new ReadResourceResult(List.of())); @@ -366,8 +380,12 @@ void testListResources() { .capabilities(ServerCapabilities.builder().resources(true, false).build()) .build(); - Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description", - null); + Resource resource = Resource.builder() + .uri(TEST_RESOURCE_URI) + .name("Test Resource") + .mimeType("text/plain") + .description("Test resource description") + .build(); McpServerFeatures.SyncResourceSpecification specification = new McpServerFeatures.SyncResourceSpecification( resource, (exchange, req) -> new ReadResourceResult(List.of())); @@ -377,7 +395,7 @@ void testListResources() { assertThat(resources).hasSize(1); assertThat(resources.get(0).uri()).isEqualTo(TEST_RESOURCE_URI); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -386,15 +404,19 @@ void testRemoveResource() { .capabilities(ServerCapabilities.builder().resources(true, false).build()) .build(); - Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description", - null); + Resource resource = Resource.builder() + .uri(TEST_RESOURCE_URI) + .name("Test Resource") + .mimeType("text/plain") + .description("Test resource description") + .build(); McpServerFeatures.SyncResourceSpecification specification = new McpServerFeatures.SyncResourceSpecification( resource, (exchange, req) -> new ReadResourceResult(List.of())); mcpSyncServer.addResource(specification); assertThatCode(() -> mcpSyncServer.removeResource(TEST_RESOURCE_URI)).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -407,7 +429,7 @@ void testRemoveNonexistentResource() { // as per the new implementation that just logs a warning assertThatCode(() -> mcpSyncServer.removeResource("nonexistent://resource")).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } // --------------------------------------- @@ -432,7 +454,7 @@ void testAddResourceTemplate() { assertThatCode(() -> mcpSyncServer.addResourceTemplate(specification)).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -474,7 +496,7 @@ void testRemoveResourceTemplate() { assertThatCode(() -> mcpSyncServer.removeResourceTemplate("test://template/{id}")).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -496,7 +518,7 @@ void testRemoveNonexistentResourceTemplate() { assertThatCode(() -> mcpSyncServer.removeResourceTemplate("nonexistent://template/{id}")) .doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -520,7 +542,7 @@ void testListResourceTemplates() { assertThat(templates).isNotNull(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } // --------------------------------------- @@ -531,9 +553,9 @@ void testListResourceTemplates() { void testNotifyPromptsListChanged() { var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build(); - assertThatCode(() -> mcpSyncServer.notifyPromptsListChanged()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::notifyPromptsListChanged).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -584,7 +606,7 @@ void testRemovePrompt() { assertThatCode(() -> mcpSyncServer.removePrompt(TEST_PROMPT_NAME)).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -595,7 +617,7 @@ void testRemoveNonexistentPrompt() { assertThatCode(() -> mcpSyncServer.removePrompt("nonexistent://template/{id}")).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } // --------------------------------------- @@ -617,7 +639,7 @@ void testRootsChangeHandlers() { })) .build(); assertThat(singleConsumerServer).isNotNull(); - assertThatCode(() -> singleConsumerServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(singleConsumerServer::closeGracefully).doesNotThrowAnyException(); onClose(); // Test with multiple consumers @@ -633,7 +655,7 @@ void testRootsChangeHandlers() { .build(); assertThat(multipleConsumersServer).isNotNull(); - assertThatCode(() -> multipleConsumersServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(multipleConsumersServer::closeGracefully).doesNotThrowAnyException(); onClose(); // Test error handling @@ -644,14 +666,14 @@ void testRootsChangeHandlers() { .build(); assertThat(errorHandlingServer).isNotNull(); - assertThatCode(() -> errorHandlingServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(errorHandlingServer::closeGracefully).doesNotThrowAnyException(); onClose(); // Test without consumers var noConsumersServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build(); assertThat(noConsumersServer).isNotNull(); - assertThatCode(() -> noConsumersServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(noConsumersServer::closeGracefully).doesNotThrowAnyException(); } } diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/AsyncToolSpecificationBuilderTest.java b/mcp-core/src/test/java/io/modelcontextprotocol/server/AsyncToolSpecificationBuilderTest.java index 8fe8e6fb0..ef092d241 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/AsyncToolSpecificationBuilderTest.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/server/AsyncToolSpecificationBuilderTest.java @@ -40,7 +40,7 @@ void builderShouldCreateValidAsyncToolSpecification() { McpServerFeatures.AsyncToolSpecification specification = McpServerFeatures.AsyncToolSpecification.builder() .tool(tool) .callHandler((exchange, request) -> Mono - .just(new CallToolResult(List.of(new TextContent("Test result")), false))) + .just(CallToolResult.builder().content(List.of(new TextContent("Test result"))).isError(false).build())) .build(); assertThat(specification).isNotNull(); @@ -52,7 +52,8 @@ void builderShouldCreateValidAsyncToolSpecification() { @Test void builderShouldThrowExceptionWhenToolIsNull() { assertThatThrownBy(() -> McpServerFeatures.AsyncToolSpecification.builder() - .callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .callHandler((exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build()).isInstanceOf(IllegalArgumentException.class).hasMessage("Tool must not be null"); } @@ -80,7 +81,8 @@ void builderShouldAllowMethodChaining() { // Then - verify method chaining returns the same builder instance assertThat(builder.tool(tool)).isSameAs(builder); - assertThat(builder.callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false)))) + assertThat(builder.callHandler( + (exchange, request) -> Mono.just(CallToolResult.builder().content(List.of()).isError(false).build()))) .isSameAs(builder); } @@ -96,7 +98,10 @@ void builtSpecificationShouldExecuteCallToolCorrectly() { McpServerFeatures.AsyncToolSpecification specification = McpServerFeatures.AsyncToolSpecification.builder() .tool(tool) .callHandler((exchange, request) -> { - return Mono.just(new CallToolResult(List.of(new TextContent(expectedResult)), false)); + return Mono.just(CallToolResult.builder() + .content(List.of(new TextContent(expectedResult))) + .isError(false) + .build()); }) .build(); @@ -169,7 +174,10 @@ void fromSyncShouldConvertSyncToolSpecificationCorrectly() { // Create a sync tool specification McpServerFeatures.SyncToolSpecification syncSpec = McpServerFeatures.SyncToolSpecification.builder() .tool(tool) - .callHandler((exchange, request) -> new CallToolResult(List.of(new TextContent(expectedResult)), false)) + .callHandler((exchange, request) -> CallToolResult.builder() + .content(List.of(new TextContent(expectedResult))) + .isError(false) + .build()) .build(); // Convert to async using fromSync From 79b5a295cd76069af159128cbf26b8c548f0dc20 Mon Sep 17 00:00:00 2001 From: codeboyzhou Date: Thu, 20 Nov 2025 23:41:31 +0800 Subject: [PATCH 2/3] refactor(#652): improve the PR #652 - Mark McpSchema.CallToolResult#CallToolResult(java.lang.String, java.lang.Boolean) as deprecated. - Update McpAsyncServer and McpStatelessAsyncServer to use the builder instead of that constructor. - Remove test usages of that constructor as well. --- .../server/McpAsyncServer.java | 17 ++-- .../server/McpServer.java | 30 ++++-- .../server/McpServerFeatures.java | 5 +- .../server/McpStatelessAsyncServer.java | 17 ++-- .../modelcontextprotocol/spec/McpSchema.java | 3 +- .../server/AbstractMcpAsyncServerTests.java | 53 +++++++---- ...stractMcpClientServerIntegrationTests.java | 15 ++- .../server/AbstractMcpSyncServerTests.java | 7 +- .../AsyncToolSpecificationBuilderTest.java | 12 ++- .../HttpServletStatelessIntegrationTests.java | 5 +- .../SyncToolSpecificationBuilderTest.java | 16 +++- ...stractMcpClientServerIntegrationTests.java | 15 ++- .../server/AbstractMcpAsyncServerTests.java | 53 +++++++---- .../server/AbstractMcpSyncServerTests.java | 95 ++++++++++--------- 14 files changed, 222 insertions(+), 121 deletions(-) diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java index ac4b36990..23285d514 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java @@ -24,7 +24,6 @@ import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.CompleteResult.CompleteCompletion; import io.modelcontextprotocol.spec.McpSchema.ErrorCodes; -import io.modelcontextprotocol.spec.McpSchema.JSONRPCResponse; import io.modelcontextprotocol.spec.McpSchema.LoggingLevel; import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification; import io.modelcontextprotocol.spec.McpSchema.PromptReference; @@ -398,11 +397,12 @@ public Mono apply(McpAsyncServerExchange exchange, McpSchema.Cal // results that conform to this schema. // https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema if (result.structuredContent() == null) { - logger.warn( - "Response missing structured content which is expected when calling tool with non-empty outputSchema"); - return new CallToolResult( - "Response missing structured content which is expected when calling tool with non-empty outputSchema", - true); + String content = "Response missing structured content which is expected when calling tool with non-empty outputSchema"; + logger.warn(content); + return CallToolResult.builder() + .content(List.of(new McpSchema.TextContent(content))) + .isError(true) + .build(); } // Validate the result against the output schema @@ -410,7 +410,10 @@ public Mono apply(McpAsyncServerExchange exchange, McpSchema.Cal if (!validation.valid()) { logger.warn("Tool call result validation failed: {}", validation.errorMessage()); - return new CallToolResult(validation.errorMessage(), true); + return CallToolResult.builder() + .content(List.of(new McpSchema.TextContent(validation.errorMessage()))) + .isError(true) + .build(); } if (Utils.isEmpty(result.content())) { diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java index 87c84ba1b..fe3125271 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java @@ -67,7 +67,10 @@ * McpServer.sync(transportProvider) * .serverInfo("my-server", "1.0.0") * .tool(Tool.builder().name("calculator").title("Performs calculations").inputSchema(schema).build(), - * (exchange, args) -> new CallToolResult("Result: " + calculate(args))) + * (exchange, args) -> CallToolResult.builder() + * .content(List.of(new McpSchema.TextContent("Result: " + calculate(args)))) + * .isError(false) + * .build()) * .build(); * } * @@ -76,7 +79,10 @@ * .serverInfo("my-server", "1.0.0") * .tool(Tool.builder().name("calculator").title("Performs calculations").inputSchema(schema).build(), * (exchange, args) -> Mono.fromSupplier(() -> calculate(args)) - * .map(result -> new CallToolResult("Result: " + result))) + * .map(result -> CallToolResult.builder() + * .content(List.of(new McpSchema.TextContent("Result: " + result))) + * .isError(false) + * .build())) * .build(); * } * @@ -90,12 +96,18 @@ * McpServerFeatures.AsyncToolSpecification.builder() * .tool(calculatorTool) * .callTool((exchange, args) -> Mono.fromSupplier(() -> calculate(args.arguments())) - * .map(result -> new CallToolResult("Result: " + result)))) + * .map(result -> CallToolResult.builder() + * .content(List.of(new McpSchema.TextContent("Result: " + result))) + * .isError(false) + * .build())) *. .build(), * McpServerFeatures.AsyncToolSpecification.builder() * .tool((weatherTool) * .callTool((exchange, args) -> Mono.fromSupplier(() -> getWeather(args.arguments())) - * .map(result -> new CallToolResult("Weather: " + result)))) + * .map(result -> CallToolResult.builder() + * .content(List.of(new McpSchema.TextContent("Weather: " + result))) + * .isError(false) + * .build())) * .build() * ) * // Register resources @@ -425,7 +437,10 @@ public AsyncSpecification capabilities(McpSchema.ServerCapabilities serverCap * .tool( * Tool.builder().name("calculator").title("Performs calculations").inputSchema(schema).build(), * (exchange, args) -> Mono.fromSupplier(() -> calculate(args)) - * .map(result -> new CallToolResult("Result: " + result)) + * .map(result -> CallToolResult.builder() + * .content(List.of(new McpSchema.TextContent("Result: " + result))) + * .isError(false) + * .build())) * ) * } * @param tool The tool definition including name, description, and schema. Must @@ -1022,7 +1037,10 @@ public SyncSpecification capabilities(McpSchema.ServerCapabilities serverCapa * Example usage:
{@code
 		 * .tool(
 		 *     Tool.builder().name("calculator").title("Performs calculations".inputSchema(schema).build(),
-		 *     (exchange, args) -> new CallToolResult("Result: " + calculate(args))
+		 *     (exchange, args) -> CallToolResult.builder()
+		 *                   .content(List.of(new McpSchema.TextContent("Result: " + calculate(args))))
+		 *                   .isError(false)
+		 *                   .build())
 		 * )
 		 * }
* @param tool The tool definition including name, description, and schema. Must diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java index fc5bdfe4e..5a12914a9 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java @@ -508,7 +508,10 @@ static AsyncCompletionSpecification fromSync(SyncCompletionSpecification complet * .build() * .toolHandler((exchange, req) -> { * String expr = (String) req.arguments().get("expression"); - * return new CallToolResult("Result: " + evaluate(expr)); + * return CallToolResult.builder() + * .content(List.of(new McpSchema.TextContent("Result: " + evaluate(expr)))) + * .isError(false) + * .build(); * })) * .build(); * } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java index 997df7225..c7a1fd0d7 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java @@ -14,7 +14,6 @@ import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.CompleteResult.CompleteCompletion; import io.modelcontextprotocol.spec.McpSchema.ErrorCodes; -import io.modelcontextprotocol.spec.McpSchema.JSONRPCResponse; import io.modelcontextprotocol.spec.McpSchema.PromptReference; import io.modelcontextprotocol.spec.McpSchema.ResourceReference; import io.modelcontextprotocol.spec.McpSchema.Tool; @@ -277,11 +276,12 @@ public Mono apply(McpTransportContext transportContext, McpSchem // results that conform to this schema. // https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema if (result.structuredContent() == null) { - logger.warn( - "Response missing structured content which is expected when calling tool with non-empty outputSchema"); - return new CallToolResult( - "Response missing structured content which is expected when calling tool with non-empty outputSchema", - true); + String content = "Response missing structured content which is expected when calling tool with non-empty outputSchema"; + logger.warn(content); + return CallToolResult.builder() + .content(List.of(new McpSchema.TextContent(content))) + .isError(true) + .build(); } // Validate the result against the output schema @@ -289,7 +289,10 @@ public Mono apply(McpTransportContext transportContext, McpSchem if (!validation.valid()) { logger.warn("Tool call result validation failed: {}", validation.errorMessage()); - return new CallToolResult(validation.errorMessage(), true); + return CallToolResult.builder() + .content(List.of(new McpSchema.TextContent(validation.errorMessage()))) + .isError(true) + .build(); } if (Utils.isEmpty(result.content())) { diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java index 342fc5347..734cff237 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -1562,6 +1562,7 @@ public CallToolResult(List content, Boolean isError, Map meta) { * @return a new CallToolResult instance */ public CallToolResult build() { - return new CallToolResult(content, isError, (Object) structuredContent, meta); + return new CallToolResult(content, isError, structuredContent, meta); } } diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java b/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java index aa68203dd..0bd7640dc 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java @@ -7,7 +7,6 @@ import java.time.Duration; import java.util.List; -import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.GetPromptResult; @@ -86,7 +85,7 @@ void testGracefulShutdown() { void testImmediateClose() { var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build(); - assertThatCode(() -> mcpAsyncServer.close()).doesNotThrowAnyException(); + assertThatCode(mcpAsyncServer::close).doesNotThrowAnyException(); } // --------------------------------------- @@ -104,8 +103,9 @@ void testAddTool() { .capabilities(ServerCapabilities.builder().tools(true).build()) .build(); - StepVerifier.create(mcpAsyncServer.addTool(new McpServerFeatures.AsyncToolSpecification(newTool, - (exchange, args) -> Mono.just(new CallToolResult(List.of(), false))))) + StepVerifier + .create(mcpAsyncServer.addTool(new McpServerFeatures.AsyncToolSpecification(newTool, + (exchange, args) -> Mono.just(CallToolResult.builder().content(List.of()).isError(false).build())))) .verifyComplete(); assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException(); @@ -125,7 +125,8 @@ void testAddToolCall() { StepVerifier.create(mcpAsyncServer.addTool(McpServerFeatures.AsyncToolSpecification.builder() .tool(newTool) - .callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .callHandler((exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build())).verifyComplete(); assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException(); @@ -142,11 +143,13 @@ void testAddDuplicateTool() { var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .tool(duplicateTool, (exchange, args) -> Mono.just(new CallToolResult(List.of(), false))) + .tool(duplicateTool, + (exchange, args) -> Mono.just(CallToolResult.builder().content(List.of()).isError(false).build())) .build(); - StepVerifier.create(mcpAsyncServer.addTool(new McpServerFeatures.AsyncToolSpecification(duplicateTool, - (exchange, args) -> Mono.just(new CallToolResult(List.of(), false))))) + StepVerifier + .create(mcpAsyncServer.addTool(new McpServerFeatures.AsyncToolSpecification(duplicateTool, + (exchange, args) -> Mono.just(CallToolResult.builder().content(List.of()).isError(false).build())))) .verifyComplete(); assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException(); @@ -162,12 +165,15 @@ void testAddDuplicateToolCall() { var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .toolCall(duplicateTool, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .toolCall(duplicateTool, + (exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build(); StepVerifier.create(mcpAsyncServer.addTool(McpServerFeatures.AsyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .callHandler((exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build())).verifyComplete(); assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException(); @@ -183,8 +189,12 @@ void testDuplicateToolCallDuringBuilding() { assertThatThrownBy(() -> prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .toolCall(duplicateTool, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) - .toolCall(duplicateTool, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) // Duplicate! + .toolCall(duplicateTool, + (exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) + .toolCall(duplicateTool, + (exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) // Duplicate! .build()).isInstanceOf(IllegalArgumentException.class) .hasMessage("Tool with name 'duplicate-build-toolcall' is already registered."); } @@ -200,11 +210,13 @@ void testDuplicateToolsInBatchListRegistration() { List specs = List.of( McpServerFeatures.AsyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .callHandler((exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build(), McpServerFeatures.AsyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .callHandler((exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build() // Duplicate! ); @@ -227,11 +239,13 @@ void testDuplicateToolsInBatchVarargsRegistration() { .capabilities(ServerCapabilities.builder().tools(true).build()) .tools(McpServerFeatures.AsyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .callHandler((exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build(), McpServerFeatures.AsyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .callHandler((exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build() // Duplicate! ) .build()).isInstanceOf(IllegalArgumentException.class) @@ -248,7 +262,9 @@ void testRemoveTool() { var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .toolCall(too, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .toolCall(too, + (exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build(); StepVerifier.create(mcpAsyncServer.removeTool(TEST_TOOL_NAME)).verifyComplete(); @@ -277,7 +293,8 @@ void testNotifyToolsListChanged() { var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .toolCall(too, (exchange, args) -> Mono.just(new CallToolResult(List.of(), false))) + .toolCall(too, + (exchange, args) -> Mono.just(CallToolResult.builder().content(List.of()).isError(false).build())) .build(); StepVerifier.create(mcpAsyncServer.notifyToolsListChanged()).verifyComplete(); diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpClientServerIntegrationTests.java b/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpClientServerIntegrationTests.java index ed9c6f84b..1f5387f37 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpClientServerIntegrationTests.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpClientServerIntegrationTests.java @@ -1077,7 +1077,10 @@ void testLoggingNotification(String clientType) throws InterruptedException { .logger("test-logger") .data("Another error message") .build())) - .thenReturn(new CallToolResult("Logging test completed", false)); + .thenReturn(CallToolResult.builder() + .content(List.of(new McpSchema.TextContent("Logging test completed"))) + .isError(false) + .build()); //@formatter:on }) .build(); @@ -1172,7 +1175,10 @@ void testProgressNotification(String clientType) throws InterruptedException { 0.0, 1.0, "Another processing started"))) .then(exchange.progressNotification( new McpSchema.ProgressNotification(progressToken, 1.0, 1.0, "Processing completed"))) - .thenReturn(new CallToolResult(("Progress test completed"), false)); + .thenReturn(CallToolResult.builder() + .content(List.of(new McpSchema.TextContent("Progress test completed"))) + .isError(false) + .build()); }) .build(); @@ -1326,7 +1332,10 @@ void testPingSuccess(String clientType) { assertThat(result).isNotNull(); }).then(Mono.fromCallable(() -> { executionOrder.set(executionOrder.get() + "3"); - return new CallToolResult("Async ping test completed", false); + return CallToolResult.builder() + .content(List.of(new McpSchema.TextContent("Async ping test completed"))) + .isError(false) + .build(); })); }) .build(); diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java b/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java index d5f673a13..915c658e3 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java @@ -6,7 +6,6 @@ import java.util.List; -import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.GetPromptResult; @@ -114,7 +113,7 @@ void testAddTool() { .inputSchema(EMPTY_JSON_SCHEMA) .build(); assertThatCode(() -> mcpSyncServer.addTool(new McpServerFeatures.SyncToolSpecification(newTool, - (exchange, args) -> new CallToolResult(List.of(), false)))) + (exchange, args) -> CallToolResult.builder().content(List.of()).isError(false).build()))) .doesNotThrowAnyException(); assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); @@ -151,11 +150,11 @@ void testAddDuplicateTool() { var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .tool(duplicateTool, (exchange, args) -> new CallToolResult(List.of(), false)) + .tool(duplicateTool, (exchange, args) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build(); assertThatCode(() -> mcpSyncServer.addTool(new McpServerFeatures.SyncToolSpecification(duplicateTool, - (exchange, args) -> new CallToolResult(List.of(), false)))) + (exchange, args) -> CallToolResult.builder().content(List.of()).isError(false).build()))) .doesNotThrowAnyException(); assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/AsyncToolSpecificationBuilderTest.java b/mcp-core/src/test/java/io/modelcontextprotocol/server/AsyncToolSpecificationBuilderTest.java index ef092d241..62332fcdb 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/AsyncToolSpecificationBuilderTest.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/server/AsyncToolSpecificationBuilderTest.java @@ -129,8 +129,11 @@ void deprecatedConstructorShouldWorkCorrectly() { // Test the deprecated constructor that takes a 'call' function McpServerFeatures.AsyncToolSpecification specification = new McpServerFeatures.AsyncToolSpecification(tool, - (exchange, arguments) -> Mono - .just(new CallToolResult(List.of(new TextContent(expectedResult)), false))); + (exchange, + arguments) -> Mono.just(CallToolResult.builder() + .content(List.of(new TextContent(expectedResult))) + .isError(false) + .build())); assertThat(specification).isNotNull(); assertThat(specification.tool()).isEqualTo(tool); @@ -217,7 +220,10 @@ void fromSyncShouldConvertSyncToolSpecificationWithDeprecatedCallCorrectly() { // Create a sync tool specification using the deprecated constructor McpServerFeatures.SyncToolSpecification syncSpec = new McpServerFeatures.SyncToolSpecification(tool, - (exchange, arguments) -> new CallToolResult(List.of(new TextContent(expectedResult)), false)); + (exchange, arguments) -> CallToolResult.builder() + .content(List.of(new TextContent(expectedResult))) + .isError(false) + .build()); // Convert to async using fromSync McpServerFeatures.AsyncToolSpecification asyncSpec = McpServerFeatures.AsyncToolSpecification diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/HttpServletStatelessIntegrationTests.java b/mcp-core/src/test/java/io/modelcontextprotocol/server/HttpServletStatelessIntegrationTests.java index de74bafc1..491c2d4ed 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/HttpServletStatelessIntegrationTests.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/server/HttpServletStatelessIntegrationTests.java @@ -114,7 +114,10 @@ void testToolCallSuccess(String clientType) { var clientBuilder = clientBuilders.get(clientType); - var callResponse = new CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")), null); + var callResponse = CallToolResult.builder() + .content(List.of(new McpSchema.TextContent("CALL RESPONSE"))) + .isError(false) + .build(); McpStatelessServerFeatures.SyncToolSpecification tool1 = new McpStatelessServerFeatures.SyncToolSpecification( Tool.builder().name("tool1").title("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build(), (transportContext, request) -> { diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/SyncToolSpecificationBuilderTest.java b/mcp-core/src/test/java/io/modelcontextprotocol/server/SyncToolSpecificationBuilderTest.java index cd643c600..9bcd2bc84 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/SyncToolSpecificationBuilderTest.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/server/SyncToolSpecificationBuilderTest.java @@ -32,7 +32,10 @@ void builderShouldCreateValidSyncToolSpecification() { McpServerFeatures.SyncToolSpecification specification = McpServerFeatures.SyncToolSpecification.builder() .tool(tool) - .callHandler((exchange, request) -> new CallToolResult(List.of(new TextContent("Test result")), false)) + .callHandler((exchange, request) -> CallToolResult.builder() + .content(List.of(new TextContent("Test result"))) + .isError(false) + .build()) .build(); assertThat(specification).isNotNull(); @@ -44,7 +47,7 @@ void builderShouldCreateValidSyncToolSpecification() { @Test void builderShouldThrowExceptionWhenToolIsNull() { assertThatThrownBy(() -> McpServerFeatures.SyncToolSpecification.builder() - .callHandler((exchange, request) -> new CallToolResult(List.of(), false)) + .callHandler((exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build()).isInstanceOf(IllegalArgumentException.class).hasMessage("Tool must not be null"); } @@ -64,7 +67,9 @@ void builderShouldAllowMethodChaining() { // Then - verify method chaining returns the same builder instance assertThat(builder.tool(tool)).isSameAs(builder); - assertThat(builder.callHandler((exchange, request) -> new CallToolResult(List.of(), false))).isSameAs(builder); + assertThat(builder + .callHandler((exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build())) + .isSameAs(builder); } @Test @@ -80,7 +85,10 @@ void builtSpecificationShouldExecuteCallToolCorrectly() { .tool(tool) .callHandler((exchange, request) -> { // Simple test implementation - return new CallToolResult(List.of(new TextContent(expectedResult)), false); + return CallToolResult.builder() + .content(List.of(new TextContent(expectedResult))) + .isError(false) + .build(); }) .build(); diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/AbstractMcpClientServerIntegrationTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/AbstractMcpClientServerIntegrationTests.java index 37a1ef31d..270bc4308 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/AbstractMcpClientServerIntegrationTests.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/AbstractMcpClientServerIntegrationTests.java @@ -1081,7 +1081,10 @@ void testLoggingNotification(String clientType) throws InterruptedException { .logger("test-logger") .data("Another error message") .build())) - .thenReturn(new CallToolResult("Logging test completed", false)); + .thenReturn(CallToolResult.builder() + .content(List.of(new McpSchema.TextContent("Logging test completed"))) + .isError(false) + .build()); //@formatter:on }) .build(); @@ -1176,7 +1179,10 @@ void testProgressNotification(String clientType) throws InterruptedException { 0.0, 1.0, "Another processing started"))) .then(exchange.progressNotification( new McpSchema.ProgressNotification(progressToken, 1.0, 1.0, "Processing completed"))) - .thenReturn(new CallToolResult(("Progress test completed"), false)); + .thenReturn(CallToolResult.builder() + .content(List.of(new McpSchema.TextContent("Progress test completed"))) + .isError(false) + .build()); }) .build(); @@ -1330,7 +1336,10 @@ void testPingSuccess(String clientType) { assertThat(result).isNotNull(); }).then(Mono.fromCallable(() -> { executionOrder.set(executionOrder.get() + "3"); - return new CallToolResult("Async ping test completed", false); + return CallToolResult.builder() + .content(List.of(new McpSchema.TextContent("Async ping test completed"))) + .isError(false) + .build(); })); }) .build(); diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java index c24bcd622..f36015e18 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java @@ -7,7 +7,6 @@ import java.time.Duration; import java.util.List; -import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.GetPromptResult; @@ -90,7 +89,7 @@ void testGracefulShutdown() { void testImmediateClose() { var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build(); - assertThatCode(() -> mcpAsyncServer.close()).doesNotThrowAnyException(); + assertThatCode(mcpAsyncServer::close).doesNotThrowAnyException(); } // --------------------------------------- @@ -108,8 +107,9 @@ void testAddTool() { .capabilities(ServerCapabilities.builder().tools(true).build()) .build(); - StepVerifier.create(mcpAsyncServer.addTool(new McpServerFeatures.AsyncToolSpecification(newTool, - (exchange, args) -> Mono.just(new CallToolResult(List.of(), false))))) + StepVerifier + .create(mcpAsyncServer.addTool(new McpServerFeatures.AsyncToolSpecification(newTool, + (exchange, args) -> Mono.just(CallToolResult.builder().content(List.of()).isError(false).build())))) .verifyComplete(); assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException(); @@ -129,7 +129,8 @@ void testAddToolCall() { StepVerifier.create(mcpAsyncServer.addTool(McpServerFeatures.AsyncToolSpecification.builder() .tool(newTool) - .callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .callHandler((exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build())).verifyComplete(); assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException(); @@ -146,11 +147,13 @@ void testAddDuplicateTool() { var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .tool(duplicateTool, (exchange, args) -> Mono.just(new CallToolResult(List.of(), false))) + .tool(duplicateTool, + (exchange, args) -> Mono.just(CallToolResult.builder().content(List.of()).isError(false).build())) .build(); - StepVerifier.create(mcpAsyncServer.addTool(new McpServerFeatures.AsyncToolSpecification(duplicateTool, - (exchange, args) -> Mono.just(new CallToolResult(List.of(), false))))) + StepVerifier + .create(mcpAsyncServer.addTool(new McpServerFeatures.AsyncToolSpecification(duplicateTool, + (exchange, args) -> Mono.just(CallToolResult.builder().content(List.of()).isError(false).build())))) .verifyComplete(); assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException(); @@ -166,12 +169,15 @@ void testAddDuplicateToolCall() { var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .toolCall(duplicateTool, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .toolCall(duplicateTool, + (exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build(); StepVerifier.create(mcpAsyncServer.addTool(McpServerFeatures.AsyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .callHandler((exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build())).verifyComplete(); assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException(); @@ -187,8 +193,12 @@ void testDuplicateToolCallDuringBuilding() { assertThatThrownBy(() -> prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .toolCall(duplicateTool, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) - .toolCall(duplicateTool, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) // Duplicate! + .toolCall(duplicateTool, + (exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) + .toolCall(duplicateTool, + (exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) // Duplicate! .build()).isInstanceOf(IllegalArgumentException.class) .hasMessage("Tool with name 'duplicate-build-toolcall' is already registered."); } @@ -204,11 +214,13 @@ void testDuplicateToolsInBatchListRegistration() { List specs = List.of( McpServerFeatures.AsyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .callHandler((exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build(), McpServerFeatures.AsyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .callHandler((exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build() // Duplicate! ); @@ -231,11 +243,13 @@ void testDuplicateToolsInBatchVarargsRegistration() { .capabilities(ServerCapabilities.builder().tools(true).build()) .tools(McpServerFeatures.AsyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .callHandler((exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build(), McpServerFeatures.AsyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .callHandler((exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build() // Duplicate! ) .build()).isInstanceOf(IllegalArgumentException.class) @@ -252,7 +266,9 @@ void testRemoveTool() { var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .toolCall(too, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) + .toolCall(too, + (exchange, request) -> Mono + .just(CallToolResult.builder().content(List.of()).isError(false).build())) .build(); StepVerifier.create(mcpAsyncServer.removeTool(TEST_TOOL_NAME)).verifyComplete(); @@ -281,7 +297,8 @@ void testNotifyToolsListChanged() { var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .toolCall(too, (exchange, args) -> Mono.just(new CallToolResult(List.of(), false))) + .toolCall(too, + (exchange, args) -> Mono.just(CallToolResult.builder().content(List.of()).isError(false).build())) .build(); StepVerifier.create(mcpAsyncServer.notifyToolsListChanged()).verifyComplete(); diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java index 591f750cb..28fd57e77 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java @@ -6,7 +6,6 @@ import java.util.List; -import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.GetPromptResult; @@ -77,14 +76,14 @@ void testConstructorWithInvalidArguments() { void testGracefulShutdown() { var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test void testImmediateClose() { var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build(); - assertThatCode(() -> mcpSyncServer.close()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::close).doesNotThrowAnyException(); } @Test @@ -93,7 +92,7 @@ void testGetAsyncServer() { assertThat(mcpSyncServer.getAsyncServer()).isNotNull(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } // --------------------------------------- @@ -113,10 +112,10 @@ void testAddTool() { .inputSchema(EMPTY_JSON_SCHEMA) .build(); assertThatCode(() -> mcpSyncServer.addTool(new McpServerFeatures.SyncToolSpecification(newTool, - (exchange, args) -> new CallToolResult(List.of(), false)))) + (exchange, args) -> CallToolResult.builder().content(List.of()).isError(false).build()))) .doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -133,10 +132,10 @@ void testAddToolCall() { assertThatCode(() -> mcpSyncServer.addTool(McpServerFeatures.SyncToolSpecification.builder() .tool(newTool) - .callHandler((exchange, request) -> new CallToolResult(List.of(), false)) + .callHandler((exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build())).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -150,14 +149,14 @@ void testAddDuplicateTool() { var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .tool(duplicateTool, (exchange, args) -> new CallToolResult(List.of(), false)) + .tool(duplicateTool, (exchange, args) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build(); assertThatCode(() -> mcpSyncServer.addTool(new McpServerFeatures.SyncToolSpecification(duplicateTool, - (exchange, args) -> new CallToolResult(List.of(), false)))) + (exchange, args) -> CallToolResult.builder().content(List.of()).isError(false).build()))) .doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -170,15 +169,16 @@ void testAddDuplicateToolCall() { var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .toolCall(duplicateTool, (exchange, request) -> new CallToolResult(List.of(), false)) + .toolCall(duplicateTool, + (exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build(); assertThatCode(() -> mcpSyncServer.addTool(McpServerFeatures.SyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> new CallToolResult(List.of(), false)) + .callHandler((exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build())).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -191,8 +191,10 @@ void testDuplicateToolCallDuringBuilding() { assertThatThrownBy(() -> prepareSyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .toolCall(duplicateTool, (exchange, request) -> new CallToolResult(List.of(), false)) - .toolCall(duplicateTool, (exchange, request) -> new CallToolResult(List.of(), false)) // Duplicate! + .toolCall(duplicateTool, + (exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) + .toolCall(duplicateTool, + (exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) // Duplicate! .build()).isInstanceOf(IllegalArgumentException.class) .hasMessage("Tool with name 'duplicate-build-toolcall' is already registered."); } @@ -207,11 +209,13 @@ void testDuplicateToolsInBatchListRegistration() { List specs = List.of( McpServerFeatures.SyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> new CallToolResult(List.of(), false)) + .callHandler( + (exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build(), McpServerFeatures.SyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> new CallToolResult(List.of(), false)) + .callHandler( + (exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build() // Duplicate! ); @@ -234,11 +238,12 @@ void testDuplicateToolsInBatchVarargsRegistration() { .capabilities(ServerCapabilities.builder().tools(true).build()) .tools(McpServerFeatures.SyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> new CallToolResult(List.of(), false)) + .callHandler((exchange, request) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build(), McpServerFeatures.SyncToolSpecification.builder() .tool(duplicateTool) - .callHandler((exchange, request) -> new CallToolResult(List.of(), false)) + .callHandler((exchange, + request) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build() // Duplicate! ) .build()).isInstanceOf(IllegalArgumentException.class) @@ -255,12 +260,12 @@ void testRemoveTool() { var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0") .capabilities(ServerCapabilities.builder().tools(true).build()) - .toolCall(tool, (exchange, args) -> new CallToolResult(List.of(), false)) + .toolCall(tool, (exchange, args) -> CallToolResult.builder().content(List.of()).isError(false).build()) .build(); assertThatCode(() -> mcpSyncServer.removeTool(TEST_TOOL_NAME)).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -271,16 +276,16 @@ void testRemoveNonexistentTool() { assertThatCode(() -> mcpSyncServer.removeTool("nonexistent-tool")).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test void testNotifyToolsListChanged() { var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build(); - assertThatCode(() -> mcpSyncServer.notifyToolsListChanged()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::notifyToolsListChanged).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } // --------------------------------------- @@ -291,9 +296,9 @@ void testNotifyToolsListChanged() { void testNotifyResourcesListChanged() { var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build(); - assertThatCode(() -> mcpSyncServer.notifyResourcesListChanged()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::notifyResourcesListChanged).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -304,7 +309,7 @@ void testNotifyResourcesUpdated() { .notifyResourcesUpdated(new McpSchema.ResourcesUpdatedNotification(TEST_RESOURCE_URI))) .doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -320,7 +325,7 @@ void testAddResource() { assertThatCode(() -> mcpSyncServer.addResource(specification)).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -333,7 +338,7 @@ void testAddResourceWithNullSpecification() { .isInstanceOf(IllegalArgumentException.class) .hasMessage("Resource must not be null"); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -376,7 +381,7 @@ void testListResources() { assertThat(resources).hasSize(1); assertThat(resources.get(0).uri()).isEqualTo(TEST_RESOURCE_URI); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -393,7 +398,7 @@ void testRemoveResource() { mcpSyncServer.addResource(specification); assertThatCode(() -> mcpSyncServer.removeResource(TEST_RESOURCE_URI)).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -406,7 +411,7 @@ void testRemoveNonexistentResource() { // as per the new implementation that just logs a warning assertThatCode(() -> mcpSyncServer.removeResource("nonexistent://resource")).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } // --------------------------------------- @@ -431,7 +436,7 @@ void testAddResourceTemplate() { assertThatCode(() -> mcpSyncServer.addResourceTemplate(specification)).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -473,7 +478,7 @@ void testRemoveResourceTemplate() { assertThatCode(() -> mcpSyncServer.removeResourceTemplate("test://template/{id}")).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -495,7 +500,7 @@ void testRemoveNonexistentResourceTemplate() { assertThatCode(() -> mcpSyncServer.removeResourceTemplate("nonexistent://template/{id}")) .doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -519,7 +524,7 @@ void testListResourceTemplates() { assertThat(templates).isNotNull(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } // --------------------------------------- @@ -530,9 +535,9 @@ void testListResourceTemplates() { void testNotifyPromptsListChanged() { var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build(); - assertThatCode(() -> mcpSyncServer.notifyPromptsListChanged()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::notifyPromptsListChanged).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -583,7 +588,7 @@ void testRemovePrompt() { assertThatCode(() -> mcpSyncServer.removePrompt(TEST_PROMPT_NAME)).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } @Test @@ -594,7 +599,7 @@ void testRemoveNonexistentPrompt() { assertThatCode(() -> mcpSyncServer.removePrompt("nonexistent://template/{id}")).doesNotThrowAnyException(); - assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException(); } // --------------------------------------- @@ -616,7 +621,7 @@ void testRootsChangeHandlers() { })) .build(); assertThat(singleConsumerServer).isNotNull(); - assertThatCode(() -> singleConsumerServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(singleConsumerServer::closeGracefully).doesNotThrowAnyException(); onClose(); // Test with multiple consumers @@ -632,7 +637,7 @@ void testRootsChangeHandlers() { .build(); assertThat(multipleConsumersServer).isNotNull(); - assertThatCode(() -> multipleConsumersServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(multipleConsumersServer::closeGracefully).doesNotThrowAnyException(); onClose(); // Test error handling @@ -643,14 +648,14 @@ void testRootsChangeHandlers() { .build(); assertThat(errorHandlingServer).isNotNull(); - assertThatCode(() -> errorHandlingServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(errorHandlingServer::closeGracefully).doesNotThrowAnyException(); onClose(); // Test without consumers var noConsumersServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build(); assertThat(noConsumersServer).isNotNull(); - assertThatCode(() -> noConsumersServer.closeGracefully()).doesNotThrowAnyException(); + assertThatCode(noConsumersServer::closeGracefully).doesNotThrowAnyException(); } } From 927696ef56153024b0c7d9ec743f27be72e6e113 Mon Sep 17 00:00:00 2001 From: codeboyzhou Date: Thu, 20 Nov 2025 23:59:37 +0800 Subject: [PATCH 3/3] refactor(#652): remove test usages of deprecated constructor for the Resource class --- .../server/McpServerFeatures.java | 16 +++++++-- .../server/AbstractMcpAsyncServerTests.java | 36 ++++++++++++++----- .../server/AbstractMcpAsyncServerTests.java | 36 ++++++++++++++----- .../server/AbstractMcpSyncServerTests.java | 36 ++++++++++++++----- 4 files changed, 98 insertions(+), 26 deletions(-) diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java index 5a12914a9..fe0608b1c 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java @@ -334,7 +334,13 @@ public static Builder builder() { * *
{@code
 	 * new McpServerFeatures.AsyncResourceSpecification(
-	 * 		new Resource("docs", "Documentation files", "text/markdown"),
+	 *     Resource.builder()
+	 *         .uri("docs")
+	 *         .name("Documentation files")
+	 * 		   .title("Documentation files")
+	 * 		   .mimeType("text/markdown")
+	 * 		   .description("Markdown documentation files")
+	 * 		   .build(),
 	 * 		(exchange, request) -> Mono.fromSupplier(() -> readFile(request.getPath()))
 	 * 				.map(ReadResourceResult::new))
 	 * }
@@ -607,7 +613,13 @@ public static Builder builder() { * *
{@code
 	 * new McpServerFeatures.SyncResourceSpecification(
-	 * 		new Resource("docs", "Documentation files", "text/markdown"),
+	 *     Resource.builder()
+	 *         .uri("docs")
+	 *         .name("Documentation files")
+	 * 		   .title("Documentation files")
+	 * 		   .mimeType("text/markdown")
+	 * 		   .description("Markdown documentation files")
+	 * 		   .build(),
 	 * 		(exchange, request) -> {
 	 * 			String content = readFile(request.getPath());
 	 * 			return new ReadResourceResult(content);
diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java b/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java
index 0bd7640dc..090710248 100644
--- a/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java
+++ b/mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java
@@ -333,8 +333,13 @@ void testAddResource() {
 			.capabilities(ServerCapabilities.builder().resources(true, false).build())
 			.build();
 
-		Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
-				null);
+		Resource resource = Resource.builder()
+			.uri(TEST_RESOURCE_URI)
+			.name("Test Resource")
+			.title("Test Resource")
+			.mimeType("text/plain")
+			.description("Test resource description")
+			.build();
 		McpServerFeatures.AsyncResourceSpecification specification = new McpServerFeatures.AsyncResourceSpecification(
 				resource, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
 
@@ -362,8 +367,13 @@ void testAddResourceWithoutCapability() {
 		// Create a server without resource capabilities
 		McpAsyncServer serverWithoutResources = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build();
 
-		Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
-				null);
+		Resource resource = Resource.builder()
+			.uri(TEST_RESOURCE_URI)
+			.name("Test Resource")
+			.title("Test Resource")
+			.mimeType("text/plain")
+			.description("Test resource description")
+			.build();
 		McpServerFeatures.AsyncResourceSpecification specification = new McpServerFeatures.AsyncResourceSpecification(
 				resource, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
 
@@ -390,8 +400,13 @@ void testListResources() {
 			.capabilities(ServerCapabilities.builder().resources(true, false).build())
 			.build();
 
-		Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
-				null);
+		Resource resource = Resource.builder()
+			.uri(TEST_RESOURCE_URI)
+			.name("Test Resource")
+			.title("Test Resource")
+			.mimeType("text/plain")
+			.description("Test resource description")
+			.build();
 		McpServerFeatures.AsyncResourceSpecification specification = new McpServerFeatures.AsyncResourceSpecification(
 				resource, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
 
@@ -409,8 +424,13 @@ void testRemoveResource() {
 			.capabilities(ServerCapabilities.builder().resources(true, false).build())
 			.build();
 
-		Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
-				null);
+		Resource resource = Resource.builder()
+			.uri(TEST_RESOURCE_URI)
+			.name("Test Resource")
+			.title("Test Resource")
+			.mimeType("text/plain")
+			.description("Test resource description")
+			.build();
 		McpServerFeatures.AsyncResourceSpecification specification = new McpServerFeatures.AsyncResourceSpecification(
 				resource, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
 
diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java
index f36015e18..d6677ec9a 100644
--- a/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java
+++ b/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java
@@ -337,8 +337,13 @@ void testAddResource() {
 			.capabilities(ServerCapabilities.builder().resources(true, false).build())
 			.build();
 
-		Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
-				null);
+		Resource resource = Resource.builder()
+			.uri(TEST_RESOURCE_URI)
+			.name("Test Resource")
+			.title("Test Resource")
+			.mimeType("text/plain")
+			.description("Test resource description")
+			.build();
 		McpServerFeatures.AsyncResourceSpecification specification = new McpServerFeatures.AsyncResourceSpecification(
 				resource, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
 
@@ -366,8 +371,13 @@ void testAddResourceWithoutCapability() {
 		// Create a server without resource capabilities
 		McpAsyncServer serverWithoutResources = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build();
 
-		Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
-				null);
+		Resource resource = Resource.builder()
+			.uri(TEST_RESOURCE_URI)
+			.name("Test Resource")
+			.title("Test Resource")
+			.mimeType("text/plain")
+			.description("Test resource description")
+			.build();
 		McpServerFeatures.AsyncResourceSpecification specification = new McpServerFeatures.AsyncResourceSpecification(
 				resource, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
 
@@ -394,8 +404,13 @@ void testListResources() {
 			.capabilities(ServerCapabilities.builder().resources(true, false).build())
 			.build();
 
-		Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
-				null);
+		Resource resource = Resource.builder()
+			.uri(TEST_RESOURCE_URI)
+			.name("Test Resource")
+			.title("Test Resource")
+			.mimeType("text/plain")
+			.description("Test resource description")
+			.build();
 		McpServerFeatures.AsyncResourceSpecification specification = new McpServerFeatures.AsyncResourceSpecification(
 				resource, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
 
@@ -413,8 +428,13 @@ void testRemoveResource() {
 			.capabilities(ServerCapabilities.builder().resources(true, false).build())
 			.build();
 
-		Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
-				null);
+		Resource resource = Resource.builder()
+			.uri(TEST_RESOURCE_URI)
+			.name("Test Resource")
+			.title("Test Resource")
+			.mimeType("text/plain")
+			.description("Test resource description")
+			.build();
 		McpServerFeatures.AsyncResourceSpecification specification = new McpServerFeatures.AsyncResourceSpecification(
 				resource, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
 
diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java
index 28fd57e77..0a59d0aae 100644
--- a/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java
+++ b/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java
@@ -318,8 +318,13 @@ void testAddResource() {
 			.capabilities(ServerCapabilities.builder().resources(true, false).build())
 			.build();
 
-		Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
-				null);
+		Resource resource = Resource.builder()
+			.uri(TEST_RESOURCE_URI)
+			.name("Test Resource")
+			.title("Test Resource")
+			.mimeType("text/plain")
+			.description("Test resource description")
+			.build();
 		McpServerFeatures.SyncResourceSpecification specification = new McpServerFeatures.SyncResourceSpecification(
 				resource, (exchange, req) -> new ReadResourceResult(List.of()));
 
@@ -345,8 +350,13 @@ void testAddResourceWithNullSpecification() {
 	void testAddResourceWithoutCapability() {
 		var serverWithoutResources = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build();
 
-		Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
-				null);
+		Resource resource = Resource.builder()
+			.uri(TEST_RESOURCE_URI)
+			.name("Test Resource")
+			.title("Test Resource")
+			.mimeType("text/plain")
+			.description("Test resource description")
+			.build();
 		McpServerFeatures.SyncResourceSpecification specification = new McpServerFeatures.SyncResourceSpecification(
 				resource, (exchange, req) -> new ReadResourceResult(List.of()));
 
@@ -370,8 +380,13 @@ void testListResources() {
 			.capabilities(ServerCapabilities.builder().resources(true, false).build())
 			.build();
 
-		Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
-				null);
+		Resource resource = Resource.builder()
+			.uri(TEST_RESOURCE_URI)
+			.name("Test Resource")
+			.title("Test Resource")
+			.mimeType("text/plain")
+			.description("Test resource description")
+			.build();
 		McpServerFeatures.SyncResourceSpecification specification = new McpServerFeatures.SyncResourceSpecification(
 				resource, (exchange, req) -> new ReadResourceResult(List.of()));
 
@@ -390,8 +405,13 @@ void testRemoveResource() {
 			.capabilities(ServerCapabilities.builder().resources(true, false).build())
 			.build();
 
-		Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
-				null);
+		Resource resource = Resource.builder()
+			.uri(TEST_RESOURCE_URI)
+			.name("Test Resource")
+			.title("Test Resource")
+			.mimeType("text/plain")
+			.description("Test resource description")
+			.build();
 		McpServerFeatures.SyncResourceSpecification specification = new McpServerFeatures.SyncResourceSpecification(
 				resource, (exchange, req) -> new ReadResourceResult(List.of()));