This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-23855 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 92b4f3c20c1ef332d369a0e4f48934a250724d05 Author: Claus Ibsen <[email protected]> AuthorDate: Mon Jun 29 18:45:44 2026 +0200 CAMEL-23855: camel-jbang - Add missing MCP tools to AI assistant Co-Authored-By: Claude <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../camel/dsl/jbang/core/commands/AskTools.java | 130 +++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/AskTools.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/AskTools.java index 50c196a83a92..05a89ddddbe4 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/AskTools.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/AskTools.java @@ -112,6 +112,26 @@ public class AskTools { "get_properties", "Show configuration properties of the running Camel application.", emptyParams())); + tools.add(new LlmClient.ToolDef( + "get_memory", + "Show JVM memory usage (heap/non-heap), garbage collection stats, and thread counts.", + emptyParams())); + tools.add(new LlmClient.ToolDef( + "get_errors", + "Get captured routing errors from the running Camel application. Returns error details including exception, exchange context, and route information.", + emptyParams())); + tools.add(new LlmClient.ToolDef( + "get_history", + "Get the message history trace of the last completed exchange. Shows the route path, processors visited, headers, body, and timing.", + emptyParams())); + tools.add(new LlmClient.ToolDef( + "get_variables", + "Show exchange variables in the Camel context.", + emptyParams())); + tools.add(new LlmClient.ToolDef( + "get_services", + "Show services registered in the Camel service registry.", + emptyParams())); tools.add(new LlmClient.ToolDef( "get_route_source", @@ -133,12 +153,40 @@ public class AskTools { "get_top_processors", "Show top processor statistics: which processors are slowest and most active.", emptyParams())); + tools.add(new LlmClient.ToolDef( + "get_route_topology", + "Get the inter-route topology showing how routes connect to each other and to external endpoints.", + emptyParams())); tools.add(new LlmClient.ToolDef( "trace_control", "Enable, disable, or dump message tracing.", objectParams(Map.of( "action", stringProp("Action: enable, disable, or dump"))))); + tools.add(new LlmClient.ToolDef( + "send_message", + "Send a test message to a Camel endpoint in the running application.", + objectParams(Map.of( + "endpoint", stringProp("Endpoint URI to send to (e.g., direct:myRoute, seda:queue)"), + "body", stringProp("Message body to send"), + "headers", stringProp("Message headers as key=value pairs separated by newlines"))))); + tools.add(new LlmClient.ToolDef( + "eval_expression", + "Evaluate a Camel expression in the given language (e.g., simple, jsonpath, xpath) against the running context.", + objectParams(Map.of( + "language", stringProp("Expression language (e.g., simple, jsonpath, xpath, jq)"), + "expression", stringProp("Expression to evaluate"))))); + tools.add(new LlmClient.ToolDef( + "browse_endpoint", + "Browse messages in a Camel endpoint (e.g., browse messages queued in a SEDA endpoint).", + objectParams(Map.of( + "endpoint", stringProp("Endpoint URI to browse (e.g., seda:queue)"), + "limit", stringProp("Maximum number of messages to return (default: 50)"))))); + tools.add(new LlmClient.ToolDef( + "get_thread_dump", + "Get a JVM thread dump showing thread names, states, and stack traces.", + emptyParams())); + tools.add(new LlmClient.ToolDef( "stop_route", "Gracefully stop a route. The route will finish processing in-flight exchanges before stopping.", @@ -255,12 +303,30 @@ public class AskTools { targetPid < 0 ? NO_PROCESS : RuntimeHelper.readStatusSection(targetPid, "consumers"); case "get_properties" -> targetPid < 0 ? NO_PROCESS : RuntimeHelper.readStatusSection(targetPid, "properties"); + case "get_memory" -> + targetPid < 0 ? NO_PROCESS : RuntimeHelper.readStatusSection(targetPid, "memory"); + case "get_errors" -> targetPid < 0 ? NO_PROCESS : executeGetErrors(); + case "get_history" -> targetPid < 0 ? NO_PROCESS : executeGetHistory(); + case "get_variables" -> + targetPid < 0 ? NO_PROCESS : RuntimeHelper.readStatusSection(targetPid, "variables"); + case "get_services" -> + targetPid < 0 ? NO_PROCESS : RuntimeHelper.readStatusSection(targetPid, "services"); case "get_route_source" -> targetPid < 0 ? NO_PROCESS : executeRouteSource(args); case "get_route_dump" -> targetPid < 0 ? NO_PROCESS : executeRouteDump(args); case "get_route_structure" -> targetPid < 0 ? NO_PROCESS : executeRouteStructure(args); case "get_top_processors" -> targetPid < 0 ? NO_PROCESS : RuntimeHelper.executeAction(targetPid, "top-processors", null); + case "get_route_topology" -> + targetPid < 0 ? NO_PROCESS : RuntimeHelper.executeAction(targetPid, "route-topology", root -> { + root.put("metric", "true"); + root.put("external", "true"); + }); case "trace_control" -> targetPid < 0 ? NO_PROCESS : executeTraceControl(args); + case "send_message" -> targetPid < 0 ? NO_PROCESS : executeSendMessage(args); + case "eval_expression" -> targetPid < 0 ? NO_PROCESS : executeEvalExpression(args); + case "browse_endpoint" -> targetPid < 0 ? NO_PROCESS : executeBrowseEndpoint(args); + case "get_thread_dump" -> + targetPid < 0 ? NO_PROCESS : RuntimeHelper.executeAction(targetPid, "thread-dump", null); case "stop_route" -> targetPid < 0 ? NO_PROCESS : executeRouteCommand(args, "stop"); case "start_route" -> targetPid < 0 ? NO_PROCESS : executeRouteCommand(args, "start"); case "suspend_route" -> targetPid < 0 ? NO_PROCESS : executeRouteCommand(args, "suspend"); @@ -414,6 +480,70 @@ public class AskTools { }); } + private String executeGetErrors() { + JsonObject errors = RuntimeHelper.readErrorFile(targetPid); + if (errors == null) { + return "No errors captured."; + } + return errors.toJson(); + } + + private String executeGetHistory() { + JsonObject history = RuntimeHelper.readHistoryFile(targetPid); + if (history == null) { + return "No message history available."; + } + return history.toJson(); + } + + private String executeSendMessage(JsonObject args) { + String endpoint = args.getString("endpoint"); + if (endpoint == null || endpoint.isBlank()) { + return "Error: endpoint is required"; + } + String body = args.getString("body"); + String headers = args.getString("headers"); + JsonObject result = RuntimeHelper.sendMessage(targetPid, endpoint, body, headers); + return result.toJson(); + } + + private String executeEvalExpression(JsonObject args) { + String language = args.getString("language"); + String expression = args.getString("expression"); + if (language == null || language.isBlank()) { + return "Error: language is required"; + } + if (expression == null || expression.isBlank()) { + return "Error: expression is required"; + } + return RuntimeHelper.executeAction(targetPid, "eval", root -> { + root.put("language", language); + root.put("predicate", "false"); + root.put("template", Jsoner.escape(expression)); + }); + } + + private String executeBrowseEndpoint(JsonObject args) { + String endpoint = args.getString("endpoint"); + if (endpoint == null || endpoint.isBlank()) { + return "Error: endpoint is required"; + } + String limitStr = args.getString("limit"); + int limit = 50; + if (limitStr != null && !limitStr.isBlank()) { + try { + limit = Integer.parseInt(limitStr); + } catch (NumberFormatException e) { + // use default + } + } + int browseLimit = limit; + return RuntimeHelper.executeAction(targetPid, "browse", root -> { + root.put("filter", endpoint); + root.put("limit", browseLimit); + }); + } + // ---- Catalog tools ---- private String executeCatalogComponents(JsonObject args) {
