Skip to content

CAMEL-25985: Expose AI tool JSON arguments and invocation identifiers - #28295

Merged
davsclaus merged 3 commits into
apache:mainfrom
Croway:feature/CAMEL-25985-ai-tool-arguments
Oct 22, 2026
Merged

davsclaus merged 3 commits into
apache:mainfrom
Croway:feature/CAMEL-25985-ai-tool-arguments

Conversation

@Croway

@Croway Croway commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Description

Tool routes currently receive arguments only as individual headers, requiring users to reconstruct JSON for tools such as Kaoto Data Mapper. Add argumentsAsBody (default false) to populate the body with the complete tool arguments as a JSON string, preserving nested objects, arrays and null values.

The body and individual headers use the same declared-argument allowlist. Undeclared arguments are filtered out with a warning. Reserved argument names are excluded from both before required-argument validation, so a required reserved argument rejects the call before the route runs. Missing required arguments also reject the call.

Propagate CamelAiToolCallId and CamelAiToolConversationId independently of the body option wherever adapters supply them. LangChain4j supplies the call ID and propagates the memory ID only for agents with memory, including an intentional ID of default. OpenAI supplies the call ID and, for the Responses API, the resolved conversation ID from configuration or headers. Spring AI supplies the conversation ID. Unavailable identifiers are omitted.

Update the component documentation and regenerate its catalog copy, component metadata, configurers, and Java DSLs.

Target

  • Targets main.

Tracking

CAMEL-25985

Validation

Latest main validation: all 201 selected tests passed (145 AI Tool tests, 9 LangChain4j concurrency/filter tests, and 47 OpenAI executor/Responses tests). Coverage includes body allowlisting, required reserved arguments, memory-less agents, an intentional default memory ID, Responses configuration/header precedence, and parallel tool calls. The two Spring AI invocation tests passed earlier.

The applicable changes were also tested on the local 4.22.x backport: 113 AI Tool tests and 9 LangChain4j tests passed. The newer Responses tool loop is not part of that branch.

Formatting and import sorting ran through the Maven lifecycle. Catalog generation and DSL generation succeeded for both branches; the catalog's AI Tool documentation matches its source exactly.

AI-assisted contributions

AI-assisted with Codex on behalf of @Croway.

@Croway Croway added the enhancement New feature or request label Oct 22, 2026
@Croway Croway self-assigned this Oct 22, 2026
@Croway
Croway requested review from davsclaus and oscerd October 22, 2026 13:01

@gnodet-bot gnodet-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The required-argument validation runs before the camel-prefix strip, which can silently discard a declared and required parameter.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

@github-actions

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@davsclaus davsclaus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Federico, three points need a change before merge. The rest is optional.

  1. Blocking - LangChain4j: an agent without memory gets CamelAiToolConversationId: default. LangChain4j 1.22 falls back to ChatMemoryService.DEFAULT ("default") as the chat memory id when the service method has no @MemoryId, and AiAgentWithoutMemoryService has none. So memoryId is never null here. The tool route then receives a made-up id, and every caller of a memory-less agent shares the same "conversation". This goes against the JIRA and the new docs ("An unavailable identifier is omitted rather than generated"). Pass the memory id only when the agent has memory (inline comment). The concurrency test now always configures a chat memory provider, so the memory-less path is no longer covered. Please add a test for it that expects no conversation header.

  2. Blocking - The body keeps undeclared arguments. The JIRA asks for "the complete accepted arguments" built "from arguments accepted by the executor's existing filtering". The adoc also says "Camel always allowlists top-level property names when invoking the tool route, even if the schema sets additionalProperties: true" (line 199), and the generated schema publishes additionalProperties: false. With this PR, an argument the schema forbids still reaches the route through the body. A route that forwards the body to a back end (an obvious use of argumentsAsBody) then passes on fields the model invented, for example a price or role field added after a prompt injection. Declared arguments are untrusted too, but the allowlist exists so the route only gets the fields the tool declares. Please build the body from the filtered set (the same keys as the headers). If you do want to keep extra fields when the schema allows additionalProperties, make it a separate decision and update line 199 to match. The test preservesUndeclaredArgumentsOnlyInTheBody (zero-parameter tool, see CAMEL-24241) would then expect {}.

  3. Blocking - The catalog copy of the doc is not regenerated. catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ai-tool-component.adoc does not have the two new sections, so the uncommitted-changes check will fail. Regenerate the catalog and commit it.

  4. Not blocking - OpenAI Responses API: OpenAIResponsesProducer resolves a conversation id (CamelOpenAIConversationId / conversationId), but McpToolCallExecutor always passes null. The docs only mention the chat-completion callback. Either pass the Responses conversation id through, or say in the docs that the Responses API does not supply one either.

  5. Not blocking - My PR #28250 (still open) adds @BodyTypes to AiToolEndpoint with consumerOut = no body. Whichever PR merges second has to describe the JSON String body that depends on argumentsAsBody.

  6. Optional - McpServerBridge still calls the 3-arg execute. The MCP request id or session id could fill these headers later. The docs already say MCP does not propagate them, so this can be a follow-up.

  7. Optional - In LangChain4jAgentExecuteToolsConcurrentlyTest, label.toLowerCase() should use Locale.ROOT.

Checked, no change needed:

  • Both headers are Camel-prefixed and documented with @Metadata through headersClass, so they appear in the catalog and the endpoint DSL.
  • The default header filter keeps them away from http and other outbound producers.
  • Model-supplied CamelAiTool* argument names cannot override them, and stale values are removed.
  • Nothing evaluates the arguments or the JSON body.
  • Existing headers are unchanged, and the new option defaults to false, so no upgrade-guide entry is needed.
  • Invalid JSON is still rejected in each adapter before the executor runs.
  • Tests use AssertJ, like the rest of these modules, and do not use Thread.sleep.
  • CI: the build is still pending.

Claude Code on behalf of @davsclaus.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

@gnodet-bot gnodet-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing catalog adoc sync (CI-breaking)

The source adoc at components/camel-ai/camel-ai-tool/src/main/docs/ai-tool-component.adoc has been updated with new sections (Tool Arguments in the Body, new headers, etc.), but the generated copy at catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ai-tool-component.adoc is not included in this PR. The PrepareCatalogMojo regenerates this file during build, so CI's "Fail if there are uncommitted changes" step will detect the desync and fail. Previous commits in this area (e.g. cc762d8) always include both files together. Please run mvn process-resources -pl catalog/camel-catalog -am (or equivalent) and commit the regenerated file.

Addressed: the required-argument validation ordering concern from the previous review is resolved — bodyArguments.keySet().removeIf(camelReserved) now runs before the required-check, so a Camel-reserved name cannot satisfy a required parameter.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

@gnodet-bot gnodet-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following up on my earlier reviews — all prior findings have been addressed.

Previous findings resolved:

  • ✅ Required-argument ordering: Camel-prefix filter now runs before the required-argument check — a reserved-name arg can no longer pass the required check and then be silently discarded.
  • ✅ Memory-less agent conversation ID: hasMemory && memoryId != null guard correctly suppresses CamelAiToolConversationId for agents without memory. The new test confirms memory-less agents propagate no conversation ID.
  • ✅ Body uses filtered arguments only: body is serialized from the post-filter argsCopy — undeclared and reserved-name arguments are excluded from both headers and body.
  • ✅ Catalog adoc sync: catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ai-tool-component.adoc is included in the PR.
  • ✅ OpenAI Responses API conversation ID: OpenAIResponsesProducer now resolves and propagates conversationId through McpToolCallExecutor to AiToolExecutor.
  • ✅ Locale.ROOT added to label.toLowerCase() in the concurrency test.

The test suite is comprehensive: body serialization, header/body filtering, invocation identifier propagation, stale identifier cleanup, reserved-name injection prevention, and parallel concurrency. LGTM.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

@davsclaus davsclaus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Federico, the three blocking points are fixed, and so is the optional Responses API one. I resolved the threads, including the gnodet-bot ones that the new commits cover.

  • Memory-less agents: hasMemory && memoryId != null omits the conversation header, and the concurrency test covers both the memory-less path and a memory agent that picks "default" on purpose.
  • The body is built from the filtered argsCopy, so it has the same keys as the headers, and the docs and option description now say so.
  • The catalog copy of the doc is regenerated.
  • The Responses API passes its resolved conversation id (header or option) through McpToolCallExecutor, with tests for both the header and the option.
  • Reserved names are now removed before the required check, so a required camel* argument rejects the call.

Two small things, neither blocking:

  1. camel-4x-upgrade-guide-4_22.adoc gets an entry under "Upgrading from 4.22.2 to 4.22.3". That only fits if the required-argument fix in AiToolExecutor is backported to camel-4.22.x (the gap exists there too). CAMEL-25985 has no fix version yet. Please either plan that backport (only the filter ordering, not the new option and headers) or drop the 4.22 entry.
  2. agent instanceof AgentWithMemory means a custom Agent implementation that does use memory gets no conversation header. Omitting it is safe and matches "omitted rather than generated", so this is fine. The docs could say "an AgentWithMemory" instead of "when the agent uses memory".

Point 5 from my earlier review still applies: #28250 is still open, so whichever of the two merges second has to describe the JSON String body.

CI: Build and test is still queued.

Claude Code on behalf of @davsclaus.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • catalog/camel-catalog
  • components/camel-ai/camel-ai-tool
  • components/camel-ai/camel-langchain4j-agent
  • components/camel-ai/camel-openai
  • components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat
  • docs
  • dsl/camel-componentdsl
  • dsl/camel-endpointdsl

🔬 Scalpel shadow comparison — Scalpel: 14 of 707 tested, 23 compile-only — current: 14 all tested

Maveniverse Scalpel detected 14 affected modules (current approach: 14).

Skip-tests mode would test 14 modules (8 direct + 10 downstream), skip tests for 23 (generated code, meta-modules)

Modules Scalpel would test (14)
  • camel-ai-tool ← components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolComponentConfigurer.java, components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolConfigurationConfigurer.java, components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolEndpointConfigurer.java, components/camel-ai/camel-ai-tool/src/generated/java/org/apache/camel/component/ai/tool/AiToolEndpointUriFactory.java, components/camel-ai/camel-ai-tool/src/generated/resources/META-INF/org/apache/camel/component/ai/tool/ai-tool.json, components/camel-ai/camel-ai-tool/src/main/docs/ai-tool-component.adoc, components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolConfiguration.java, components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolConstants.java, components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolEndpoint.java, components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolExecutor.java, components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolArgumentsBodyTest.java
  • camel-jbang-mcp ← downstream of org.apache.camel:camel-catalog
  • camel-jbang-plugin-mcp ← downstream of org.apache.camel:camel-jbang-core
  • camel-jbang-plugin-tui ← downstream of org.apache.camel:camel-catalog
  • camel-jbang-plugin-validate ← downstream of org.apache.camel:camel-yaml-dsl-validator
  • camel-langchain4j-agent ← components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentProducer.java, components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentExecuteToolsConcurrentlyTest.java
  • camel-launcher-container ← downstream of org.apache.camel:camel-launcher
  • camel-mcp-server ← downstream of org.apache.camel:camel-openai
  • camel-mcp-server-api ← downstream of org.apache.camel:camel-ai-tool
  • camel-openai ← components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/McpToolCallExecutor.java, components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIResponsesProducer.java, components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/McpToolCallExecutorTest.java, components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIResponsesMockTest.java
  • camel-semantic-openai ← downstream of org.apache.camel:camel-openai
  • camel-spring-ai-chat ← components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/AiToolSpecToSpringAi.java, components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/AiToolInvocationTest.java
  • camel-yaml-dsl-validator ← downstream of org.apache.camel:camel-catalog
  • camel-yaml-dsl-validator-maven-plugin ← downstream of org.apache.camel:camel-yaml-dsl-validator
Modules with tests skipped (23)
  • apache-camel
  • camel-allcomponents
  • camel-catalog-console
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • dummy-component

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

⚠️ Some tests are disabled on GitHub Actions (@DisabledIfSystemProperty(named = "ci.env.name")) and require manual verification:

  • components/camel-ai/camel-langchain4j-agent: 16 test(s) disabled on GitHub Actions
  • components/camel-ai/camel-openai: 7 test(s) disabled on GitHub Actions
  • components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat: 21 test(s) disabled on GitHub Actions
All tested modules (41 modules, 8m 38s total)

Total reactor time: 8m 38s

Module Duration Status
Camel :: Launcher 51.2s SUCCESS
Camel :: AI :: MCP Server 48.2s SUCCESS
Camel :: AI :: OpenAI 48.0s SUCCESS
Camel :: Component DSL 47.9s SUCCESS
Camel :: JBang :: MCP 42.2s SUCCESS
Camel :: JBang :: Plugin :: Validate 29.0s SUCCESS
Camel :: Catalog :: Camel Catalog 23.7s SUCCESS
Camel :: YAML DSL 22.5s SUCCESS
Camel :: YAML DSL :: Validator 21.5s SUCCESS
Camel :: AI :: MCP Server API 18.0s SUCCESS
Camel :: Kamelet Main 16.6s SUCCESS
Camel :: AI :: LangChain4j :: Agent 15.8s SUCCESS
Camel :: Docs 15.1s SUCCESS
Camel :: JBang :: Plugin :: Testing 14.4s SUCCESS
Camel :: Spring AI :: Chat 14.4s SUCCESS
Camel :: JBang :: Plugin :: Kubernetes 13.1s SUCCESS
Camel :: AI :: Tool 12.2s SUCCESS
Camel :: Catalog :: Camel Report Maven Plugin 8.7s SUCCESS
Camel :: Catalog :: Camel Route Parser 7.7s SUCCESS
Camel :: YAML DSL :: Deserializers 6.9s SUCCESS
Camel :: Catalog :: Suggest (deprecated) 6.8s SUCCESS
Camel :: AI :: Semantic OpenAI 6.6s SUCCESS
Camel :: All Components Sync point 4.4s SUCCESS
Camel :: Catalog :: Maven 4.3s SUCCESS
Camel :: YAML DSL :: Validator Maven Plugin 3.9s SUCCESS
Camel :: YAML DSL :: Maven Plugins 2.6s SUCCESS
Camel :: Assembly 2.0s SUCCESS
Camel :: JBang :: Plugin :: Edit 1.7s SUCCESS
Camel :: Coverage 1.3s SUCCESS
Camel :: JBang :: Plugin :: Generate 1.0s SUCCESS
Camel :: Endpoint DSL :: Support 0.9s SUCCESS
Camel :: JBang :: Plugin :: MCP 0.9s SUCCESS
Camel :: Catalog :: Console 0.9s SUCCESS
Camel :: JBang :: Main 0.9s SUCCESS
Camel :: Catalog :: Dummy Component 0.8s SUCCESS
Camel :: Launcher :: Container 0.7s SUCCESS
Camel :: JBang :: Integration tests 0.7s SUCCESS
Camel :: Endpoint DSL n/a
Camel :: Integration Tests n/a
Camel :: JBang :: Core n/a
Camel :: JBang :: Plugin :: TUI n/a

Top 20 slowest modules:

  • Camel :: Launcher (51.2s)
  • Camel :: AI :: MCP Server (48.2s)
  • Camel :: AI :: OpenAI (48.0s)
  • Camel :: Component DSL (47.9s)
  • Camel :: JBang :: MCP (42.2s)
  • Camel :: JBang :: Plugin :: Validate (29.0s)
  • Camel :: Catalog :: Camel Catalog (23.7s)
  • Camel :: YAML DSL (22.5s)
  • Camel :: YAML DSL :: Validator (21.5s)
  • Camel :: AI :: MCP Server API (18.0s)
  • Camel :: Kamelet Main (16.6s)
  • Camel :: AI :: LangChain4j :: Agent (15.8s)
  • Camel :: Docs (15.1s)
  • Camel :: JBang :: Plugin :: Testing (14.4s)
  • Camel :: Spring AI :: Chat (14.4s)
  • Camel :: JBang :: Plugin :: Kubernetes (13.1s)
  • Camel :: AI :: Tool (12.2s)
  • Camel :: Catalog :: Camel Report Maven Plugin (8.7s)
  • Camel :: Catalog :: Camel Route Parser (7.7s)
  • Camel :: YAML DSL :: Deserializers (6.9s)

⚙️ View full build and test results

@davsclaus davsclaus added this to the 4.24.0 milestone Oct 22, 2026
@davsclaus
davsclaus merged commit 50234ed into apache:main Oct 22, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants