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 314c463c55be CAMEL-23424: Add --node-label option to route-diagram for 
description display
314c463c55be is described below

commit 314c463c55bee041cd942a84d9a0edcdf8af7158
Author: Guillaume Nodet <[email protected]>
AuthorDate: Tue May 5 22:18:07 2026 +0200

    CAMEL-23424: Add --node-label option to route-diagram for description 
display
    
    - Add --node-label option with code/description/both modes (default: both)
    - Parse description field from route structure JSON
    - Fix DefaultDumpRoutesStrategy to include description in JSON
    - Expose nodeLabel parameter in MCP tool
---
 .../apache/camel/diagram/RouteDiagramHelper.java   |   1 +
 .../camel/diagram/RouteDiagramLayoutEngine.java    |  48 +++++++-
 .../apache/camel/diagram/RouteDiagramRenderer.java |  19 ++--
 .../org/apache/camel/diagram/RouteDiagramTest.java | 123 ++++++++++++++++++++-
 .../camel/impl/DefaultDumpRoutesStrategy.java      |   3 +
 .../camel-jbang-cmd-route-diagram.adoc             |   1 +
 .../META-INF/camel-jbang-commands-metadata.json    |   2 +-
 .../commands/action/CamelRouteDiagramAction.java   |  27 ++++-
 .../action/CamelRouteDiagramActionTest.java        |  49 ++++++++
 .../jbang/core/commands/mcp/RouteDiagramTools.java |   5 +-
 .../core/commands/mcp/RouteDiagramToolsTest.java   |   6 +-
 11 files changed, 260 insertions(+), 24 deletions(-)

diff --git 
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramHelper.java
 
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramHelper.java
index 8eef000a444c..75dd4faeaa4f 100644
--- 
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramHelper.java
+++ 
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramHelper.java
@@ -60,6 +60,7 @@ public final class RouteDiagramHelper {
                     NodeInfo node = new NodeInfo();
                     node.type = line.getString("type");
                     node.code = Jsoner.unescape(line.getString("code"));
+                    node.description = line.getString("description");
                     Integer level = line.getInteger("level");
                     node.level = level != null ? level : 0;
                     route.nodes.add(node);
diff --git 
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramLayoutEngine.java
 
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramLayoutEngine.java
index 3fe193394239..a139fab5aa52 100644
--- 
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramLayoutEngine.java
+++ 
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramLayoutEngine.java
@@ -38,9 +38,16 @@ public class RouteDiagramLayoutEngine {
     private static final int NODE_TEXT_PADDING = 16 * SCALE;
     private static final int MAX_WRAP_LINES = 3;
 
+    public enum NodeLabelMode {
+        CODE,
+        DESCRIPTION,
+        BOTH
+    }
+
     private final int nodeWidth;
     private final int baseNodeHeight;
     private final FontMetrics fontMetrics;
+    private final NodeLabelMode nodeLabelMode;
 
     private static final Set<String> BRANCHING_EIPS = Set.of(
             "choice", "multicast", "doTry", "loadBalance", "recipientList", 
"circuitBreaker");
@@ -60,8 +67,18 @@ public class RouteDiagramLayoutEngine {
      * @param fontSize font size in logical pixels (before SCALE)
      */
     public RouteDiagramLayoutEngine(int boxWidth, int fontSize) {
+        this(boxWidth, fontSize, NodeLabelMode.CODE);
+    }
+
+    /**
+     * @param boxWidth      logical node width in pixels (before SCALE)
+     * @param fontSize      font size in logical pixels (before SCALE)
+     * @param nodeLabelMode controls what text is displayed in node labels
+     */
+    public RouteDiagramLayoutEngine(int boxWidth, int fontSize, NodeLabelMode 
nodeLabelMode) {
         this.nodeWidth = boxWidth * SCALE;
         this.baseNodeHeight = Math.max(32, fontSize * 32 / 12) * SCALE;
+        this.nodeLabelMode = nodeLabelMode != null ? nodeLabelMode : 
NodeLabelMode.CODE;
         BufferedImage scratch = new BufferedImage(1, 1, 
BufferedImage.TYPE_INT_ARGB);
         Graphics2D g = scratch.createGraphics();
         g.setFont(new Font("SansSerif", Font.PLAIN, fontSize * SCALE));
@@ -80,6 +97,7 @@ public class RouteDiagramLayoutEngine {
     public static class NodeInfo {
         public String type;
         public String code;
+        public String description;
         public int level;
     }
 
@@ -102,7 +120,6 @@ public class RouteDiagramLayoutEngine {
     }
 
     public static class LayoutNode {
-        public String label;
         public String type;
         public int x;
         public int y;
@@ -216,8 +233,7 @@ public class RouteDiagramLayoutEngine {
         return lines;
     }
 
-    int computeNodeHeight(String label) {
-        List<String> lines = wrapLabel(label);
+    int computeNodeHeight(List<String> lines) {
         int lineCount = Math.min(lines.size(), MAX_WRAP_LINES);
         if (lineCount <= 1) {
             return baseNodeHeight;
@@ -226,6 +242,26 @@ public class RouteDiagramLayoutEngine {
         return baseNodeHeight + (lineCount - 1) * lineSpacing;
     }
 
+    List<String> resolveLabel(NodeInfo info) {
+        return resolveLabel(info, nodeLabelMode);
+    }
+
+    public static List<String> resolveLabel(NodeInfo info, NodeLabelMode mode) 
{
+        String code = cleanLabel(info.code);
+        boolean hasDesc = info.description != null && 
!info.description.isBlank();
+        switch (mode) {
+            case DESCRIPTION:
+                return List.of(hasDesc ? info.description : code);
+            case BOTH:
+                if (hasDesc && !info.description.equals(code)) {
+                    return List.of(info.description, code);
+                }
+                return List.of(code);
+            default:
+                return List.of(code);
+        }
+    }
+
     public LayoutRoute layoutRoute(RouteInfo route, int startY) {
         LayoutRoute lr = new LayoutRoute();
         lr.routeId = route.routeId;
@@ -296,12 +332,12 @@ public class RouteDiagramLayoutEngine {
         int nodeX = x + (availableWidth - nodeWidth) / 2;
 
         LayoutNode ln = new LayoutNode();
-        ln.label = cleanLabel(node.info.code);
         ln.type = node.info.type;
         ln.x = nodeX;
         ln.y = y;
-        ln.wrappedLines = wrapLabel(ln.label);
-        ln.height = computeNodeHeight(ln.label);
+        ln.wrappedLines = resolveLabel(node.info).stream()
+                .flatMap(s -> wrapLabel(s).stream()).toList();
+        ln.height = computeNodeHeight(ln.wrappedLines);
         ln.treeNode = node;
         node.layoutNode = ln;
         lr.nodes.add(ln);
diff --git 
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramRenderer.java
 
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramRenderer.java
index 84bf0d358e23..2a04dfec5396 100644
--- 
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramRenderer.java
+++ 
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramRenderer.java
@@ -307,9 +307,6 @@ public class RouteDiagramRenderer {
         FontMetrics fm = g.getFontMetrics();
 
         List<String> lines = node.wrappedLines;
-        if (lines == null || lines.isEmpty()) {
-            lines = List.of(node.label);
-        }
 
         int lineHeight = fm.getHeight();
         int totalTextHeight = lines.size() * lineHeight;
@@ -379,6 +376,10 @@ public class RouteDiagramRenderer {
     }
 
     public List<String> printTextDiagram(List<RouteInfo> routes) {
+        return printTextDiagram(routes, 
RouteDiagramLayoutEngine.NodeLabelMode.CODE);
+    }
+
+    public List<String> printTextDiagram(List<RouteInfo> routes, 
RouteDiagramLayoutEngine.NodeLabelMode mode) {
         List<String> lines = new ArrayList<>();
         for (RouteInfo route : routes) {
             lines.add("");
@@ -390,14 +391,16 @@ public class RouteDiagramRenderer {
 
             TreeNode tree = RouteDiagramLayoutEngine.buildTree(route.nodes);
             if (tree != null) {
-                printTreeNode(tree, "", true, true, lines);
+                printTreeNode(tree, "", true, true, lines, mode);
             }
             lines.add("");
         }
         return lines;
     }
 
-    private void printTreeNode(TreeNode node, String prefix, boolean isLast, 
boolean isRoot, List<String> lines) {
+    private void printTreeNode(
+            TreeNode node, String prefix, boolean isLast, boolean isRoot, 
List<String> lines,
+            RouteDiagramLayoutEngine.NodeLabelMode mode) {
         String connector;
         if (isRoot) {
             connector = "  ";
@@ -408,9 +411,9 @@ public class RouteDiagramRenderer {
         }
 
         String typeTag = node.info.type != null ? "[" + node.info.type + "] " 
: "";
-        String code = RouteDiagramLayoutEngine.cleanLabel(node.info.code);
+        String label = String.join(" | ", 
RouteDiagramLayoutEngine.resolveLabel(node.info, mode));
 
-        lines.add(prefix + connector + typeTag + code);
+        lines.add(prefix + connector + typeTag + label);
 
         String childPrefix;
         if (isRoot) {
@@ -422,7 +425,7 @@ public class RouteDiagramRenderer {
         }
 
         for (int i = 0; i < node.children.size(); i++) {
-            printTreeNode(node.children.get(i), childPrefix, i == 
node.children.size() - 1, false, lines);
+            printTreeNode(node.children.get(i), childPrefix, i == 
node.children.size() - 1, false, lines, mode);
         }
     }
 }
diff --git 
a/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java
 
b/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java
index ae16b66aca0b..5c75685598fa 100644
--- 
a/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java
+++ 
b/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java
@@ -410,7 +410,7 @@ class RouteDiagramTest {
         LayoutRoute lr = engine.layoutRoute(route, 
RouteDiagramLayoutEngine.PADDING);
 
         LayoutNode logAfter = lr.nodes.get(lr.nodes.size() - 1);
-        assertEquals("log[Do other processing...]", logAfter.label);
+        assertEquals("log[Do other processing...]", String.join("", 
logAfter.wrappedLines));
         assertTrue(logAfter.connectFromMerge, "Should connect from doTry merge 
point");
 
         LayoutNode choiceNode = lr.nodes.stream()
@@ -437,7 +437,7 @@ class RouteDiagramTest {
         assertEquals(5, lr.nodes.size());
 
         LayoutNode toNode = lr.nodes.get(4);
-        assertEquals("to[log:after]", toNode.label);
+        assertEquals("to[log:after]", String.join("", toNode.wrappedLines));
         assertTrue(toNode.connectFromMerge, "Node after filter should connect 
from merge point");
 
         LayoutNode filterNode = lr.nodes.get(2);
@@ -593,10 +593,129 @@ class RouteDiagramTest {
         assertTrue(image.getHeight() > 0);
     }
 
+    @Test
+    void testNodeLabelModeDescription() {
+        RouteInfo route = new RouteInfo();
+        route.routeId = "route1";
+        route.nodes.add(node("from", "timer:tick?period=1000", 0, "Poll every 
second"));
+        route.nodes.add(node("to", "log:a", 1, null));
+
+        RouteDiagramLayoutEngine engine
+                = new RouteDiagramLayoutEngine(180, 12, 
RouteDiagramLayoutEngine.NodeLabelMode.DESCRIPTION);
+        LayoutRoute lr = engine.layoutRoute(route, 
RouteDiagramLayoutEngine.PADDING);
+
+        assertEquals("Poll every second", String.join("", 
lr.nodes.get(0).wrappedLines));
+        assertEquals("log:a", String.join("", lr.nodes.get(1).wrappedLines));
+    }
+
+    @Test
+    void testNodeLabelModeDescriptionFallsBackToCode() {
+        RouteInfo route = new RouteInfo();
+        route.routeId = "route1";
+        route.nodes.add(node("from", "timer:tick", 0, null));
+
+        RouteDiagramLayoutEngine engine
+                = new RouteDiagramLayoutEngine(180, 12, 
RouteDiagramLayoutEngine.NodeLabelMode.DESCRIPTION);
+        LayoutRoute lr = engine.layoutRoute(route, 
RouteDiagramLayoutEngine.PADDING);
+
+        assertEquals("timer:tick", String.join("", 
lr.nodes.get(0).wrappedLines));
+    }
+
+    @Test
+    void testNodeLabelModeBoth() {
+        RouteInfo route = new RouteInfo();
+        route.routeId = "route1";
+        route.nodes.add(node("from", "timer:tick?period=1000", 0, "Poll every 
second"));
+        route.nodes.add(node("to", "log:a", 1, null));
+
+        RouteDiagramLayoutEngine engine
+                = new RouteDiagramLayoutEngine(180, 12, 
RouteDiagramLayoutEngine.NodeLabelMode.BOTH);
+        LayoutRoute lr = engine.layoutRoute(route, 
RouteDiagramLayoutEngine.PADDING);
+
+        List<String> fromLines = lr.nodes.get(0).wrappedLines;
+        assertTrue(fromLines.size() >= 2, "Both mode should produce at least 2 
lines when description differs from code");
+        String allFromText = String.join("", fromLines);
+        assertTrue(allFromText.contains("Poll every second"));
+        assertTrue(allFromText.contains("timer:tick"));
+
+        assertEquals("log:a", String.join("", lr.nodes.get(1).wrappedLines));
+    }
+
+    @Test
+    void testNodeLabelModeCodeDefault() {
+        RouteInfo route = new RouteInfo();
+        route.routeId = "route1";
+        route.nodes.add(node("from", "timer:tick", 0, "Poll every second"));
+
+        RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine();
+        LayoutRoute lr = engine.layoutRoute(route, 
RouteDiagramLayoutEngine.PADDING);
+
+        assertEquals("timer:tick", String.join("", 
lr.nodes.get(0).wrappedLines));
+    }
+
+    @Test
+    void testTextDiagramWithDescriptionMode() {
+        RouteInfo route = new RouteInfo();
+        route.routeId = "route1";
+        route.nodes.add(node("from", "timer:tick?period=1000", 0, "Poll every 
second"));
+        route.nodes.add(node("to", "log:a", 1, null));
+
+        RouteDiagramRenderer renderer = new RouteDiagramRenderer();
+        List<String> lines
+                = renderer.printTextDiagram(List.of(route), 
RouteDiagramLayoutEngine.NodeLabelMode.DESCRIPTION);
+
+        String output = String.join("\n", lines);
+        assertTrue(output.contains("Poll every second"));
+        assertTrue(output.contains("log:a"));
+    }
+
+    @Test
+    void testTextDiagramWithBothMode() {
+        RouteInfo route = new RouteInfo();
+        route.routeId = "route1";
+        route.nodes.add(node("from", "timer:tick", 0, "Poll every second"));
+        route.nodes.add(node("to", "log:a", 1, null));
+
+        RouteDiagramRenderer renderer = new RouteDiagramRenderer();
+        List<String> lines = renderer.printTextDiagram(List.of(route), 
RouteDiagramLayoutEngine.NodeLabelMode.BOTH);
+
+        String output = String.join("\n", lines);
+        assertTrue(output.contains("Poll every second"));
+        assertTrue(output.contains("timer:tick"));
+        assertTrue(output.contains("log:a"));
+    }
+
+    @Test
+    void testRenderDiagramWithDescriptionMode() {
+        System.setProperty("java.awt.headless", "true");
+
+        RouteInfo route = new RouteInfo();
+        route.routeId = "route1";
+        route.nodes.add(node("from", "timer:tick?period=1000", 0, "Poll every 
second"));
+        route.nodes.add(node("to", "log:a", 1, null));
+
+        RouteDiagramLayoutEngine engine
+                = new RouteDiagramLayoutEngine(180, 12, 
RouteDiagramLayoutEngine.NodeLabelMode.DESCRIPTION);
+        LayoutRoute lr = engine.layoutRoute(route, 
RouteDiagramLayoutEngine.PADDING);
+
+        RouteDiagramRenderer renderer = new RouteDiagramRenderer();
+        DiagramColors colors = DiagramColors.parse("dark");
+        BufferedImage image = renderer.renderDiagram(List.of(lr), lr.maxY + 
RouteDiagramLayoutEngine.V_GAP, colors);
+
+        assertNotNull(image);
+        assertTrue(image.getWidth() > 0);
+        assertTrue(image.getHeight() > 0);
+    }
+
     private static NodeInfo node(String type, String code, int level) {
+        return node(type, code, level, null);
+    }
+
+    private static NodeInfo node(String type, String code, int level, String 
description) {
         NodeInfo n = new NodeInfo();
         n.type = type;
         n.code = code;
+        n.description = description;
         n.level = level;
         return n;
     }
diff --git 
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultDumpRoutesStrategy.java
 
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultDumpRoutesStrategy.java
index aa0642a404b4..a380537ba119 100644
--- 
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultDumpRoutesStrategy.java
+++ 
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultDumpRoutesStrategy.java
@@ -448,6 +448,9 @@ public class DefaultDumpRoutesStrategy extends 
ServiceSupport implements DumpRou
             c.put("type", line.type());
             c.put("id", line.id());
             c.put("level", line.level());
+            if (line.description() != null) {
+                c.put("description", line.description());
+            }
             c.put("code", Jsoner.escape(line.code()));
             code.add(c);
         }
diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-route-diagram.adoc
 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-route-diagram.adoc
index 1909b47dc09f..5741b65b6c0a 100644
--- 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-route-diagram.adoc
+++ 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-route-diagram.adoc
@@ -23,6 +23,7 @@ camel cmd route-diagram [options]
 | `--filter` | Filter route by filename or route id |  | String
 | `--font-size` | Font size in logical pixels for node text | 12 | int
 | `--ignore-loading-error` | Whether to ignore route loading and compilation 
errors (use this with care!) | false | boolean
+| `--node-label` | What text to display in diagram nodes: code, description, 
or both (default) | both | String
 | `--output` | Save diagram to a PNG file instead of displaying in terminal |  
| String
 | `--theme,--colors` | Color theme preset (dark, light, transparent) or custom 
colors (e.g. bg=#1e1e1e:from=#2e7d32:to=#1565c0). Values can be #hex or ANSI 
color names (e.g. from=seagreen:to=steelblue). Use bg= for transparent. Can 
also be set via DIAGRAM_COLORS env var. | dark | String
 | `--width` | Image width in pixels (0 = auto) | 0 | int
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
 
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
index 43700abf34c1..86ff873fd444 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
@@ -2,7 +2,7 @@
   "commands": [
     { "name": "bind", "fullName": "bind", "description": "DEPRECATED: Bind 
source and sink Kamelets as a new Camel integration", "deprecated": true, 
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.bind.Bind", "options": 
[ { "names": "--error-handler", "description": "Add error handler 
(none|log|sink:<endpoint>). Sink endpoints are expected in the format 
[[apigroup\/]version:]kind:[namespace\/]name, plain Camel URIs or Kamelet 
name.", "javaType": "java.lang.String", "type": "stri [...]
     { "name": "catalog", "fullName": "catalog", "description": "List artifacts 
from Camel Catalog", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.catalog.CatalogCommand", "options": [ 
{ "names": "-h,--help", "description": "Display the help and sub-commands", 
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": 
"component", "fullName": "catalog component", "description": "List components 
from the Camel Catalog", "sourceClass": "org.apache.camel.dsl.jbang.co [...]
-    { "name": "cmd", "fullName": "cmd", "description": "Performs commands in 
the running Camel integrations, such as start\/stop route, or change logging 
levels.", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.action.CamelAction", "options": [ { 
"names": "-h,--help", "description": "Display the help and sub-commands", 
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": 
"browse", "fullName": "cmd browse", "description": "Browse pending messages on 
endpoints [...]
+    { "name": "cmd", "fullName": "cmd", "description": "Performs commands in 
the running Camel integrations, such as start\/stop route, or change logging 
levels.", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.action.CamelAction", "options": [ { 
"names": "-h,--help", "description": "Display the help and sub-commands", 
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": 
"browse", "fullName": "cmd browse", "description": "Browse pending messages on 
endpoints [...]
     { "name": "completion", "fullName": "completion", "description": "Generate 
completion script for bash\/zsh", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.Complete", "options": [ { "names": 
"-h,--help", "description": "Display the help and sub-commands", "javaType": 
"boolean", "type": "boolean" } ] },
     { "name": "config", "fullName": "config", "description": "Get and set user 
configuration values", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.config.ConfigCommand", "options": [ { 
"names": "-h,--help", "description": "Display the help and sub-commands", 
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "get", 
"fullName": "config get", "description": "Display user configuration value", 
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.config. [...]
     { "name": "debug", "fullName": "debug", "description": "Debug local Camel 
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Debug", 
"options": [ { "names": "--ago", "description": "Use ago instead of yyyy-MM-dd 
HH:mm:ss in timestamp.", "javaType": "boolean", "type": "boolean" }, { "names": 
"--background", "description": "Run in the background", "defaultValue": 
"false", "javaType": "boolean", "type": "boolean" }, { "names": 
"--background-wait", "description": "To  [...]
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
index 269cfdc25917..3b84256441bc 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
@@ -29,6 +29,7 @@ import javax.imageio.ImageIO;
 import org.apache.camel.diagram.RouteDiagramHelper;
 import org.apache.camel.diagram.RouteDiagramLayoutEngine;
 import org.apache.camel.diagram.RouteDiagramLayoutEngine.LayoutRoute;
+import org.apache.camel.diagram.RouteDiagramLayoutEngine.NodeLabelMode;
 import org.apache.camel.diagram.RouteDiagramLayoutEngine.RouteInfo;
 import org.apache.camel.diagram.RouteDiagramRenderer;
 import org.apache.camel.diagram.RouteDiagramRenderer.DiagramColors;
@@ -82,6 +83,11 @@ public class CamelRouteDiagramAction extends 
ActionBaseCommand {
                         description = "Node box width in logical pixels", 
defaultValue = "180")
     int boxWidth;
 
+    @CommandLine.Option(names = { "--node-label" },
+                        description = "What text to display in diagram nodes: 
code, description, or both (default)",
+                        defaultValue = "both")
+    String nodeLabel;
+
     @CommandLine.Option(names = { "--ignore-loading-error" }, defaultValue = 
"false",
                         description = "Whether to ignore route loading and 
compilation errors (use this with care!)")
     boolean ignoreLoadingError;
@@ -109,7 +115,7 @@ public class CamelRouteDiagramAction extends 
ActionBaseCommand {
     public Integer renderSourceToFile(
             String sourceFile, String outputFile, String theme, String filter,
             int width, boolean ignoreLoadingError,
-            int fontSize, int boxWidth)
+            int fontSize, int boxWidth, String nodeLabel)
             throws Exception {
         this.name = sourceFile;
         this.output = outputFile;
@@ -121,6 +127,9 @@ public class CamelRouteDiagramAction extends 
ActionBaseCommand {
         this.ignoreLoadingError = ignoreLoadingError;
         this.fontSize = fontSize;
         this.boxWidth = boxWidth;
+        if (nodeLabel != null && !nodeLabel.isBlank()) {
+            this.nodeLabel = nodeLabel;
+        }
         return doCall();
     }
 
@@ -179,7 +188,8 @@ public class CamelRouteDiagramAction extends 
ActionBaseCommand {
                 return 0;
             }
 
-            RouteDiagramLayoutEngine engine = new 
RouteDiagramLayoutEngine(boxWidth, fontSize);
+            NodeLabelMode labelMode = parseNodeLabelMode(nodeLabel);
+            RouteDiagramLayoutEngine engine = new 
RouteDiagramLayoutEngine(boxWidth, fontSize, labelMode);
             RouteDiagramRenderer renderer = new RouteDiagramRenderer(
                     engine.getNodeWidth(), fontSize * 
RouteDiagramLayoutEngine.SCALE);
 
@@ -225,7 +235,7 @@ public class CamelRouteDiagramAction extends 
ActionBaseCommand {
                                     "Terminal does not support graphics 
protocols (Kitty, iTerm2, or Sixel).");
                             printer().println(
                                     "Try running in a supported terminal: 
Kitty, iTerm2, WezTerm, Ghostty, or VS Code.");
-                            for (String line : 
renderer.printTextDiagram(routes)) {
+                            for (String line : 
renderer.printTextDiagram(routes, labelMode)) {
                                 printer().println(line);
                             }
                         }
@@ -293,4 +303,15 @@ public class CamelRouteDiagramAction extends 
ActionBaseCommand {
     List<RouteInfo> parseRoutes(JsonObject jo) {
         return RouteDiagramHelper.parseRoutes(jo);
     }
+
+    static NodeLabelMode parseNodeLabelMode(String value) {
+        if (value == null || value.isBlank() || 
"code".equalsIgnoreCase(value)) {
+            return NodeLabelMode.CODE;
+        } else if ("description".equalsIgnoreCase(value)) {
+            return NodeLabelMode.DESCRIPTION;
+        } else if ("both".equalsIgnoreCase(value)) {
+            return NodeLabelMode.BOTH;
+        }
+        return NodeLabelMode.CODE;
+    }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramActionTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramActionTest.java
index aa83ac006945..008e9b8f08d5 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramActionTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramActionTest.java
@@ -18,10 +18,12 @@ package org.apache.camel.dsl.jbang.core.commands.action;
 
 import java.util.List;
 
+import org.apache.camel.diagram.RouteDiagramLayoutEngine.NodeLabelMode;
 import org.apache.camel.diagram.RouteDiagramLayoutEngine.RouteInfo;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 class CamelRouteDiagramActionTest {
@@ -69,4 +71,51 @@ class CamelRouteDiagramActionTest {
         assertEquals("from", routes.get(0).nodes.get(0).type);
         assertEquals("timer:tick", routes.get(0).nodes.get(0).code);
     }
+
+    @Test
+    void testParseRoutesWithDescription() {
+        org.apache.camel.util.json.JsonObject line1 = new 
org.apache.camel.util.json.JsonObject();
+        line1.put("type", "from");
+        line1.put("code", "timer:tick?period=1000");
+        line1.put("description", "Poll every second");
+        line1.put("level", 0);
+
+        org.apache.camel.util.json.JsonObject line2 = new 
org.apache.camel.util.json.JsonObject();
+        line2.put("type", "to");
+        line2.put("code", "log:a");
+        line2.put("level", 1);
+
+        org.apache.camel.util.json.JsonArray code = new 
org.apache.camel.util.json.JsonArray();
+        code.add(line1);
+        code.add(line2);
+
+        org.apache.camel.util.json.JsonObject routeObj = new 
org.apache.camel.util.json.JsonObject();
+        routeObj.put("routeId", "route1");
+        routeObj.put("code", code);
+
+        org.apache.camel.util.json.JsonArray routesArr = new 
org.apache.camel.util.json.JsonArray();
+        routesArr.add(routeObj);
+
+        org.apache.camel.util.json.JsonObject jo = new 
org.apache.camel.util.json.JsonObject();
+        jo.put("routes", routesArr);
+
+        CamelRouteDiagramAction action = new CamelRouteDiagramAction(null);
+        List<RouteInfo> routes = action.parseRoutes(jo);
+
+        assertEquals("Poll every second", 
routes.get(0).nodes.get(0).description);
+        assertNull(routes.get(0).nodes.get(1).description);
+    }
+
+    @Test
+    void testParseNodeLabelMode() {
+        assertEquals(NodeLabelMode.CODE, 
CamelRouteDiagramAction.parseNodeLabelMode(null));
+        assertEquals(NodeLabelMode.CODE, 
CamelRouteDiagramAction.parseNodeLabelMode(""));
+        assertEquals(NodeLabelMode.CODE, 
CamelRouteDiagramAction.parseNodeLabelMode("code"));
+        assertEquals(NodeLabelMode.CODE, 
CamelRouteDiagramAction.parseNodeLabelMode("CODE"));
+        assertEquals(NodeLabelMode.DESCRIPTION, 
CamelRouteDiagramAction.parseNodeLabelMode("description"));
+        assertEquals(NodeLabelMode.DESCRIPTION, 
CamelRouteDiagramAction.parseNodeLabelMode("Description"));
+        assertEquals(NodeLabelMode.BOTH, 
CamelRouteDiagramAction.parseNodeLabelMode("both"));
+        assertEquals(NodeLabelMode.BOTH, 
CamelRouteDiagramAction.parseNodeLabelMode("BOTH"));
+        assertEquals(NodeLabelMode.CODE, 
CamelRouteDiagramAction.parseNodeLabelMode("invalid"));
+    }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/RouteDiagramTools.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/RouteDiagramTools.java
index 5dab71e25f65..452ee8892eaf 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/RouteDiagramTools.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/RouteDiagramTools.java
@@ -51,6 +51,8 @@ public class RouteDiagramTools {
             @ToolArg(description = "Image width in pixels; 0 (or unset) = 
auto") Integer width,
             @ToolArg(description = "Font size in logical pixels for node text 
(default 12)") Integer fontSize,
             @ToolArg(description = "Node box width in logical pixels (default 
180)") Integer boxWidth,
+            @ToolArg(description = "What text to display in diagram nodes: 
'code' (default), 'description' (prefer "
+                                   + "description over code if available), or 
'both' (show description and code)") String nodeLabel,
             @ToolArg(description = "Whether to ignore route loading and 
compilation errors (use with care)") Boolean ignoreLoadingError) {
 
         if (sourceFile == null || sourceFile.isBlank()) {
@@ -88,7 +90,8 @@ public class RouteDiagramTools {
                     width != null ? width : 0,
                     ignoreLoadingError != null && ignoreLoadingError,
                     fontSize != null ? fontSize : 12,
-                    boxWidth != null ? boxWidth : 180);
+                    boxWidth != null ? boxWidth : 180,
+                    nodeLabel);
 
             File out = new File(resolvedOutput);
             boolean success = exit == 0 && out.isFile() && out.length() > 0;
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/RouteDiagramToolsTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/RouteDiagramToolsTest.java
index 1554ace299fe..fcceec06a600 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/RouteDiagramToolsTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/RouteDiagramToolsTest.java
@@ -27,7 +27,7 @@ class RouteDiagramToolsTest {
     void missingSourceFileThrows() {
         RouteDiagramTools tools = new RouteDiagramTools();
 
-        assertThatThrownBy(() -> tools.camel_render_route_diagram(null, null, 
null, null, null, null, null, null))
+        assertThatThrownBy(() -> tools.camel_render_route_diagram(null, null, 
null, null, null, null, null, null, null))
                 .isInstanceOf(ToolCallException.class)
                 .hasMessageContaining("sourceFile");
     }
@@ -36,7 +36,7 @@ class RouteDiagramToolsTest {
     void blankSourceFileThrows() {
         RouteDiagramTools tools = new RouteDiagramTools();
 
-        assertThatThrownBy(() -> tools.camel_render_route_diagram("   ", null, 
null, null, null, null, null, null))
+        assertThatThrownBy(() -> tools.camel_render_route_diagram("   ", null, 
null, null, null, null, null, null, null))
                 .isInstanceOf(ToolCallException.class)
                 .hasMessageContaining("sourceFile");
     }
@@ -46,7 +46,7 @@ class RouteDiagramToolsTest {
         RouteDiagramTools tools = new RouteDiagramTools();
 
         assertThatThrownBy(() -> tools.camel_render_route_diagram(
-                "/no/such/file.yaml", null, null, null, null, null, null, 
null))
+                "/no/such/file.yaml", null, null, null, null, null, null, 
null, null))
                 .isInstanceOf(ToolCallException.class)
                 .hasMessageContaining("does not exist");
     }

Reply via email to