Skip to content

CAMEL-24334: Add argSchema raw JSON Schema support to camel-ai-tool - #25368

Merged
davsclaus merged 5 commits into
apache:mainfrom
atiaomar1978-hub:cursor/CAMEL-24334-ai-tool-argschema-c587
Aug 6, 2026
Merged

CAMEL-24334: Add argSchema raw JSON Schema support to camel-ai-tool#25368
davsclaus merged 5 commits into
apache:mainfrom
atiaomar1978-hub:cursor/CAMEL-24334-ai-tool-argschema-c587

Conversation

@atiaomar1978-hub

Copy link
Copy Markdown
Contributor

Description

Adds optional argSchema endpoint option to camel-ai-tool for declaring complex nested tool parameters as raw JSON Schema instead of flat parameter.* options.

Key changes:

  • New argSchema @UriParam on AiToolConfiguration (inline JSON or classpath:/file:/resource: reference)
  • argSchema and parameter.* are mutually exclusive (validated at consumer start)
  • Raw schema stored verbatim in AiToolSpec.parametersJsonSchema; executor allowlist/required names derived from top-level properties/required keys
  • LangChain4j adapter converts nested schemas via JsonSchemaElementJsonUtils.fromMap() with metadata stripping fallback
  • Startup validation: root object, non-empty properties, valid required array, required names must exist in properties
  • Documentation, catalog sync, and AssertJ tests

Fixes CAMEL-24334.

Target

  • I checked that the commit is targeting the correct branch (Camel 4 uses the main branch)

Tracking

Apache Camel coding standards and style

  • I checked that each commit in the pull request has a meaningful subject line and body.
  • I have run mvn clean install -DskipTests locally from root folder and I have committed all auto-generated changes.

AI-assisted contributions

  • If this PR includes AI-generated code, commits have proper co-authorship attribution (e.g., Co-authored-by trailers) and the PR description identifies the AI tool used.

AI-generated on behalf of atiaomar1978-hub via Cursor Cloud Agent.

Testing

./mvnw -B -pl components/camel-ai/camel-ai-tool,components/camel-ai/camel-langchain4j-agent -am test \
  -Dtest=AiToolParameterHelperTest,AiToolExecutorTest,AiToolEndpointLifecycleTest,AiToolSpecToLangChain4jTest

All tests pass.

Introduce optional argSchema endpoint option for declaring complex nested
tool parameters as raw JSON Schema (inline or via classpath/file/resource
references). argSchema is mutually exclusive with flat parameter.* options.

Derive declared/required argument names from schema properties for executor
allowlisting, and adapt LangChain4j tool conversion for nested schemas.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
- Validate required property names against properties map at startup
- Handle JSON null and malformed properties/required with clear errors
- Strip common JSON Schema metadata before LangChain4j conversion
- Regenerate camel-catalog and add edge-case AssertJ tests
- Document top-level allowlist behavior for argSchema tools

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Check required tool arguments against the allowlisted argsCopy rather than
the original arguments map, so undeclared required names cannot slip through
after filtering.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
@atiaomar1978-hub

Copy link
Copy Markdown
Contributor Author

Implementation summary

This PR adds optional argSchema to camel-ai-tool for declaring nested tool parameters as raw JSON Schema (inline or via classpath:/file:/resource: references), mutually exclusive with flat parameter.* options.

Highlights

  • Executor allowlist/required derived from schema top-level properties/required (CAMEL-24241 follow-up)
  • Startup validation: root object, non-empty properties, valid required array, required names must exist in properties
  • LangChain4j adapter converts nested schemas with metadata stripping fallback
  • Catalog metadata regenerated; AssertJ tests added

Review feedback addressed

  • JSON null / malformed schema NPEs fixed
  • Required-parameter check uses filtered argsCopy after allowlist filtering
  • Documentation clarifies top-level allowlist behavior

Testing

./mvnw -B -pl components/camel-ai/camel-ai-tool,components/camel-ai/camel-langchain4j-agent -am test \
  -Dtest=AiToolParameterHelperTest,AiToolExecutorTest,AiToolEndpointLifecycleTest,AiToolSpecToLangChain4jTest

All targeted tests pass.

AI-generated summary on behalf of atiaomar1978-hub via Cursor Cloud Agent.

@atiaomar1978-hub
atiaomar1978-hub marked this pull request as ready for review August 6, 2026 03:34
@atiaomar1978-hub

atiaomar1978-hub commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Code review fixes (Bugbot + Grok)

AI-generated summary on behalf of atiaomar1978-hub via Cursor Cloud Agent.

This PR went through automated review (Bugbot + Grok). Below is what was addressed before marking ready for review.

Blockers fixed

Finding Fix
Catalog metadata driftargSchema missing from camel-catalog Regenerated and committed catalog/camel-catalog/.../ai-tool.json and related generated docs
NPE on JSON nullJsoner.deserialize("null") returned Java null, then getClass() NPE'd Null-safe parsing in AiToolParameterHelper.parseJsonObject() and AiToolSpecToLangChain4j.buildSchemaFromJson()

Security / correctness fixes

Finding Fix
Empty/missing properties{"type":"object"} passed validation → empty allowlist validateRootSchemaObject() now requires a non-empty top-level properties map
Required names not in properties — orphan required entries caused executor confusion Startup validation rejects required entries not declared in properties
Required check used unfiltered arguments map — undeclared required names could pass validation but never reach headers AiToolExecutor now validates required args against argsCopy after allowlist filtering
Malformed properties/required typesClassCastException on bad schemas Type-checked validation with clear IllegalArgumentException messages

LangChain4j adapter

Finding Fix
Root schemas with $schema/$id metadata rejected Added metadata stripping fallback before JsonSchemaElementJsonUtils.fromMap()
Internal LC4j API usage Kept JsonSchemaElementJsonUtils (no stable public alternative); documented limitation for schemas with unsupported root keywords (format, patternProperties, object-valued additionalProperties)

Documentation / metadata

Finding Fix
parameter.. description corruption (annotation strips *) Rephrased to "parameter multi-value options"
Allowlist behavior undocumented Added docs clarifying Camel always allowlists top-level properties names regardless of additionalProperties

Tests added (AssertJ)

18 new test methods added across 4 test classes:

Test class New tests Focus
AiToolParameterHelperTest 11 Schema validation, extraction, mutual exclusivity, edge cases
AiToolExecutorTest 4 Nested args, undeclared filtering, required-arg errors
AiToolEndpointLifecycleTest 2 Classpath argSchema registration, mutual exclusivity
AiToolSpecToLangChain4jTest 1 Nested raw schema conversion

Test run results (2026-08-06)

Status: BUILD SUCCESS — all targeted tests pass.

./mvnw -B -pl components/camel-ai/camel-ai-tool,components/camel-ai/camel-langchain4j-agent -am test \
  -Dtest=AiToolParameterHelperTest,AiToolExecutorTest,AiToolEndpointLifecycleTest,AiToolSpecToLangChain4jTest
Test class Tests run Failures Errors Skipped Time
AiToolParameterHelperTest 24 0 0 0 0.14s
AiToolExecutorTest 17 0 0 0 0.36s
AiToolEndpointLifecycleTest 15 0 0 0 0.74s
AiToolSpecToLangChain4jTest 10 0 0 0 0.34s
Total 66 0 0 0 ~1.6s

Build time: ~41s (including module dependencies).

Known limitations (accepted)

  • LangChain4j conversion may still fail for root schemas with keywords LC4j cannot model as JsonObjectSchema (clear error message at startup/conversion time)
  • AiToolSpecToLangChain4jTest still uses JUnit assertions in pre-existing methods (AssertJ used in new ai-tool tests)

Commits addressing review feedback: f777588, f22f932.

@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.

Thank you for this contribution — the argSchema feature is well-designed with good validation and test coverage.

Two items need attention before this can merge:

Blocking

  1. CI has not run — no checks are reported on this branch. Please push to trigger the CI workflow so we can verify the build is green.

  2. Unrelated generated file change (scope drift) — the diff includes a camel.jbang.mcp entry in catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/jbang/camel-jbang-configuration-metadata.json that has no backing source code in the jbang modules. This appears to be a phantom regeneration artifact from a local build. Please revert this file change from the PR.

Non-blocking convention notes

  • The new testRawArgSchemaConversion in AiToolSpecToLangChain4jTest uses JUnit assertions (assertNotNull, assertEquals, assertTrue) — per project conventions, new test code should prefer AssertJ (assertThat(...)).
  • New test methods in AiToolEndpointLifecycleTest, AiToolExecutorTest, and AiToolParameterHelperTest use the public modifier — JUnit 5 test methods should be package-private (no modifier).
  • Minor: copy.argSchema = this.argSchema in AiToolConfiguration.copy() is redundant after super.clone() since String is immutable.

This review covers project rules and conventions only — it does not replace specialized AI review tools (CodeRabbit, Sourcery) or static analysis (SonarCloud).

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

Claude Code on behalf of davsclaus

@github-actions

github-actions Bot commented Aug 6, 2026

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.

- Revert unrelated camel.jbang.mcp catalog metadata change
- Use AssertJ in testRawArgSchemaConversion
- Drop public modifier from new test methods
- Remove redundant argSchema assignment in copy()

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
@atiaomar1978-hub

Copy link
Copy Markdown
Contributor Author

Review feedback addressed (@davsclaus)

AI-generated on behalf of atiaomar1978-hub via Cursor Cloud Agent.

Pushed commit 9c7fea5 with the following fixes:

Blocking

  • Reverted unrelated camel.jbang.mcp catalog changecatalog/camel-catalog/.../camel-jbang-configuration-metadata.json restored to main state

Convention fixes

  • testRawArgSchemaConversion — migrated to AssertJ (assertThat)
  • New test methods — dropped unnecessary public modifier in AiToolParameterHelperTest, AiToolExecutorTest, and AiToolEndpointLifecycleTest
  • AiToolConfiguration.copy() — removed redundant copy.argSchema = this.argSchema (String is immutable, clone already copies the field)

Tests (BUILD SUCCESS)

./mvnw -B -pl components/camel-ai/camel-ai-tool,components/camel-ai/camel-langchain4j-agent -am test \
  -Dtest=AiToolParameterHelperTest,AiToolExecutorTest,AiToolEndpointLifecycleTest,AiToolSpecToLangChain4jTest

66 tests, 0 failures/errors/skipped.

CI should re-run on this push. Ready for re-review.

Commit generated AiTool DSL builder updates so CI uncommitted-changes check passes.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
@atiaomar1978-hub

Copy link
Copy Markdown
Contributor Author

CI fix — uncommitted generated files

AI-generated on behalf of atiaomar1978-hub via Cursor Cloud Agent.

CI failed the uncommitted changes check (run 31068754671) because the full build regenerated argSchema DSL builder updates that were not committed.

Fixed in 0f437de:

  • Regenerated and committed AiToolComponentBuilderFactory.java (component DSL)
  • Regenerated and committed AiToolEndpointBuilderFactory.java (endpoint DSL)

The unrelated camel.jbang.mcp catalog drift was already reverted in 9c7fea5 — current branch HEAD matches main for jbang metadata.

CI should pass the generated-files check on the next run.

@github-actions github-actions Bot added the dsl label Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

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
  • dsl/camel-componentdsl
  • dsl/camel-endpointdsl

🔬 Scalpel shadow comparison — Scalpel: 16 tested, 26 compile-only — current: 13 all tested

Maveniverse Scalpel detected 42 affected modules (current approach: 13).

⚠️ Modules only in Scalpel (29)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • 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
  • docs
  • dummy-component

Skip-tests mode would test 16 modules (5 direct + 11 downstream), skip tests for 26 (generated code, meta-modules)

Modules Scalpel would test (16)
  • camel-ai-tool
  • camel-catalog
  • camel-componentdsl
  • camel-endpointdsl
  • camel-jbang-mcp
  • camel-jbang-plugin-mcp
  • camel-jbang-plugin-route-parser
  • camel-jbang-plugin-tui
  • camel-jbang-plugin-validate
  • camel-langchain4j-agent
  • camel-launcher-container
  • camel-mcp-server
  • camel-mcp-server-api
  • camel-spring-ai-chat
  • camel-yaml-dsl-validator
  • camel-yaml-dsl-validator-maven-plugin
Modules with tests skipped (26)
  • apache-camel
  • camel-allcomponents
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-csimple-maven-plugin
  • 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
  • docs
  • 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
All tested modules (42 modules)
  • Camel :: AI :: LangChain4j :: Agent
  • Camel :: AI :: MCP Server
  • Camel :: AI :: MCP Server API
  • Camel :: AI :: Tool
  • Camel :: All Components Sync point
  • Camel :: Assembly
  • Camel :: Catalog :: CSimple Maven Plugin (deprecated)
  • Camel :: Catalog :: Camel Catalog
  • Camel :: Catalog :: Camel Report Maven Plugin
  • Camel :: Catalog :: Camel Route Parser
  • Camel :: Catalog :: Console
  • Camel :: Catalog :: Dummy Component
  • Camel :: Catalog :: Lucene (deprecated)
  • Camel :: Catalog :: Maven
  • Camel :: Catalog :: Suggest
  • Camel :: Component DSL
  • Camel :: Coverage
  • Camel :: Docs
  • Camel :: Endpoint DSL
  • Camel :: Endpoint DSL :: Support
  • Camel :: Integration Tests
  • Camel :: JBang :: Core
  • Camel :: JBang :: Integration tests
  • Camel :: JBang :: MCP
  • Camel :: JBang :: Main
  • Camel :: JBang :: Plugin :: Edit
  • Camel :: JBang :: Plugin :: Generate
  • Camel :: JBang :: Plugin :: Kubernetes
  • Camel :: JBang :: Plugin :: MCP
  • Camel :: JBang :: Plugin :: Route Parser
  • Camel :: JBang :: Plugin :: TUI
  • Camel :: JBang :: Plugin :: Testing
  • Camel :: JBang :: Plugin :: Validate
  • Camel :: Kamelet Main
  • Camel :: Launcher
  • Camel :: Launcher :: Container
  • Camel :: Spring AI :: Chat
  • Camel :: YAML DSL
  • Camel :: YAML DSL :: Deserializers
  • Camel :: YAML DSL :: Maven Plugins
  • Camel :: YAML DSL :: Validator
  • Camel :: YAML DSL :: Validator Maven Plugin

⚙️ View full build and test results

@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.

Thank you for addressing the previous review feedback — the CI is green and the jbang metadata file has been reverted.

The argSchema feature is well-designed: proper mutual exclusion validation, comprehensive startup schema validation, good test coverage across unit/lifecycle/executor/langchain4j integration, and clear documentation.

Non-blocking observations

  1. Internal LangChain4j APIdev.langchain4j.internal.JsonSchemaElementJsonUtils.fromMap() is from the internal package and may change without notice in future LangChain4j releases. The stripSchemaMetadata() fallback mitigates conversion failures, but this remains a maintenance risk worth noting.

  2. Derived fields in AiToolSpec.equals/hashCodedeclaredArgumentNames and requiredArgumentNames are computed from parameterDefs / parametersJsonSchema. Including them in equals() and hashCode() is redundant since the source fields already participate.

  3. Broad catch (Exception) in resolveResourceContent — the catch-all silently swallows errors when a value isn't a resolvable resource, then treats it as inline JSON. If a user provides a scheme-less resource path (e.g., schemas/order.json) that fails to load, they would see a confusing JSON parse error rather than a "resource not found" error.

None of these are blocking — the feature is ready to merge.

This review covers project rules and conventions only — it does not replace specialized AI review tools (CodeRabbit, Sourcery) or static analysis (SonarCloud).

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

Claude Code on behalf of davsclaus

@@ -48,11 +53,60 @@ public static ToolSpecification toToolSpecification(AiToolSpec spec) {

if (spec.getParameterDefs() != null && !spec.getParameterDefs().isEmpty()) {
builder.parameters(buildSchema(spec.getParameterDefs()));

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.

Non-blocking: JsonSchemaElementJsonUtils is from dev.langchain4j.internal — an internal API that may change without notice in future LangChain4j releases. The stripSchemaMetadata() fallback helps, but this is a maintenance risk worth tracking.

names.add(value.toString());
}
}
return Set.copyOf(names);

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.

Non-blocking: this broad catch (Exception) silently swallows errors for non-resource values. If a user provides a scheme-less resource path like schemas/order.json that fails to load, the error is swallowed and the value is treated as inline JSON — resulting in a confusing JSON parse error rather than a clear "resource not found" error.

@davsclaus davsclaus added this to the 4.22.0 milestone Aug 6, 2026
@davsclaus davsclaus added the enhancement New feature or request label Aug 6, 2026
@davsclaus
davsclaus merged commit f09459b into apache:main Aug 6, 2026
6 checks passed

@gnodet gnodet 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.

Well-designed feature addition with proper mutual exclusivity validation, comprehensive startup schema validation, good test coverage (18 new tests), and documentation. All blocking feedback from the prior review round has been addressed, CI is green, and the implementation cleanly extends the existing abstraction so that both LangChain4j and Spring AI adapters work correctly.

The architecture is clean: AiToolSpec stores the resolved JSON schema in parametersJsonSchema regardless of whether it came from flat parameter.* options or argSchema, and all downstream adapters consume from that field. The Spring AI adapter automatically works with argSchema without any code changes, which validates the design.

All four low-severity observations (redundant derived fields in equals/hashCode, double JSON parse in constructor, broad catch in resolveResourceContent, internal LangChain4j API usage) were already documented in davsclaus's approval review — no new issues found.

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

Claude Code on behalf of @gnodet

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.

4 participants