This is an automated email from the ASF dual-hosted git repository. acosentino pushed a commit to branch 23167 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 52f91e8076d3f13c0df8db0c16303a354aa5cfd9 Author: Andrea Cosentino <[email protected]> AuthorDate: Tue Mar 10 11:50:22 2026 +0100 CAMEL-23167 - Camel-jbang-mcp - Extract exception catalog from DiagnoseTools into MCP Resource Signed-off-by: Andrea Cosentino <[email protected]> --- .../mcp/{DiagnoseTools.java => DiagnoseData.java} | 277 +++-------------- .../jbang/core/commands/mcp/DiagnoseResources.java | 128 ++++++++ .../dsl/jbang/core/commands/mcp/DiagnoseTools.java | 343 +-------------------- .../jbang/core/commands/mcp/DiagnoseToolsTest.java | 7 +- 4 files changed, 181 insertions(+), 574 deletions(-) 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/DiagnoseData.java similarity index 66% copy from dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseTools.java copy to dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseData.java index 879063c6e283..008641b7fe73 100644 --- 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/DiagnoseData.java @@ -16,250 +16,32 @@ */ package org.apache.camel.dsl.jbang.core.commands.mcp; -import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; 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. + * Shared holder for Camel exception diagnostic reference data used by both {@link DiagnoseTools} (MCP Tool) and + * {@link DiagnoseResources} (MCP Resources). * <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. + * Contains the registry of known Camel exceptions with their descriptions, common causes, suggested fixes, and + * documentation links. */ @ApplicationScoped -public class DiagnoseTools { +public class DiagnoseData { 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; - } + private static final Map<String, ExceptionInfo> KNOWN_EXCEPTIONS; - /** - * 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() { + static { Map<String, ExceptionInfo> exceptions = new LinkedHashMap<>(); exceptions.put("NoSuchEndpointException", new ExceptionInfo( @@ -556,24 +338,39 @@ public class DiagnoseTools { Arrays.asList( CAMEL_MANUAL_DOC + "security.html"))); - return exceptions; + KNOWN_EXCEPTIONS = Collections.unmodifiableMap(exceptions); } /** - * Holds diagnostic information about a known Camel exception. + * Get all known Camel exceptions with their diagnostic information. + */ + public Map<String, ExceptionInfo> getKnownExceptions() { + return KNOWN_EXCEPTIONS; + } + + /** + * Get diagnostic information for a specific exception by name. + * + * @return the exception info, or null if not found */ - static class ExceptionInfo { - final String description; - final List<String> commonCauses; - final List<String> suggestedFixes; - final List<String> documentationLinks; + public ExceptionInfo getException(String name) { + return KNOWN_EXCEPTIONS.get(name); + } - ExceptionInfo(String description, List<String> commonCauses, List<String> suggestedFixes, - List<String> documentationLinks) { - this.description = description; - this.commonCauses = commonCauses; - this.suggestedFixes = suggestedFixes; - this.documentationLinks = documentationLinks; - } + /** + * Get all known exception names. + */ + public List<String> getExceptionNames() { + return List.copyOf(KNOWN_EXCEPTIONS.keySet()); + } + + /** + * Holds diagnostic information about a known Camel exception. + */ + public record ExceptionInfo( + String description, + List<String> commonCauses, + List<String> suggestedFixes, + List<String> documentationLinks) { } } 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 new file mode 100644 index 000000000000..570dfee4535d --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseResources.java @@ -0,0 +1,128 @@ +/* + * 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.Map; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import io.quarkiverse.mcp.server.Resource; +import io.quarkiverse.mcp.server.ResourceTemplate; +import io.quarkiverse.mcp.server.ResourceTemplateArg; +import io.quarkiverse.mcp.server.TextResourceContents; +import org.apache.camel.util.json.JsonArray; +import org.apache.camel.util.json.JsonObject; + +/** + * MCP Resources exposing Camel exception diagnostic reference data. + * <p> + * These resources provide browseable exception catalog data that clients can pull into context independently of error + * diagnosis. This allows LLM clients to proactively load exception reference data without needing a stack trace to + * trigger the diagnose tool. + */ +@ApplicationScoped +public class DiagnoseResources { + + @Inject + DiagnoseData diagnoseData; + + /** + * All known Camel exceptions with descriptions and documentation links. + */ + @Resource(uri = "camel://error/exception-catalog", + name = "camel_error_exception_catalog", + title = "Camel Exception Catalog", + description = "Registry of all known Camel exceptions with descriptions, common causes, " + + "suggested fixes, and links to relevant documentation. Covers exceptions like " + + "NoSuchEndpointException, ResolveEndpointFailedException, FailedToCreateRouteException, " + + "and more.", + mimeType = "application/json") + public TextResourceContents exceptionCatalog() { + JsonObject result = new JsonObject(); + + JsonArray exceptions = new JsonArray(); + for (Map.Entry<String, DiagnoseData.ExceptionInfo> entry : diagnoseData.getKnownExceptions().entrySet()) { + JsonObject exJson = new JsonObject(); + exJson.put("name", entry.getKey()); + exJson.put("description", entry.getValue().description()); + + JsonArray docsJson = new JsonArray(); + for (String doc : entry.getValue().documentationLinks()) { + docsJson.add(doc); + } + exJson.put("documentationLinks", docsJson); + + exceptions.add(exJson); + } + + result.put("exceptions", exceptions); + result.put("totalCount", exceptions.size()); + + return new TextResourceContents("camel://error/exception-catalog", result.toJson(), "application/json"); + } + + /** + * Detail for a specific Camel exception by name. + */ + @ResourceTemplate(uriTemplate = "camel://error/exception/{name}", + name = "camel_error_exception_detail", + title = "Exception Detail", + description = "Full diagnostic detail for a specific Camel exception including description, " + + "common causes, suggested fixes, and documentation links.", + mimeType = "application/json") + public TextResourceContents exceptionDetail( + @ResourceTemplateArg(name = "name") String name) { + + String uri = "camel://error/exception/" + name; + + DiagnoseData.ExceptionInfo info = diagnoseData.getException(name); + if (info == null) { + JsonObject result = new JsonObject(); + result.put("name", name); + 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 = new JsonObject(); + result.put("name", name); + result.put("found", true); + result.put("description", info.description()); + + JsonArray causesJson = new JsonArray(); + for (String cause : info.commonCauses()) { + causesJson.add(cause); + } + result.put("commonCauses", causesJson); + + JsonArray fixesJson = new JsonArray(); + for (String fix : info.suggestedFixes()) { + fixesJson.add(fix); + } + result.put("suggestedFixes", fixesJson); + + JsonArray docsJson = new JsonArray(); + for (String doc : info.documentationLinks()) { + docsJson.add(doc); + } + result.put("documentationLinks", docsJson); + + return new TextResourceContents(uri, result.toJson(), "application/json"); + } +} 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 index 879063c6e283..7f2b7cdb6ae8 100644 --- 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 @@ -17,14 +17,12 @@ 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 jakarta.inject.Inject; import io.quarkiverse.mcp.server.Tool; import io.quarkiverse.mcp.server.ToolArg; @@ -47,7 +45,6 @@ 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/"; /** @@ -68,12 +65,13 @@ public class DiagnoseTools { private static final Pattern ROUTE_ID_PATTERN = Pattern.compile("route[:\\s]+['\"]?([a-zA-Z0-9_-]+)['\"]?", Pattern.CASE_INSENSITIVE); + @Inject + DiagnoseData diagnoseData; + private final CamelCatalog catalog; - private final Map<String, ExceptionInfo> knownExceptions; public DiagnoseTools() { this.catalog = new DefaultCamelCatalog(); - this.knownExceptions = buildKnownExceptions(); } /** @@ -98,26 +96,26 @@ public class DiagnoseTools { List<String> matchedExceptions = identifyExceptions(error); JsonArray exceptionsJson = new JsonArray(); for (String exceptionName : matchedExceptions) { - ExceptionInfo info = knownExceptions.get(exceptionName); + DiagnoseData.ExceptionInfo info = diagnoseData.getException(exceptionName); if (info != null) { JsonObject exJson = new JsonObject(); exJson.put("exception", exceptionName); - exJson.put("description", info.description); + exJson.put("description", info.description()); JsonArray causesJson = new JsonArray(); - for (String cause : info.commonCauses) { + for (String cause : info.commonCauses()) { causesJson.add(cause); } exJson.put("commonCauses", causesJson); JsonArray fixesJson = new JsonArray(); - for (String fix : info.suggestedFixes) { + for (String fix : info.suggestedFixes()) { fixesJson.add(fix); } exJson.put("suggestedFixes", fixesJson); JsonArray docsJson = new JsonArray(); - for (String doc : info.documentationLinks) { + for (String doc : info.documentationLinks()) { docsJson.add(doc); } exJson.put("documentationLinks", docsJson); @@ -187,7 +185,7 @@ public class DiagnoseTools { */ private List<String> identifyExceptions(String error) { List<String> matched = new ArrayList<>(); - for (String exceptionName : knownExceptions.keySet()) { + for (String exceptionName : diagnoseData.getKnownExceptions().keySet()) { if (error.contains(exceptionName)) { matched.add(exceptionName); } @@ -255,325 +253,4 @@ public class DiagnoseTools { || 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"))); - - 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 index 92486b03c223..be45f4d52942 100644 --- 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 @@ -27,7 +27,12 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; class DiagnoseToolsTest { - private final DiagnoseTools tools = new DiagnoseTools(); + private final DiagnoseTools tools; + + DiagnoseToolsTest() { + tools = new DiagnoseTools(); + tools.diagnoseData = new DiagnoseData(); + } // ---- Input validation ----
