props = new HashSet<>(66);
props.add("additionalBodyProperty");
props.add("additionalHeader");
props.add("additionalResponseHeader");
@@ -86,6 +86,7 @@ public class OpenAIEndpointUriFactory extends org.apache.camel.support.component
props.add("streaming");
props.add("stripThinking");
props.add("systemMessage");
+ props.add("tags");
props.add("temperature");
props.add("toolExecutionErrorStrategy");
props.add("topP");
diff --git a/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/openai.json b/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/openai.json
index 75596ffafff6b..7bddb08018fd9 100644
--- a/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/openai.json
+++ b/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/openai.json
@@ -133,23 +133,24 @@
"streaming": { "index": 44, "kind": "parameter", "displayName": "Streaming", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Enable streaming responses" },
"stripThinking": { "index": 45, "kind": "parameter", "displayName": "Strip Thinking", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Strip ... blocks from model responses (used by reasoning models like Qwen3, DeepSeek-R1). The thinking content is stored in the CamelOpenAIThinkingContent header." },
"systemMessage": { "index": 46, "kind": "parameter", "displayName": "System Message", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "System message to prepend. When set and conversationMemory is enabled, the conversation history is reset." },
- "temperature": { "index": 47, "kind": "parameter", "displayName": "Temperature", "group": "producer", "label": "", "required": false, "type": "number", "javaType": "java.lang.Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Temperature for response generation (0.0 to 2.0)" },
- "toolExecutionErrorStrategy": { "index": 48, "kind": "parameter", "displayName": "Tool Execution Error Strategy", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.openai.ToolExecutionErrorStrategy", "enum": [ "failExchange", "repromptModel" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "failExchange", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Strategy for handling exceptions thrown during MCP tool execution. 'failExchange' (default) propagates the exception to the Camel exchange so that standard Camel error handling (onException, dead-letter channel) can process it. This is the safer default because 'repromptModel' sends raw exception messages (which may contain connection strings, hostnames, or internal paths) to a third-party LLM provider. 'repromptModel' catches the error and sends it back to the model as a tool result so the model can attempt to recover." },
- "topP": { "index": 49, "kind": "parameter", "displayName": "Top P", "group": "producer", "label": "", "required": false, "type": "number", "javaType": "java.lang.Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Top P for response generation (0.0 to 1.0)" },
- "userMessage": { "index": 50, "kind": "parameter", "displayName": "User Message", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Default user message text to use when no prompt is provided" },
- "lazyStartProducer": { "index": 51, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." },
- "oauthProfile": { "index": 52, "kind": "parameter", "displayName": "Oauth Profile", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "OAuth profile name for obtaining an access token via the OAuth 2.0 Client Credentials grant. When set, the token is acquired from the configured identity provider and used instead of apiKey. Requires camel-oauth on the classpath. The profile properties are resolved from camel.oauth..client-id, camel.oauth..client-secret, and camel.oauth..token-endpoint." },
- "sslContextParameters": { "index": 53, "kind": "parameter", "displayName": "Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "SSLContextParameters to use for configuring SSL\/TLS. When set, takes precedence over the individual sslTruststore, sslKeystore, and sslProtocol options." },
- "sslEndpointAlgorithm": { "index": 54, "kind": "parameter", "displayName": "Ssl Endpoint Algorithm", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "security": "insecure:ssl", "insecureValue": "none", "defaultValue": "https", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The endpoint identification algorithm to validate the server hostname using the server certificate. Set to an empty string or 'none' to disable hostname verification" },
- "sslKeymanagerAlgorithm": { "index": 55, "kind": "parameter", "displayName": "Ssl Keymanager Algorithm", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "SunX509", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The algorithm used by the key manager factory for SSL connections" },
- "sslKeyPassword": { "index": 56, "kind": "parameter", "displayName": "Ssl Key Password", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The password of the private key in the key store file" },
- "sslKeystoreLocation": { "index": 57, "kind": "parameter", "displayName": "Ssl Keystore Location", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The location of the key store file. This is optional and can be used for two-way authentication for the OpenAI API" },
- "sslKeystorePassword": { "index": 58, "kind": "parameter", "displayName": "Ssl Keystore Password", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The store password for the key store file" },
- "sslKeystoreType": { "index": 59, "kind": "parameter", "displayName": "Ssl Keystore Type", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "JKS", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The file format of the key store file" },
- "sslProtocol": { "index": 60, "kind": "parameter", "displayName": "Ssl Protocol", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "TLSv1.3", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The SSL protocol used to generate the SSLContext" },
- "sslTrustmanagerAlgorithm": { "index": 61, "kind": "parameter", "displayName": "Ssl Trustmanager Algorithm", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "PKIX", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The algorithm used by the trust manager factory for SSL connections" },
- "sslTruststoreLocation": { "index": 62, "kind": "parameter", "displayName": "Ssl Truststore Location", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The location of the trust store file, used to validate the server's certificate" },
- "sslTruststorePassword": { "index": 63, "kind": "parameter", "displayName": "Ssl Truststore Password", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The password for the trust store file. If a password is not set, the configured trust store can still be used, but integrity checking is disabled" },
- "sslTruststoreType": { "index": 64, "kind": "parameter", "displayName": "Ssl Truststore Type", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "JKS", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The file format of the trust store file" }
+ "tags": { "index": 47, "kind": "parameter", "displayName": "Tags", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Comma-separated tags to filter tools from the shared AiToolRegistry. Tools registered via ai-tool: consumer endpoints with matching tags are discovered and exposed as OpenAI function-calling tools alongside any MCP tools. Tools with no tags (default pool) are always included." },
+ "temperature": { "index": 48, "kind": "parameter", "displayName": "Temperature", "group": "producer", "label": "", "required": false, "type": "number", "javaType": "java.lang.Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Temperature for response generation (0.0 to 2.0)" },
+ "toolExecutionErrorStrategy": { "index": 49, "kind": "parameter", "displayName": "Tool Execution Error Strategy", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.openai.ToolExecutionErrorStrategy", "enum": [ "failExchange", "repromptModel" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "failExchange", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Strategy for handling exceptions thrown during MCP tool execution. 'failExchange' (default) propagates the exception to the Camel exchange so that standard Camel error handling (onException, dead-letter channel) can process it. This is the safer default because 'repromptModel' sends raw exception messages (which may contain connection strings, hostnames, or internal paths) to a third-party LLM provider. 'repromptModel' catches the error and sends it back to the model as a tool result so the model can attempt to recover." },
+ "topP": { "index": 50, "kind": "parameter", "displayName": "Top P", "group": "producer", "label": "", "required": false, "type": "number", "javaType": "java.lang.Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Top P for response generation (0.0 to 1.0)" },
+ "userMessage": { "index": 51, "kind": "parameter", "displayName": "User Message", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Default user message text to use when no prompt is provided" },
+ "lazyStartProducer": { "index": 52, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." },
+ "oauthProfile": { "index": 53, "kind": "parameter", "displayName": "Oauth Profile", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "OAuth profile name for obtaining an access token via the OAuth 2.0 Client Credentials grant. When set, the token is acquired from the configured identity provider and used instead of apiKey. Requires camel-oauth on the classpath. The profile properties are resolved from camel.oauth..client-id, camel.oauth..client-secret, and camel.oauth..token-endpoint." },
+ "sslContextParameters": { "index": 54, "kind": "parameter", "displayName": "Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "SSLContextParameters to use for configuring SSL\/TLS. When set, takes precedence over the individual sslTruststore, sslKeystore, and sslProtocol options." },
+ "sslEndpointAlgorithm": { "index": 55, "kind": "parameter", "displayName": "Ssl Endpoint Algorithm", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "security": "insecure:ssl", "insecureValue": "none", "defaultValue": "https", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The endpoint identification algorithm to validate the server hostname using the server certificate. Set to an empty string or 'none' to disable hostname verification" },
+ "sslKeymanagerAlgorithm": { "index": 56, "kind": "parameter", "displayName": "Ssl Keymanager Algorithm", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "SunX509", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The algorithm used by the key manager factory for SSL connections" },
+ "sslKeyPassword": { "index": 57, "kind": "parameter", "displayName": "Ssl Key Password", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The password of the private key in the key store file" },
+ "sslKeystoreLocation": { "index": 58, "kind": "parameter", "displayName": "Ssl Keystore Location", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The location of the key store file. This is optional and can be used for two-way authentication for the OpenAI API" },
+ "sslKeystorePassword": { "index": 59, "kind": "parameter", "displayName": "Ssl Keystore Password", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The store password for the key store file" },
+ "sslKeystoreType": { "index": 60, "kind": "parameter", "displayName": "Ssl Keystore Type", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "JKS", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The file format of the key store file" },
+ "sslProtocol": { "index": 61, "kind": "parameter", "displayName": "Ssl Protocol", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "TLSv1.3", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The SSL protocol used to generate the SSLContext" },
+ "sslTrustmanagerAlgorithm": { "index": 62, "kind": "parameter", "displayName": "Ssl Trustmanager Algorithm", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "PKIX", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The algorithm used by the trust manager factory for SSL connections" },
+ "sslTruststoreLocation": { "index": 63, "kind": "parameter", "displayName": "Ssl Truststore Location", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The location of the trust store file, used to validate the server's certificate" },
+ "sslTruststorePassword": { "index": 64, "kind": "parameter", "displayName": "Ssl Truststore Password", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The password for the trust store file. If a password is not set, the configured trust store can still be used, but integrity checking is disabled" },
+ "sslTruststoreType": { "index": 65, "kind": "parameter", "displayName": "Ssl Truststore Type", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "JKS", "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The file format of the trust store file" }
}
}
diff --git a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/AiToolSpecToOpenAI.java b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/AiToolSpecToOpenAI.java
new file mode 100644
index 0000000000000..bb29b9c2e0edb
--- /dev/null
+++ b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/AiToolSpecToOpenAI.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.openai;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.openai.core.JsonValue;
+import com.openai.models.FunctionDefinition;
+import com.openai.models.FunctionParameters;
+import com.openai.models.chat.completions.ChatCompletionFunctionTool;
+import org.apache.camel.component.ai.tool.AiToolSpec;
+
+/**
+ * Converts {@link AiToolSpec} instances to OpenAI {@link ChatCompletionFunctionTool} objects.
+ *
+ * Uses the pre-built JSON Schema string from {@link AiToolSpec#getParametersJsonSchema()} and parses it into the OpenAI
+ * SDK's {@link FunctionParameters} format via {@link JsonValue#from(Object)}.
+ */
+final class AiToolSpecToOpenAI {
+
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+ private static final TypeReference