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

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


The following commit(s) were added to refs/heads/main by this push:
     new e0855013fbdb CAMEL-25642: camel-jbang-mcp - migration and dependency 
tools answer with the right data (#27959)
e0855013fbdb is described below

commit e0855013fbdbfc3e7e811bf0b82415d8459d67a8
Author: Federico Mariani <[email protected]>
AuthorDate: Sat Oct 17 12:36:28 2026 +0200

    CAMEL-25642: camel-jbang-mcp - migration and dependency tools answer with 
the right data (#27959)
    
    camel_migration_recipes: the Spring Boot command activates
    CamelSpringBootMigrationRecipe (the recipe camel-spring-boot-upgrade-recipes
    ships), the recipes artifact is the <major>.<minor>.0 release of the target,
    the rewrite-maven-plugin is not pinned (as in the recipes README), and the
    Quarkus command runs quarkus-maven-plugin at a Quarkus platform version
    (new quarkusPlatformVersion argument, else the latest) with -Drewrite=true
    for a full run instead of the unknown -DrewriteFullRun.
    
    camel_migration_compatibility: the Java requirement comes from the JDK
    versions of the release in the catalog instead of "4.19+ requires Java 21";
    the unused camelComponents argument is removed.
    
    camel_dependency_check / camel_dependency_security_audit /
    camel_migration_analyze: Spring Boot starters and Camel Quarkus extensions
    count as their component (mapping shared with DependencyLookup), components
    only in dependencyManagement or plugin dependencies are not declared, the
    camel-quarkus-bom version is not taken as the Camel version nor recommended
    with it, the version is compared with the latest release instead of the
    SNAPSHOT catalog version, and the audit says when the Camel version is not
    detected, reports advisories without components as uncheckedAdvisories (not
    clean), and leaves reachable unset without routes.
    
    Runtime detection: a WAR is WildFly only without Spring Boot or Quarkus, and
    the war, bundle and WildFly plugins are read from <plugin> elements.
    
    camel_properties_translate: the Spring Boot only options keep their Spring
    Boot key (camel.springboot.lenient-configuration-binding) and have no
    equivalent on the other runtimes; camel.server.* is camel-main.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |  23 +++
 .../modules/ROOT/pages/camel-jbang-mcp.adoc        |  39 +++--
 .../jbang/core/commands/ai/DependencyLookup.java   |  47 ++++-
 .../dsl/jbang/core/commands/mcp/CamelReleases.java | 126 +++++++++++++
 .../core/commands/mcp/DependencyCheckTools.java    |  86 +++++----
 .../jbang/core/commands/mcp/DependencyData.java    |  51 +++++-
 .../commands/mcp/DependencySecurityAuditTools.java | 166 ++++++++++++------
 .../dsl/jbang/core/commands/mcp/MigrationData.java | 195 ++++++++++++++++-----
 .../jbang/core/commands/mcp/MigrationTools.java    | 189 ++++++++++++--------
 .../jbang/core/commands/mcp/PromptDefinitions.java |   8 +-
 .../commands/mcp/PropertiesTranslateTools.java     |  68 ++++++-
 .../jbang/core/commands/mcp/CamelReleasesTest.java |  60 +++++++
 .../commands/mcp/DependencyCheckToolsTest.java     |  99 ++++++++++-
 .../mcp/DependencySecurityAuditToolsTest.java      |  55 +++++-
 .../core/commands/mcp/MigrationToolsTest.java      | 189 ++++++++++++++++++++
 .../core/commands/mcp/PromptDefinitionsTest.java   |   4 +
 .../commands/mcp/PropertiesTranslateToolsTest.java |  16 ++
 17 files changed, 1180 insertions(+), 241 deletions(-)

diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 8077be87d3b3..83252eab6082 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -686,6 +686,29 @@ such as `+${body} == "hello"+`, and a slash (as in every 
XPath) or a quote in th
 * `get_route_analysis`, `get_eip_stats` and `detect_config_drift` have the 
answers of the integration as JSON, instead
   of as JSON text inside their JSON.
 
+=== camel-jbang-mcp - migration and dependency tools
+
+The `camel_migration_compatibility` tool no longer takes the `camelComponents` 
argument, which it never used. It
+now takes the Java requirement from the JDK versions of the target release in 
the catalog (the same data as
+`camel_version_list`): Camel 4.19 and later no longer report Java 17 as a 
blocker, and `javaCompatibility` also
+lists the `supportedVersions`. A target version newer than every release in 
the catalog has no Java requirement: the
+tool says so in `warnings` instead of taking the one of the newest release.
+
+`camel_migration_recipes` returns commands that run: the Spring Boot command 
activates
+`org.apache.camel.upgrade.CamelSpringBootMigrationRecipe`, the recipes 
artifact is the `<major>.<minor>.0` release
+of the target version, the OpenRewrite plugin is no longer pinned to a 
version, and the Quarkus command runs the
+`quarkus-maven-plugin` at the Quarkus platform version of the new optional 
`quarkusPlatformVersion` argument (or the
+latest one), with `-Drewrite=true` for a full run.
+
+In `camel_dependency_check` and `camel_dependency_security_audit` the 
dependencies are the declared ones: a
+component only in `dependencyManagement` or in the dependencies of a plugin no 
longer counts, and Spring Boot starters
+and Camel Quarkus extensions count as the component they provide. The version 
status of `camel_dependency_check`
+compares the project with the latest Camel release, added as `latestVersion`, 
instead of the catalog version of the
+server. The security audit no longer audits the server's catalog version when 
the `pom.xml` sets no Camel version:
+it audits the `camelVersion` argument, else lists every advisory of the 
declared components, and says so in
+`warnings`. Its `reachable` is null when no routes are given, advisories 
without components are returned as
+`uncheckedAdvisories`, and `clean` is true only when none of them apply.
+
 === camel-jpa, camel-hazelcast - an exchange marked rollback only is not 
consumed
 
 When a route marks the exchange rollback only (for example with 
`markRollbackOnly()` or `markRollbackOnlyLast()`)
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
index ad226858c497..4be96356681e 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
@@ -623,8 +623,12 @@ client to the next.
 | `camel_dependency_security_audit`
 | Audits a project's dependencies for known vulnerabilities: cross-references 
the `pom.xml` with the Camel
   security advisory database for the CVEs affecting each artifact at the 
project's Camel version, and reports
-  severity, affected and fixed version ranges, and whether the vulnerable 
component is actually used (reachable)
-  or only a transitive dependency. The POM content is sanitized to mask 
sensitive data.
+  severity, affected and fixed version ranges, and, when routes are given, 
whether the vulnerable component is
+  actually used by them (reachable). Spring Boot starters and Camel Quarkus 
extensions are audited as the component
+  they provide. When the `pom.xml` does not set the Camel version 
(parent-managed), the answer says so and audits
+  the `camelVersion` argument, or without it lists every advisory of the 
declared components. Advisories that name
+  no component cannot be matched to a dependency and are listed as 
`uncheckedAdvisories`; the audit is only
+  reported `clean` when there are none. The POM content is sanitized to mask 
sensitive data.
 |===
 
 ==== Security advisory data
@@ -660,9 +664,11 @@ The advisories are also browseable as MCP resources: 
`camel://security/advisorie
 
 | `camel_dependency_check`
 | Checks Camel project dependency hygiene given a `pom.xml` and optional route 
definitions. Detects outdated
-  Camel versions compared to the latest catalog release, identifies missing 
Maven dependencies for components
-  used in routes, and flags version conflicts between the Camel BOM and 
explicit dependency overrides.
-  Returns actionable recommendations with corrected dependency snippets.
+  Camel versions compared to the latest Camel release, identifies missing 
Maven dependencies for components
+  used in routes (a Spring Boot starter or Camel Quarkus extension counts as 
its component, and the snippet uses
+  the project's runtime artifact; a component only in `dependencyManagement` 
is not declared), and flags version
+  conflicts between the Camel BOM and explicit dependency overrides. Returns 
actionable recommendations with
+  corrected dependency snippets.
 |===
 
 === Validation and Transformation
@@ -683,7 +689,10 @@ The advisories are also browseable as MCP resources: 
`camel://security/advisorie
 
 | `camel_properties_translate`
 | Translate Camel configuration properties between runtimes (`main`, 
`spring-boot`, `quarkus`). Handles
-  runtime-specific keys like HTTP server and management endpoint configuration.
+  runtime-specific keys like HTTP server and management endpoint configuration 
(`+camel.server.*+` and
+  `+camel.management.*+` on Camel Main). Legacy `+camel.springboot.*+` keys 
become `+camel.main.*+`, except the Spring
+  Boot only options, which keep their Spring Boot key 
(`camel.springboot.lenient-configuration-binding`) and have
+  no equivalent on the other runtimes.
 |===
 
 === Route Diagram
@@ -734,16 +743,22 @@ help validate, scaffold, and provide mock guidance for 
that workflow.
 | `camel_migration_analyze`
 | Analyzes a Camel project's `pom.xml` to detect the runtime type (`main`, 
`spring-boot`, `quarkus`, `wildfly`,
   `karaf`), Camel version, Java version, and Camel component dependencies. 
This is the first step in a migration
-  workflow.
+  workflow. A WAR is `wildfly` only without Spring Boot or Quarkus, and the 
dependencies are the declared ones,
+  not those in `dependencyManagement` or of a plugin.
 
 | `camel_migration_compatibility`
-| Checks migration compatibility for Camel components by providing relevant 
migration guide URLs and Java version
-  requirements. The LLM consults the migration guides for detailed component 
rename mappings and API changes.
+| Checks whether a migration between two Camel versions is possible on the 
project's Java version, from the JDK
+  versions each release supports in the catalog (as `camel_version_list` 
reports them), and returns the relevant
+  migration guide URLs. The LLM consults the migration guides for detailed 
component rename mappings and API
+  changes.
 
 | `camel_migration_recipes`
-| Returns Maven commands to run Camel OpenRewrite migration recipes for 
upgrading between versions. The project
-  must compile successfully before running the recipes, as OpenRewrite 
requires a compilable project to parse
-  and transform the code.
+| Returns Maven commands to run Camel OpenRewrite migration recipes for 
upgrading between versions: the
+  `CamelMigrationRecipe` of `camel-upgrade-recipes` for Camel Main, the 
`CamelSpringBootMigrationRecipe` of
+  `camel-spring-boot-upgrade-recipes` for Spring Boot, and the `update` goal 
of the `quarkus-maven-plugin` for
+  Quarkus, at the Quarkus platform version given as `quarkusPlatformVersion` 
(the one `camel_version_list` reports
+  for the target Camel version), else at the latest recommended platform. The 
project must compile successfully
+  before running the recipes, as OpenRewrite requires a compilable project to 
parse and transform the code.
 
 | `camel_migration_guide_search`
 | Searches Camel migration and upgrade guides for a specific term or component 
name. Each result is one topic
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/DependencyLookup.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/DependencyLookup.java
index 68d5e3c2729a..d3f7700ce6c3 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/DependencyLookup.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/DependencyLookup.java
@@ -168,17 +168,60 @@ public final class DependencyLookup {
                     "org.apache.camel:camel-bom:" + camelVersion));
         }
         if (rt == null || rt.equals("spring-boot")) {
-            declare.put("pomSpringBoot", pom("org.apache.camel.springboot", 
"camel-" + shortName + "-starter", null,
+            RuntimeArtifact sb = runtimeArtifact("camel-" + shortName, 
"spring-boot");
+            declare.put("pomSpringBoot", pom(sb.groupId(), sb.artifactId(), 
null,
                     "org.apache.camel.springboot:camel-spring-boot-bom:" + 
camelVersion));
         }
         if (rt == null || rt.equals("quarkus")) {
-            declare.put("pomQuarkus", pom("org.apache.camel.quarkus", 
"camel-quarkus-" + shortName, null,
+            RuntimeArtifact q = runtimeArtifact("camel-" + shortName, 
"quarkus");
+            declare.put("pomQuarkus", pom(q.groupId(), q.artifactId(), null,
                     "org.apache.camel.quarkus:camel-quarkus-bom:<camel-quarkus 
version>"));
         }
         answer.put("declare", declare);
         return answer;
     }
 
+    /** The Maven coordinates (without version) of a Camel artifact in one 
runtime. */
+    public record RuntimeArtifact(String groupId, String artifactId) {
+    }
+
+    /**
+     * The artifact that provides a Camel artifact ({@code camel-kafka}) in a 
runtime: {@code camel-kafka} on Camel
+     * Main, {@code org.apache.camel.springboot:camel-kafka-starter} on Spring 
Boot and
+     * {@code org.apache.camel.quarkus:camel-quarkus-kafka} on Quarkus.
+     *
+     * @param camelArtifactId the artifact id of the Camel catalog, starting 
with {@code camel-}
+     * @param runtime         main, spring-boot or quarkus; null means main
+     */
+    public static RuntimeArtifact runtimeArtifact(String camelArtifactId, 
String runtime) {
+        String shortName = camelArtifactId.startsWith("camel-") ? 
camelArtifactId.substring(6) : camelArtifactId;
+        if ("spring-boot".equals(runtime)) {
+            return new RuntimeArtifact("org.apache.camel.springboot", "camel-" 
+ shortName + "-starter");
+        }
+        if ("quarkus".equals(runtime)) {
+            return new RuntimeArtifact("org.apache.camel.quarkus", 
"camel-quarkus-" + shortName);
+        }
+        return new RuntimeArtifact("org.apache.camel", "camel-" + shortName);
+    }
+
+    /**
+     * The reverse of {@link #runtimeArtifact}: the Camel catalog artifact id 
provided by an artifact of any runtime
+     * ({@code camel-kafka-starter} and {@code camel-quarkus-kafka} both give 
{@code camel-kafka}), or the artifact id
+     * itself when it is not a runtime-specific Camel artifact.
+     */
+    public static String camelArtifactId(String artifactId) {
+        if (artifactId == null) {
+            return null;
+        }
+        if (artifactId.startsWith("camel-quarkus-") && artifactId.length() > 
14) {
+            return "camel-" + artifactId.substring(14);
+        }
+        if (artifactId.startsWith("camel-") && artifactId.endsWith("-starter") 
&& artifactId.length() > 14) {
+            return artifactId.substring(0, artifactId.length() - 8);
+        }
+        return artifactId;
+    }
+
     /** A third-party library: the same coordinates in every runtime. */
     static JsonObject declare(JsonObject answer, String groupId, String 
artifactId, String version, String rt) {
         answer.put("groupId", groupId);
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/CamelReleases.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/CamelReleases.java
new file mode 100644
index 000000000000..2f217ee715a4
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/CamelReleases.java
@@ -0,0 +1,126 @@
+/*
+ * 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.dsl.jbang.core.commands.mcp;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.tooling.model.ReleaseModel;
+
+/**
+ * Reads the Camel releases known to the catalog ({@link 
CamelCatalog#camelReleases()}, the data behind
+ * {@code camel_version_list}): the latest release and the JDK versions a 
release supports.
+ */
+final class CamelReleases {
+
+    private static final Pattern RELEASE_VERSION = 
Pattern.compile("\\d+\\.\\d+\\.\\d+");
+
+    private CamelReleases() {
+    }
+
+    /**
+     * The newest Camel release in the catalog, or null when the catalog has 
no release data. A SNAPSHOT catalog version
+     * is not a release, so this is what a project should be compared with.
+     */
+    static String latest(CamelCatalog catalog) {
+        String latest = null;
+        for (ReleaseModel release : releases(catalog)) {
+            if (latest == null || 
DependencyCheckTools.compareVersions(release.getVersion(), latest) > 0) {
+                latest = release.getVersion();
+            }
+        }
+        return latest;
+    }
+
+    /**
+     * The release that tells the requirements of a version: the release 
itself, else the newest release of the same
+     * major.minor (a patch release does not change the JDK baseline). Returns 
null for any other version, including a
+     * version newer than every release in the catalog (such as the one in 
development): the catalog only learns a
+     * release once it is out, and a new minor version may drop a JDK version 
(Camel 4.24 drops Java 17).
+     */
+    static ReleaseModel forVersion(CamelCatalog catalog, String version) {
+        if (version == null || version.isBlank()) {
+            return null;
+        }
+        String plain = version.trim().replace("-SNAPSHOT", "");
+        String[] parts = plain.split("\\.");
+        String minor = parts.length >= 2 ? parts[0] + "." + parts[1] + "." : 
null;
+        ReleaseModel exact = null;
+        ReleaseModel sameMinor = null;
+        for (ReleaseModel release : releases(catalog)) {
+            String v = release.getVersion();
+            if (v.equals(plain)) {
+                exact = release;
+            }
+            if (minor != null && v.startsWith(minor)
+                    && (sameMinor == null || 
DependencyCheckTools.compareVersions(v, sameMinor.getVersion()) > 0)) {
+                sameMinor = release;
+            }
+        }
+        if (exact != null) {
+            return exact;
+        }
+        return sameMinor;
+    }
+
+    /** The JDK versions a release supports, oldest first (the catalog writes 
the old ones as 1.x). */
+    static List<Integer> jdks(ReleaseModel release) {
+        List<Integer> answer = new ArrayList<>();
+        if (release == null || release.getJdk() == null) {
+            return answer;
+        }
+        for (String jdk : release.getJdk().split(",")) {
+            int v = javaVersion(jdk);
+            if (v > 0) {
+                answer.add(v);
+            }
+        }
+        answer.sort(Integer::compare);
+        return answer;
+    }
+
+    /** A Java version as its feature number: 1.8 and 8 are 8, 21.0.2 is 21; 0 
when it cannot be read. */
+    static int javaVersion(String version) {
+        if (version == null || version.isBlank()) {
+            return 0;
+        }
+        try {
+            String v = version.trim();
+            if (v.startsWith("1.")) {
+                v = v.substring(2);
+            }
+            return Integer.parseInt(v.split("\\.")[0]);
+        } catch (NumberFormatException e) {
+            return 0;
+        }
+    }
+
+    private static List<ReleaseModel> releases(CamelCatalog catalog) {
+        List<ReleaseModel> answer = new ArrayList<>();
+        List<ReleaseModel> all = catalog.camelReleases();
+        if (all != null) {
+            for (ReleaseModel release : all) {
+                if (release.getVersion() != null && 
RELEASE_VERSION.matcher(release.getVersion()).matches()) {
+                    answer.add(release);
+                }
+            }
+        }
+        return answer;
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckTools.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckTools.java
index 0e9a419773ea..01e07e330fac 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckTools.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckTools.java
@@ -17,7 +17,10 @@
 package org.apache.camel.dsl.jbang.core.commands.mcp;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
 
 import jakarta.enterprise.context.ApplicationScoped;
 import jakarta.inject.Inject;
@@ -26,6 +29,7 @@ import io.quarkiverse.mcp.server.Tool;
 import io.quarkiverse.mcp.server.ToolArg;
 import io.quarkiverse.mcp.server.ToolCallException;
 import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.dsl.jbang.core.commands.ai.DependencyLookup;
 import org.apache.camel.dsl.jbang.core.commands.ai.RouteComponents;
 import org.apache.camel.tooling.model.ComponentModel;
 
@@ -50,8 +54,9 @@ public class DependencyCheckTools {
      */
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "Check Camel project dependency hygiene. Given a 
pom.xml (and optionally route definitions), "
-                        + "detects outdated Camel dependencies compared to the 
latest catalog version, "
-                        + "missing Maven dependencies for components used in 
routes, "
+                        + "detects an outdated Camel version compared to the 
latest Camel release, "
+                        + "missing Maven dependencies for components used in 
routes (Spring Boot starters and "
+                        + "Camel Quarkus extensions count as the component 
they provide), "
                         + "and version conflicts between the Camel BOM and 
explicit dependency overrides. "
                         + "Returns actionable recommendations with corrected 
dependency snippets. "
                         + "POM content is automatically sanitized to mask 
sensitive data (passwords, tokens, API keys) "
@@ -88,12 +93,12 @@ public class DependencyCheckTools {
                     pom.camelVersion(), pom.runtimeType(), pom.javaVersion(), 
pom.dependencies().size());
 
             // 1. Check for outdated Camel version
-            VersionStatus versionStatus = checkVersionStatus(pom, catalog);
+            VersionStatus versionStatus = checkVersionStatus(pom, catalog, 
catalogService.getDefaultCatalog());
 
             // 2. Check for missing dependencies from routes
             List<MissingDependency> missingDeps = List.of();
             if (routes != null && !routes.isBlank()) {
-                missingDeps = checkMissingDependencies(routes, 
pom.dependencies(), catalog);
+                missingDeps = checkMissingDependencies(routes, pom, catalog);
             }
 
             // 3. Check for version conflicts (explicit overrides when BOM is 
present)
@@ -120,36 +125,38 @@ public class DependencyCheckTools {
     }
 
     /**
-     * Check if the project's Camel version is outdated compared to the 
catalog version.
+     * Check if the project's Camel version is outdated compared to the latest 
Camel release known to the catalog (not
+     * the catalog version, which is a SNAPSHOT on a development build).
      */
-    private VersionStatus checkVersionStatus(MigrationData.PomAnalysis pom, 
CamelCatalog catalog) {
+    private VersionStatus checkVersionStatus(MigrationData.PomAnalysis pom, 
CamelCatalog catalog, CamelCatalog releases) {
         String catalogVersion = catalog.getCatalogVersion();
+        String latest = CamelReleases.latest(releases);
+        if (latest == null) {
+            latest = catalogVersion;
+        }
 
         if (pom.camelVersion() == null) {
             return new VersionStatus(
-                    catalogVersion, null, null, "unknown",
+                    catalogVersion, latest, null, null, "unknown",
                     "Could not detect Camel version from pom.xml. "
-                                                           + "Check if the 
version is defined in a parent POM or BOM.");
+                                                                   + "Check if 
the version is defined in a parent POM or BOM.");
         }
 
-        int comparison = compareVersions(pom.camelVersion(), catalogVersion);
+        int comparison = compareVersions(pom.camelVersion(), latest);
         if (comparison < 0) {
             return new VersionStatus(
-                    catalogVersion, pom.camelVersion(), true, "outdated",
-                    "Project uses Camel " + pom.camelVersion()
-                                                                          + " 
but " + catalogVersion
-                                                                          + " 
is available. "
-                                                                          + 
"Consider upgrading for bug fixes and new features.");
+                    catalogVersion, latest, pom.camelVersion(), true, 
"outdated",
+                    "Project uses Camel " + pom.camelVersion() + " but " + 
latest + " is the latest release. "
+                                                                               
   + "Consider upgrading for bug fixes and new features.");
         } else if (comparison == 0) {
             return new VersionStatus(
-                    catalogVersion, pom.camelVersion(), false, "current",
-                    "Project uses the latest Camel version.");
+                    catalogVersion, latest, pom.camelVersion(), false, 
"current",
+                    "Project uses the latest Camel release.");
         } else {
             return new VersionStatus(
-                    catalogVersion, pom.camelVersion(), false, "newer",
-                    "Project uses Camel " + pom.camelVersion()
-                                                                        + " 
which is newer than catalog version "
-                                                                        + 
catalogVersion + ".");
+                    catalogVersion, latest, pom.camelVersion(), false, "newer",
+                    "Project uses Camel " + pom.camelVersion() + " which is 
newer than the latest release "
+                                                                               
 + latest + ".");
         }
     }
 
@@ -157,8 +164,16 @@ public class DependencyCheckTools {
      * Check for components used in routes that are missing from the project's 
dependencies.
      */
     private List<MissingDependency> checkMissingDependencies(
-            String routes, List<String> existingDeps, CamelCatalog catalog) {
+            String routes, MigrationData.PomAnalysis pom, CamelCatalog 
catalog) {
         List<MissingDependency> missing = new ArrayList<>();
+        String runtime = pom.runtimeType();
+
+        // a Spring Boot starter or Camel Quarkus extension provides the 
component of the same name
+        Set<String> declared = new HashSet<>();
+        for (String dep : pom.dependencies()) {
+            declared.add(dep);
+            declared.add(DependencyLookup.camelArtifactId(dep));
+        }
 
         // the components of the routes' endpoints, not every name in the text 
(CAMEL-25586)
         for (String comp : RouteComponents.of(routes, null, catalog)) {
@@ -176,10 +191,13 @@ public class DependencyCheckTools {
                 continue;
             }
 
-            if (!existingDeps.contains(artifactId)) {
+            if (!declared.contains(artifactId)) {
+                DependencyLookup.RuntimeArtifact ga = 
"spring-boot".equals(runtime) || "quarkus".equals(runtime)
+                        ? DependencyLookup.runtimeArtifact(artifactId, runtime)
+                        : new 
DependencyLookup.RuntimeArtifact(model.getGroupId(), artifactId);
                 missing.add(new MissingDependency(
-                        comp, artifactId, model.getGroupId(), model.getTitle(),
-                        formatDependencySnippet(model.getGroupId(), 
artifactId)));
+                        comp, ga.artifactId(), ga.groupId(), model.getTitle(),
+                        formatDependencySnippet(ga.groupId(), 
ga.artifactId())));
             }
         }
 
@@ -195,6 +213,7 @@ public class DependencyCheckTools {
         boolean hasBom = pomContent.contains("camel-bom")
                 || pomContent.contains("camel-spring-boot-bom")
                 || pomContent.contains("camel-quarkus-bom")
+                || pomContent.contains("quarkus-camel-bom")
                 || pomContent.contains("camel-dependencies");
 
         if (!hasBom) {
@@ -233,9 +252,8 @@ public class DependencyCheckTools {
         if (versionStatus.outdated() != null && versionStatus.outdated()) {
             recommendations.add(new Recommendation(
                     "medium", "Version Upgrade",
-                    "Upgrade Camel from " + pom.camelVersion() + " to " + 
versionStatus.catalogVersion(),
-                    "Use camel_migration_compatibility and 
camel_migration_recipes tools "
-                                                                               
                           + "to check compatibility and get automated upgrade 
commands.",
+                    "Upgrade Camel from " + pom.camelVersion() + " to " + 
versionStatus.latestVersion(),
+                    "Use the camel_migration_recipes tool to get automated 
upgrade commands.",
                     null));
         }
 
@@ -336,8 +354,9 @@ public class DependencyCheckTools {
     }
 
     private boolean hasCamelBom(MigrationData.PomAnalysis pom) {
-        return pom.dependencies().stream()
-                .anyMatch(d -> d.endsWith("-bom") || 
d.equals("camel-dependencies"));
+        return Stream.concat(pom.dependencies().stream(), 
pom.managedDependencies().stream())
+                .anyMatch(d -> d.startsWith("camel-") && d.endsWith("-bom") || 
d.equals("camel-dependencies")
+                        || d.equals("quarkus-camel-bom"));
     }
 
     private String formatDependencySnippet(String groupId, String artifactId) {
@@ -348,7 +367,8 @@ public class DependencyCheckTools {
     }
 
     private String formatBomSnippet(MigrationData.PomAnalysis pom) {
-        return dependencyData.formatBomSnippet(pom.runtimeType(), 
pom.camelVersion());
+        return dependencyData.formatBomSnippet(
+                pom.runtimeType(), pom.camelVersion(), pom.quarkusVersion(), 
pom.camelQuarkusVersion());
     }
 
     // Result records
@@ -362,8 +382,12 @@ public class DependencyCheckTools {
     public record ProjectInfo(String camelVersion, String runtimeType, String 
javaVersion, int dependencyCount) {
     }
 
+    /**
+     * @param catalogVersion the version of the catalog this server answers 
from
+     * @param latestVersion  the latest Camel release, what the project 
version is compared with
+     */
     public record VersionStatus(
-            String catalogVersion, String projectVersion, Boolean outdated,
+            String catalogVersion, String latestVersion, String 
projectVersion, Boolean outdated,
             String status, String message) {
     }
 
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyData.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyData.java
index 92023349388b..350e855a0f17 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyData.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyData.java
@@ -92,19 +92,60 @@ public class DependencyData {
     }
 
     /**
-     * Format a BOM dependency management snippet for a given runtime and 
version.
+     * Format a BOM dependency management snippet for a given runtime and 
Camel version.
      */
-    public String formatBomSnippet(String runtime, String version) {
+    public String formatBomSnippet(String runtime, String camelVersion) {
+        return formatBomSnippet(runtime, camelVersion, null);
+    }
+
+    /**
+     * Format a BOM dependency management snippet for a given runtime, without 
a Camel Quarkus version.
+     */
+    public String formatBomSnippet(String runtime, String camelVersion, String 
quarkusPlatformVersion) {
+        return formatBomSnippet(runtime, camelVersion, quarkusPlatformVersion, 
null);
+    }
+
+    /**
+     * Format a BOM dependency management snippet for a given runtime.
+     * <p>
+     * The Camel version is the version of the Camel and Camel Spring Boot 
BOMs only: Camel Quarkus has its own version
+     * line, so a Quarkus project imports {@code 
io.quarkus.platform:quarkus-camel-bom} at the Quarkus platform version
+     * when it is known, else the camel-quarkus-bom at the Camel Quarkus 
version when it is known, else the
+     * camel-quarkus-bom with a version to look up.
+     *
+     * @param camelVersion           the Camel version, or null
+     * @param quarkusPlatformVersion the Quarkus platform version, or null
+     * @param camelQuarkusVersion    the Camel Quarkus version, or null
+     */
+    public String formatBomSnippet(
+            String runtime, String camelVersion, String 
quarkusPlatformVersion, String camelQuarkusVersion) {
         BomTemplate template = getBomTemplate(runtime);
         if (template == null) {
             template = BOM_TEMPLATES.get(0); // default to main
         }
-        String ver = version != null ? version : "${camel.version}";
+        String groupId = template.groupId();
+        String artifactId = template.artifactId();
+        String ver = camelVersion != null ? camelVersion : "${camel.version}";
+        String comment = "";
+        if ("quarkus".equals(template.runtime())) {
+            if (quarkusPlatformVersion != null) {
+                groupId = "io.quarkus.platform";
+                artifactId = "quarkus-camel-bom";
+                ver = quarkusPlatformVersion;
+            } else if (camelQuarkusVersion != null) {
+                ver = camelQuarkusVersion;
+            } else {
+                ver = "${camel-quarkus.version}";
+                comment = "            <!-- a Camel Quarkus version, not a 
Camel version: look it up with "
+                          + "camel_version_list runtime=quarkus -->\n";
+            }
+        }
         return "<dependencyManagement>\n"
                + "    <dependencies>\n"
                + "        <dependency>\n"
-               + "            <groupId>" + template.groupId() + "</groupId>\n"
-               + "            <artifactId>" + template.artifactId() + 
"</artifactId>\n"
+               + comment
+               + "            <groupId>" + groupId + "</groupId>\n"
+               + "            <artifactId>" + artifactId + "</artifactId>\n"
                + "            <version>" + ver + "</version>\n"
                + "            <type>pom</type>\n"
                + "            <scope>import</scope>\n"
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencySecurityAuditTools.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencySecurityAuditTools.java
index 43ca52fcd94a..1353b38ce5db 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencySecurityAuditTools.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencySecurityAuditTools.java
@@ -17,9 +17,11 @@
 package org.apache.camel.dsl.jbang.core.commands.mcp;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import jakarta.enterprise.context.ApplicationScoped;
 import jakarta.inject.Inject;
@@ -28,6 +30,7 @@ import io.quarkiverse.mcp.server.Tool;
 import io.quarkiverse.mcp.server.ToolArg;
 import io.quarkiverse.mcp.server.ToolCallException;
 import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.dsl.jbang.core.commands.ai.DependencyLookup;
 import org.apache.camel.dsl.jbang.core.commands.ai.RouteComponents;
 import org.apache.camel.tooling.model.ComponentModel;
 import org.apache.camel.tooling.model.SecurityAdvisoryModel;
@@ -51,10 +54,12 @@ public class DependencySecurityAuditTools {
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "Perform a security vulnerability audit on a Camel 
project's dependencies. "
-                        + "Cross-references the project's pom.xml dependencies 
with the Camel security advisory "
+                        + "Cross-references the project's pom.xml dependencies 
(Spring Boot starters and Camel Quarkus "
+                        + "extensions count as the component they provide) 
with the Camel security advisory "
                         + "database to identify CVEs affecting each artifact 
at the project's Camel version. "
-                        + "Reports severity, affected version ranges, fixed 
versions, and whether the vulnerable "
-                        + "component is directly used (reachable) or only a 
transitive dependency. "
+                        + "Reports severity, affected version ranges, fixed 
versions, and, when routes are given, "
+                        + "whether the vulnerable component is used by them 
(reachable). Advisories that name no "
+                        + "component cannot be matched to dependencies and are 
listed as uncheckedAdvisories. "
                         + "POM content is automatically sanitized to mask 
sensitive data.")
     public AuditResult camel_dependency_security_audit(
             @ToolArg(description = "The pom.xml file content") String 
pomContent,
@@ -62,7 +67,9 @@ public class DependencySecurityAuditTools {
                                    + "are actually used (for reachability 
analysis)",
                      required = false) String routes,
             @ToolArg(description = ToolArgDocs.RUNTIME, required = false) 
String runtime,
-            @ToolArg(description = ToolArgDocs.CAMEL_VERSION, required = 
false) String camelVersion,
+            @ToolArg(description = ToolArgDocs.CAMEL_VERSION
+                                   + " Also the version audited when the 
pom.xml does not set it (parent-managed).",
+                     required = false) String camelVersion,
             @ToolArg(description = ToolArgDocs.PLATFORM_BOM, required = false) 
String platformBom,
             @ToolArg(description = "If true (default), mask credentials in POM 
content",
                      required = false) Boolean sanitizePom) {
@@ -76,57 +83,69 @@ public class DependencySecurityAuditTools {
             CamelCatalog catalog = catalogService.loadCatalog(runtime, 
camelVersion, platformBom);
             MigrationData.PomAnalysis pom = 
MigrationData.parsePomContent(processed.content());
 
-            String effectiveVersion = pom.camelVersion() != null ? 
pom.camelVersion() : catalog.getCatalogVersion();
+            List<String> warnings = new ArrayList<>();
+            boolean versionDetected = pom.camelVersion() != null;
+            String effectiveVersion = pom.camelVersion();
+            if (!versionDetected) {
+                if (camelVersion != null && !camelVersion.isBlank() && 
Character.isDigit(camelVersion.trim().charAt(0))) {
+                    effectiveVersion = camelVersion.trim();
+                    warnings.add("Could not detect the Camel version from 
pom.xml (it is likely managed by a parent "
+                                 + "POM or a platform BOM): audited Camel " + 
effectiveVersion
+                                 + " from the camelVersion argument.");
+                } else {
+                    warnings.add("Could not detect the Camel version from 
pom.xml (it is likely managed by a parent "
+                                 + "POM or a platform BOM) and no camelVersion 
was given: every advisory naming a "
+                                 + "declared component is listed, whether or 
not it affects the project's version. "
+                                 + "Call again with camelVersion to audit that 
version.");
+                }
+            }
 
             List<SecurityAdvisoryModel> allAdvisories = 
advisoryService.advisories();
 
-            List<String> usedSchemes = routes != null && !routes.isBlank()
-                    ? extractUsedSchemes(routes, catalog) : List.of();
+            boolean routesGiven = routes != null && !routes.isBlank();
+            List<String> usedSchemes = routesGiven ? 
extractUsedSchemes(routes, catalog) : List.of();
 
             Map<String, ArtifactAudit> auditByArtifact = new LinkedHashMap<>();
+            Set<String> audited = new HashSet<>();
 
             for (String dep : pom.dependencies()) {
+                // camel-kafka-starter and camel-quarkus-kafka are camel-kafka 
for the advisories
+                String camelArtifact = DependencyLookup.camelArtifactId(dep);
+                audited.add(camelArtifact);
                 List<AdvisoryService.AdvisoryView> matched
-                        = AdvisoryService.query(allAdvisories, 
effectiveVersion, dep, null);
+                        = AdvisoryService.query(allAdvisories, 
effectiveVersion, camelArtifact, null);
 
                 if (!matched.isEmpty()) {
-                    boolean reachable = isReachable(dep, usedSchemes, catalog);
-                    List<VulnerabilityFinding> findings = new ArrayList<>();
-                    for (AdvisoryService.AdvisoryView adv : matched) {
-                        findings.add(new VulnerabilityFinding(
-                                adv.cve(),
-                                adv.summary(),
-                                adv.severity(),
-                                adv.affected(),
-                                adv.fixed(),
-                                adv.url()));
-                    }
-                    auditByArtifact.put(dep, new ArtifactAudit(dep, reachable, 
findings));
+                    Boolean reachable = routesGiven ? 
isReachable(camelArtifact, usedSchemes, catalog) : null;
+                    auditByArtifact.put(dep, new ArtifactAudit(dep, reachable, 
findings(matched)));
                 }
             }
 
             // Check components used in routes but not declared as POM 
dependencies (transitively available)
-            for (String compName : catalog.findComponentNames()) {
+            for (String compName : usedSchemes) {
                 ComponentModel model = catalog.componentModel(compName);
                 if (model == null || model.getArtifactId() == null) {
                     continue;
                 }
                 String artifactId = model.getArtifactId();
-                if (auditByArtifact.containsKey(artifactId)) {
+                if (!audited.add(artifactId)) {
                     continue;
                 }
-
                 List<AdvisoryService.AdvisoryView> matched
                         = AdvisoryService.query(allAdvisories, 
effectiveVersion, artifactId, null);
-                if (!matched.isEmpty() && usedSchemes.contains(compName)) {
-                    List<VulnerabilityFinding> findings = new ArrayList<>();
-                    for (AdvisoryService.AdvisoryView adv : matched) {
-                        findings.add(new VulnerabilityFinding(
-                                adv.cve(), adv.summary(), adv.severity(),
-                                adv.affected(), adv.fixed(),
-                                adv.url()));
+                if (!matched.isEmpty()) {
+                    auditByArtifact.put(artifactId, new 
ArtifactAudit(artifactId, true, findings(matched)));
+                }
+            }
+
+            // advisories that name no component can never match a dependency: 
list those that may affect the version
+            List<VulnerabilityFinding> unchecked = new ArrayList<>();
+            for (SecurityAdvisoryModel advisory : allAdvisories) {
+                if (advisory.getComponents() == null || 
advisory.getComponents().isEmpty()) {
+                    AdvisoryService.AdvisoryView view = 
AdvisoryService.AdvisoryView.of(advisory, effectiveVersion);
+                    if (!Boolean.FALSE.equals(view.affectsGivenVersion())) {
+                        unchecked.add(finding(view));
                     }
-                    auditByArtifact.put(artifactId, new 
ArtifactAudit(artifactId, true, findings));
                 }
             }
 
@@ -136,22 +155,27 @@ public class DependencySecurityAuditTools {
                     .flatMap(a -> a.findings().stream())
                     .filter(f -> "critical".equalsIgnoreCase(f.severity()) || 
"high".equalsIgnoreCase(f.severity()))
                     .count();
-            long reachableCount = 
vulnerableArtifacts.stream().filter(ArtifactAudit::reachable).count();
+            long reachableCount = vulnerableArtifacts.stream().filter(a -> 
Boolean.TRUE.equals(a.reachable())).count();
 
-            List<String> recommendations = 
buildRecommendations(vulnerableArtifacts, effectiveVersion, catalog);
+            List<String> recommendations
+                    = buildRecommendations(vulnerableArtifacts, unchecked, 
effectiveVersion, routesGiven);
 
             AuditSummary summary = new AuditSummary(
                     effectiveVersion,
+                    versionDetected,
                     pom.dependencies().size(),
                     vulnerableArtifacts.size(),
                     totalCves,
                     (int) criticalCount,
                     (int) reachableCount,
-                    totalCves == 0);
+                    unchecked.size(),
+                    totalCves == 0 && unchecked.isEmpty() && effectiveVersion 
!= null);
 
             return new AuditResult(
                     processed.warnings().isEmpty() ? null : 
processed.warnings(),
+                    warnings.isEmpty() ? null : warnings,
                     vulnerableArtifacts.isEmpty() ? null : vulnerableArtifacts,
+                    unchecked.isEmpty() ? null : unchecked,
                     recommendations.isEmpty() ? null : recommendations,
                     summary);
 
@@ -163,6 +187,18 @@ public class DependencySecurityAuditTools {
         }
     }
 
+    private static List<VulnerabilityFinding> 
findings(List<AdvisoryService.AdvisoryView> matched) {
+        List<VulnerabilityFinding> findings = new ArrayList<>();
+        for (AdvisoryService.AdvisoryView adv : matched) {
+            findings.add(finding(adv));
+        }
+        return findings;
+    }
+
+    private static VulnerabilityFinding finding(AdvisoryService.AdvisoryView 
adv) {
+        return new VulnerabilityFinding(adv.cve(), adv.summary(), 
adv.severity(), adv.affected(), adv.fixed(), adv.url());
+    }
+
     /**
      * The components the routes use: the components of their endpoints, read 
as routes of their DSL (CAMEL-25586).
      * Tokenizing the route text instead would treat the DSL's own structural 
keywords ({@code from:}, {@code to:},
@@ -172,10 +208,8 @@ public class DependencySecurityAuditTools {
         return RouteComponents.of(routes, null, catalog);
     }
 
+    /** Whether the routes use the component of the (catalog) artifact; only 
called when routes were given. */
     private boolean isReachable(String artifactId, List<String> usedSchemes, 
CamelCatalog catalog) {
-        if (usedSchemes.isEmpty()) {
-            return true;
-        }
         for (String scheme : usedSchemes) {
             ComponentModel model = catalog.componentModel(scheme);
             if (model != null && artifactId.equals(model.getArtifactId())) {
@@ -187,7 +221,8 @@ public class DependencySecurityAuditTools {
     }
 
     private List<String> buildRecommendations(
-            List<ArtifactAudit> vulnerableArtifacts, String version, 
CamelCatalog catalog) {
+            List<ArtifactAudit> vulnerableArtifacts, 
List<VulnerabilityFinding> unchecked, String version,
+            boolean routesGiven) {
         List<String> recs = new ArrayList<>();
 
         boolean hasCritical = vulnerableArtifacts.stream()
@@ -195,30 +230,38 @@ public class DependencySecurityAuditTools {
                 .anyMatch(f -> "critical".equalsIgnoreCase(f.severity()));
         if (hasCritical) {
             recs.add("URGENT: Critical vulnerabilities found. Upgrade Camel 
version immediately. "
-                     + "Use camel_migration_compatibility to check upgrade 
path.");
-        }
-
-        long reachableVulnCount = 
vulnerableArtifacts.stream().filter(ArtifactAudit::reachable).count();
-        if (reachableVulnCount > 0) {
-            recs.add("Found " + reachableVulnCount
-                     + " vulnerable artifact(s) that are directly used in 
routes. "
-                     + "These are reachable and should be prioritized for 
patching.");
+                     + "Use camel_migration_recipes to get the upgrade 
commands.");
         }
 
-        long unreachableCount = vulnerableArtifacts.stream().filter(a -> 
!a.reachable()).count();
-        if (unreachableCount > 0) {
-            recs.add("Found " + unreachableCount
-                     + " vulnerable artifact(s) not directly used in routes. "
-                     + "These are lower priority but should still be 
evaluated.");
+        if (routesGiven) {
+            long reachableVulnCount = vulnerableArtifacts.stream().filter(a -> 
Boolean.TRUE.equals(a.reachable())).count();
+            if (reachableVulnCount > 0) {
+                recs.add("Found " + reachableVulnCount
+                         + " vulnerable artifact(s) that are directly used in 
routes. "
+                         + "These are reachable and should be prioritized for 
patching.");
+            }
+            long unreachableCount = vulnerableArtifacts.stream().filter(a -> 
Boolean.FALSE.equals(a.reachable())).count();
+            if (unreachableCount > 0) {
+                recs.add("Found " + unreachableCount
+                         + " vulnerable artifact(s) not directly used in 
routes. "
+                         + "These are lower priority but should still be 
evaluated.");
+            }
+        } else if (!vulnerableArtifacts.isEmpty()) {
+            recs.add("Found " + vulnerableArtifacts.size() + " vulnerable 
artifact(s). Pass the routes to tell which "
+                     + "of them the routes use (reachable).");
         }
 
-        String latestVersion = catalog.getCatalogVersion();
-        if (DependencyCheckTools.compareVersions(version, latestVersion) < 0) {
+        String latestVersion = 
CamelReleases.latest(catalogService.getDefaultCatalog());
+        if (version != null && latestVersion != null && 
DependencyCheckTools.compareVersions(version, latestVersion) < 0) {
             recs.add("Upgrade from Camel " + version + " to " + latestVersion
                      + " to pick up security fixes. Use 
camel_migration_recipes for automated upgrade.");
         }
 
-        if (vulnerableArtifacts.isEmpty()) {
+        if (!unchecked.isEmpty()) {
+            recs.add(unchecked.size() + " advisory(ies) name no component, so 
they could not be matched to the "
+                     + "dependencies; review uncheckedAdvisories against the 
project.");
+        }
+        if (vulnerableArtifacts.isEmpty() && unchecked.isEmpty() && version != 
null) {
             recs.add("No known Camel CVEs affect the declared dependencies at 
version " + version + ".");
         }
 
@@ -229,14 +272,19 @@ public class DependencySecurityAuditTools {
 
     public record AuditResult(
             List<String> sanitizationWarnings,
+            List<String> warnings,
             List<ArtifactAudit> vulnerableArtifacts,
+            List<VulnerabilityFinding> uncheckedAdvisories,
             List<String> recommendations,
             AuditSummary summary) {
     }
 
+    /**
+     * @param reachable whether the routes use the artifact's component; null 
when no routes were given
+     */
     public record ArtifactAudit(
             String artifactId,
-            boolean reachable,
+            Boolean reachable,
             List<VulnerabilityFinding> findings) {
     }
 
@@ -249,13 +297,21 @@ public class DependencySecurityAuditTools {
             String advisoryUrl) {
     }
 
+    /**
+     * @param camelVersion         the audited Camel version: the project's, 
else the camelVersion argument, else null
+     * @param camelVersionDetected whether the version was read from the 
pom.xml
+     * @param uncheckedAdvisories  advisories that may affect the version but 
name no component to match
+     * @param clean                no matched CVE, no unchecked advisory, and 
a known version
+     */
     public record AuditSummary(
             String camelVersion,
+            boolean camelVersionDetected,
             int totalDependencies,
             int vulnerableArtifacts,
             int totalCves,
             int criticalAndHighCves,
             int reachableVulnerableArtifacts,
+            int uncheckedAdvisories,
             boolean clean) {
     }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationData.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationData.java
index e764c8a19d1e..4781c6f96013 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationData.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationData.java
@@ -44,6 +44,7 @@ import javax.xml.parsers.ParserConfigurationException;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
+import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
 import org.apache.commons.text.similarity.LevenshteinDistance;
@@ -94,7 +95,7 @@ public class MigrationData {
             "org.wildfly.camel", "org.wildfly",
             "org.wildfly.plugins",
             "javax.ejb", "jakarta.ejb",
-            "camel-cdi", "maven-war-plugin");
+            "camel-cdi");
 
     private static final List<String> KARAF_MARKERS = Arrays.asList(
             "org.apache.karaf", "camel-blueprint",
@@ -144,25 +145,31 @@ public class MigrationData {
 
     /**
      * Parse pom.xml content and extract project analysis data.
+     * <p>
+     * The declared Camel dependencies are the {@code <dependency>} elements 
of the project (and its profiles), not the
+     * ones in {@code <dependencyManagement>} (a BOM or a managed version 
declares nothing) nor the dependencies of a
+     * plugin. Runtime markers are read from dependencies, plugins and the 
parent alike.
      */
     public static PomAnalysis parsePomContent(String pomContent) throws 
Exception {
         DocumentBuilderFactory factory = createSecureDocumentBuilderFactory();
         Document doc = factory.newDocumentBuilder()
                 .parse(new 
ByteArrayInputStream(pomContent.getBytes(StandardCharsets.UTF_8)));
+        Element project = doc.getDocumentElement();
 
         String camelVersion = null;
+        String camelQuarkusVersion = null;
         String springBootVersion = null;
         String quarkusVersion = null;
         String javaVersion = null;
-        boolean isWildfly = false;
-        boolean isKaraf = false;
         List<String> dependencies = new ArrayList<>();
+        List<String> managedDependencies = new ArrayList<>();
 
-        // Detect packaging type (war packaging is a strong WildFly/app-server 
signal)
-        String packaging = getElementText(doc.getDocumentElement(), 
"packaging");
-        if ("war".equalsIgnoreCase(packaging)) {
-            isWildfly = true;
-        }
+        // war packaging alone says only that the project is deployed to a 
servlet container or application server:
+        // a Spring Boot WAR is deployed to Tomcat, so it is a WildFly signal 
only without Spring Boot or Quarkus
+        String packaging = getChildText(project, "packaging");
+        boolean war = "war".equalsIgnoreCase(packaging);
+        boolean wildfly = false;
+        boolean karaf = "bundle".equalsIgnoreCase(packaging);
 
         // Extract properties
         NodeList propertiesNodes = doc.getElementsByTagName("properties");
@@ -172,6 +179,7 @@ public class MigrationData {
             if (camelVersion == null) {
                 camelVersion = getElementText(properties, "camel-version");
             }
+            camelQuarkusVersion = getElementText(properties, 
"camel-quarkus.version");
             springBootVersion = getElementText(properties, 
"spring-boot.version");
             if (springBootVersion == null) {
                 springBootVersion = getElementText(properties, 
"spring-boot-version");
@@ -188,67 +196,146 @@ public class MigrationData {
                 javaVersion = getElementText(properties, 
"maven.compiler.target");
             }
         }
+        boolean springBoot = springBootVersion != null;
+        boolean quarkus = quarkusVersion != null;
+
+        Element parent = getChild(project, "parent");
+        if (parent != null) {
+            String artifactId = getChildText(parent, "artifactId");
+            String version = getChildText(parent, "version");
+            if ("spring-boot-starter-parent".equals(artifactId) || 
"spring-boot-dependencies".equals(artifactId)) {
+                springBoot = true;
+                if (springBootVersion == null && version != null && 
!version.startsWith("$")) {
+                    springBootVersion = version;
+                }
+            }
+        }
 
-        // Scan dependencyManagement and dependencies for version and runtime 
detection
+        // Scan dependencyManagement, dependencies and plugin dependencies for 
version and runtime detection
         NodeList allDeps = doc.getElementsByTagName("dependency");
         for (int i = 0; i < allDeps.getLength(); i++) {
             Element dep = (Element) allDeps.item(i);
-            String groupId = getElementText(dep, "groupId");
-            String artifactId = getElementText(dep, "artifactId");
-            String version = getElementText(dep, "version");
+            String groupId = getChildText(dep, "groupId");
+            String artifactId = getChildText(dep, "artifactId");
+            String version = getChildText(dep, "version");
 
             if (groupId == null || artifactId == null) {
                 continue;
             }
+            boolean fixedVersion = version != null && !version.startsWith("$");
 
-            // Detect Camel version from BOM
-            if (camelVersion == null && version != null && 
!version.startsWith("$")) {
-                if ("camel-bom".equals(artifactId) || 
"camel-spring-boot-bom".equals(artifactId)
-                        || "camel-quarkus-bom".equals(artifactId)) {
-                    camelVersion = version;
-                }
+            // Detect Camel version from BOM (the camel-quarkus-bom version is 
a Camel Quarkus version)
+            if (camelVersion == null && fixedVersion
+                    && ("camel-bom".equals(artifactId) || 
"camel-spring-boot-bom".equals(artifactId))) {
+                camelVersion = version;
+            }
+            if (camelQuarkusVersion == null && fixedVersion && 
"camel-quarkus-bom".equals(artifactId)) {
+                camelQuarkusVersion = version;
             }
 
-            // Detect Spring Boot
-            if (springBootVersion == null && version != null && 
!version.startsWith("$")) {
-                if ("spring-boot-dependencies".equals(artifactId)
-                        || "spring-boot-starter-parent".equals(artifactId)) {
-                    springBootVersion = version;
-                }
+            // Detect Spring Boot by the group: an artifact name with 
spring-boot is not enough, such as
+            // io.quarkus:quarkus-spring-boot-properties in a Camel Quarkus 
project
+            if ("org.springframework.boot".equals(groupId) || 
"org.apache.camel.springboot".equals(groupId)) {
+                springBoot = true;
+            }
+            if (springBootVersion == null && fixedVersion
+                    && ("spring-boot-dependencies".equals(artifactId)
+                            || 
"spring-boot-starter-parent".equals(artifactId))) {
+                springBootVersion = version;
             }
 
             // Detect Quarkus
-            if (quarkusVersion == null && version != null && 
!version.startsWith("$")) {
-                if ("quarkus-bom".equals(artifactId)) {
-                    quarkusVersion = version;
-                }
+            if (groupId.startsWith("io.quarkus") || 
artifactId.startsWith("camel-quarkus-")) {
+                quarkus = true;
             }
-
-            // Detect WildFly
-            for (String marker : WILDFLY_MARKERS) {
-                if (groupId.contains(marker) || artifactId.contains(marker)) {
-                    isWildfly = true;
-                }
+            if (quarkusVersion == null && fixedVersion
+                    && ("quarkus-bom".equals(artifactId) || 
"quarkus-camel-bom".equals(artifactId))) {
+                quarkusVersion = version;
             }
 
-            // Detect Karaf
-            for (String marker : KARAF_MARKERS) {
-                if (groupId.contains(marker) || artifactId.contains(marker)) {
-                    isKaraf = true;
-                }
-            }
+            wildfly |= matchesMarker(groupId, artifactId, WILDFLY_MARKERS);
+            karaf |= matchesMarker(groupId, artifactId, KARAF_MARKERS);
 
-            // Collect Camel dependencies
-            if (artifactId.startsWith("camel-")) {
+            if (hasAncestor(dep, "dependencyManagement")) {
+                managedDependencies.add(artifactId);
+            } else if (artifactId.startsWith("camel-") && !hasAncestor(dep, 
"plugin")) {
                 dependencies.add(artifactId);
             }
         }
 
+        // Plugins carry the WAR, WildFly and OSGi bundle markers
+        NodeList plugins = doc.getElementsByTagName("plugin");
+        for (int i = 0; i < plugins.getLength(); i++) {
+            Element plugin = (Element) plugins.item(i);
+            String groupId = getChildText(plugin, "groupId");
+            String artifactId = getChildText(plugin, "artifactId");
+            if (artifactId == null) {
+                continue;
+            }
+            if (groupId == null) {
+                groupId = "org.apache.maven.plugins";
+            }
+            if ("maven-war-plugin".equals(artifactId)) {
+                war = true;
+            } else if ("spring-boot-maven-plugin".equals(artifactId)) {
+                springBoot = true;
+            } else if ("quarkus-maven-plugin".equals(artifactId)) {
+                quarkus = true;
+            }
+            wildfly |= matchesMarker(groupId, artifactId, WILDFLY_MARKERS);
+            karaf |= matchesMarker(groupId, artifactId, KARAF_MARKERS);
+        }
+
+        if (war && !springBoot && !quarkus) {
+            wildfly = true;
+        }
+
         return new PomAnalysis(
-                camelVersion, springBootVersion, quarkusVersion, javaVersion,
-                dependencies, isWildfly, isKaraf);
+                camelVersion, camelQuarkusVersion, springBootVersion, 
quarkusVersion, javaVersion,
+                dependencies, managedDependencies, wildfly, karaf, springBoot, 
quarkus);
     }
 
+    private static boolean matchesMarker(String groupId, String artifactId, 
List<String> markers) {
+        for (String marker : markers) {
+            if (groupId.contains(marker) || artifactId.contains(marker)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean hasAncestor(Element element, String tagName) {
+        for (Node node = element.getParentNode(); node != null; node = 
node.getParentNode()) {
+            if (node instanceof Element e && tagName.equals(localName(e))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static Element getChild(Element parent, String tagName) {
+        for (Node node = parent.getFirstChild(); node != null; node = 
node.getNextSibling()) {
+            if (node instanceof Element e && tagName.equals(localName(e))) {
+                return e;
+            }
+        }
+        return null;
+    }
+
+    private static String getChildText(Element parent, String tagName) {
+        Element child = getChild(parent, tagName);
+        if (child != null) {
+            String text = child.getTextContent();
+            if (text != null && !text.isBlank()) {
+                return text.trim();
+            }
+        }
+        return null;
+    }
+
+    private static String localName(Element element) {
+        return element.getLocalName() != null ? element.getLocalName() : 
element.getTagName();
+    }
     // XML helper
 
     private static DocumentBuilderFactory createSecureDocumentBuilderFactory() 
{
@@ -827,14 +914,28 @@ public class MigrationData {
     private record GuideIndex(List<GuideSection> sections, Map<String, 
Integer> documentFrequencies, double averageLength) {
     }
 
+    /**
+     * What {@link #parsePomContent} reads from a pom.xml.
+     *
+     * @param camelVersion        the Camel version (camel.version property, 
camel-bom or camel-spring-boot-bom), or
+     *                            null
+     * @param camelQuarkusVersion the Camel Quarkus version 
(camel-quarkus.version property or camel-quarkus-bom), or
+     *                            null
+     * @param dependencies        the Camel artifacts the project declares as 
dependencies
+     * @param managedDependencies the artifacts in dependencyManagement (BOMs, 
managed versions): declared nowhere
+     */
     public record PomAnalysis(
             String camelVersion,
+            String camelQuarkusVersion,
             String springBootVersion,
             String quarkusVersion,
             String javaVersion,
             List<String> dependencies,
+            List<String> managedDependencies,
             boolean isWildfly,
-            boolean isKaraf) {
+            boolean isKaraf,
+            boolean isSpringBoot,
+            boolean isQuarkus) {
 
         /**
          * Determine the runtime type from the POM analysis.
@@ -846,10 +947,10 @@ public class MigrationData {
             if (isKaraf) {
                 return "karaf";
             }
-            if (springBootVersion != null || dependencies.stream().anyMatch(d 
-> d.contains("spring-boot"))) {
+            if (isSpringBoot) {
                 return "spring-boot";
             }
-            if (quarkusVersion != null || dependencies.stream().anyMatch(d -> 
d.contains("quarkus"))) {
+            if (isQuarkus) {
                 return "quarkus";
             }
             return "main";
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationTools.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationTools.java
index e5fe38144c71..7ec2bc9c343b 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationTools.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationTools.java
@@ -26,6 +26,7 @@ import jakarta.inject.Inject;
 import io.quarkiverse.mcp.server.Tool;
 import io.quarkiverse.mcp.server.ToolArg;
 import io.quarkiverse.mcp.server.ToolCallException;
+import org.apache.camel.tooling.model.ReleaseModel;
 
 /**
  * MCP Tools for Camel migration and upgrade workflows.
@@ -38,14 +39,20 @@ import io.quarkiverse.mcp.server.ToolCallException;
 @ApplicationScoped
 public class MigrationTools {
 
-    private static final String OPENREWRITE_VERSION = "6.29.0";
     private static final String CAMEL_UPGRADE_RECIPES_ARTIFACT = 
"camel-upgrade-recipes";
     private static final String CAMEL_SPRING_BOOT_UPGRADE_RECIPES_ARTIFACT = 
"camel-spring-boot-upgrade-recipes";
+    // the recipes of META-INF/rewrite/latest.yaml in each artifact of 
apache/camel-upgrade-recipes
+    private static final String CAMEL_MIGRATION_RECIPE = 
"org.apache.camel.upgrade.CamelMigrationRecipe";
+    private static final String CAMEL_SPRING_BOOT_MIGRATION_RECIPE
+            = "org.apache.camel.upgrade.CamelSpringBootMigrationRecipe";
     private static final int SNIPPET_LINES = 30;
 
     @Inject
     MigrationData migrationData;
 
+    @Inject
+    CatalogService catalogService;
+
     /**
      * Step 1: Analyze a project's pom.xml to detect runtime, Camel version, 
Java version, and components.
      */
@@ -91,7 +98,7 @@ public class MigrationTools {
             if ("wildfly".equals(runtimeType) || "karaf".equals(runtimeType)) {
                 nextStep = "Call camel_migration_wildfly_karaf to get 
migration guidance for " + runtimeType + ".";
             } else {
-                nextStep = "Call camel_migration_compatibility with the 
detected components and target version.";
+                nextStep = "Call camel_migration_compatibility with the 
detected and the target Camel version.";
             }
 
             return new ProjectAnalysisResult(
@@ -108,19 +115,16 @@ public class MigrationTools {
      * Step 2: Check compatibility and provide relevant migration guide 
references.
      */
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
-          description = "Check migration compatibility for Camel components by 
providing relevant migration guide "
-                        + "URLs and Java version requirements. The LLM should 
consult the migration guides for "
-                        + "detailed component rename mappings and API 
changes.")
+          description = "Check whether a migration between two Camel versions 
is possible on the project's Java version "
+                        + "(the JDK versions each Camel release supports, as 
camel_version_list reports them) and "
+                        + "return the relevant migration guide URLs. The LLM 
should consult the migration guides, or "
+                        + "camel_migration_guide_search, for component renames 
and API changes.")
     public CompatibilityResult camel_migration_compatibility(
-            @ToolArg(description = "Comma-separated list of Camel component 
artifactIds") String camelComponents,
             @ToolArg(description = "Current Camel version (e.g., 3.20.0)") 
String currentVersion,
             @ToolArg(description = "Target Camel version (e.g., 4.18.0)") 
String targetVersion,
             @ToolArg(description = ToolArgDocs.RUNTIME_REQUIRED) String 
runtime,
             @ToolArg(description = "Current Java version (e.g., 11, 17, 21)", 
required = false) String javaVersion) {
 
-        if (camelComponents == null || camelComponents.isBlank()) {
-            throw new ToolCallException("camelComponents is required", null);
-        }
         if (currentVersion == null || currentVersion.isBlank()) {
             throw new ToolCallException("currentVersion is required", null);
         }
@@ -138,30 +142,19 @@ public class MigrationTools {
         JavaCompatibility javaCompat = null;
 
         if (javaVersion != null && !javaVersion.isBlank()) {
-            int javaVer = parseJavaVersion(javaVersion);
-            boolean javaCompatible = true;
-            String requiredVersion;
-
-            if (targetMajor >= 4) {
-                int targetMinor = parseMinorVersion(targetVersion);
-                if (targetMajor > 4 || targetMinor >= 19) {
-                    requiredVersion = "21";
-                    if (javaVer < 21) {
-                        javaCompatible = false;
-                        blockers.add("Camel 4.19+ requires Java 21+. Current 
Java version is " + javaVersion + ".");
-                    }
-                } else {
-                    requiredVersion = "17";
-                    if (javaVer < 17) {
-                        javaCompatible = false;
-                        blockers.add("Camel 4.x requires Java 17+. Current 
Java version is " + javaVersion + ".");
-                    }
-                }
+            JavaRequirement requirement = javaRequirement(targetVersion);
+            if (requirement == null) {
+                warnings.add("The catalog has no release data for Camel " + 
targetVersion
+                             + ": check its Java requirement with 
camel_version_list.");
             } else {
-                requiredVersion = "11";
+                int javaVer = CamelReleases.javaVersion(javaVersion);
+                boolean javaCompatible = javaVer >= requirement.minimum();
+                if (!javaCompatible) {
+                    blockers.add(requirement.describe(targetVersion) + " 
Current Java version is " + javaVersion + ".");
+                }
+                javaCompat = new JavaCompatibility(
+                        javaVersion, String.valueOf(requirement.minimum()), 
requirement.supported(), javaCompatible);
             }
-
-            javaCompat = new JavaCompatibility(javaVersion, requiredVersion, 
javaCompatible);
         }
 
         // Collect relevant migration guides
@@ -208,7 +201,9 @@ public class MigrationTools {
      */
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "Get Maven commands to run Camel OpenRewrite migration 
recipes for upgrading between versions. "
-                        + "Returns the exact Maven commands to execute on the 
project. "
+                        + "Returns the exact Maven commands to execute on the 
project: camel-upgrade-recipes for "
+                        + "Camel Main, camel-spring-boot-upgrade-recipes for 
Spring Boot, and the quarkus-maven-plugin "
+                        + "update goal for Quarkus. "
                         + "PREREQUISITE: The project MUST compile successfully 
('mvn clean compile' must pass) "
                         + "BEFORE running the OpenRewrite recipes. If the 
project does not compile, fix the build "
                         + "errors first. OpenRewrite requires a compilable 
project to parse and transform the code.")
@@ -218,7 +213,11 @@ public class MigrationTools {
             @ToolArg(description = "Target Camel version (e.g., 4.18.0)") 
String targetVersion,
             @ToolArg(description = "Current Java version (e.g., 11, 17)", 
required = false) String javaVersion,
             @ToolArg(description = "If true, perform a dry run without making 
changes (default: true)",
-                     required = false) Boolean dryRun) {
+                     required = false) Boolean dryRun,
+            @ToolArg(description = "Quarkus only: the Quarkus platform version 
to update to (e.g. 3.27.1), the one "
+                                   + "camel_version_list runtime=quarkus 
reports for the target Camel version. "
+                                   + "Without it the update goes to the latest 
recommended Quarkus platform.",
+                     required = false) String quarkusPlatformVersion) {
 
         if (runtime == null || runtime.isBlank()) {
             throw new ToolCallException("runtime is required", null);
@@ -238,17 +237,25 @@ public class MigrationTools {
             // The quarkus-maven-plugin:update command internally uses 
OpenRewrite recipes
             // including Camel-specific recipes (e.g., 
io.quarkus.updates.camel.camel44.CamelQuarkusMigrationRecipe)
             // to handle both Quarkus platform and Camel Quarkus upgrades.
-            String quarkusCommand;
-            if (targetVersion != null && !targetVersion.isBlank()) {
-                quarkusCommand = String.format(
-                        "mvn --no-transfer-progress 
io.quarkus.platform:quarkus-maven-plugin:%s:update %s",
-                        targetVersion, isDryRun ? "-DrewriteDryRun" : 
"-DrewriteFullRun");
+            // The plugin is versioned like the Quarkus platform, not like 
Camel; its update goal reads
+            // platformVersion (or stream), rewriteDryRun and rewrite.
+            String rewriteMode = isDryRun ? "-DrewriteDryRun=true" : 
"-Drewrite=true";
+            if (quarkusPlatformVersion != null && 
!quarkusPlatformVersion.isBlank()) {
+                String qv = quarkusPlatformVersion.trim();
+                mavenCommands.add(String.format(
+                        "mvn --no-transfer-progress 
io.quarkus.platform:quarkus-maven-plugin:%s:update "
+                                                + "-DplatformVersion=%s %s",
+                        qv, qv, rewriteMode));
             } else {
-                quarkusCommand = String.format(
-                        "mvn --no-transfer-progress 
io.quarkus.platform:quarkus-maven-plugin:update %s",
-                        isDryRun ? "-DrewriteDryRun" : "-DrewriteFullRun");
+                mavenCommands.add(String.format(
+                        "mvn -U --no-transfer-progress 
io.quarkus.platform:quarkus-maven-plugin:update %s",
+                        rewriteMode));
+                notes.add("No quarkusPlatformVersion was given, so the update 
goes to the latest recommended Quarkus "
+                          + "platform, which may ship a newer Camel than " + 
targetVersion
+                          + ". To stop at Camel " + targetVersion
+                          + ", call camel_version_list with runtime=quarkus, 
take the Quarkus platform version of "
+                          + "that Camel release and call this tool again with 
quarkusPlatformVersion.");
             }
-            mavenCommands.add(quarkusCommand);
             notes.add("The quarkus-maven-plugin:update command uses 
OpenRewrite under the hood. "
                       + "It automatically applies both Quarkus platform 
upgrade recipes and Camel Quarkus "
                       + "migration recipes (e.g., 
CamelQuarkusMigrationRecipe). "
@@ -259,26 +266,36 @@ public class MigrationTools {
                       + "you must first create a new Quarkus project using the 
archetype from "
                       + "camel_migration_wildfly_karaf, then run this update 
command.");
         } else {
-            String artifact = "spring-boot".equals(resolvedRuntime)
-                    ? CAMEL_SPRING_BOOT_UPGRADE_RECIPES_ARTIFACT
-                    : CAMEL_UPGRADE_RECIPES_ARTIFACT;
+            boolean springBoot = "spring-boot".equals(resolvedRuntime);
+            String artifact = springBoot ? 
CAMEL_SPRING_BOOT_UPGRADE_RECIPES_ARTIFACT : CAMEL_UPGRADE_RECIPES_ARTIFACT;
+            String recipe = springBoot ? CAMEL_SPRING_BOOT_MIGRATION_RECIPE : 
CAMEL_MIGRATION_RECIPE;
+            String recipesVersion = recipesVersion(targetVersion);
 
+            // the recipes README runs the plugin without a version (with -U), 
so Maven resolves its latest release
             String command = String.format(
-                    "mvn --no-transfer-progress 
org.openrewrite.maven:rewrite-maven-plugin:%s:%s "
+                    "mvn -U --no-transfer-progress 
org.openrewrite.maven:rewrite-maven-plugin:%s "
                                            + 
"-Drewrite.recipeArtifactCoordinates=org.apache.camel.upgrade:%s:%s "
-                                           + 
"-Drewrite.activeRecipes=org.apache.camel.upgrade.CamelMigrationRecipe",
-                    OPENREWRITE_VERSION, runMode, artifact, targetVersion);
+                                           + "-Drewrite.activeRecipes=%s",
+                    runMode, artifact, recipesVersion, recipe);
             mavenCommands.add(command);
+            notes.add("The recipes are released once per Camel minor version 
(" + artifact + ":<major>.<minor>.0), "
+                      + "and " + recipe + " of version " + recipesVersion + " 
migrates the project to Camel "
+                      + recipesVersion + ". For a patch release, set the Camel 
version afterwards. If "
+                      + recipesVersion + " is not published (there is no 
recipes release for every minor version), "
+                      + "use the next published one, which migrates to its own 
version, or LATEST for the newest.");
         }
 
-        // Java upgrade suggestion
+        // Java upgrade suggestion, from the JDK versions of the target release
         List<String> javaUpgradeSuggestions = new ArrayList<>();
         if (javaVersion != null && !javaVersion.isBlank()) {
-            int javaVer = parseJavaVersion(javaVersion);
-            if (javaVer < 21) {
-                javaUpgradeSuggestions.add(
-                        "Camel 4.19+ requires Java 21+. "
-                                           + "OpenRewrite recipe: 
org.openrewrite.java.migrate.UpgradeToJava21");
+            JavaRequirement requirement = javaRequirement(targetVersion);
+            int javaVer = CamelReleases.javaVersion(javaVersion);
+            if (requirement != null && javaVer > 0 && javaVer < 
requirement.minimum()) {
+                String suggestion = requirement.describe(targetVersion) + " 
The project uses Java " + javaVersion + ".";
+                if (requirement.minimum() == 17 && 
!"quarkus".equals(resolvedRuntime)) {
+                    suggestion += " The Camel recipes include 
org.apache.camel.upgrade.UpgradeToJava17.";
+                }
+                javaUpgradeSuggestions.add(suggestion);
             }
         }
 
@@ -358,32 +375,47 @@ public class MigrationTools {
         }
     }
 
-    private int parseMinorVersion(String version) {
-        if (version == null) {
-            return 0;
-        }
-        try {
-            String[] parts = version.split("\\.");
-            if (parts.length >= 2) {
-                return Integer.parseInt(parts[1]);
-            }
-            return 0;
-        } catch (NumberFormatException e) {
-            return 0;
+    /**
+     * The camel-upgrade-recipes version for a target Camel version: the 
recipes are released per minor version.
+     */
+    static String recipesVersion(String targetVersion) {
+        String[] parts = targetVersion.trim().split("[.-]");
+        if (parts.length >= 2 && isNumber(parts[0]) && isNumber(parts[1])) {
+            return parts[0] + "." + parts[1] + ".0";
         }
+        return targetVersion.trim();
     }
 
-    private int parseJavaVersion(String version) {
-        if (version == null) {
-            return 0;
+    private static boolean isNumber(String s) {
+        return !s.isEmpty() && s.chars().allMatch(Character::isDigit);
+    }
+
+    /**
+     * The Java versions the target Camel version supports, from the catalog's 
release data, or null when the catalog
+     * does not know the version.
+     */
+    private JavaRequirement javaRequirement(String targetVersion) {
+        ReleaseModel release = 
CamelReleases.forVersion(catalogService.getDefaultCatalog(), targetVersion);
+        List<Integer> jdks = CamelReleases.jdks(release);
+        if (jdks.isEmpty()) {
+            return null;
         }
-        try {
-            if (version.startsWith("1.")) {
-                return Integer.parseInt(version.substring(2));
-            }
-            return Integer.parseInt(version.split("\\.")[0]);
-        } catch (NumberFormatException e) {
-            return 0;
+        return new JavaRequirement(
+                release.getVersion(), jdks.get(0),
+                
jdks.stream().map(String::valueOf).collect(Collectors.joining(", ")));
+    }
+
+    /**
+     * @param release   the release whose data was used (the target itself, or 
the release of the same line)
+     * @param minimum   the oldest supported Java version
+     * @param supported the supported Java versions, e.g. "17, 21, 25"
+     */
+    private record JavaRequirement(String release, int minimum, String 
supported) {
+
+        String describe(String targetVersion) {
+            String source = release.equals(targetVersion.trim()) ? "" : " (as 
Camel " + release + ")";
+            return "Camel " + targetVersion.trim() + source + " requires Java 
" + minimum + "+ (supported: "
+                   + supported + ").";
         }
     }
 
@@ -409,9 +441,14 @@ public class MigrationTools {
             String nextStep) {
     }
 
+    /**
+     * @param requiredVersion   the oldest Java version the target Camel 
version supports
+     * @param supportedVersions the Java versions the target Camel version 
supports, e.g. "17, 21, 25"
+     */
     public record JavaCompatibility(
             String currentVersion,
             String requiredVersion,
+            String supportedVersions,
             boolean compatible) {
     }
 
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
index bb8b13d151de..92383d9696b3 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
@@ -133,9 +133,11 @@ public class PromptDefinitions {
                         3. **Compatibility** (based on detected runtime):
                            - **wildfly** or **karaf**: call 
`camel_migration_wildfly_karaf` with the pom, target runtime, and target 
version.
                            - **main**, **spring-boot**, or **quarkus**: call 
`camel_migration_compatibility` \
-                        with components, current/target version, runtime, and 
Java version.
-                        4. **Recipes**: call `camel_migration_recipes` with 
runtime, versions, Java version, and `dryRun=true` \
-                        to get the OpenRewrite Maven commands.
+                        with `currentVersion`, `targetVersion`, `runtime`, and 
`javaVersion`.
+                        4. **Recipes**: call `camel_migration_recipes` with 
`runtime`, `currentVersion`, `targetVersion`, \
+                        `javaVersion`, and `dryRun=true` to get the 
OpenRewrite Maven commands. For **quarkus**, also pass \
+                        `quarkusPlatformVersion`: the Quarkus version 
`camel_version_list` with `runtime=quarkus` reports \
+                        for the target version (otherwise the update goes to 
the latest Quarkus platform).
                         5. **Breaking changes**: for each detected component, 
call `camel_migration_guide_search` \
                         with the current and target Camel versions as 
`currentVersion` and `targetVersion` \
                         to find renames and API changes.
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PropertiesTranslateTools.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PropertiesTranslateTools.java
index 17a4d2fbc768..675f90eb86bd 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PropertiesTranslateTools.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PropertiesTranslateTools.java
@@ -35,8 +35,9 @@ import io.quarkiverse.mcp.server.ToolCallException;
  *
  * Most Camel properties are runtime-agnostic ({@code camel.main.*}, {@code 
camel.component.*}, etc.) and are returned
  * unchanged. The properties that actually differ between runtimes are the 
embedded HTTP server and management endpoints
- * ({@code camel.server.*} / {@code camel.management.*} are jbang-only) and 
the legacy {@code camel.springboot.*}
- * configuration that was harmonized to {@code camel.main.*} in Camel 4.5 and 
removed in 4.13.
+ * ({@code camel.server.*} / {@code camel.management.*} are camel-main's, with 
camel-platform-http-main) and the legacy
+ * {@code camel.springboot.*} configuration that was harmonized to {@code 
camel.main.*} in Camel 4.5 and removed in
+ * 4.13, except the options that exist on Spring Boot only.
  */
 @McpSecured
 @ApplicationScoped
@@ -151,6 +152,21 @@ public class PropertiesTranslateTools {
         return m;
     }
 
+    /**
+     * The options of camel-spring-boot that camel-main does not have, by 
their name after {@code camel.springboot.}
+     * (lower case, without dashes), with their key on Spring Boot today. In 
4.13 the deprecated
+     * {@code camel.springboot.*} options were removed and these moved to 
{@code camel.main.*}, except
+     * lenient-configuration-binding, which keeps its {@code 
camel.springboot.} name; main-run-controller is deprecated
+     * in favour of run-controller since 4.14.
+     */
+    private static final Map<String, String> SPRING_BOOT_ONLY_OPTIONS = Map.of(
+            "lenientconfigurationbinding", 
"camel.springboot.lenient-configuration-binding",
+            "mainruncontroller", "camel.main.run-controller",
+            "runcontroller", "camel.main.run-controller",
+            "includenonsingletons", "camel.main.include-non-singletons",
+            "warnonearlyshutdown", "camel.main.warn-on-early-shutdown",
+            "typeconversion", "camel.main.type-conversion");
+
     /**
      * Camel property prefixes that are runtime-agnostic and must NOT be 
rewritten when changing runtime.
      */
@@ -195,13 +211,17 @@ public class PropertiesTranslateTools {
                         +
                         "actually differ are the embedded HTTP server and 
management endpoint configuration "
                         +
-                        "(camel.server.* / camel.management.* are jbang-only; 
camel-spring-boot uses server.* / "
+                        "(camel.server.* / camel.management.* on camel-main; 
camel-spring-boot uses server.* / "
                         +
                         "management.* via Spring Boot; camel-quarkus uses 
quarkus.http.* / quarkus.management.*). "
                         +
                         "Legacy camel.springboot.* keys are rewritten to 
camel.main.* (deprecated in 4.5, removed in "
                         +
-                        "4.13). Properties without a known translation are 
passed through with status=unknown so the "
+                        "4.13), except the Spring Boot only options 
(camel.springboot.lenient-configuration-binding "
+                        +
+                        "keeps its name), which have no equivalent on the 
other runtimes. "
+                        +
+                        "Properties without a known translation are passed 
through with status=unknown so the "
                         +
                         "caller can review them — the tool never guesses.")
     public PropertiesTranslateResult camel_properties_translate(
@@ -269,7 +289,23 @@ public class PropertiesTranslateTools {
         String value = line.substring(eq + 1);
         String key = rawKey.trim();
 
-        // 1. Legacy cleanup: camel.springboot.* is deprecated and removed 
since 4.13.
+        // 1. Spring Boot only options: no equivalent on camel-main or Quarkus
+        String springBootOnly = springBootOnlyOption(key);
+        if (springBootOnly != null && (key.startsWith("camel.springboot.") || 
from == Runtime.SPRING_BOOT)) {
+            if (to != Runtime.SPRING_BOOT) {
+                return new TranslatedLine(
+                        lineNumber, line, line, "unknown",
+                        "Spring Boot only option (" + springBootOnly + "): no 
equivalent in " + to.label());
+            }
+            if (springBootOnly.equals(key) || 
!key.startsWith("camel.springboot.")) {
+                return new TranslatedLine(lineNumber, line, line, "unchanged", 
null);
+            }
+            return new TranslatedLine(
+                    lineNumber, line, springBootOnly + "=" + value, 
"translated",
+                    "Legacy camel.springboot.* option is " + springBootOnly + 
" since Camel 4.13");
+        }
+
+        // 2. Legacy cleanup: camel.springboot.* is deprecated and removed 
since 4.13.
         // Rewrite to camel.main.* regardless of source/target runtime.
         if (key.startsWith("camel.springboot.")) {
             String newKey = "camel.main." + 
key.substring("camel.springboot.".length());
@@ -279,7 +315,7 @@ public class PropertiesTranslateTools {
                     "Legacy camel.springboot.* prefix rewritten to 
camel.main.* (deprecated since Camel 4.5, removed in 4.13)");
         }
 
-        // 2. Known per-runtime physical keys -> look up the canonical, then 
re-emit for the target.
+        // 3. Known per-runtime physical keys -> look up the canonical, then 
re-emit for the target.
         String canonical = KEY_TO_CANONICAL.get(from).get(key);
         if (canonical != null) {
             if (from == to) {
@@ -296,20 +332,36 @@ public class PropertiesTranslateTools {
             return new TranslatedLine(lineNumber, line, translatedLine, 
"translated", null);
         }
 
-        // 3. Camel-agnostic prefixes are identical across runtimes.
+        // 4. Camel-agnostic prefixes are identical across runtimes.
         for (String prefix : CAMEL_AGNOSTIC_PREFIXES) {
             if (key.startsWith(prefix)) {
                 return new TranslatedLine(lineNumber, line, line, "unchanged", 
null);
             }
         }
 
-        // 4. Anything else (server.*, quarkus.datasource.*, spring.jpa.*, 
logging.*, etc.) is out of scope —
+        // 5. Anything else (server.*, quarkus.datasource.*, spring.jpa.*, 
logging.*, etc.) is out of scope —
         // we never guess. Pass through unchanged with a note.
         return new TranslatedLine(
                 lineNumber, line, line, "unknown",
                 "No translation rule for this key; passed through unchanged");
     }
 
+    /**
+     * The Spring Boot key of a Spring Boot only option written as {@code 
camel.springboot.<name>} or
+     * {@code camel.main.<name>} (any relaxed-binding spelling), else null.
+     */
+    private static String springBootOnlyOption(String key) {
+        String name;
+        if (key.startsWith("camel.springboot.")) {
+            name = key.substring("camel.springboot.".length());
+        } else if (key.startsWith("camel.main.")) {
+            name = key.substring("camel.main.".length());
+        } else {
+            return null;
+        }
+        return SPRING_BOOT_ONLY_OPTIONS.get(name.replace("-", "").replace("_", 
"").toLowerCase(Locale.ENGLISH));
+    }
+
     // Result record classes for Jackson serialization
 
     public record PropertiesTranslateResult(String fromRuntime, String 
toRuntime, TranslateSummary summary,
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/CamelReleasesTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/CamelReleasesTest.java
new file mode 100644
index 000000000000..00ab07182c54
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/CamelReleasesTest.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.dsl.jbang.core.commands.mcp;
+
+import java.util.List;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.tooling.model.ReleaseModel;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class CamelReleasesTest {
+
+    private static final CamelCatalog CATALOG = new DefaultCamelCatalog() {
+        @Override
+        public List<ReleaseModel> camelReleases() {
+            return List.of(release("4.22.0", "17,21,25"), release("4.22.1", 
"17,21,25"), release("4.23.0", "17,21,25"));
+        }
+    };
+
+    @Test
+    void exactAndSameMinorReleases() {
+        assertThat(CamelReleases.forVersion(CATALOG, 
"4.22.0").getVersion()).isEqualTo("4.22.0");
+        // a patch release does not change the JDK baseline: the newest 
release of the same minor version tells it
+        assertThat(CamelReleases.forVersion(CATALOG, 
"4.22.5").getVersion()).isEqualTo("4.22.1");
+        assertThat(CamelReleases.forVersion(CATALOG, 
"4.23.1-SNAPSHOT").getVersion()).isEqualTo("4.23.0");
+    }
+
+    @Test
+    void noReleaseForAVersionNewerThanTheCatalog() {
+        // the catalog only learns a release once it is out, and a new minor 
version may drop a JDK version
+        assertThat(CamelReleases.forVersion(CATALOG, "4.24.0")).isNull();
+        assertThat(CamelReleases.forVersion(CATALOG, 
"4.24.0-SNAPSHOT")).isNull();
+        assertThat(CamelReleases.forVersion(CATALOG, "5.0.0")).isNull();
+        assertThat(CamelReleases.forVersion(CATALOG, "4.21.0")).isNull();
+    }
+
+    private static ReleaseModel release(String version, String jdk) {
+        ReleaseModel release = new ReleaseModel();
+        release.setVersion(version);
+        release.setJdk(jdk);
+        return release;
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckToolsTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckToolsTest.java
index 27a6a6cecd5a..46227fa9081e 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckToolsTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckToolsTest.java
@@ -181,10 +181,107 @@ class DependencyCheckToolsTest {
         DependencyCheckTools.DependencyCheckResult result
                 = tools.camel_dependency_check(POM_WITH_BOM, null, null, null, 
null, null);
 
-        // 4.10.0 is older than the catalog version (4.24.0-SNAPSHOT)
+        // 4.10.0 is older than the latest release, which is a release and not 
the SNAPSHOT catalog version
         assertThat(result.versionStatus().status()).isEqualTo("outdated");
         assertThat(result.versionStatus().outdated()).isTrue();
         assertThat(result.versionStatus().catalogVersion()).isNotEmpty();
+        
assertThat(result.versionStatus().latestVersion()).matches("\\d+\\.\\d+\\.\\d+");
+        assertThat(result.recommendations()).filteredOn(r -> 
r.category().equals("Version Upgrade"))
+                .singleElement().satisfies(r -> 
assertThat(r.action()).doesNotContain("SNAPSHOT"));
+    }
+
+    @Test
+    void runtimeArtifactsProvideTheirComponent() {
+        String route = "from:\n  uri: kafka:myTopic\n  steps:\n    - to: 
jms:queue:out";
+        String springBoot = 
POM_SPRING_BOOT.replace("<artifactId>camel-spring-boot-starter</artifactId>",
+                "<artifactId>camel-kafka-starter</artifactId>");
+        String quarkus = """
+                <project>
+                    <dependencyManagement>
+                        <dependencies>
+                            <dependency>
+                                <groupId>io.quarkus.platform</groupId>
+                                <artifactId>quarkus-camel-bom</artifactId>
+                                <version>${quarkus.platform.version}</version>
+                                <type>pom</type>
+                                <scope>import</scope>
+                            </dependency>
+                            <dependency>
+                                <groupId>org.apache.camel.quarkus</groupId>
+                                <artifactId>camel-quarkus-jms</artifactId>
+                                <version>3.27.0</version>
+                            </dependency>
+                        </dependencies>
+                    </dependencyManagement>
+                    <dependencies>
+                        <dependency>
+                            <groupId>org.apache.camel.quarkus</groupId>
+                            <artifactId>camel-quarkus-kafka</artifactId>
+                        </dependency>
+                    </dependencies>
+                </project>
+                """;
+
+        // camel-kafka-starter and camel-quarkus-kafka are camel-kafka; jms is 
missing, also when only managed
+        DependencyCheckTools.DependencyCheckResult sb = 
tools.camel_dependency_check(springBoot, route, null, null, null, null);
+        assertThat(sb.missingDependencies()).singleElement().satisfies(d -> {
+            assertThat(d.component()).isEqualTo("jms");
+            assertThat(d.groupId()).isEqualTo("org.apache.camel.springboot");
+            assertThat(d.artifactId()).isEqualTo("camel-jms-starter");
+        });
+
+        DependencyCheckTools.DependencyCheckResult q = 
tools.camel_dependency_check(quarkus, route, null, null, null, null);
+        assertThat(q.projectInfo().runtimeType()).isEqualTo("quarkus");
+        assertThat(q.missingDependencies()).singleElement().satisfies(d -> {
+            assertThat(d.groupId()).isEqualTo("org.apache.camel.quarkus");
+            assertThat(d.artifactId()).isEqualTo("camel-quarkus-jms");
+        });
+    }
+
+    @Test
+    void quarkusBomIsNotVersionedLikeCamel() {
+        DependencyData data = new DependencyData();
+
+        assertThat(data.formatBomSnippet("quarkus", "4.22.0", null))
+                .contains("<artifactId>camel-quarkus-bom</artifactId>")
+                .contains("<version>${camel-quarkus.version}</version>")
+                .contains("camel_version_list")
+                .doesNotContain("4.22.0");
+        assertThat(data.formatBomSnippet("quarkus", "4.22.0", "3.27.1"))
+                .contains("<groupId>io.quarkus.platform</groupId>")
+                .contains("<artifactId>quarkus-camel-bom</artifactId>")
+                .contains("<version>3.27.1</version>");
+        // the Camel Quarkus version of the project (camel-quarkus.version or 
the camel-quarkus-bom), when known
+        assertThat(data.formatBomSnippet("quarkus", "4.22.0", null, "3.27.0"))
+                .contains("<groupId>org.apache.camel.quarkus</groupId>")
+                .contains("<artifactId>camel-quarkus-bom</artifactId>")
+                .contains("<version>3.27.0</version>")
+                .doesNotContain("camel_version_list");
+    }
+
+    @Test
+    void quarkusBomRecommendationUsesTheCamelQuarkusVersionOfTheProject() {
+        String pom = """
+                <project>
+                    <properties>
+                        <camel.version>4.14.0</camel.version>
+                        <camel-quarkus.version>3.27.0</camel-quarkus.version>
+                    </properties>
+                    <dependencies>
+                        <dependency>
+                            <groupId>org.apache.camel.quarkus</groupId>
+                            <artifactId>camel-quarkus-kafka</artifactId>
+                            <version>${camel-quarkus.version}</version>
+                        </dependency>
+                    </dependencies>
+                </project>
+                """;
+
+        DependencyCheckTools.DependencyCheckResult result = 
tools.camel_dependency_check(pom, null, null, null, null, null);
+        assertThat(result.recommendations()).filteredOn(r -> 
r.category().equals("Best Practice"))
+                .singleElement().satisfies(r -> assertThat(r.snippet())
+                        .contains("<artifactId>camel-quarkus-bom</artifactId>")
+                        .contains("<version>3.27.0</version>"));
     }
 
     @Test
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencySecurityAuditToolsTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencySecurityAuditToolsTest.java
index 186958df6dd1..61269fa6772f 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencySecurityAuditToolsTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencySecurityAuditToolsTest.java
@@ -161,7 +161,60 @@ class DependencySecurityAuditToolsTest {
                 = tools.camel_dependency_security_audit(SIMPLE_POM, null, 
null, null, null, null);
 
         assertThat(result.recommendations()).isNotNull();
-        assertThat(result.recommendations()).anyMatch(r -> 
r.contains("Upgrade"));
+        assertThat(result.recommendations()).anyMatch(r -> 
r.startsWith("Upgrade from Camel 4.10.0"))
+                .noneMatch(r -> r.contains("SNAPSHOT"));
+    }
+
+    @Test
+    void shouldSayWhenTheCamelVersionIsNotDetected() {
+        // a Spring Boot project whose Camel version comes from the parent: 
the starter is audited as camel-http
+        String parentManaged = """
+                <project>
+                    <parent>
+                        <groupId>com.example</groupId>
+                        <artifactId>company-parent</artifactId>
+                        <version>1.0</version>
+                    </parent>
+                    <dependencies>
+                        <dependency>
+                            <groupId>org.apache.camel.springboot</groupId>
+                            <artifactId>camel-http-starter</artifactId>
+                        </dependency>
+                    </dependencies>
+                </project>
+                """;
+
+        DependencySecurityAuditTools.AuditResult unknown
+                = tools.camel_dependency_security_audit(parentManaged, null, 
null, null, null, null);
+        assertThat(unknown.summary().camelVersion()).isNull();
+        assertThat(unknown.summary().camelVersionDetected()).isFalse();
+        assertThat(unknown.summary().clean()).isFalse();
+        
assertThat(unknown.warnings()).singleElement().asString().contains("camelVersion");
+        assertThat(unknown.vulnerableArtifacts()).singleElement().satisfies(a 
-> {
+            assertThat(a.artifactId()).isEqualTo("camel-http-starter");
+            assertThat(a.reachable()).as("no routes, reachability 
unknown").isNull();
+        });
+        assertThat(unknown.recommendations()).noneMatch(r -> 
r.contains("directly used in routes"));
+
+        // the version of this server's catalog, so that no other catalog is 
downloaded
+        String version = 
tools.catalogService.getDefaultCatalog().getCatalogVersion().replace("-SNAPSHOT",
 "");
+        DependencySecurityAuditTools.AuditResult given
+                = tools.camel_dependency_security_audit(parentManaged, null, 
null, version, null, null);
+        assertThat(given.summary().camelVersion()).isEqualTo(version);
+        assertThat(given.summary().camelVersionDetected()).isFalse();
+        
assertThat(given.warnings()).singleElement().asString().contains(version);
+    }
+
+    @Test
+    void shouldNotBeCleanWhenAdvisoriesWithoutComponentsMayApply() {
+        // CVE-2024-22371 names no component and affects 4.1.0 before 4.4.0
+        DependencySecurityAuditTools.AuditResult result = 
tools.camel_dependency_security_audit(
+                CURRENT_VERSION_POM.replace("4.22.0", "4.2.0"), null, null, 
null, null, null);
+
+        
assertThat(result.uncheckedAdvisories()).extracting(DependencySecurityAuditTools.VulnerabilityFinding::cve)
+                .contains("CVE-2024-22371");
+        assertThat(result.summary().uncheckedAdvisories()).isPositive();
+        assertThat(result.summary().clean()).isFalse();
     }
 
     @Test
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationToolsTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationToolsTest.java
index 97acfc6bb992..39108501378a 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationToolsTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationToolsTest.java
@@ -29,8 +29,61 @@ class MigrationToolsTest {
     MigrationToolsTest() {
         tools = new MigrationTools();
         tools.migrationData = new MigrationData();
+        tools.catalogService = new CatalogService();
     }
 
+    // a Spring Boot application packaged as a WAR for Tomcat: the BOM and a 
managed-only component declare nothing,
+    // the plugin dependency is not a project dependency
+    private static final String SPRING_BOOT_WAR_POM = """
+            <project>
+                <packaging>war</packaging>
+                <parent>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-parent</artifactId>
+                    <version>3.5.0</version>
+                </parent>
+                <dependencyManagement>
+                    <dependencies>
+                        <dependency>
+                            <groupId>org.apache.camel.springboot</groupId>
+                            <artifactId>camel-spring-boot-bom</artifactId>
+                            <version>4.18.0</version>
+                            <type>pom</type>
+                            <scope>import</scope>
+                        </dependency>
+                        <dependency>
+                            <groupId>org.apache.camel.springboot</groupId>
+                            <artifactId>camel-jms-starter</artifactId>
+                            <version>4.18.0</version>
+                        </dependency>
+                    </dependencies>
+                </dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.camel.springboot</groupId>
+                        <artifactId>camel-kafka-starter</artifactId>
+                    </dependency>
+                </dependencies>
+                <build>
+                    <plugins>
+                        <plugin>
+                            <artifactId>maven-war-plugin</artifactId>
+                        </plugin>
+                        <plugin>
+                            <groupId>org.apache.camel</groupId>
+                            <artifactId>camel-maven-plugin</artifactId>
+                            <dependencies>
+                                <dependency>
+                                    <groupId>org.apache.camel</groupId>
+                                    <artifactId>camel-xml-io</artifactId>
+                                </dependency>
+                            </dependencies>
+                        </plugin>
+                    </plugins>
+                </build>
+            </project>
+            """;
+
     private static final String CLEAN_POM = """
             <project>
                 <properties>
@@ -98,6 +151,142 @@ class MigrationToolsTest {
         assertThat(result.warnings()).noneMatch(w -> w.contains("Sensitive 
data detected"));
     }
 
+    // ---- Runtime detection ----
+
+    @Test
+    void springBootWarIsSpringBootAndOnlyDeclaredDependenciesCount() {
+        MigrationTools.ProjectAnalysisResult result = 
tools.camel_migration_analyze(SPRING_BOOT_WAR_POM, null);
+
+        assertThat(result.runtimeType()).isEqualTo("spring-boot");
+        assertThat(result.camelVersion()).isEqualTo("4.18.0");
+        
assertThat(result.camelComponents()).containsExactly("camel-kafka-starter");
+        
assertThat(result.nextStep()).contains("camel_migration_compatibility");
+    }
+
+    @Test
+    void pluginsAreRuntimeMarkers() {
+        String plainWar = """
+                <project>
+                    
<properties><camel.version>3.20.0</camel.version></properties>
+                    
<build><plugins><plugin><artifactId>maven-war-plugin</artifactId></plugin></plugins></build>
+                </project>
+                """;
+        String bundle = """
+                <project>
+                    
<properties><camel.version>3.20.0</camel.version></properties>
+                    <build><plugins><plugin>
+                        
<groupId>org.apache.felix</groupId><artifactId>maven-bundle-plugin</artifactId>
+                    </plugin></plugins></build>
+                </project>
+                """;
+
+        assertThat(tools.camel_migration_analyze(plainWar, 
null).runtimeType()).isEqualTo("wildfly");
+        assertThat(tools.camel_migration_analyze(bundle, 
null).runtimeType()).isEqualTo("karaf");
+    }
+
+    @Test
+    void quarkusSpringBootPropertiesIsNotSpringBoot() {
+        // a Camel Quarkus project with the Quarkus extension for Spring Boot 
properties: Spring Boot is told by the group
+        String pom = """
+                <project>
+                    <dependencyManagement>
+                        <dependencies>
+                            <dependency>
+                                <groupId>io.quarkus.platform</groupId>
+                                <artifactId>quarkus-camel-bom</artifactId>
+                                <version>3.27.1</version>
+                                <type>pom</type>
+                                <scope>import</scope>
+                            </dependency>
+                        </dependencies>
+                    </dependencyManagement>
+                    <dependencies>
+                        <dependency>
+                            <groupId>org.apache.camel.quarkus</groupId>
+                            <artifactId>camel-quarkus-kafka</artifactId>
+                        </dependency>
+                        <dependency>
+                            <groupId>io.quarkus</groupId>
+                            
<artifactId>quarkus-spring-boot-properties</artifactId>
+                        </dependency>
+                    </dependencies>
+                </project>
+                """;
+
+        assertThat(tools.camel_migration_analyze(pom, 
null).runtimeType()).isEqualTo("quarkus");
+    }
+
+    // ---- Compatibility and recipes ----
+
+    @Test
+    void compatibilityTakesTheJavaRequirementFromTheReleaseData() {
+        // the catalog's release data: 4.20 supports Java 17, 21 and 25; 4.0 
needs Java 17
+        MigrationTools.CompatibilityResult java17 = 
tools.camel_migration_compatibility("4.18.0", "4.20.0", "main", "17");
+        assertThat(java17.compatible()).isTrue();
+        
assertThat(java17.javaCompatibility().requiredVersion()).isEqualTo("17");
+        
assertThat(java17.javaCompatibility().supportedVersions()).isEqualTo("17, 21, 
25");
+
+        MigrationTools.CompatibilityResult java11 = 
tools.camel_migration_compatibility("3.20.0", "4.0.0", "main", "11");
+        assertThat(java11.compatible()).isFalse();
+        
assertThat(java11.blockers()).singleElement().asString().contains("requires 
Java 17+");
+    }
+
+    @Test
+    void compatibilityHasNoJavaRequirementForAVersionNewerThanTheCatalog() {
+        // the catalog only learns a release once it is out: the next minor 
version may drop a JDK version (as 4.24
+        // drops Java 17), so the newest release must not tell its Java 
requirement
+        String latest = 
CamelReleases.latest(tools.catalogService.getDefaultCatalog());
+        String[] parts = latest.split("\\.");
+        String nextMinor = parts[0] + "." + (Integer.parseInt(parts[1]) + 1) + 
".0";
+
+        MigrationTools.CompatibilityResult result = 
tools.camel_migration_compatibility(latest, nextMinor, "main", "17");
+        assertThat(result.javaCompatibility()).isNull();
+        assertThat(result.blockers()).isEmpty();
+        assertThat(result.warnings()).singleElement().asString()
+                .contains("no release data for Camel " + 
nextMinor).contains("camel_version_list");
+
+        // a patch release of a known minor version takes the Java requirement 
of that minor version
+        String patch = parts[0] + "." + parts[1] + ".99";
+        MigrationTools.CompatibilityResult samePatch = 
tools.camel_migration_compatibility(latest, patch, "main", "17");
+        assertThat(samePatch.javaCompatibility()).isNotNull();
+        assertThat(samePatch.warnings()).noneMatch(w -> w.contains("no release 
data"));
+    }
+
+    @Test
+    void recipesUseTheRecipeOfEachArtifact() {
+        MigrationTools.MigrationRecipesResult main
+                = tools.camel_migration_recipes("main", "4.14.0", "4.22.1", 
"17", null, null);
+        assertThat(main.mavenCommands()).singleElement().asString()
+                .contains("rewrite-maven-plugin:dryRun ")
+                
.contains("org.apache.camel.upgrade:camel-upgrade-recipes:4.22.0 ")
+                
.endsWith("-Drewrite.activeRecipes=org.apache.camel.upgrade.CamelMigrationRecipe");
+        assertThat(main.javaUpgradeSuggestions()).isEmpty();
+
+        MigrationTools.MigrationRecipesResult springBoot
+                = tools.camel_migration_recipes("spring-boot", "4.14.0", 
"4.22.0", "11", false, null);
+        assertThat(springBoot.mavenCommands()).singleElement().asString()
+                .contains("rewrite-maven-plugin:run ")
+                
.contains("org.apache.camel.upgrade:camel-spring-boot-upgrade-recipes:4.22.0 ")
+                
.endsWith("-Drewrite.activeRecipes=org.apache.camel.upgrade.CamelSpringBootMigrationRecipe");
+        
assertThat(springBoot.javaUpgradeSuggestions()).singleElement().asString()
+                .contains("requires Java 17+").contains("UpgradeToJava17");
+    }
+
+    @Test
+    void quarkusRecipesUseTheQuarkusPlatformVersion() {
+        MigrationTools.MigrationRecipesResult pinned
+                = tools.camel_migration_recipes("quarkus", "4.14.0", "4.22.0", 
null, false, "3.27.1");
+        assertThat(pinned.mavenCommands()).singleElement().asString()
+                
.contains("io.quarkus.platform:quarkus-maven-plugin:3.27.1:update 
-DplatformVersion=3.27.1 -Drewrite=true");
+
+        MigrationTools.MigrationRecipesResult latest
+                = tools.camel_migration_recipes("quarkus", "4.14.0", "4.22.0", 
null, null, null);
+        assertThat(latest.mavenCommands()).singleElement().asString()
+                .contains("io.quarkus.platform:quarkus-maven-plugin:update 
-DrewriteDryRun=true")
+                .doesNotContain("4.22.0");
+        assertThat(latest.notes()).anyMatch(n -> 
n.contains("camel_version_list") && n.contains("quarkusPlatformVersion"));
+    }
+
     // ---- Guide search ----
 
     @Test
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitionsTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitionsTest.java
index bc605edd1199..405e9b0407a7 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitionsTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitionsTest.java
@@ -140,6 +140,10 @@ class PromptDefinitionsTest {
         assertThat(text).contains("camel_migration_wildfly_karaf");
         assertThat(text).contains("camel_migration_recipes");
         assertThat(text).contains("camel_migration_guide_search");
+        // the arguments the tools take: camel_migration_compatibility has no 
components argument
+        assertThat(text).contains(
+                "`camel_migration_compatibility` with `currentVersion`, 
`targetVersion`, `runtime`, and `javaVersion`.");
+        
assertThat(text).contains("`quarkusPlatformVersion`").contains("`runtime=quarkus`");
     }
 
     @Test
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PropertiesTranslateToolsTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PropertiesTranslateToolsTest.java
index 4f5703e3d305..42ca2adf9c7d 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PropertiesTranslateToolsTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PropertiesTranslateToolsTest.java
@@ -111,6 +111,22 @@ class PropertiesTranslateToolsTest {
         
assertThat(result.properties().get(1).translated()).isEqualTo("camel.main.streamCachingEnabled=true");
     }
 
+    @Test
+    void springBootOnlyOptionsKeepTheirSpringBootKey() {
+        PropertiesTranslateTools.PropertiesTranslateResult springBoot = 
tools.camel_properties_translate(
+                
"camel.springboot.lenient-configuration-binding=true\ncamel.springboot.main-run-controller=true",
+                "spring-boot", "spring-boot");
+        
assertThat(springBoot.properties().get(0).status()).isEqualTo("unchanged");
+        
assertThat(springBoot.properties().get(1).translated()).isEqualTo("camel.main.run-controller=true");
+
+        PropertiesTranslateTools.PropertiesTranslateResult main = 
tools.camel_properties_translate(
+                
"camel.springboot.lenient-configuration-binding=true\ncamel.main.include-non-singletons=true",
+                "spring-boot", "main");
+        assertThat(main.summary().unknown()).isEqualTo(2);
+        assertThat(main.properties().get(0).note()).contains("Spring Boot 
only");
+        
assertThat(main.properties().get(1).translated()).isEqualTo("camel.main.include-non-singletons=true");
+    }
+
     @Test
     void legacySpringbootPrefixIsRewrittenEvenWhenTargetIsQuarkus() {
         PropertiesTranslateTools.PropertiesTranslateResult result = 
tools.camel_properties_translate(

Reply via email to