This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 6ab243b20b31 CAMEL-23962: Preserve all MCP inputSchema keywords in
tool conversion
6ab243b20b31 is described below
commit 6ab243b20b310aeb158420a1886e2d9f7b8a1f10
Author: Omar Atie <[email protected]>
AuthorDate: Sat Jul 18 00:13:38 2026 -0700
CAMEL-23962: Preserve all MCP inputSchema keywords in tool conversion
McpToolConverter.convertTool() previously only copied type, properties,
and required from the MCP tool inputSchema, dropping all other JSON
Schema keywords. This broke schemas using $defs/$ref, additionalProperties,
oneOf/anyOf, and root-level description. Now copies the full inputSchema
map, defaulting type to "object" when absent.
Closes #24881
---
.../camel/component/openai/McpToolConverter.java | 15 +++-----
.../component/openai/McpToolConverterTest.java | 45 ++++++++++++++++++++++
2 files changed, 51 insertions(+), 9 deletions(-)
diff --git
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/McpToolConverter.java
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/McpToolConverter.java
index 03a9cfd7118b..e9bf4ab08ad1 100644
---
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/McpToolConverter.java
+++
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/McpToolConverter.java
@@ -17,6 +17,7 @@
package org.apache.camel.component.openai;
import java.util.List;
+import java.util.Map;
import com.openai.core.JsonValue;
import com.openai.models.FunctionDefinition;
@@ -48,17 +49,13 @@ final class McpToolConverter {
if (tool.inputSchema() != null) {
FunctionParameters.Builder paramsBuilder =
FunctionParameters.builder();
- paramsBuilder.putAdditionalProperty("type",
- JsonValue.from(tool.inputSchema().getOrDefault("type",
"object")));
+ Map<String, Object> inputSchema = tool.inputSchema();
- Object properties = tool.inputSchema().get("properties");
- if (properties != null) {
- paramsBuilder.putAdditionalProperty("properties",
JsonValue.from(properties));
+ if (!inputSchema.containsKey("type")) {
+ paramsBuilder.putAdditionalProperty("type",
JsonValue.from("object"));
}
-
- Object required = tool.inputSchema().get("required");
- if (required != null) {
- paramsBuilder.putAdditionalProperty("required",
JsonValue.from(required));
+ for (Map.Entry<String, Object> entry : inputSchema.entrySet()) {
+ paramsBuilder.putAdditionalProperty(entry.getKey(),
JsonValue.from(entry.getValue()));
}
funcBuilder.parameters(paramsBuilder.build());
diff --git
a/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/McpToolConverterTest.java
b/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/McpToolConverterTest.java
index d496fdfad10b..d488f40be155 100644
---
a/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/McpToolConverterTest.java
+++
b/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/McpToolConverterTest.java
@@ -20,10 +20,13 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
+import com.openai.core.JsonValue;
+import com.openai.models.FunctionParameters;
import com.openai.models.chat.completions.ChatCompletionFunctionTool;
import io.modelcontextprotocol.spec.McpSchema;
import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -98,4 +101,46 @@ class McpToolConverterTest {
assertNotNull(result);
assertTrue(result.isEmpty());
}
+
+ @Test
+ void convertPreservesAllInputSchemaKeywords() {
+ Map<String, Object> addressDef = Map.of("type", "object",
"properties", Map.of("city", Map.of("type", "string")));
+ Map<String, Object> inputSchema = Map.of(
+ "type", "object",
+ "description", "Search parameters",
+ "additionalProperties", false,
+ "$defs", Map.of("Address", addressDef),
+ "properties", Map.of("query", Map.of("type", "string"),
"address", Map.of("$ref", "#/$defs/Address")),
+ "required", List.of("query"));
+
+ McpSchema.Tool tool = McpSchema.Tool.builder("search", inputSchema)
+ .description("Search tool")
+ .build();
+
+ FunctionParameters parameters =
McpToolConverter.convert(List.of(tool)).get(0).function().parameters().get();
+ Map<String, JsonValue> converted = parameters._additionalProperties();
+
+ assertThat(converted).containsKeys("type", "description",
"additionalProperties", "$defs", "properties", "required");
+ assertThat(converted.get("type").asString()).contains("object");
+ assertThat(converted.get("description").asString()).contains("Search
parameters");
+
assertThat(converted.get("additionalProperties").asBoolean()).contains(false);
+ @SuppressWarnings("unchecked")
+ Map<String, JsonValue> defs = (Map<String, JsonValue>)
converted.get("$defs").asObject().orElse(Map.of());
+ @SuppressWarnings("unchecked")
+ Map<String, JsonValue> properties = (Map<String, JsonValue>)
converted.get("properties").asObject().orElse(Map.of());
+ assertThat(defs).containsKey("Address");
+ assertThat(properties).containsKey("address");
+ assertThat(converted.get("required").asArray()).isNotEmpty();
+ }
+
+ @Test
+ void convertDefaultsTypeToObjectWhenMissing() {
+ McpSchema.Tool tool = McpSchema.Tool.builder("bare_schema",
Map.of("properties", Map.of("x", Map.of("type", "string"))))
+ .build();
+
+ FunctionParameters parameters =
McpToolConverter.convert(List.of(tool)).get(0).function().parameters().get();
+
+
assertThat(parameters._additionalProperties().get("type").asString()).contains("object");
+
assertThat(parameters._additionalProperties()).containsKey("properties");
+ }
}