jamesnetherton commented on code in PR #9341:
URL: https://github.com/apache/camel-quarkus/pull/9341#discussion_r4294788906


##########
extensions/mcp-server/runtime/src/main/doc/usage.adoc:
##########
@@ -33,6 +33,18 @@ the JSON type (`string`, `integer`, `number`, `boolean`), 
and the
 `parameter.NAME.enum` options refine the generated input schema. Arguments
 arrive as message headers in the route (`${header.customerId}`).
 
+=== Structured tool output
+
+Tools declaring their output with the `outputSchema` or `outputParameter.NAME`
+options publish it as the MCP `outputSchema`. The route body must then be

Review Comment:
   Nit: "The route body must then be JSON" could be "must then be a JSON 
object", since MCP expects `structuredContent` to be an object.



##########
extensions/mcp-server/runtime/src/main/java/org/apache/camel/quarkus/component/mcp/server/QuarkusMcpServerEngine.java:
##########
@@ -80,12 +81,17 @@ public void toolAdded(McpServerTool tool) {
         if (tool.inputSchemaJson() != null) {
             definition.setInputSchema(new JsonObject(tool.inputSchemaJson()));
         }
+        if (tool.outputSchemaJson() != null) {
+            definition.setOutputSchema(new 
JsonObject(tool.outputSchemaJson()));
+        }
         applyAnnotations(definition, tool.annotations());
         definition.setHandler(arguments -> {
             Map<String, Object> args = arguments.args() != null ? 
arguments.args() : Map.of();
             McpToolCallResult result = tool.handler().call(args);
+            // the text content is kept next to structuredContent for clients 
without structured output support
             return result.isError()
-                    ? ToolResponse.error(result.text()) : 
ToolResponse.success(new TextContent(result.text()));
+                    ? ToolResponse.error(result.text())
+                    : new ToolResponse(false, List.of(new 
TextContent(result.text())), result.structuredContent(), null);

Review Comment:
   Question: `result.structuredContent()` is passed through as-is. As far as I 
can tell, Camel's `AiToolParameterHelper.parseStructuredOutput` can also return 
a `List`, `Number` or `Boolean` (for example, a route body of `[1,2]` or `42`). 
The MCP spec requires `structuredContent` to be a JSON object, so a strict 
client could reject that response. Should we guard here, passing through only 
`Map`/`JsonObject` and returning `ToolResponse.error(...)` otherwise? Or should 
Camel enforce this instead?



##########
integration-tests/mcp-server/src/test/java/org/apache/camel/quarkus/component/mcp/server/it/McpServerTest.java:
##########
@@ -115,6 +117,54 @@ void testArgSchemaToolExecutesWithNestedArguments() {
                 .thenAssertResults();
     }
 
+    @Test
+    void testOutputSchemaIsPublished() {
+        client().when()
+                .toolsList(page -> {
+                    // outputSchema resource, published as-is with its nested 
structure
+                    JsonObject orderStatus = toolByName(page, 
"order_status").outputSchema();
+                    assertThat(orderStatus).isNotNull();
+                    
assertThat(orderStatus.getJsonObject("properties").getJsonObject("items")
+                            
.getJsonObject("items").getString("type")).isEqualTo("string");
+                    
assertThat(orderStatus.getJsonArray("required")).containsExactly("orderId", 
"status", "items");
+
+                    // flat outputParameter.* options, published as the 
generated schema
+                    JsonObject weather = toolByName(page, 
"get_weather").outputSchema();
+                    assertThat(weather).isNotNull();
+                    
assertThat(weather.getJsonObject("properties").getJsonObject("temperature")
+                            .getString("type")).isEqualTo("number");
+                    
assertThat(weather.getJsonObject("properties").getJsonObject("unit").getJsonArray("enum"))
+                            .containsExactly("celsius", "fahrenheit");
+                    
assertThat(weather.getJsonArray("required")).containsExactly("temperature");
+
+                    // tools without an output declaration are unchanged
+                    assertThat(toolByName(page, 
"say_hello").outputSchema()).isNull();
+                })
+                .thenAssertResults();
+    }
+
+    @Test
+    void testStructuredContentIsReturned() {

Review Comment:
   Nit: could we add a case for the error path? For example, a tool with an 
output schema whose route returns a body that isn't valid JSON, asserting that 
`isError()` is true and `structuredContent()` is null.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to