refactor: use builder pattern for CallToolResult and Resource (#652)
* use builder pattern for CallToolResult * use builder pattern for Resource * simplify lambda expressions in tests
This commit is contained in:
parent
c561675e74
commit
7e950ebb6c
5 changed files with 405 additions and 202 deletions
|
|
@ -24,7 +24,6 @@ import io.modelcontextprotocol.spec.McpSchema;
|
|||
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 class McpAsyncServer {
|
|||
// 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 class McpAsyncServer {
|
|||
|
||||
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())) {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,10 @@ import reactor.core.publisher.Mono;
|
|||
* 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();
|
||||
* }</pre>
|
||||
*
|
||||
|
|
@ -76,7 +79,10 @@ import reactor.core.publisher.Mono;
|
|||
* .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();
|
||||
* }</pre>
|
||||
*
|
||||
|
|
@ -90,12 +96,18 @@ import reactor.core.publisher.Mono;
|
|||
* 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 interface McpServer {
|
|||
* .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()))
|
||||
* )
|
||||
* }</pre>
|
||||
* @param tool The tool definition including name, description, and schema. Must
|
||||
|
|
@ -1022,7 +1037,10 @@ public interface McpServer {
|
|||
* Example usage: <pre>{@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())
|
||||
* )
|
||||
* }</pre>
|
||||
* @param tool The tool definition including name, description, and schema. Must
|
||||
|
|
|
|||
|
|
@ -334,7 +334,13 @@ public class McpServerFeatures {
|
|||
*
|
||||
* <pre>{@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))
|
||||
* }</pre>
|
||||
|
|
@ -508,7 +514,10 @@ public class McpServerFeatures {
|
|||
* .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();
|
||||
* }</pre>
|
||||
|
|
@ -604,7 +613,13 @@ public class McpServerFeatures {
|
|||
*
|
||||
* <pre>{@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);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import io.modelcontextprotocol.spec.McpSchema;
|
|||
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 class McpStatelessAsyncServer {
|
|||
// 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 class McpStatelessAsyncServer {
|
|||
|
||||
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())) {
|
||||
|
|
|
|||
|
|
@ -1562,6 +1562,7 @@ public final class McpSchema {
|
|||
* content contains error information. If false or absent, indicates successful
|
||||
* execution.
|
||||
*/
|
||||
@Deprecated
|
||||
public CallToolResult(String content, Boolean isError) {
|
||||
this(List.of(new TextContent(content)), isError, null);
|
||||
}
|
||||
|
|
@ -1676,7 +1677,7 @@ public final class McpSchema {
|
|||
* @return a new CallToolResult instance
|
||||
*/
|
||||
public CallToolResult build() {
|
||||
return new CallToolResult(content, isError, (Object) structuredContent, meta);
|
||||
return new CallToolResult(content, isError, structuredContent, meta);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue