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

davsclaus 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 69601c7e9015 CAMEL-23678: camel-jbang-mcp - Add runtime tools for 
topology, errors, thread-dump, history, stop, and receive (#23730)
69601c7e9015 is described below

commit 69601c7e9015c385b10c57138010d38dc1c4f4cb
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Jun 3 12:48:46 2026 +0200

    CAMEL-23678: camel-jbang-mcp - Add runtime tools for topology, errors, 
thread-dump, history, stop, and receive (#23730)
    
    Signed-off-by: Claus Ibsen <[email protected]>
    Co-authored-by: Claude <[email protected]>
---
 .../camel/dsl/jbang/core/common/RuntimeHelper.java | 20 ++++++
 .../jbang/core/commands/mcp/RuntimeService.java    | 24 +++++++
 .../dsl/jbang/core/commands/mcp/RuntimeTools.java  | 74 ++++++++++++++++++++++
 3 files changed, 118 insertions(+)

diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/RuntimeHelper.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/RuntimeHelper.java
index 94897399442a..94f7585ab1fb 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/RuntimeHelper.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/RuntimeHelper.java
@@ -233,6 +233,26 @@ public final class RuntimeHelper {
         }
     }
 
+    /**
+     * Reads the error file for the given process.
+     *
+     * @return the parsed error data, or {@code null} if the file does not 
exist or is unreadable
+     */
+    public static JsonObject readErrorFile(long pid) {
+        Path errorFile = CommandLineHelper.getCamelDir().resolve(pid + 
"-error.json");
+        return readStatusFromFile(errorFile);
+    }
+
+    /**
+     * Reads the message history file for the given process (trace of the last 
completed exchange).
+     *
+     * @return the parsed history data, or {@code null} if the file does not 
exist or is unreadable
+     */
+    public static JsonObject readHistoryFile(long pid) {
+        Path historyFile = CommandLineHelper.getCamelDir().resolve(pid + 
"-history.json");
+        return readStatusFromFile(historyFile);
+    }
+
     public static JsonObject readStatusFromFile(Path path) {
         try {
             if (Files.exists(path) && path.toFile().length() > 0) {
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/RuntimeService.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/RuntimeService.java
index 7d349268a836..a75f5e63191b 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/RuntimeService.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/RuntimeService.java
@@ -99,6 +99,30 @@ public class RuntimeService {
         return new JsonObject();
     }
 
+    public JsonObject readErrorFile(long pid) {
+        JsonObject result = RuntimeHelper.readErrorFile(pid);
+        if (result == null) {
+            JsonObject empty = new JsonObject();
+            empty.put("errors", new org.apache.camel.util.json.JsonArray());
+            return empty;
+        }
+        return result;
+    }
+
+    public JsonObject readHistoryFile(long pid) {
+        JsonObject result = RuntimeHelper.readHistoryFile(pid);
+        if (result == null) {
+            JsonObject empty = new JsonObject();
+            empty.put("message", "No message history available for PID " + 
pid);
+            return empty;
+        }
+        return result;
+    }
+
+    public String stopApplication(long pid) {
+        return RuntimeHelper.stopApplication(pid);
+    }
+
     public JsonObject executeAction(long pid, String action, 
Consumer<JsonObject> configure) {
         String result = RuntimeHelper.executeAction(pid, action, configure);
         if (result != null && result.startsWith("Timeout")) {
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 7369845a9fa4..2a00858f51d3 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
@@ -284,6 +284,80 @@ public class RuntimeTools {
         });
     }
 
+    @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
+          description = """
+                  Get the inter-route topology showing how routes connect to 
each other \
+                  and to external endpoints. Returns nodes and edges 
describing the route graph.""")
+    public JsonObject camel_runtime_route_topology(
+            @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");
+        });
+    }
+
+    @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
+          description = """
+                  Get captured routing errors from the running Camel 
application. \
+                  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());
+    }
+
+    @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);
+    }
+
+    @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
+          description = """
+                  Get the message history trace of the last completed 
exchange. \
+                  This is always captured (no need to enable tracing) and 
shows the single most recent exchange \
+                  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());
+    }
+
+    @Tool(annotations = @Tool.Annotations(readOnlyHint = false, 
destructiveHint = true, openWorldHint = false),
+          description = """
+                  Initiate graceful shutdown of a running Camel application. \
+                  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;
+    }
+
+    @Tool(annotations = @Tool.Annotations(readOnlyHint = false, 
destructiveHint = false, openWorldHint = false),
+          description = """
+                  Receive (poll) a message from a Camel endpoint in the 
running application. \
+                  This is the complement to camel_runtime_send — it consumes 
one message from the endpoint.""")
+    public JsonObject camel_runtime_receive(
+            @ToolArg(description = NAME_OR_PID_DESC) String nameOrPid,
+            @ToolArg(description = "Endpoint URI to receive from") String 
endpoint) {
+        if (endpoint == null || endpoint.isBlank()) {
+            throw new ToolCallException("endpoint is required", null);
+        }
+        RuntimeService.ProcessInfo p = 
runtimeService.findSingleProcess(nameOrPid);
+        return runtimeService.executeAction(p.pid(), "receive", root -> {
+            root.put("enabled", "true");
+            root.put("endpoint", endpoint);
+        });
+    }
+
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           description = "Browse messages in a Camel endpoint (e.g., browse 
messages queued in a SEDA endpoint).")
     public JsonObject camel_runtime_browse(

Reply via email to