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 f09459b3c2ea CAMEL-24334: Add argSchema raw JSON Schema support to 
camel-ai-tool
f09459b3c2ea is described below

commit f09459b3c2ea605f5d7c3b5ffda0fdaec95a4780
Author: Omar Atie <[email protected]>
AuthorDate: Thu Aug 6 03:26:10 2026 -0700

    CAMEL-24334: Add argSchema raw JSON Schema support to camel-ai-tool
    
    Adds optional argSchema endpoint option for declaring complex nested tool
    parameters as raw JSON Schema (inline or via classpath/file/resource
    references) instead of flat parameter.* options. Includes mutual exclusion
    validation, startup schema validation, LangChain4j adapter conversion,
    documentation, and tests.
    
    Closes #25368
    
    Co-authored-by: Cursor Agent <[email protected]>
---
 .../apache/camel/catalog/components/ai-tool.json   |  26 ++--
 .../camel/catalog/docs/ai-tool-component.adoc      |  52 +++++++
 .../ai/tool/AiToolComponentConfigurer.java         |   6 +
 .../ai/tool/AiToolConfigurationConfigurer.java     |   6 +
 .../ai/tool/AiToolEndpointConfigurer.java          |   6 +
 .../ai/tool/AiToolEndpointUriFactory.java          |   3 +-
 .../apache/camel/component/ai/tool/ai-tool.json    |  26 ++--
 .../src/main/docs/ai-tool-component.adoc           |  52 +++++++
 .../component/ai/tool/AiToolConfiguration.java     |  17 ++-
 .../camel/component/ai/tool/AiToolConsumer.java    |  18 ++-
 .../camel/component/ai/tool/AiToolExecutor.java    |  11 +-
 .../component/ai/tool/AiToolParameterHelper.java   | 164 +++++++++++++++++++++
 .../apache/camel/component/ai/tool/AiToolSpec.java |  38 ++++-
 .../ai/tool/AiToolEndpointLifecycleTest.java       |  36 +++++
 .../component/ai/tool/AiToolExecutorTest.java      |  83 +++++++++++
 .../ai/tool/AiToolParameterHelperTest.java         | 105 +++++++++++++
 .../test/resources/arg-schemas/create-order.json   |  26 ++++
 .../langchain4j/agent/AiToolSpecToLangChain4j.java |  54 +++++++
 .../agent/AiToolSpecToLangChain4jTest.java         |  37 +++++
 .../dsl/AiToolComponentBuilderFactory.java         |  26 +++-
 .../endpoint/dsl/AiToolEndpointBuilderFactory.java |  28 +++-
 21 files changed, 774 insertions(+), 46 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ai-tool.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ai-tool.json
index f0fd90ba449e..e0ae716192d7 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ai-tool.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ai-tool.json
@@ -24,20 +24,22 @@
     "remote": false
   },
   "componentProperties": {
-    "bridgeErrorHandler": { "index": 0, "kind": "property", "displayName": 
"Bridge Error Handler", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Allows for bridging the consumer to the Camel routing Error Handler, which 
mean any exceptions (if possible) occurred while the Camel consumer is trying 
to pickup incoming messages, or the like [...]
-    "configuration": { "index": 1, "kind": "property", "displayName": 
"Configuration", "group": "consumer", "label": "", "required": false, "type": 
"object", "javaType": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"deprecated": false, "autowired": false, "secret": false, "description": "The 
component configuration" },
-    "description": { "index": 2, "kind": "property", "displayName": 
"Description", "group": "consumer", "label": "consumer", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"configurationField": "configuration", "description": "Human-readable 
description of what this tool does. Passed verbatim to the LLM; be  [...]
-    "parameters": { "index": 3, "kind": "property", "displayName": 
"Parameters", "group": "consumer", "label": "consumer", "required": false, 
"type": "object", "javaType": "java.util.Map<java.lang.String, 
java.lang.String>", "prefix": "parameter.", "multiValue": true, "deprecated": 
false, "deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"configurationField": "configuration", "description": "Tool in [...]
-    "tags": { "index": 4, "kind": "property", "displayName": "Tags", "group": 
"consumer", "label": "consumer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.ai.tool.AiToolConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of tags used to group 
tools. Producers filter the registry by these tags to [...]
-    "autowiredEnabled": { "index": 5, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching t [...]
+    "argSchema": { "index": 0, "kind": "property", "displayName": "Arg 
Schema", "group": "consumer", "label": "consumer", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"supportFileReference": true, "configurationClass": 
"org.apache.camel.component.ai.tool.AiToolConfiguration", "configurationField": 
"configuration", "description": "Raw JSON Schema for tool input parameters. 
Supports inl [...]
+    "bridgeErrorHandler": { "index": 1, "kind": "property", "displayName": 
"Bridge Error Handler", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Allows for bridging the consumer to the Camel routing Error Handler, which 
mean any exceptions (if possible) occurred while the Camel consumer is trying 
to pickup incoming messages, or the like [...]
+    "configuration": { "index": 2, "kind": "property", "displayName": 
"Configuration", "group": "consumer", "label": "", "required": false, "type": 
"object", "javaType": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"deprecated": false, "autowired": false, "secret": false, "description": "The 
component configuration" },
+    "description": { "index": 3, "kind": "property", "displayName": 
"Description", "group": "consumer", "label": "consumer", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"configurationField": "configuration", "description": "Human-readable 
description of what this tool does. Passed verbatim to the LLM; be  [...]
+    "parameters": { "index": 4, "kind": "property", "displayName": 
"Parameters", "group": "consumer", "label": "consumer", "required": false, 
"type": "object", "javaType": "java.util.Map<java.lang.String, 
java.lang.String>", "prefix": "parameter.", "multiValue": true, "deprecated": 
false, "deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"configurationField": "configuration", "description": "Tool in [...]
+    "tags": { "index": 5, "kind": "property", "displayName": "Tags", "group": 
"consumer", "label": "consumer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.ai.tool.AiToolConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of tags used to group 
tools. Producers filter the registry by these tags to [...]
+    "autowiredEnabled": { "index": 6, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching t [...]
   },
   "properties": {
     "toolName": { "index": 0, "kind": "path", "displayName": "Tool Name", 
"group": "consumer", "label": "", "required": true, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The tool name. This is the 
name the LLM sees and uses to invoke the tool." },
-    "description": { "index": 1, "kind": "parameter", "displayName": 
"Description", "group": "consumer", "label": "consumer", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"configurationField": "configuration", "description": "Human-readable 
description of what this tool does. Passed verbatim to the LLM; be [...]
-    "parameters": { "index": 2, "kind": "parameter", "displayName": 
"Parameters", "group": "consumer", "label": "consumer", "required": false, 
"type": "object", "javaType": "java.util.Map<java.lang.String, 
java.lang.String>", "prefix": "parameter.", "multiValue": true, "deprecated": 
false, "deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"configurationField": "configuration", "description": "Tool i [...]
-    "tags": { "index": 3, "kind": "parameter", "displayName": "Tags", "group": 
"consumer", "label": "consumer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.ai.tool.AiToolConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of tags used to group 
tools. Producers filter the registry by these tags t [...]
-    "bridgeErrorHandler": { "index": 4, "kind": "parameter", "displayName": 
"Bridge Error Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Allows for bridging the consumer to the 
Camel routing Error Handler, which mean any exceptions (if possible) occurred 
while the Camel consumer is trying to pickup incoming  [...]
-    "exceptionHandler": { "index": 5, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By def [...]
-    "exchangePattern": { "index": 6, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "enum", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." }
+    "argSchema": { "index": 1, "kind": "parameter", "displayName": "Arg 
Schema", "group": "consumer", "label": "consumer", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"supportFileReference": true, "configurationClass": 
"org.apache.camel.component.ai.tool.AiToolConfiguration", "configurationField": 
"configuration", "description": "Raw JSON Schema for tool input parameters. 
Supports in [...]
+    "description": { "index": 2, "kind": "parameter", "displayName": 
"Description", "group": "consumer", "label": "consumer", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"configurationField": "configuration", "description": "Human-readable 
description of what this tool does. Passed verbatim to the LLM; be [...]
+    "parameters": { "index": 3, "kind": "parameter", "displayName": 
"Parameters", "group": "consumer", "label": "consumer", "required": false, 
"type": "object", "javaType": "java.util.Map<java.lang.String, 
java.lang.String>", "prefix": "parameter.", "multiValue": true, "deprecated": 
false, "deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"configurationField": "configuration", "description": "Tool i [...]
+    "tags": { "index": 4, "kind": "parameter", "displayName": "Tags", "group": 
"consumer", "label": "consumer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.ai.tool.AiToolConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of tags used to group 
tools. Producers filter the registry by these tags t [...]
+    "bridgeErrorHandler": { "index": 5, "kind": "parameter", "displayName": 
"Bridge Error Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Allows for bridging the consumer to the 
Camel routing Error Handler, which mean any exceptions (if possible) occurred 
while the Camel consumer is trying to pickup incoming  [...]
+    "exceptionHandler": { "index": 6, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By def [...]
+    "exchangePattern": { "index": 7, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "enum", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." }
   }
 }
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ai-tool-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ai-tool-component.adoc
index e478e135d097..a55a8b898782 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ai-tool-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ai-tool-component.adoc
@@ -145,6 +145,58 @@ YAML::
 ----
 ====
 
+=== Complex Parameters with argSchema
+
+Use `argSchema` when the flat `parameter.*` syntax is not expressive enough 
for nested objects, arrays, or other advanced JSON Schema constructs. 
`argSchema` is mutually exclusive with `parameter.*`.
+
+[tabs]
+====
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: ai-tool:createOrder
+      parameters:
+        tags: orders
+        description: "Create an order"
+        argSchema: |
+          {
+            "type": "object",
+            "properties": {
+              "customer": {
+                "type": "object",
+                "properties": {
+                  "id": { "type": "string" }
+                },
+                "required": ["id"]
+              },
+              "items": {
+                "type": "array",
+                "items": {
+                  "type": "object",
+                  "properties": {
+                    "sku": { "type": "string" },
+                    "qty": { "type": "integer" }
+                  }
+                }
+              }
+            },
+            "required": ["customer", "items"],
+            "additionalProperties": false
+          }
+      steps:
+        - to: direct:createOrder
+----
+====
+
+Top-level schema properties are exposed as exchange headers. Nested values are 
passed as Map or List objects.
+
+`argSchema` also supports Camel resource references such as 
`classpath:schemas/create-order.json`.
+
+The root schema must be a JSON Schema object with a top-level `properties` 
map. Camel always allowlists top-level property names when invoking the tool 
route, even if the schema sets `additionalProperties: true`. Nested fields are 
not flattened into headers; only top-level properties become exchange headers 
(Map/List/primitive values).
+
 === Tag-Based Discovery
 
 Tags group tools so that AI producers can select relevant subsets.
diff --git 
a/components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolComponentConfigurer.java
 
b/components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolComponentConfigurer.java
index 5eb0aeb43c84..029c6b816335 100644
--- 
a/components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolComponentConfigurer.java
+++ 
b/components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolComponentConfigurer.java
@@ -30,6 +30,8 @@ public class AiToolComponentConfigurer extends 
PropertyConfigurerSupport impleme
     public boolean configure(CamelContext camelContext, Object obj, String 
name, Object value, boolean ignoreCase) {
         AiToolComponent target = (AiToolComponent) obj;
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "argschema":
+        case "argSchema": 
getOrCreateConfiguration(target).setArgSchema(property(camelContext, 
java.lang.String.class, value)); return true;
         case "autowiredenabled":
         case "autowiredEnabled": 
target.setAutowiredEnabled(property(camelContext, boolean.class, value)); 
return true;
         case "bridgeerrorhandler":
@@ -45,6 +47,8 @@ public class AiToolComponentConfigurer extends 
PropertyConfigurerSupport impleme
     @Override
     public Class<?> getOptionType(String name, boolean ignoreCase) {
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "argschema":
+        case "argSchema": return java.lang.String.class;
         case "autowiredenabled":
         case "autowiredEnabled": return boolean.class;
         case "bridgeerrorhandler":
@@ -61,6 +65,8 @@ public class AiToolComponentConfigurer extends 
PropertyConfigurerSupport impleme
     public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
         AiToolComponent target = (AiToolComponent) obj;
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "argschema":
+        case "argSchema": return 
getOrCreateConfiguration(target).getArgSchema();
         case "autowiredenabled":
         case "autowiredEnabled": return target.isAutowiredEnabled();
         case "bridgeerrorhandler":
diff --git 
a/components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolConfigurationConfigurer.java
 
b/components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolConfigurationConfigurer.java
index 5639cfc7474c..f79d66df604b 100644
--- 
a/components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolConfigurationConfigurer.java
+++ 
b/components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolConfigurationConfigurer.java
@@ -23,6 +23,8 @@ public class AiToolConfigurationConfigurer extends 
org.apache.camel.support.comp
     public boolean configure(CamelContext camelContext, Object obj, String 
name, Object value, boolean ignoreCase) {
         org.apache.camel.component.ai.tool.AiToolConfiguration target = 
(org.apache.camel.component.ai.tool.AiToolConfiguration) obj;
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "argschema":
+        case "argSchema": target.setArgSchema(property(camelContext, 
java.lang.String.class, value)); return true;
         case "description": target.setDescription(property(camelContext, 
java.lang.String.class, value)); return true;
         case "parameters": target.setParameters(property(camelContext, 
java.util.Map.class, value)); return true;
         case "tags": target.setTags(property(camelContext, 
java.lang.String.class, value)); return true;
@@ -33,6 +35,8 @@ public class AiToolConfigurationConfigurer extends 
org.apache.camel.support.comp
     @Override
     public Class<?> getOptionType(String name, boolean ignoreCase) {
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "argschema":
+        case "argSchema": return java.lang.String.class;
         case "description": return java.lang.String.class;
         case "parameters": return java.util.Map.class;
         case "tags": return java.lang.String.class;
@@ -44,6 +48,8 @@ public class AiToolConfigurationConfigurer extends 
org.apache.camel.support.comp
     public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
         org.apache.camel.component.ai.tool.AiToolConfiguration target = 
(org.apache.camel.component.ai.tool.AiToolConfiguration) obj;
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "argschema":
+        case "argSchema": return target.getArgSchema();
         case "description": return target.getDescription();
         case "parameters": return target.getParameters();
         case "tags": return target.getTags();
diff --git 
a/components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolEndpointConfigurer.java
 
b/components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolEndpointConfigurer.java
index e799e69354da..d658045e6821 100644
--- 
a/components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolEndpointConfigurer.java
+++ 
b/components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolEndpointConfigurer.java
@@ -23,6 +23,8 @@ public class AiToolEndpointConfigurer extends 
PropertyConfigurerSupport implemen
     public boolean configure(CamelContext camelContext, Object obj, String 
name, Object value, boolean ignoreCase) {
         AiToolEndpoint target = (AiToolEndpoint) obj;
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "argschema":
+        case "argSchema": 
target.getConfiguration().setArgSchema(property(camelContext, 
java.lang.String.class, value)); return true;
         case "bridgeerrorhandler":
         case "bridgeErrorHandler": 
target.setBridgeErrorHandler(property(camelContext, boolean.class, value)); 
return true;
         case "description": 
target.getConfiguration().setDescription(property(camelContext, 
java.lang.String.class, value)); return true;
@@ -39,6 +41,8 @@ public class AiToolEndpointConfigurer extends 
PropertyConfigurerSupport implemen
     @Override
     public Class<?> getOptionType(String name, boolean ignoreCase) {
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "argschema":
+        case "argSchema": return java.lang.String.class;
         case "bridgeerrorhandler":
         case "bridgeErrorHandler": return boolean.class;
         case "description": return java.lang.String.class;
@@ -56,6 +60,8 @@ public class AiToolEndpointConfigurer extends 
PropertyConfigurerSupport implemen
     public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
         AiToolEndpoint target = (AiToolEndpoint) obj;
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "argschema":
+        case "argSchema": return target.getConfiguration().getArgSchema();
         case "bridgeerrorhandler":
         case "bridgeErrorHandler": return target.isBridgeErrorHandler();
         case "description": return target.getConfiguration().getDescription();
diff --git 
a/components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolEndpointUriFactory.java
 
b/components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolEndpointUriFactory.java
index 3bdaa5000b8c..85d40f65427c 100644
--- 
a/components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolEndpointUriFactory.java
+++ 
b/components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolEndpointUriFactory.java
@@ -24,7 +24,8 @@ public class AiToolEndpointUriFactory extends 
org.apache.camel.support.component
     private static final Set<String> ENDPOINT_IDENTITY_PROPERTY_NAMES;
     private static final Map<String, String> MULTI_VALUE_PREFIXES;
     static {
-        Set<String> props = new HashSet<>(7);
+        Set<String> props = new HashSet<>(8);
+        props.add("argSchema");
         props.add("bridgeErrorHandler");
         props.add("description");
         props.add("exceptionHandler");
diff --git 
a/components/camel-ai/camel-ai-tool/src/generated/resources/META-INF/org/apache/camel/component/ai/tool/ai-tool.json
 
b/components/camel-ai/camel-ai-tool/src/generated/resources/META-INF/org/apache/camel/component/ai/tool/ai-tool.json
index f0fd90ba449e..e0ae716192d7 100644
--- 
a/components/camel-ai/camel-ai-tool/src/generated/resources/META-INF/org/apache/camel/component/ai/tool/ai-tool.json
+++ 
b/components/camel-ai/camel-ai-tool/src/generated/resources/META-INF/org/apache/camel/component/ai/tool/ai-tool.json
@@ -24,20 +24,22 @@
     "remote": false
   },
   "componentProperties": {
-    "bridgeErrorHandler": { "index": 0, "kind": "property", "displayName": 
"Bridge Error Handler", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Allows for bridging the consumer to the Camel routing Error Handler, which 
mean any exceptions (if possible) occurred while the Camel consumer is trying 
to pickup incoming messages, or the like [...]
-    "configuration": { "index": 1, "kind": "property", "displayName": 
"Configuration", "group": "consumer", "label": "", "required": false, "type": 
"object", "javaType": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"deprecated": false, "autowired": false, "secret": false, "description": "The 
component configuration" },
-    "description": { "index": 2, "kind": "property", "displayName": 
"Description", "group": "consumer", "label": "consumer", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"configurationField": "configuration", "description": "Human-readable 
description of what this tool does. Passed verbatim to the LLM; be  [...]
-    "parameters": { "index": 3, "kind": "property", "displayName": 
"Parameters", "group": "consumer", "label": "consumer", "required": false, 
"type": "object", "javaType": "java.util.Map<java.lang.String, 
java.lang.String>", "prefix": "parameter.", "multiValue": true, "deprecated": 
false, "deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"configurationField": "configuration", "description": "Tool in [...]
-    "tags": { "index": 4, "kind": "property", "displayName": "Tags", "group": 
"consumer", "label": "consumer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.ai.tool.AiToolConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of tags used to group 
tools. Producers filter the registry by these tags to [...]
-    "autowiredEnabled": { "index": 5, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching t [...]
+    "argSchema": { "index": 0, "kind": "property", "displayName": "Arg 
Schema", "group": "consumer", "label": "consumer", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"supportFileReference": true, "configurationClass": 
"org.apache.camel.component.ai.tool.AiToolConfiguration", "configurationField": 
"configuration", "description": "Raw JSON Schema for tool input parameters. 
Supports inl [...]
+    "bridgeErrorHandler": { "index": 1, "kind": "property", "displayName": 
"Bridge Error Handler", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Allows for bridging the consumer to the Camel routing Error Handler, which 
mean any exceptions (if possible) occurred while the Camel consumer is trying 
to pickup incoming messages, or the like [...]
+    "configuration": { "index": 2, "kind": "property", "displayName": 
"Configuration", "group": "consumer", "label": "", "required": false, "type": 
"object", "javaType": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"deprecated": false, "autowired": false, "secret": false, "description": "The 
component configuration" },
+    "description": { "index": 3, "kind": "property", "displayName": 
"Description", "group": "consumer", "label": "consumer", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"configurationField": "configuration", "description": "Human-readable 
description of what this tool does. Passed verbatim to the LLM; be  [...]
+    "parameters": { "index": 4, "kind": "property", "displayName": 
"Parameters", "group": "consumer", "label": "consumer", "required": false, 
"type": "object", "javaType": "java.util.Map<java.lang.String, 
java.lang.String>", "prefix": "parameter.", "multiValue": true, "deprecated": 
false, "deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"configurationField": "configuration", "description": "Tool in [...]
+    "tags": { "index": 5, "kind": "property", "displayName": "Tags", "group": 
"consumer", "label": "consumer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.ai.tool.AiToolConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of tags used to group 
tools. Producers filter the registry by these tags to [...]
+    "autowiredEnabled": { "index": 6, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching t [...]
   },
   "properties": {
     "toolName": { "index": 0, "kind": "path", "displayName": "Tool Name", 
"group": "consumer", "label": "", "required": true, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The tool name. This is the 
name the LLM sees and uses to invoke the tool." },
-    "description": { "index": 1, "kind": "parameter", "displayName": 
"Description", "group": "consumer", "label": "consumer", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"configurationField": "configuration", "description": "Human-readable 
description of what this tool does. Passed verbatim to the LLM; be [...]
-    "parameters": { "index": 2, "kind": "parameter", "displayName": 
"Parameters", "group": "consumer", "label": "consumer", "required": false, 
"type": "object", "javaType": "java.util.Map<java.lang.String, 
java.lang.String>", "prefix": "parameter.", "multiValue": true, "deprecated": 
false, "deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"configurationField": "configuration", "description": "Tool i [...]
-    "tags": { "index": 3, "kind": "parameter", "displayName": "Tags", "group": 
"consumer", "label": "consumer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.ai.tool.AiToolConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of tags used to group 
tools. Producers filter the registry by these tags t [...]
-    "bridgeErrorHandler": { "index": 4, "kind": "parameter", "displayName": 
"Bridge Error Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Allows for bridging the consumer to the 
Camel routing Error Handler, which mean any exceptions (if possible) occurred 
while the Camel consumer is trying to pickup incoming  [...]
-    "exceptionHandler": { "index": 5, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By def [...]
-    "exchangePattern": { "index": 6, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "enum", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." }
+    "argSchema": { "index": 1, "kind": "parameter", "displayName": "Arg 
Schema", "group": "consumer", "label": "consumer", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"supportFileReference": true, "configurationClass": 
"org.apache.camel.component.ai.tool.AiToolConfiguration", "configurationField": 
"configuration", "description": "Raw JSON Schema for tool input parameters. 
Supports in [...]
+    "description": { "index": 2, "kind": "parameter", "displayName": 
"Description", "group": "consumer", "label": "consumer", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"configurationField": "configuration", "description": "Human-readable 
description of what this tool does. Passed verbatim to the LLM; be [...]
+    "parameters": { "index": 3, "kind": "parameter", "displayName": 
"Parameters", "group": "consumer", "label": "consumer", "required": false, 
"type": "object", "javaType": "java.util.Map<java.lang.String, 
java.lang.String>", "prefix": "parameter.", "multiValue": true, "deprecated": 
false, "deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.ai.tool.AiToolConfiguration", 
"configurationField": "configuration", "description": "Tool i [...]
+    "tags": { "index": 4, "kind": "parameter", "displayName": "Tags", "group": 
"consumer", "label": "consumer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.ai.tool.AiToolConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of tags used to group 
tools. Producers filter the registry by these tags t [...]
+    "bridgeErrorHandler": { "index": 5, "kind": "parameter", "displayName": 
"Bridge Error Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Allows for bridging the consumer to the 
Camel routing Error Handler, which mean any exceptions (if possible) occurred 
while the Camel consumer is trying to pickup incoming  [...]
+    "exceptionHandler": { "index": 6, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By def [...]
+    "exchangePattern": { "index": 7, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "enum", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." }
   }
 }
diff --git 
a/components/camel-ai/camel-ai-tool/src/main/docs/ai-tool-component.adoc 
b/components/camel-ai/camel-ai-tool/src/main/docs/ai-tool-component.adoc
index e478e135d097..a55a8b898782 100644
--- a/components/camel-ai/camel-ai-tool/src/main/docs/ai-tool-component.adoc
+++ b/components/camel-ai/camel-ai-tool/src/main/docs/ai-tool-component.adoc
@@ -145,6 +145,58 @@ YAML::
 ----
 ====
 
+=== Complex Parameters with argSchema
+
+Use `argSchema` when the flat `parameter.*` syntax is not expressive enough 
for nested objects, arrays, or other advanced JSON Schema constructs. 
`argSchema` is mutually exclusive with `parameter.*`.
+
+[tabs]
+====
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: ai-tool:createOrder
+      parameters:
+        tags: orders
+        description: "Create an order"
+        argSchema: |
+          {
+            "type": "object",
+            "properties": {
+              "customer": {
+                "type": "object",
+                "properties": {
+                  "id": { "type": "string" }
+                },
+                "required": ["id"]
+              },
+              "items": {
+                "type": "array",
+                "items": {
+                  "type": "object",
+                  "properties": {
+                    "sku": { "type": "string" },
+                    "qty": { "type": "integer" }
+                  }
+                }
+              }
+            },
+            "required": ["customer", "items"],
+            "additionalProperties": false
+          }
+      steps:
+        - to: direct:createOrder
+----
+====
+
+Top-level schema properties are exposed as exchange headers. Nested values are 
passed as Map or List objects.
+
+`argSchema` also supports Camel resource references such as 
`classpath:schemas/create-order.json`.
+
+The root schema must be a JSON Schema object with a top-level `properties` 
map. Camel always allowlists top-level property names when invoking the tool 
route, even if the schema sets `additionalProperties: true`. Nested fields are 
not flattened into headers; only top-level properties become exchange headers 
(Map/List/primitive values).
+
 === Tag-Based Discovery
 
 Tags group tools so that AI producers can select relevant subsets.
diff --git 
a/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolConfiguration.java
 
b/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolConfiguration.java
index c74738951808..78259c98687e 100644
--- 
a/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolConfiguration.java
+++ 
b/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolConfiguration.java
@@ -50,10 +50,17 @@ public class AiToolConfiguration implements Cloneable {
     @UriParam(description = "Tool input parameters. "
                             + "Format: parameter.NAME=TYPE, 
parameter.NAME.description=TEXT, "
                             + "parameter.NAME.required=true or false, 
parameter.NAME.enum=val1,val2. "
-                            + "Supported types: string, integer, number, 
boolean.",
+                            + "Supported types: string, integer, number, 
boolean. "
+                            + "Mutually exclusive with argSchema.",
               prefix = "parameter.", multiValue = true)
     private Map<String, String> parameters;
 
+    @UriParam(description = "Raw JSON Schema for tool input parameters. 
Supports inline JSON and resource "
+                            + "references (classpath:, file:, resource:). 
Mutually exclusive with the parameter "
+                            + "multi-value options. Use for nested objects, 
arrays, oneOf, and other complex schemas.")
+    @Metadata(label = "consumer", supportFileReference = true, largeInput = 
true, inputLanguage = "json")
+    private String argSchema;
+
     public AiToolConfiguration() {
     }
 
@@ -81,6 +88,14 @@ public class AiToolConfiguration implements Cloneable {
         this.parameters = parameters;
     }
 
+    public String getArgSchema() {
+        return argSchema;
+    }
+
+    public void setArgSchema(String argSchema) {
+        this.argSchema = argSchema;
+    }
+
     public AiToolConfiguration copy() {
         try {
             AiToolConfiguration copy = (AiToolConfiguration) super.clone();
diff --git 
a/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolConsumer.java
 
b/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolConsumer.java
index 0b07f7c25821..a2ca9db90a38 100644
--- 
a/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolConsumer.java
+++ 
b/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolConsumer.java
@@ -49,14 +49,18 @@ public class AiToolConsumer extends DefaultConsumer {
         super.doStart();
 
         Map<String, String> params = configuration.getParameters();
-        Map<String, AiToolParameterHelper.ParameterDef> parameterDefs
-                = (params != null && !params.isEmpty())
-                        ? AiToolParameterHelper.parseParameterMetadata(params)
-                        : Map.of();
+        String argSchema = configuration.getArgSchema();
+        AiToolParameterHelper.validateParameterSourceExclusive(params, 
argSchema);
 
-        String jsonSchema = !parameterDefs.isEmpty()
-                ? AiToolParameterHelper.buildJsonSchemaFromDefs(parameterDefs)
-                : null;
+        Map<String, AiToolParameterHelper.ParameterDef> parameterDefs = 
Map.of();
+        String jsonSchema = null;
+
+        if (params != null && !params.isEmpty()) {
+            parameterDefs = 
AiToolParameterHelper.parseParameterMetadata(params);
+            jsonSchema = 
AiToolParameterHelper.buildJsonSchemaFromDefs(parameterDefs);
+        } else if (argSchema != null && !argSchema.isBlank()) {
+            jsonSchema = 
AiToolParameterHelper.resolveArgSchema(getEndpoint().getCamelContext(), 
argSchema);
+        }
 
         String desc = configuration.getDescription();
         if (desc == null || desc.isBlank()) {
diff --git 
a/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolExecutor.java
 
b/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolExecutor.java
index bacd29dbc37b..8f918232d77a 100644
--- 
a/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolExecutor.java
+++ 
b/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolExecutor.java
@@ -100,7 +100,7 @@ public final class AiToolExecutor {
         // A tool that declares no parameters accepts none, so an empty 
declaration must filter
         // everything rather than let every argument through.
         if (!argsCopy.isEmpty()) {
-            Set<String> declaredParams = spec.getParameterDefs().keySet();
+            Set<String> declaredParams = spec.getDeclaredArgumentNames();
 
             argsCopy.keySet().removeIf(name -> {
                 if (!declaredParams.contains(name)) {
@@ -113,14 +113,13 @@ public final class AiToolExecutor {
             });
         }
 
-        for (Map.Entry<String, AiToolParameterHelper.ParameterDef> entry : 
spec.getParameterDefs().entrySet()) {
-            if (entry.getValue().isRequired()
-                    && (arguments == null || 
!arguments.containsKey(entry.getKey()))) {
+        for (String requiredName : spec.getRequiredArgumentNames()) {
+            if (!argsCopy.containsKey(requiredName)) {
                 LOG.warn("Missing required argument '{}' for tool '{}' -- the 
LLM did not send "
                          + "a parameter that is declared as required in the 
tool specification",
-                        entry.getKey(), toolName);
+                        requiredName, toolName);
                 IllegalArgumentException cause = new IllegalArgumentException(
-                        String.format("Missing required argument '%s' for tool 
'%s'", entry.getKey(), toolName));
+                        String.format("Missing required argument '%s' for tool 
'%s'", requiredName, toolName));
                 return new AiToolResult.ArgumentError(cause.getMessage(), 
cause);
             }
         }
diff --git 
a/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolParameterHelper.java
 
b/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolParameterHelper.java
index 95732b14d1de..2be215949b48 100644
--- 
a/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolParameterHelper.java
+++ 
b/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolParameterHelper.java
@@ -16,16 +16,25 @@
  */
 package org.apache.camel.component.ai.tool;
 
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 
+import org.apache.camel.CamelContext;
+import org.apache.camel.support.ResourceHelper;
+import org.apache.camel.util.json.DeserializationException;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -150,6 +159,161 @@ public final class AiToolParameterHelper {
         return schema.toJson();
     }
 
+    /**
+     * Validates that flat {@code parameter.*} metadata and {@code argSchema} 
are not both configured.
+     */
+    public static void validateParameterSourceExclusive(Map<String, String> 
parameters, String argSchema) {
+        boolean hasParameters = parameters != null && !parameters.isEmpty();
+        boolean hasArgSchema = argSchema != null && !argSchema.isBlank();
+        if (hasParameters && hasArgSchema) {
+            throw new IllegalArgumentException(
+                    "argSchema and parameter.* are mutually exclusive on 
ai-tool endpoints");
+        }
+    }
+
+    /**
+     * Resolves, validates, and normalizes a raw JSON Schema for tool input.
+     */
+    public static String resolveArgSchema(CamelContext camelContext, String 
argSchema) {
+        if (argSchema == null || argSchema.isBlank()) {
+            throw new IllegalArgumentException("argSchema must not be blank");
+        }
+
+        String resolved = camelContext.resolvePropertyPlaceholders(argSchema);
+        String content = resolveResourceContent(camelContext, resolved);
+        if (content != null) {
+            resolved = content;
+        }
+
+        JsonObject root = parseJsonObject(resolved, argSchema);
+        validateRootSchemaObject(root);
+        return root.toJson();
+    }
+
+    /**
+     * Returns top-level property names declared in a JSON Schema object.
+     */
+    public static Set<String> extractTopLevelPropertyNames(String jsonSchema) {
+        if (jsonSchema == null || jsonSchema.isBlank()) {
+            return Set.of();
+        }
+        JsonObject root = parseJsonObject(jsonSchema, jsonSchema);
+        Map<String, Object> properties = requirePropertiesMap(root, 
jsonSchema);
+        if (properties.isEmpty()) {
+            return Set.of();
+        }
+        return Set.copyOf(properties.keySet());
+    }
+
+    /**
+     * Returns top-level required property names declared in a JSON Schema 
object.
+     */
+    public static Set<String> extractRequiredPropertyNames(String jsonSchema) {
+        if (jsonSchema == null || jsonSchema.isBlank()) {
+            return Set.of();
+        }
+        JsonObject root = parseJsonObject(jsonSchema, jsonSchema);
+        Collection<?> required = readRequiredArray(root, jsonSchema);
+        if (required.isEmpty()) {
+            return Set.of();
+        }
+        Set<String> names = new LinkedHashSet<>();
+        for (Object value : required) {
+            if (value != null) {
+                names.add(value.toString());
+            }
+        }
+        return Set.copyOf(names);
+    }
+
+    private static JsonObject parseJsonObject(String json, String 
originalValue) {
+        try {
+            Object parsed = Jsoner.deserialize(json);
+            if (parsed == null) {
+                throw new IllegalArgumentException("argSchema must be a JSON 
object, but was: null");
+            }
+            if (!(parsed instanceof JsonObject root)) {
+                throw new IllegalArgumentException(
+                        "argSchema must be a JSON object, but was: " + 
parsed.getClass().getSimpleName());
+            }
+            return root;
+        } catch (DeserializationException e) {
+            throw new IllegalArgumentException(
+                    "argSchema does not contain valid JSON. Provided value: " 
+ originalValue, e);
+        }
+    }
+
+    private static void validateRootSchemaObject(JsonObject root) {
+        Object type = root.get("type");
+        if (type != null && !"object".equals(type)) {
+            throw new IllegalArgumentException(
+                    "argSchema root type must be 'object' when specified, but 
was: " + type);
+        }
+        Map<String, Object> properties = requirePropertiesMap(root, 
"argSchema");
+        if (properties.isEmpty()) {
+            throw new IllegalArgumentException("argSchema must declare at 
least one top-level property");
+        }
+        Collection<?> required = readRequiredArray(root, "argSchema");
+        for (Object requiredName : required) {
+            if (requiredName == null || 
!properties.containsKey(requiredName.toString())) {
+                throw new IllegalArgumentException(
+                        "argSchema required entry '" + requiredName + "' is 
not declared in properties");
+            }
+        }
+    }
+
+    private static Map<String, Object> requirePropertiesMap(JsonObject root, 
String context) {
+        Object properties = root.get("properties");
+        if (properties == null) {
+            throw new IllegalArgumentException(context + " must be a JSON 
Schema object with a properties map");
+        }
+        if (!(properties instanceof Map<?, ?> map)) {
+            throw new IllegalArgumentException(
+                    context + " properties must be a JSON object, but was: "
+                                               + 
properties.getClass().getSimpleName());
+        }
+        Map<String, Object> typed = new HashMap<>();
+        for (Map.Entry<?, ?> entry : map.entrySet()) {
+            if (entry.getKey() != null) {
+                typed.put(entry.getKey().toString(), entry.getValue());
+            }
+        }
+        return typed;
+    }
+
+    private static Collection<?> readRequiredArray(JsonObject root, String 
context) {
+        Object required = root.get("required");
+        if (required == null) {
+            return List.of();
+        }
+        if (!(required instanceof Collection<?> collection)) {
+            throw new IllegalArgumentException(
+                    context + " required must be a JSON array, but was: "
+                                               + 
required.getClass().getSimpleName());
+        }
+        return collection;
+    }
+
+    private static String resolveResourceContent(CamelContext camelContext, 
String property) {
+        try {
+            if (ResourceHelper.hasScheme(property)) {
+                try (InputStream is = 
ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext, property)) {
+                    return 
camelContext.getTypeConverter().convertTo(String.class, is);
+                }
+            }
+            try (InputStream is = 
ResourceHelper.resolveResourceAsInputStream(camelContext, property)) {
+                if (is != null) {
+                    return 
camelContext.getTypeConverter().convertTo(String.class, is);
+                }
+            }
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Failed to load argSchema 
resource: " + property, e);
+        } catch (Exception e) {
+            // not a resolvable resource URI — fall through and treat as 
inline JSON content
+        }
+        return null;
+    }
+
     private static String mapType(String type) {
         if (type == null) {
             return "string";
diff --git 
a/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolSpec.java
 
b/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolSpec.java
index dbecd4f7d41c..2206b06fc6d0 100644
--- 
a/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolSpec.java
+++ 
b/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolSpec.java
@@ -19,6 +19,8 @@ package org.apache.camel.component.ai.tool;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import org.apache.camel.support.DefaultConsumer;
 
@@ -36,6 +38,8 @@ public final class AiToolSpec {
     private final String description;
     private final Map<String, AiToolParameterHelper.ParameterDef> 
parameterDefs;
     private final String parametersJsonSchema;
+    private final Set<String> declaredArgumentNames;
+    private final Set<String> requiredArgumentNames;
     private final DefaultConsumer consumer;
 
     public AiToolSpec(
@@ -48,6 +52,19 @@ public final class AiToolSpec {
         this.description = description;
         this.parameterDefs = parameterDefs != null ? 
Collections.unmodifiableMap(parameterDefs) : Map.of();
         this.parametersJsonSchema = parametersJsonSchema;
+        if (!this.parameterDefs.isEmpty()) {
+            this.declaredArgumentNames = 
Set.copyOf(this.parameterDefs.keySet());
+            this.requiredArgumentNames = this.parameterDefs.entrySet().stream()
+                    .filter(entry -> entry.getValue().isRequired())
+                    .map(Map.Entry::getKey)
+                    .collect(Collectors.toUnmodifiableSet());
+        } else if (parametersJsonSchema != null && 
!parametersJsonSchema.isBlank()) {
+            this.declaredArgumentNames = 
AiToolParameterHelper.extractTopLevelPropertyNames(parametersJsonSchema);
+            this.requiredArgumentNames = 
AiToolParameterHelper.extractRequiredPropertyNames(parametersJsonSchema);
+        } else {
+            this.declaredArgumentNames = Set.of();
+            this.requiredArgumentNames = Set.of();
+        }
         this.consumer = consumer;
     }
 
@@ -75,6 +92,22 @@ public final class AiToolSpec {
         return parametersJsonSchema;
     }
 
+    /**
+     * Top-level argument names accepted by this tool. Derived from flat 
parameter definitions or from a raw
+     * {@code argSchema}'s top-level {@code properties} map.
+     */
+    public Set<String> getDeclaredArgumentNames() {
+        return declaredArgumentNames;
+    }
+
+    /**
+     * Top-level required argument names. Derived from flat parameter 
definitions or from a raw schema's top-level
+     * {@code required} array.
+     */
+    public Set<String> getRequiredArgumentNames() {
+        return requiredArgumentNames;
+    }
+
     /**
      * The Camel consumer that executes this tool's route when the LLM invokes 
it.
      */
@@ -97,12 +130,15 @@ public final class AiToolSpec {
                 && Objects.equals(description, that.description)
                 && Objects.equals(parameterDefs, that.parameterDefs)
                 && Objects.equals(parametersJsonSchema, 
that.parametersJsonSchema)
+                && Objects.equals(declaredArgumentNames, 
that.declaredArgumentNames)
+                && Objects.equals(requiredArgumentNames, 
that.requiredArgumentNames)
                 && Objects.equals(consumer, that.consumer);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(name, description, parameterDefs, 
parametersJsonSchema, consumer);
+        return Objects.hash(name, description, parameterDefs, 
parametersJsonSchema, declaredArgumentNames,
+                requiredArgumentNames, consumer);
     }
 
     @Override
diff --git 
a/components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolEndpointLifecycleTest.java
 
b/components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolEndpointLifecycleTest.java
index 762a0349f856..d5367b4b8222 100644
--- 
a/components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolEndpointLifecycleTest.java
+++ 
b/components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolEndpointLifecycleTest.java
@@ -320,6 +320,42 @@ public class AiToolEndpointLifecycleTest extends 
CamelTestSupport {
                 .contains("suspendableDefault");
     }
 
+    @Test
+    void testArgSchemaToolRegisteredOnStart() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            public void configure() {
+                from("ai-tool:createOrder"
+                     + "?tags=orders"
+                     + "&description=Create an order"
+                     + "&argSchema=classpath:arg-schemas/create-order.json")
+                        .setBody(constant("ok"));
+            }
+        });
+
+        AiToolRegistry registry = AiToolRegistry.getOrCreate(context);
+        AiToolSpec spec = registry.getToolsByTag("orders").iterator().next();
+
+        assertThat(spec.getName()).isEqualTo("createOrder");
+        assertThat(spec.getParameterDefs()).isEmpty();
+        
assertThat(spec.getParametersJsonSchema()).isNotNull().contains("\"customer\"");
+        
assertThat(spec.getDeclaredArgumentNames()).containsExactlyInAnyOrder("customer",
 "items");
+        
assertThat(spec.getRequiredArgumentNames()).containsExactlyInAnyOrder("customer",
 "items");
+    }
+
+    @Test
+    void testArgSchemaAndParametersAreMutuallyExclusive() {
+        assertThatThrownBy(() -> context.addRoutes(new RouteBuilder() {
+            public void configure() {
+                from("ai-tool:invalid"
+                     + "?tags=test"
+                     + "&parameter.city=string"
+                     + "&argSchema={\"type\":\"object\"}")
+                        .setBody(constant("invalid"));
+            }
+        })).isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("mutually exclusive");
+    }
+
     @Test
     public void testGetAllToolsReturnsAllPools() throws Exception {
         context.addRoutes(new RouteBuilder() {
diff --git 
a/components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolExecutorTest.java
 
b/components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolExecutorTest.java
index 5b04de63623b..605486c8d798 100644
--- 
a/components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolExecutorTest.java
+++ 
b/components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolExecutorTest.java
@@ -17,6 +17,7 @@
 package org.apache.camel.component.ai.tool;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.camel.Exchange;
@@ -74,6 +75,16 @@ public class AiToolExecutorTest extends CamelTestSupport {
                      + "&description=A tool that sets exception on exchange")
                         .process(exchange -> exchange.setException(
                                 new RuntimeException("Exchange-level 
failure")));
+
+                from("ai-tool:createOrder"
+                     + "?tags=test"
+                     + "&description=Create an order"
+                     + "&argSchema=classpath:arg-schemas/create-order.json")
+                        .process(exchange -> {
+                            Map<?, ?> customer = 
exchange.getMessage().getHeader("customer", Map.class);
+                            List<?> items = 
exchange.getMessage().getHeader("items", List.class);
+                            exchange.getMessage().setBody("order:" + 
customer.get("id") + ":" + items.size());
+                        });
             }
         };
     }
@@ -298,6 +309,78 @@ public class AiToolExecutorTest extends CamelTestSupport {
                 .isEqualTo("ok");
     }
 
+    @Test
+    void testExecuteWithRawArgSchemaNestedArguments() {
+        AiToolSpec spec = findSpec("createOrder");
+        Exchange exchange = new DefaultExchange(context);
+
+        Map<String, Object> customer = Map.of("id", "cust-1", "email", 
"[email protected]");
+        List<Map<String, Object>> items = List.of(Map.of("sku", "ABC", "qty", 
2));
+
+        Map<String, Object> arguments = new HashMap<>();
+        arguments.put("customer", customer);
+        arguments.put("items", items);
+
+        AiToolResult result = AiToolExecutor.execute(spec, arguments, 
exchange);
+
+        assertThat(result).isInstanceOf(AiToolResult.Success.class);
+        assertThat(((AiToolResult.Success) 
result).value()).isEqualTo("order:cust-1:1");
+        assertThat(exchange.getMessage().getHeader("customer", 
Map.class)).isEqualTo(customer);
+        assertThat(exchange.getMessage().getHeader("items", 
List.class)).isEqualTo(items);
+    }
+
+    @Test
+    void testExecuteIgnoresUndeclaredArgumentsForRawArgSchema() {
+        AiToolSpec spec = findSpec("createOrder");
+        Exchange exchange = new DefaultExchange(context);
+
+        Map<String, Object> arguments = new HashMap<>();
+        arguments.put("customer", Map.of("id", "cust-1"));
+        arguments.put("items", List.of(Map.of("sku", "ABC", "qty", 1)));
+        arguments.put("promoCode", "SAVE10");
+
+        AiToolResult result = AiToolExecutor.execute(spec, arguments, 
exchange);
+
+        assertThat(result).isInstanceOf(AiToolResult.Success.class);
+        assertThat(exchange.getMessage().getHeader("promoCode")).isNull();
+    }
+
+    @Test
+    void testExecuteReturnsArgumentErrorForMissingRequiredRawSchemaArgument() {
+        AiToolSpec spec = findSpec("createOrder");
+        Exchange exchange = new DefaultExchange(context);
+
+        Map<String, Object> arguments = new HashMap<>();
+        arguments.put("customer", Map.of("id", "cust-1"));
+
+        AiToolResult result = AiToolExecutor.execute(spec, arguments, 
exchange);
+
+        assertThat(result).isInstanceOf(AiToolResult.ArgumentError.class);
+        assertThat(((AiToolResult.ArgumentError) 
result).message()).contains("Missing required argument 'items'");
+    }
+
+    @Test
+    void testExecuteReturnsArgumentErrorWhenRequiredNameIsUndeclared() {
+        String schema = """
+                {
+                  "type": "object",
+                  "properties": {
+                    "city": { "type": "string" }
+                  },
+                  "required": ["country"]
+                }
+                """;
+        AiToolSpec spec = new AiToolSpec("badRequired", "test", Map.of(), 
schema, findSpec("greetUser").getConsumer());
+        Exchange exchange = new DefaultExchange(context);
+
+        Map<String, Object> arguments = Map.of("country", "Paris");
+
+        AiToolResult result = AiToolExecutor.execute(spec, arguments, 
exchange);
+
+        assertThat(result).isInstanceOf(AiToolResult.ArgumentError.class);
+        assertThat(((AiToolResult.ArgumentError) 
result).message()).contains("Missing required argument 'country'");
+    }
+
     private AiToolSpec findSpec(String toolName) {
         return 
AiToolRegistry.getOrCreate(context).getToolsByTag("test").stream()
                 .filter(s -> toolName.equals(s.getName()))
diff --git 
a/components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolParameterHelperTest.java
 
b/components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolParameterHelperTest.java
index 09b1c9144b00..81068d72ba90 100644
--- 
a/components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolParameterHelperTest.java
+++ 
b/components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolParameterHelperTest.java
@@ -19,6 +19,7 @@ package org.apache.camel.component.ai.tool;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
+import org.apache.camel.impl.DefaultCamelContext;
 import org.apache.camel.util.json.DeserializationException;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
@@ -26,9 +27,12 @@ import org.apache.camel.util.json.Jsoner;
 import org.junit.jupiter.api.Test;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 public class AiToolParameterHelperTest {
 
+    private final DefaultCamelContext camelContext = new DefaultCamelContext();
+
     @Test
     public void testSplitTags() {
         assertThat(AiToolParameterHelper.splitTags("a,b,c"))
@@ -266,4 +270,105 @@ public class AiToolParameterHelperTest {
                 .as("Default type should be string when only description is 
provided")
                 .isEqualTo("string");
     }
+
+    @Test
+    void testValidateParameterSourceExclusive() {
+        assertThatThrownBy(() -> 
AiToolParameterHelper.validateParameterSourceExclusive(
+                Map.of("city", "string"), "{\"type\":\"object\"}"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("mutually exclusive");
+    }
+
+    @Test
+    void testExtractTopLevelPropertyNamesFromRawSchema() throws 
DeserializationException {
+        String schema = """
+                {
+                  "type": "object",
+                  "properties": {
+                    "customer": { "type": "object" },
+                    "items": { "type": "array" }
+                  }
+                }
+                """;
+
+        assertThat(AiToolParameterHelper.extractTopLevelPropertyNames(schema))
+                .containsExactlyInAnyOrder("customer", "items");
+    }
+
+    @Test
+    void testExtractRequiredPropertyNamesFromRawSchema() throws 
DeserializationException {
+        String schema = """
+                {
+                  "type": "object",
+                  "properties": {
+                    "customer": { "type": "object" },
+                    "items": { "type": "array" }
+                  },
+                  "required": ["customer"]
+                }
+                """;
+
+        assertThat(AiToolParameterHelper.extractRequiredPropertyNames(schema))
+                .containsExactly("customer");
+    }
+
+    @Test
+    void testResolveArgSchemaRejectsJsonNull() {
+        assertThatThrownBy(() -> 
AiToolParameterHelper.resolveArgSchema(camelContext, "null"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("null");
+    }
+
+    @Test
+    void testResolveArgSchemaRejectsInvalidJson() {
+        assertThatThrownBy(() -> 
AiToolParameterHelper.resolveArgSchema(camelContext, "{not-json"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("valid JSON");
+    }
+
+    @Test
+    void testResolveArgSchemaRejectsNonObjectRootType() {
+        assertThatThrownBy(() -> 
AiToolParameterHelper.resolveArgSchema(camelContext, "{\"type\":\"string\"}"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("root type must be 'object'");
+    }
+
+    @Test
+    void testResolveArgSchemaRejectsMissingProperties() {
+        assertThatThrownBy(() -> 
AiToolParameterHelper.resolveArgSchema(camelContext, "{\"type\":\"object\"}"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("properties map");
+    }
+
+    @Test
+    void testResolveArgSchemaRejectsEmptyProperties() {
+        assertThatThrownBy(() -> 
AiToolParameterHelper.resolveArgSchema(camelContext,
+                "{\"type\":\"object\",\"properties\":{}}"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("at least one top-level property");
+    }
+
+    @Test
+    void testResolveArgSchemaRejectsInvalidRequiredArray() {
+        assertThatThrownBy(() -> 
AiToolParameterHelper.resolveArgSchema(camelContext,
+                
"{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":\"city\"}"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("required must be a JSON array");
+    }
+
+    @Test
+    void testResolveArgSchemaRejectsUnknownRequiredName() {
+        assertThatThrownBy(() -> 
AiToolParameterHelper.resolveArgSchema(camelContext,
+                
"{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"country\"]}"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("not declared in properties");
+    }
+
+    @Test
+    void testExtractTopLevelPropertyNamesRejectsInvalidProperties() {
+        assertThatThrownBy(() -> 
AiToolParameterHelper.extractTopLevelPropertyNames(
+                "{\"type\":\"object\",\"properties\":[]}"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("properties must be a JSON object");
+    }
 }
diff --git 
a/components/camel-ai/camel-ai-tool/src/test/resources/arg-schemas/create-order.json
 
b/components/camel-ai/camel-ai-tool/src/test/resources/arg-schemas/create-order.json
new file mode 100644
index 000000000000..04bb17a74a94
--- /dev/null
+++ 
b/components/camel-ai/camel-ai-tool/src/test/resources/arg-schemas/create-order.json
@@ -0,0 +1,26 @@
+{
+  "type": "object",
+  "properties": {
+    "customer": {
+      "type": "object",
+      "properties": {
+        "id": { "type": "string" },
+        "email": { "type": "string" }
+      },
+      "required": ["id"]
+    },
+    "items": {
+      "type": "array",
+      "items": {
+        "type": "object",
+        "properties": {
+          "sku": { "type": "string" },
+          "qty": { "type": "integer" }
+        },
+        "required": ["sku", "qty"]
+      }
+    }
+  },
+  "required": ["customer", "items"],
+  "additionalProperties": false
+}
diff --git 
a/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/AiToolSpecToLangChain4j.java
 
b/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/AiToolSpecToLangChain4j.java
index ab398899c72e..31e34dc3300a 100644
--- 
a/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/AiToolSpecToLangChain4j.java
+++ 
b/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/AiToolSpecToLangChain4j.java
@@ -22,15 +22,20 @@ import java.util.Locale;
 import java.util.Map;
 
 import dev.langchain4j.agent.tool.ToolSpecification;
+import dev.langchain4j.internal.JsonSchemaElementJsonUtils;
 import dev.langchain4j.model.chat.request.json.JsonBooleanSchema;
 import dev.langchain4j.model.chat.request.json.JsonEnumSchema;
 import dev.langchain4j.model.chat.request.json.JsonIntegerSchema;
 import dev.langchain4j.model.chat.request.json.JsonNumberSchema;
 import dev.langchain4j.model.chat.request.json.JsonObjectSchema;
+import dev.langchain4j.model.chat.request.json.JsonRawSchema;
 import dev.langchain4j.model.chat.request.json.JsonSchemaElement;
 import dev.langchain4j.model.chat.request.json.JsonStringSchema;
 import org.apache.camel.component.ai.tool.AiToolParameterHelper;
 import org.apache.camel.component.ai.tool.AiToolSpec;
+import org.apache.camel.util.json.DeserializationException;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
 
 /**
  * Converts {@link AiToolSpec} instances to langchain4j {@link 
ToolSpecification} objects, mapping Camel parameter
@@ -48,11 +53,60 @@ public final class AiToolSpecToLangChain4j {
 
         if (spec.getParameterDefs() != null && 
!spec.getParameterDefs().isEmpty()) {
             builder.parameters(buildSchema(spec.getParameterDefs()));
+        } else if (spec.getParametersJsonSchema() != null && 
!spec.getParametersJsonSchema().isBlank()) {
+            
builder.parameters(buildSchemaFromJson(spec.getParametersJsonSchema()));
         }
 
         return builder.build();
     }
 
+    private static JsonObjectSchema buildSchemaFromJson(String jsonSchema) {
+        try {
+            Object parsed = Jsoner.deserialize(jsonSchema);
+            if (parsed == null) {
+                throw new IllegalArgumentException("Tool JSON Schema must be a 
JSON object, but was: null");
+            }
+            if (!(parsed instanceof JsonObject root)) {
+                throw new IllegalArgumentException(
+                        "Tool JSON Schema must be a JSON object, but was: " + 
parsed.getClass().getSimpleName());
+            }
+            JsonSchemaElement element = 
JsonSchemaElementJsonUtils.fromMap(root);
+            if (element instanceof JsonObjectSchema objectSchema) {
+                return objectSchema;
+            }
+            JsonObject stripped = stripSchemaMetadata(root);
+            if (!stripped.equals(root)) {
+                element = JsonSchemaElementJsonUtils.fromMap(stripped);
+                if (element instanceof JsonObjectSchema objectSchema) {
+                    return objectSchema;
+                }
+            }
+            if (element instanceof JsonRawSchema rawSchema) {
+                throw new IllegalArgumentException(
+                        "Tool JSON Schema root could not be converted to 
JsonObjectSchema. "
+                                                   + "Remove unsupported root 
keywords such as $schema or schema-valued "
+                                                   + "additionalProperties, or 
simplify the root schema.");
+            }
+            throw new IllegalArgumentException(
+                    "Tool JSON Schema root must deserialize to 
JsonObjectSchema, but was: "
+                                               + 
element.getClass().getSimpleName());
+        } catch (DeserializationException e) {
+            throw new IllegalArgumentException("Tool JSON Schema is not valid 
JSON", e);
+        }
+    }
+
+    private static JsonObject stripSchemaMetadata(JsonObject root) {
+        JsonObject copy = new JsonObject();
+        for (Map.Entry<String, Object> entry : root.entrySet()) {
+            String key = entry.getKey();
+            if ("$schema".equals(key) || "$id".equals(key) || 
"$defs".equals(key) || "definitions".equals(key)) {
+                continue;
+            }
+            copy.put(key, entry.getValue());
+        }
+        return copy;
+    }
+
     private static JsonObjectSchema buildSchema(Map<String, 
AiToolParameterHelper.ParameterDef> defs) {
         JsonObjectSchema.Builder schemaBuilder = JsonObjectSchema.builder();
         List<String> required = new ArrayList<>();
diff --git 
a/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/AiToolSpecToLangChain4jTest.java
 
b/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/AiToolSpecToLangChain4jTest.java
index c6bb8e509677..ae8d51b65465 100644
--- 
a/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/AiToolSpecToLangChain4jTest.java
+++ 
b/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/AiToolSpecToLangChain4jTest.java
@@ -30,6 +30,7 @@ import 
org.apache.camel.component.ai.tool.AiToolParameterHelper;
 import org.apache.camel.component.ai.tool.AiToolSpec;
 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.assertInstanceOf;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -160,6 +161,42 @@ class AiToolSpecToLangChain4jTest {
         assertEquals(1, result.parameters().required().size());
     }
 
+    @Test
+    void testRawArgSchemaConversion() {
+        String schema = """
+                {
+                  "type": "object",
+                  "properties": {
+                    "customer": {
+                      "type": "object",
+                      "properties": {
+                        "id": { "type": "string" }
+                      },
+                      "required": ["id"]
+                    },
+                    "items": {
+                      "type": "array",
+                      "items": {
+                        "type": "object",
+                        "properties": {
+                          "sku": { "type": "string" },
+                          "qty": { "type": "integer" }
+                        }
+                      }
+                    }
+                  },
+                  "required": ["customer", "items"]
+                }
+                """;
+
+        AiToolSpec spec = new AiToolSpec("createOrder", "Create order", 
Map.of(), schema, null);
+        ToolSpecification result = 
AiToolSpecToLangChain4j.toToolSpecification(spec);
+
+        assertThat(result.parameters()).isNotNull();
+        
assertThat(result.parameters().properties()).hasSize(2).containsKeys("customer",
 "items");
+        assertThat(result.parameters().required()).contains("customer", 
"items");
+    }
+
     @Test
     void testEmptyParameters() {
         AiToolSpec spec = new AiToolSpec("noParams", "Tool with no params", 
Map.of(), null, null);
diff --git 
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/AiToolComponentBuilderFactory.java
 
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/AiToolComponentBuilderFactory.java
index c21d5d719391..b91bac833c9d 100644
--- 
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/AiToolComponentBuilderFactory.java
+++ 
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/AiToolComponentBuilderFactory.java
@@ -52,6 +52,27 @@ public interface AiToolComponentBuilderFactory {
      */
     interface AiToolComponentBuilder extends ComponentBuilder<AiToolComponent> 
{
     
+        /**
+         * Raw JSON Schema for tool input parameters. Supports inline JSON and
+         * resource references (classpath:, file:, resource:). Mutually
+         * exclusive with the parameter multi-value options. Use for nested
+         * objects, arrays, oneOf, and other complex schemas.
+         * 
+         * This option can also be loaded from an existing file, by prefixing
+         * with file: or classpath: followed by the location of the file.
+         * 
+         * The option is a: &lt;code&gt;java.lang.String&lt;/code&gt; type.
+         * 
+         * Group: consumer
+         * 
+         * @param argSchema the value to set
+         * @return the dsl builder
+         */
+        default AiToolComponentBuilder argSchema(java.lang.String argSchema) {
+            doSetProperty("argSchema", argSchema);
+            return this;
+        }
+    
         
         /**
          * Allows for bridging the consumer to the Camel routing Error Handler,
@@ -117,8 +138,8 @@ public interface AiToolComponentBuilderFactory {
          * Tool input parameters. Format: parameter.NAME=TYPE,
          * parameter.NAME.description=TEXT, parameter.NAME.required=true or
          * false, parameter.NAME.enum=val1,val2. Supported types: string,
-         * integer, number, boolean. This is a multi-value option with prefix:
-         * parameter.
+         * integer, number, boolean. Mutually exclusive with argSchema. This is
+         * a multi-value option with prefix: parameter.
          * 
          * The option is a: &lt;code&gt;java.util.Map&amp;lt;java.lang.String,
          * java.lang.String&amp;gt;&lt;/code&gt; type.
@@ -193,6 +214,7 @@ public interface AiToolComponentBuilderFactory {
                 String name,
                 Object value) {
             switch (name) {
+            case "argSchema": getOrCreateConfiguration((AiToolComponent) 
component).setArgSchema((java.lang.String) value); return true;
             case "bridgeErrorHandler": ((AiToolComponent) 
component).setBridgeErrorHandler((boolean) value); return true;
             case "configuration": ((AiToolComponent) 
component).setConfiguration((org.apache.camel.component.ai.tool.AiToolConfiguration)
 value); return true;
             case "description": getOrCreateConfiguration((AiToolComponent) 
component).setDescription((java.lang.String) value); return true;
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AiToolEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AiToolEndpointBuilderFactory.java
index e0828c82a002..abf21d5ff39e 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AiToolEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AiToolEndpointBuilderFactory.java
@@ -45,6 +45,26 @@ public interface AiToolEndpointBuilderFactory {
             return (AdvancedAiToolEndpointBuilder) this;
         }
 
+        /**
+         * Raw JSON Schema for tool input parameters. Supports inline JSON and
+         * resource references (classpath:, file:, resource:). Mutually
+         * exclusive with the parameter multi-value options. Use for nested
+         * objects, arrays, oneOf, and other complex schemas.
+         * 
+         * This option can also be loaded from an existing file, by prefixing
+         * with file: or classpath: followed by the location of the file.
+         * 
+         * The option is a: <code>java.lang.String</code> type.
+         * 
+         * Group: consumer
+         * 
+         * @param argSchema the value to set
+         * @return the dsl builder
+         */
+        default AiToolEndpointBuilder argSchema(String argSchema) {
+            doSetProperty("argSchema", argSchema);
+            return this;
+        }
         /**
          * Human-readable description of what this tool does. Passed verbatim 
to
          * the LLM; be precise and action-oriented. When omitted, defaults to
@@ -65,8 +85,8 @@ public interface AiToolEndpointBuilderFactory {
          * Tool input parameters. Format: parameter.NAME=TYPE,
          * parameter.NAME.description=TEXT, parameter.NAME.required=true or
          * false, parameter.NAME.enum=val1,val2. Supported types: string,
-         * integer, number, boolean. This is a multi-value option with prefix:
-         * parameter.
+         * integer, number, boolean. Mutually exclusive with argSchema. This is
+         * a multi-value option with prefix: parameter.
          * 
          * The option is a: <code>java.util.Map&lt;java.lang.String,
          * java.lang.String&gt;</code> type.
@@ -88,8 +108,8 @@ public interface AiToolEndpointBuilderFactory {
          * Tool input parameters. Format: parameter.NAME=TYPE,
          * parameter.NAME.description=TEXT, parameter.NAME.required=true or
          * false, parameter.NAME.enum=val1,val2. Supported types: string,
-         * integer, number, boolean. This is a multi-value option with prefix:
-         * parameter.
+         * integer, number, boolean. Mutually exclusive with argSchema. This is
+         * a multi-value option with prefix: parameter.
          * 
          * The option is a: <code>java.util.Map&lt;java.lang.String,
          * java.lang.String&gt;</code> type.

Reply via email to