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

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


The following commit(s) were added to refs/heads/main by this push:
     new 95d5e757391a CAMEL-23873: Introduce shared ToolRegistry for AI tools 
(#24337)
95d5e757391a is described below

commit 95d5e757391afb1a1930e99b5b2d00aae6fa494b
Author: Guillaume Nodet <[email protected]>
AuthorDate: Thu Jul 2 09:43:29 2026 +0200

    CAMEL-23873: Introduce shared ToolRegistry for AI tools (#24337)
    
    Add a shared ToolRegistry in camel-jbang-core that serves as the single
    source of truth for AI tool definitions and execution logic, eliminating
    duplication between the MCP server and Agent REPL.
    
    New classes:
    - ToolRegistry: central registry of 50 shared tool descriptors with
      ToolDescriptor, ToolContext, and ToolExecutor functional interface
    - ToolDescriptor: builder-style descriptor bundling metadata + executor
    - ToolContext: shared execution context (RuntimeHelper + CamelCatalog)
    - ToolExecutionException: unchecked exception for tool failures
    
    Refactored consumers:
    - AskTools (Agent REPL): delegates to ToolRegistry for shared tools,
      keeps CLI/file tools locally (995 -> 478 lines)
    - RuntimeTools (MCP server): thin MCP wrappers delegating to ToolRegistry
      via delegateToRegistry() helper. Only camel_runtime_processes and
      camel_runtime_receive keep their own implementations.
    
    New tools added to ToolRegistry:
    - DevConsole: circuit breakers, startup steps, datasources, spans, metrics
    - Analysis: route analysis with anti-pattern hints, EIP stats,
      config drift detection
    - Catalog: dataformats, languages, EIP doc (previously MCP-only)
    
    New MCP prompts:
    - camel_diagnose_route: 7-step guided diagnosis workflow
    - camel_optimize_route: 6-step guided optimization workflow
    
    Co-authored-by: Claude Opus 4.6 <[email protected]>
---
 .../camel/dsl/jbang/core/commands/AskTools.java    | 673 ++------------
 .../dsl/jbang/core/commands/ai/ToolContext.java    | 121 +++
 .../dsl/jbang/core/commands/ai/ToolDescriptor.java | 102 +++
 .../core/commands/ai/ToolExecutionException.java   |  31 +
 .../dsl/jbang/core/commands/ai/ToolRegistry.java   | 972 +++++++++++++++++++++
 .../jbang/core/commands/ai/ToolRegistryTest.java   | 166 ++++
 .../jbang/core/commands/mcp/PromptDefinitions.java | 173 ++++
 .../dsl/jbang/core/commands/mcp/RuntimeTools.java  | 234 ++---
 8 files changed, 1746 insertions(+), 726 deletions(-)

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 aa4097c77503..89902c6c7766 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
@@ -27,15 +27,13 @@ import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import org.apache.camel.catalog.CamelCatalog;
-import org.apache.camel.catalog.DefaultCamelCatalog;
-import org.apache.camel.dsl.jbang.core.common.ExampleHelper;
+import org.apache.camel.dsl.jbang.core.commands.ai.ToolContext;
+import org.apache.camel.dsl.jbang.core.commands.ai.ToolDescriptor;
+import org.apache.camel.dsl.jbang.core.commands.ai.ToolRegistry;
 import org.apache.camel.dsl.jbang.core.common.Printer;
 import org.apache.camel.dsl.jbang.core.common.RuntimeHelper;
-import org.apache.camel.tooling.model.ComponentModel;
 import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.json.JsonObject;
 import org.apache.camel.util.json.Jsoner;
@@ -43,18 +41,24 @@ import org.apache.camel.util.json.Jsoner;
 /**
  * Shared tool definitions and execution logic for the Camel AI assistant. 
Used by both the {@code camel ask} CLI
  * command and the TUI AI panel.
+ *
+ * <p>
+ * Delegates shared tool execution (process discovery, runtime inspection, 
catalog, examples) to
+ * {@link ToolRegistry}/{@link ToolContext}. CLI-specific and file-system 
tools remain here because they depend on
+ * {@link CamelJBangMain} and direct filesystem access.
  */
 public class AskTools {
 
-    private static final String NO_PROCESS
-            = "No running Camel process connected. Start one with: camel run 
<file>";
-
     private long targetPid;
-    private CamelCatalog catalog;
+    private final ToolContext ctx;
     private volatile List<JsonObject> commandMetadataCache;
 
     public AskTools(long targetPid) {
         this.targetPid = targetPid;
+        this.ctx = new ToolContext();
+        if (targetPid >= 0) {
+            ctx.selectProcess(targetPid);
+        }
     }
 
     public long getTargetPid() {
@@ -63,6 +67,9 @@ public class AskTools {
 
     public void setTargetPid(long targetPid) {
         this.targetPid = targetPid;
+        if (targetPid >= 0) {
+            ctx.selectProcess(targetPid);
+        }
     }
 
     // ---- Tool definitions ----
@@ -70,197 +77,22 @@ public class AskTools {
     public List<LlmClient.ToolDef> buildToolDefinitions() {
         List<LlmClient.ToolDef> tools = new ArrayList<>();
 
-        tools.add(new LlmClient.ToolDef(
-                "list_processes",
-                "List all running Camel processes with their PID and name. Use 
this to discover available processes before selecting one.",
-                emptyParams()));
-        tools.add(new LlmClient.ToolDef(
-                "select_process",
-                "Select a running Camel process by name or PID to inspect. 
Required when multiple processes are running. After selection, all runtime 
tools (get_routes, get_context, etc.) will target this process.",
-                objectParams(Map.of(
-                        "name", stringProp("Name or PID of the Camel process 
to connect to")))));
-
-        tools.add(new LlmClient.ToolDef(
-                "get_context",
-                "Get Camel context info: name, version, state, uptime, route 
count, exchange statistics.",
-                emptyParams()));
-        tools.add(new LlmClient.ToolDef(
-                "get_routes",
-                "List all routes with their state, uptime, messages processed, 
last error, and throughput.",
-                emptyParams()));
-        tools.add(new LlmClient.ToolDef(
-                "get_health",
-                "Get health check status for the Camel application.",
-                emptyParams()));
-        tools.add(new LlmClient.ToolDef(
-                "get_endpoints",
-                "List all endpoints registered in the Camel context with URIs 
and usage stats.",
-                emptyParams()));
-        tools.add(new LlmClient.ToolDef(
-                "get_inflight",
-                "Show currently in-flight exchanges (messages being 
processed).",
-                emptyParams()));
-        tools.add(new LlmClient.ToolDef(
-                "get_blocked",
-                "Show blocked exchanges that are stuck or waiting.",
-                emptyParams()));
-        tools.add(new LlmClient.ToolDef(
-                "get_consumers",
-                "Show consumer statistics (polling and event-driven 
consumers).",
-                emptyParams()));
-        tools.add(new LlmClient.ToolDef(
-                "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",
-                "Get the source code of routes. Use filter to limit by 
filename (supports wildcards).",
-                objectParams(Map.of(
-                        "filter", stringProp("Filter source files by name 
(supports wildcards). Use * for all.")))));
-        tools.add(new LlmClient.ToolDef(
-                "get_route_dump",
-                "Dump route definitions in XML or YAML format.",
-                objectParams(Map.of(
-                        "routeId", stringProp("Route ID to dump (use * for all 
routes)"),
-                        "format", stringProp("Output format: xml or yaml 
(default: yaml)")))));
-        tools.add(new LlmClient.ToolDef(
-                "get_route_structure",
-                "Show the route structure as a tree of processors.",
-                objectParams(Map.of(
-                        "routeId", stringProp("Route ID to inspect (use * for 
all routes)")))));
-        tools.add(new LlmClient.ToolDef(
-                "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_sql_trace",
-                "Get traced SQL query executions flowing through camel-sql and 
camel-jdbc components. "
-                                 + "Returns per-query timing, row counts, 
category (SELECT/INSERT/UPDATE/DELETE), "
-                                 + "route ID, and failure status. Includes 
summary statistics: total queries, "
-                                 + "average time, slowest time, slow count 
(>=100ms), and failed count. "
-                                 + "Use to identify slow queries, fastest 
queries, most frequent queries, "
-                                 + "and failed SQL executions.",
-                emptyParams()));
-        tools.add(new LlmClient.ToolDef(
-                "execute_sql",
-                "Execute a SQL query against a DataSource in the running Camel 
application. "
-                               + "Returns structured JSON with columns, rows, 
and metadata for SELECT queries, "
-                               + "or an update count for 
INSERT/UPDATE/DELETE.",
-                objectParams(Map.of(
-                        "query", stringProp("The SQL query to execute"),
-                        "datasource", stringProp("Name of the DataSource bean 
(auto-detected if only one exists)"),
-                        "maxRows", stringProp("Maximum number of rows to 
return (default: 100)")))));
-        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.",
-                objectParams(Map.of(
-                        "routeId", stringProp("The ID of the route to 
stop")))));
-        tools.add(new LlmClient.ToolDef(
-                "start_route",
-                "Start a stopped route.",
-                objectParams(Map.of(
-                        "routeId", stringProp("The ID of the route to 
start")))));
-        tools.add(new LlmClient.ToolDef(
-                "suspend_route",
-                "Suspend a route (pauses the consumer but keeps the route 
loaded).",
-                objectParams(Map.of(
-                        "routeId", stringProp("The ID of the route to 
suspend")))));
-        tools.add(new LlmClient.ToolDef(
-                "resume_route",
-                "Resume a suspended route.",
-                objectParams(Map.of(
-                        "routeId", stringProp("The ID of the route to 
resume")))));
-
-        tools.add(new LlmClient.ToolDef(
-                "stop_application",
-                "Gracefully stop the Camel application. The application will 
finish processing in-flight exchanges and shut down cleanly. Use this instead 
of kill.",
-                emptyParams()));
-
-        tools.add(new LlmClient.ToolDef(
-                "catalog_components",
-                "Search the Camel component catalog by name or label. Returns 
component name, title, description, and labels.",
-                objectParams(Map.of(
-                        "filter", stringProp("Filter by name, title, or 
description (case-insensitive substring)"),
-                        "label", stringProp("Filter by category label (e.g., 
cloud, messaging, database, file)")))));
-        tools.add(new LlmClient.ToolDef(
-                "catalog_component_doc",
-                "Get detailed documentation for a Camel component: URI syntax 
and endpoint options.",
-                objectParams(Map.of(
-                        "component", stringProp("Component name (e.g., kafka, 
http, file, timer)")))));
-        tools.add(new LlmClient.ToolDef(
-                "catalog_eips",
-                "Search EIPs (Enterprise Integration Patterns) like split, 
aggregate, filter, choice, multicast.",
-                objectParams(Map.of(
-                        "filter", stringProp("Filter by name, title, or 
description (case-insensitive substring)")))));
-
-        tools.add(new LlmClient.ToolDef(
-                "list_examples",
-                "List available Camel CLI examples. Returns name, title, 
description, difficulty level, and tags.",
-                objectParams(Map.of(
-                        "filter", stringProp("Filter by name, description, or 
tag (case-insensitive)"),
-                        "level", stringProp("Filter by difficulty: beginner, 
intermediate, or advanced")))));
-        tools.add(new LlmClient.ToolDef(
-                "get_example_file",
-                "Get the content of a file from a bundled Camel CLI example. 
Use list_examples first to find available examples.",
-                objectParams(Map.of(
-                        "example", stringProp("Example name (e.g., timer-log, 
rest-api, circuit-breaker)"),
-                        "file", stringProp("File name within the example 
(e.g., route.camel.yaml)")))));
+        // Build definitions from the shared registry
+        for (ToolDescriptor td : ToolRegistry.allTools()) {
+            JsonObject params;
+            if (td.params().isEmpty()) {
+                params = emptyParams();
+            } else {
+                Map<String, JsonObject> props = new LinkedHashMap<>();
+                for (ToolDescriptor.Param p : td.params()) {
+                    props.put(p.name(), stringProp(p.description()));
+                }
+                params = objectParams(props);
+            }
+            tools.add(new LlmClient.ToolDef(td.name(), td.description(), 
params));
+        }
 
+        // CLI tools (AskTools-specific, depend on CamelJBangMain)
         tools.add(new LlmClient.ToolDef(
                 "cli_list_commands",
                 "List available Camel CLI commands. Returns command names and 
descriptions. Use filter to narrow results.",
@@ -278,6 +110,7 @@ public class AskTools {
                         "command", stringProp(
                                 "The full command line to execute (e.g., 'get 
error --diagram', 'catalog component --filter=kafka')")))));
 
+        // File tools (AskTools-specific, depend on filesystem access)
         tools.add(new LlmClient.ToolDef(
                 "list_files",
                 "List files in a directory (up to 2 levels deep). Defaults to 
current working directory.",
@@ -302,62 +135,31 @@ public class AskTools {
 
     public String executeTool(String name, JsonObject args) {
         try {
+            // Try shared registry first
+            ToolDescriptor td = ToolRegistry.findTool(name);
+            if (td != null) {
+                // Convert JsonObject args to Map<String, String> for the 
registry
+                Map<String, String> argMap = new LinkedHashMap<>();
+                if (args != null) {
+                    for (String key : args.keySet()) {
+                        Object val = args.get(key);
+                        if (val != null) {
+                            argMap.put(key, val.toString());
+                        }
+                    }
+                }
+
+                // Special handling: select_process must also update our 
targetPid
+                Object result = ToolRegistry.execute(name, ctx, argMap);
+                if ("select_process".equals(name) && ctx.hasProcess()) {
+                    this.targetPid = ctx.pid();
+                }
+
+                return result != null ? result.toString() : "";
+            }
+
+            // Fall through to CLI and file tools
             return switch (name) {
-                case "list_processes" -> executeListProcesses();
-                case "select_process" -> executeSelectProcess(args);
-                case "get_context" ->
-                    targetPid < 0 ? NO_PROCESS : 
RuntimeHelper.readStatusSection(targetPid, "context");
-                case "get_routes" ->
-                    targetPid < 0 ? NO_PROCESS : 
RuntimeHelper.readStatusSection(targetPid, "routes");
-                case "get_health" ->
-                    targetPid < 0 ? NO_PROCESS : 
RuntimeHelper.readStatusSection(targetPid, "healthChecks");
-                case "get_endpoints" ->
-                    targetPid < 0 ? NO_PROCESS : 
RuntimeHelper.readStatusSection(targetPid, "endpoints");
-                case "get_inflight" ->
-                    targetPid < 0 ? NO_PROCESS : 
RuntimeHelper.readStatusSection(targetPid, "inflight");
-                case "get_blocked" ->
-                    targetPid < 0 ? NO_PROCESS : 
RuntimeHelper.readStatusSection(targetPid, "blocked");
-                case "get_consumers" ->
-                    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_sql_trace" ->
-                    targetPid < 0 ? NO_PROCESS : 
RuntimeHelper.readStatusSection(targetPid, "sqlTrace");
-                case "execute_sql" -> targetPid < 0 ? NO_PROCESS : 
executeSQL(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");
-                case "resume_route" -> targetPid < 0 ? NO_PROCESS : 
executeRouteCommand(args, "resume");
-                case "stop_application" -> targetPid < 0 ? NO_PROCESS : 
RuntimeHelper.stopApplication(targetPid);
-                case "catalog_components" -> executeCatalogComponents(args);
-                case "catalog_component_doc" -> 
executeCatalogComponentDoc(args);
-                case "catalog_eips" -> executeCatalogEips(args);
-                case "list_examples" -> executeListExamples(args);
-                case "get_example_file" -> executeGetExampleFile(args);
                 case "cli_list_commands" -> executeCliListCommands(args);
                 case "cli_command_help" -> executeCliCommandHelp(args);
                 case "cli_exec" -> executeCliExec(args);
@@ -411,349 +213,7 @@ public class AskTools {
         return sb.toString();
     }
 
-    // ---- Runtime tool execution ----
-
-    private String executeListProcesses() {
-        List<RuntimeHelper.ProcessInfo> processes = 
RuntimeHelper.discoverProcesses();
-        if (processes.isEmpty()) {
-            return "No running Camel processes found. Start one with: camel 
run <file>";
-        }
-        JsonObject response = new JsonObject();
-        response.put("count", processes.size());
-        List<JsonObject> list = new ArrayList<>();
-        for (RuntimeHelper.ProcessInfo p : processes) {
-            JsonObject entry = new JsonObject();
-            entry.put("pid", p.pid());
-            entry.put("name", p.name());
-            entry.put("selected", p.pid() == targetPid);
-            list.add(entry);
-        }
-        response.put("processes", list);
-        if (targetPid < 0) {
-            response.put("hint", "No process selected. Use select_process to 
connect to one.");
-        }
-        return response.toJson();
-    }
-
-    private String executeSelectProcess(JsonObject args) {
-        String name = args.getString("name");
-        if (name == null || name.isBlank()) {
-            return "Error: name or PID is required";
-        }
-        RuntimeHelper.ProcessInfo found = RuntimeHelper.findProcess(name);
-        if (found == null) {
-            List<RuntimeHelper.ProcessInfo> processes = 
RuntimeHelper.discoverProcesses();
-            if (processes.isEmpty()) {
-                return "No running Camel processes found.";
-            }
-            StringBuilder sb = new StringBuilder("No process found matching: " 
+ name + ". Available:\n");
-            processes.forEach(p -> sb.append("  ").append(p.name()).append(" 
(PID ").append(p.pid()).append(")\n"));
-            return sb.toString();
-        }
-        targetPid = found.pid();
-        return "Connected to " + found.name() + " (PID " + found.pid() + "). 
Runtime tools are now active.";
-    }
-
-    private String executeRouteSource(JsonObject args) {
-        String filter = args.getString("filter");
-        return RuntimeHelper.executeAction(targetPid, "source",
-                root -> root.put("filter", filter != null ? filter : "*"));
-    }
-
-    private String executeRouteDump(JsonObject args) {
-        String routeId = args.getString("routeId");
-        String format = args.getString("format");
-        return RuntimeHelper.executeAction(targetPid, "route-dump", root -> {
-            root.put("id", routeId != null ? routeId : "*");
-            root.put("format", format != null ? format : "yaml");
-        });
-    }
-
-    private String executeRouteStructure(JsonObject args) {
-        String routeId = args.getString("routeId");
-        return RuntimeHelper.executeAction(targetPid, "route-structure",
-                root -> root.put("id", routeId != null ? routeId : "*"));
-    }
-
-    private String executeTraceControl(JsonObject args) {
-        String action = args.getString("action");
-        if (action == null) {
-            return "Error: action is required (enable, disable, dump)";
-        }
-        return RuntimeHelper.executeAction(targetPid, "trace", root -> {
-            switch (action.toLowerCase()) {
-                case "enable" -> root.put("enabled", "true");
-                case "disable" -> root.put("enabled", "false");
-                case "dump" -> root.put("dump", "true");
-                default -> root.put("enabled", action);
-            }
-        });
-    }
-
-    private String executeRouteCommand(JsonObject args, String command) {
-        String routeId = args.getString("routeId");
-        if (routeId == null || routeId.isBlank()) {
-            return "Error: routeId is required";
-        }
-        return RuntimeHelper.executeAction(targetPid, "route", root -> {
-            root.put("id", routeId);
-            root.put("command", command);
-        });
-    }
-
-    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);
-        });
-    }
-
-    private String executeSQL(JsonObject args) {
-        String sql = args.getString("query");
-        if (sql == null || sql.isBlank()) {
-            return "Error: 'query' parameter is required";
-        }
-        String datasource = args.getString("datasource");
-        int maxRows = 100;
-        String maxRowsStr = args.getString("maxRows");
-        if (maxRowsStr != null) {
-            try {
-                maxRows = Integer.parseInt(maxRowsStr);
-            } catch (NumberFormatException e) {
-                // use default
-            }
-        }
-        JsonObject result = RuntimeHelper.executeSqlQuery(targetPid, sql, 
datasource, maxRows, 30);
-        return Jsoner.serialize(result);
-    }
-
-    // ---- Catalog tools ----
-
-    private String executeCatalogComponents(JsonObject args) {
-        String filter = args.getString("filter");
-        String label = args.getString("label");
-        CamelCatalog cat = getCatalog();
-
-        List<JsonObject> results = cat.findComponentNames().stream()
-                .map(cat::componentModel)
-                .filter(m -> m != null)
-                .filter(m -> matchesFilter(m.getScheme(), m.getTitle(), 
m.getDescription(), filter))
-                .filter(m -> label == null || label.isBlank()
-                        || (m.getLabel() != null && 
m.getLabel().toLowerCase().contains(label.toLowerCase())))
-                .limit(20)
-                .map(m -> {
-                    JsonObject jo = new JsonObject();
-                    jo.put("name", m.getScheme());
-                    jo.put("title", m.getTitle());
-                    jo.put("description", m.getDescription());
-                    jo.put("label", m.getLabel());
-                    return jo;
-                })
-                .collect(Collectors.toList());
-
-        JsonObject response = new JsonObject();
-        response.put("count", results.size());
-        response.put("components", results);
-        return response.toJson();
-    }
-
-    private String executeCatalogComponentDoc(JsonObject args) {
-        String component = args.getString("component");
-        if (component == null || component.isBlank()) {
-            return "Error: component name is required";
-        }
-        CamelCatalog cat = getCatalog();
-        ComponentModel model = cat.componentModel(component);
-        if (model == null) {
-            return "Component not found: " + component;
-        }
-
-        JsonObject response = new JsonObject();
-        response.put("name", model.getScheme());
-        response.put("title", model.getTitle());
-        response.put("description", model.getDescription());
-        response.put("syntax", model.getSyntax());
-        response.put("consumerOnly", model.isConsumerOnly());
-        response.put("producerOnly", model.isProducerOnly());
-
-        List<JsonObject> options = new ArrayList<>();
-        if (model.getEndpointOptions() != null) {
-            model.getEndpointOptions().stream()
-                    .filter(opt -> !opt.isDeprecated())
-                    .forEach(opt -> {
-                        JsonObject jo = new JsonObject();
-                        jo.put("name", opt.getName());
-                        jo.put("description", opt.getDescription());
-                        jo.put("type", opt.getType());
-                        jo.put("required", opt.isRequired());
-                        if (opt.getDefaultValue() != null) {
-                            jo.put("defaultValue", 
opt.getDefaultValue().toString());
-                        }
-                        options.add(jo);
-                    });
-        }
-        response.put("options", options);
-        return response.toJson();
-    }
-
-    private String executeCatalogEips(JsonObject args) {
-        String filter = args.getString("filter");
-        CamelCatalog cat = getCatalog();
-
-        List<JsonObject> results = cat.findModelNames().stream()
-                .map(cat::eipModel)
-                .filter(m -> m != null)
-                .filter(m -> matchesFilter(m.getName(), m.getTitle(), 
m.getDescription(), filter))
-                .limit(20)
-                .map(m -> {
-                    JsonObject jo = new JsonObject();
-                    jo.put("name", m.getName());
-                    jo.put("title", m.getTitle());
-                    jo.put("description", m.getDescription());
-                    jo.put("label", m.getLabel());
-                    return jo;
-                })
-                .collect(Collectors.toList());
-
-        JsonObject response = new JsonObject();
-        response.put("count", results.size());
-        response.put("eips", results);
-        return response.toJson();
-    }
-
-    // ---- Example tools ----
-
-    @SuppressWarnings("unchecked")
-    private String executeListExamples(JsonObject args) {
-        String filter = args.getString("filter");
-        String level = args.getString("level");
-
-        List<JsonObject> catalog2 = ExampleHelper.loadCatalog();
-        List<JsonObject> filtered = ExampleHelper.filterExamples(catalog2, 
filter);
-
-        List<JsonObject> results = new ArrayList<>();
-        for (JsonObject entry : filtered) {
-            if (level != null && !level.isBlank()) {
-                String entryLevel = entry.getString("level");
-                if (entryLevel == null || !entryLevel.equalsIgnoreCase(level)) 
{
-                    continue;
-                }
-            }
-            JsonObject jo = new JsonObject();
-            jo.put("name", entry.getString("name"));
-            jo.put("title", entry.getString("title"));
-            jo.put("description", entry.getString("description"));
-            jo.put("level", entry.getString("level"));
-            jo.put("tags", entry.get("tags"));
-            jo.put("bundled", ExampleHelper.isBundled(entry));
-            jo.put("files", ExampleHelper.getFiles(entry));
-            results.add(jo);
-
-            if (results.size() >= 20) {
-                break;
-            }
-        }
-
-        JsonObject response = new JsonObject();
-        response.put("count", results.size());
-        response.put("examples", results);
-        return response.toJson();
-    }
-
-    private String executeGetExampleFile(JsonObject args) {
-        String example = args.getString("example");
-        String file = args.getString("file");
-        if (example == null || example.isBlank()) {
-            return "Error: example name is required";
-        }
-        if (file == null || file.isBlank()) {
-            return "Error: file name is required";
-        }
-
-        List<JsonObject> catalog2 = ExampleHelper.loadCatalog();
-        JsonObject entry = ExampleHelper.findExample(catalog2, example);
-        if (entry == null) {
-            return "Example not found: " + example;
-        }
-
-        List<String> files = ExampleHelper.getFiles(entry);
-        if (!files.contains(file)) {
-            return "File '" + file + "' not found in example '" + example + 
"'. Available files: " + files;
-        }
-
-        if (ExampleHelper.isBundled(entry)) {
-            String resourcePath = "examples/" + example + "/" + file;
-            try (InputStream is = 
ExampleHelper.class.getClassLoader().getResourceAsStream(resourcePath)) {
-                if (is != null) {
-                    return IOHelper.loadText(is);
-                }
-            } catch (Exception e) {
-                return "Error reading file: " + e.getMessage();
-            }
-            return "Could not read bundled file: " + resourcePath;
-        } else {
-            return "This example is not bundled. View it on GitHub: " + 
ExampleHelper.getGithubUrl(entry) + "/" + file;
-        }
-    }
-
-    // ---- CLI tools ----
+    // ---- CLI tools (AskTools-specific) ----
 
     @SuppressWarnings("unchecked")
     private List<JsonObject> loadCommandMetadata() {
@@ -914,7 +374,7 @@ public class AskTools {
         }
     }
 
-    // ---- File tools ----
+    // ---- File tools (AskTools-specific) ----
 
     private String executeListFiles(JsonObject args) throws IOException {
         String pathStr = args.getString("path");
@@ -988,13 +448,6 @@ public class AskTools {
         return "File written: " + cwd.relativize(filePath);
     }
 
-    private CamelCatalog getCatalog() {
-        if (catalog == null) {
-            catalog = new DefaultCamelCatalog();
-        }
-        return catalog;
-    }
-
     // ---- JSON schema helpers ----
 
     public static JsonObject emptyParams() {
@@ -1022,14 +475,4 @@ public class AskTools {
         prop.put("description", description);
         return prop;
     }
-
-    static boolean matchesFilter(String name, String title, String 
description, String filter) {
-        if (filter == null || filter.isBlank()) {
-            return true;
-        }
-        String lf = filter.toLowerCase();
-        return (name != null && name.toLowerCase().contains(lf))
-                || (title != null && title.toLowerCase().contains(lf))
-                || (description != null && 
description.toLowerCase().contains(lf));
-    }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolContext.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolContext.java
new file mode 100644
index 000000000000..85105260eb75
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolContext.java
@@ -0,0 +1,121 @@
+/*
+ * 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.ai;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.dsl.jbang.core.common.RuntimeHelper;
+import org.apache.camel.util.json.JsonObject;
+
+/**
+ * Shared execution context for AI tools. Wraps {@link RuntimeHelper} (process 
IPC) and {@link CamelCatalog} (component
+ * metadata). This class intentionally has no dependency on MCP or Quarkus so 
it can be used by both the Agent REPL and
+ * the MCP server.
+ */
+public class ToolContext {
+
+    private volatile long pid = -1;
+    private volatile CamelCatalog catalog;
+
+    public long pid() {
+        return pid;
+    }
+
+    public void selectProcess(long pid) {
+        this.pid = pid;
+    }
+
+    public boolean hasProcess() {
+        return pid >= 0;
+    }
+
+    /**
+     * Returns the shared {@link CamelCatalog} instance, creating it lazily on 
first access. Thread-safe: concurrent
+     * callers may race but will all see a fully constructed catalog.
+     */
+    public CamelCatalog catalog() {
+        CamelCatalog result = catalog;
+        if (result == null) {
+            synchronized (this) {
+                result = catalog;
+                if (result == null) {
+                    result = new DefaultCamelCatalog();
+                    catalog = result;
+                }
+            }
+        }
+        return result;
+    }
+
+    public String readStatus(String section) {
+        requireProcess();
+        return RuntimeHelper.readStatusSection(pid, section);
+    }
+
+    public JsonObject readFullStatus() {
+        requireProcess();
+        return RuntimeHelper.readStatus(pid);
+    }
+
+    public String executeAction(String action, Consumer<JsonObject> configure) 
{
+        requireProcess();
+        return RuntimeHelper.executeAction(pid, action, configure);
+    }
+
+    public JsonObject readErrorFile() {
+        requireProcess();
+        return RuntimeHelper.readErrorFile(pid);
+    }
+
+    public JsonObject readHistoryFile() {
+        requireProcess();
+        return RuntimeHelper.readHistoryFile(pid);
+    }
+
+    public String stopApplication() {
+        requireProcess();
+        return RuntimeHelper.stopApplication(pid);
+    }
+
+    public JsonObject sendMessage(String endpoint, String body, String 
headers) {
+        requireProcess();
+        return RuntimeHelper.sendMessage(pid, endpoint, body, headers);
+    }
+
+    public List<RuntimeHelper.ProcessInfo> discoverProcesses() {
+        return RuntimeHelper.discoverProcesses();
+    }
+
+    public RuntimeHelper.ProcessInfo findProcess(String name) {
+        return RuntimeHelper.findProcess(name);
+    }
+
+    public JsonObject executeSqlQuery(String sql, String datasource, int 
maxRows, int queryTimeout) {
+        requireProcess();
+        return RuntimeHelper.executeSqlQuery(pid, sql, datasource, maxRows, 
queryTimeout);
+    }
+
+    public void requireProcess() {
+        if (pid < 0) {
+            throw new ToolExecutionException(
+                    "No running Camel process connected. Start one with: camel 
run <file>");
+        }
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolDescriptor.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolDescriptor.java
new file mode 100644
index 000000000000..6f24714f4a21
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolDescriptor.java
@@ -0,0 +1,102 @@
+/*
+ * 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.ai;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Describes a single AI tool: its name, description, parameters, and 
execution logic. Used by both the Agent REPL and
+ * the MCP server to avoid duplicating tool definitions.
+ */
+public class ToolDescriptor {
+
+    private final String name;
+    private final String description;
+    private final List<Param> params;
+    private ToolExecutor executor;
+    private boolean readOnly = true;
+    private boolean destructive = false;
+
+    public record Param(String name, String type, String description, boolean 
required) {
+    }
+
+    @FunctionalInterface
+    public interface ToolExecutor {
+        Object execute(ToolContext ctx, Map<String, String> args) throws 
ToolExecutionException;
+    }
+
+    private ToolDescriptor(String name, String description) {
+        this.name = name;
+        this.description = description;
+        this.params = new ArrayList<>();
+    }
+
+    public static ToolDescriptor tool(String name, String description) {
+        return new ToolDescriptor(name, description);
+    }
+
+    // Builder methods
+
+    public ToolDescriptor param(String name, String type, String description, 
boolean required) {
+        params.add(new Param(name, type, description, required));
+        return this;
+    }
+
+    public ToolDescriptor readOnly(boolean v) {
+        readOnly = v;
+        return this;
+    }
+
+    public ToolDescriptor destructive(boolean v) {
+        destructive = v;
+        return this;
+    }
+
+    public ToolDescriptor executor(ToolExecutor exec) {
+        this.executor = exec;
+        return this;
+    }
+
+    // Getters
+
+    public String name() {
+        return name;
+    }
+
+    public String description() {
+        return description;
+    }
+
+    public List<Param> params() {
+        return Collections.unmodifiableList(params);
+    }
+
+    public boolean isReadOnly() {
+        return readOnly;
+    }
+
+    public boolean isDestructive() {
+        return destructive;
+    }
+
+    public ToolExecutor executor() {
+        return executor;
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolExecutionException.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolExecutionException.java
new file mode 100644
index 000000000000..6ffac7aad6ff
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolExecutionException.java
@@ -0,0 +1,31 @@
+/*
+ * 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.ai;
+
+/**
+ * Unchecked exception thrown when a tool execution fails.
+ */
+public class ToolExecutionException extends RuntimeException {
+
+    public ToolExecutionException(String message) {
+        super(message);
+    }
+
+    public ToolExecutionException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistry.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistry.java
new file mode 100644
index 000000000000..53aa43a55795
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistry.java
@@ -0,0 +1,972 @@
+/*
+ * 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.ai;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.dsl.jbang.core.common.ExampleHelper;
+import org.apache.camel.dsl.jbang.core.common.RuntimeHelper;
+import org.apache.camel.tooling.model.ComponentModel;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+
+import static org.apache.camel.dsl.jbang.core.commands.ai.ToolDescriptor.tool;
+
+/**
+ * Central registry of all shared AI tool descriptors. Tools registered here 
are available to both the Agent REPL
+ * ({@code AskTools}) and the MCP server. CLI-specific and file-system tools 
that depend on {@code CamelJBangMain} or
+ * direct filesystem access remain in {@code AskTools}.
+ */
+public final class ToolRegistry {
+
+    private static final List<ToolDescriptor> TOOLS = new ArrayList<>();
+    private static final Map<String, ToolDescriptor> BY_NAME = new 
LinkedHashMap<>();
+
+    static {
+        registerProcessTools();
+        registerRuntimeStatusTools();
+        registerRuntimeActionTools();
+        registerDevConsoleTools();
+        registerAnalysisTools();
+        registerCatalogTools();
+        registerExampleTools();
+    }
+
+    private ToolRegistry() {
+    }
+
+    public static List<ToolDescriptor> allTools() {
+        return Collections.unmodifiableList(TOOLS);
+    }
+
+    public static ToolDescriptor findTool(String name) {
+        return BY_NAME.get(name);
+    }
+
+    public static Object execute(String name, ToolContext ctx, Map<String, 
String> args) {
+        ToolDescriptor desc = BY_NAME.get(name);
+        if (desc == null) {
+            throw new ToolExecutionException("Unknown tool: " + name);
+        }
+        return desc.executor().execute(ctx, args);
+    }
+
+    private static void register(ToolDescriptor descriptor) {
+        TOOLS.add(descriptor);
+        BY_NAME.put(descriptor.name(), descriptor);
+    }
+
+    // ---- Process discovery and selection ----
+
+    private static void registerProcessTools() {
+        register(tool("list_processes",
+                "List all running Camel processes with their PID and name. Use 
this to discover available processes before selecting one.")
+                .executor((ctx, args) -> {
+                    List<RuntimeHelper.ProcessInfo> processes = 
ctx.discoverProcesses();
+                    if (processes.isEmpty()) {
+                        return "No running Camel processes found. Start one 
with: camel run <file>";
+                    }
+                    JsonObject response = new JsonObject();
+                    response.put("count", processes.size());
+                    List<JsonObject> list = new ArrayList<>();
+                    for (RuntimeHelper.ProcessInfo p : processes) {
+                        JsonObject entry = new JsonObject();
+                        entry.put("pid", p.pid());
+                        entry.put("name", p.name());
+                        entry.put("selected", p.pid() == ctx.pid());
+                        list.add(entry);
+                    }
+                    response.put("processes", list);
+                    if (!ctx.hasProcess()) {
+                        response.put("hint", "No process selected. Use 
select_process to connect to one.");
+                    }
+                    return response.toJson();
+                }));
+
+        register(tool("select_process",
+                "Select a running Camel process by name or PID to inspect. 
Required when multiple processes are running.")
+                .param("name", "string", "Name or PID of the Camel process to 
connect to", true)
+                .executor((ctx, args) -> {
+                    String name = args.get("name");
+                    if (name == null || name.isBlank()) {
+                        throw new ToolExecutionException("name or PID is 
required");
+                    }
+                    RuntimeHelper.ProcessInfo found = ctx.findProcess(name);
+                    if (found == null) {
+                        List<RuntimeHelper.ProcessInfo> processes = 
ctx.discoverProcesses();
+                        if (processes.isEmpty()) {
+                            return "No running Camel processes found.";
+                        }
+                        StringBuilder sb
+                                = new StringBuilder("No process found 
matching: " + name + ". Available:\n");
+                        processes.forEach(
+                                p -> sb.append("  ").append(p.name()).append(" 
(PID ").append(p.pid()).append(")\n"));
+                        return sb.toString();
+                    }
+                    ctx.selectProcess(found.pid());
+                    return "Connected to " + found.name() + " (PID " + 
found.pid()
+                           + "). Runtime tools are now active.";
+                }));
+    }
+
+    // ---- Runtime status (simple read-section) tools ----
+
+    private static void registerRuntimeStatusTools() {
+        registerStatusTool("get_context", "context",
+                "Get Camel context info: name, version, state, uptime, route 
count, exchange statistics.");
+        registerStatusTool("get_routes", "routes",
+                "List all routes with their state, uptime, messages processed, 
last error, and throughput.");
+        registerStatusTool("get_health", "healthChecks",
+                "Get health check status for the Camel application.");
+        registerStatusTool("get_endpoints", "endpoints",
+                "List all endpoints registered in the Camel context with URIs 
and usage stats.");
+        registerStatusTool("get_inflight", "inflight",
+                "Show currently in-flight exchanges (messages being 
processed).");
+        registerStatusTool("get_blocked", "blocked",
+                "Show blocked exchanges that are stuck or waiting.");
+        registerStatusTool("get_consumers", "consumers",
+                "Show consumer statistics (polling and event-driven 
consumers).");
+        registerStatusTool("get_properties", "properties",
+                "Show configuration properties of the running Camel 
application.");
+        // get_memory combines memory, gc, and threads sections for a complete 
JVM resource view
+        register(tool("get_memory",
+                "Show JVM memory usage (heap/non-heap), garbage collection 
stats, and thread counts.")
+                .executor((ctx, args) -> {
+                    JsonObject root = ctx.readFullStatus();
+                    if (root == null) {
+                        return "{}";
+                    }
+                    JsonObject result = new JsonObject();
+                    if (root.containsKey("memory")) {
+                        result.put("memory", root.get("memory"));
+                    }
+                    if (root.containsKey("gc")) {
+                        result.put("gc", root.get("gc"));
+                    }
+                    if (root.containsKey("threads")) {
+                        result.put("threads", root.get("threads"));
+                    }
+                    return result.toJson();
+                }));
+        registerStatusTool("get_variables", "variables",
+                "Show exchange variables in the Camel context.");
+        registerStatusTool("get_services", "services",
+                "Show services registered in the Camel service registry.");
+        registerStatusTool("get_sql_trace", "sqlTrace",
+                "Get traced SQL query executions flowing through camel-sql and 
camel-jdbc components. "
+                                                        + "Returns per-query 
timing, row counts, category (SELECT/INSERT/UPDATE/DELETE), "
+                                                        + "route ID, and 
failure status. Includes summary statistics.");
+    }
+
+    private static void registerStatusTool(String name, String section, String 
description) {
+        register(tool(name, description)
+                .executor((ctx, args) -> ctx.readStatus(section)));
+    }
+
+    // ---- Runtime action tools (with parameters) ----
+
+    private static void registerRuntimeActionTools() {
+        register(tool("get_errors",
+                "Get captured routing errors from the running Camel 
application.")
+                .executor((ctx, args) -> {
+                    JsonObject errors = ctx.readErrorFile();
+                    return errors != null ? errors.toJson() : "No errors 
captured.";
+                }));
+
+        register(tool("get_history",
+                "Get the message history trace of the last completed 
exchange.")
+                .executor((ctx, args) -> {
+                    JsonObject history = ctx.readHistoryFile();
+                    return history != null ? history.toJson() : "No message 
history available.";
+                }));
+
+        register(tool("get_route_source",
+                "Get the source code of routes. Use filter to limit by 
filename (supports wildcards).")
+                .param("filter", "string",
+                        "Filter source files by name (supports wildcards). Use 
* for all.", false)
+                .executor((ctx, args) -> {
+                    String filter = args.get("filter");
+                    return ctx.executeAction("source",
+                            root -> root.put("filter", filter != null ? filter 
: "*"));
+                }));
+
+        register(tool("get_route_dump",
+                "Dump route definitions in XML or YAML format.")
+                .param("routeId", "string", "Route ID to dump (use * for all 
routes)", false)
+                .param("format", "string", "Output format: xml or yaml 
(default: yaml)", false)
+                .executor((ctx, args) -> {
+                    String routeId = args.get("routeId");
+                    String format = args.get("format");
+                    return ctx.executeAction("route-dump", root -> {
+                        root.put("id", routeId != null ? routeId : "*");
+                        root.put("format", format != null ? format : "yaml");
+                    });
+                }));
+
+        register(tool("get_route_structure",
+                "Show the route structure as a tree of processors.")
+                .param("routeId", "string", "Route ID to inspect (use * for 
all routes)", false)
+                .executor((ctx, args) -> {
+                    String routeId = args.get("routeId");
+                    return ctx.executeAction("route-structure",
+                            root -> root.put("id", routeId != null ? routeId : 
"*"));
+                }));
+
+        register(tool("get_top_processors",
+                "Show top processor statistics: which processors are slowest 
and most active.")
+                .executor((ctx, args) -> ctx.executeAction("top-processors", 
null)));
+
+        register(tool("get_route_topology",
+                "Get the inter-route topology showing how routes connect to 
each other and to external endpoints.")
+                .param("metric", "string", "Include live metrics on nodes and 
edges (default: true)", false)
+                .param("external", "string", "Include external systems as 
nodes (default: true)", false)
+                .executor((ctx, args) -> ctx.executeAction("route-topology", 
root -> {
+                    root.put("metric", args.getOrDefault("metric", "true"));
+                    root.put("external", args.getOrDefault("external", 
"true"));
+                })));
+
+        register(tool("trace_control",
+                "Enable, disable, or dump message tracing.")
+                .param("action", "string", "Action: enable, disable, or dump", 
true)
+                .executor((ctx, args) -> {
+                    String action = args.get("action");
+                    if (action == null) {
+                        throw new ToolExecutionException("action is required 
(enable, disable, dump)");
+                    }
+                    return ctx.executeAction("trace", root -> {
+                        switch (action.toLowerCase()) {
+                            case "enable" -> root.put("enabled", "true");
+                            case "disable" -> root.put("enabled", "false");
+                            case "dump" -> root.put("dump", "true");
+                            default -> throw new ToolExecutionException(
+                                    "Unknown trace action: " + action + ". Use 
'enable', 'disable', or 'dump'.");
+                        }
+                    });
+                }));
+
+        register(tool("send_message",
+                "Send a test message to a Camel endpoint in the running 
application.")
+                .param("endpoint", "string",
+                        "Endpoint URI to send to (e.g., direct:myRoute, 
seda:queue)", true)
+                .param("body", "string", "Message body to send", false)
+                .param("headers", "string",
+                        "Message headers as key=value pairs separated by 
newlines", false)
+                .readOnly(false).destructive(false)
+                .executor((ctx, args) -> {
+                    String endpoint = args.get("endpoint");
+                    if (endpoint == null || endpoint.isBlank()) {
+                        throw new ToolExecutionException("endpoint is 
required");
+                    }
+                    return ctx.sendMessage(endpoint, args.get("body"), 
args.get("headers")).toJson();
+                }));
+
+        register(tool("eval_expression",
+                "Evaluate a Camel expression in the given language against the 
running context.")
+                .param("language", "string",
+                        "Expression language (e.g., simple, jsonpath, xpath, 
jq)", true)
+                .param("expression", "string", "Expression to evaluate", true)
+                .executor((ctx, args) -> {
+                    String language = args.get("language");
+                    String expression = args.get("expression");
+                    if (language == null || language.isBlank()) {
+                        throw new ToolExecutionException("language is 
required");
+                    }
+                    if (expression == null || expression.isBlank()) {
+                        throw new ToolExecutionException("expression is 
required");
+                    }
+                    return ctx.executeAction("eval", root -> {
+                        root.put("language", language);
+                        root.put("predicate", "false");
+                        root.put("template", Jsoner.escape(expression));
+                    });
+                }));
+
+        register(tool("browse_endpoint",
+                "Browse messages in a Camel endpoint (e.g., browse messages 
queued in a SEDA endpoint).")
+                .param("endpoint", "string", "Endpoint URI to browse (e.g., 
seda:queue)", true)
+                .param("limit", "string",
+                        "Maximum number of messages to return (default: 50)", 
false)
+                .executor((ctx, args) -> {
+                    String endpoint = args.get("endpoint");
+                    if (endpoint == null || endpoint.isBlank()) {
+                        throw new ToolExecutionException("endpoint is 
required");
+                    }
+                    int limit = 50;
+                    String limitStr = args.get("limit");
+                    if (limitStr != null && !limitStr.isBlank()) {
+                        try {
+                            limit = Integer.parseInt(limitStr);
+                        } catch (NumberFormatException e) {
+                            // use default
+                        }
+                    }
+                    int browseLimit = limit;
+                    return ctx.executeAction("browse", root -> {
+                        root.put("filter", endpoint);
+                        root.put("limit", browseLimit);
+                    });
+                }));
+
+        register(tool("get_thread_dump",
+                "Get a JVM thread dump showing thread names, states, and stack 
traces.")
+                .executor((ctx, args) -> ctx.executeAction("thread-dump", 
null)));
+
+        register(tool("get_heap_histogram",
+                "Get a class-level heap histogram showing instance counts and 
byte usage per class. "
+                                            + "Useful for diagnosing memory 
leaks and understanding which classes dominate heap usage.")
+                .executor((ctx, args) -> ctx.executeAction("heap-histogram", 
null)));
+
+        register(tool("execute_sql",
+                "Execute a SQL query against a DataSource in the running Camel 
application. "
+                                     + "Returns structured JSON with columns, 
rows, and metadata for SELECT queries, "
+                                     + "or an update count for 
INSERT/UPDATE/DELETE.")
+                .param("query", "string", "The SQL query to execute", true)
+                .param("datasource", "string",
+                        "Name of the DataSource bean (auto-detected if only 
one exists)", false)
+                .param("maxRows", "string", "Maximum number of rows to return 
(default: 100)", false)
+                .readOnly(false).destructive(true)
+                .executor((ctx, args) -> {
+                    String sql = args.get("query");
+                    if (sql == null || sql.isBlank()) {
+                        throw new ToolExecutionException("'query' parameter is 
required");
+                    }
+                    String datasource = args.get("datasource");
+                    int maxRows = 100;
+                    String maxRowsStr = args.get("maxRows");
+                    if (maxRowsStr != null && !maxRowsStr.isBlank()) {
+                        try {
+                            maxRows = Integer.parseInt(maxRowsStr);
+                        } catch (NumberFormatException e) {
+                            // use default
+                        }
+                    }
+                    JsonObject result = ctx.executeSqlQuery(sql, datasource, 
maxRows, 30);
+                    return result.toJson();
+                }));
+
+        // Route control
+        registerRouteControlTool("stop_route", "stop",
+                "Gracefully stop a route. The route will finish processing 
in-flight exchanges before stopping.");
+        registerRouteControlTool("start_route", "start", "Start a stopped 
route.");
+        registerRouteControlTool("suspend_route", "suspend",
+                "Suspend a route (pauses the consumer but keeps the route 
loaded).");
+        registerRouteControlTool("resume_route", "resume", "Resume a suspended 
route.");
+
+        register(tool("stop_application",
+                "Gracefully stop the Camel application. Finishes in-flight 
exchanges then shuts down cleanly.")
+                .readOnly(false).destructive(true)
+                .executor((ctx, args) -> ctx.stopApplication()));
+    }
+
+    private static void registerRouteControlTool(String name, String command, 
String description) {
+        register(tool(name, description)
+                .param("routeId", "string", "The ID of the route", true)
+                .readOnly(false).destructive(command.equals("stop"))
+                .executor((ctx, args) -> {
+                    String routeId = args.get("routeId");
+                    if (routeId == null || routeId.isBlank()) {
+                        throw new ToolExecutionException("routeId is 
required");
+                    }
+                    return ctx.executeAction("route", root -> {
+                        root.put("id", routeId);
+                        root.put("command", command);
+                    });
+                }));
+    }
+
+    // ---- DevConsole tools (data available in TUI, now surfaced for AI) ----
+
+    private static void registerDevConsoleTools() {
+        register(tool("get_circuit_breakers",
+                "Get circuit breaker status from the running Camel 
application. Shows state (CLOSED/OPEN/HALF_OPEN), "
+                                              + "call counts, failure rates, 
and not-permitted calls for each breaker. "
+                                              + "Supports Resilience4j and 
MicroProfile Fault Tolerance implementations.")
+                .executor((ctx, args) -> {
+                    JsonObject root = ctx.readFullStatus();
+                    if (root == null) {
+                        return "No status available.";
+                    }
+                    // Circuit breaker data can be under resilience4j, 
fault-tolerance, or circuit-breaker sections
+                    JsonArray allBreakers = new JsonArray();
+                    collectCircuitBreakers(root, "resilience4j", allBreakers);
+                    collectCircuitBreakers(root, "fault-tolerance", 
allBreakers);
+                    collectCircuitBreakers(root, "circuit-breaker", 
allBreakers);
+
+                    JsonObject response = new JsonObject();
+                    response.put("count", allBreakers.size());
+                    response.put("circuitBreakers", allBreakers);
+                    if (allBreakers.isEmpty()) {
+                        response.put("hint",
+                                "No circuit breakers found. Add 
camel-resilience4j or camel-microprofile-fault-tolerance to use circuit 
breakers.");
+                    }
+                    return response.toJson();
+                }));
+
+        register(tool("get_startup_steps",
+                "Get startup recorder steps showing component initialization 
timing. "
+                                           + "Shows each startup step with 
duration, level, and type. "
+                                           + "Useful for diagnosing slow 
application startup. "
+                                           + "Requires startup recording to be 
enabled (camel.main.startup-recorder=true).")
+                .executor((ctx, args) -> ctx.executeAction("startup-recorder", 
null)));
+
+        register(tool("get_datasources",
+                "Get datasource connection pool status. Shows active, idle, 
and total connections, "
+                                         + "max pool size, and waiting threads 
for each datasource. "
+                                         + "Supports HikariCP and Agroal 
connection pools.")
+                .executor((ctx, args) -> ctx.readStatus("dataSources")));
+
+        register(tool("get_spans",
+                "Get OpenTelemetry trace spans from the running Camel 
application. "
+                                   + "Shows trace IDs, span names, durations, 
route IDs, and status. "
+                                   + "Requires OpenTelemetry tracing to be 
enabled (run with --observe).")
+                .param("limit", "string", "Maximum number of spans to return 
(default: 100)", false)
+                .executor((ctx, args) -> {
+                    String limitStr = args.get("limit");
+                    int limit = 100;
+                    if (limitStr != null && !limitStr.isBlank()) {
+                        try {
+                            limit = Integer.parseInt(limitStr);
+                        } catch (NumberFormatException e) {
+                            // use default
+                        }
+                    }
+                    int spanLimit = limit;
+                    return ctx.executeAction("span", root -> {
+                        root.put("dump", "true");
+                        root.put("limit", String.valueOf(spanLimit));
+                    });
+                }));
+
+        register(tool("get_metrics",
+                "Get Micrometer metrics from the running Camel application. "
+                                     + "Shows counters, gauges, timers, 
long-task timers, and distributions. "
+                                     + "Useful for monitoring throughput, 
latency, and resource usage. "
+                                     + "Requires micrometer to be enabled (run 
with --observe or add camel-micrometer).")
+                .executor((ctx, args) -> ctx.readStatus("micrometer")));
+    }
+
+    @SuppressWarnings("unchecked")
+    private static void collectCircuitBreakers(JsonObject root, String 
sectionKey, JsonArray target) {
+        Object section = root.get(sectionKey);
+        if (!(section instanceof JsonObject sectionObj)) {
+            return;
+        }
+        Object breakers = sectionObj.get("circuitBreakers");
+        if (!(breakers instanceof List<?> breakerList)) {
+            return;
+        }
+        String component = switch (sectionKey) {
+            case "resilience4j" -> "resilience4j";
+            case "fault-tolerance" -> "fault-tolerance";
+            default -> "core";
+        };
+        for (Object b : breakerList) {
+            if (b instanceof JsonObject bj) {
+                JsonObject entry = new JsonObject();
+                entry.put("component", component);
+                entry.put("routeId", bj.get("routeId"));
+                entry.put("id", bj.get("id"));
+                entry.put("state", bj.get("state"));
+                entry.put("bufferedCalls", bj.get("bufferedCalls"));
+                entry.put("successfulCalls", bj.get("successfulCalls"));
+                entry.put("failedCalls", bj.get("failedCalls"));
+                entry.put("notPermittedCalls", bj.get("notPermittedCalls"));
+                entry.put("failureRate", bj.get("failureRate"));
+                target.add(entry);
+            }
+        }
+    }
+
+    // ---- Analysis tools (higher-level diagnostic tools) ----
+
+    private static void registerAnalysisTools() {
+        register(tool("get_route_analysis",
+                "Analyze route definitions for common anti-patterns and 
structural issues. "
+                                            + "Checks each route for: error 
handler configuration, dead letter channel, "
+                                            + "route complexity (processor 
count and nesting depth), component usage, "
+                                            + "and potential issues. Returns 
structured analysis results per route.")
+                .param("routeId", "string", "Route ID to analyze (use * for 
all routes, default: *)", false)
+                .executor((ctx, args) -> {
+                    String routeId = args.get("routeId");
+                    // Get route structure for analysis
+                    String structureJson = ctx.executeAction("route-structure",
+                            root -> root.put("id", routeId != null ? routeId : 
"*"));
+                    // Get route dump for error handler info
+                    String dumpJson = ctx.executeAction("route-dump", root -> {
+                        root.put("id", routeId != null ? routeId : "*");
+                        root.put("format", "yaml");
+                    });
+                    JsonObject response = new JsonObject();
+                    response.put("routeStructure", structureJson);
+                    response.put("routeDump", dumpJson);
+                    response.put("analysisHints", buildAnalysisHints());
+                    return response.toJson();
+                }));
+
+        register(tool("get_eip_stats",
+                "Aggregate EIP (Enterprise Integration Pattern) usage 
statistics across all routes. "
+                                       + "Shows which processors are used, how 
often, and their performance metrics "
+                                       + "(total exchanges, failures, mean/max 
processing time). "
+                                       + "Useful for understanding route 
complexity and identifying bottlenecks.")
+                .executor((ctx, args) -> {
+                    // Get top processor stats which include per-processor 
metrics
+                    String topJson = ctx.executeAction("top-processors", null);
+                    // Get route structure for EIP type counting
+                    String structureJson = ctx.executeAction("route-structure",
+                            root -> root.put("id", "*"));
+                    JsonObject response = new JsonObject();
+                    response.put("processorStats", topJson);
+                    response.put("routeStructures", structureJson);
+                    return response.toJson();
+                }));
+
+        register(tool("detect_config_drift",
+                "Compare the running route configuration with the original 
source files. "
+                                             + "Dumps the running route 
definitions and the source files "
+                                             + "so they can be compared to 
detect configuration drift. "
+                                             + "Useful for verifying that 
runtime matches deployment artifacts.")
+                .param("format", "string", "Dump format for comparison: yaml 
or xml (default: yaml)", false)
+                .executor((ctx, args) -> {
+                    String format = args.get("format");
+                    if (format == null || format.isBlank()) {
+                        format = "yaml";
+                    }
+                    // Get running route definitions
+                    String dumpFormat = format;
+                    String runningRoutes = ctx.executeAction("route-dump", 
root -> {
+                        root.put("id", "*");
+                        root.put("format", dumpFormat);
+                    });
+                    // Get source files
+                    String sourceFiles = ctx.executeAction("source",
+                            root -> root.put("filter", "*"));
+                    JsonObject response = new JsonObject();
+                    response.put("runningRoutes", runningRoutes);
+                    response.put("sourceFiles", sourceFiles);
+                    response.put("format", dumpFormat);
+                    response.put("hint",
+                            "Compare the running route definitions with the 
source files to identify any differences. "
+                                         + "Common drift causes: dynamic route 
modification, runtime property resolution, "
+                                         + "and hot-reloading changes.");
+                    return response.toJson();
+                }));
+    }
+
+    private static JsonObject buildAnalysisHints() {
+        JsonObject hints = new JsonObject();
+        hints.put("errorHandling",
+                "Check if routes define an errorHandler or onException clause. 
"
+                                   + "Routes without error handling will 
propagate exceptions to the caller.");
+        hints.put("deadLetterChannel",
+                "Routes processing messages from queues should have a 
deadLetterChannel "
+                                       + "to avoid losing messages on repeated 
failures.");
+        hints.put("timeout",
+                "Routes calling external services (http, rest, soap) should 
configure "
+                             + "connection and read timeouts to avoid hanging 
threads.");
+        hints.put("idempotency",
+                "Routes consuming from non-idempotent sources (JMS, Kafka 
without exactly-once) "
+                                 + "should use an idempotentConsumer to handle 
duplicate messages.");
+        hints.put("circuitBreaker",
+                "Routes calling unreliable external services should wrap calls 
in a "
+                                    + "circuitBreaker (resilience4j) to 
prevent cascade failures.");
+        hints.put("logging",
+                "Routes should include appropriate logging (log EIP) at key 
points "
+                             + "for operational visibility, especially on 
error paths.");
+        return hints;
+    }
+
+    // ---- Catalog tools ----
+
+    private static void registerCatalogTools() {
+        register(tool("catalog_components",
+                "Search the Camel component catalog by name or label.")
+                .param("filter", "string",
+                        "Filter by name, title, or description 
(case-insensitive substring)", false)
+                .param("label", "string",
+                        "Filter by category label (e.g., cloud, messaging, 
database, file)", false)
+                .executor((ctx, args) -> {
+                    String filter = args.get("filter");
+                    String label = args.get("label");
+                    CamelCatalog cat = ctx.catalog();
+                    List<JsonObject> results = 
cat.findComponentNames().stream()
+                            .map(cat::componentModel)
+                            .filter(Objects::nonNull)
+                            .filter(m -> matchesFilter(m.getScheme(), 
m.getTitle(), m.getDescription(), filter))
+                            .filter(m -> label == null || label.isBlank()
+                                    || (m.getLabel() != null
+                                            && 
m.getLabel().toLowerCase().contains(label.toLowerCase())))
+                            .limit(20)
+                            .map(m -> {
+                                JsonObject jo = new JsonObject();
+                                jo.put("name", m.getScheme());
+                                jo.put("title", m.getTitle());
+                                jo.put("description", m.getDescription());
+                                jo.put("label", m.getLabel());
+                                return jo;
+                            })
+                            .collect(Collectors.toList());
+                    JsonObject response = new JsonObject();
+                    response.put("count", results.size());
+                    response.put("components", results);
+                    return response.toJson();
+                }));
+
+        register(tool("catalog_component_doc",
+                "Get detailed documentation for a Camel component: URI syntax 
and endpoint options.")
+                .param("component", "string",
+                        "Component name (e.g., kafka, http, file, timer)", 
true)
+                .executor((ctx, args) -> {
+                    String component = args.get("component");
+                    if (component == null || component.isBlank()) {
+                        throw new ToolExecutionException("component name is 
required");
+                    }
+                    CamelCatalog cat = ctx.catalog();
+                    ComponentModel model = cat.componentModel(component);
+                    if (model == null) {
+                        return "Component not found: " + component;
+                    }
+                    JsonObject response = new JsonObject();
+                    response.put("name", model.getScheme());
+                    response.put("title", model.getTitle());
+                    response.put("description", model.getDescription());
+                    response.put("syntax", model.getSyntax());
+                    response.put("consumerOnly", model.isConsumerOnly());
+                    response.put("producerOnly", model.isProducerOnly());
+                    List<JsonObject> options = new ArrayList<>();
+                    if (model.getEndpointOptions() != null) {
+                        model.getEndpointOptions().stream()
+                                .filter(opt -> !opt.isDeprecated())
+                                .forEach(opt -> {
+                                    JsonObject jo = new JsonObject();
+                                    jo.put("name", opt.getName());
+                                    jo.put("description", 
opt.getDescription());
+                                    jo.put("type", opt.getType());
+                                    jo.put("required", opt.isRequired());
+                                    if (opt.getDefaultValue() != null) {
+                                        jo.put("defaultValue", 
opt.getDefaultValue().toString());
+                                    }
+                                    options.add(jo);
+                                });
+                    }
+                    response.put("options", options);
+                    return response.toJson();
+                }));
+
+        register(tool("catalog_eips",
+                "Search EIPs (Enterprise Integration Patterns) like split, 
aggregate, filter, choice, multicast.")
+                .param("filter", "string",
+                        "Filter by name, title, or description 
(case-insensitive substring)", false)
+                .executor((ctx, args) -> {
+                    String filter = args.get("filter");
+                    CamelCatalog cat = ctx.catalog();
+                    List<JsonObject> results = cat.findModelNames().stream()
+                            .map(cat::eipModel)
+                            .filter(Objects::nonNull)
+                            .filter(m -> matchesFilter(m.getName(), 
m.getTitle(), m.getDescription(), filter))
+                            .limit(20)
+                            .map(m -> {
+                                JsonObject jo = new JsonObject();
+                                jo.put("name", m.getName());
+                                jo.put("title", m.getTitle());
+                                jo.put("description", m.getDescription());
+                                jo.put("label", m.getLabel());
+                                return jo;
+                            })
+                            .collect(Collectors.toList());
+                    JsonObject response = new JsonObject();
+                    response.put("count", results.size());
+                    response.put("eips", results);
+                    return response.toJson();
+                }));
+
+        // New catalog tools (previously MCP-only)
+
+        register(tool("catalog_dataformats",
+                "List available Camel data formats (e.g., json, xml, csv, 
avro).")
+                .param("filter", "string", "Filter by name or description", 
false)
+                .executor((ctx, args) -> {
+                    String filter = args.get("filter");
+                    CamelCatalog cat = ctx.catalog();
+                    List<JsonObject> results = 
cat.findDataFormatNames().stream()
+                            .map(cat::dataFormatModel)
+                            .filter(Objects::nonNull)
+                            .filter(m -> matchesFilter(m.getName(), 
m.getTitle(), m.getDescription(), filter))
+                            .limit(20)
+                            .map(m -> {
+                                JsonObject jo = new JsonObject();
+                                jo.put("name", m.getName());
+                                jo.put("title", m.getTitle());
+                                jo.put("description", m.getDescription());
+                                return jo;
+                            })
+                            .collect(Collectors.toList());
+                    JsonObject response = new JsonObject();
+                    response.put("count", results.size());
+                    response.put("dataformats", results);
+                    return response.toJson();
+                }));
+
+        register(tool("catalog_dataformat_doc",
+                "Get detailed documentation for a Camel data format.")
+                .param("dataformat", "string",
+                        "Data format name (e.g., jackson, jaxb, csv)", true)
+                .executor((ctx, args) -> {
+                    String name = args.get("dataformat");
+                    if (name == null || name.isBlank()) {
+                        throw new ToolExecutionException("dataformat name is 
required");
+                    }
+                    CamelCatalog cat = ctx.catalog();
+                    var model = cat.dataFormatModel(name);
+                    if (model == null) {
+                        return "Data format not found: " + name;
+                    }
+                    JsonObject response = new JsonObject();
+                    response.put("name", model.getName());
+                    response.put("title", model.getTitle());
+                    response.put("description", model.getDescription());
+                    response.put("modelJavaType", model.getModelJavaType());
+                    if (model.getOptions() != null) {
+                        List<JsonObject> options = model.getOptions().stream()
+                                .filter(opt -> !opt.isDeprecated())
+                                .map(opt -> {
+                                    JsonObject jo = new JsonObject();
+                                    jo.put("name", opt.getName());
+                                    jo.put("description", 
opt.getDescription());
+                                    jo.put("type", opt.getType());
+                                    jo.put("required", opt.isRequired());
+                                    if (opt.getDefaultValue() != null) {
+                                        jo.put("defaultValue", 
opt.getDefaultValue().toString());
+                                    }
+                                    return jo;
+                                })
+                                .collect(Collectors.toList());
+                        response.put("options", options);
+                    }
+                    return response.toJson();
+                }));
+
+        register(tool("catalog_languages",
+                "List available Camel expression languages (e.g., simple, 
jsonpath, xpath, jq).")
+                .param("filter", "string", "Filter by name or description", 
false)
+                .executor((ctx, args) -> {
+                    String filter = args.get("filter");
+                    CamelCatalog cat = ctx.catalog();
+                    List<JsonObject> results = cat.findLanguageNames().stream()
+                            .map(cat::languageModel)
+                            .filter(Objects::nonNull)
+                            .filter(m -> matchesFilter(m.getName(), 
m.getTitle(), m.getDescription(), filter))
+                            .limit(20)
+                            .map(m -> {
+                                JsonObject jo = new JsonObject();
+                                jo.put("name", m.getName());
+                                jo.put("title", m.getTitle());
+                                jo.put("description", m.getDescription());
+                                return jo;
+                            })
+                            .collect(Collectors.toList());
+                    JsonObject response = new JsonObject();
+                    response.put("count", results.size());
+                    response.put("languages", results);
+                    return response.toJson();
+                }));
+
+        register(tool("catalog_language_doc",
+                "Get detailed documentation for a Camel expression language.")
+                .param("language", "string",
+                        "Language name (e.g., simple, jsonpath, xpath, jq)", 
true)
+                .executor((ctx, args) -> {
+                    String name = args.get("language");
+                    if (name == null || name.isBlank()) {
+                        throw new ToolExecutionException("language name is 
required");
+                    }
+                    CamelCatalog cat = ctx.catalog();
+                    var model = cat.languageModel(name);
+                    if (model == null) {
+                        return "Language not found: " + name;
+                    }
+                    JsonObject response = new JsonObject();
+                    response.put("name", model.getName());
+                    response.put("title", model.getTitle());
+                    response.put("description", model.getDescription());
+                    response.put("modelJavaType", model.getModelJavaType());
+                    if (model.getOptions() != null) {
+                        List<JsonObject> options = model.getOptions().stream()
+                                .filter(opt -> !opt.isDeprecated())
+                                .map(opt -> {
+                                    JsonObject jo = new JsonObject();
+                                    jo.put("name", opt.getName());
+                                    jo.put("description", 
opt.getDescription());
+                                    jo.put("type", opt.getType());
+                                    jo.put("required", opt.isRequired());
+                                    if (opt.getDefaultValue() != null) {
+                                        jo.put("defaultValue", 
opt.getDefaultValue().toString());
+                                    }
+                                    return jo;
+                                })
+                                .collect(Collectors.toList());
+                        response.put("options", options);
+                    }
+                    return response.toJson();
+                }));
+
+        register(tool("catalog_eip_doc",
+                "Get detailed documentation for a Camel EIP (Enterprise 
Integration Pattern).")
+                .param("eip", "string",
+                        "EIP name (e.g., split, aggregate, filter, choice)", 
true)
+                .executor((ctx, args) -> {
+                    String name = args.get("eip");
+                    if (name == null || name.isBlank()) {
+                        throw new ToolExecutionException("EIP name is 
required");
+                    }
+                    CamelCatalog cat = ctx.catalog();
+                    var model = cat.eipModel(name);
+                    if (model == null) {
+                        return "EIP not found: " + name;
+                    }
+                    JsonObject response = new JsonObject();
+                    response.put("name", model.getName());
+                    response.put("title", model.getTitle());
+                    response.put("description", model.getDescription());
+                    response.put("label", model.getLabel());
+                    response.put("input", model.isInput());
+                    response.put("output", model.isOutput());
+                    if (model.getOptions() != null) {
+                        List<JsonObject> options = model.getOptions().stream()
+                                .filter(opt -> !opt.isDeprecated())
+                                .map(opt -> {
+                                    JsonObject jo = new JsonObject();
+                                    jo.put("name", opt.getName());
+                                    jo.put("description", 
opt.getDescription());
+                                    jo.put("type", opt.getType());
+                                    jo.put("required", opt.isRequired());
+                                    if (opt.getDefaultValue() != null) {
+                                        jo.put("defaultValue", 
opt.getDefaultValue().toString());
+                                    }
+                                    return jo;
+                                })
+                                .collect(Collectors.toList());
+                        response.put("options", options);
+                    }
+                    return response.toJson();
+                }));
+    }
+
+    // ---- Example tools ----
+
+    private static void registerExampleTools() {
+        register(tool("list_examples",
+                "List available Camel CLI examples. Returns name, title, 
description, difficulty level, and tags.")
+                .param("filter", "string",
+                        "Filter by name, description, or tag 
(case-insensitive)", false)
+                .param("level", "string",
+                        "Filter by difficulty: beginner, intermediate, or 
advanced", false)
+                .executor((ctx, args) -> {
+                    String filter = args.get("filter");
+                    String level = args.get("level");
+                    List<JsonObject> catalog2 = ExampleHelper.loadCatalog();
+                    List<JsonObject> filtered = 
ExampleHelper.filterExamples(catalog2, filter);
+                    List<JsonObject> results = new ArrayList<>();
+                    for (JsonObject entry : filtered) {
+                        if (level != null && !level.isBlank()) {
+                            String entryLevel = entry.getString("level");
+                            if (entryLevel == null || 
!entryLevel.equalsIgnoreCase(level)) {
+                                continue;
+                            }
+                        }
+                        JsonObject jo = new JsonObject();
+                        jo.put("name", entry.getString("name"));
+                        jo.put("title", entry.getString("title"));
+                        jo.put("description", entry.getString("description"));
+                        jo.put("level", entry.getString("level"));
+                        jo.put("tags", entry.get("tags"));
+                        jo.put("bundled", ExampleHelper.isBundled(entry));
+                        jo.put("files", ExampleHelper.getFiles(entry));
+                        results.add(jo);
+                        if (results.size() >= 20) {
+                            break;
+                        }
+                    }
+                    JsonObject response = new JsonObject();
+                    response.put("count", results.size());
+                    response.put("examples", results);
+                    return response.toJson();
+                }));
+
+        register(tool("get_example_file",
+                "Get the content of a file from a bundled Camel CLI example.")
+                .param("example", "string",
+                        "Example name (e.g., timer-log, rest-api, 
circuit-breaker)", true)
+                .param("file", "string",
+                        "File name within the example (e.g., 
route.camel.yaml)", true)
+                .executor((ctx, args) -> {
+                    String example = args.get("example");
+                    String file = args.get("file");
+                    if (example == null || example.isBlank()) {
+                        throw new ToolExecutionException("example name is 
required");
+                    }
+                    if (file == null || file.isBlank()) {
+                        throw new ToolExecutionException("file name is 
required");
+                    }
+                    List<JsonObject> catalog2 = ExampleHelper.loadCatalog();
+                    JsonObject entry = ExampleHelper.findExample(catalog2, 
example);
+                    if (entry == null) {
+                        return "Example not found: " + example;
+                    }
+                    List<String> files = ExampleHelper.getFiles(entry);
+                    if (!files.contains(file)) {
+                        return "File '" + file + "' not found in example '" + 
example + "'. Available: " + files;
+                    }
+                    if (ExampleHelper.isBundled(entry)) {
+                        String resourcePath = "examples/" + example + "/" + 
file;
+                        try (InputStream is
+                                = 
ExampleHelper.class.getClassLoader().getResourceAsStream(resourcePath)) {
+                            if (is != null) {
+                                return IOHelper.loadText(is);
+                            }
+                        } catch (Exception e) {
+                            return "Error reading file: " + e.getMessage();
+                        }
+                        return "Could not read bundled file: " + resourcePath;
+                    } else {
+                        return "This example is not bundled. View it on 
GitHub: "
+                               + ExampleHelper.getGithubUrl(entry) + "/" + 
file;
+                    }
+                }));
+    }
+
+    // ---- Utility ----
+
+    static boolean matchesFilter(String name, String title, String 
description, String filter) {
+        if (filter == null || filter.isBlank()) {
+            return true;
+        }
+        String lf = filter.toLowerCase();
+        return (name != null && name.toLowerCase().contains(lf))
+                || (title != null && title.toLowerCase().contains(lf))
+                || (description != null && 
description.toLowerCase().contains(lf));
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistryTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistryTest.java
new file mode 100644
index 000000000000..514e2d77c0a9
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistryTest.java
@@ -0,0 +1,166 @@
+/*
+ * 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.ai;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ToolRegistryTest {
+
+    @Test
+    void allToolsHaveUniqueNames() {
+        List<String> names = ToolRegistry.allTools().stream()
+                .map(ToolDescriptor::name).toList();
+        Set<String> unique = new HashSet<>(names);
+        assertEquals(unique.size(), names.size(), "Duplicate tool names 
found");
+    }
+
+    @Test
+    void allToolsHaveDescriptions() {
+        for (ToolDescriptor t : ToolRegistry.allTools()) {
+            assertNotNull(t.description(), t.name() + " has no description");
+            assertFalse(t.description().isBlank(), t.name() + " has blank 
description");
+        }
+    }
+
+    @Test
+    void allToolsHaveExecutors() {
+        for (ToolDescriptor t : ToolRegistry.allTools()) {
+            assertNotNull(t.executor(), t.name() + " has no executor");
+        }
+    }
+
+    @Test
+    void findToolReturnsRegisteredTool() {
+        ToolDescriptor tool = ToolRegistry.findTool("catalog_components");
+        assertNotNull(tool);
+        assertEquals("catalog_components", tool.name());
+    }
+
+    @Test
+    void findToolReturnsNullForUnknown() {
+        assertNull(ToolRegistry.findTool("nonexistent_tool"));
+    }
+
+    @Test
+    void catalogToolsWorkWithoutProcess() {
+        // Catalog tools should not require a running process
+        ToolContext ctx = new ToolContext();
+        Object result = ToolRegistry.execute("catalog_components",
+                ctx, Map.of("filter", "timer"));
+        assertNotNull(result);
+        String json = result.toString();
+        assertTrue(json.contains("timer"), "Should find timer component");
+    }
+
+    @Test
+    void catalogEipToolsWorkWithoutProcess() {
+        ToolContext ctx = new ToolContext();
+        Object result = ToolRegistry.execute("catalog_eips",
+                ctx, Map.of("filter", "split"));
+        assertNotNull(result);
+        String json = result.toString();
+        assertTrue(json.contains("split"), "Should find split EIP");
+    }
+
+    @Test
+    void runtimeToolThrowsWithoutProcess() {
+        ToolContext ctx = new ToolContext();
+        assertThrows(ToolExecutionException.class,
+                () -> ToolRegistry.execute("get_context", ctx, Map.of()));
+    }
+
+    @Test
+    void registryContainsExpectedToolCount() {
+        // Should have at least 50 tools (process + status + actions + 
DevConsole + analysis + catalog + examples)
+        assertTrue(ToolRegistry.allTools().size() >= 50,
+                "Expected at least 50 tools, got " + 
ToolRegistry.allTools().size());
+    }
+
+    @Test
+    void newCatalogToolsPresent() {
+        // Verify the new catalog tools that were MCP-only are now registered
+        assertNotNull(ToolRegistry.findTool("catalog_dataformats"), 
"catalog_dataformats should be registered");
+        assertNotNull(ToolRegistry.findTool("catalog_dataformat_doc"), 
"catalog_dataformat_doc should be registered");
+        assertNotNull(ToolRegistry.findTool("catalog_languages"), 
"catalog_languages should be registered");
+        assertNotNull(ToolRegistry.findTool("catalog_language_doc"), 
"catalog_language_doc should be registered");
+        assertNotNull(ToolRegistry.findTool("catalog_eip_doc"), 
"catalog_eip_doc should be registered");
+    }
+
+    @Test
+    void devConsoleToolsPresent() {
+        // Verify DevConsole tools are registered
+        assertNotNull(ToolRegistry.findTool("get_circuit_breakers"), 
"get_circuit_breakers should be registered");
+        assertNotNull(ToolRegistry.findTool("get_startup_steps"), 
"get_startup_steps should be registered");
+        assertNotNull(ToolRegistry.findTool("get_datasources"), 
"get_datasources should be registered");
+        assertNotNull(ToolRegistry.findTool("execute_sql"), "execute_sql 
should be registered");
+        assertNotNull(ToolRegistry.findTool("get_spans"), "get_spans should be 
registered");
+        assertNotNull(ToolRegistry.findTool("get_metrics"), "get_metrics 
should be registered");
+    }
+
+    @Test
+    void executeSqlToolRequiresProcess() {
+        ToolContext ctx = new ToolContext();
+        assertThrows(ToolExecutionException.class,
+                () -> ToolRegistry.execute("execute_sql", ctx, Map.of("query", 
"SELECT 1")));
+    }
+
+    @Test
+    void executeSqlToolRequiresQueryParam() {
+        ToolContext ctx = new ToolContext();
+        ctx.selectProcess(99999);
+        // execute_sql should throw for empty query parameter
+        assertThrows(ToolExecutionException.class,
+                () -> ToolRegistry.execute("execute_sql", ctx, Map.of()));
+    }
+
+    @Test
+    void circuitBreakerToolRequiresProcess() {
+        ToolContext ctx = new ToolContext();
+        assertThrows(ToolExecutionException.class,
+                () -> ToolRegistry.execute("get_circuit_breakers", ctx, 
Map.of()));
+    }
+
+    @Test
+    void analysisToolsPresent() {
+        assertNotNull(ToolRegistry.findTool("get_route_analysis"), 
"get_route_analysis should be registered");
+        assertNotNull(ToolRegistry.findTool("get_eip_stats"), "get_eip_stats 
should be registered");
+        assertNotNull(ToolRegistry.findTool("detect_config_drift"), 
"detect_config_drift should be registered");
+    }
+
+    @Test
+    void analysisToolsRequireProcess() {
+        ToolContext ctx = new ToolContext();
+        assertThrows(ToolExecutionException.class,
+                () -> ToolRegistry.execute("get_route_analysis", ctx, 
Map.of()));
+        assertThrows(ToolExecutionException.class,
+                () -> ToolRegistry.execute("get_eip_stats", ctx, Map.of()));
+        assertThrows(ToolExecutionException.class,
+                () -> ToolRegistry.execute("detect_config_drift", ctx, 
Map.of()));
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
index 05e7e26a0512..a4bfa93a682d 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
@@ -204,4 +204,177 @@ public class PromptDefinitions {
 
         return List.of(PromptMessage.withUserRole(instructions));
     }
+
+    /**
+     * Guided workflow for diagnosing issues with a running Camel route.
+     */
+    @Prompt(name = "camel_diagnose_route",
+            description = "Guided workflow to diagnose issues with a running 
Camel route: "
+                          + "gather runtime state, errors, health, message 
history, "
+                          + "and produce a root cause analysis with actionable 
fixes.")
+    public List<PromptMessage> diagnoseRoute(
+            @PromptArg(name = "routeId",
+                       description = "Route ID to diagnose (optional — if 
omitted, diagnoses all routes with issues)",
+                       required = false) String routeId,
+            @PromptArg(name = "symptom",
+                       description = "Description of the observed problem 
(e.g., 'messages are not being processed', "
+                                     + "'high error rate', 'route is 
suspended')",
+                       required = false) String symptom) {
+
+        String routeFilter = routeId != null && !routeId.isBlank() ? routeId : 
"*";
+        String symptomNote = symptom != null && !symptom.isBlank()
+                ? "Reported symptom: " + symptom
+                : "No specific symptom reported — perform a general health 
diagnosis.";
+
+        String instructions = """
+                You are diagnosing a running Camel application.
+
+                %s
+                Target route(s): %s
+
+                ## Workflow
+
+                Follow these steps in order:
+
+                ### Step 1: Gather context
+                Call `get_context` to understand the application state 
(version, uptime, total exchanges).
+                Call `get_routes` to see all routes, their states, and error 
counts.
+
+                ### Step 2: Check health
+                Call `get_health` to see if any health checks are failing.
+                Note any DOWN or UNKNOWN health statuses.
+
+                ### Step 3: Collect errors
+                Call `get_errors` to get captured routing errors with stack 
traces.
+                Call `get_history` to see the message history trace of the 
last completed exchange.
+
+                ### Step 4: Inspect route details
+                Call `get_route_source` with filter="%s" to see the route 
definition.
+                Call `get_top_processors` to identify slow or failing 
processors.
+                Call `get_inflight` to check for stuck exchanges.
+                Call `get_blocked` to check for blocked exchanges.
+
+                ### Step 5: Check infrastructure
+                Call `get_memory` to check for memory pressure or excessive GC.
+                Call `get_endpoints` to verify all endpoints are reachable.
+
+                ### Step 6: Advanced diagnostics (if needed)
+                If circuit breakers are involved, call `get_circuit_breakers`.
+                If datasources are involved, call `get_datasources`.
+                If the issue involves timing, call `get_spans` for trace data.
+                If the issue involves metrics, call `get_metrics`.
+
+                ### Step 7: Root cause analysis
+                Synthesize the gathered data into a structured diagnosis:
+
+                **Summary**: One-sentence description of the root cause.
+
+                **Evidence**: List the specific data points that led to this 
conclusion:
+                - Which tools provided the key evidence
+                - Specific values that indicate the problem (error counts, 
timing, states)
+
+                **Root Cause**: Detailed explanation of what is going wrong 
and why.
+
+                **Immediate Actions**: Steps to take right now to mitigate:
+                - Route actions (stop, restart, suspend)
+                - Configuration changes
+                - Resource adjustments
+
+                **Permanent Fix**: What needs to change in the route 
definition or configuration \
+                to prevent recurrence. Include specific code examples if 
applicable.
+
+                **Monitoring**: What to watch going forward to detect this 
issue early.
+                """.formatted(symptomNote, routeFilter, routeFilter);
+
+        return List.of(PromptMessage.withUserRole(instructions));
+    }
+
+    /**
+     * Guided workflow for optimizing a running Camel application's 
performance.
+     */
+    @Prompt(name = "camel_optimize_route",
+            description = "Guided workflow to optimize a Camel application's 
performance: "
+                          + "analyze throughput, identify bottlenecks, review 
resource usage, "
+                          + "and produce prioritized optimization 
recommendations.")
+    public List<PromptMessage> optimizeRoute(
+            @PromptArg(name = "routeId",
+                       description = "Route ID to optimize (optional — if 
omitted, analyzes all routes)",
+                       required = false) String routeId,
+            @PromptArg(name = "goal",
+                       description = "Optimization goal: throughput, latency, 
memory, or general (default: general)",
+                       required = false) String goal) {
+
+        String routeFilter = routeId != null && !routeId.isBlank() ? routeId : 
"*";
+        String resolvedGoal = goal != null && !goal.isBlank() ? goal : 
"general";
+
+        String instructions = """
+                You are optimizing a running Camel application for: **%s**.
+
+                Target route(s): %s
+
+                ## Workflow
+
+                Follow these steps in order:
+
+                ### Step 1: Baseline performance
+                Call `get_context` to get overall exchange statistics (total, 
completed, failed, mean time).
+                Call `get_routes` to get per-route throughput and timing data.
+                Call `get_top_processors` to identify the slowest processors.
+
+                ### Step 2: Resource analysis
+                Call `get_memory` to assess JVM memory usage, GC frequency, 
and thread counts.
+                Call `get_consumers` to check consumer configuration and 
polling strategies.
+                Call `get_endpoints` to review endpoint usage patterns.
+
+                ### Step 3: Route structure analysis
+                Call `get_route_analysis` to get route structure and 
anti-pattern hints.
+                Call `get_eip_stats` to understand EIP usage and processor 
performance distribution.
+
+                ### Step 4: Infrastructure checks
+                Call `get_metrics` for detailed Micrometer metrics (if 
available).
+                Call `get_datasources` if database access is involved.
+                Call `get_circuit_breakers` if resilience patterns are in use.
+
+                ### Step 5: Route inspection
+                Call `get_route_source` with filter="%s" to review the route 
code.
+                Call `get_route_dump` with routeId="%s" and format="yaml" to 
see the full definition.
+
+                ### Step 6: Optimization report
+                Produce a structured optimization report:
+
+                **Current Performance Baseline**:
+                - Overall throughput (exchanges/sec)
+                - Mean and max processing time
+                - Error rate
+                - Resource utilization (memory, threads)
+
+                **Bottlenecks Identified** (ordered by impact):
+                For each bottleneck:
+                - What: the specific processor, endpoint, or pattern
+                - Impact: how much time or throughput it costs
+                - Evidence: the specific metrics that revealed it
+
+                **Optimization Recommendations** (ordered by priority):
+
+                For each recommendation:
+                1. **Change**: What to modify (with specific code examples)
+                2. **Expected Impact**: Estimated improvement
+                3. **Risk**: What could go wrong
+                4. **Effort**: Low/Medium/High
+
+                Common optimizations to evaluate:
+                - **Parallelism**: Can `split` with `parallelProcessing` or 
`threads` EIP help?
+                - **Batching**: Should messages be batched with `aggregate`?
+                - **Caching**: Can frequently-accessed data be cached?
+                - **Connection pooling**: Are connection pools properly sized?
+                - **Async processing**: Can synchronous calls be made 
asynchronous?
+                - **Backpressure**: Is `throttle` or `circuitBreaker` needed?
+                - **Serialization**: Is a more efficient data format available?
+
+                **Trade-offs**: Note any trade-offs between the optimization 
goal \
+                and other qualities (e.g., throughput vs. latency, performance 
vs. reliability).
+                """.formatted(resolvedGoal, routeFilter, routeFilter, 
routeFilter);
+
+        return List.of(PromptMessage.withUserRole(instructions));
+    }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/RuntimeTools.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/RuntimeTools.java
index 3f86c4ecd9a6..91123420f214 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/RuntimeTools.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/RuntimeTools.java
@@ -16,7 +16,9 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.mcp;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import jakarta.enterprise.context.ApplicationScoped;
 import jakarta.inject.Inject;
@@ -24,13 +26,21 @@ import jakarta.inject.Inject;
 import io.quarkiverse.mcp.server.Tool;
 import io.quarkiverse.mcp.server.ToolArg;
 import io.quarkiverse.mcp.server.ToolCallException;
+import org.apache.camel.dsl.jbang.core.commands.ai.ToolContext;
+import org.apache.camel.dsl.jbang.core.commands.ai.ToolExecutionException;
+import org.apache.camel.dsl.jbang.core.commands.ai.ToolRegistry;
 import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
 
 /**
  * MCP tools for inspecting and interacting with running Camel applications.
  * <p>
- * These tools communicate with running Camel processes via the file-based IPC 
protocol in ~/.camel/. Status data is
- * read from periodic snapshots; interactive commands use the multi-client 
action file protocol.
+ * These tools are thin MCP wrappers that delegate to the shared {@link 
ToolRegistry}. Each method resolves the
+ * {@code nameOrPid} parameter (an MCP-specific concern for multi-process 
discovery) and then delegates to the
+ * corresponding {@link ToolRegistry} tool for the actual logic.
+ * <p>
+ * The {@code @Tool} and {@code @ToolArg} annotations are required by the 
Quarkus MCP server for protocol discovery but
+ * all tool logic lives in {@link ToolRegistry}, ensuring a single source of 
truth shared with the Agent REPL.
  */
 @ApplicationScoped
 public class RuntimeTools {
@@ -41,7 +51,55 @@ public class RuntimeTools {
     @Inject
     RuntimeService runtimeService;
 
-    // ---- Process discovery ----
+    // ---- Delegation helpers ----
+
+    /**
+     * Resolve nameOrPid via {@link RuntimeService}, create a {@link 
ToolContext} with the resolved PID, and delegate to
+     * the shared {@link ToolRegistry}.
+     *
+     * @param  registryToolName the tool name in ToolRegistry (e.g., 
"get_context")
+     * @param  nameOrPid        MCP process selector (name, PID, or empty for 
auto-detect)
+     * @param  args             tool arguments as a String map
+     * @return                  the tool result as a JsonObject suitable for 
MCP responses
+     */
+    private JsonObject delegateToRegistry(String registryToolName, String 
nameOrPid, Map<String, String> args) {
+        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
+        ToolContext ctx = new ToolContext();
+        ctx.selectProcess(p.pid());
+        try {
+            Object result = ToolRegistry.execute(registryToolName, ctx, args);
+            return toJsonObject(result);
+        } catch (ToolExecutionException e) {
+            throw new ToolCallException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Convert a {@link ToolRegistry} result (typically a JSON String or 
JsonObject) into a {@link JsonObject} for MCP
+     * serialization.
+     */
+    static JsonObject toJsonObject(Object result) {
+        if (result instanceof JsonObject jo) {
+            return jo;
+        }
+        if (result == null) {
+            return new JsonObject();
+        }
+        String str = result.toString();
+        try {
+            Object parsed = Jsoner.deserialize(str);
+            if (parsed instanceof JsonObject jo) {
+                return jo;
+            }
+        } catch (Exception e) {
+            // not a valid JSON object — wrap as plain text
+        }
+        JsonObject wrapper = new JsonObject();
+        wrapper.put("result", str);
+        return wrapper;
+    }
+
+    // ---- Process discovery (MCP-specific, not in ToolRegistry) ----
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = """
@@ -51,121 +109,94 @@ public class RuntimeTools {
         return runtimeService.discoverProcesses();
     }
 
-    // ---- Read-only tools (from status file) ----
+    // ---- Read-only status tools ----
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "Get Camel context information: name, version, state, 
uptime, route count, exchange statistics.")
     public JsonObject camel_runtime_context(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.readStatusSection(p.pid(), "context");
+        return delegateToRegistry("get_context", nameOrPid, Map.of());
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "List Camel routes with their state, uptime, messages 
processed, last error, and throughput statistics.")
     public JsonObject camel_runtime_routes(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.readStatusSection(p.pid(), "routes");
+        return delegateToRegistry("get_routes", nameOrPid, Map.of());
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "Get health check status for the Camel application.")
     public JsonObject camel_runtime_health(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.readStatusSection(p.pid(), "healthChecks");
+        return delegateToRegistry("get_health", nameOrPid, Map.of());
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "List all endpoints registered in the Camel context 
with their URIs and usage statistics.")
     public JsonObject camel_runtime_endpoints(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.readStatusSection(p.pid(), "endpoints");
+        return delegateToRegistry("get_endpoints", nameOrPid, Map.of());
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "Show currently in-flight exchanges (messages being 
processed).")
     public JsonObject camel_runtime_inflight(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.readStatusSection(p.pid(), "inflight");
+        return delegateToRegistry("get_inflight", nameOrPid, Map.of());
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "Show blocked exchanges that are stuck or waiting.")
     public JsonObject camel_runtime_blocked(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.readStatusSection(p.pid(), "blocked");
+        return delegateToRegistry("get_blocked", nameOrPid, Map.of());
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "Show exchange variables in the Camel context.")
     public JsonObject camel_runtime_variables(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.readStatusSection(p.pid(), "variables");
+        return delegateToRegistry("get_variables", nameOrPid, Map.of());
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "Show consumer statistics (polling consumers, 
event-driven consumers).")
     public JsonObject camel_runtime_consumers(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.readStatusSection(p.pid(), "consumers");
+        return delegateToRegistry("get_consumers", nameOrPid, Map.of());
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "Show configuration properties of the running Camel 
application.")
     public JsonObject camel_runtime_properties(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.readStatusSection(p.pid(), "properties");
+        return delegateToRegistry("get_properties", nameOrPid, Map.of());
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "Show services registered in the Camel service 
registry.")
     public JsonObject camel_runtime_services(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.readStatusSection(p.pid(), "services");
+        return delegateToRegistry("get_services", nameOrPid, Map.of());
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "Show JVM memory usage (heap/non-heap), garbage 
collection stats, and thread counts.")
     public JsonObject camel_runtime_memory(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        JsonObject status = runtimeService.readStatus(p.pid());
-        if (status == null) {
-            return new JsonObject();
-        }
-        JsonObject result = new JsonObject();
-        if (status.containsKey("memory")) {
-            result.put("memory", status.get("memory"));
-        }
-        if (status.containsKey("gc")) {
-            result.put("gc", status.get("gc"));
-        }
-        if (status.containsKey("threads")) {
-            result.put("threads", status.get("threads"));
-        }
-        return result;
+        return delegateToRegistry("get_memory", nameOrPid, Map.of());
     }
 
-    // ---- Interactive tools (via action file IPC) ----
+    // ---- Interactive action tools ----
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "Get the source code of routes in the running Camel 
application.")
     public JsonObject camel_runtime_route_source(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid,
             @ToolArg(description = "Filter source files by name (supports 
wildcards)") String filter) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.executeAction(p.pid(), "source", root -> {
-            root.put("filter", filter != null ? filter : "*");
-        });
+        return delegateToRegistry("get_route_source", nameOrPid,
+                Map.of("filter", filter != null ? filter : "*"));
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
@@ -174,11 +205,10 @@ public class RuntimeTools {
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid,
             @ToolArg(description = "Route ID to dump (use * for all routes)") 
String routeId,
             @ToolArg(description = "Output format: xml, yaml, or java 
(default: yaml)") String format) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.executeAction(p.pid(), "route-dump", root -> {
-            root.put("id", routeId != null ? routeId : "*");
-            root.put("format", format != null ? format : "yaml");
-        });
+        Map<String, String> args = new HashMap<>();
+        args.put("routeId", routeId != null ? routeId : "*");
+        args.put("format", format != null ? format : "yaml");
+        return delegateToRegistry("get_route_dump", nameOrPid, args);
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
@@ -186,10 +216,8 @@ public class RuntimeTools {
     public JsonObject camel_runtime_route_structure(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid,
             @ToolArg(description = "Route ID to inspect (use * for all 
routes)") String routeId) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.executeAction(p.pid(), "route-structure", root 
-> {
-            root.put("id", routeId != null ? routeId : "*");
-        });
+        return delegateToRegistry("get_route_structure", nameOrPid,
+                Map.of("routeId", routeId != null ? routeId : "*"));
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = false, 
destructiveHint = true, openWorldHint = false),
@@ -204,11 +232,16 @@ public class RuntimeTools {
         if (command == null || command.isBlank()) {
             throw new ToolCallException("command is required (start, stop, 
suspend, resume)", null);
         }
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.executeAction(p.pid(), "route", root -> {
-            root.put("id", routeId);
-            root.put("command", command);
-        });
+        // Map the MCP command to the corresponding ToolRegistry tool
+        String toolName = switch (command.toLowerCase()) {
+            case "start" -> "start_route";
+            case "stop" -> "stop_route";
+            case "suspend" -> "suspend_route";
+            case "resume" -> "resume_route";
+            default -> throw new ToolCallException(
+                    "Unknown command: " + command + ". Use start, stop, 
suspend, or resume.", null);
+        };
+        return delegateToRegistry(toolName, nameOrPid, Map.of("routeId", 
routeId));
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = false, 
destructiveHint = true, openWorldHint = false),
@@ -221,16 +254,15 @@ public class RuntimeTools {
         if (endpoint == null || endpoint.isBlank()) {
             throw new ToolCallException("endpoint is required", null);
         }
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.executeAction(p.pid(), "send", root -> {
-            root.put("endpoint", endpoint);
-            if (body != null) {
-                root.put("body", body);
-            }
-            if (headers != null) {
-                root.put("headers", headers);
-            }
-        });
+        Map<String, String> args = new HashMap<>();
+        args.put("endpoint", endpoint);
+        if (body != null) {
+            args.put("body", body);
+        }
+        if (headers != null) {
+            args.put("headers", headers);
+        }
+        return delegateToRegistry("send_message", nameOrPid, args);
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = false, 
destructiveHint = false, openWorldHint = false),
@@ -241,26 +273,14 @@ public class RuntimeTools {
         if (action == null || action.isBlank()) {
             throw new ToolCallException("action is required (enable, disable, 
dump)", null);
         }
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.executeAction(p.pid(), "trace", root -> {
-            switch (action.toLowerCase()) {
-                case "enable" -> root.put("enabled", "true");
-                case "disable" -> root.put("enabled", "false");
-                case "dump" -> root.put("dump", "true");
-                default -> throw new ToolCallException(
-                        "Unknown trace action: " + action
-                                                       + ". Use 'enable', 
'disable', or 'dump'.",
-                        null);
-            }
-        });
+        return delegateToRegistry("trace_control", nameOrPid, Map.of("action", 
action));
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "Show top processor statistics: which processors are 
slowest and most active.")
     public JsonObject camel_runtime_top(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.executeAction(p.pid(), "top-processors", null);
+        return delegateToRegistry("get_top_processors", nameOrPid, Map.of());
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
@@ -277,11 +297,10 @@ public class RuntimeTools {
         if (expression == null || expression.isBlank()) {
             throw new ToolCallException("expression is required", null);
         }
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.executeAction(p.pid(), "eval", root -> {
-            root.put("language", language);
-            root.put("expression", expression);
-        });
+        Map<String, String> args = new HashMap<>();
+        args.put("language", language);
+        args.put("expression", expression);
+        return delegateToRegistry("eval_expression", nameOrPid, args);
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
@@ -292,11 +311,10 @@ public class RuntimeTools {
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid,
             @ToolArg(description = "Include live metrics (message counts, 
throughput) on nodes and edges") Boolean metric,
             @ToolArg(description = "Include external systems (databases, 
messaging brokers, etc.) as nodes") Boolean external) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.executeAction(p.pid(), "route-topology", root -> 
{
-            root.put("metric", metric != null && metric ? "true" : "false");
-            root.put("external", external != null && external ? "true" : 
"false");
-        });
+        Map<String, String> args = new HashMap<>();
+        args.put("metric", metric == null || metric ? "true" : "false");
+        args.put("external", external == null || external ? "true" : "false");
+        return delegateToRegistry("get_route_topology", nameOrPid, args);
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
@@ -305,16 +323,14 @@ public class RuntimeTools {
                   Returns error details including exception, exchange context, 
and route information.""")
     public JsonObject camel_runtime_errors(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.readErrorFile(p.pid());
+        return delegateToRegistry("get_errors", nameOrPid, Map.of());
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "Get a JVM thread dump showing thread names, states, 
and stack traces.")
     public JsonObject camel_runtime_thread_dump(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.executeAction(p.pid(), "thread-dump", null);
+        return delegateToRegistry("get_thread_dump", nameOrPid, Map.of());
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
@@ -323,8 +339,7 @@ public class RuntimeTools {
                   Useful for diagnosing memory leaks and understanding which 
classes dominate heap usage.""")
     public JsonObject camel_runtime_heap_histogram(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.executeAction(p.pid(), "heap-histogram", null);
+        return delegateToRegistry("get_heap_histogram", nameOrPid, Map.of());
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
@@ -334,8 +349,7 @@ public class RuntimeTools {
                   with its route path, processors visited, headers, body, and 
timing.""")
     public JsonObject camel_runtime_history(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.readHistoryFile(p.pid());
+        return delegateToRegistry("get_history", nameOrPid, Map.of());
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = false, 
destructiveHint = true, openWorldHint = false),
@@ -344,11 +358,7 @@ public class RuntimeTools {
                   The application will finish processing in-flight exchanges 
before stopping.""")
     public JsonObject camel_runtime_stop(
             @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid) {
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        String result = runtimeService.stopApplication(p.pid());
-        JsonObject response = new JsonObject();
-        response.put("result", result);
-        return response;
+        return delegateToRegistry("stop_application", nameOrPid, Map.of());
     }
 
     @Tool(annotations = @Tool.Annotations(readOnlyHint = false, 
destructiveHint = false, openWorldHint = false),
@@ -361,6 +371,7 @@ public class RuntimeTools {
         if (endpoint == null || endpoint.isBlank()) {
             throw new ToolCallException("endpoint is required", null);
         }
+        // receive is MCP-only (no ToolRegistry equivalent) — call 
RuntimeService directly
         RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
         return runtimeService.executeAction(p.pid(), "receive", root -> {
             root.put("enabled", "true");
@@ -377,10 +388,11 @@ public class RuntimeTools {
         if (endpoint == null || endpoint.isBlank()) {
             throw new ToolCallException("endpoint is required", null);
         }
-        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
-        return runtimeService.executeAction(p.pid(), "browse", root -> {
-            root.put("endpoint", endpoint);
-            root.put("limit", limit != null ? limit : 50);
-        });
+        Map<String, String> args = new HashMap<>();
+        args.put("endpoint", endpoint);
+        if (limit != null) {
+            args.put("limit", limit.toString());
+        }
+        return delegateToRegistry("browse_endpoint", nameOrPid, args);
     }
 }


Reply via email to