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

davsclaus pushed a commit to branch fix/CAMEL-25076
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 3f40009208779b7fbea7e5753d48ace56981bb65
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Sep 28 09:33:38 2026 +0200

    CAMEL-25076: camel-mcp-server - stdio: adding a tool must not fail when 
stdin is closed
    
    McpSyncServer.addTool registers the tool and then notifies the client that
    the tool list changed. When stdin has ended (the client went away) the SDK
    has closed the outbound side of the session, so the notification fails with
    "Failed to enqueue message" and that failed CamelContext startup or the 
added
    route. The tool is registered either way, so a failed notification is now
    logged as a WARN. Invalid tools still fail.
    
    StdioMcpServerEngineTest uses an empty stdin and raced with this.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../mcp/server/stdio/StdioMcpServerEngine.java        | 17 +++++++++++++++--
 .../mcp/server/stdio/StdioMcpServerEngineTest.java    | 19 +++++++++++++++++++
 2 files changed, 34 insertions(+), 2 deletions(-)

diff --git 
a/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerEngine.java
 
b/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerEngine.java
index 15cd4e7dccab..6db97cf5f055 100644
--- 
a/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerEngine.java
+++ 
b/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerEngine.java
@@ -115,7 +115,7 @@ public class StdioMcpServerEngine extends ServiceSupport 
implements McpServerEng
                 .immediateExecution(true)
                 .build();
         for (McpServerFeatures.SyncToolSpecification spec : pendingTools) {
-            server.addTool(spec);
+            addTool(spec);
         }
         pendingTools.clear();
         LOG.info("MCP server '{}' serving ai-tool routes over stdio", 
info.serverName());
@@ -141,10 +141,23 @@ public class StdioMcpServerEngine extends ServiceSupport 
implements McpServerEng
             LOG.debug("MCP tool queued until stdio server starts: {}", 
tool.name());
             return;
         }
-        server.addTool(spec);
+        addTool(spec);
         LOG.debug("MCP tool added: {}", tool.name());
     }
 
+    private void addTool(McpServerFeatures.SyncToolSpecification spec) {
+        try {
+            server.addTool(spec);
+        } catch (IllegalArgumentException | IllegalStateException e) {
+            // an invalid tool (such as its input schema)
+            throw e;
+        } catch (RuntimeException e) {
+            // the tool is registered before the connected client is notified 
that the tool list changed, and that
+            // notification fails when stdin is closed (the client went away); 
the tool must not fail the route
+            LOG.warn("MCP tool {} added, but the client could not be notified: 
{}", spec.tool().name(), e.getMessage());
+        }
+    }
+
     @Override
     public void toolRemoved(String toolName) {
         removePendingTool(toolName);
diff --git 
a/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerEngineTest.java
 
b/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerEngineTest.java
index 4d7c61b21926..371774e52cbf 100644
--- 
a/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerEngineTest.java
+++ 
b/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerEngineTest.java
@@ -90,6 +90,25 @@ class StdioMcpServerEngineTest extends CamelTestSupport {
         assertThatCode(() -> 
engine.toolRemoved("ephemeral")).doesNotThrowAnyException();
     }
 
+    @Test
+    void toolAddedAfterStdinClosedDoesNotFail() throws Exception {
+        StdioMcpServerEngine engine = new StdioMcpServerEngine();
+        engine.initialize(new 
org.apache.camel.component.mcp.server.McpServerInfo(
+                "test", "1.0", "/mcp", 0, 0, null, null, null, null, null));
+        // an empty stdin is a client that went away: the session closes and 
notifications can no longer be sent
+        engine.setTransportStreams(new ByteArrayInputStream(new byte[0]), new 
ByteArrayOutputStream());
+        engine.start();
+        try {
+            for (int i = 0; i < 20; i++) {
+                String name = "late_tool_" + i;
+                assertThatCode(() -> engine.toolAdded(sampleTool(name, "Added 
after stdin closed")))
+                        .doesNotThrowAnyException();
+            }
+        } finally {
+            engine.stop();
+        }
+    }
+
     private static McpServerTool sampleTool(String name, String description) {
         McpToolCallHandler handler = arguments -> new McpToolCallResult("ok", 
false);
         return new McpServerTool() {

Reply via email to