Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,62 @@ Supported options: `title`, `readOnlyHint`, `destructiveHint`, `idempotentHint`,

The `title` option maps to the MCP tool's top-level `title` field (not `ToolAnnotations.title`). Boolean hint options use nullable configuration: when unset, Camel omits them from the published MCP tool even though the component catalog may list `defaultValue: false` for documentation tooling.

=== Tool Arguments in the Body

By default, tool arguments are exposed as individual headers and the message body is left unchanged.
Set `argumentsAsBody=true` to also populate the body with a JSON string containing the tool-call arguments.
This is useful when mapping a complete JSON document, for example with Kaoto Data Mapper.

[source,yaml]
----
- route:
from:
uri: ai-tool:place_order
parameters:
description: Place a merchandise order
argSchema: classpath:place-order.args.schema.json
argumentsAsBody: true
steps:
- to:
uri: direct:mapOrder
----

For a tool call with a customer name and two items, `direct:mapOrder` receives this JSON-string body:

[source,json]
----
{
"customerName": "Alice",
"items": [
{ "sku": "TSHIRT", "quantity": 2 },
{ "sku": "MUG", "quantity": 1 }
]
}
----

Nested objects, arrays and JSON value types are preserved. A call with no arguments produces `{}`.
The body and individual argument headers include only declared arguments, after the same allowlist filtering.
Camel logs a warning for each undeclared argument. Reserved names starting with `camel` or `org.apache.camel.`
(case-insensitive) are excluded from both the body and argument headers. Missing required arguments still reject
an invocation before the tool route runs. Required arguments are checked after reserved names are removed,
so a required argument with a reserved name rejects the call even if the model supplied it.
Rename such arguments to use names outside the reserved namespace. This option does not add full JSON Schema validation.

=== Invocation Identifiers

Tool routes receive `CamelAiToolCallId` and `CamelAiToolConversationId` when the calling adapter can supply them.
These headers are populated independently of `argumentsAsBody`. An unavailable identifier is omitted rather than generated.

* LangChain4j Agent supplies the tool execution request ID and the memory ID (converted to a string) as the conversation ID when the agent uses memory.
Agents without memory omit the conversation header, including the framework's default memory ID.
* OpenAI supplies the tool-call ID. The Responses API also supplies its resolved conversation ID
(`CamelOpenAIConversationId` or the `conversationId` option); chat-completion callbacks omit it.
* Spring AI Chat supplies the `CamelSpringAiChatConversationId` from the calling exchange. Its tool callback does not supply a call ID.
* MCP server tool callbacks currently do not propagate these identifiers.

For example, use `${header.CamelAiToolCallId}` to correlate route processing with a particular tool invocation.
Several calls in the same conversation can share `CamelAiToolConversationId` while having different call IDs.

=== Tag-Based Discovery

Tags group tools so that AI producers can select relevant subsets.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public boolean configure(CamelContext camelContext, Object obj, String name, Obj
switch (ignoreCase ? name.toLowerCase() : name) {
case "argschema":
case "argSchema": getOrCreateConfiguration(target).setArgSchema(property(camelContext, java.lang.String.class, value)); return true;
case "argumentsasbody":
case "argumentsAsBody": getOrCreateConfiguration(target).setArgumentsAsBody(property(camelContext, boolean.class, value)); return true;
case "authorizationpolicy":
case "authorizationPolicy": getOrCreateConfiguration(target).setAuthorizationPolicy(property(camelContext, org.apache.camel.spi.AuthorizationPolicy.class, value)); return true;
case "autowiredenabled":
Expand Down Expand Up @@ -66,6 +68,8 @@ public Class<?> getOptionType(String name, boolean ignoreCase) {
switch (ignoreCase ? name.toLowerCase() : name) {
case "argschema":
case "argSchema": return java.lang.String.class;
case "argumentsasbody":
case "argumentsAsBody": return boolean.class;
case "authorizationpolicy":
case "authorizationPolicy": return org.apache.camel.spi.AuthorizationPolicy.class;
case "autowiredenabled":
Expand Down Expand Up @@ -101,6 +105,8 @@ public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
switch (ignoreCase ? name.toLowerCase() : name) {
case "argschema":
case "argSchema": return getOrCreateConfiguration(target).getArgSchema();
case "argumentsasbody":
case "argumentsAsBody": return getOrCreateConfiguration(target).isArgumentsAsBody();
case "authorizationpolicy":
case "authorizationPolicy": return getOrCreateConfiguration(target).getAuthorizationPolicy();
case "autowiredenabled":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public boolean configure(CamelContext camelContext, Object obj, String name, Obj
switch (ignoreCase ? name.toLowerCase() : name) {
case "argschema":
case "argSchema": target.setArgSchema(property(camelContext, java.lang.String.class, value)); return true;
case "argumentsasbody":
case "argumentsAsBody": target.setArgumentsAsBody(property(camelContext, boolean.class, value)); return true;
case "authorizationpolicy":
case "authorizationPolicy": target.setAuthorizationPolicy(property(camelContext, org.apache.camel.spi.AuthorizationPolicy.class, value)); return true;
case "description": target.setDescription(property(camelContext, java.lang.String.class, value)); return true;
Expand Down Expand Up @@ -54,6 +56,8 @@ public Class<?> getOptionType(String name, boolean ignoreCase) {
switch (ignoreCase ? name.toLowerCase() : name) {
case "argschema":
case "argSchema": return java.lang.String.class;
case "argumentsasbody":
case "argumentsAsBody": return boolean.class;
case "authorizationpolicy":
case "authorizationPolicy": return org.apache.camel.spi.AuthorizationPolicy.class;
case "description": return java.lang.String.class;
Expand Down Expand Up @@ -84,6 +88,8 @@ public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
switch (ignoreCase ? name.toLowerCase() : name) {
case "argschema":
case "argSchema": return target.getArgSchema();
case "argumentsasbody":
case "argumentsAsBody": return target.isArgumentsAsBody();
case "authorizationpolicy":
case "authorizationPolicy": return target.getAuthorizationPolicy();
case "description": return target.getDescription();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public boolean configure(CamelContext camelContext, Object obj, String name, Obj
switch (ignoreCase ? name.toLowerCase() : name) {
case "argschema":
case "argSchema": target.getConfiguration().setArgSchema(property(camelContext, java.lang.String.class, value)); return true;
case "argumentsasbody":
case "argumentsAsBody": target.getConfiguration().setArgumentsAsBody(property(camelContext, boolean.class, value)); return true;
case "authorizationpolicy":
case "authorizationPolicy": target.getConfiguration().setAuthorizationPolicy(property(camelContext, org.apache.camel.spi.AuthorizationPolicy.class, value)); return true;
case "bridgeerrorhandler":
Expand Down Expand Up @@ -60,6 +62,8 @@ public Class<?> getOptionType(String name, boolean ignoreCase) {
switch (ignoreCase ? name.toLowerCase() : name) {
case "argschema":
case "argSchema": return java.lang.String.class;
case "argumentsasbody":
case "argumentsAsBody": return boolean.class;
case "authorizationpolicy":
case "authorizationPolicy": return org.apache.camel.spi.AuthorizationPolicy.class;
case "bridgeerrorhandler":
Expand Down Expand Up @@ -96,6 +100,8 @@ public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
switch (ignoreCase ? name.toLowerCase() : name) {
case "argschema":
case "argSchema": return target.getConfiguration().getArgSchema();
case "argumentsasbody":
case "argumentsAsBody": return target.getConfiguration().isArgumentsAsBody();
case "authorizationpolicy":
case "authorizationPolicy": return target.getConfiguration().getAuthorizationPolicy();
case "bridgeerrorhandler":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ 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<>(17);
Set<String> props = new HashSet<>(18);
props.add("argSchema");
props.add("argumentsAsBody");
props.add("authorizationPolicy");
props.add("bridgeErrorHandler");
props.add("description");
Expand Down
Loading
Loading