|
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,48 @@ 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 |
| 150 | + // structured content SHOULD also return the serialized JSON in a TextContent |
| 151 | + // block. |
| 152 | + if (this.returnMode == ReturnMode.STRUCTURED) { |
| 153 | + String jsonOutput = JsonParser.toJson(result); |
| 154 | + Object structuredOutput = JsonParser.fromJson(jsonOutput, Object.class); |
| 155 | + callToolResultBuilder.structuredContent(structuredOutput); |
| 156 | + } |
| 157 | + |
147 | 158 | // Return the result if it's already a CallToolResult |
148 | 159 | if (result instanceof CallToolResult) { |
149 | 160 | return (CallToolResult) result; |
150 | 161 | } |
| 162 | + else if (result instanceof TextContent textContent) { |
| 163 | + // Structured content is only supported in TextContent |
| 164 | + return callToolResultBuilder.addContent(textContent).isError(false).meta(null).build(); |
| 165 | + } |
| 166 | + else if (result instanceof Content content) { |
| 167 | + return CallToolResult.builder().addContent(content).isError(false).meta(null).build(); |
| 168 | + } |
151 | 169 |
|
152 | 170 | Type returnType = this.toolMethod.getGenericReturnType(); |
153 | 171 |
|
154 | 172 | 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(); |
| 173 | + return callToolResultBuilder.addTextContent(JsonParser.toJson("Done")).build(); |
162 | 174 | } |
163 | 175 |
|
164 | 176 | // Default to text output |
165 | 177 | if (result == null) { |
166 | | - return CallToolResult.builder().addTextContent("null").build(); |
| 178 | + return callToolResultBuilder.addTextContent("null").build(); |
167 | 179 | } |
168 | 180 |
|
169 | 181 | // For string results in TEXT mode, return the string directly without JSON |
170 | 182 | // serialization |
171 | 183 | if (result instanceof String) { |
172 | | - return CallToolResult.builder().addTextContent((String) result).build(); |
| 184 | + return callToolResultBuilder.addTextContent((String) result).build(); |
173 | 185 | } |
174 | 186 |
|
175 | 187 | // For other types, serialize to JSON |
176 | | - return CallToolResult.builder().addTextContent(JsonParser.toJson(result)).build(); |
| 188 | + return callToolResultBuilder.addTextContent(JsonParser.toJson(result)).build(); |
177 | 189 | } |
178 | 190 |
|
179 | 191 | /** |
|
0 commit comments