Skip to content

Commit dd63728

Browse files
committed
- 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.
1 parent c1976f9 commit dd63728

14 files changed

+222
-121
lines changed

mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
2525
import io.modelcontextprotocol.spec.McpSchema.CompleteResult.CompleteCompletion;
2626
import io.modelcontextprotocol.spec.McpSchema.ErrorCodes;
27-
import io.modelcontextprotocol.spec.McpSchema.JSONRPCResponse;
2827
import io.modelcontextprotocol.spec.McpSchema.LoggingLevel;
2928
import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification;
3029
import io.modelcontextprotocol.spec.McpSchema.PromptReference;
@@ -398,19 +397,23 @@ public Mono<CallToolResult> apply(McpAsyncServerExchange exchange, McpSchema.Cal
398397
// results that conform to this schema.
399398
// https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema
400399
if (result.structuredContent() == null) {
401-
logger.warn(
402-
"Response missing structured content which is expected when calling tool with non-empty outputSchema");
403-
return new CallToolResult(
404-
"Response missing structured content which is expected when calling tool with non-empty outputSchema",
405-
true);
400+
String content = "Response missing structured content which is expected when calling tool with non-empty outputSchema";
401+
logger.warn(content);
402+
return CallToolResult.builder()
403+
.content(List.of(new McpSchema.TextContent(content)))
404+
.isError(true)
405+
.build();
406406
}
407407

408408
// Validate the result against the output schema
409409
var validation = this.jsonSchemaValidator.validate(outputSchema, result.structuredContent());
410410

411411
if (!validation.valid()) {
412412
logger.warn("Tool call result validation failed: {}", validation.errorMessage());
413-
return new CallToolResult(validation.errorMessage(), true);
413+
return CallToolResult.builder()
414+
.content(List.of(new McpSchema.TextContent(validation.errorMessage())))
415+
.isError(true)
416+
.build();
414417
}
415418

416419
if (Utils.isEmpty(result.content())) {

mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@
6767
* McpServer.sync(transportProvider)
6868
* .serverInfo("my-server", "1.0.0")
6969
* .tool(Tool.builder().name("calculator").title("Performs calculations").inputSchema(schema).build(),
70-
* (exchange, args) -> new CallToolResult("Result: " + calculate(args)))
70+
* (exchange, args) -> CallToolResult.builder()
71+
* .content(List.of(new McpSchema.TextContent("Result: " + calculate(args))))
72+
* .isError(false)
73+
* .build())
7174
* .build();
7275
* }</pre>
7376
*
@@ -76,7 +79,10 @@
7679
* .serverInfo("my-server", "1.0.0")
7780
* .tool(Tool.builder().name("calculator").title("Performs calculations").inputSchema(schema).build(),
7881
* (exchange, args) -> Mono.fromSupplier(() -> calculate(args))
79-
* .map(result -> new CallToolResult("Result: " + result)))
82+
* .map(result -> CallToolResult.builder()
83+
* .content(List.of(new McpSchema.TextContent("Result: " + result)))
84+
* .isError(false)
85+
* .build()))
8086
* .build();
8187
* }</pre>
8288
*
@@ -90,12 +96,18 @@
9096
* McpServerFeatures.AsyncToolSpecification.builder()
9197
* .tool(calculatorTool)
9298
* .callTool((exchange, args) -> Mono.fromSupplier(() -> calculate(args.arguments()))
93-
* .map(result -> new CallToolResult("Result: " + result))))
99+
* .map(result -> CallToolResult.builder()
100+
* .content(List.of(new McpSchema.TextContent("Result: " + result)))
101+
* .isError(false)
102+
* .build()))
94103
*. .build(),
95104
* McpServerFeatures.AsyncToolSpecification.builder()
96105
* .tool((weatherTool)
97106
* .callTool((exchange, args) -> Mono.fromSupplier(() -> getWeather(args.arguments()))
98-
* .map(result -> new CallToolResult("Weather: " + result))))
107+
* .map(result -> CallToolResult.builder()
108+
* .content(List.of(new McpSchema.TextContent("Weather: " + result)))
109+
* .isError(false)
110+
* .build()))
99111
* .build()
100112
* )
101113
* // Register resources
@@ -425,7 +437,10 @@ public AsyncSpecification<S> capabilities(McpSchema.ServerCapabilities serverCap
425437
* .tool(
426438
* Tool.builder().name("calculator").title("Performs calculations").inputSchema(schema).build(),
427439
* (exchange, args) -> Mono.fromSupplier(() -> calculate(args))
428-
* .map(result -> new CallToolResult("Result: " + result))
440+
* .map(result -> CallToolResult.builder()
441+
* .content(List.of(new McpSchema.TextContent("Result: " + result)))
442+
* .isError(false)
443+
* .build()))
429444
* )
430445
* }</pre>
431446
* @param tool The tool definition including name, description, and schema. Must
@@ -1023,7 +1038,10 @@ public SyncSpecification<S> capabilities(McpSchema.ServerCapabilities serverCapa
10231038
* Example usage: <pre>{@code
10241039
* .tool(
10251040
* Tool.builder().name("calculator").title("Performs calculations".inputSchema(schema).build(),
1026-
* (exchange, args) -> new CallToolResult("Result: " + calculate(args))
1041+
* (exchange, args) -> CallToolResult.builder()
1042+
* .content(List.of(new McpSchema.TextContent("Result: " + calculate(args))))
1043+
* .isError(false)
1044+
* .build())
10271045
* )
10281046
* }</pre>
10291047
* @param tool The tool definition including name, description, and schema. Must

mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,10 @@ static AsyncCompletionSpecification fromSync(SyncCompletionSpecification complet
508508
* .build()
509509
* .toolHandler((exchange, req) -> {
510510
* String expr = (String) req.arguments().get("expression");
511-
* return new CallToolResult("Result: " + evaluate(expr));
511+
* return CallToolResult.builder()
512+
* .content(List.of(new McpSchema.TextContent("Result: " + evaluate(expr))))
513+
* .isError(false)
514+
* .build();
512515
* }))
513516
* .build();
514517
* }</pre>

mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
1515
import io.modelcontextprotocol.spec.McpSchema.CompleteResult.CompleteCompletion;
1616
import io.modelcontextprotocol.spec.McpSchema.ErrorCodes;
17-
import io.modelcontextprotocol.spec.McpSchema.JSONRPCResponse;
1817
import io.modelcontextprotocol.spec.McpSchema.PromptReference;
1918
import io.modelcontextprotocol.spec.McpSchema.ResourceReference;
2019
import io.modelcontextprotocol.spec.McpSchema.Tool;
@@ -277,19 +276,23 @@ public Mono<CallToolResult> apply(McpTransportContext transportContext, McpSchem
277276
// results that conform to this schema.
278277
// https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema
279278
if (result.structuredContent() == null) {
280-
logger.warn(
281-
"Response missing structured content which is expected when calling tool with non-empty outputSchema");
282-
return new CallToolResult(
283-
"Response missing structured content which is expected when calling tool with non-empty outputSchema",
284-
true);
279+
String content = "Response missing structured content which is expected when calling tool with non-empty outputSchema";
280+
logger.warn(content);
281+
return CallToolResult.builder()
282+
.content(List.of(new McpSchema.TextContent(content)))
283+
.isError(true)
284+
.build();
285285
}
286286

287287
// Validate the result against the output schema
288288
var validation = this.jsonSchemaValidator.validate(outputSchema, result.structuredContent());
289289

290290
if (!validation.valid()) {
291291
logger.warn("Tool call result validation failed: {}", validation.errorMessage());
292-
return new CallToolResult(validation.errorMessage(), true);
292+
return CallToolResult.builder()
293+
.content(List.of(new McpSchema.TextContent(validation.errorMessage())))
294+
.isError(true)
295+
.build();
293296
}
294297

295298
if (Utils.isEmpty(result.content())) {

mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1562,6 +1562,7 @@ public CallToolResult(List<Content> content, Boolean isError, Map<String, Object
15621562
* content contains error information. If false or absent, indicates successful
15631563
* execution.
15641564
*/
1565+
@Deprecated
15651566
public CallToolResult(String content, Boolean isError) {
15661567
this(List.of(new TextContent(content)), isError, null);
15671568
}
@@ -1676,7 +1677,7 @@ public Builder meta(Map<String, Object> meta) {
16761677
* @return a new CallToolResult instance
16771678
*/
16781679
public CallToolResult build() {
1679-
return new CallToolResult(content, isError, (Object) structuredContent, meta);
1680+
return new CallToolResult(content, isError, structuredContent, meta);
16801681
}
16811682

16821683
}

mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import java.time.Duration;
88
import java.util.List;
99

10-
import io.modelcontextprotocol.spec.McpError;
1110
import io.modelcontextprotocol.spec.McpSchema;
1211
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
1312
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
@@ -86,7 +85,7 @@ void testGracefulShutdown() {
8685
void testImmediateClose() {
8786
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build();
8887

89-
assertThatCode(() -> mcpAsyncServer.close()).doesNotThrowAnyException();
88+
assertThatCode(mcpAsyncServer::close).doesNotThrowAnyException();
9089
}
9190

9291
// ---------------------------------------
@@ -104,8 +103,9 @@ void testAddTool() {
104103
.capabilities(ServerCapabilities.builder().tools(true).build())
105104
.build();
106105

107-
StepVerifier.create(mcpAsyncServer.addTool(new McpServerFeatures.AsyncToolSpecification(newTool,
108-
(exchange, args) -> Mono.just(new CallToolResult(List.of(), false)))))
106+
StepVerifier
107+
.create(mcpAsyncServer.addTool(new McpServerFeatures.AsyncToolSpecification(newTool,
108+
(exchange, args) -> Mono.just(CallToolResult.builder().content(List.of()).isError(false).build()))))
109109
.verifyComplete();
110110

111111
assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException();
@@ -125,7 +125,8 @@ void testAddToolCall() {
125125

126126
StepVerifier.create(mcpAsyncServer.addTool(McpServerFeatures.AsyncToolSpecification.builder()
127127
.tool(newTool)
128-
.callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false)))
128+
.callHandler((exchange, request) -> Mono
129+
.just(CallToolResult.builder().content(List.of()).isError(false).build()))
129130
.build())).verifyComplete();
130131

131132
assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException();
@@ -142,11 +143,13 @@ void testAddDuplicateTool() {
142143

143144
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
144145
.capabilities(ServerCapabilities.builder().tools(true).build())
145-
.tool(duplicateTool, (exchange, args) -> Mono.just(new CallToolResult(List.of(), false)))
146+
.tool(duplicateTool,
147+
(exchange, args) -> Mono.just(CallToolResult.builder().content(List.of()).isError(false).build()))
146148
.build();
147149

148-
StepVerifier.create(mcpAsyncServer.addTool(new McpServerFeatures.AsyncToolSpecification(duplicateTool,
149-
(exchange, args) -> Mono.just(new CallToolResult(List.of(), false)))))
150+
StepVerifier
151+
.create(mcpAsyncServer.addTool(new McpServerFeatures.AsyncToolSpecification(duplicateTool,
152+
(exchange, args) -> Mono.just(CallToolResult.builder().content(List.of()).isError(false).build()))))
150153
.verifyComplete();
151154

152155
assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException();
@@ -162,12 +165,15 @@ void testAddDuplicateToolCall() {
162165

163166
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
164167
.capabilities(ServerCapabilities.builder().tools(true).build())
165-
.toolCall(duplicateTool, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false)))
168+
.toolCall(duplicateTool,
169+
(exchange, request) -> Mono
170+
.just(CallToolResult.builder().content(List.of()).isError(false).build()))
166171
.build();
167172

168173
StepVerifier.create(mcpAsyncServer.addTool(McpServerFeatures.AsyncToolSpecification.builder()
169174
.tool(duplicateTool)
170-
.callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false)))
175+
.callHandler((exchange, request) -> Mono
176+
.just(CallToolResult.builder().content(List.of()).isError(false).build()))
171177
.build())).verifyComplete();
172178

173179
assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException();
@@ -183,8 +189,12 @@ void testDuplicateToolCallDuringBuilding() {
183189

184190
assertThatThrownBy(() -> prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
185191
.capabilities(ServerCapabilities.builder().tools(true).build())
186-
.toolCall(duplicateTool, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false)))
187-
.toolCall(duplicateTool, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) // Duplicate!
192+
.toolCall(duplicateTool,
193+
(exchange, request) -> Mono
194+
.just(CallToolResult.builder().content(List.of()).isError(false).build()))
195+
.toolCall(duplicateTool,
196+
(exchange, request) -> Mono
197+
.just(CallToolResult.builder().content(List.of()).isError(false).build())) // Duplicate!
188198
.build()).isInstanceOf(IllegalArgumentException.class)
189199
.hasMessage("Tool with name 'duplicate-build-toolcall' is already registered.");
190200
}
@@ -200,11 +210,13 @@ void testDuplicateToolsInBatchListRegistration() {
200210
List<McpServerFeatures.AsyncToolSpecification> specs = List.of(
201211
McpServerFeatures.AsyncToolSpecification.builder()
202212
.tool(duplicateTool)
203-
.callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false)))
213+
.callHandler((exchange, request) -> Mono
214+
.just(CallToolResult.builder().content(List.of()).isError(false).build()))
204215
.build(),
205216
McpServerFeatures.AsyncToolSpecification.builder()
206217
.tool(duplicateTool)
207-
.callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false)))
218+
.callHandler((exchange, request) -> Mono
219+
.just(CallToolResult.builder().content(List.of()).isError(false).build()))
208220
.build() // Duplicate!
209221
);
210222

@@ -227,11 +239,13 @@ void testDuplicateToolsInBatchVarargsRegistration() {
227239
.capabilities(ServerCapabilities.builder().tools(true).build())
228240
.tools(McpServerFeatures.AsyncToolSpecification.builder()
229241
.tool(duplicateTool)
230-
.callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false)))
242+
.callHandler((exchange, request) -> Mono
243+
.just(CallToolResult.builder().content(List.of()).isError(false).build()))
231244
.build(),
232245
McpServerFeatures.AsyncToolSpecification.builder()
233246
.tool(duplicateTool)
234-
.callHandler((exchange, request) -> Mono.just(new CallToolResult(List.of(), false)))
247+
.callHandler((exchange, request) -> Mono
248+
.just(CallToolResult.builder().content(List.of()).isError(false).build()))
235249
.build() // Duplicate!
236250
)
237251
.build()).isInstanceOf(IllegalArgumentException.class)
@@ -248,7 +262,9 @@ void testRemoveTool() {
248262

249263
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
250264
.capabilities(ServerCapabilities.builder().tools(true).build())
251-
.toolCall(too, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false)))
265+
.toolCall(too,
266+
(exchange, request) -> Mono
267+
.just(CallToolResult.builder().content(List.of()).isError(false).build()))
252268
.build();
253269

254270
StepVerifier.create(mcpAsyncServer.removeTool(TEST_TOOL_NAME)).verifyComplete();
@@ -277,7 +293,8 @@ void testNotifyToolsListChanged() {
277293

278294
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
279295
.capabilities(ServerCapabilities.builder().tools(true).build())
280-
.toolCall(too, (exchange, args) -> Mono.just(new CallToolResult(List.of(), false)))
296+
.toolCall(too,
297+
(exchange, args) -> Mono.just(CallToolResult.builder().content(List.of()).isError(false).build()))
281298
.build();
282299

283300
StepVerifier.create(mcpAsyncServer.notifyToolsListChanged()).verifyComplete();

mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpClientServerIntegrationTests.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,7 +1077,10 @@ void testLoggingNotification(String clientType) throws InterruptedException {
10771077
.logger("test-logger")
10781078
.data("Another error message")
10791079
.build()))
1080-
.thenReturn(new CallToolResult("Logging test completed", false));
1080+
.thenReturn(CallToolResult.builder()
1081+
.content(List.of(new McpSchema.TextContent("Logging test completed")))
1082+
.isError(false)
1083+
.build());
10811084
//@formatter:on
10821085
})
10831086
.build();
@@ -1172,7 +1175,10 @@ void testProgressNotification(String clientType) throws InterruptedException {
11721175
0.0, 1.0, "Another processing started")))
11731176
.then(exchange.progressNotification(
11741177
new McpSchema.ProgressNotification(progressToken, 1.0, 1.0, "Processing completed")))
1175-
.thenReturn(new CallToolResult(("Progress test completed"), false));
1178+
.thenReturn(CallToolResult.builder()
1179+
.content(List.of(new McpSchema.TextContent("Progress test completed")))
1180+
.isError(false)
1181+
.build());
11761182
})
11771183
.build();
11781184

@@ -1326,7 +1332,10 @@ void testPingSuccess(String clientType) {
13261332
assertThat(result).isNotNull();
13271333
}).then(Mono.fromCallable(() -> {
13281334
executionOrder.set(executionOrder.get() + "3");
1329-
return new CallToolResult("Async ping test completed", false);
1335+
return CallToolResult.builder()
1336+
.content(List.of(new McpSchema.TextContent("Async ping test completed")))
1337+
.isError(false)
1338+
.build();
13301339
}));
13311340
})
13321341
.build();

mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import java.util.List;
88

9-
import io.modelcontextprotocol.spec.McpError;
109
import io.modelcontextprotocol.spec.McpSchema;
1110
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
1211
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
@@ -114,7 +113,7 @@ void testAddTool() {
114113
.inputSchema(EMPTY_JSON_SCHEMA)
115114
.build();
116115
assertThatCode(() -> mcpSyncServer.addTool(new McpServerFeatures.SyncToolSpecification(newTool,
117-
(exchange, args) -> new CallToolResult(List.of(), false))))
116+
(exchange, args) -> CallToolResult.builder().content(List.of()).isError(false).build())))
118117
.doesNotThrowAnyException();
119118

120119
assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException();
@@ -151,11 +150,11 @@ void testAddDuplicateTool() {
151150

152151
var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
153152
.capabilities(ServerCapabilities.builder().tools(true).build())
154-
.tool(duplicateTool, (exchange, args) -> new CallToolResult(List.of(), false))
153+
.tool(duplicateTool, (exchange, args) -> CallToolResult.builder().content(List.of()).isError(false).build())
155154
.build();
156155

157156
assertThatCode(() -> mcpSyncServer.addTool(new McpServerFeatures.SyncToolSpecification(duplicateTool,
158-
(exchange, args) -> new CallToolResult(List.of(), false))))
157+
(exchange, args) -> CallToolResult.builder().content(List.of()).isError(false).build())))
159158
.doesNotThrowAnyException();
160159

161160
assertThatCode(mcpSyncServer::closeGracefully).doesNotThrowAnyException();

0 commit comments

Comments
 (0)