Skip to content

Commit 2ab3ee2

Browse files
committed
refactor: fix the return format of CallToolResult
- corrected the return format for structured content results, following the MCP protocol. - fix variable name
1 parent 1bac33d commit 2ab3ee2

File tree

2 files changed

+30
-14
lines changed

2 files changed

+30
-14
lines changed

mcp-annotations/src/main/java/org/springaicommunity/mcp/method/tool/AbstractMcpToolMethodCallback.java

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
import java.util.Objects;
2424
import java.util.stream.Stream;
2525

26-
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
27-
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
26+
import io.modelcontextprotocol.spec.McpSchema.*;
27+
import io.modelcontextprotocol.spec.McpSchema.CallToolResult.*;
2828
import org.springaicommunity.mcp.annotation.McpMeta;
2929
import org.springaicommunity.mcp.annotation.McpProgressToken;
3030
import org.springaicommunity.mcp.annotation.McpTool;
@@ -144,36 +144,52 @@ protected Object buildTypedArgument(Object value, Type type) {
144144
* @return A CallToolResult representing the processed result
145145
*/
146146
protected CallToolResult convertValueToCallToolResult(Object result) {
147+
Builder callToolResultBuilder = CallToolResult.builder();
148+
149+
// According to the MCP protocol For backwards compatibility, a tool that returns structured content SHOULD also return the serialized JSON in a TextContent block.
150+
if (this.returnMode == ReturnMode.STRUCTURED) {
151+
String jsonOutput = JsonParser.toJson(result);
152+
Object structuredOutput = JsonParser.fromJson(jsonOutput, Object.class);
153+
callToolResultBuilder.structuredContent(structuredOutput);
154+
}
155+
147156
// Return the result if it's already a CallToolResult
148157
if (result instanceof CallToolResult) {
149158
return (CallToolResult) result;
159+
} else if (result instanceof TextContent textContent) {
160+
// Structured content is only supported in TextContent
161+
return callToolResultBuilder
162+
.addContent(textContent)
163+
.isError(false)
164+
.meta(null)
165+
.build();
166+
} else if (result instanceof Content content) {
167+
return CallToolResult.builder()
168+
.addContent(content)
169+
.isError(false)
170+
.meta(null)
171+
.build();
150172
}
151173

152174
Type returnType = this.toolMethod.getGenericReturnType();
153175

154176
if (returnMode == ReturnMode.VOID || returnType == Void.TYPE || returnType == void.class) {
155-
return CallToolResult.builder().addTextContent(JsonParser.toJson("Done")).build();
156-
}
157-
158-
if (this.returnMode == ReturnMode.STRUCTURED) {
159-
String jsonOutput = JsonParser.toJson(result);
160-
Object structuredOutput = JsonParser.fromJson(jsonOutput, Object.class);
161-
return CallToolResult.builder().structuredContent(structuredOutput).build();
177+
return callToolResultBuilder.addTextContent(JsonParser.toJson("Done")).build();
162178
}
163179

164180
// Default to text output
165181
if (result == null) {
166-
return CallToolResult.builder().addTextContent("null").build();
182+
return callToolResultBuilder.addTextContent("null").build();
167183
}
168184

169185
// For string results in TEXT mode, return the string directly without JSON
170186
// serialization
171187
if (result instanceof String) {
172-
return CallToolResult.builder().addTextContent((String) result).build();
188+
return callToolResultBuilder.addTextContent((String) result).build();
173189
}
174190

175191
// For other types, serialize to JSON
176-
return CallToolResult.builder().addTextContent(JsonParser.toJson(result)).build();
192+
return callToolResultBuilder.addTextContent(JsonParser.toJson(result)).build();
177193
}
178194

179195
/**

mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/SyncMcpToolProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,9 @@ public List<SyncToolSpecification> getToolSpecifications() {
120120

121121
var tool = toolBuilder.build();
122122

123-
boolean useStructuredOtput = tool.outputSchema() != null;
123+
boolean useStructuredOutput = tool.outputSchema() != null;
124124

125-
ReturnMode returnMode = useStructuredOtput ? ReturnMode.STRUCTURED
125+
ReturnMode returnMode = useStructuredOutput ? ReturnMode.STRUCTURED
126126
: (methodReturnType == Void.TYPE || methodReturnType == void.class ? ReturnMode.VOID
127127
: ReturnMode.TEXT);
128128

0 commit comments

Comments
 (0)