This is an automated email from the ASF dual-hosted git repository.

jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git


The following commit(s) were added to refs/heads/main by this push:
     new d222d7a708 Move AI tool specific code into the ai-tool extension
d222d7a708 is described below

commit d222d7a7086679229f4fad179317c635f581cc82
Author: James Netherton <[email protected]>
AuthorDate: Tue Sep 8 10:29:40 2026 +0100

    Move AI tool specific code into the ai-tool extension
    
    * Move AI tool specific code into the ai-tool extension
    
    * Register the AI tool bridge only via the ai-tool extension, and say so
    
    The bridge exposing ai-tool: routes to Quarkus LangChain4j AI services now
    lives in camel-quarkus-ai-tool, so it is registered only when that extension
    is a dependency. integration-tests/langchain4j-agent-ql4j was getting it
    transitively via camel-quarkus-support-langchain4j and silently lost its
    Camel tools, failing Langchain4jAgentTest.customAiService.
    
    * Declare camel-quarkus-ai-tool in langchain4j-agent-ql4j, which uses
      ai-tool: routes directly
    * Warn at Camel context start when tools are registered but the bridge is
      absent, so the misconfiguration is not silent
    * Warn when @CamelAiTools is used without Quarkus LangChain4j on the
      classpath, where the annotation has no effect
    * Reject a blank @CamelAiTools tag at build time: it exposed every
      registered tool to the service instead of the intended subset
    * Make quarkus-jackson optional, since only the bridge uses an ObjectMapper
      and Quarkus LangChain4j depends on quarkus-jackson itself
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
 .../modules/ROOT/pages/migration-guide/3.40.0.adoc |  19 ++++
 .../ROOT/pages/reference/extensions/ai-tool.adoc   |  14 ++-
 extensions-support/langchain4j/deployment/pom.xml  |   5 -
 .../SupportQuarkusLangchain4jProcessor.java        | 107 -------------------
 extensions-support/langchain4j/runtime/pom.xml     |   5 -
 .../langchain4j/QuarkusLangchain4jRecorder.java    |   5 -
 .../ai/tool}/deployment/AiToolPresent.java         |  11 +-
 .../ai/tool/deployment/AiToolProcessor.java        | 118 +++++++++++++++++++++
 .../tool/deployment/QuarkusLangchain4jPresent.java |  11 +-
 extensions/ai-tool/runtime/pom.xml                 |  15 +++
 extensions/ai-tool/runtime/src/main/doc/usage.adoc |  14 ++-
 .../quarkus/component/ai/tool/AiToolRecorder.java} |  15 ++-
 .../component/ai/tool}/AiToolSpecConverter.java    |   2 +-
 .../component/ai/tool/AiToolSpecConverterImpl.java |  18 ++--
 .../component/ai/tool}/CamelAiToolProvider.java    |  10 +-
 .../quarkus/component/ai/tool}/CamelAiTools.java   |   2 +-
 .../ai/tool}/CamelAiToolsInterceptor.java          |   2 +-
 .../agent/deployment/CamelAiToolBridgeAbsent.java  |  19 ++--
 .../deployment/Langchain4jAgentProcessor.java      |  13 +++
 extensions/langchain4j-agent/runtime/pom.xml       |   4 +
 .../agent/Langchain4jAgentRecorder.java            |  60 +++++++++++
 integration-tests/ai-tool-langchain4j/pom.xml      |  11 +-
 .../langchain4j/it/service/AdminAiService.java     |   2 +-
 .../langchain4j/it/service/WeatherAiService.java   |   2 +-
 .../it/service/WeatherAiServiceOllama.java         |   2 +-
 .../it/service/WeatherAiServiceOllamaWrongTag.java |   2 +-
 .../it/service/WeatherAiServiceWrongTag.java       |   2 +-
 integration-tests/langchain4j-agent-ql4j/pom.xml   |  17 +++
 28 files changed, 327 insertions(+), 180 deletions(-)

diff --git a/docs/modules/ROOT/pages/migration-guide/3.40.0.adoc 
b/docs/modules/ROOT/pages/migration-guide/3.40.0.adoc
index 099532dae0..d1f7b80309 100644
--- a/docs/modules/ROOT/pages/migration-guide/3.40.0.adoc
+++ b/docs/modules/ROOT/pages/migration-guide/3.40.0.adoc
@@ -19,6 +19,25 @@ To keep the previous behaviour for a context that relied on 
the default, set it
 quarkus.camel.ldap.dir-contexts."my-context".security-authentication=none
 ----
 
+== AI Tool extension changes
+
+=== The LangChain4j tool bridge moved to the AI Tool extension
+
+The bridge that exposes `ai-tool:` routes to LangChain4j AI services was part 
of `camel-quarkus-support-langchain4j`, an internal artifact that applications 
had to declare by hand alongside `camel-quarkus-ai-tool`. It is now part of 
`camel-quarkus-ai-tool` itself, and that dependency can be removed.
+
+The `@CamelAiTools` annotation moved with it. Update the import.
+
+[source,java]
+----
+// Was: import 
org.apache.camel.quarkus.component.support.langchain4j.CamelAiTools;
+import org.apache.camel.quarkus.component.ai.tool.CamelAiTools;
+----
+
+The bridge is registered when `camel-quarkus-ai-tool` is a dependency and 
Quarkus LangChain4j and
+`camel-langchain4j-agent` are on the classpath. Applications that used 
`ai-tool:` routes without
+declaring `camel-quarkus-ai-tool` must now add it. A warning naming the 
affected tools is logged at
+startup when they are registered without it. Its behaviour is otherwise 
unchanged.
+
 == JTA extension changes
 
 === Rollback and resume failures are no longer swallowed
diff --git a/docs/modules/ROOT/pages/reference/extensions/ai-tool.adoc 
b/docs/modules/ROOT/pages/reference/extensions/ai-tool.adoc
index 86ff0a1f5f..cc1bf8a1af 100644
--- a/docs/modules/ROOT/pages/reference/extensions/ai-tool.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/ai-tool.adoc
@@ -100,5 +100,17 @@ endpoint's `tags` parameter — an agent without `tags` 
receives no registry too
 the agent's other sources, such as MCP clients or custom tool objects, are 
unaffected).
 Quarkus LangChain4j AI services (`@RegisterAiService` interfaces and 
programmatically built
 `AiServices`) receive every registered tool by default; restrict a service to 
a subset with
-the `@CamelAiTools` annotation.
+the `org.apache.camel.quarkus.component.ai.tool.CamelAiTools` annotation.
+
+[source,java]
+----
+@RegisterAiService
+@CamelAiTools("support")
+public interface SupportAgent {
+    String chat(@UserMessage String message);
+}
+----
+
+The tool provider backing this is registered automatically when Quarkus 
LangChain4j and
+`camel-langchain4j-agent` are on the classpath.
 
diff --git a/extensions-support/langchain4j/deployment/pom.xml 
b/extensions-support/langchain4j/deployment/pom.xml
index cb956f0a65..0ca00589e8 100644
--- a/extensions-support/langchain4j/deployment/pom.xml
+++ b/extensions-support/langchain4j/deployment/pom.xml
@@ -42,11 +42,6 @@
             <groupId>org.apache.camel.quarkus</groupId>
             <artifactId>camel-quarkus-support-langchain4j</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.apache.camel</groupId>
-            <artifactId>camel-ai-tool</artifactId>
-            <optional>true</optional>
-        </dependency>
         <dependency>
             <groupId>io.quarkus</groupId>
             <artifactId>quarkus-junit-internal</artifactId>
diff --git 
a/extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/SupportQuarkusLangchain4jProcessor.java
 
b/extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/SupportQuarkusLangchain4jProcessor.java
index 815265a93f..25cadd2a55 100644
--- 
a/extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/SupportQuarkusLangchain4jProcessor.java
+++ 
b/extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/SupportQuarkusLangchain4jProcessor.java
@@ -17,8 +17,6 @@
 package org.apache.camel.quarkus.component.support.langchain4j.deployment;
 
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -31,10 +29,7 @@ import dev.langchain4j.guardrail.OutputGuardrail;
 import dev.langchain4j.model.embedding.EmbeddingModel;
 import dev.langchain4j.rag.RetrievalAugmentor;
 import dev.langchain4j.store.embedding.EmbeddingStore;
-import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
 import io.quarkus.arc.deployment.BeanDiscoveryFinishedBuildItem;
-import io.quarkus.arc.deployment.GeneratedBeanBuildItem;
-import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor;
 import io.quarkus.arc.deployment.QualifierRegistrarBuildItem;
 import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
 import io.quarkus.arc.deployment.SyntheticBeansRuntimeInitBuildItem;
@@ -51,18 +46,11 @@ import 
io.quarkus.deployment.builditem.CombinedIndexBuildItem;
 import io.quarkus.deployment.builditem.SystemPropertyBuildItem;
 import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
 import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild;
-import io.quarkus.gizmo.ClassCreator;
-import io.quarkus.gizmo.MethodCreator;
-import io.quarkus.gizmo.MethodDescriptor;
-import io.quarkus.gizmo.ResultHandle;
 import io.quarkus.runtime.configuration.ConfigurationException;
 import jakarta.enterprise.context.ApplicationScoped;
 import jakarta.enterprise.context.Dependent;
 import jakarta.inject.Named;
 import jakarta.inject.Singleton;
-import 
org.apache.camel.quarkus.component.support.langchain4j.AiToolSpecConverter;
-import 
org.apache.camel.quarkus.component.support.langchain4j.CamelAiToolProvider;
-import 
org.apache.camel.quarkus.component.support.langchain4j.CamelAiToolsInterceptor;
 import 
org.apache.camel.quarkus.component.support.langchain4j.QuarkusLangchain4jRecorder;
 import org.apache.camel.quarkus.component.support.langchain4j.RagAugmentorName;
 import org.apache.camel.quarkus.component.support.langchain4j.RagBridgeConfig;
@@ -88,8 +76,6 @@ class SupportQuarkusLangchain4jProcessor {
 
     public static final DotName REGISTER_AI_SERVICES_DOTNAME = DotName
             .createSimple("io.quarkiverse.langchain4j.RegisterAiService");
-    private static final DotName CAMEL_AI_TOOLS_DOTNAME = DotName
-            
.createSimple("org.apache.camel.quarkus.component.support.langchain4j.CamelAiTools");
     private static final DotName EMBEDDING_STORE_NAME_DOTNAME = DotName
             .createSimple("io.quarkiverse.langchain4j.EmbeddingStoreName");
     private static final DotName RAG_RETRIEVAL_FILTER_SUPPLIER_DOTNAME = 
DotName
@@ -174,85 +160,6 @@ class SupportQuarkusLangchain4jProcessor {
                 .build());
     }
 
-    @BuildStep(onlyIf = AiToolPresent.class)
-    AdditionalBeanBuildItem registerCamelAiToolProvider() {
-        LOG.info("Camel AI Tool detected - registering CamelAiToolProvider as 
CDI bean for ToolProvider auto-discovery");
-        return 
AdditionalBeanBuildItem.unremovableOf(CamelAiToolProvider.class);
-    }
-
-    @BuildStep(onlyIf = AiToolPresent.class)
-    void generateAiToolSpecConverter(BuildProducer<GeneratedBeanBuildItem> 
generatedBeans,
-            BuildProducer<UnremovableBeanBuildItem> unremovableBeans) {
-
-        String generatedClassName = 
"org.apache.camel.quarkus.component.support.langchain4j.AiToolSpecConverterImpl";
-
-        try (ClassCreator cc = ClassCreator.builder()
-                .classOutput(new GeneratedBeanGizmoAdaptor(generatedBeans))
-                .className(generatedClassName)
-                .interfaces(AiToolSpecConverter.class)
-                .build()) {
-
-            cc.addAnnotation(Singleton.class);
-
-            String toolSpecClass = 
"dev.langchain4j.agent.tool.ToolSpecification";
-            String aiToolSpecClass = 
"org.apache.camel.component.ai.tool.AiToolSpec";
-
-            try (MethodCreator mc = cc.getMethodCreator("toToolSpecification",
-                    toolSpecClass, aiToolSpecClass)) {
-
-                ResultHandle spec = mc.getMethodParam(0);
-                ResultHandle result = mc.invokeStaticMethod(
-                        MethodDescriptor.ofMethod(
-                                
"org.apache.camel.component.langchain4j.agent.AiToolSpecToLangChain4j",
-                                "toToolSpecification",
-                                toolSpecClass,
-                                aiToolSpecClass),
-                        spec);
-                mc.returnValue(result);
-            }
-        }
-
-        unremovableBeans.produce(beanClassNames(generatedClassName));
-    }
-
-    @BuildStep(onlyIf = AiToolPresent.class)
-    @Record(ExecutionTime.STATIC_INIT)
-    void configureCamelAiToolTags(
-            CombinedIndexBuildItem combinedIndex,
-            BuildProducer<AdditionalBeanBuildItem> additionalBeans,
-            QuarkusLangchain4jRecorder recorder) {
-
-        IndexView index = combinedIndex.getIndex();
-        Map<String, String> tagMap = new HashMap<>();
-        for (AnnotationInstance annotation : 
index.getAnnotations(CAMEL_AI_TOOLS_DOTNAME)) {
-            if (annotation.target().kind() == AnnotationTarget.Kind.CLASS) {
-                String className = 
annotation.target().asClass().name().toString();
-                if (annotation.value() == null) {
-                    LOG.warnf("@CamelAiTools on %s has no value — skipping", 
className);
-                    continue;
-                }
-                String tagValue = annotation.value().asString();
-                if (tagValue.isBlank()) {
-                    LOG.warnf("@CamelAiTools on %s has blank value — 
skipping", className);
-                    continue;
-                }
-                tagMap.put(className, tagValue);
-                LOG.infof("Discovered @CamelAiTools(\"%s\") on %s", tagValue, 
className);
-            }
-        }
-
-        if (tagMap.isEmpty()) {
-            return;
-        }
-
-        recorder.setCamelAiToolTagMap(tagMap);
-
-        additionalBeans.produce(AdditionalBeanBuildItem.builder()
-                .addBeanClasses(CamelAiToolsInterceptor.class)
-                .setUnremovable()
-                .build());
-    }
-
     // The retrieval filter supplier is only resolved programmatically at 
augmentor creation
     // time, so ArC would remove a user's implementation as unused.
     @BuildStep
@@ -330,20 +237,6 @@ class SupportQuarkusLangchain4jProcessor {
             CombinedIndexBuildItem indexBuildItem,
             BuildProducer<UnremovableBeanBuildItem> unremovableBeans) {
 
-        if (!new AiToolPresent().getAsBoolean()) {
-            Collection<AnnotationInstance> aiToolsAnnotations = 
indexBuildItem.getIndex()
-                    .getAnnotations(CAMEL_AI_TOOLS_DOTNAME);
-            if (!aiToolsAnnotations.isEmpty()) {
-                LOG.warnf("@CamelAiTools annotations found but 
camel-langchain4j-agent is not on the classpath. "
-                        + "Add camel-langchain4j-agent dependency to enable 
the Camel AI tool bridge. "
-                        + "Affected classes: %s",
-                        aiToolsAnnotations.stream()
-                                .filter(a -> a.target().kind() == 
AnnotationTarget.Kind.CLASS)
-                                .map(a -> 
a.target().asClass().name().toString())
-                                .collect(Collectors.joining(", ")));
-            }
-        }
-
         LOG.debug("Discovering classes annotated with @RegisterAiService to 
mark implementation beans as unremovable");
 
         for (AnnotationInstance instance : 
indexBuildItem.getIndex().getAnnotations(REGISTER_AI_SERVICES_DOTNAME)) {
diff --git a/extensions-support/langchain4j/runtime/pom.xml 
b/extensions-support/langchain4j/runtime/pom.xml
index 15a1effe91..2569f94a27 100644
--- a/extensions-support/langchain4j/runtime/pom.xml
+++ b/extensions-support/langchain4j/runtime/pom.xml
@@ -55,11 +55,6 @@
             <artifactId>quarkus-langchain4j-core</artifactId>
             <optional>true</optional>
         </dependency>
-        <dependency>
-            <groupId>org.apache.camel</groupId>
-            <artifactId>camel-ai-tool</artifactId>
-            <optional>true</optional>
-        </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-langchain4j-agent</artifactId>
diff --git 
a/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/QuarkusLangchain4jRecorder.java
 
b/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/QuarkusLangchain4jRecorder.java
index 7b80f57df8..cb8dbd37a9 100644
--- 
a/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/QuarkusLangchain4jRecorder.java
+++ 
b/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/QuarkusLangchain4jRecorder.java
@@ -19,7 +19,6 @@ package 
org.apache.camel.quarkus.component.support.langchain4j;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationTargetException;
 import java.util.List;
-import java.util.Map;
 import java.util.function.Supplier;
 
 import dev.langchain4j.data.segment.TextSegment;
@@ -45,10 +44,6 @@ public class QuarkusLangchain4jRecorder {
     private static final TypeLiteral<EmbeddingStore<TextSegment>> 
EMBEDDING_STORE_TYPE = new TypeLiteral<>() {
     };
 
-    public void setCamelAiToolTagMap(Map<String, String> tagMap) {
-        CamelAiToolProvider.TAG_MAP.putAll(tagMap);
-    }
-
     public void registerNamedEmbeddingStores(RuntimeValue<Registry> 
camelRegistry) {
         List<InstanceHandle<EmbeddingStore<TextSegment>>> handles = 
Arc.container()
                 .listAll(EMBEDDING_STORE_TYPE, Any.Literal.INSTANCE);
diff --git 
a/extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/AiToolPresent.java
 
b/extensions/ai-tool/deployment/src/main/java/org/apache/camel/quarkus/component/ai/tool/deployment/AiToolPresent.java
similarity index 76%
copy from 
extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/AiToolPresent.java
copy to 
extensions/ai-tool/deployment/src/main/java/org/apache/camel/quarkus/component/ai/tool/deployment/AiToolPresent.java
index 7168ec2d43..f85d13c852 100644
--- 
a/extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/AiToolPresent.java
+++ 
b/extensions/ai-tool/deployment/src/main/java/org/apache/camel/quarkus/component/ai/tool/deployment/AiToolPresent.java
@@ -14,20 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.quarkus.component.support.langchain4j.deployment;
+package org.apache.camel.quarkus.component.ai.tool.deployment;
 
 import java.util.function.BooleanSupplier;
 
+/**
+ * Whether {@code camel-langchain4j-agent} is on the classpath, providing the
+ * {@code AiToolSpec} to {@code ToolSpecification} conversion the bridge is 
generated against.
+ */
 public class AiToolPresent implements BooleanSupplier {
-    private static final String AI_TOOL_REGISTRY_CLASS = 
"org.apache.camel.component.ai.tool.AiToolRegistry";
     private static final String AI_TOOL_SPEC_CONVERTER_CLASS = 
"org.apache.camel.component.langchain4j.agent.AiToolSpecToLangChain4j";
 
     @Override
     public boolean getAsBoolean() {
-        ClassLoader cl = Thread.currentThread().getContextClassLoader();
         try {
-            cl.loadClass(AI_TOOL_REGISTRY_CLASS);
-            cl.loadClass(AI_TOOL_SPEC_CONVERTER_CLASS);
+            
Thread.currentThread().getContextClassLoader().loadClass(AI_TOOL_SPEC_CONVERTER_CLASS);
             return true;
         } catch (ClassNotFoundException e) {
             return false;
diff --git 
a/extensions/ai-tool/deployment/src/main/java/org/apache/camel/quarkus/component/ai/tool/deployment/AiToolProcessor.java
 
b/extensions/ai-tool/deployment/src/main/java/org/apache/camel/quarkus/component/ai/tool/deployment/AiToolProcessor.java
index e132611dd1..579ebab368 100644
--- 
a/extensions/ai-tool/deployment/src/main/java/org/apache/camel/quarkus/component/ai/tool/deployment/AiToolProcessor.java
+++ 
b/extensions/ai-tool/deployment/src/main/java/org/apache/camel/quarkus/component/ai/tool/deployment/AiToolProcessor.java
@@ -16,15 +16,133 @@
  */
 package org.apache.camel.quarkus.component.ai.tool.deployment;
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
+import 
io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem;
+import io.quarkus.deployment.annotations.BuildProducer;
 import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.annotations.ExecutionTime;
+import io.quarkus.deployment.annotations.Record;
+import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
 import io.quarkus.deployment.builditem.FeatureBuildItem;
+import io.quarkus.runtime.configuration.ConfigurationException;
+import org.apache.camel.quarkus.component.ai.tool.AiToolRecorder;
+import org.apache.camel.quarkus.component.ai.tool.AiToolSpecConverterImpl;
+import org.apache.camel.quarkus.component.ai.tool.CamelAiToolProvider;
+import org.apache.camel.quarkus.component.ai.tool.CamelAiToolsInterceptor;
+import org.jboss.jandex.AnnotationInstance;
+import org.jboss.jandex.AnnotationTarget;
+import org.jboss.jandex.DotName;
+import org.jboss.jandex.IndexView;
+import org.jboss.logging.Logger;
 
 class AiToolProcessor {
 
     private static final String FEATURE = "camel-ai-tool";
+    private static final DotName CAMEL_AI_TOOLS_DOTNAME = DotName
+            
.createSimple("org.apache.camel.quarkus.component.ai.tool.CamelAiTools");
+
+    private static final Logger LOG = Logger.getLogger(AiToolProcessor.class);
 
     @BuildStep
     FeatureBuildItem feature() {
         return new FeatureBuildItem(FEATURE);
     }
+
+    /**
+     * Diagnostics for {@code @CamelAiTools} usage. Runs unconditionally: the 
pieces the annotation needs are exactly
+     * what may be missing, so gating this on their presence would silence the 
report.
+     */
+    @BuildStep
+    void validateCamelAiToolsUsage(
+            CombinedIndexBuildItem combinedIndex,
+            BuildProducer<ValidationErrorBuildItem> validationErrors) {
+
+        List<AnnotationInstance> annotations = combinedIndex.getIndex()
+                .getAnnotations(CAMEL_AI_TOOLS_DOTNAME)
+                .stream()
+                .filter(annotation -> annotation.target().kind() == 
AnnotationTarget.Kind.CLASS)
+                .toList();
+
+        if (annotations.isEmpty()) {
+            return;
+        }
+
+        String affected = annotations.stream()
+                .map(annotation -> 
annotation.target().asClass().name().toString())
+                .collect(Collectors.joining(", "));
+
+        if (!new QuarkusLangchain4jPresent().getAsBoolean()) {
+            LOG.warnf("@CamelAiTools annotations found but Quarkus LangChain4j 
is not on the classpath. "
+                    + "The annotation only filters tools provided to Quarkus 
LangChain4j AI services, so it has no "
+                    + "effect here. Affected classes: %s", affected);
+            return;
+        }
+
+        if (!new AiToolPresent().getAsBoolean()) {
+            LOG.warnf("@CamelAiTools annotations found but 
camel-langchain4j-agent is not on the classpath. "
+                    + "Add camel-langchain4j-agent dependency to enable the 
Camel AI tool bridge. "
+                    + "Affected classes: %s", affected);
+            return;
+        }
+
+        for (AnnotationInstance annotation : annotations) {
+            if (annotation.value() == null || 
annotation.value().asString().isBlank()) {
+                validationErrors.produce(new ValidationErrorBuildItem(new 
ConfigurationException(
+                        "@CamelAiTools on " + 
annotation.target().asClass().name() + " has no tag. An empty tag "
+                                + "would expose every registered Camel AI tool 
to the service instead of the "
+                                + "intended subset. Give it the tag used by 
the ai-tool: routes it should see, or "
+                                + "remove the annotation to receive all tools 
deliberately.")));
+            }
+        }
+    }
+
+    @BuildStep(onlyIf = { QuarkusLangchain4jPresent.class, AiToolPresent.class 
})
+    AdditionalBeanBuildItem registerCamelAiToolProvider() {
+        LOG.info("Camel AI Tool detected - registering CamelAiToolProvider as 
CDI bean for ToolProvider auto-discovery");
+        return 
AdditionalBeanBuildItem.unremovableOf(CamelAiToolProvider.class);
+    }
+
+    @BuildStep(onlyIf = { QuarkusLangchain4jPresent.class, AiToolPresent.class 
})
+    AdditionalBeanBuildItem registerAiToolSpecConverter() {
+        return 
AdditionalBeanBuildItem.unremovableOf(AiToolSpecConverterImpl.class);
+    }
+
+    @BuildStep(onlyIf = { QuarkusLangchain4jPresent.class, AiToolPresent.class 
})
+    @Record(ExecutionTime.STATIC_INIT)
+    void configureCamelAiToolTags(
+            CombinedIndexBuildItem combinedIndex,
+            BuildProducer<AdditionalBeanBuildItem> additionalBeans,
+            AiToolRecorder recorder) {
+
+        IndexView index = combinedIndex.getIndex();
+        Map<String, String> tagMap = new HashMap<>();
+        for (AnnotationInstance annotation : 
index.getAnnotations(CAMEL_AI_TOOLS_DOTNAME)) {
+            if (annotation.target().kind() == AnnotationTarget.Kind.CLASS) {
+                String className = 
annotation.target().asClass().name().toString();
+                // Blank tags are rejected by validateCamelAiToolsUsage
+                if (annotation.value() == null || 
annotation.value().asString().isBlank()) {
+                    continue;
+                }
+                String tagValue = annotation.value().asString();
+                tagMap.put(className, tagValue);
+                LOG.infof("Discovered @CamelAiTools(\"%s\") on %s", tagValue, 
className);
+            }
+        }
+
+        if (tagMap.isEmpty()) {
+            return;
+        }
+
+        recorder.setCamelAiToolTagMap(tagMap);
+
+        additionalBeans.produce(AdditionalBeanBuildItem.builder()
+                .addBeanClasses(CamelAiToolsInterceptor.class)
+                .setUnremovable()
+                .build());
+    }
 }
diff --git 
a/extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/AiToolPresent.java
 
b/extensions/ai-tool/deployment/src/main/java/org/apache/camel/quarkus/component/ai/tool/deployment/QuarkusLangchain4jPresent.java
similarity index 64%
copy from 
extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/AiToolPresent.java
copy to 
extensions/ai-tool/deployment/src/main/java/org/apache/camel/quarkus/component/ai/tool/deployment/QuarkusLangchain4jPresent.java
index 7168ec2d43..0338954600 100644
--- 
a/extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/AiToolPresent.java
+++ 
b/extensions/ai-tool/deployment/src/main/java/org/apache/camel/quarkus/component/ai/tool/deployment/QuarkusLangchain4jPresent.java
@@ -14,20 +14,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.quarkus.component.support.langchain4j.deployment;
+package org.apache.camel.quarkus.component.ai.tool.deployment;
 
 import java.util.function.BooleanSupplier;
 
-public class AiToolPresent implements BooleanSupplier {
-    private static final String AI_TOOL_REGISTRY_CLASS = 
"org.apache.camel.component.ai.tool.AiToolRegistry";
-    private static final String AI_TOOL_SPEC_CONVERTER_CLASS = 
"org.apache.camel.component.langchain4j.agent.AiToolSpecToLangChain4j";
+public class QuarkusLangchain4jPresent implements BooleanSupplier {
+    private static final String REGISTER_AI_SERVICE_CLASS = 
"io.quarkiverse.langchain4j.RegisterAiService";
 
     @Override
     public boolean getAsBoolean() {
-        ClassLoader cl = Thread.currentThread().getContextClassLoader();
         try {
-            cl.loadClass(AI_TOOL_REGISTRY_CLASS);
-            cl.loadClass(AI_TOOL_SPEC_CONVERTER_CLASS);
+            
Thread.currentThread().getContextClassLoader().loadClass(REGISTER_AI_SERVICE_CLASS);
             return true;
         } catch (ClassNotFoundException e) {
             return false;
diff --git a/extensions/ai-tool/runtime/pom.xml 
b/extensions/ai-tool/runtime/pom.xml
index b2ae74c9b4..53a5da1b59 100644
--- a/extensions/ai-tool/runtime/pom.xml
+++ b/extensions/ai-tool/runtime/pom.xml
@@ -45,6 +45,21 @@
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-ai-tool</artifactId>
         </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-jackson</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>dev.langchain4j</groupId>
+            <artifactId>langchain4j</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-langchain4j-agent</artifactId>
+            <optional>true</optional>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/extensions/ai-tool/runtime/src/main/doc/usage.adoc 
b/extensions/ai-tool/runtime/src/main/doc/usage.adoc
index 555525dda4..cec5fd7f4c 100644
--- a/extensions/ai-tool/runtime/src/main/doc/usage.adoc
+++ b/extensions/ai-tool/runtime/src/main/doc/usage.adoc
@@ -49,4 +49,16 @@ endpoint's `tags` parameter — an agent without `tags` 
receives no registry too
 the agent's other sources, such as MCP clients or custom tool objects, are 
unaffected).
 Quarkus LangChain4j AI services (`@RegisterAiService` interfaces and 
programmatically built
 `AiServices`) receive every registered tool by default; restrict a service to 
a subset with
-the `@CamelAiTools` annotation.
+the `org.apache.camel.quarkus.component.ai.tool.CamelAiTools` annotation.
+
+[source,java]
+----
+@RegisterAiService
+@CamelAiTools("support")
+public interface SupportAgent {
+    String chat(@UserMessage String message);
+}
+----
+
+The tool provider backing this is registered automatically when Quarkus 
LangChain4j and
+`camel-langchain4j-agent` are on the classpath.
diff --git 
a/extensions/ai-tool/deployment/src/main/java/org/apache/camel/quarkus/component/ai/tool/deployment/AiToolProcessor.java
 
b/extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/AiToolRecorder.java
similarity index 70%
copy from 
extensions/ai-tool/deployment/src/main/java/org/apache/camel/quarkus/component/ai/tool/deployment/AiToolProcessor.java
copy to 
extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/AiToolRecorder.java
index e132611dd1..be4b8d4172 100644
--- 
a/extensions/ai-tool/deployment/src/main/java/org/apache/camel/quarkus/component/ai/tool/deployment/AiToolProcessor.java
+++ 
b/extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/AiToolRecorder.java
@@ -14,17 +14,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.quarkus.component.ai.tool.deployment;
+package org.apache.camel.quarkus.component.ai.tool;
 
-import io.quarkus.deployment.annotations.BuildStep;
-import io.quarkus.deployment.builditem.FeatureBuildItem;
+import java.util.Map;
 
-class AiToolProcessor {
+import io.quarkus.runtime.annotations.Recorder;
 
-    private static final String FEATURE = "camel-ai-tool";
+@Recorder
+public class AiToolRecorder {
 
-    @BuildStep
-    FeatureBuildItem feature() {
-        return new FeatureBuildItem(FEATURE);
+    public void setCamelAiToolTagMap(Map<String, String> tagMap) {
+        CamelAiToolProvider.TAG_MAP.putAll(tagMap);
     }
 }
diff --git 
a/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/AiToolSpecConverter.java
 
b/extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/AiToolSpecConverter.java
similarity index 95%
copy from 
extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/AiToolSpecConverter.java
copy to 
extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/AiToolSpecConverter.java
index adf443d9fc..9f749df0b1 100644
--- 
a/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/AiToolSpecConverter.java
+++ 
b/extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/AiToolSpecConverter.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.quarkus.component.support.langchain4j;
+package org.apache.camel.quarkus.component.ai.tool;
 
 import dev.langchain4j.agent.tool.ToolSpecification;
 import org.apache.camel.component.ai.tool.AiToolSpec;
diff --git 
a/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/AiToolSpecConverter.java
 
b/extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/AiToolSpecConverterImpl.java
similarity index 62%
rename from 
extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/AiToolSpecConverter.java
rename to 
extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/AiToolSpecConverterImpl.java
index adf443d9fc..adc8afb42c 100644
--- 
a/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/AiToolSpecConverter.java
+++ 
b/extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/AiToolSpecConverterImpl.java
@@ -14,16 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.quarkus.component.support.langchain4j;
+package org.apache.camel.quarkus.component.ai.tool;
 
 import dev.langchain4j.agent.tool.ToolSpecification;
+import jakarta.inject.Singleton;
 import org.apache.camel.component.ai.tool.AiToolSpec;
+import org.apache.camel.component.langchain4j.agent.AiToolSpecToLangChain4j;
 
 /**
- * Converts a Camel {@link AiToolSpec} to a langchain4j {@link 
ToolSpecification}.
- * The implementation is generated at build time via Gizmo to avoid a 
compile-time
- * dependency on {@code camel-langchain4j-agent}.
+ * Resolves {@code camel-langchain4j-agent} types, so it is only registered as 
a bean when that optional dependency is
+ * present. See {@code AiToolProcessor}.
  */
-public interface AiToolSpecConverter {
-    ToolSpecification toToolSpecification(AiToolSpec spec);
+@Singleton
+public class AiToolSpecConverterImpl implements AiToolSpecConverter {
+
+    @Override
+    public ToolSpecification toToolSpecification(AiToolSpec spec) {
+        return AiToolSpecToLangChain4j.toToolSpecification(spec);
+    }
 }
diff --git 
a/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/CamelAiToolProvider.java
 
b/extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/CamelAiToolProvider.java
similarity index 94%
rename from 
extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/CamelAiToolProvider.java
rename to 
extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/CamelAiToolProvider.java
index 2c8c093f69..b21f27a699 100644
--- 
a/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/CamelAiToolProvider.java
+++ 
b/extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/CamelAiToolProvider.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.quarkus.component.support.langchain4j;
+package org.apache.camel.quarkus.component.ai.tool;
 
 import java.util.Map;
 import java.util.Set;
@@ -39,10 +39,10 @@ import org.apache.camel.support.DefaultExchange;
 import org.jboss.logging.Logger;
 
 /**
- * Bridges Camel's {@link AiToolRegistry} to langchain4j's {@link 
ToolProvider} SPI. When registered as a CDI bean (done
- * automatically by the deployment processor when both {@code camel-ai-tool} 
and quarkus-langchain4j are on the
- * classpath), all Camel routes registered via {@code ai-tool:} endpoints 
become available to
- * {@code @RegisterAiService} AI services without explicit configuration.
+ * Bridges Camel's {@link AiToolRegistry} to langchain4j's {@link 
ToolProvider} SPI. The deployment processor registers
+ * it as a CDI bean when Quarkus LangChain4j and {@code 
camel-langchain4j-agent} are on the classpath, at which point
+ * all Camel routes registered via {@code ai-tool:} endpoints become available 
to {@code @RegisterAiService} AI
+ * services without explicit configuration.
  */
 public class CamelAiToolProvider implements ToolProvider {
 
diff --git 
a/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/CamelAiTools.java
 
b/extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/CamelAiTools.java
similarity index 96%
rename from 
extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/CamelAiTools.java
rename to 
extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/CamelAiTools.java
index 8be0eeacbf..0e2efde5ff 100644
--- 
a/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/CamelAiTools.java
+++ 
b/extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/CamelAiTools.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.quarkus.component.support.langchain4j;
+package org.apache.camel.quarkus.component.ai.tool;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Inherited;
diff --git 
a/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/CamelAiToolsInterceptor.java
 
b/extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/CamelAiToolsInterceptor.java
similarity index 97%
rename from 
extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/CamelAiToolsInterceptor.java
rename to 
extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/CamelAiToolsInterceptor.java
index 239ab26976..8ccd7ec0ff 100644
--- 
a/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/CamelAiToolsInterceptor.java
+++ 
b/extensions/ai-tool/runtime/src/main/java/org/apache/camel/quarkus/component/ai/tool/CamelAiToolsInterceptor.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.quarkus.component.support.langchain4j;
+package org.apache.camel.quarkus.component.ai.tool;
 
 import jakarta.annotation.Priority;
 import jakarta.interceptor.AroundInvoke;
diff --git 
a/extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/AiToolPresent.java
 
b/extensions/langchain4j-agent/deployment/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/deployment/CamelAiToolBridgeAbsent.java
similarity index 64%
rename from 
extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/AiToolPresent.java
rename to 
extensions/langchain4j-agent/deployment/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/deployment/CamelAiToolBridgeAbsent.java
index 7168ec2d43..2e2dcab46d 100644
--- 
a/extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/AiToolPresent.java
+++ 
b/extensions/langchain4j-agent/deployment/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/deployment/CamelAiToolBridgeAbsent.java
@@ -14,23 +14,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.quarkus.component.support.langchain4j.deployment;
+package org.apache.camel.quarkus.component.langchain4j.agent.deployment;
 
 import java.util.function.BooleanSupplier;
 
-public class AiToolPresent implements BooleanSupplier {
-    private static final String AI_TOOL_REGISTRY_CLASS = 
"org.apache.camel.component.ai.tool.AiToolRegistry";
-    private static final String AI_TOOL_SPEC_CONVERTER_CLASS = 
"org.apache.camel.component.langchain4j.agent.AiToolSpecToLangChain4j";
+/**
+ * Whether the LangChain4j tool bridge from {@code camel-quarkus-ai-tool} is 
missing. Without it, tools registered in
+ * the {@code AiToolRegistry} are not exposed to Quarkus LangChain4j AI 
services.
+ */
+public class CamelAiToolBridgeAbsent implements BooleanSupplier {
+    private static final String CAMEL_AI_TOOL_PROVIDER_CLASS = 
"org.apache.camel.quarkus.component.ai.tool.CamelAiToolProvider";
 
     @Override
     public boolean getAsBoolean() {
-        ClassLoader cl = Thread.currentThread().getContextClassLoader();
         try {
-            cl.loadClass(AI_TOOL_REGISTRY_CLASS);
-            cl.loadClass(AI_TOOL_SPEC_CONVERTER_CLASS);
-            return true;
-        } catch (ClassNotFoundException e) {
+            
Thread.currentThread().getContextClassLoader().loadClass(CAMEL_AI_TOOL_PROVIDER_CLASS);
             return false;
+        } catch (ClassNotFoundException e) {
+            return true;
         }
     }
 }
diff --git 
a/extensions/langchain4j-agent/deployment/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/deployment/Langchain4jAgentProcessor.java
 
b/extensions/langchain4j-agent/deployment/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/deployment/Langchain4jAgentProcessor.java
index 8498e4c5e8..716e51a6bb 100644
--- 
a/extensions/langchain4j-agent/deployment/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/deployment/Langchain4jAgentProcessor.java
+++ 
b/extensions/langchain4j-agent/deployment/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/deployment/Langchain4jAgentProcessor.java
@@ -20,10 +20,15 @@ import java.util.Set;
 import java.util.stream.Collectors;
 
 import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.annotations.ExecutionTime;
+import io.quarkus.deployment.annotations.Record;
 import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
 import io.quarkus.deployment.builditem.FeatureBuildItem;
 import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
 import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild;
+import 
org.apache.camel.quarkus.component.langchain4j.agent.Langchain4jAgentRecorder;
+import 
org.apache.camel.quarkus.component.support.langchain4j.deployment.QuarkusLangchain4jPresent;
+import 
org.apache.camel.quarkus.core.deployment.spi.RuntimeCamelContextCustomizerBuildItem;
 import org.jboss.jandex.ClassInfo;
 import org.jboss.jandex.DotName;
 
@@ -51,4 +56,12 @@ class Langchain4jAgentProcessor {
                 .methods(true)
                 .build();
     }
+
+    // The bridge that exposes registry tools to Quarkus LangChain4j AI 
services ships with camel-quarkus-ai-tool.
+    // Without that extension the tools are registered but silently 
unreachable, so flag it at startup.
+    @BuildStep(onlyIf = { QuarkusLangchain4jPresent.class, 
CamelAiToolBridgeAbsent.class })
+    @Record(ExecutionTime.RUNTIME_INIT)
+    RuntimeCamelContextCustomizerBuildItem 
warnAboutMissingAiToolBridge(Langchain4jAgentRecorder recorder) {
+        return new 
RuntimeCamelContextCustomizerBuildItem(recorder.createMissingAiToolBridgeWarning());
+    }
 }
diff --git a/extensions/langchain4j-agent/runtime/pom.xml 
b/extensions/langchain4j-agent/runtime/pom.xml
index 17e0a4f2af..deb3d53cc0 100644
--- a/extensions/langchain4j-agent/runtime/pom.xml
+++ b/extensions/langchain4j-agent/runtime/pom.xml
@@ -49,6 +49,10 @@
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-langchain4j-agent</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-ai-tool</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
diff --git 
a/extensions/langchain4j-agent/runtime/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/Langchain4jAgentRecorder.java
 
b/extensions/langchain4j-agent/runtime/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/Langchain4jAgentRecorder.java
new file mode 100644
index 0000000000..3eb26166d8
--- /dev/null
+++ 
b/extensions/langchain4j-agent/runtime/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/Langchain4jAgentRecorder.java
@@ -0,0 +1,60 @@
+/*
+ * 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.quarkus.component.langchain4j.agent;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import io.quarkus.runtime.RuntimeValue;
+import io.quarkus.runtime.annotations.Recorder;
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.ai.tool.AiToolRegistry;
+import org.apache.camel.component.ai.tool.AiToolSpec;
+import org.apache.camel.spi.CamelContextCustomizer;
+import org.apache.camel.support.LifecycleStrategySupport;
+import org.jboss.logging.Logger;
+
+@Recorder
+public class Langchain4jAgentRecorder {
+
+    private static final Logger LOG = 
Logger.getLogger(Langchain4jAgentRecorder.class);
+
+    /**
+     * Creates a customizer that warns when Camel AI tools are registered but 
the tool bridge shipped with
+     * {@code camel-quarkus-ai-tool} is absent, in which case Quarkus 
LangChain4j AI services receive no Camel tools.
+     * Tools are registered as {@code ai-tool:} routes start, so the check 
runs once the Camel context has started.
+     */
+    public RuntimeValue<CamelContextCustomizer> 
createMissingAiToolBridgeWarning() {
+        CamelContextCustomizer customizer = camelContext -> 
camelContext.addLifecycleStrategy(
+                LifecycleStrategySupport.adapt(
+                        
LifecycleStrategySupport.onCamelContextStarted(Langchain4jAgentRecorder::warnIfToolsRegistered)));
+        return new RuntimeValue<>(customizer);
+    }
+
+    private static void warnIfToolsRegistered(CamelContext camelContext) {
+        Set<AiToolSpec> tools = 
AiToolRegistry.getOrCreate(camelContext).getAllTools();
+        if (tools.isEmpty()) {
+            return;
+        }
+
+        LOG.warnf("Camel AI tools are registered but camel-quarkus-ai-tool is 
not on the classpath. "
+                + "Quarkus LangChain4j AI services will not receive them. "
+                + "Add the camel-quarkus-ai-tool dependency to enable the 
Camel AI tool bridge. "
+                + "Affected tools: %s",
+                
tools.stream().map(AiToolSpec::getName).sorted().collect(Collectors.joining(", 
")));
+    }
+}
diff --git a/integration-tests/ai-tool-langchain4j/pom.xml 
b/integration-tests/ai-tool-langchain4j/pom.xml
index f1a297c9f6..e26b0b399f 100644
--- a/integration-tests/ai-tool-langchain4j/pom.xml
+++ b/integration-tests/ai-tool-langchain4j/pom.xml
@@ -60,15 +60,10 @@
             <artifactId>quarkus-rest</artifactId>
         </dependency>
 
-        <!-- Camel Quarkus LangChain4j support (provides the AiToolRegistry → 
ToolProvider bridge) -->
+        <!-- Required at runtime for AiToolSpec → ToolSpecification conversion 
(optional in the ai-tool extension) -->
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
-            <artifactId>camel-quarkus-support-langchain4j</artifactId>
-        </dependency>
-        <!-- Required at runtime for AiToolSpec → ToolSpecification conversion 
(optional in support module) -->
-        <dependency>
-            <groupId>org.apache.camel</groupId>
-            <artifactId>camel-langchain4j-agent</artifactId>
+            <artifactId>camel-quarkus-langchain4j-agent</artifactId>
         </dependency>
         <!-- Quarkus LangChain4j -->
         <dependency>
@@ -192,7 +187,7 @@
                 </dependency>
                 <dependency>
                     <groupId>org.apache.camel.quarkus</groupId>
-                    
<artifactId>camel-quarkus-support-langchain4j-deployment</artifactId>
+                    
<artifactId>camel-quarkus-langchain4j-agent-deployment</artifactId>
                     <version>${project.version}</version>
                     <type>pom</type>
                     <scope>test</scope>
diff --git 
a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/AdminAiService.java
 
b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/AdminAiService.java
index b54e0d4d12..9943aa964d 100644
--- 
a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/AdminAiService.java
+++ 
b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/AdminAiService.java
@@ -19,8 +19,8 @@ package 
org.apache.camel.quarkus.component.ai.tool.langchain4j.it.service;
 import dev.langchain4j.service.UserMessage;
 import io.quarkiverse.langchain4j.RegisterAiService;
 import jakarta.enterprise.context.ApplicationScoped;
+import org.apache.camel.quarkus.component.ai.tool.CamelAiTools;
 import 
org.apache.camel.quarkus.component.ai.tool.langchain4j.it.model.AdminToolCallingChatModel;
-import org.apache.camel.quarkus.component.support.langchain4j.CamelAiTools;
 
 @ApplicationScoped
 @RegisterAiService(chatLanguageModelSupplier = AdminToolCallingChatModel.class)
diff --git 
a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiService.java
 
b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiService.java
index 9a1e8501a1..8a0679c09a 100644
--- 
a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiService.java
+++ 
b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiService.java
@@ -19,8 +19,8 @@ package 
org.apache.camel.quarkus.component.ai.tool.langchain4j.it.service;
 import dev.langchain4j.service.UserMessage;
 import io.quarkiverse.langchain4j.RegisterAiService;
 import jakarta.enterprise.context.ApplicationScoped;
+import org.apache.camel.quarkus.component.ai.tool.CamelAiTools;
 import 
org.apache.camel.quarkus.component.ai.tool.langchain4j.it.model.WeatherToolCallingChatModel;
-import org.apache.camel.quarkus.component.support.langchain4j.CamelAiTools;
 
 @ApplicationScoped
 @RegisterAiService(chatLanguageModelSupplier = 
WeatherToolCallingChatModel.class)
diff --git 
a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceOllama.java
 
b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceOllama.java
index 76829813cb..5fbb4415c2 100644
--- 
a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceOllama.java
+++ 
b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceOllama.java
@@ -19,7 +19,7 @@ package 
org.apache.camel.quarkus.component.ai.tool.langchain4j.it.service;
 import dev.langchain4j.service.UserMessage;
 import io.quarkiverse.langchain4j.RegisterAiService;
 import jakarta.enterprise.context.ApplicationScoped;
-import org.apache.camel.quarkus.component.support.langchain4j.CamelAiTools;
+import org.apache.camel.quarkus.component.ai.tool.CamelAiTools;
 
 @ApplicationScoped
 @RegisterAiService
diff --git 
a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceOllamaWrongTag.java
 
b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceOllamaWrongTag.java
index 1545589a33..b69596224f 100644
--- 
a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceOllamaWrongTag.java
+++ 
b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceOllamaWrongTag.java
@@ -19,7 +19,7 @@ package 
org.apache.camel.quarkus.component.ai.tool.langchain4j.it.service;
 import dev.langchain4j.service.UserMessage;
 import io.quarkiverse.langchain4j.RegisterAiService;
 import jakarta.enterprise.context.ApplicationScoped;
-import org.apache.camel.quarkus.component.support.langchain4j.CamelAiTools;
+import org.apache.camel.quarkus.component.ai.tool.CamelAiTools;
 
 @ApplicationScoped
 @RegisterAiService
diff --git 
a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceWrongTag.java
 
b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceWrongTag.java
index 7e4a78d7b6..929a950021 100644
--- 
a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceWrongTag.java
+++ 
b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceWrongTag.java
@@ -19,8 +19,8 @@ package 
org.apache.camel.quarkus.component.ai.tool.langchain4j.it.service;
 import dev.langchain4j.service.UserMessage;
 import io.quarkiverse.langchain4j.RegisterAiService;
 import jakarta.enterprise.context.ApplicationScoped;
+import org.apache.camel.quarkus.component.ai.tool.CamelAiTools;
 import 
org.apache.camel.quarkus.component.ai.tool.langchain4j.it.model.WrongTagToolCallingChatModel;
-import org.apache.camel.quarkus.component.support.langchain4j.CamelAiTools;
 
 @ApplicationScoped
 @RegisterAiService(chatLanguageModelSupplier = 
WrongTagToolCallingChatModel.class)
diff --git a/integration-tests/langchain4j-agent-ql4j/pom.xml 
b/integration-tests/langchain4j-agent-ql4j/pom.xml
index c58cc7e135..634ebcd4b9 100644
--- a/integration-tests/langchain4j-agent-ql4j/pom.xml
+++ b/integration-tests/langchain4j-agent-ql4j/pom.xml
@@ -43,6 +43,10 @@
     </dependencyManagement>
 
     <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-ai-tool</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
             <artifactId>camel-quarkus-direct</artifactId>
@@ -191,6 +195,19 @@
             </activation>
             <dependencies>
                 <!-- The following dependencies guarantee that this module is 
built after them. You can update them by running `mvn process-resources 
-Pformat -N` from the source tree root directory -->
+                <dependency>
+                    <groupId>org.apache.camel.quarkus</groupId>
+                    <artifactId>camel-quarkus-ai-tool-deployment</artifactId>
+                    <version>${project.version}</version>
+                    <type>pom</type>
+                    <scope>test</scope>
+                    <exclusions>
+                        <exclusion>
+                            <groupId>*</groupId>
+                            <artifactId>*</artifactId>
+                        </exclusion>
+                    </exclusions>
+                </dependency>
                 <dependency>
                     <groupId>org.apache.camel.quarkus</groupId>
                     <artifactId>camel-quarkus-direct-deployment</artifactId>

Reply via email to