This is an automated email from the ASF dual-hosted git repository.
gnodet 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 2a1a4e117d52 CAMEL-23963: configurable strategies for tool errors and
hallucinated tool names
2a1a4e117d52 is described below
commit 2a1a4e117d52c4aabf8e7ef21577479c7b082e7c
Author: Guillaume Nodet <[email protected]>
AuthorDate: Thu Jul 30 17:54:37 2026 +0200
CAMEL-23963: configurable strategies for tool errors and hallucinated tool
names
Add two new endpoint options to the OpenAI component's agentic loop:
- toolExecutionErrorStrategy: controls whether MCP tool execution errors
propagate to the exchange (failExchange, default) or are sent back to
the model (repromptModel).
- hallucinatedToolNameStrategy: controls whether hallucinated tool names
fail the exchange (failExchange, default) or send a corrective result
listing available tools (repromptModel).
The default failExchange is the safer choice because repromptModel sends
raw exception messages (which may contain connection strings, hostnames,
or internal paths) to a third-party LLM provider.
Closes #25216
Co-authored-by: Claude Opus 4.6 <[email protected]>
---
.../apache/camel/catalog/components/openai.json | 86 +++---
.../org/apache/camel/catalog/docs/openai-mcp.adoc | 23 +-
.../component/openai/OpenAIEndpointConfigurer.java | 12 +
.../component/openai/OpenAIEndpointUriFactory.java | 4 +-
.../org/apache/camel/component/openai/openai.json | 86 +++---
.../camel-openai/src/main/docs/openai-mcp.adoc | 23 +-
.../openai/HallucinatedToolNameStrategy.java | 36 +++
.../component/openai/OpenAIConfiguration.java | 33 +++
.../camel/component/openai/OpenAIProducer.java | 20 +-
.../openai/OpenAIToolExecutionProducer.java | 24 +-
.../openai/ToolExecutionErrorStrategy.java | 38 +++
...penAIAgenticLoopMalformedToolArgumentsTest.java | 8 +-
.../openai/OpenAIProducerMcpMockTest.java | 17 +-
.../openai/OpenAIToolErrorStrategyTest.java | 289 +++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_22.adoc | 17 ++
.../endpoint/dsl/OpenAIEndpointBuilderFactory.java | 90 +++++++
16 files changed, 703 insertions(+), 103 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openai.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openai.json
index f0c60e6ac00c..75596ffafff6 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openai.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openai.json
@@ -107,47 +107,49 @@
"embeddingModel": { "index": 18, "kind": "parameter", "displayName":
"Embedding Model", "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": "The model to use for
embeddings" },
"encodingFormat": { "index": 19, "kind": "parameter", "displayName":
"Encoding Format", "group": "producer", "label": "", "required": false, "type":
"enum", "javaType": "java.lang.String", "enum": [ "float", "base64" ],
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "defaultValue": "base64", "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "The format for embedding ou [...]
"fileSearchVectorStoreIds": { "index": 20, "kind": "parameter",
"displayName": "File Search Vector Store Ids", "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 vector store ids required when
builtinTools [...]
- "hostedMcpTools": { "index": 21, "kind": "parameter", "displayName":
"Hosted Mcp Tools", "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": "JSON array of hosted MCP
tool definitions (OpenAI Tool.Mcp) passed through to the [...]
- "jsonSchema": { "index": 22, "kind": "parameter", "displayName": "Json
Schema", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "supportFileReference": true,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "JSON schema for
structured output validation" },
- "maxAgenticTokens": { "index": 23, "kind": "parameter", "displayName":
"Max Agentic Tokens", "group": "producer", "label": "", "required": false,
"type": "integer", "javaType": "long", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "defaultValue": 0,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "Maximum cumulative
prompt plus completion tokens allowed across the M [...]
- "maxHistoryMessages": { "index": 24, "kind": "parameter", "displayName":
"Max History Messages", "group": "producer", "label": "", "required": false,
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "defaultValue": 0,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "When conversationMemory
is enabled, retain at most this many messa [...]
- "maxHistoryTokens": { "index": 25, "kind": "parameter", "displayName":
"Max History Tokens", "group": "producer", "label": "", "required": false,
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "defaultValue": 0,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "When conversationMemory
is enabled, trim conversation history using a [...]
- "maxRetries": { "index": 26, "kind": "parameter", "displayName": "Max
Retries", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "int", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": 2, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "Maximum number of times the OpenAI SDK client
retries failed requests. The SDK retr [...]
- "maxTokens": { "index": 27, "kind": "parameter", "displayName": "Max
Tokens", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "java.lang.Integer", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "Maximum number of tokens
to generate" },
- "maxToolIterations": { "index": 28, "kind": "parameter", "displayName":
"Max Tool Iterations", "group": "producer", "label": "", "required": false,
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "defaultValue": 50,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "Maximum number of tool
call loop iterations to prevent infinite loops" },
- "mcpProtocolVersions": { "index": 29, "kind": "parameter", "displayName":
"Mcp Protocol Versions", "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 list of
MCP protocol versions to advertise when connect [...]
- "mcpReconnect": { "index": 30, "kind": "parameter", "displayName": "Mcp
Reconnect", "group": "producer", "label": "", "required": false, "type":
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": true,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "Automatically reconnect
to MCP servers when a tool call fails due to a t [...]
- "mcpServer": { "index": 31, "kind": "parameter", "displayName": "Mcp
Server", "group": "producer", "label": "", "required": false, "type": "object",
"javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"mcpServer.", "multiValue": true, "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "MCP (Model Cont [...]
- "mcpTimeout": { "index": 32, "kind": "parameter", "displayName": "Mcp
Timeout", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "int", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": 20, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "Timeout in seconds for MCP tool call requests.
Applies to all MCP operations inclu [...]
- "model": { "index": 33, "kind": "parameter", "displayName": "Model",
"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": "The model to use for chat completion" },
- "outputClass": { "index": 34, "kind": "parameter", "displayName": "Output
Class", "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": "Fully qualified class name for structured
output using response format" },
- "previousResponseId": { "index": 35, "kind": "parameter", "displayName":
"Previous Response Id", "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": "Previous response id for
OpenAI server-side conversation state (Responses [...]
- "requestTimeout": { "index": 36, "kind": "parameter", "displayName":
"Request Timeout", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "long", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": 0, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "HTTP request timeout in milliseconds for the
OpenAI SDK client. When 0 or [...]
- "speechInstructions": { "index": 37, "kind": "parameter", "displayName":
"Speech Instructions", "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": "Optional instructions to
control the voice of the generated audio. Does no [...]
- "speechModel": { "index": 38, "kind": "parameter", "displayName": "Speech
Model", "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": "The model to use for text-to-speech (e.g.,
gpt-4o-mini-tts, tts-1, tts-1-hd)" },
- "speechResponseFormat": { "index": 39, "kind": "parameter", "displayName":
"Speech Response Format", "group": "producer", "label": "", "required": false,
"type": "enum", "javaType": "java.lang.String", "enum": [ "mp3", "opus", "aac",
"flac", "wav", "pcm" ], "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": "mp3",
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "descrip [...]
- "speechSpeed": { "index": 40, "kind": "parameter", "displayName": "Speech
Speed", "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": "The speed of the generated audio, from 0.25 to
4.0 where 1.0 is normal speed" },
- "speechVoice": { "index": 41, "kind": "parameter", "displayName": "Speech
Voice", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": "alloy",
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "The voice to use for
text-to-speech (e.g., alloy, echo, fable, [...]
- "storeFullResponse": { "index": 42, "kind": "parameter", "displayName":
"Store Full Response", "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": "Store the full SDK response in non-streaming
mode: chat-comp [...]
- "streaming": { "index": 43, "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": 44, "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 [...]
- "systemMessage": { "index": 45, "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 conversat [...]
- "temperature": { "index": 46, "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)" },
- "topP": { "index": 47, "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": 48, "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": 49, "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 produ [...]
- "oauthProfile": { "index": 50, "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 Cred [...]
- "sslContextParameters": { "index": 51, "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 [...]
- "sslEndpointAlgorithm": { "index": 52, "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", [...]
- "sslKeymanagerAlgorithm": { "index": 53, "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 [...]
- "sslKeyPassword": { "index": 54, "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": 55, "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 [...]
- "sslKeystorePassword": { "index": 56, "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": 57, "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": 58, "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": 59, "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 tru [...]
- "sslTruststoreLocation": { "index": 60, "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 [...]
- "sslTruststorePassword": { "index": 61, "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. [...]
- "sslTruststoreType": { "index": 62, "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" }
+ "hallucinatedToolNameStrategy": { "index": 21, "kind": "parameter",
"displayName": "Hallucinated Tool Name Strategy", "group": "producer", "label":
"", "required": false, "type": "enum", "javaType":
"org.apache.camel.component.openai.HallucinatedToolNameStrategy", "enum": [
"failExchange", "repromptModel" ], "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": "failExchange",
"configurationClass": "org.apache.camel.component.openai.OpenAICo [...]
+ "hostedMcpTools": { "index": 22, "kind": "parameter", "displayName":
"Hosted Mcp Tools", "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": "JSON array of hosted MCP
tool definitions (OpenAI Tool.Mcp) passed through to the [...]
+ "jsonSchema": { "index": 23, "kind": "parameter", "displayName": "Json
Schema", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "supportFileReference": true,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "JSON schema for
structured output validation" },
+ "maxAgenticTokens": { "index": 24, "kind": "parameter", "displayName":
"Max Agentic Tokens", "group": "producer", "label": "", "required": false,
"type": "integer", "javaType": "long", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "defaultValue": 0,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "Maximum cumulative
prompt plus completion tokens allowed across the M [...]
+ "maxHistoryMessages": { "index": 25, "kind": "parameter", "displayName":
"Max History Messages", "group": "producer", "label": "", "required": false,
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "defaultValue": 0,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "When conversationMemory
is enabled, retain at most this many messa [...]
+ "maxHistoryTokens": { "index": 26, "kind": "parameter", "displayName":
"Max History Tokens", "group": "producer", "label": "", "required": false,
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "defaultValue": 0,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "When conversationMemory
is enabled, trim conversation history using a [...]
+ "maxRetries": { "index": 27, "kind": "parameter", "displayName": "Max
Retries", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "int", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": 2, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "Maximum number of times the OpenAI SDK client
retries failed requests. The SDK retr [...]
+ "maxTokens": { "index": 28, "kind": "parameter", "displayName": "Max
Tokens", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "java.lang.Integer", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "Maximum number of tokens
to generate" },
+ "maxToolIterations": { "index": 29, "kind": "parameter", "displayName":
"Max Tool Iterations", "group": "producer", "label": "", "required": false,
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "defaultValue": 50,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "Maximum number of tool
call loop iterations to prevent infinite loops" },
+ "mcpProtocolVersions": { "index": 30, "kind": "parameter", "displayName":
"Mcp Protocol Versions", "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 list of
MCP protocol versions to advertise when connect [...]
+ "mcpReconnect": { "index": 31, "kind": "parameter", "displayName": "Mcp
Reconnect", "group": "producer", "label": "", "required": false, "type":
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": true,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "Automatically reconnect
to MCP servers when a tool call fails due to a t [...]
+ "mcpServer": { "index": 32, "kind": "parameter", "displayName": "Mcp
Server", "group": "producer", "label": "", "required": false, "type": "object",
"javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"mcpServer.", "multiValue": true, "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "MCP (Model Cont [...]
+ "mcpTimeout": { "index": 33, "kind": "parameter", "displayName": "Mcp
Timeout", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "int", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": 20, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "Timeout in seconds for MCP tool call requests.
Applies to all MCP operations inclu [...]
+ "model": { "index": 34, "kind": "parameter", "displayName": "Model",
"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": "The model to use for chat completion" },
+ "outputClass": { "index": 35, "kind": "parameter", "displayName": "Output
Class", "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": "Fully qualified class name for structured
output using response format" },
+ "previousResponseId": { "index": 36, "kind": "parameter", "displayName":
"Previous Response Id", "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": "Previous response id for
OpenAI server-side conversation state (Responses [...]
+ "requestTimeout": { "index": 37, "kind": "parameter", "displayName":
"Request Timeout", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "long", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": 0, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "HTTP request timeout in milliseconds for the
OpenAI SDK client. When 0 or [...]
+ "speechInstructions": { "index": 38, "kind": "parameter", "displayName":
"Speech Instructions", "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": "Optional instructions to
control the voice of the generated audio. Does no [...]
+ "speechModel": { "index": 39, "kind": "parameter", "displayName": "Speech
Model", "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": "The model to use for text-to-speech (e.g.,
gpt-4o-mini-tts, tts-1, tts-1-hd)" },
+ "speechResponseFormat": { "index": 40, "kind": "parameter", "displayName":
"Speech Response Format", "group": "producer", "label": "", "required": false,
"type": "enum", "javaType": "java.lang.String", "enum": [ "mp3", "opus", "aac",
"flac", "wav", "pcm" ], "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": "mp3",
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "descrip [...]
+ "speechSpeed": { "index": 41, "kind": "parameter", "displayName": "Speech
Speed", "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": "The speed of the generated audio, from 0.25 to
4.0 where 1.0 is normal speed" },
+ "speechVoice": { "index": 42, "kind": "parameter", "displayName": "Speech
Voice", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": "alloy",
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "The voice to use for
text-to-speech (e.g., alloy, echo, fable, [...]
+ "storeFullResponse": { "index": 43, "kind": "parameter", "displayName":
"Store Full Response", "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": "Store the full SDK response in non-streaming
mode: chat-comp [...]
+ "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 [...]
+ "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 conversat [...]
+ "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.OpenAIConfigur [...]
+ "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 produ [...]
+ "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 Cred [...]
+ "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 [...]
+ "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", [...]
+ "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 [...]
+ "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 [...]
+ "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 tru [...]
+ "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 [...]
+ "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. [...]
+ "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" }
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openai-mcp.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openai-mcp.adoc
index 8635736c2da3..4b8160b3bf2e 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openai-mcp.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openai-mcp.adoc
@@ -580,15 +580,32 @@ YAML::
=== Error Handling in the Agentic Loop
+Two endpoint options control what happens when a tool call fails:
+
+* **`toolExecutionErrorStrategy`** (default `failExchange`): controls what
happens when a tool
+ execution throws an exception (including malformed tool arguments).
+ - `failExchange` -- 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, logs a WARN, and sends it back to
the model as a tool
+ result so the model can attempt recovery.
+* **`hallucinatedToolNameStrategy`** (default `failExchange`): controls what
happens when the
+ model requests a tool that does not exist in any configured MCP server.
+ - `failExchange` -- throws an `IllegalStateException`, failing the exchange
immediately.
+ - `repromptModel` -- sends a corrective tool result listing the available
tools so the model
+ can self-correct and retry with a valid tool name. The `maxToolIterations`
option bounds
+ the number of retries.
+
[cols="1,3"]
|===
| Scenario | Behavior
| MCP client initialization failure | Route fails to start (`RuntimeException`
during `doStart()`)
-| Tool execution throws an exception | Error is caught, logged as WARN, and
sent as tool result text to the model
-| MCP transport error (`mcpReconnect=true`) | Automatic reconnection and retry
(once). If retry fails, error is sent to the model
+| Tool execution throws an exception | Governed by
`toolExecutionErrorStrategy`: `failExchange` (default) propagates the exception
to the exchange; `repromptModel` catches the error, logs it as WARN, and sends
it as tool result text to the model
+| MCP transport error (`mcpReconnect=true`) | Automatic reconnection and retry
(once). If retry fails, behavior follows `toolExecutionErrorStrategy`
| MCP `CallToolResult.isError()` is true | Error content is sent as tool
result text to the model
-| Tool name not found in any server | `IllegalStateException` is thrown
+| Tool name not found in any server | Governed by
`hallucinatedToolNameStrategy`: `failExchange` (default) throws
`IllegalStateException`; `repromptModel` sends a corrective tool result listing
available tools
| Max iterations exceeded | `IllegalStateException` is thrown with the tool
call log
| Streaming + MCP tools with autoToolExecution | Falls back to non-streaming
(logged as INFO)
|===
diff --git
a/components/camel-ai/camel-openai/src/generated/java/org/apache/camel/component/openai/OpenAIEndpointConfigurer.java
b/components/camel-ai/camel-openai/src/generated/java/org/apache/camel/component/openai/OpenAIEndpointConfigurer.java
index 3949023299e3..825570b9a22b 100644
---
a/components/camel-ai/camel-openai/src/generated/java/org/apache/camel/component/openai/OpenAIEndpointConfigurer.java
+++
b/components/camel-ai/camel-openai/src/generated/java/org/apache/camel/component/openai/OpenAIEndpointConfigurer.java
@@ -62,6 +62,8 @@ public class OpenAIEndpointConfigurer extends
PropertyConfigurerSupport implemen
case "encodingFormat":
target.getConfiguration().setEncodingFormat(property(camelContext,
java.lang.String.class, value)); return true;
case "filesearchvectorstoreids":
case "fileSearchVectorStoreIds":
target.getConfiguration().setFileSearchVectorStoreIds(property(camelContext,
java.lang.String.class, value)); return true;
+ case "hallucinatedtoolnamestrategy":
+ case "hallucinatedToolNameStrategy":
target.getConfiguration().setHallucinatedToolNameStrategy(property(camelContext,
org.apache.camel.component.openai.HallucinatedToolNameStrategy.class, value));
return true;
case "hostedmcptools":
case "hostedMcpTools":
target.getConfiguration().setHostedMcpTools(property(camelContext,
java.lang.String.class, value)); return true;
case "jsonschema":
@@ -139,6 +141,8 @@ public class OpenAIEndpointConfigurer extends
PropertyConfigurerSupport implemen
case "systemmessage":
case "systemMessage":
target.getConfiguration().setSystemMessage(property(camelContext,
java.lang.String.class, value)); return true;
case "temperature":
target.getConfiguration().setTemperature(property(camelContext,
java.lang.Double.class, value)); return true;
+ case "toolexecutionerrorstrategy":
+ case "toolExecutionErrorStrategy":
target.getConfiguration().setToolExecutionErrorStrategy(property(camelContext,
org.apache.camel.component.openai.ToolExecutionErrorStrategy.class, value));
return true;
case "topp":
case "topP": target.getConfiguration().setTopP(property(camelContext,
java.lang.Double.class, value)); return true;
case "usermessage":
@@ -189,6 +193,8 @@ public class OpenAIEndpointConfigurer extends
PropertyConfigurerSupport implemen
case "encodingFormat": return java.lang.String.class;
case "filesearchvectorstoreids":
case "fileSearchVectorStoreIds": return java.lang.String.class;
+ case "hallucinatedtoolnamestrategy":
+ case "hallucinatedToolNameStrategy": return
org.apache.camel.component.openai.HallucinatedToolNameStrategy.class;
case "hostedmcptools":
case "hostedMcpTools": return java.lang.String.class;
case "jsonschema":
@@ -266,6 +272,8 @@ public class OpenAIEndpointConfigurer extends
PropertyConfigurerSupport implemen
case "systemmessage":
case "systemMessage": return java.lang.String.class;
case "temperature": return java.lang.Double.class;
+ case "toolexecutionerrorstrategy":
+ case "toolExecutionErrorStrategy": return
org.apache.camel.component.openai.ToolExecutionErrorStrategy.class;
case "topp":
case "topP": return java.lang.Double.class;
case "usermessage":
@@ -317,6 +325,8 @@ public class OpenAIEndpointConfigurer extends
PropertyConfigurerSupport implemen
case "encodingFormat": return
target.getConfiguration().getEncodingFormat();
case "filesearchvectorstoreids":
case "fileSearchVectorStoreIds": return
target.getConfiguration().getFileSearchVectorStoreIds();
+ case "hallucinatedtoolnamestrategy":
+ case "hallucinatedToolNameStrategy": return
target.getConfiguration().getHallucinatedToolNameStrategy();
case "hostedmcptools":
case "hostedMcpTools": return
target.getConfiguration().getHostedMcpTools();
case "jsonschema":
@@ -394,6 +404,8 @@ public class OpenAIEndpointConfigurer extends
PropertyConfigurerSupport implemen
case "systemmessage":
case "systemMessage": return
target.getConfiguration().getSystemMessage();
case "temperature": return target.getConfiguration().getTemperature();
+ case "toolexecutionerrorstrategy":
+ case "toolExecutionErrorStrategy": return
target.getConfiguration().getToolExecutionErrorStrategy();
case "topp":
case "topP": return target.getConfiguration().getTopP();
case "usermessage":
diff --git
a/components/camel-ai/camel-openai/src/generated/java/org/apache/camel/component/openai/OpenAIEndpointUriFactory.java
b/components/camel-ai/camel-openai/src/generated/java/org/apache/camel/component/openai/OpenAIEndpointUriFactory.java
index ab83d5467df2..60bf0d2c653d 100644
---
a/components/camel-ai/camel-openai/src/generated/java/org/apache/camel/component/openai/OpenAIEndpointUriFactory.java
+++
b/components/camel-ai/camel-openai/src/generated/java/org/apache/camel/component/openai/OpenAIEndpointUriFactory.java
@@ -24,7 +24,7 @@ public class OpenAIEndpointUriFactory 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<>(63);
+ Set<String> props = new HashSet<>(65);
props.add("additionalBodyProperty");
props.add("additionalHeader");
props.add("additionalResponseHeader");
@@ -45,6 +45,7 @@ public class OpenAIEndpointUriFactory extends
org.apache.camel.support.component
props.add("embeddingModel");
props.add("encodingFormat");
props.add("fileSearchVectorStoreIds");
+ props.add("hallucinatedToolNameStrategy");
props.add("hostedMcpTools");
props.add("jsonSchema");
props.add("lazyStartProducer");
@@ -86,6 +87,7 @@ public class OpenAIEndpointUriFactory extends
org.apache.camel.support.component
props.add("stripThinking");
props.add("systemMessage");
props.add("temperature");
+ props.add("toolExecutionErrorStrategy");
props.add("topP");
props.add("userMessage");
PROPERTY_NAMES = Collections.unmodifiableSet(props);
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 f0c60e6ac00c..75596ffafff6 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
@@ -107,47 +107,49 @@
"embeddingModel": { "index": 18, "kind": "parameter", "displayName":
"Embedding Model", "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": "The model to use for
embeddings" },
"encodingFormat": { "index": 19, "kind": "parameter", "displayName":
"Encoding Format", "group": "producer", "label": "", "required": false, "type":
"enum", "javaType": "java.lang.String", "enum": [ "float", "base64" ],
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "defaultValue": "base64", "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "The format for embedding ou [...]
"fileSearchVectorStoreIds": { "index": 20, "kind": "parameter",
"displayName": "File Search Vector Store Ids", "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 vector store ids required when
builtinTools [...]
- "hostedMcpTools": { "index": 21, "kind": "parameter", "displayName":
"Hosted Mcp Tools", "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": "JSON array of hosted MCP
tool definitions (OpenAI Tool.Mcp) passed through to the [...]
- "jsonSchema": { "index": 22, "kind": "parameter", "displayName": "Json
Schema", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "supportFileReference": true,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "JSON schema for
structured output validation" },
- "maxAgenticTokens": { "index": 23, "kind": "parameter", "displayName":
"Max Agentic Tokens", "group": "producer", "label": "", "required": false,
"type": "integer", "javaType": "long", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "defaultValue": 0,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "Maximum cumulative
prompt plus completion tokens allowed across the M [...]
- "maxHistoryMessages": { "index": 24, "kind": "parameter", "displayName":
"Max History Messages", "group": "producer", "label": "", "required": false,
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "defaultValue": 0,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "When conversationMemory
is enabled, retain at most this many messa [...]
- "maxHistoryTokens": { "index": 25, "kind": "parameter", "displayName":
"Max History Tokens", "group": "producer", "label": "", "required": false,
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "defaultValue": 0,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "When conversationMemory
is enabled, trim conversation history using a [...]
- "maxRetries": { "index": 26, "kind": "parameter", "displayName": "Max
Retries", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "int", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": 2, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "Maximum number of times the OpenAI SDK client
retries failed requests. The SDK retr [...]
- "maxTokens": { "index": 27, "kind": "parameter", "displayName": "Max
Tokens", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "java.lang.Integer", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "Maximum number of tokens
to generate" },
- "maxToolIterations": { "index": 28, "kind": "parameter", "displayName":
"Max Tool Iterations", "group": "producer", "label": "", "required": false,
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "defaultValue": 50,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "Maximum number of tool
call loop iterations to prevent infinite loops" },
- "mcpProtocolVersions": { "index": 29, "kind": "parameter", "displayName":
"Mcp Protocol Versions", "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 list of
MCP protocol versions to advertise when connect [...]
- "mcpReconnect": { "index": 30, "kind": "parameter", "displayName": "Mcp
Reconnect", "group": "producer", "label": "", "required": false, "type":
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": true,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "Automatically reconnect
to MCP servers when a tool call fails due to a t [...]
- "mcpServer": { "index": 31, "kind": "parameter", "displayName": "Mcp
Server", "group": "producer", "label": "", "required": false, "type": "object",
"javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"mcpServer.", "multiValue": true, "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "MCP (Model Cont [...]
- "mcpTimeout": { "index": 32, "kind": "parameter", "displayName": "Mcp
Timeout", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "int", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": 20, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "Timeout in seconds for MCP tool call requests.
Applies to all MCP operations inclu [...]
- "model": { "index": 33, "kind": "parameter", "displayName": "Model",
"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": "The model to use for chat completion" },
- "outputClass": { "index": 34, "kind": "parameter", "displayName": "Output
Class", "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": "Fully qualified class name for structured
output using response format" },
- "previousResponseId": { "index": 35, "kind": "parameter", "displayName":
"Previous Response Id", "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": "Previous response id for
OpenAI server-side conversation state (Responses [...]
- "requestTimeout": { "index": 36, "kind": "parameter", "displayName":
"Request Timeout", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "long", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": 0, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "HTTP request timeout in milliseconds for the
OpenAI SDK client. When 0 or [...]
- "speechInstructions": { "index": 37, "kind": "parameter", "displayName":
"Speech Instructions", "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": "Optional instructions to
control the voice of the generated audio. Does no [...]
- "speechModel": { "index": 38, "kind": "parameter", "displayName": "Speech
Model", "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": "The model to use for text-to-speech (e.g.,
gpt-4o-mini-tts, tts-1, tts-1-hd)" },
- "speechResponseFormat": { "index": 39, "kind": "parameter", "displayName":
"Speech Response Format", "group": "producer", "label": "", "required": false,
"type": "enum", "javaType": "java.lang.String", "enum": [ "mp3", "opus", "aac",
"flac", "wav", "pcm" ], "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": "mp3",
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "descrip [...]
- "speechSpeed": { "index": 40, "kind": "parameter", "displayName": "Speech
Speed", "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": "The speed of the generated audio, from 0.25 to
4.0 where 1.0 is normal speed" },
- "speechVoice": { "index": 41, "kind": "parameter", "displayName": "Speech
Voice", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": "alloy",
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "The voice to use for
text-to-speech (e.g., alloy, echo, fable, [...]
- "storeFullResponse": { "index": 42, "kind": "parameter", "displayName":
"Store Full Response", "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": "Store the full SDK response in non-streaming
mode: chat-comp [...]
- "streaming": { "index": 43, "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": 44, "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 [...]
- "systemMessage": { "index": 45, "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 conversat [...]
- "temperature": { "index": 46, "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)" },
- "topP": { "index": 47, "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": 48, "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": 49, "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 produ [...]
- "oauthProfile": { "index": 50, "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 Cred [...]
- "sslContextParameters": { "index": 51, "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 [...]
- "sslEndpointAlgorithm": { "index": 52, "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", [...]
- "sslKeymanagerAlgorithm": { "index": 53, "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 [...]
- "sslKeyPassword": { "index": 54, "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": 55, "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 [...]
- "sslKeystorePassword": { "index": 56, "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": 57, "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": 58, "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": 59, "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 tru [...]
- "sslTruststoreLocation": { "index": 60, "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 [...]
- "sslTruststorePassword": { "index": 61, "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. [...]
- "sslTruststoreType": { "index": 62, "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" }
+ "hallucinatedToolNameStrategy": { "index": 21, "kind": "parameter",
"displayName": "Hallucinated Tool Name Strategy", "group": "producer", "label":
"", "required": false, "type": "enum", "javaType":
"org.apache.camel.component.openai.HallucinatedToolNameStrategy", "enum": [
"failExchange", "repromptModel" ], "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": "failExchange",
"configurationClass": "org.apache.camel.component.openai.OpenAICo [...]
+ "hostedMcpTools": { "index": 22, "kind": "parameter", "displayName":
"Hosted Mcp Tools", "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": "JSON array of hosted MCP
tool definitions (OpenAI Tool.Mcp) passed through to the [...]
+ "jsonSchema": { "index": 23, "kind": "parameter", "displayName": "Json
Schema", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "supportFileReference": true,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "JSON schema for
structured output validation" },
+ "maxAgenticTokens": { "index": 24, "kind": "parameter", "displayName":
"Max Agentic Tokens", "group": "producer", "label": "", "required": false,
"type": "integer", "javaType": "long", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "defaultValue": 0,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "Maximum cumulative
prompt plus completion tokens allowed across the M [...]
+ "maxHistoryMessages": { "index": 25, "kind": "parameter", "displayName":
"Max History Messages", "group": "producer", "label": "", "required": false,
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "defaultValue": 0,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "When conversationMemory
is enabled, retain at most this many messa [...]
+ "maxHistoryTokens": { "index": 26, "kind": "parameter", "displayName":
"Max History Tokens", "group": "producer", "label": "", "required": false,
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "defaultValue": 0,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "When conversationMemory
is enabled, trim conversation history using a [...]
+ "maxRetries": { "index": 27, "kind": "parameter", "displayName": "Max
Retries", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "int", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": 2, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "Maximum number of times the OpenAI SDK client
retries failed requests. The SDK retr [...]
+ "maxTokens": { "index": 28, "kind": "parameter", "displayName": "Max
Tokens", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "java.lang.Integer", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "Maximum number of tokens
to generate" },
+ "maxToolIterations": { "index": 29, "kind": "parameter", "displayName":
"Max Tool Iterations", "group": "producer", "label": "", "required": false,
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "defaultValue": 50,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "Maximum number of tool
call loop iterations to prevent infinite loops" },
+ "mcpProtocolVersions": { "index": 30, "kind": "parameter", "displayName":
"Mcp Protocol Versions", "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 list of
MCP protocol versions to advertise when connect [...]
+ "mcpReconnect": { "index": 31, "kind": "parameter", "displayName": "Mcp
Reconnect", "group": "producer", "label": "", "required": false, "type":
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": true,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "Automatically reconnect
to MCP servers when a tool call fails due to a t [...]
+ "mcpServer": { "index": 32, "kind": "parameter", "displayName": "Mcp
Server", "group": "producer", "label": "", "required": false, "type": "object",
"javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"mcpServer.", "multiValue": true, "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "MCP (Model Cont [...]
+ "mcpTimeout": { "index": 33, "kind": "parameter", "displayName": "Mcp
Timeout", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "int", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": 20, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "Timeout in seconds for MCP tool call requests.
Applies to all MCP operations inclu [...]
+ "model": { "index": 34, "kind": "parameter", "displayName": "Model",
"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": "The model to use for chat completion" },
+ "outputClass": { "index": 35, "kind": "parameter", "displayName": "Output
Class", "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": "Fully qualified class name for structured
output using response format" },
+ "previousResponseId": { "index": 36, "kind": "parameter", "displayName":
"Previous Response Id", "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": "Previous response id for
OpenAI server-side conversation state (Responses [...]
+ "requestTimeout": { "index": 37, "kind": "parameter", "displayName":
"Request Timeout", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "long", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": 0, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "HTTP request timeout in milliseconds for the
OpenAI SDK client. When 0 or [...]
+ "speechInstructions": { "index": 38, "kind": "parameter", "displayName":
"Speech Instructions", "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": "Optional instructions to
control the voice of the generated audio. Does no [...]
+ "speechModel": { "index": 39, "kind": "parameter", "displayName": "Speech
Model", "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": "The model to use for text-to-speech (e.g.,
gpt-4o-mini-tts, tts-1, tts-1-hd)" },
+ "speechResponseFormat": { "index": 40, "kind": "parameter", "displayName":
"Speech Response Format", "group": "producer", "label": "", "required": false,
"type": "enum", "javaType": "java.lang.String", "enum": [ "mp3", "opus", "aac",
"flac", "wav", "pcm" ], "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": "mp3",
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "descrip [...]
+ "speechSpeed": { "index": 41, "kind": "parameter", "displayName": "Speech
Speed", "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": "The speed of the generated audio, from 0.25 to
4.0 where 1.0 is normal speed" },
+ "speechVoice": { "index": 42, "kind": "parameter", "displayName": "Speech
Voice", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": "alloy",
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "The voice to use for
text-to-speech (e.g., alloy, echo, fable, [...]
+ "storeFullResponse": { "index": 43, "kind": "parameter", "displayName":
"Store Full Response", "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": "Store the full SDK response in non-streaming
mode: chat-comp [...]
+ "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 [...]
+ "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 conversat [...]
+ "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.OpenAIConfigur [...]
+ "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 produ [...]
+ "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 Cred [...]
+ "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 [...]
+ "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", [...]
+ "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 [...]
+ "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 [...]
+ "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 tru [...]
+ "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 [...]
+ "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. [...]
+ "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" }
}
}
diff --git a/components/camel-ai/camel-openai/src/main/docs/openai-mcp.adoc
b/components/camel-ai/camel-openai/src/main/docs/openai-mcp.adoc
index 8635736c2da3..4b8160b3bf2e 100644
--- a/components/camel-ai/camel-openai/src/main/docs/openai-mcp.adoc
+++ b/components/camel-ai/camel-openai/src/main/docs/openai-mcp.adoc
@@ -580,15 +580,32 @@ YAML::
=== Error Handling in the Agentic Loop
+Two endpoint options control what happens when a tool call fails:
+
+* **`toolExecutionErrorStrategy`** (default `failExchange`): controls what
happens when a tool
+ execution throws an exception (including malformed tool arguments).
+ - `failExchange` -- 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, logs a WARN, and sends it back to
the model as a tool
+ result so the model can attempt recovery.
+* **`hallucinatedToolNameStrategy`** (default `failExchange`): controls what
happens when the
+ model requests a tool that does not exist in any configured MCP server.
+ - `failExchange` -- throws an `IllegalStateException`, failing the exchange
immediately.
+ - `repromptModel` -- sends a corrective tool result listing the available
tools so the model
+ can self-correct and retry with a valid tool name. The `maxToolIterations`
option bounds
+ the number of retries.
+
[cols="1,3"]
|===
| Scenario | Behavior
| MCP client initialization failure | Route fails to start (`RuntimeException`
during `doStart()`)
-| Tool execution throws an exception | Error is caught, logged as WARN, and
sent as tool result text to the model
-| MCP transport error (`mcpReconnect=true`) | Automatic reconnection and retry
(once). If retry fails, error is sent to the model
+| Tool execution throws an exception | Governed by
`toolExecutionErrorStrategy`: `failExchange` (default) propagates the exception
to the exchange; `repromptModel` catches the error, logs it as WARN, and sends
it as tool result text to the model
+| MCP transport error (`mcpReconnect=true`) | Automatic reconnection and retry
(once). If retry fails, behavior follows `toolExecutionErrorStrategy`
| MCP `CallToolResult.isError()` is true | Error content is sent as tool
result text to the model
-| Tool name not found in any server | `IllegalStateException` is thrown
+| Tool name not found in any server | Governed by
`hallucinatedToolNameStrategy`: `failExchange` (default) throws
`IllegalStateException`; `repromptModel` sends a corrective tool result listing
available tools
| Max iterations exceeded | `IllegalStateException` is thrown with the tool
call log
| Streaming + MCP tools with autoToolExecution | Falls back to non-streaming
(logged as INFO)
|===
diff --git
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/HallucinatedToolNameStrategy.java
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/HallucinatedToolNameStrategy.java
new file mode 100644
index 000000000000..fd05e709ec1c
--- /dev/null
+++
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/HallucinatedToolNameStrategy.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+/**
+ * Strategy for handling tool names hallucinated by the model (i.e. the model
requests a tool that does not exist in any
+ * configured MCP server).
+ */
+public enum HallucinatedToolNameStrategy {
+
+ /**
+ * Throw an {@link IllegalStateException}, failing the exchange
immediately. This is the default behavior.
+ */
+ FAIL_EXCHANGE,
+
+ /**
+ * Send a corrective tool result back to the model listing the available
tools, giving the model a chance to
+ * self-correct and retry with a valid tool name. The agentic loop's
{@code maxToolIterations} bounds the number of
+ * retries.
+ */
+ REPROMPT_MODEL
+}
diff --git
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIConfiguration.java
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIConfiguration.java
index ba8ea8f484dd..0c866ce17662 100644
---
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIConfiguration.java
+++
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIConfiguration.java
@@ -209,6 +209,23 @@ public class OpenAIConfiguration implements Cloneable {
+ "and retry the call once.")
private boolean mcpReconnect = true;
+ @UriParam(enums = "failExchange,repromptModel", defaultValue =
"failExchange")
+ @Metadata(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.")
+ private ToolExecutionErrorStrategy toolExecutionErrorStrategy =
ToolExecutionErrorStrategy.FAIL_EXCHANGE;
+
+ @UriParam(enums = "failExchange,repromptModel", defaultValue =
"failExchange")
+ @Metadata(description = "Strategy for handling tool names hallucinated by
the model (tool not found in any MCP server). "
+ + "'failExchange' (default) throws an
IllegalStateException, failing the exchange immediately. "
+ + "'repromptModel' sends a corrective tool result
listing the available tools so the model "
+ + "can self-correct and retry. The
maxToolIterations option bounds retries.")
+ private HallucinatedToolNameStrategy hallucinatedToolNameStrategy =
HallucinatedToolNameStrategy.FAIL_EXCHANGE;
+
// ========== EMBEDDINGS CONFIGURATION ==========
@UriParam
@@ -721,6 +738,22 @@ public class OpenAIConfiguration implements Cloneable {
this.mcpReconnect = mcpReconnect;
}
+ public ToolExecutionErrorStrategy getToolExecutionErrorStrategy() {
+ return toolExecutionErrorStrategy;
+ }
+
+ public void setToolExecutionErrorStrategy(ToolExecutionErrorStrategy
toolExecutionErrorStrategy) {
+ this.toolExecutionErrorStrategy = toolExecutionErrorStrategy;
+ }
+
+ public HallucinatedToolNameStrategy getHallucinatedToolNameStrategy() {
+ return hallucinatedToolNameStrategy;
+ }
+
+ public void setHallucinatedToolNameStrategy(HallucinatedToolNameStrategy
hallucinatedToolNameStrategy) {
+ this.hallucinatedToolNameStrategy = hallucinatedToolNameStrategy;
+ }
+
public SSLContextParameters getSslContextParameters() {
return sslContextParameters;
}
diff --git
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java
index 282e9b022311..d36c7d6e48e2 100644
---
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java
+++
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java
@@ -545,8 +545,18 @@ public class OpenAIProducer extends DefaultAsyncProducer {
McpToolState mcpToolState = getEndpoint().getMcpToolState();
McpSyncClient mcpClient =
mcpToolState.toolClientMap().get(toolName);
if (mcpClient == null) {
- throw new IllegalStateException(
- "Tool '" + toolName + "' not found in any
configured MCP server");
+ if (config.getHallucinatedToolNameStrategy() ==
HallucinatedToolNameStrategy.FAIL_EXCHANGE) {
+ throw new IllegalStateException(
+ "Tool '" + toolName + "' not found in any
configured MCP server");
+ }
+ // repromptModel: send a corrective tool result listing
available tools
+ String available = String.join(", ",
mcpToolState.toolClientMap().keySet());
+ String errorMsg = "Error: tool '" + toolName
+ + "' does not exist. Available tools: "
+ available;
+ LOG.warn("Hallucinated tool name '{}', sending corrective
result to model", toolName);
+ batchResults.add(new ToolResultEntry(toolCallId,
errorMsg));
+ allReturnDirect = false;
+ continue;
}
LOG.debug("Executing MCP tool '{}' with args: {}", toolName,
argsJson);
@@ -567,10 +577,16 @@ public class OpenAIProducer extends DefaultAsyncProducer {
}
}
} catch (JsonProcessingException e) {
+ if (config.getToolExecutionErrorStrategy() ==
ToolExecutionErrorStrategy.FAIL_EXCHANGE) {
+ throw e;
+ }
LOG.warn("Invalid tool arguments for '{}': {}", toolName,
argsJson, e);
resultContent = "Error: invalid tool arguments: " +
e.getMessage();
allReturnDirect = false;
} catch (Exception e) {
+ if (config.getToolExecutionErrorStrategy() ==
ToolExecutionErrorStrategy.FAIL_EXCHANGE) {
+ throw e;
+ }
LOG.warn("MCP tool '{}' execution failed: {}", toolName,
e.getMessage(), e);
resultContent = "Error: Tool execution failed: " +
e.getMessage();
allReturnDirect = false;
diff --git
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIToolExecutionProducer.java
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIToolExecutionProducer.java
index e5a01352dddf..a5313f0d7089 100644
---
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIToolExecutionProducer.java
+++
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIToolExecutionProducer.java
@@ -139,8 +139,22 @@ public class OpenAIToolExecutionProducer extends
DefaultProducer {
McpToolState mcpToolState = getEndpoint().getMcpToolState();
McpSyncClient mcpClient =
mcpToolState.toolClientMap().get(toolName);
if (mcpClient == null) {
- throw new IllegalStateException(
- "Tool '" + toolName + "' not found in any configured
MCP server");
+ if (config.getHallucinatedToolNameStrategy() ==
HallucinatedToolNameStrategy.FAIL_EXCHANGE) {
+ throw new IllegalStateException(
+ "Tool '" + toolName + "' not found in any
configured MCP server");
+ }
+ // repromptModel: send a corrective tool result listing
available tools
+ String available = String.join(", ",
mcpToolState.toolClientMap().keySet());
+ String errorMsg = "Error: tool '" + toolName
+ + "' does not exist. Available tools: " +
available;
+ LOG.warn("Hallucinated tool name '{}', sending corrective
result to model", toolName);
+ history.add(ChatCompletionMessageParam.ofTool(
+ ChatCompletionToolMessageParam.builder()
+ .toolCallId(toolCallId)
+ .content(errorMsg)
+ .build()));
+ executedCount++;
+ continue;
}
String resultContent;
@@ -157,9 +171,15 @@ public class OpenAIToolExecutionProducer extends
DefaultProducer {
resultContent = extractTextContent(toolResult.content());
}
} catch (JsonProcessingException e) {
+ if (config.getToolExecutionErrorStrategy() ==
ToolExecutionErrorStrategy.FAIL_EXCHANGE) {
+ throw e;
+ }
LOG.warn("Invalid tool arguments for '{}': {}", toolName,
argsJson, e);
resultContent = "Error: invalid tool arguments: " +
e.getMessage();
} catch (Exception e) {
+ if (config.getToolExecutionErrorStrategy() ==
ToolExecutionErrorStrategy.FAIL_EXCHANGE) {
+ throw e;
+ }
LOG.warn("MCP tool '{}' execution failed: {}", toolName,
e.getMessage(), e);
resultContent = "Error: Tool execution failed: " +
e.getMessage();
}
diff --git
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/ToolExecutionErrorStrategy.java
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/ToolExecutionErrorStrategy.java
new file mode 100644
index 000000000000..399283ffeb99
--- /dev/null
+++
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/ToolExecutionErrorStrategy.java
@@ -0,0 +1,38 @@
+/*
+ * 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;
+
+/**
+ * Strategy for handling exceptions thrown during MCP tool execution in the
agentic loop.
+ */
+public enum ToolExecutionErrorStrategy {
+
+ /**
+ * Propagate the exception to the Camel exchange so that standard Camel
error handling ({@code onException},
+ * dead-letter channel) can process it. This is the default behavior, and
the safer choice because
+ * {@code REPROMPT_MODEL} sends raw exception messages (which may contain
connection strings, hostnames, or internal
+ * paths) to a third-party LLM provider.
+ */
+ FAIL_EXCHANGE,
+
+ /**
+ * Catch the exception, format it as a tool result error message, and send
it back to the model so it can attempt to
+ * recover. Note that this sends raw {@code e.getMessage()} content to the
LLM provider, which may include internal
+ * details.
+ */
+ REPROMPT_MODEL
+}
diff --git
a/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIAgenticLoopMalformedToolArgumentsTest.java
b/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIAgenticLoopMalformedToolArgumentsTest.java
index e618b44731a9..0f01effc9154 100644
---
a/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIAgenticLoopMalformedToolArgumentsTest.java
+++
b/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIAgenticLoopMalformedToolArgumentsTest.java
@@ -54,7 +54,7 @@ import static org.mockito.Mockito.when;
* malformed arguments string crashes the whole exchange with a raw Jackson
{@code JsonParseException} instead of giving
* the model a chance to correct itself.
*/
-public class OpenAIAgenticLoopMalformedToolArgumentsTest extends
CamelTestSupport {
+class OpenAIAgenticLoopMalformedToolArgumentsTest extends CamelTestSupport {
private static final ObjectMapper MAPPER = new ObjectMapper();
@@ -84,7 +84,8 @@ public class OpenAIAgenticLoopMalformedToolArgumentsTest
extends CamelTestSuppor
@Override
public void configure() {
from("direct:mcp-chat")
-
.to("openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true&baseUrl="
+
.to("openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true"
+ +
"&toolExecutionErrorStrategy=repromptModel&baseUrl="
+ openAIMock.getBaseUrl() + "/v1");
}
};
@@ -99,7 +100,8 @@ public class OpenAIAgenticLoopMalformedToolArgumentsTest
extends CamelTestSuppor
.build();
when(client.callTool(any(McpSchema.CallToolRequest.class))).thenReturn(toolResult);
- String endpointUri =
"openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true&baseUrl="
+ String endpointUri =
"openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true"
+ +
"&toolExecutionErrorStrategy=repromptModel&baseUrl="
+ openAIMock.getBaseUrl() + "/v1";
OpenAIEndpoint endpoint = context.getEndpoint(endpointUri,
OpenAIEndpoint.class);
List<McpSchema.Tool> mcpTools = List.of(
diff --git
a/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIProducerMcpMockTest.java
b/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIProducerMcpMockTest.java
index d329bd6835ae..a59aa751ffad 100644
---
a/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIProducerMcpMockTest.java
+++
b/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIProducerMcpMockTest.java
@@ -41,7 +41,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-public class OpenAIProducerMcpMockTest extends CamelTestSupport {
+class OpenAIProducerMcpMockTest extends CamelTestSupport {
@RegisterExtension
public OpenAIMock openAIMock = new OpenAIMock().builder()
@@ -92,6 +92,11 @@ public class OpenAIProducerMcpMockTest extends
CamelTestSupport {
from("direct:mcp-chat-max1")
.to("openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true&maxToolIterations=1&baseUrl="
+ openAIMock.getBaseUrl() + "/v1");
+
+ from("direct:mcp-chat-reprompt")
+
.to("openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true"
+ +
"&toolExecutionErrorStrategy=repromptModel&baseUrl="
+ + openAIMock.getBaseUrl() + "/v1");
}
};
}
@@ -201,7 +206,8 @@ public class OpenAIProducerMcpMockTest extends
CamelTestSupport {
@Test
void toolExecutionErrorSentToModel() {
- String endpointUri =
"openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true&baseUrl="
+ String endpointUri =
"openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true"
+ +
"&toolExecutionErrorStrategy=repromptModel&baseUrl="
+ openAIMock.getBaseUrl() + "/v1";
Map<String, McpSyncClient> toolClients = new HashMap<>();
@@ -209,7 +215,7 @@ public class OpenAIProducerMcpMockTest extends
CamelTestSupport {
injectMcpTools(endpointUri, toolClients);
// The error is caught and sent back to the model; the mock responds
with final text
- Exchange result = template.request("direct:mcp-chat", e ->
e.getIn().setBody("call one tool"));
+ Exchange result = template.request("direct:mcp-chat-reprompt", e ->
e.getIn().setBody("call one tool"));
// The model should still produce a final response (the mock handles
this)
assertNotNull(result.getMessage().getBody(String.class));
@@ -273,7 +279,8 @@ public class OpenAIProducerMcpMockTest extends
CamelTestSupport {
@Test
void returnDirectWithErrorFallsBackToModel() {
- String endpointUri =
"openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true&baseUrl="
+ String endpointUri =
"openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true"
+ +
"&toolExecutionErrorStrategy=repromptModel&baseUrl="
+ openAIMock.getBaseUrl() + "/v1";
Map<String, McpSyncClient> toolClients = new HashMap<>();
@@ -283,7 +290,7 @@ public class OpenAIProducerMcpMockTest extends
CamelTestSupport {
injectMcpTools(endpointUri, toolClients, returnDirect);
// Error should override returnDirect — results go back to model
- Exchange result = template.request("direct:mcp-chat", e ->
e.getIn().setBody("call one tool"));
+ Exchange result = template.request("direct:mcp-chat-reprompt", e ->
e.getIn().setBody("call one tool"));
assertNotNull(result.getMessage().getBody(String.class));
assertFalse(result.getMessage().getHeader(OpenAIConstants.MCP_RETURN_DIRECT,
Boolean.class));
diff --git
a/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIToolErrorStrategyTest.java
b/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIToolErrorStrategyTest.java
new file mode 100644
index 000000000000..9a1cbcad3df1
--- /dev/null
+++
b/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIToolErrorStrategyTest.java
@@ -0,0 +1,289 @@
+/*
+ * 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.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.modelcontextprotocol.client.McpSyncClient;
+import io.modelcontextprotocol.spec.McpSchema;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.infra.openai.mock.OpenAIMock;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for the configurable tool error strategies introduced by CAMEL-23963.
+ *
+ * <p>
+ * Two endpoint options are tested:
+ * <ul>
+ * <li>{@code hallucinatedToolNameStrategy} -- controls what happens when the
model requests a tool that does not exist
+ * in any configured MCP server.</li>
+ * <li>{@code toolExecutionErrorStrategy} -- controls what happens when a tool
execution throws an exception.</li>
+ * </ul>
+ */
+class OpenAIToolErrorStrategyTest extends CamelTestSupport {
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ private final AtomicInteger hallucinatedCallCounter = new AtomicInteger();
+ private final AtomicInteger toolErrorCallCounter = new AtomicInteger();
+ private final AtomicInteger repromptCallCounter = new AtomicInteger();
+
+ @RegisterExtension
+ public OpenAIMock openAIMock = new OpenAIMock().builder()
+ // hallucinated tool: first call returns a tool call with a
non-existent tool name,
+ // second call (after corrective feedback) returns the final answer
+ .when("use hallucinated tool")
+ .thenRespondWith((exchange, input) -> {
+ try {
+ if (hallucinatedCallCounter.getAndIncrement() == 0) {
+ return toolCallResponse("nonexistent_tool", "{\"arg\":
\"value\"}");
+ }
+ return simpleTextResponse("Recovered after hallucinated
tool correction");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ })
+ .end()
+ // tool execution error: first call returns a tool call that will
throw
+ .when("trigger tool error")
+ .thenRespondWith((exchange, input) -> {
+ try {
+ if (toolErrorCallCounter.getAndIncrement() == 0) {
+ return toolCallResponse("get_weather", "{\"city\":
\"London\"}");
+ }
+ return simpleTextResponse("Should not reach here");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ })
+ .end()
+ // tool execution error with repromptModel: first call triggers
error,
+ // second call (after error feedback) returns the final answer
+ .when("trigger tool error reprompt")
+ .thenRespondWith((exchange, input) -> {
+ try {
+ if (repromptCallCounter.getAndIncrement() == 0) {
+ return toolCallResponse("get_weather", "{\"city\":
\"London\"}");
+ }
+ return simpleTextResponse("Recovered after tool error
feedback");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ })
+ .end()
+ .build();
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ // Route with hallucinatedToolNameStrategy=repromptModel
+ from("direct:hallucinated-reprompt")
+
.to("openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true"
+ +
"&hallucinatedToolNameStrategy=repromptModel&baseUrl="
+ + openAIMock.getBaseUrl() + "/v1");
+
+ // Route with default toolExecutionErrorStrategy (failExchange)
+ from("direct:error-fail-exchange")
+
.to("openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true&baseUrl="
+ + openAIMock.getBaseUrl() + "/v1");
+
+ // Route with toolExecutionErrorStrategy=repromptModel
+ from("direct:error-reprompt")
+
.to("openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true"
+ +
"&toolExecutionErrorStrategy=repromptModel&baseUrl="
+ + openAIMock.getBaseUrl() + "/v1");
+ }
+ };
+ }
+
+ @Test
+ void hallucinatedToolNameRepromptModelSendsCorrectiveResultToModel() {
+ // Set up a real tool so the model can self-correct
+ McpSyncClient client = mock(McpSyncClient.class);
+ McpSchema.CallToolResult toolResult =
McpSchema.CallToolResult.builder()
+ .content(List.of(new McpSchema.TextContent(null, "Sunny",
null)))
+ .isError(false)
+ .build();
+
when(client.callTool(any(McpSchema.CallToolRequest.class))).thenReturn(toolResult);
+
+ String endpointUri =
"openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true"
+ +
"&hallucinatedToolNameStrategy=repromptModel&baseUrl="
+ + openAIMock.getBaseUrl() + "/v1";
+ OpenAIEndpoint endpoint = context.getEndpoint(endpointUri,
OpenAIEndpoint.class);
+ List<McpSchema.Tool> mcpTools = List.of(
+ McpSchema.Tool.builder("get_weather", Map.of("type", "object"))
+ .description("Mock tool: get_weather")
+ .build());
+ endpoint.setMcpToolState(new McpToolState(
+ McpToolConverter.convert(mcpTools),
+ Map.of("get_weather", client),
+ Map.of(),
+ Set.of()));
+
+ Exchange result = template.request("direct:hallucinated-reprompt",
+ e -> e.getIn().setBody("use hallucinated tool"));
+
+ assertThat(result.getException())
+ .as("With repromptModel, hallucinated tool names should not
crash the exchange")
+ .isNull();
+ assertThat(result.getMessage().getBody(String.class))
+ .isEqualTo("Recovered after hallucinated tool correction");
+ }
+
+ @Test
+ void hallucinatedToolNameDefaultFailExchangeThrowsException() {
+ // Use the default strategy (failExchange) via a separate endpoint
+ McpSyncClient client = mock(McpSyncClient.class);
+ String defaultUri =
"openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true&baseUrl="
+ + openAIMock.getBaseUrl() + "/v1";
+ OpenAIEndpoint endpoint = context.getEndpoint(defaultUri,
OpenAIEndpoint.class);
+ List<McpSchema.Tool> mcpTools = List.of(
+ McpSchema.Tool.builder("get_weather", Map.of("type", "object"))
+ .description("Mock tool: get_weather")
+ .build());
+ endpoint.setMcpToolState(new McpToolState(
+ McpToolConverter.convert(mcpTools),
+ Map.of("get_weather", client),
+ Map.of(),
+ Set.of()));
+
+ // The default hallucinatedToolNameStrategy is failExchange, so this
should throw
+ assertThatThrownBy(() -> template.requestBody(defaultUri, "use
hallucinated tool"))
+ .isInstanceOf(CamelExecutionException.class)
+ .hasCauseInstanceOf(IllegalStateException.class)
+ .cause()
+ .hasMessageContaining("not found in any configured MCP
server");
+ }
+
+ @Test
+ void toolExecutionErrorDefaultFailExchangePropagatesToExchange() {
+ McpSyncClient client = mock(McpSyncClient.class);
+ when(client.callTool(any(McpSchema.CallToolRequest.class)))
+ .thenThrow(new RuntimeException("Connection refused to weather
service"));
+
+ String endpointUri =
"openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true&baseUrl="
+ + openAIMock.getBaseUrl() + "/v1";
+ OpenAIEndpoint endpoint = context.getEndpoint(endpointUri,
OpenAIEndpoint.class);
+ List<McpSchema.Tool> mcpTools = List.of(
+ McpSchema.Tool.builder("get_weather", Map.of("type", "object"))
+ .description("Mock tool: get_weather")
+ .build());
+ endpoint.setMcpToolState(new McpToolState(
+ McpToolConverter.convert(mcpTools),
+ Map.of("get_weather", client),
+ Map.of(),
+ Set.of()));
+
+ // The default toolExecutionErrorStrategy is failExchange, so this
should propagate
+ assertThatThrownBy(() ->
template.requestBody("direct:error-fail-exchange", "trigger tool error"))
+ .isInstanceOf(CamelExecutionException.class)
+ .hasCauseInstanceOf(RuntimeException.class)
+ .cause()
+ .hasMessageContaining("Connection refused to weather service");
+ }
+
+ @Test
+ void toolExecutionErrorRepromptModelSendsErrorBackToModel() {
+ McpSyncClient client = mock(McpSyncClient.class);
+ when(client.callTool(any(McpSchema.CallToolRequest.class)))
+ .thenThrow(new RuntimeException("Connection refused to weather
service"));
+
+ String endpointUri =
"openai:chat-completion?model=gpt-5&apiKey=dummy&autoToolExecution=true"
+ +
"&toolExecutionErrorStrategy=repromptModel&baseUrl="
+ + openAIMock.getBaseUrl() + "/v1";
+ OpenAIEndpoint endpoint = context.getEndpoint(endpointUri,
OpenAIEndpoint.class);
+ List<McpSchema.Tool> mcpTools = List.of(
+ McpSchema.Tool.builder("get_weather", Map.of("type", "object"))
+ .description("Mock tool: get_weather")
+ .build());
+ endpoint.setMcpToolState(new McpToolState(
+ McpToolConverter.convert(mcpTools),
+ Map.of("get_weather", client),
+ Map.of(),
+ Set.of()));
+
+ Exchange result = template.request("direct:error-reprompt",
+ e -> e.getIn().setBody("trigger tool error reprompt"));
+
+ assertThat(result.getException())
+ .as("With repromptModel, tool execution errors should not
crash the exchange")
+ .isNull();
+ assertThat(result.getMessage().getBody(String.class))
+ .isEqualTo("Recovered after tool error feedback");
+ }
+
+ // ---------- helpers ----------
+
+ private static String toolCallResponse(String toolName, String arguments)
throws Exception {
+ Map<String, Object> function = new HashMap<>();
+ function.put("name", toolName);
+ function.put("arguments", arguments);
+
+ Map<String, Object> toolCall = new HashMap<>();
+ toolCall.put("id", UUID.randomUUID().toString());
+ toolCall.put("type", "function");
+ toolCall.put("function", function);
+
+ Map<String, Object> message = new HashMap<>();
+ message.put("role", "assistant");
+ message.put("content", null);
+ message.put("tool_calls", List.of(toolCall));
+
+ return chatCompletion(message, "tool_calls");
+ }
+
+ private static String simpleTextResponse(String content) throws Exception {
+ Map<String, Object> message = new HashMap<>();
+ message.put("role", "assistant");
+ message.put("content", content);
+ return chatCompletion(message, "stop");
+ }
+
+ private static String chatCompletion(Map<String, Object> message, String
finishReason) throws Exception {
+ Map<String, Object> choice = new HashMap<>();
+ choice.put("finish_reason", finishReason);
+ choice.put("index", 0);
+ choice.put("message", message);
+
+ Map<String, Object> completion = new HashMap<>();
+ completion.put("id", UUID.randomUUID().toString());
+ completion.put("choices", List.of(choice));
+ completion.put("created", System.currentTimeMillis() / 1000L);
+ completion.put("model", "openai-mock");
+ completion.put("object", "chat.completion");
+ return MAPPER.writeValueAsString(completion);
+ }
+}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
index fcd765269257..093597f13729 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
@@ -366,6 +366,23 @@ Component metadata for exchange property
`CamelOpenAIResponse` (`storeFullRespon
update imports and `exchange.getProperty(..., ChatCompletion.class)`
references if your code still
uses the old package name.
+==== Configurable tool error strategies in the agentic loop
+
+Two new endpoint options control how the agentic loop handles tool execution
errors and hallucinated
+tool names:
+
+* `toolExecutionErrorStrategy` — default `failExchange` (propagates exceptions
to the Camel exchange).
+ Set to `repromptModel` to catch exceptions and send them back to the model
as tool results so it can
+ attempt recovery. Note that `repromptModel` sends raw exception messages to
the LLM provider, which
+ may include internal details.
+* `hallucinatedToolNameStrategy` — default `failExchange` (throws
`IllegalStateException`). Set to
+ `repromptModel` to send a corrective tool result listing available tools.
+
+Previously, tool execution errors were always sent back to the model and
hallucinated tool names always
+threw an `IllegalStateException`. The new defaults change the tool execution
error behavior: errors now
+propagate to the exchange by default. Routes that rely on the model recovering
from tool errors must
+add `&toolExecutionErrorStrategy=repromptModel` to their endpoint URI.
+
The `conversationMemory` feature on the `chat-completion` operation has two
behavior fixes:
* User messages are now stored in the `CamelOpenAIConversationHistory`
exchange property (configurable via
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpenAIEndpointBuilderFactory.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpenAIEndpointBuilderFactory.java
index 62deec247e96..ce0c3e15890a 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpenAIEndpointBuilderFactory.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpenAIEndpointBuilderFactory.java
@@ -494,6 +494,48 @@ public interface OpenAIEndpointBuilderFactory {
doSetProperty("fileSearchVectorStoreIds",
fileSearchVectorStoreIds);
return this;
}
+ /**
+ * Strategy for handling tool names hallucinated by the model (tool not
+ * found in any MCP server). 'failExchange' (default) throws an
+ * IllegalStateException, failing the exchange immediately.
+ * 'repromptModel' sends a corrective tool result listing the available
+ * tools so the model can self-correct and retry. The maxToolIterations
+ * option bounds retries.
+ *
+ * The option is a:
+ *
<code>org.apache.camel.component.openai.HallucinatedToolNameStrategy</code>
type.
+ *
+ * Default: failExchange
+ * Group: producer
+ *
+ * @param hallucinatedToolNameStrategy the value to set
+ * @return the dsl builder
+ */
+ default OpenAIEndpointBuilder
hallucinatedToolNameStrategy(org.apache.camel.component.openai.HallucinatedToolNameStrategy
hallucinatedToolNameStrategy) {
+ doSetProperty("hallucinatedToolNameStrategy",
hallucinatedToolNameStrategy);
+ return this;
+ }
+ /**
+ * Strategy for handling tool names hallucinated by the model (tool not
+ * found in any MCP server). 'failExchange' (default) throws an
+ * IllegalStateException, failing the exchange immediately.
+ * 'repromptModel' sends a corrective tool result listing the available
+ * tools so the model can self-correct and retry. The maxToolIterations
+ * option bounds retries.
+ *
+ * The option will be converted to a
+ *
<code>org.apache.camel.component.openai.HallucinatedToolNameStrategy</code>
type.
+ *
+ * Default: failExchange
+ * Group: producer
+ *
+ * @param hallucinatedToolNameStrategy the value to set
+ * @return the dsl builder
+ */
+ default OpenAIEndpointBuilder hallucinatedToolNameStrategy(String
hallucinatedToolNameStrategy) {
+ doSetProperty("hallucinatedToolNameStrategy",
hallucinatedToolNameStrategy);
+ return this;
+ }
/**
* JSON array of hosted MCP tool definitions (OpenAI Tool.Mcp) passed
* through to the Responses API.
@@ -1186,6 +1228,54 @@ public interface OpenAIEndpointBuilderFactory {
doSetProperty("temperature", temperature);
return this;
}
+ /**
+ * 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.
+ *
+ * The option is a:
+ *
<code>org.apache.camel.component.openai.ToolExecutionErrorStrategy</code> type.
+ *
+ * Default: failExchange
+ * Group: producer
+ *
+ * @param toolExecutionErrorStrategy the value to set
+ * @return the dsl builder
+ */
+ default OpenAIEndpointBuilder
toolExecutionErrorStrategy(org.apache.camel.component.openai.ToolExecutionErrorStrategy
toolExecutionErrorStrategy) {
+ doSetProperty("toolExecutionErrorStrategy",
toolExecutionErrorStrategy);
+ return this;
+ }
+ /**
+ * 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.
+ *
+ * The option will be converted to a
+ *
<code>org.apache.camel.component.openai.ToolExecutionErrorStrategy</code> type.
+ *
+ * Default: failExchange
+ * Group: producer
+ *
+ * @param toolExecutionErrorStrategy the value to set
+ * @return the dsl builder
+ */
+ default OpenAIEndpointBuilder toolExecutionErrorStrategy(String
toolExecutionErrorStrategy) {
+ doSetProperty("toolExecutionErrorStrategy",
toolExecutionErrorStrategy);
+ return this;
+ }
/**
* Top P for response generation (0.0 to 1.0).
*