diff --git a/mcp/common/src/main/java/org/springframework/ai/mcp/McpToolUtils.java b/mcp/common/src/main/java/org/springframework/ai/mcp/McpToolUtils.java index 01c78d35516..263d0446c07 100644 --- a/mcp/common/src/main/java/org/springframework/ai/mcp/McpToolUtils.java +++ b/mcp/common/src/main/java/org/springframework/ai/mcp/McpToolUtils.java @@ -24,9 +24,11 @@ import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.ObjectMapper; import io.micrometer.common.util.StringUtils; import io.modelcontextprotocol.client.McpAsyncClient; import io.modelcontextprotocol.client.McpSyncClient; +import io.modelcontextprotocol.json.jackson.JacksonMcpJsonMapper; import io.modelcontextprotocol.server.McpServerFeatures; import io.modelcontextprotocol.server.McpStatelessServerFeatures; import io.modelcontextprotocol.server.McpSyncServerExchange; @@ -74,6 +76,8 @@ public final class McpToolUtils { */ public static final String TOOL_CONTEXT_MCP_EXCHANGE_KEY = "exchange"; + private static final JacksonMcpJsonMapper objectMapper = new JacksonMcpJsonMapper(new ObjectMapper()); + private McpToolUtils() { } @@ -253,6 +257,7 @@ private static SharedSyncToolSpecification toSharedSyncToolSpecification(ToolCal .description(toolCallback.getToolDefinition().description()) .inputSchema(ModelOptionsUtils.jsonToObject(toolCallback.getToolDefinition().inputSchema(), McpSchema.JsonSchema.class)) + .outputSchema(objectMapper, toolCallback.getToolDefinition().outputSchema()) .build(); return new SharedSyncToolSpecification(tool, (exchangeOrContext, request) -> { diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/definition/DefaultToolDefinition.java b/spring-ai-model/src/main/java/org/springframework/ai/tool/definition/DefaultToolDefinition.java index cafd1a70364..cbdbc81b2ef 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/tool/definition/DefaultToolDefinition.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/tool/definition/DefaultToolDefinition.java @@ -26,7 +26,8 @@ * @author Thomas Vitale * @since 1.0.0 */ -public record DefaultToolDefinition(String name, String description, String inputSchema) implements ToolDefinition { +public record DefaultToolDefinition(String name, String description, String inputSchema, + String outputSchema) implements ToolDefinition { public DefaultToolDefinition { Assert.hasText(name, "name cannot be null or empty"); @@ -46,6 +47,8 @@ public static final class Builder { private String inputSchema; + private String outputSchema; + private Builder() { } @@ -64,12 +67,17 @@ public Builder inputSchema(String inputSchema) { return this; } + public Builder outputSchema(String outputSchema) { + this.outputSchema = outputSchema; + return this; + } + public ToolDefinition build() { if (!StringUtils.hasText(this.description)) { Assert.hasText(this.name, "toolName cannot be null or empty"); this.description = ParsingUtils.reConcatenateCamelCase(this.name, " "); } - return new DefaultToolDefinition(this.name, this.description, this.inputSchema); + return new DefaultToolDefinition(this.name, this.description, this.inputSchema, this.outputSchema); } } diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/definition/ToolDefinition.java b/spring-ai-model/src/main/java/org/springframework/ai/tool/definition/ToolDefinition.java index 517a0061712..01f3f3072f6 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/tool/definition/ToolDefinition.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/tool/definition/ToolDefinition.java @@ -39,6 +39,8 @@ public interface ToolDefinition { */ String inputSchema(); + String outputSchema(); + /** * Create a default {@link ToolDefinition} builder. */ diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/function/FunctionToolCallback.java b/spring-ai-model/src/main/java/org/springframework/ai/tool/function/FunctionToolCallback.java index 4892a803bfe..ca4e52c40fa 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/tool/function/FunctionToolCallback.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/tool/function/FunctionToolCallback.java @@ -172,6 +172,10 @@ public static final class Builder { private Type inputType; + private String outputSchema; + + private Type outputType; + private ToolMetadata toolMetadata; private BiFunction toolFunction; @@ -206,6 +210,16 @@ public Builder inputType(ParameterizedTypeReference inputType) { return this; } + public Builder outputSchema(String outputSchema) { + this.outputSchema = outputSchema; + return this; + } + + public Builder outputType(Type outputType) { + this.outputType = outputType; + return this; + } + public Builder toolMetadata(ToolMetadata toolMetadata) { this.toolMetadata = toolMetadata; return this; @@ -224,6 +238,8 @@ public FunctionToolCallback build() { : ToolUtils.getToolDescriptionFromName(this.name)) .inputSchema(StringUtils.hasText(this.inputSchema) ? this.inputSchema : JsonSchemaGenerator.generateForType(this.inputType)) + .outputSchema(StringUtils.hasText(this.outputSchema) ? this.outputSchema + : JsonSchemaGenerator.generateForType(this.outputType)) .build(); return new FunctionToolCallback<>(toolDefinition, this.toolMetadata, this.inputType, this.toolFunction, this.toolCallResultConverter); diff --git a/spring-ai-model/src/test/java/org/springframework/ai/tool/execution/DefaultToolExecutionExceptionProcessorTests.java b/spring-ai-model/src/test/java/org/springframework/ai/tool/execution/DefaultToolExecutionExceptionProcessorTests.java index cf0aca7022f..2e26f640232 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/tool/execution/DefaultToolExecutionExceptionProcessorTests.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/tool/execution/DefaultToolExecutionExceptionProcessorTests.java @@ -40,7 +40,7 @@ class DefaultToolExecutionExceptionProcessorTests { private final Error toolError = new Error("Error"); private final DefaultToolDefinition toolDefinition = new DefaultToolDefinition("toolName", "toolDescription", - "inputSchema"); + "inputSchema", "outputSchema"); private final ToolExecutionException toolExecutionException = new ToolExecutionException(this.toolDefinition, this.toolException);