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

acosentino pushed a commit to branch 23168-comp
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 5b11633ba6105854f4792997366bbeb82fca0958
Author: Andrea Cosentino <[email protected]>
AuthorDate: Tue Mar 10 12:48:37 2026 +0100

    CAMEL-23168 - Camel-Jbang-mcp: Make MCP exception catalog resource doc 
links version-aware
    
    Signed-off-by: Andrea Cosentino <[email protected]>
---
 .../dsl/jbang/core/commands/mcp/DiagnoseData.java  | 58 ++++++++++++-
 .../jbang/core/commands/mcp/DiagnoseResources.java | 66 +++++++++++++++
 .../core/commands/mcp/DiagnoseResourcesTest.java   | 99 ++++++++++++++++++++++
 3 files changed, 221 insertions(+), 2 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseData.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseData.java
index 30265ce3575d..f65da48865db 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseData.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseData.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.mcp;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -41,6 +42,41 @@ public class DiagnoseData {
     public static final String CAMEL_MANUAL_DOC = CAMEL_DOC_BASE + "manual/";
     public static final String CAMEL_EIP_DOC = CAMEL_COMPONENT_DOC + "eips/";
 
+    /**
+     * Component doc base URL for a specific Camel version.
+     *
+     * @param version the version segment (e.g., "4.18.x", "4.14.x"), or 
null/"next" for the latest development docs
+     */
+    public static String componentDocBase(String version) {
+        String v = (version == null || version.isBlank() || 
"next".equals(version)) ? "next" : version;
+        return CAMEL_DOC_BASE + "components/" + v + "/";
+    }
+
+    /**
+     * EIP doc base URL for a specific Camel version.
+     */
+    public static String eipDocBase(String version) {
+        return componentDocBase(version) + "eips/";
+    }
+
+    /**
+     * Resolve documentation links for a specific version by replacing the 
"next" version segment in component/EIP doc
+     * URLs with the specified version. Manual URLs (which have no version 
segment) are left unchanged.
+     *
+     * @param links   the original documentation links (using "next")
+     * @param version the target version (e.g., "4.18.x"), or null/"next" to 
keep defaults
+     */
+    public static List<String> resolveDocLinks(List<String> links, String 
version) {
+        if (version == null || version.isBlank() || "next".equals(version)) {
+            return links;
+        }
+        List<String> resolved = new ArrayList<>(links.size());
+        for (String link : links) {
+            resolved.add(link.replace("components/next/", "components/" + 
version + "/"));
+        }
+        return resolved;
+    }
+
     private static final Map<String, ExceptionInfo> KNOWN_EXCEPTIONS;
 
     static {
@@ -379,11 +415,20 @@ public class DiagnoseData {
          * Convert this exception info to a full JSON object with all fields.
          */
         public JsonObject toJson() {
+            return toJson(null);
+        }
+
+        /**
+         * Convert this exception info to a full JSON object, resolving doc 
links for the given version.
+         *
+         * @param version the Camel doc version (e.g., "4.18.x"), or null for 
"next"
+         */
+        public JsonObject toJson(String version) {
             JsonObject json = new JsonObject();
             json.put("description", description);
             json.put("commonCauses", toJsonArray(commonCauses));
             json.put("suggestedFixes", toJsonArray(suggestedFixes));
-            json.put("documentationLinks", toJsonArray(documentationLinks));
+            json.put("documentationLinks", 
toJsonArray(resolveDocLinks(documentationLinks, version)));
             return json;
         }
 
@@ -391,9 +436,18 @@ public class DiagnoseData {
          * Convert this exception info to a summary JSON object (description 
and doc links only).
          */
         public JsonObject toSummaryJson() {
+            return toSummaryJson(null);
+        }
+
+        /**
+         * Convert this exception info to a summary JSON object, resolving doc 
links for the given version.
+         *
+         * @param version the Camel doc version (e.g., "4.18.x"), or null for 
"next"
+         */
+        public JsonObject toSummaryJson(String version) {
             JsonObject json = new JsonObject();
             json.put("description", description);
-            json.put("documentationLinks", toJsonArray(documentationLinks));
+            json.put("documentationLinks", 
toJsonArray(resolveDocLinks(documentationLinks, version)));
             return json;
         }
 
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseResources.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseResources.java
index ebcd5794b357..f8650009c29d 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseResources.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseResources.java
@@ -68,6 +68,37 @@ public class DiagnoseResources {
         return new TextResourceContents("camel://error/exception-catalog", 
result.toJson(), "application/json");
     }
 
+    /**
+     * All known Camel exceptions with version-specific documentation links.
+     */
+    @ResourceTemplate(uriTemplate = 
"camel://error/exception-catalog/{version}",
+                      name = "camel_error_exception_catalog_versioned",
+                      title = "Camel Exception Catalog (Versioned)",
+                      description = "Registry of all known Camel exceptions 
with documentation links resolved "
+                                    + "for a specific Camel version (e.g., 
'4.18.x', '4.14.x'). "
+                                    + "Use 'next' for the latest development 
docs.",
+                      mimeType = "application/json")
+    public TextResourceContents exceptionCatalogVersioned(
+            @ResourceTemplateArg(name = "version") String version) {
+
+        String uri = "camel://error/exception-catalog/" + version;
+
+        JsonObject result = new JsonObject();
+        result.put("version", version);
+
+        JsonArray exceptions = new JsonArray();
+        for (Map.Entry<String, DiagnoseData.ExceptionInfo> entry : 
diagnoseData.getKnownExceptions().entrySet()) {
+            JsonObject exJson = entry.getValue().toSummaryJson(version);
+            exJson.put("name", entry.getKey());
+            exceptions.add(exJson);
+        }
+
+        result.put("exceptions", exceptions);
+        result.put("totalCount", exceptions.size());
+
+        return new TextResourceContents(uri, result.toJson(), 
"application/json");
+    }
+
     /**
      * Detail for a specific Camel exception by name.
      */
@@ -98,4 +129,39 @@ public class DiagnoseResources {
 
         return new TextResourceContents(uri, result.toJson(), 
"application/json");
     }
+
+    /**
+     * Detail for a specific Camel exception with version-specific 
documentation links.
+     */
+    @ResourceTemplate(uriTemplate = "camel://error/exception/{name}/{version}",
+                      name = "camel_error_exception_detail_versioned",
+                      title = "Exception Detail (Versioned)",
+                      description = "Full diagnostic detail for a specific 
Camel exception with documentation links "
+                                    + "resolved for a specific Camel version 
(e.g., '4.18.x', '4.14.x'). "
+                                    + "Use 'next' for the latest development 
docs.",
+                      mimeType = "application/json")
+    public TextResourceContents exceptionDetailVersioned(
+            @ResourceTemplateArg(name = "name") String name,
+            @ResourceTemplateArg(name = "version") String version) {
+
+        String uri = "camel://error/exception/" + name + "/" + version;
+
+        DiagnoseData.ExceptionInfo info = diagnoseData.getException(name);
+        if (info == null) {
+            JsonObject result = new JsonObject();
+            result.put("name", name);
+            result.put("version", version);
+            result.put("found", false);
+            result.put("message", "Exception '" + name + "' is not in the 
known exceptions catalog. "
+                                  + "Use the camel://error/exception-catalog 
resource to see all known exceptions.");
+            return new TextResourceContents(uri, result.toJson(), 
"application/json");
+        }
+
+        JsonObject result = info.toJson(version);
+        result.put("name", name);
+        result.put("version", version);
+        result.put("found", true);
+
+        return new TextResourceContents(uri, result.toJson(), 
"application/json");
+    }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseResourcesTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseResourcesTest.java
index 9a9575c1b753..b31259a71fc7 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseResourcesTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseResourcesTest.java
@@ -132,4 +132,103 @@ class DiagnoseResourcesTest {
         assertThat(result.getBoolean("found")).isFalse();
         assertThat(result.getString("message")).contains("not in the known 
exceptions catalog");
     }
+
+    // ---- Versioned catalog ----
+
+    @Test
+    void versionedCatalogReturnsVersionSpecificDocLinks() throws Exception {
+        TextResourceContents contents = 
resources.exceptionCatalogVersioned("4.18.x");
+
+        
assertThat(contents.uri()).isEqualTo("camel://error/exception-catalog/4.18.x");
+        assertThat(contents.mimeType()).isEqualTo("application/json");
+
+        JsonObject result = (JsonObject) Jsoner.deserialize(contents.text());
+        assertThat(result.getString("version")).isEqualTo("4.18.x");
+        assertThat(result.getInteger("totalCount")).isGreaterThan(0);
+
+        // Verify doc links use 4.18.x instead of next
+        JsonArray exceptions = result.getCollection("exceptions");
+        for (Object obj : exceptions) {
+            JsonObject entry = (JsonObject) obj;
+            JsonArray docs = entry.getCollection("documentationLinks");
+            for (Object doc : docs) {
+                String link = (String) doc;
+                if (link.contains("/components/")) {
+                    assertThat(link).contains("components/4.18.x/");
+                    assertThat(link).doesNotContain("components/next/");
+                }
+            }
+        }
+    }
+
+    @Test
+    void versionedCatalogWithNextReturnsDefaultLinks() throws Exception {
+        TextResourceContents contents = 
resources.exceptionCatalogVersioned("next");
+        JsonObject result = (JsonObject) Jsoner.deserialize(contents.text());
+
+        JsonArray exceptions = result.getCollection("exceptions");
+        for (Object obj : exceptions) {
+            JsonObject entry = (JsonObject) obj;
+            JsonArray docs = entry.getCollection("documentationLinks");
+            for (Object doc : docs) {
+                String link = (String) doc;
+                if (link.contains("/components/")) {
+                    assertThat(link).contains("components/next/");
+                }
+            }
+        }
+    }
+
+    // ---- Versioned detail ----
+
+    @Test
+    void versionedDetailReturnsVersionSpecificDocLinks() throws Exception {
+        TextResourceContents contents = 
resources.exceptionDetailVersioned("DirectConsumerNotAvailableException", 
"4.14.x");
+
+        
assertThat(contents.uri()).isEqualTo("camel://error/exception/DirectConsumerNotAvailableException/4.14.x");
+
+        JsonObject result = (JsonObject) Jsoner.deserialize(contents.text());
+        assertThat(result.getBoolean("found")).isTrue();
+        assertThat(result.getString("version")).isEqualTo("4.14.x");
+
+        // DirectConsumerNotAvailableException has component doc links 
(direct-component.html, seda-component.html)
+        JsonArray docs = result.getCollection("documentationLinks");
+        for (Object doc : docs) {
+            String link = (String) doc;
+            if (link.contains("/components/")) {
+                assertThat(link).contains("components/4.14.x/");
+                assertThat(link).doesNotContain("components/next/");
+            }
+        }
+    }
+
+    @Test
+    void versionedDetailNotFoundIncludesVersion() throws Exception {
+        TextResourceContents contents = 
resources.exceptionDetailVersioned("NonExistentException", "4.18.x");
+
+        
assertThat(contents.uri()).isEqualTo("camel://error/exception/NonExistentException/4.18.x");
+
+        JsonObject result = (JsonObject) Jsoner.deserialize(contents.text());
+        assertThat(result.getBoolean("found")).isFalse();
+        assertThat(result.getString("version")).isEqualTo("4.18.x");
+    }
+
+    @Test
+    void versionedDetailPreservesManualLinks() throws Exception {
+        // CamelExecutionException has manual doc links 
(manual/exception-clause.html, etc.)
+        TextResourceContents contents = 
resources.exceptionDetailVersioned("CamelExecutionException", "4.18.x");
+        JsonObject result = (JsonObject) Jsoner.deserialize(contents.text());
+
+        JsonArray docs = result.getCollection("documentationLinks");
+        boolean hasManualLink = false;
+        for (Object doc : docs) {
+            String link = (String) doc;
+            if (link.contains("/manual/")) {
+                hasManualLink = true;
+                // Manual links should NOT have a version segment inserted
+                assertThat(link).doesNotContain("components/");
+            }
+        }
+        assertThat(hasManualLink).as("CamelExecutionException should have at 
least one manual doc link").isTrue();
+    }
 }

Reply via email to