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

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

commit dd67d381e00c6743a5ec123166da268bac876638
Author: Andrea Cosentino <[email protected]>
AuthorDate: Thu Mar 5 10:38:54 2026 +0100

    CAMEL-23131 - Camel-jbang-mcp: Add camel_error_diagnose MCP tool for stack 
trace diagnosis
    
    Add a new DiagnoseTools MCP tool that accepts Camel stack traces or error
    messages and returns structured diagnosis including the identified
    component/EIP, common causes, documentation links, and suggested fixes.
    Covers 17 known Camel exceptions (NoSuchEndpointException,
    ResolveEndpointFailedException, FailedToCreateRouteException, etc.).
    
    Signed-off-by: Andrea Cosentino <[email protected]>
---
 .../modules/ROOT/pages/camel-jbang-mcp.adoc        | 105 +++-
 .../dsl/jbang/core/commands/mcp/DiagnoseTools.java | 580 +++++++++++++++++++++
 .../jbang/core/commands/mcp/DiagnoseToolsTest.java | 312 +++++++++++
 3 files changed, 996 insertions(+), 1 deletion(-)

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 50ee05be58ec..1270c9ff3cd9 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
@@ -24,7 +24,8 @@ By default, the HTTP server is disabled. To enable it, set 
`quarkus.http.host-en
 
 == Available Tools
 
-The server exposes 24 tools organized into eight functional areas.
+The server exposes 25 tools organized into nine functional areas, plus 3 
prompts that provide structured
+multi-step workflows.
 
 === Catalog Exploration
 
@@ -99,6 +100,19 @@ The server exposes 24 tools organized into eight functional 
areas.
   best practices.
 |===
 
+=== Error Diagnosis
+
+[cols="1,3",options="header"]
+|===
+| Tool | Description
+
+| `camel_error_diagnose`
+| Diagnoses Camel errors from stack traces or error messages. Identifies the 
exception type against 17 known
+  Camel exceptions (such as `NoSuchEndpointException`, 
`ResolveEndpointFailedException`,
+  `FailedToCreateRouteException`, `PropertyBindingException`, and others), 
extracts the components and EIPs
+  involved, and returns common causes, suggested fixes, and links to relevant 
Camel documentation.
+|===
+
 === Validation and Transformation
 
 [cols="1,3",options="header"]
@@ -187,6 +201,40 @@ help validate, scaffold, and provide mock guidance for 
that workflow.
 | Lists available Camel versions for a given runtime, including release dates, 
JDK requirements, and LTS status.
 |===
 
+== Available Prompts
+
+Prompts are structured multi-step workflows that guide the LLM through 
orchestrating multiple tools in the correct
+sequence. Instead of the LLM having to discover which tools to call and in 
what order, a prompt provides the complete
+workflow as a step-by-step plan.
+
+MCP clients that support prompts (such as Claude Desktop) expose them as 
selectable workflows. The LLM receives the
+instructions and executes each step by calling the referenced tools.
+
+[cols="1,1,3",options="header"]
+|===
+| Prompt | Arguments | Description
+
+| `camel_build_integration`
+| `requirements` (required), `runtime` (optional)
+| Guided workflow to build a Camel integration from natural-language 
requirements. Walks through seven steps:
+  identify components, identify EIPs, get component documentation, build the 
YAML route, validate it with the
+  YAML DSL schema, run a security review, and present the final result with 
explanations and run instructions.
+
+| `camel_migrate_project`
+| `pomContent` (required), `targetVersion` (optional)
+| Guided workflow to migrate a Camel project to a newer version. Walks through 
six steps: analyze the project's
+  `pom.xml`, determine the target version, check compatibility (including 
WildFly/Karaf detection), get
+  OpenRewrite migration recipes, search migration guides for breaking changes 
per component, and produce a
+  structured migration summary with blockers, breaking changes, commands, and 
manual steps.
+
+| `camel_security_review`
+| `route` (required), `format` (optional)
+| Guided workflow to perform a security audit of a Camel route. Walks through 
three steps: analyze the route
+  for security-sensitive components and vulnerabilities, understand the route 
structure and data flow, and
+  produce an actionable audit checklist organized into critical issues, 
warnings, positive findings,
+  recommendations, and compliance notes.
+|===
+
 == Setup
 
 The MCP server requires https://www.jbang.dev/[JBang] to be installed and 
available on your PATH.
@@ -376,6 +424,22 @@ The assistant calls `camel_route_harden_context` which 
analyzes the route for se
 detects issues (hardcoded credentials, HTTP instead of HTTPS, plain FTP, 
etc.), assigns risk levels, and
 returns structured findings with remediation recommendations.
 
+=== Diagnosing an Error
+
+Paste a stack trace or error message and ask:
+
+----
+I'm getting this error when starting my Camel route. What's wrong?
+
+org.apache.camel.FailedToCreateRouteException: Failed to create route route1 
at: >>> To[kafka:myTopic] <<<
+Caused by: org.apache.camel.ResolveEndpointFailedException: Failed to resolve 
endpoint: kafka:myTopic
+Caused by: org.apache.camel.NoSuchEndpointException: No endpoint could be 
found for: kafka:myTopic
+----
+
+The assistant calls `camel_error_diagnose` which identifies all three 
exceptions in the chain, extracts the
+`kafka` component, and returns common causes (missing `camel-kafka` 
dependency, typo in URI scheme), suggested
+fixes (add the dependency, verify the URI), and links to the relevant Camel 
documentation.
+
 === Checking Camel Versions
 
 ----
@@ -466,3 +530,42 @@ with mock mode so I can prototype quickly.
 The assistant first calls `camel_openapi_validate` to check for issues, then 
calls `camel_openapi_scaffold`
 to generate the route scaffold. This gives you a validated spec and a complete 
starting point where you can
 implement routes one at a time while Camel auto-mocks the rest.
+
+=== Using the Build Integration Prompt
+
+In MCP clients that support prompts, select the `camel_build_integration` 
prompt and provide your requirements:
+
+----
+Requirements: Read messages from a Kafka topic, filter them by a JSON field, 
and write matching messages to an AWS S3 bucket.
+Runtime: quarkus
+----
+
+The prompt guides the assistant through a structured seven-step workflow: 
discovering the right components
+(`kafka`, `aws2-s3`), selecting EIPs (`filter`), retrieving their 
documentation, building a YAML route,
+validating it against the YAML DSL schema, and running a security review — all 
in the correct order.
+
+=== Using the Migrate Project Prompt
+
+Select the `camel_migrate_project` prompt and provide your project's `pom.xml`:
+
+----
+pomContent: <paste your pom.xml here>
+targetVersion: 4.18.0
+----
+
+The prompt orchestrates the full migration workflow: analyzing your project, 
checking component compatibility,
+retrieving OpenRewrite recipes, searching migration guides for per-component 
breaking changes, and producing a
+structured migration summary with blockers, manual steps, and the exact 
commands to run.
+
+=== Using the Security Review Prompt
+
+Select the `camel_security_review` prompt and provide a route:
+
+----
+Route: <paste your route here>
+Format: yaml
+----
+
+The prompt guides the assistant through a security audit: analyzing the route 
for vulnerabilities and
+security-sensitive components, understanding the data flow, and producing a 
structured audit checklist with
+critical issues, warnings, positive findings, and actionable recommendations.
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseTools.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseTools.java
new file mode 100644
index 000000000000..261370e549e8
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseTools.java
@@ -0,0 +1,580 @@
+/*
+ * 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.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jakarta.enterprise.context.ApplicationScoped;
+
+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.catalog.DefaultCamelCatalog;
+import org.apache.camel.tooling.model.ComponentModel;
+import org.apache.camel.tooling.model.EipModel;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+
+/**
+ * MCP Tool for diagnosing Camel errors from stack traces or error messages.
+ * <p>
+ * Accepts a Camel stack trace or error message and returns the likely 
component/EIP involved, common causes, links to
+ * relevant documentation, and suggested fixes.
+ */
+@ApplicationScoped
+public class DiagnoseTools {
+
+    private static final String CAMEL_DOC_BASE = "https://camel.apache.org/";;
+    private static final String CAMEL_COMPONENT_DOC = CAMEL_DOC_BASE + 
"components/next/";
+    private static final String CAMEL_MANUAL_DOC = CAMEL_DOC_BASE + "manual/";
+    private static final String CAMEL_EIP_DOC = CAMEL_COMPONENT_DOC + "eips/";
+
+    /**
+     * Pattern to extract component names from endpoint URIs in error messages 
(e.g., "kafka:myTopic", "http://host";).
+     */
+    private static final Pattern ENDPOINT_URI_PATTERN = Pattern
+            
.compile("(?:endpoint|uri)[:\\s]+['\"]?([a-zA-Z][a-zA-Z0-9+.-]*):(?://)?[^\\s'\"]*",
 Pattern.CASE_INSENSITIVE);
+
+    /**
+     * Pattern to extract component scheme from common error contexts.
+     */
+    private static final Pattern COMPONENT_SCHEME_PATTERN
+            = 
Pattern.compile("(?:component|scheme)[:\\s]+['\"]?([a-zA-Z][a-zA-Z0-9+.-]*)['\"]?",
 Pattern.CASE_INSENSITIVE);
+
+    /**
+     * Pattern to extract route IDs from error messages.
+     */
+    private static final Pattern ROUTE_ID_PATTERN
+            = Pattern.compile("route[:\\s]+['\"]?([a-zA-Z0-9_-]+)['\"]?", 
Pattern.CASE_INSENSITIVE);
+
+    private final CamelCatalog catalog;
+    private final Map<String, ExceptionInfo> knownExceptions;
+
+    public DiagnoseTools() {
+        this.catalog = new DefaultCamelCatalog();
+        this.knownExceptions = buildKnownExceptions();
+    }
+
+    /**
+     * Tool to diagnose Camel errors from stack traces or error messages.
+     */
+    @Tool(description = "Diagnose a Camel error from a stack trace or error 
message. "
+                        + "Returns the identified component/EIP involved, 
common causes for the error, "
+                        + "links to relevant Camel documentation, and 
suggested fixes. "
+                        + "Covers the most common Camel exceptions including 
NoSuchEndpointException, "
+                        + "ResolveEndpointFailedException, 
FailedToCreateRouteException, and more.")
+    public String camel_error_diagnose(
+            @ToolArg(description = "The Camel stack trace or error message to 
diagnose") String error) {
+
+        if (error == null || error.isBlank()) {
+            throw new ToolCallException("Error message or stack trace is 
required", null);
+        }
+
+        try {
+            JsonObject result = new JsonObject();
+
+            // Identify matching exceptions
+            List<String> matchedExceptions = identifyExceptions(error);
+            JsonArray exceptionsJson = new JsonArray();
+            for (String exceptionName : matchedExceptions) {
+                ExceptionInfo info = knownExceptions.get(exceptionName);
+                if (info != null) {
+                    JsonObject exJson = new JsonObject();
+                    exJson.put("exception", exceptionName);
+                    exJson.put("description", info.description);
+
+                    JsonArray causesJson = new JsonArray();
+                    for (String cause : info.commonCauses) {
+                        causesJson.add(cause);
+                    }
+                    exJson.put("commonCauses", causesJson);
+
+                    JsonArray fixesJson = new JsonArray();
+                    for (String fix : info.suggestedFixes) {
+                        fixesJson.add(fix);
+                    }
+                    exJson.put("suggestedFixes", fixesJson);
+
+                    JsonArray docsJson = new JsonArray();
+                    for (String doc : info.documentationLinks) {
+                        docsJson.add(doc);
+                    }
+                    exJson.put("documentationLinks", docsJson);
+
+                    exceptionsJson.add(exJson);
+                }
+            }
+            result.put("identifiedExceptions", exceptionsJson);
+
+            // Identify components from the error
+            List<String> componentNames = extractComponentNames(error);
+            JsonArray componentsJson = new JsonArray();
+            for (String comp : componentNames) {
+                ComponentModel model = catalog.componentModel(comp);
+                if (model != null) {
+                    JsonObject compJson = new JsonObject();
+                    compJson.put("name", comp);
+                    compJson.put("title", model.getTitle());
+                    compJson.put("description", model.getDescription());
+                    compJson.put("documentationUrl", CAMEL_COMPONENT_DOC + 
comp + "-component.html");
+                    componentsJson.add(compJson);
+                }
+            }
+            result.put("identifiedComponents", componentsJson);
+
+            // Identify EIPs from the error
+            List<String> eipNames = extractEipNames(error);
+            JsonArray eipsJson = new JsonArray();
+            for (String eip : eipNames) {
+                EipModel model = catalog.eipModel(eip);
+                if (model != null) {
+                    JsonObject eipJson = new JsonObject();
+                    eipJson.put("name", eip);
+                    eipJson.put("title", model.getTitle());
+                    eipJson.put("description", model.getDescription());
+                    eipJson.put("documentationUrl", CAMEL_EIP_DOC + eip + 
"-eip.html");
+                    eipsJson.add(eipJson);
+                }
+            }
+            result.put("identifiedEips", eipsJson);
+
+            // Extract route ID if present
+            Matcher routeMatcher = ROUTE_ID_PATTERN.matcher(error);
+            if (routeMatcher.find()) {
+                result.put("routeId", routeMatcher.group(1));
+            }
+
+            // Summary
+            JsonObject summary = new JsonObject();
+            summary.put("exceptionCount", exceptionsJson.size());
+            summary.put("componentCount", componentsJson.size());
+            summary.put("eipCount", eipsJson.size());
+            summary.put("diagnosed", !exceptionsJson.isEmpty());
+            result.put("summary", summary);
+
+            return result.toJson();
+        } catch (ToolCallException e) {
+            throw e;
+        } catch (Throwable e) {
+            throw new ToolCallException(
+                    "Failed to diagnose error (" + e.getClass().getName() + 
"): " + e.getMessage(), null);
+        }
+    }
+
+    /**
+     * Identify known Camel exceptions in the error text.
+     */
+    private List<String> identifyExceptions(String error) {
+        List<String> matched = new ArrayList<>();
+        for (String exceptionName : knownExceptions.keySet()) {
+            if (error.contains(exceptionName)) {
+                matched.add(exceptionName);
+            }
+        }
+        return matched;
+    }
+
+    /**
+     * Extract component names from endpoint URIs and other patterns in the 
error text.
+     */
+    private List<String> extractComponentNames(String error) {
+        List<String> found = new ArrayList<>();
+
+        // Try endpoint URI pattern
+        Matcher uriMatcher = ENDPOINT_URI_PATTERN.matcher(error);
+        while (uriMatcher.find()) {
+            String scheme = uriMatcher.group(1).toLowerCase();
+            if (catalog.componentModel(scheme) != null && 
!found.contains(scheme)) {
+                found.add(scheme);
+            }
+        }
+
+        // Try component scheme pattern
+        Matcher schemeMatcher = COMPONENT_SCHEME_PATTERN.matcher(error);
+        while (schemeMatcher.find()) {
+            String scheme = schemeMatcher.group(1).toLowerCase();
+            if (catalog.componentModel(scheme) != null && 
!found.contains(scheme)) {
+                found.add(scheme);
+            }
+        }
+
+        // Scan for known component names in the error text
+        String lowerError = error.toLowerCase();
+        for (String comp : catalog.findComponentNames()) {
+            if (!found.contains(comp) && containsComponent(lowerError, comp)) {
+                found.add(comp);
+            }
+        }
+
+        return found;
+    }
+
+    /**
+     * Extract EIP names from the error text.
+     */
+    private List<String> extractEipNames(String error) {
+        List<String> found = new ArrayList<>();
+        String lowerError = error.toLowerCase();
+
+        for (String eip : catalog.findModelNames()) {
+            EipModel model = catalog.eipModel(eip);
+            if (model != null) {
+                String eipLower = eip.toLowerCase();
+                if (lowerError.contains(eipLower) && !found.contains(eip)) {
+                    found.add(eip);
+                }
+            }
+        }
+
+        return found;
+    }
+
+    private boolean containsComponent(String content, String comp) {
+        return content.contains(comp + ":")
+                || content.contains("\"" + comp + "\"")
+                || content.contains("'" + comp + "'");
+    }
+
+    /**
+     * Build the registry of known Camel exceptions with their causes, fixes, 
and documentation.
+     */
+    private Map<String, ExceptionInfo> buildKnownExceptions() {
+        Map<String, ExceptionInfo> exceptions = new LinkedHashMap<>();
+
+        exceptions.put("NoSuchEndpointException", new ExceptionInfo(
+                "The specified endpoint URI could not be resolved to any known 
Camel component.",
+                Arrays.asList(
+                        "Typo in the endpoint URI scheme (e.g., 'kafak:' 
instead of 'kafka:')",
+                        "Missing component dependency in pom.xml or 
build.gradle",
+                        "Component not on the classpath",
+                        "Using a component scheme that does not exist"),
+                Arrays.asList(
+                        "Verify the endpoint URI scheme is spelled correctly",
+                        "Add the required camel-<component> dependency to your 
project",
+                        "Check available components with 'camel-catalog' or 
the Camel documentation",
+                        "Ensure the component JAR is on the classpath"),
+                Arrays.asList(
+                        CAMEL_MANUAL_DOC + "component.html",
+                        CAMEL_MANUAL_DOC + 
"faq/why-is-my-message-body-empty.html")));
+
+        exceptions.put("ResolveEndpointFailedException", new ExceptionInfo(
+                "Failed to resolve or create an endpoint from the given URI. 
The URI syntax may be invalid or required options may be missing.",
+                Arrays.asList(
+                        "Invalid endpoint URI syntax",
+                        "Missing required endpoint options",
+                        "Unknown or misspelled endpoint options",
+                        "Invalid option values (wrong type or format)",
+                        "Special characters in URI not properly encoded"),
+                Arrays.asList(
+                        "Check the endpoint URI syntax against the component 
documentation",
+                        "Ensure all required options are provided",
+                        "Verify option names are spelled correctly",
+                        "URL-encode special characters in the URI",
+                        "Use the endpoint DSL for type-safe endpoint 
configuration"),
+                Arrays.asList(
+                        CAMEL_MANUAL_DOC + "endpoint.html",
+                        CAMEL_MANUAL_DOC + "uris.html")));
+
+        exceptions.put("FailedToCreateRouteException", new ExceptionInfo(
+                "A route could not be created. This is typically a 
configuration or wiring issue.",
+                Arrays.asList(
+                        "Invalid endpoint URI in from() or to()",
+                        "Missing required component dependency",
+                        "Bean reference that cannot be resolved",
+                        "Invalid route configuration or DSL syntax",
+                        "Circular route dependencies"),
+                Arrays.asList(
+                        "Check the full exception chain for the root cause",
+                        "Verify all endpoint URIs in the route are valid",
+                        "Ensure all referenced beans are available in the 
registry",
+                        "Validate the route DSL syntax",
+                        "Check for missing component dependencies"),
+                Arrays.asList(
+                        CAMEL_MANUAL_DOC + "routes.html",
+                        CAMEL_MANUAL_DOC + "route-configuration.html")));
+
+        exceptions.put("FailedToStartRouteException", new ExceptionInfo(
+                "A route was created but could not be started. This often 
indicates a connectivity or resource issue.",
+                Arrays.asList(
+                        "Cannot connect to external service (broker, database, 
etc.)",
+                        "Port already in use for server-side components",
+                        "Authentication/authorization failure",
+                        "Missing or invalid SSL/TLS configuration",
+                        "Resource not available (queue, topic, table, etc.)"),
+                Arrays.asList(
+                        "Verify network connectivity to external services",
+                        "Check credentials and authentication configuration",
+                        "Ensure the target resource exists (queue, topic, 
etc.)",
+                        "Review SSL/TLS configuration if using secure 
connections",
+                        "Check if the port is already in use"),
+                Arrays.asList(
+                        CAMEL_MANUAL_DOC + "routes.html",
+                        CAMEL_MANUAL_DOC + "lifecycle.html")));
+
+        exceptions.put("NoTypeConversionAvailableException", new ExceptionInfo(
+                "Camel could not find a type converter to convert between the 
required types.",
+                Arrays.asList(
+                        "Trying to convert a message body to an incompatible 
type",
+                        "Missing type converter on the classpath",
+                        "Custom type without a registered converter",
+                        "Null body when a non-null type is expected"),
+                Arrays.asList(
+                        "Check the source and target types in the conversion",
+                        "Add appropriate data format or converter dependency",
+                        "Use explicit marshal/unmarshal instead of implicit 
conversion",
+                        "Register a custom TypeConverter if needed",
+                        "Check if the message body is null"),
+                Arrays.asList(
+                        CAMEL_MANUAL_DOC + "type-converter.html",
+                        CAMEL_MANUAL_DOC + "data-format.html")));
+
+        exceptions.put("CamelExecutionException", new ExceptionInfo(
+                "A wrapper exception thrown during route execution. The root 
cause is in the nested exception.",
+                Arrays.asList(
+                        "Exception thrown by a processor or bean in the route",
+                        "External service failure (HTTP error, broker 
disconnect, etc.)",
+                        "Data transformation error",
+                        "Timeout during synchronous processing"),
+                Arrays.asList(
+                        "Inspect the nested/caused-by exception for the actual 
error",
+                        "Add error handling (onException, errorHandler, 
doTry/doCatch) to the route",
+                        "Check the processor or bean that failed",
+                        "Review the full stack trace for the root cause"),
+                Arrays.asList(
+                        CAMEL_MANUAL_DOC + "exception-clause.html",
+                        CAMEL_MANUAL_DOC + "error-handler.html",
+                        CAMEL_MANUAL_DOC + "try-catch-finally.html")));
+
+        exceptions.put("ExchangeTimedOutException", new ExceptionInfo(
+                "An exchange did not complete within the configured timeout 
period.",
+                Arrays.asList(
+                        "Slow downstream service or endpoint",
+                        "Network latency or connectivity issues",
+                        "Timeout value too low for the operation",
+                        "Deadlock or resource contention",
+                        "Direct/SEDA consumer not available"),
+                Arrays.asList(
+                        "Increase the timeout value if appropriate",
+                        "Add circuit breaker pattern for unreliable services",
+                        "Check network connectivity to the target service",
+                        "Use async processing for long-running operations",
+                        "Add timeout error handling in the route"),
+                Arrays.asList(
+                        CAMEL_MANUAL_DOC + "request-reply.html",
+                        CAMEL_EIP_DOC + "circuitBreaker-eip.html")));
+
+        exceptions.put("DirectConsumerNotAvailableException", new 
ExceptionInfo(
+                "No consumer is available for a direct endpoint. The direct 
component requires an active consumer.",
+                Arrays.asList(
+                        "Target route with the direct endpoint is not started",
+                        "Typo in the direct endpoint name",
+                        "Route with the direct consumer was stopped or 
removed",
+                        "Timing issue during startup — producer route started 
before consumer route"),
+                Arrays.asList(
+                        "Ensure a route with from(\"direct:name\") exists and 
is started",
+                        "Verify the direct endpoint name matches between 
producer and consumer",
+                        "Use SEDA instead of direct if startup ordering is 
uncertain",
+                        "Configure route startup ordering if needed"),
+                Arrays.asList(
+                        CAMEL_COMPONENT_DOC + "direct-component.html",
+                        CAMEL_COMPONENT_DOC + "seda-component.html")));
+
+        exceptions.put("CamelExchangeException", new ExceptionInfo(
+                "A general exception related to exchange processing.",
+                Arrays.asList(
+                        "Processor failure during exchange handling",
+                        "Invalid exchange pattern (InOnly vs InOut mismatch)",
+                        "Missing required headers or properties",
+                        "Exchange body cannot be processed"),
+                Arrays.asList(
+                        "Check the exchange pattern matches the endpoint 
requirements",
+                        "Verify required headers are set on the exchange",
+                        "Add error handling to catch and process the 
exception",
+                        "Inspect the exchange body type and content"),
+                Arrays.asList(
+                        CAMEL_MANUAL_DOC + "exchange.html",
+                        CAMEL_MANUAL_DOC + "exchange-pattern.html")));
+
+        exceptions.put("InvalidPayloadException", new ExceptionInfo(
+                "The message payload (body) is not of the expected type and 
cannot be converted.",
+                Arrays.asList(
+                        "Message body is null when a value is expected",
+                        "Message body type does not match the expected type",
+                        "Missing type converter for the body type",
+                        "Upstream processor produced unexpected output"),
+                Arrays.asList(
+                        "Check the message body type before the failing 
processor",
+                        "Use convertBodyTo() to explicitly convert the body 
type",
+                        "Add a null check or default value for the body",
+                        "Add the appropriate data format dependency for 
marshalling/unmarshalling"),
+                Arrays.asList(
+                        CAMEL_MANUAL_DOC + "message.html",
+                        CAMEL_MANUAL_DOC + "type-converter.html")));
+
+        exceptions.put("PropertyBindingException", new ExceptionInfo(
+                "Failed to bind a property or option to a Camel component, 
endpoint, or bean.",
+                Arrays.asList(
+                        "Property name does not exist on the target object",
+                        "Property value has wrong type (e.g., string for a 
boolean)",
+                        "Misspelled property or option name",
+                        "Property placeholder could not be resolved"),
+                Arrays.asList(
+                        "Check the property name spelling against the 
component documentation",
+                        "Verify the property value type matches the expected 
type",
+                        "Use property placeholders correctly: 
{{property.name}}",
+                        "Check application.properties or YAML configuration 
for correct keys"),
+                Arrays.asList(
+                        CAMEL_MANUAL_DOC + "using-propertyplaceholder.html",
+                        CAMEL_MANUAL_DOC + "component.html")));
+
+        exceptions.put("NoSuchBeanException", new ExceptionInfo(
+                "A referenced bean could not be found in the Camel registry.",
+                Arrays.asList(
+                        "Bean not registered in the Spring/CDI/Camel registry",
+                        "Typo in the bean name reference",
+                        "Bean class not on the classpath",
+                        "Missing @Named or @Component annotation on the bean 
class",
+                        "Bean definition not scanned by component scan"),
+                Arrays.asList(
+                        "Verify the bean is registered with the correct name",
+                        "Check spelling of the bean reference",
+                        "Ensure the bean class has proper annotations (@Named, 
@Component, etc.)",
+                        "Verify component scanning includes the bean's 
package",
+                        "Register the bean manually in RouteBuilder 
configure() if needed"),
+                Arrays.asList(
+                        CAMEL_MANUAL_DOC + "registry.html",
+                        CAMEL_MANUAL_DOC + "bean-binding.html")));
+
+        exceptions.put("NoSuchHeaderException", new ExceptionInfo(
+                "A required header was not found on the message.",
+                Arrays.asList(
+                        "Header not set by upstream processors",
+                        "Header name is misspelled",
+                        "Header was removed by a previous processor",
+                        "Using wrong header constant name"),
+                Arrays.asList(
+                        "Verify the header name matches what upstream 
processors set",
+                        "Use header constants from the component's class 
(e.g., KafkaConstants)",
+                        "Add a setHeader() before the processor that requires 
it",
+                        "Add a null check or default value for the header"),
+                Arrays.asList(
+                        CAMEL_MANUAL_DOC + "message.html")));
+
+        exceptions.put("PredicateValidationException", new ExceptionInfo(
+                "A predicate validation failed. This is typically thrown by 
the validate() DSL or a filter condition.",
+                Arrays.asList(
+                        "Message did not match the expected validation 
predicate",
+                        "Invalid or unexpected message content",
+                        "Predicate expression has a syntax error",
+                        "Null values in the expression evaluation"),
+                Arrays.asList(
+                        "Review the predicate expression for correctness",
+                        "Check the message content matches the expected 
format",
+                        "Add error handling for validation failures",
+                        "Use a more lenient predicate or add default values"),
+                Arrays.asList(
+                        CAMEL_EIP_DOC + "validate-eip.html",
+                        CAMEL_MANUAL_DOC + "predicate.html")));
+
+        exceptions.put("NoSuchLanguageException", new ExceptionInfo(
+                "The specified expression language is not available.",
+                Arrays.asList(
+                        "Missing language dependency (e.g., camel-jsonpath, 
camel-xpath)",
+                        "Typo in the language name",
+                        "Using a language that does not exist"),
+                Arrays.asList(
+                        "Add the required camel-<language> dependency",
+                        "Verify the language name is spelled correctly",
+                        "Use 'simple' language which is included in 
camel-core"),
+                Arrays.asList(
+                        CAMEL_MANUAL_DOC + "languages.html")));
+
+        exceptions.put("FailedToCreateConsumerException", new ExceptionInfo(
+                "A consumer could not be created for the endpoint.",
+                Arrays.asList(
+                        "Cannot connect to the source system (broker, server, 
etc.)",
+                        "Invalid consumer configuration options",
+                        "Authentication failure",
+                        "Missing required consumer options",
+                        "Resource does not exist (topic, queue, file path, 
etc.)"),
+                Arrays.asList(
+                        "Verify connectivity to the source system",
+                        "Check the consumer configuration options",
+                        "Ensure credentials are correct",
+                        "Verify the target resource exists",
+                        "Check the full exception chain for the root cause"),
+                Arrays.asList(
+                        CAMEL_MANUAL_DOC + "component.html")));
+
+        exceptions.put("FailedToCreateProducerException", new ExceptionInfo(
+                "A producer could not be created for the endpoint.",
+                Arrays.asList(
+                        "Cannot connect to the target system",
+                        "Invalid producer configuration options",
+                        "Authentication failure",
+                        "Missing required producer options"),
+                Arrays.asList(
+                        "Verify connectivity to the target system",
+                        "Check the producer configuration options",
+                        "Ensure credentials are correct",
+                        "Check the full exception chain for the root cause"),
+                Arrays.asList(
+                        CAMEL_MANUAL_DOC + "component.html")));
+
+        exceptions.put("CamelAuthorizationException", new ExceptionInfo(
+                "An authorization check failed. The current identity does not 
have permission to perform the operation.",
+                Arrays.asList(
+                        "Insufficient permissions for the user or service 
account",
+                        "Missing or expired authentication token",
+                        "Security policy denying the operation",
+                        "Incorrect RBAC or ACL configuration"),
+                Arrays.asList(
+                        "Check the user/service account permissions",
+                        "Verify the authentication token is valid and not 
expired",
+                        "Review the security policies and ACLs",
+                        "Ensure the correct security provider is configured"),
+                Arrays.asList(
+                        CAMEL_MANUAL_DOC + "security.html")));
+
+        return exceptions;
+    }
+
+    /**
+     * Holds diagnostic information about a known Camel exception.
+     */
+    static class ExceptionInfo {
+        final String description;
+        final List<String> commonCauses;
+        final List<String> suggestedFixes;
+        final List<String> documentationLinks;
+
+        ExceptionInfo(String description, List<String> commonCauses, 
List<String> suggestedFixes,
+                      List<String> documentationLinks) {
+            this.description = description;
+            this.commonCauses = commonCauses;
+            this.suggestedFixes = suggestedFixes;
+            this.documentationLinks = documentationLinks;
+        }
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseToolsTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseToolsTest.java
new file mode 100644
index 000000000000..92486b03c223
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseToolsTest.java
@@ -0,0 +1,312 @@
+/*
+ * 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 io.quarkiverse.mcp.server.ToolCallException;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class DiagnoseToolsTest {
+
+    private final DiagnoseTools tools = new DiagnoseTools();
+
+    // ---- Input validation ----
+
+    @Test
+    void nullErrorThrows() {
+        assertThatThrownBy(() -> tools.camel_error_diagnose(null))
+                .isInstanceOf(ToolCallException.class)
+                .hasMessageContaining("required");
+    }
+
+    @Test
+    void blankErrorThrows() {
+        assertThatThrownBy(() -> tools.camel_error_diagnose("   "))
+                .isInstanceOf(ToolCallException.class)
+                .hasMessageContaining("required");
+    }
+
+    // ---- Exception identification ----
+
+    @Test
+    void identifiesNoSuchEndpointException() throws Exception {
+        String error = "org.apache.camel.NoSuchEndpointException: No endpoint 
could be found for: kafak:myTopic";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonArray exceptions = result.getCollection("identifiedExceptions");
+
+        assertThat(exceptions).isNotEmpty();
+        
assertThat(exceptions.getMap(0).get("exception")).isEqualTo("NoSuchEndpointException");
+    }
+
+    @Test
+    void identifiesResolveEndpointFailedException() throws Exception {
+        String error = "org.apache.camel.ResolveEndpointFailedException: "
+                       + "Failed to resolve endpoint: 
kafka:myTopic?unknownOption=value due to: "
+                       + "There are 1 parameters that couldn't be set on the 
endpoint.";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonArray exceptions = result.getCollection("identifiedExceptions");
+
+        assertThat(exceptions).isNotEmpty();
+        
assertThat(exceptions.getMap(0).get("exception")).isEqualTo("ResolveEndpointFailedException");
+    }
+
+    @Test
+    void identifiesFailedToCreateRouteException() throws Exception {
+        String error = "org.apache.camel.FailedToCreateRouteException: "
+                       + "Failed to create route route1: 
Route(route1)[From[direct:start] -> [To[log:out]]]";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonArray exceptions = result.getCollection("identifiedExceptions");
+
+        assertThat(exceptions).isNotEmpty();
+        JsonObject first = (JsonObject) exceptions.get(0);
+        
assertThat(first.getString("exception")).isEqualTo("FailedToCreateRouteException");
+    }
+
+    @Test
+    void identifiesMultipleExceptions() throws Exception {
+        String error = "org.apache.camel.FailedToCreateRouteException: Failed 
to create route\n"
+                       + "Caused by: 
org.apache.camel.ResolveEndpointFailedException: Failed to resolve endpoint";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonArray exceptions = result.getCollection("identifiedExceptions");
+
+        assertThat(exceptions.size()).isGreaterThanOrEqualTo(2);
+    }
+
+    @Test
+    void identifiesNoTypeConversionAvailableException() throws Exception {
+        String error = "org.apache.camel.NoTypeConversionAvailableException: "
+                       + "No type converter available to convert from type: 
java.lang.String "
+                       + "to the required type: java.io.InputStream";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonArray exceptions = result.getCollection("identifiedExceptions");
+
+        assertThat(exceptions).isNotEmpty();
+        
assertThat(exceptions.getMap(0).get("exception")).isEqualTo("NoTypeConversionAvailableException");
+    }
+
+    @Test
+    void identifiesExchangeTimedOutException() throws Exception {
+        String error = "org.apache.camel.ExchangeTimedOutException: "
+                       + "The OUT message was not received within: 30000 
millis";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonArray exceptions = result.getCollection("identifiedExceptions");
+
+        assertThat(exceptions).isNotEmpty();
+        
assertThat(exceptions.getMap(0).get("exception")).isEqualTo("ExchangeTimedOutException");
+    }
+
+    @Test
+    void identifiesDirectConsumerNotAvailableException() throws Exception {
+        String error = 
"org.apache.camel.component.direct.DirectConsumerNotAvailableException: "
+                       + "No consumers available on endpoint: 
direct://myEndpoint";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonArray exceptions = result.getCollection("identifiedExceptions");
+
+        assertThat(exceptions).isNotEmpty();
+        
assertThat(exceptions.getMap(0).get("exception")).isEqualTo("DirectConsumerNotAvailableException");
+    }
+
+    @Test
+    void identifiesPropertyBindingException() throws Exception {
+        String error = "org.apache.camel.PropertyBindingException: "
+                       + "Error binding property (brokerz=localhost:9092) with 
name: brokerz on bean";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonArray exceptions = result.getCollection("identifiedExceptions");
+
+        assertThat(exceptions).isNotEmpty();
+        
assertThat(exceptions.getMap(0).get("exception")).isEqualTo("PropertyBindingException");
+    }
+
+    @Test
+    void identifiesNoSuchBeanException() throws Exception {
+        String error = "org.apache.camel.NoSuchBeanException: "
+                       + "No bean could be found in the registry for: 
myProcessor";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonArray exceptions = result.getCollection("identifiedExceptions");
+
+        assertThat(exceptions).isNotEmpty();
+        
assertThat(exceptions.getMap(0).get("exception")).isEqualTo("NoSuchBeanException");
+    }
+
+    // ---- Component identification ----
+
+    @Test
+    void identifiesKafkaComponent() throws Exception {
+        String error = "org.apache.camel.ResolveEndpointFailedException: "
+                       + "Failed to resolve endpoint: 
kafka:myTopic?brokers=localhost:9092";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonArray components = result.getCollection("identifiedComponents");
+
+        assertThat(components).isNotEmpty();
+        assertThat(components.stream()
+                .map(c -> ((JsonObject) c).getString("name"))
+                .toList())
+                .contains("kafka");
+    }
+
+    @Test
+    void identifiesDirectComponent() throws Exception {
+        String error = "No consumers available on endpoint: direct://start";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonArray components = result.getCollection("identifiedComponents");
+
+        assertThat(components.stream()
+                .map(c -> ((JsonObject) c).getString("name"))
+                .toList())
+                .contains("direct");
+    }
+
+    // ---- Result structure ----
+
+    @Test
+    void resultContainsCommonCauses() throws Exception {
+        String error = "org.apache.camel.NoSuchEndpointException: No endpoint 
could be found for: xyz:test";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonArray exceptions = result.getCollection("identifiedExceptions");
+        JsonObject first = (JsonObject) exceptions.get(0);
+
+        JsonArray causes = (JsonArray) first.get("commonCauses");
+        assertThat(causes).isNotEmpty();
+    }
+
+    @Test
+    void resultContainsSuggestedFixes() throws Exception {
+        String error = "org.apache.camel.NoSuchEndpointException: No endpoint 
could be found for: xyz:test";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonArray exceptions = result.getCollection("identifiedExceptions");
+        JsonObject first = (JsonObject) exceptions.get(0);
+
+        JsonArray fixes = (JsonArray) first.get("suggestedFixes");
+        assertThat(fixes).isNotEmpty();
+    }
+
+    @Test
+    void resultContainsDocumentationLinks() throws Exception {
+        String error = "org.apache.camel.NoSuchEndpointException: No endpoint 
could be found for: xyz:test";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonArray exceptions = result.getCollection("identifiedExceptions");
+        JsonObject first = (JsonObject) exceptions.get(0);
+
+        JsonArray docs = first.getCollection("documentationLinks");
+        assertThat(docs).isNotEmpty();
+        
assertThat(docs.get(0).toString()).startsWith("https://camel.apache.org/";);
+    }
+
+    @Test
+    void resultContainsSummary() throws Exception {
+        String error = "org.apache.camel.NoSuchEndpointException: No endpoint";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonObject summary = result.getMap("summary");
+
+        assertThat(summary).isNotNull();
+        assertThat(summary.getBoolean("diagnosed")).isTrue();
+        assertThat(summary.getInteger("exceptionCount")).isGreaterThan(0);
+    }
+
+    @Test
+    void componentDocumentationUrlPresent() throws Exception {
+        String error = "Failed to resolve endpoint: kafka:myTopic";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonArray components = result.getCollection("identifiedComponents");
+
+        if (!components.isEmpty()) {
+            JsonObject comp = (JsonObject) components.get(0);
+            
assertThat(comp.getString("documentationUrl")).contains("camel.apache.org");
+        }
+    }
+
+    // ---- Unrecognized errors ----
+
+    @Test
+    void unrecognizedErrorReturnsDiagnosedFalse() throws Exception {
+        String error = "Some random error that is not a Camel exception";
+
+        String json = tools.camel_error_diagnose(error);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonObject summary = result.getMap("summary");
+
+        assertThat(summary.getBoolean("diagnosed")).isFalse();
+        assertThat(summary.getInteger("exceptionCount")).isEqualTo(0);
+    }
+
+    // ---- Full stack trace ----
+
+    @Test
+    void handlesFullStackTrace() throws Exception {
+        String stackTrace
+                = """
+                        org.apache.camel.FailedToCreateRouteException: Failed 
to create route route1 at: >>> To[kafka:myTopic] <<< in route: 
Route(route1)[From[timer:tick] -> [To[kafka:myTopic]]] because of Failed to 
resolve endpoint: kafka:myTopic due to: No component found with scheme: kafka
+                        \tat 
org.apache.camel.reifier.RouteReifier.doCreateRoute(RouteReifier.java:230)
+                        \tat 
org.apache.camel.reifier.RouteReifier.createRoute(RouteReifier.java:71)
+                        \tat 
org.apache.camel.impl.DefaultCamelContext.startRouteDefinitions(DefaultCamelContext.java:852)
+                        Caused by: 
org.apache.camel.ResolveEndpointFailedException: Failed to resolve endpoint: 
kafka:myTopic due to: No component found with scheme: kafka
+                        \tat 
org.apache.camel.impl.engine.AbstractCamelContext.getEndpoint(AbstractCamelContext.java:893)
+                        Caused by: org.apache.camel.NoSuchEndpointException: 
No endpoint could be found for: kafka:myTopic
+                        \tat 
org.apache.camel.component.direct.DirectComponent.createEndpoint(DirectComponent.java:62)
+                        """;
+
+        String json = tools.camel_error_diagnose(stackTrace);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonArray exceptions = result.getCollection("identifiedExceptions");
+
+        // Should identify all three exceptions in the chain
+        assertThat(exceptions.size()).isGreaterThanOrEqualTo(3);
+
+        JsonArray components = result.getCollection("identifiedComponents");
+        assertThat(components.stream()
+                .map(c -> ((JsonObject) c).getString("name"))
+                .toList())
+                .contains("kafka");
+    }
+}

Reply via email to