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() {
