|
23 | 23 | import java.util.Objects; |
24 | 24 | import java.util.stream.Stream; |
25 | 25 |
|
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.*; |
28 | 28 | import org.springaicommunity.mcp.annotation.McpMeta; |
29 | 29 | import org.springaicommunity.mcp.annotation.McpProgressToken; |
30 | 30 | import org.springaicommunity.mcp.annotation.McpTool; |
@@ -144,36 +144,52 @@ protected Object buildTypedArgument(Object value, Type type) { |
144 | 144 | * @return A CallToolResult representing the processed result |
145 | 145 | */ |
146 | 146 | 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 | + |
147 | 156 | // Return the result if it's already a CallToolResult |
148 | 157 | if (result instanceof CallToolResult) { |
149 | 158 | 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(); |
150 | 172 | } |
151 | 173 |
|
152 | 174 | Type returnType = this.toolMethod.getGenericReturnType(); |
153 | 175 |
|
154 | 176 | 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(); |
162 | 178 | } |
163 | 179 |
|
164 | 180 | // Default to text output |
165 | 181 | if (result == null) { |
166 | | - return CallToolResult.builder().addTextContent("null").build(); |
| 182 | + return callToolResultBuilder.addTextContent("null").build(); |
167 | 183 | } |
168 | 184 |
|
169 | 185 | // For string results in TEXT mode, return the string directly without JSON |
170 | 186 | // serialization |
171 | 187 | if (result instanceof String) { |
172 | | - return CallToolResult.builder().addTextContent((String) result).build(); |
| 188 | + return callToolResultBuilder.addTextContent((String) result).build(); |
173 | 189 | } |
174 | 190 |
|
175 | 191 | // For other types, serialize to JSON |
176 | | - return CallToolResult.builder().addTextContent(JsonParser.toJson(result)).build(); |
| 192 | + return callToolResultBuilder.addTextContent(JsonParser.toJson(result)).build(); |
177 | 193 | } |
178 | 194 |
|
179 | 195 | /** |
|
0 commit comments