diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/tool/AbstractSyncMcpToolMethodCallback.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/tool/AbstractSyncMcpToolMethodCallback.java index a6f9c07..235b0b3 100644 --- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/tool/AbstractSyncMcpToolMethodCallback.java +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/tool/AbstractSyncMcpToolMethodCallback.java @@ -19,7 +19,9 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.springaicommunity.mcp.annotation.McpMeta; @@ -41,6 +43,7 @@ * @param The type of the context parameter (e.g., McpTransportContext or * McpSyncServerExchange) * @author Christian Tzolov + * @author Ilayaperumal Gopinathan */ public abstract class AbstractSyncMcpToolMethodCallback { @@ -156,9 +159,15 @@ protected CallToolResult processResult(Object result) { return CallToolResult.builder().addTextContent(JsonParser.toJson("Done")).build(); } else if (this.returnMode == ReturnMode.STRUCTURED) { - String jsonOutput = JsonParser.toJson(result); - Map structuredOutput = JsonParser.fromJson(jsonOutput, MAP_TYPE_REFERENCE); - return CallToolResult.builder().structuredContent(structuredOutput).build(); + if (result instanceof List) { + List texts = ((List) result).stream().map(String::valueOf).collect(Collectors.toList()); + return CallToolResult.builder().textContent(texts).build(); + } + else { + String jsonOutput = JsonParser.toJson(result); + Map structuredOutput = JsonParser.fromJson(jsonOutput, MAP_TYPE_REFERENCE); + return CallToolResult.builder().structuredContent(structuredOutput).build(); + } } // Default to text output diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/SyncMcpToolMethodCallbackTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/SyncMcpToolMethodCallbackTests.java index 9c00379..27718d7 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/SyncMcpToolMethodCallbackTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/SyncMcpToolMethodCallbackTests.java @@ -106,6 +106,11 @@ public TestObject returnObjectTool(String name, int value) { return new TestObject(name, value); } + @McpTool(name = "return-list-tool", description = "Tool that returns a list") + public List returnListTool() { + return List.of("this", "is", "a", "test"); + } + } public static class TestObject { @@ -507,4 +512,23 @@ public void testToolReturningComplexObject() throws Exception { assertThat(result.structuredContent()).containsEntry("value", 42); } + @Test + public void testToolReturningList() throws Exception { + TestToolProvider provider = new TestToolProvider(); + Method method = TestToolProvider.class.getMethod("returnListTool", null); + SyncMcpToolMethodCallback callback = new SyncMcpToolMethodCallback(ReturnMode.STRUCTURED, method, provider); + + McpSyncServerExchange exchange = mock(McpSyncServerExchange.class); + CallToolRequest request = new CallToolRequest("return-list-tool", Map.of()); + + CallToolResult result = callback.apply(exchange, request); + + assertThat(result).isNotNull(); + assertThat(result.isError()).isFalse(); + assertThat(result.content()).isNotEmpty(); + result.content().forEach(textContent -> { + assertThat(((TextContent) textContent).text()).containsAnyOf("this", "is", "a", "test"); + }); + } + }