Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -398,19 +397,23 @@ public Mono<CallToolResult> 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
var validation = this.jsonSchemaValidator.validate(outputSchema, result.structuredContent());

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())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
* }</pre>
*
Expand All @@ -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();
* }</pre>
*
Expand All @@ -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
Expand Down Expand Up @@ -425,7 +437,10 @@ public AsyncSpecification<S> 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()))
* )
* }</pre>
* @param tool The tool definition including name, description, and schema. Must
Expand Down Expand Up @@ -1023,7 +1038,10 @@ public SyncSpecification<S> capabilities(McpSchema.ServerCapabilities serverCapa
* 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,13 @@ public static Builder builder() {
*
* <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>
Expand Down Expand Up @@ -508,7 +514,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();
* }</pre>
Expand Down Expand Up @@ -604,7 +613,13 @@ public static Builder builder() {
*
* <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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -277,19 +276,23 @@ public Mono<CallToolResult> 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
var validation = this.jsonSchemaValidator.validate(outputSchema, result.structuredContent());

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())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1562,6 +1562,7 @@ public CallToolResult(List<Content> content, Boolean isError, Map<String, Object
* 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);
}
Expand Down Expand Up @@ -1676,7 +1677,7 @@ public Builder meta(Map<String, Object> meta) {
* @return a new CallToolResult instance
*/
public CallToolResult build() {
return new CallToolResult(content, isError, (Object) structuredContent, meta);
return new CallToolResult(content, isError, structuredContent, meta);
}

}
Expand Down
Loading