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 963b900a0f9c CAMEL-24352: MCP stdio transport for ai-tool routes in 
Camel Main/JBang (#26146)
963b900a0f9c is described below

commit 963b900a0f9c0df061f0b717b7d8d92aa38b29f3
Author: Omar Atie <[email protected]>
AuthorDate: Tue Sep 8 11:57:30 2026 -0700

    CAMEL-24352: MCP stdio transport for ai-tool routes in Camel Main/JBang 
(#26146)
    
    Follow-up to CAMEL-24308 (HTTP MCP): expose tagged ai-tool routes over the
    MCP stdio transport so local IDEs and agent subprocesses can consume them.
    
    Adds StdioMcpServerEngine backed by the MCP Java SDK 
StdioServerTransportProvider,
    a camel.server.mcp-transport property (http by default, stdio for 
subprocess mode),
    and wires DefaultMcpServerFactory to bind the stdio engine when stdio is 
selected.
    Tools registered before the server starts are queued and drained on start, 
so
    registrations arriving from McpServerBridge are never dropped.
    
    On the CLI side, Camel JBang gains --mcp-stdio and --mcp-tags, mutually 
exclusive
    with the dev/diagnostics --mcp flag; --mcp-stdio overrides profile HTTP 
settings and
    --mcp-tags applies to the ai-tool MCP server on both transports. Log4j2 is
    initialized to stderr before JBang logging setup and QuietPrinter errors 
are routed
    to stderr, keeping stdout reserved for the JSON-RPC protocol stream.
    
    Includes documentation, catalog regeneration, unit tests for the pre-start
    tool lifecycle, and an integration test driving the official MCP stdio 
client.
    
    Closes #26146
    
    Co-authored-by: Cursor <[email protected]>
---
 .../org/apache/camel/catalog/docs/main.adoc        |   5 +-
 .../org/apache/camel/catalog/docs/mcp-server.adoc  | 142 +++++++++----
 .../main/camel-main-configuration-metadata.json    |   3 +-
 .../component/mcp/server/McpServerConstants.java   |  10 +
 .../camel-mcp-server/src/main/docs/mcp-server.adoc | 142 +++++++++----
 .../mcp/server/main/DefaultMcpServerFactory.java   |  11 +
 .../mcp/server/stdio/StdioMcpServerEngine.java     | 222 +++++++++++++++++++++
 .../src/main/resources/log4j2-mcp-stdio.properties |  38 ++++
 .../server/main/DefaultMcpServerFactoryTest.java   |  63 ++++++
 .../server/main/McpServerMainPropertiesTest.java   |  37 +++-
 .../mcp/server/stdio/McpStdioHarnessMain.java      |  55 +++++
 .../mcp/server/stdio/StdioMcpServerEngineTest.java | 132 ++++++++++++
 .../mcp/server/stdio/StdioMcpServerProcessIT.java  | 113 +++++++++++
 ...ttpServerConfigurationPropertiesConfigurer.java |   7 +
 .../camel-main-configuration-metadata.json         |   3 +-
 core/camel-main/src/main/docs/main.adoc            |   5 +-
 .../main/HttpServerConfigurationProperties.java    |  32 ++-
 .../pages/jbang-commands/camel-jbang-debug.adoc    |   2 +
 .../ROOT/pages/jbang-commands/camel-jbang-dev.adoc |   2 +
 .../ROOT/pages/jbang-commands/camel-jbang-run.adoc |   2 +
 .../META-INF/camel-jbang-commands-metadata.json    |   6 +-
 .../apache/camel/dsl/jbang/core/commands/Run.java  |  76 ++++++-
 .../camel/dsl/jbang/core/common/Printer.java       |   3 +-
 .../camel/dsl/jbang/core/common/RuntimeUtil.java   |   9 +
 .../src/main/resources/log4j2-mcp-stdio.properties |  38 ++++
 .../jbang/core/commands/RunMcpStdioOptionTest.java | 101 ++++++++++
 26 files changed, 1166 insertions(+), 93 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/main.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/main.adoc
index 6418250b5c3e..fbb30dd12cd3 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/main.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/main.adoc
@@ -195,7 +195,7 @@ The camel.routecontroller supports 12 options, which are 
listed below.
 
 
 === Camel Embedded HTTP Server (only for standalone; not Spring Boot or 
Quarkus) configurations
-The camel.server supports 34 options, which are listed below.
+The camel.server supports 35 options, which are listed below.
 
 [width="100%",cols="2,5,^1,2",options="header"]
 |===
@@ -215,7 +215,7 @@ The camel.server supports 34 options, which are listed 
below.
 | *camel.server.jwtKeystorePath* | Path to the keystore file used for JWT 
tokens validation. |  | String
 | *camel.server.jwtKeystoreType* | Type of the keystore used for JWT tokens 
validation (jks, pkcs12, etc.). |  | String
 | *camel.server.maxBodySize* | Maximum HTTP body size the embedded HTTP server 
can accept. |  | Long
-| *camel.server.mcpEnabled* | Whether to expose ai-tool routes as MCP tools 
and ai-resource routes as MCP resources over streamable HTTP. Requires 
camel-mcp-server on the classpath. By default, the MCP server is not enabled. | 
false | boolean
+| *camel.server.mcpEnabled* | Whether to expose ai-tool routes as MCP tools 
and ai-resource routes as MCP resources. Requires camel-mcp-server on the 
classpath. Use setMcpTransport(String) to choose streamable HTTP (default) or 
stdio for IDE subprocess integration. By default, the MCP server is not 
enabled. | false | boolean
 | *camel.server.mcpInstructions* | Top-level MCP instructions returned to 
clients on initialize. |  | String
 | *camel.server.mcpPath* | HTTP path where the MCP endpoint is served. | /mcp 
| String
 | *camel.server.mcpResourceTimeout* | Per-read MCP resource execution timeout 
in milliseconds. A read exceeding the timeout returns an error to the MCP 
client; the underlying route keeps running until it completes on its own. | 
20000 | long
@@ -228,6 +228,7 @@ The camel.server supports 34 options, which are listed 
below.
 | *camel.server.mcpSessionKeepAliveInterval* | Keep-alive ping interval in 
milliseconds for MCP sessions on the Vert.x streamable transport. Dead sessions 
are evicted after consecutive ping failures. 0 disables keep-alive pings. | 
30000 | long
 | *camel.server.mcpTags* | Comma-separated list of tag patterns selecting the 
ai-tool routes to expose as MCP tools and the ai-resource routes to expose as 
MCP resources. Matching is case-insensitive and supports exact match, wildcard 
prefix ( {code foo} ), and {code } to match all tags. Only routes registered 
under a matching tag are exposed; the untagged default pools are never exposed. 
When not set, nothing is exposed. |  | String
 | *camel.server.mcpToolTimeout* | Per-call MCP tool execution timeout in 
milliseconds. A call exceeding the timeout returns an error result to the MCP 
client; the underlying route keeps running until it completes on its own. | 
20000 | long
+| *camel.server.mcpTransport* | MCP transport for ai-tool routes: http 
(streamable HTTP on the embedded server, default) or stdio (JSON-RPC on process 
stdin/stdout for local agent subprocesses). | http | String
 | *camel.server.path* | Context-path to use for embedded HTTP server | / | 
String
 | *camel.server.port* | Port to use for binding embedded HTTP server. Use 0 to 
dynamic assign a free random port number. | 8080 | int
 | *camel.server.staticContextPath* | The context-path to use for serving 
static content. By default, the root path is used. And if there is an 
index.html page then this is automatically loaded. | / | String
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mcp-server.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mcp-server.adoc
index fdbd5c15fc34..78ed283ed140 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mcp-server.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mcp-server.adoc
@@ -13,10 +13,10 @@ The camel-mcp-server module exposes Camel routes registered 
via the
 xref:ROOT:ai-tool-component.adoc[ai-tool] and 
xref:ROOT:ai-resource-component.adoc[ai-resource]
 components as tools and resources of a
 https://modelcontextprotocol.io[Model Context Protocol] (MCP) server, served
-over MCP streamable HTTP. No route is needed for the server itself: add the
-dependency, configure which tags to expose, and every matching `ai-tool` route
-becomes an MCP tool and every matching `ai-resource` route an MCP resource,
-discoverable by any MCP client (another Camel application, an IDE, a coding
+over MCP streamable HTTP or stdio (stdin/stdout JSON-RPC). No route is needed 
for the
+server itself: add the dependency, configure which tags to expose, and every 
matching
+`ai-tool` route becomes an MCP tool and every matching `ai-resource` route an 
MCP
+resource, discoverable by any MCP client (another Camel application, an IDE, a 
coding
 agent).
 
 Maven users will need to add the following dependency to their `pom.xml`:
@@ -41,10 +41,11 @@ The module is split in two artifacts:
   sanitization) and reacts to `AiToolRegistry` and `AiResourceRegistry` changes
   when routes start and stop. It has no dependency on the MCP Java SDK.
 * `camel-mcp-server` — the serving engine for Camel Main and Camel JBang,
-  built on the official MCP Java SDK with a Vert.x streamable HTTP transport.
-  The MCP endpoint is registered on the Camel main HTTP server's router, so it
-  serves on the main server port (`camel.server.port`) and inherits its
-  lifecycle, authentication and CORS configuration.
+  built on the official MCP Java SDK. Streamable HTTP uses a Vert.x transport
+  registered on the Camel main HTTP server's router (port `camel.server.port`,
+  lifecycle, authentication and CORS). Stdio uses the MCP Java SDK
+  `StdioServerTransportProvider` so a local IDE or agent can launch the Camel
+  process as a subprocess with MCP JSON-RPC on stdin/stdout.
 
 Engine resolution mirrors the platform-http engine: a bean of type
 `McpServerEngine` in the Camel registry wins; otherwise the engine is
@@ -145,7 +146,9 @@ 
xref:ROOT:ai-tool-component.adoc#_mcp_tool_annotation_hints[MCP Tool Annotation
 for configuration examples. Hints are advisory UX metadata only — not 
authorization.
 
 On Camel Main and Camel JBang no code is needed — like Jolokia or
-Prometheus, the server starts from configuration properties alone:
+Prometheus, the server starts from configuration properties alone.
+
+=== Streamable HTTP (default)
 
 [source,properties]
 ----
@@ -163,22 +166,6 @@ camel.server.mcp-server-icons = 
[{"src":"https://example.com/icon.png","mimeType
 Tag patterns support wildcards: use `*` to expose all tagged tools, or a
 prefix pattern like `crm*` to expose all tags starting with `crm`:
 
-On other runtimes, or when wiring programmatically, add the
-`McpServerBridge` service to the CamelContext instead:
-
-[source,java]
-----
-McpServerConfiguration configuration = new McpServerConfiguration();
-configuration.setTags("crm,notify");
-configuration.setServerName("my-integration-app");
-configuration.setServerTitle("My Integration MCP");
-configuration.setServerDescription("Tools for customer operations");
-configuration.setServerWebsiteUrl("https://example.com/docs";);
-configuration.setInstructions("Use these tools to operate the integration.");
-configuration.setServerIcons(List.of(new 
McpServerIcon("https://example.com/icon.png";, "image/png", List.of("48x48"), 
"light")));
-camelContext.addService(new McpServerBridge(configuration));
-----
-
 The MCP endpoint is then served at `http://<host>:<port>/mcp` on the Camel
 main HTTP server. Any MCP client can connect over streamable HTTP, for
 example another Camel integration using the
@@ -194,6 +181,51 @@ from("direct:agent")
         + "&mcpServer.myCamelTools.url=http://localhost:8080/mcp";);
 ----
 
+=== Stdio (local IDE / agent subprocess)
+
+For local IDE or agent integration, serve MCP over process stdin/stdout instead
+of HTTP. Set `camel.server.mcp-transport=stdio` (or use the Camel JBang
+`--mcp-stdio` flag). Stdio mode does not require the Camel main HTTP server.
+
+[source,properties]
+----
+camel.server.mcp-enabled = true
+camel.server.mcp-transport = stdio
+camel.server.mcp-tags = crm,agent
+camel.server.mcp-server-name = my-camel-tools
+----
+
+With Camel JBang:
+
+[source,shell]
+----
+camel run tools.yaml --mcp-stdio --mcp-tags=crm,agent
+----
+
+NOTE: `--mcp-tags` configures the ai-tool MCP server (`camel.server.mcp-tags`) 
and works with
+`--mcp-stdio` or HTTP transport when `camel.server.mcp-enabled` is set in 
properties. It is
+not used by `--mcp`, which exposes dev/diagnostics MCP tools on the management 
HTTP server.
+
+Logging and startup summaries are routed to stderr so stdout carries MCP
+JSON-RPC only. Camel JBang configures a stderr Log4j2 appender automatically
+when `--mcp-stdio` is used or when `camel.server.mcp-transport=stdio` is set
+in the profile. On Camel Main, configure logging to stderr yourself (for
+example using the `log4j2-mcp-stdio.properties` resource shipped in
+`camel-mcp-server`). Configure your MCP client (for example in `mcp.json`) to 
launch
+the Camel process as a subprocess:
+
+[source,json]
+----
+{
+  "mcpServers": {
+    "my-camel-tools": {
+      "command": "camel",
+      "args": ["run", "tools.yaml", "--mcp-stdio", "--mcp-tags=crm,agent"]
+    }
+  }
+}
+----
+
 == Options
 
 The options, configurable as `camel.server.mcp-*` properties on Camel Main /
@@ -205,7 +237,10 @@ JBang (see the xref:main.adoc[camel-main] options) or on
 | Option | Description | Default | Owner
 
 | `camel.server.mcp-enabled` | Whether to expose ai-tool routes as MCP tools
-  and ai-resource routes as MCP resources over streamable HTTP. | `false` | 
bridge
+  and ai-resource routes as MCP resources. | `false` | bridge
+| `camel.server.mcp-transport` | MCP transport: `http` (streamable HTTP on the
+  embedded server, default) or `stdio` (JSON-RPC on process stdin/stdout for
+  local agent subprocesses). Stdio exposes tools only. | `http` | engine
 | `camel.server.mcp-tags` | Comma-separated list of tag patterns selecting the
   ai-tool and ai-resource routes to expose. Patterns support exact match,
   wildcard prefix (`foo*`), and `*` to match all tags. Only routes registered
@@ -245,17 +280,37 @@ JBang (see the xref:main.adoc[camel-main] options) or on
 |===
 
 Bridge-owned options are honored identically on every runtime. Engine-owned
-options are consumed by the Vert.x engine only; on runtimes with a native
-engine (Quarkus, Spring Boot) the native configuration decides serving
-concerns and a startup WARN is logged when an ignored option is set.
+options are consumed by the Vert.x HTTP engine or the stdio engine on Camel
+Main/JBang; on runtimes with a native engine (Quarkus, Spring Boot) the native
+configuration decides serving concerns and a startup WARN is logged when an
+ignored option is set.
+
+On other runtimes, or when wiring programmatically, add the
+`McpServerBridge` service to the CamelContext instead:
+
+[source,java]
+----
+McpServerConfiguration configuration = new McpServerConfiguration();
+configuration.setTags("crm,notify");
+configuration.setServerName("my-integration-app");
+configuration.setServerTitle("My Integration MCP");
+configuration.setServerDescription("Tools for customer operations");
+configuration.setServerWebsiteUrl("https://example.com/docs";);
+configuration.setInstructions("Use these tools to operate the integration.");
+configuration.setServerIcons(List.of(new McpServerIcon(
+        "https://example.com/icon.png";, "image/png", List.of("48x48"), 
"light")));
+camelContext.addService(new McpServerBridge(configuration));
+----
 
 == Protocol
 
-This section describes the Vert.x engine shipped in `camel-mcp-server`, which
-serves on Camel Main and Camel JBang. On Quarkus and Spring Boot the transport
-is owned by the native engine instead — quarkus-mcp-server and the Spring Boot
-embedded HTTP server (Spring AI MCP server) respectively — and the details
-below do not apply.
+This section describes the engines shipped in `camel-mcp-server` for Camel
+Main and Camel JBang. On Quarkus and Spring Boot the transport is owned by
+the native engine instead — quarkus-mcp-server and the Spring Boot embedded
+HTTP server (Spring AI MCP server) respectively — and the details below do not
+apply.
+
+=== Streamable HTTP (Vert.x engine)
 
 The Vert.x engine implements the MCP streamable HTTP transport:
 
@@ -277,6 +332,14 @@ emit `notifications/tools/list_changed` and
 subscriptions (`resources/subscribe`) are not offered: the server advertises
 `resources` with `listChanged` only.
 
+=== Stdio engine
+
+The stdio engine uses the MCP Java SDK `StdioServerTransportProvider`. MCP
+JSON-RPC is read from stdin and written to stdout. Logging and startup banners
+must not appear on stdout — Camel Main/JBang configure Log4j2 to write logs to
+stderr in stdio mode. No HTTP port is opened and no session management headers
+apply.
+
 == Security
 
 External MCP clients are *untrusted senders* under the
@@ -297,21 +360,28 @@ following rules:
   read to the `resourceTimeout`. Note that a timed-out route keeps running
   server-side until it completes; the timeout bounds the MCP request, not the
   route.
-* *Authentication*: the MCP endpoint is served through the main HTTP server
+* *Authentication (HTTP)*: the MCP endpoint is served through the main HTTP 
server
   router, so platform-http authentication (basic, JWT via
   `camel.server.authentication*` options) applies to it. The MCP
   specification's authorization model is OAuth 2.1; see
   xref:oauth.adoc[camel-oauth] for resource-server style
   protection. On Quarkus and Spring Boot, authentication is owned by the
   native runtime security.
+* *Authentication (stdio)*: stdio mode has no HTTP transport and therefore no
+  HTTP authentication layer. Trust is the parent process that launched the
+  Camel subprocess (local IDE or agent). Route authors remain fully trusted;
+  only explicitly tagged tools are exposed.
 
 == Runtime notes
 
-* *Camel Main / JBang*: requires the Camel main HTTP server
+* *Camel Main / JBang (HTTP)*: requires the Camel main HTTP server
   (`camel.server.enabled=true` with `camel-platform-http-main`, automatic
   with Camel JBang) or a `VertxPlatformHttpServer` service. Serving is fully
   asynchronous: tool calls are offloaded to the Vert.x worker pool and the
   long-lived SSE channel does not occupy a worker thread.
+* *Camel Main / JBang (stdio)*: set `camel.server.mcp-transport=stdio` or use
+  `--mcp-stdio`. Does not require the HTTP server. Distinct from the `--mcp`
+  JBang flag, which exposes dev/diagnostics tools over HTTP management.
 * *Quarkus*: use the `camel-quarkus-mcp-server` extension (serves through
   quarkus-mcp-server; the MCP Java SDK is not on the classpath).
 * *Spring Boot*: use the `camel-mcp-server-starter` (serves through the
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
index 18b928ed1d37..b2548a1b6a2e 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
@@ -442,7 +442,7 @@
     { "name": "camel.server.jwtKeystorePath", "required": false, 
"description": "Path to the keystore file used for JWT tokens validation.", 
"sourceType": "org.apache.camel.main.HttpServerConfigurationProperties", 
"type": "string", "javaType": "java.lang.String", "secret": false },
     { "name": "camel.server.jwtKeystoreType", "required": false, 
"description": "Type of the keystore used for JWT tokens validation (jks, 
pkcs12, etc.).", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string", 
"javaType": "java.lang.String", "secret": false },
     { "name": "camel.server.maxBodySize", "required": false, "description": 
"Maximum HTTP body size the embedded HTTP server can accept.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "integer", 
"javaType": "java.lang.Long", "secret": false },
-    { "name": "camel.server.mcpEnabled", "required": false, "description": 
"Whether to expose ai-tool routes as MCP tools and ai-resource routes as MCP 
resources over streamable HTTP. Requires camel-mcp-server on the classpath. By 
default, the MCP server is not enabled.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "boolean", 
"javaType": "boolean", "defaultValue": false, "secret": false },
+    { "name": "camel.server.mcpEnabled", "required": false, "description": 
"Whether to expose ai-tool routes as MCP tools and ai-resource routes as MCP 
resources. Requires camel-mcp-server on the classpath. Use 
setMcpTransport(String) to choose streamable HTTP (default) or stdio for IDE 
subprocess integration. By default, the MCP server is not enabled.", 
"sourceType": "org.apache.camel.main.HttpServerConfigurationProperties", 
"type": "boolean", "javaType": "boolean", "defaultValue": fals [...]
     { "name": "camel.server.mcpInstructions", "required": false, 
"description": "Top-level MCP instructions returned to clients on initialize.", 
"sourceType": "org.apache.camel.main.HttpServerConfigurationProperties", 
"type": "string", "javaType": "java.lang.String", "secret": false },
     { "name": "camel.server.mcpPath", "required": false, "description": "HTTP 
path where the MCP endpoint is served.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string", 
"javaType": "java.lang.String", "defaultValue": "\/mcp", "secret": false },
     { "name": "camel.server.mcpResourceTimeout", "required": false, 
"description": "Per-read MCP resource execution timeout in milliseconds. A read 
exceeding the timeout returns an error to the MCP client; the underlying route 
keeps running until it completes on its own.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "integer", 
"javaType": "long", "defaultValue": 20000, "secret": false },
@@ -455,6 +455,7 @@
     { "name": "camel.server.mcpSessionKeepAliveInterval", "required": false, 
"description": "Keep-alive ping interval in milliseconds for MCP sessions on 
the Vert.x streamable transport. Dead sessions are evicted after consecutive 
ping failures. 0 disables keep-alive pings.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "integer", 
"javaType": "long", "defaultValue": 30000, "secret": false },
     { "name": "camel.server.mcpTags", "required": false, "description": 
"Comma-separated list of tag patterns selecting the ai-tool routes to expose as 
MCP tools and the ai-resource routes to expose as MCP resources. Matching is 
case-insensitive and supports exact match, wildcard prefix ( {code foo} ), and 
{code } to match all tags. Only routes registered under a matching tag are 
exposed; the untagged default pools are never exposed. When not set, nothing is 
exposed.", "sourceType": "org [...]
     { "name": "camel.server.mcpToolTimeout", "required": false, "description": 
"Per-call MCP tool execution timeout in milliseconds. A call exceeding the 
timeout returns an error result to the MCP client; the underlying route keeps 
running until it completes on its own.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "integer", 
"javaType": "long", "defaultValue": 20000, "secret": false },
+    { "name": "camel.server.mcpTransport", "required": false, "description": 
"MCP transport for ai-tool routes: http (streamable HTTP on the embedded 
server, default) or stdio (JSON-RPC on process stdin\/stdout for local agent 
subprocesses).", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "enum", 
"javaType": "java.lang.String", "defaultValue": "http", "secret": false, 
"enum": [ "http", "stdio" ] },
     { "name": "camel.server.path", "required": false, "description": 
"Context-path to use for embedded HTTP server", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string", 
"javaType": "java.lang.String", "defaultValue": "\/", "secret": false },
     { "name": "camel.server.port", "required": false, "description": "Port to 
use for binding embedded HTTP server. Use 0 to dynamic assign a free random 
port number.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "integer", 
"javaType": "int", "defaultValue": 8080, "secret": false },
     { "name": "camel.server.staticContextPath", "required": false, 
"description": "The context-path to use for serving static content. By default, 
the root path is used. And if there is an index.html page then this is 
automatically loaded.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string", 
"javaType": "java.lang.String", "defaultValue": "\/", "secret": false },
diff --git 
a/components/camel-ai/camel-mcp-server-api/src/main/java/org/apache/camel/component/mcp/server/McpServerConstants.java
 
b/components/camel-ai/camel-mcp-server-api/src/main/java/org/apache/camel/component/mcp/server/McpServerConstants.java
index 2e4a162cb9c8..f5382d1826c7 100644
--- 
a/components/camel-ai/camel-mcp-server-api/src/main/java/org/apache/camel/component/mcp/server/McpServerConstants.java
+++ 
b/components/camel-ai/camel-mcp-server-api/src/main/java/org/apache/camel/component/mcp/server/McpServerConstants.java
@@ -34,6 +34,16 @@ public final class McpServerConstants {
      */
     public static final String DEFAULT_PATH = "/mcp";
 
+    /**
+     * MCP transport served over streamable HTTP (default).
+     */
+    public static final String TRANSPORT_HTTP = "http";
+
+    /**
+     * MCP transport served over process stdin/stdout (JSON-RPC).
+     */
+    public static final String TRANSPORT_STDIO = "stdio";
+
     /**
      * Default per-call tool execution timeout in milliseconds.
      */
diff --git a/components/camel-ai/camel-mcp-server/src/main/docs/mcp-server.adoc 
b/components/camel-ai/camel-mcp-server/src/main/docs/mcp-server.adoc
index fdbd5c15fc34..78ed283ed140 100644
--- a/components/camel-ai/camel-mcp-server/src/main/docs/mcp-server.adoc
+++ b/components/camel-ai/camel-mcp-server/src/main/docs/mcp-server.adoc
@@ -13,10 +13,10 @@ The camel-mcp-server module exposes Camel routes registered 
via the
 xref:ROOT:ai-tool-component.adoc[ai-tool] and 
xref:ROOT:ai-resource-component.adoc[ai-resource]
 components as tools and resources of a
 https://modelcontextprotocol.io[Model Context Protocol] (MCP) server, served
-over MCP streamable HTTP. No route is needed for the server itself: add the
-dependency, configure which tags to expose, and every matching `ai-tool` route
-becomes an MCP tool and every matching `ai-resource` route an MCP resource,
-discoverable by any MCP client (another Camel application, an IDE, a coding
+over MCP streamable HTTP or stdio (stdin/stdout JSON-RPC). No route is needed 
for the
+server itself: add the dependency, configure which tags to expose, and every 
matching
+`ai-tool` route becomes an MCP tool and every matching `ai-resource` route an 
MCP
+resource, discoverable by any MCP client (another Camel application, an IDE, a 
coding
 agent).
 
 Maven users will need to add the following dependency to their `pom.xml`:
@@ -41,10 +41,11 @@ The module is split in two artifacts:
   sanitization) and reacts to `AiToolRegistry` and `AiResourceRegistry` changes
   when routes start and stop. It has no dependency on the MCP Java SDK.
 * `camel-mcp-server` — the serving engine for Camel Main and Camel JBang,
-  built on the official MCP Java SDK with a Vert.x streamable HTTP transport.
-  The MCP endpoint is registered on the Camel main HTTP server's router, so it
-  serves on the main server port (`camel.server.port`) and inherits its
-  lifecycle, authentication and CORS configuration.
+  built on the official MCP Java SDK. Streamable HTTP uses a Vert.x transport
+  registered on the Camel main HTTP server's router (port `camel.server.port`,
+  lifecycle, authentication and CORS). Stdio uses the MCP Java SDK
+  `StdioServerTransportProvider` so a local IDE or agent can launch the Camel
+  process as a subprocess with MCP JSON-RPC on stdin/stdout.
 
 Engine resolution mirrors the platform-http engine: a bean of type
 `McpServerEngine` in the Camel registry wins; otherwise the engine is
@@ -145,7 +146,9 @@ 
xref:ROOT:ai-tool-component.adoc#_mcp_tool_annotation_hints[MCP Tool Annotation
 for configuration examples. Hints are advisory UX metadata only — not 
authorization.
 
 On Camel Main and Camel JBang no code is needed — like Jolokia or
-Prometheus, the server starts from configuration properties alone:
+Prometheus, the server starts from configuration properties alone.
+
+=== Streamable HTTP (default)
 
 [source,properties]
 ----
@@ -163,22 +166,6 @@ camel.server.mcp-server-icons = 
[{"src":"https://example.com/icon.png","mimeType
 Tag patterns support wildcards: use `*` to expose all tagged tools, or a
 prefix pattern like `crm*` to expose all tags starting with `crm`:
 
-On other runtimes, or when wiring programmatically, add the
-`McpServerBridge` service to the CamelContext instead:
-
-[source,java]
-----
-McpServerConfiguration configuration = new McpServerConfiguration();
-configuration.setTags("crm,notify");
-configuration.setServerName("my-integration-app");
-configuration.setServerTitle("My Integration MCP");
-configuration.setServerDescription("Tools for customer operations");
-configuration.setServerWebsiteUrl("https://example.com/docs";);
-configuration.setInstructions("Use these tools to operate the integration.");
-configuration.setServerIcons(List.of(new 
McpServerIcon("https://example.com/icon.png";, "image/png", List.of("48x48"), 
"light")));
-camelContext.addService(new McpServerBridge(configuration));
-----
-
 The MCP endpoint is then served at `http://<host>:<port>/mcp` on the Camel
 main HTTP server. Any MCP client can connect over streamable HTTP, for
 example another Camel integration using the
@@ -194,6 +181,51 @@ from("direct:agent")
         + "&mcpServer.myCamelTools.url=http://localhost:8080/mcp";);
 ----
 
+=== Stdio (local IDE / agent subprocess)
+
+For local IDE or agent integration, serve MCP over process stdin/stdout instead
+of HTTP. Set `camel.server.mcp-transport=stdio` (or use the Camel JBang
+`--mcp-stdio` flag). Stdio mode does not require the Camel main HTTP server.
+
+[source,properties]
+----
+camel.server.mcp-enabled = true
+camel.server.mcp-transport = stdio
+camel.server.mcp-tags = crm,agent
+camel.server.mcp-server-name = my-camel-tools
+----
+
+With Camel JBang:
+
+[source,shell]
+----
+camel run tools.yaml --mcp-stdio --mcp-tags=crm,agent
+----
+
+NOTE: `--mcp-tags` configures the ai-tool MCP server (`camel.server.mcp-tags`) 
and works with
+`--mcp-stdio` or HTTP transport when `camel.server.mcp-enabled` is set in 
properties. It is
+not used by `--mcp`, which exposes dev/diagnostics MCP tools on the management 
HTTP server.
+
+Logging and startup summaries are routed to stderr so stdout carries MCP
+JSON-RPC only. Camel JBang configures a stderr Log4j2 appender automatically
+when `--mcp-stdio` is used or when `camel.server.mcp-transport=stdio` is set
+in the profile. On Camel Main, configure logging to stderr yourself (for
+example using the `log4j2-mcp-stdio.properties` resource shipped in
+`camel-mcp-server`). Configure your MCP client (for example in `mcp.json`) to 
launch
+the Camel process as a subprocess:
+
+[source,json]
+----
+{
+  "mcpServers": {
+    "my-camel-tools": {
+      "command": "camel",
+      "args": ["run", "tools.yaml", "--mcp-stdio", "--mcp-tags=crm,agent"]
+    }
+  }
+}
+----
+
 == Options
 
 The options, configurable as `camel.server.mcp-*` properties on Camel Main /
@@ -205,7 +237,10 @@ JBang (see the xref:main.adoc[camel-main] options) or on
 | Option | Description | Default | Owner
 
 | `camel.server.mcp-enabled` | Whether to expose ai-tool routes as MCP tools
-  and ai-resource routes as MCP resources over streamable HTTP. | `false` | 
bridge
+  and ai-resource routes as MCP resources. | `false` | bridge
+| `camel.server.mcp-transport` | MCP transport: `http` (streamable HTTP on the
+  embedded server, default) or `stdio` (JSON-RPC on process stdin/stdout for
+  local agent subprocesses). Stdio exposes tools only. | `http` | engine
 | `camel.server.mcp-tags` | Comma-separated list of tag patterns selecting the
   ai-tool and ai-resource routes to expose. Patterns support exact match,
   wildcard prefix (`foo*`), and `*` to match all tags. Only routes registered
@@ -245,17 +280,37 @@ JBang (see the xref:main.adoc[camel-main] options) or on
 |===
 
 Bridge-owned options are honored identically on every runtime. Engine-owned
-options are consumed by the Vert.x engine only; on runtimes with a native
-engine (Quarkus, Spring Boot) the native configuration decides serving
-concerns and a startup WARN is logged when an ignored option is set.
+options are consumed by the Vert.x HTTP engine or the stdio engine on Camel
+Main/JBang; on runtimes with a native engine (Quarkus, Spring Boot) the native
+configuration decides serving concerns and a startup WARN is logged when an
+ignored option is set.
+
+On other runtimes, or when wiring programmatically, add the
+`McpServerBridge` service to the CamelContext instead:
+
+[source,java]
+----
+McpServerConfiguration configuration = new McpServerConfiguration();
+configuration.setTags("crm,notify");
+configuration.setServerName("my-integration-app");
+configuration.setServerTitle("My Integration MCP");
+configuration.setServerDescription("Tools for customer operations");
+configuration.setServerWebsiteUrl("https://example.com/docs";);
+configuration.setInstructions("Use these tools to operate the integration.");
+configuration.setServerIcons(List.of(new McpServerIcon(
+        "https://example.com/icon.png";, "image/png", List.of("48x48"), 
"light")));
+camelContext.addService(new McpServerBridge(configuration));
+----
 
 == Protocol
 
-This section describes the Vert.x engine shipped in `camel-mcp-server`, which
-serves on Camel Main and Camel JBang. On Quarkus and Spring Boot the transport
-is owned by the native engine instead — quarkus-mcp-server and the Spring Boot
-embedded HTTP server (Spring AI MCP server) respectively — and the details
-below do not apply.
+This section describes the engines shipped in `camel-mcp-server` for Camel
+Main and Camel JBang. On Quarkus and Spring Boot the transport is owned by
+the native engine instead — quarkus-mcp-server and the Spring Boot embedded
+HTTP server (Spring AI MCP server) respectively — and the details below do not
+apply.
+
+=== Streamable HTTP (Vert.x engine)
 
 The Vert.x engine implements the MCP streamable HTTP transport:
 
@@ -277,6 +332,14 @@ emit `notifications/tools/list_changed` and
 subscriptions (`resources/subscribe`) are not offered: the server advertises
 `resources` with `listChanged` only.
 
+=== Stdio engine
+
+The stdio engine uses the MCP Java SDK `StdioServerTransportProvider`. MCP
+JSON-RPC is read from stdin and written to stdout. Logging and startup banners
+must not appear on stdout — Camel Main/JBang configure Log4j2 to write logs to
+stderr in stdio mode. No HTTP port is opened and no session management headers
+apply.
+
 == Security
 
 External MCP clients are *untrusted senders* under the
@@ -297,21 +360,28 @@ following rules:
   read to the `resourceTimeout`. Note that a timed-out route keeps running
   server-side until it completes; the timeout bounds the MCP request, not the
   route.
-* *Authentication*: the MCP endpoint is served through the main HTTP server
+* *Authentication (HTTP)*: the MCP endpoint is served through the main HTTP 
server
   router, so platform-http authentication (basic, JWT via
   `camel.server.authentication*` options) applies to it. The MCP
   specification's authorization model is OAuth 2.1; see
   xref:oauth.adoc[camel-oauth] for resource-server style
   protection. On Quarkus and Spring Boot, authentication is owned by the
   native runtime security.
+* *Authentication (stdio)*: stdio mode has no HTTP transport and therefore no
+  HTTP authentication layer. Trust is the parent process that launched the
+  Camel subprocess (local IDE or agent). Route authors remain fully trusted;
+  only explicitly tagged tools are exposed.
 
 == Runtime notes
 
-* *Camel Main / JBang*: requires the Camel main HTTP server
+* *Camel Main / JBang (HTTP)*: requires the Camel main HTTP server
   (`camel.server.enabled=true` with `camel-platform-http-main`, automatic
   with Camel JBang) or a `VertxPlatformHttpServer` service. Serving is fully
   asynchronous: tool calls are offloaded to the Vert.x worker pool and the
   long-lived SSE channel does not occupy a worker thread.
+* *Camel Main / JBang (stdio)*: set `camel.server.mcp-transport=stdio` or use
+  `--mcp-stdio`. Does not require the HTTP server. Distinct from the `--mcp`
+  JBang flag, which exposes dev/diagnostics tools over HTTP management.
 * *Quarkus*: use the `camel-quarkus-mcp-server` extension (serves through
   quarkus-mcp-server; the MCP Java SDK is not on the classpath).
 * *Spring Boot*: use the `camel-mcp-server-starter` (serves through the
diff --git 
a/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/main/DefaultMcpServerFactory.java
 
b/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/main/DefaultMcpServerFactory.java
index a12bf1ed5881..e3272be303d7 100644
--- 
a/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/main/DefaultMcpServerFactory.java
+++ 
b/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/main/DefaultMcpServerFactory.java
@@ -20,6 +20,9 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.Service;
 import org.apache.camel.component.mcp.server.McpServerBridge;
 import org.apache.camel.component.mcp.server.McpServerConfiguration;
+import org.apache.camel.component.mcp.server.McpServerConstants;
+import org.apache.camel.component.mcp.server.McpServerEngine;
+import org.apache.camel.component.mcp.server.stdio.StdioMcpServerEngine;
 import org.apache.camel.main.HttpServerConfigurationProperties;
 import org.apache.camel.main.MainConstants;
 import org.apache.camel.main.McpServerFactory;
@@ -34,6 +37,10 @@ public class DefaultMcpServerFactory implements 
McpServerFactory {
 
     @Override
     public Service newMcpServer(CamelContext camelContext, 
HttpServerConfigurationProperties configuration) {
+        if (isStdioTransport(configuration.getMcpTransport())
+                && 
camelContext.getRegistry().findSingleByType(McpServerEngine.class) == null) {
+            camelContext.getRegistry().bind("mcpServerEngine", new 
StdioMcpServerEngine());
+        }
         McpServerConfiguration mcpConfiguration = new McpServerConfiguration();
         mcpConfiguration.setTags(configuration.getMcpTags());
         mcpConfiguration.setToolTimeout(configuration.getMcpToolTimeout());
@@ -49,4 +56,8 @@ public class DefaultMcpServerFactory implements 
McpServerFactory {
         
mcpConfiguration.setSessionIdleTtl(configuration.getMcpSessionIdleTtl());
         return new McpServerBridge(mcpConfiguration);
     }
+
+    static boolean isStdioTransport(String transport) {
+        return transport != null && 
McpServerConstants.TRANSPORT_STDIO.equalsIgnoreCase(transport.trim());
+    }
 }
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
new file mode 100644
index 000000000000..15cd4e7dccab
--- /dev/null
+++ 
b/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerEngine.java
@@ -0,0 +1,222 @@
+/*
+ * 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.component.mcp.server.stdio;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import io.modelcontextprotocol.json.McpJsonDefaults;
+import io.modelcontextprotocol.json.McpJsonMapper;
+import io.modelcontextprotocol.server.McpServer;
+import io.modelcontextprotocol.server.McpServerFeatures;
+import io.modelcontextprotocol.server.McpSyncServer;
+import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
+import io.modelcontextprotocol.spec.McpSchema;
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.ai.tool.AiToolAnnotations;
+import org.apache.camel.component.mcp.server.McpServerEngine;
+import org.apache.camel.component.mcp.server.McpServerInfo;
+import org.apache.camel.component.mcp.server.McpServerTool;
+import org.apache.camel.component.mcp.server.McpToolCallResult;
+import org.apache.camel.support.service.ServiceSupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link McpServerEngine} for Camel Main / JBang: serves MCP over the process 
stdio transport using the official MCP
+ * Java SDK. Intended for IDE and local agent integration where the parent 
process launches {@code camel run} as a child
+ * and speaks JSON-RPC on stdin/stdout. In this mode stdout must carry only 
MCP protocol frames — callers should route
+ * logging and startup banners to stderr.
+ *
+ * @since 4.23
+ */
+public class StdioMcpServerEngine extends ServiceSupport implements 
McpServerEngine {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(StdioMcpServerEngine.class);
+
+    private static final String EMPTY_OBJECT_SCHEMA = """
+            {
+              "type": "object",
+              "properties": {},
+              "additionalProperties": false
+            }
+            """;
+
+    private CamelContext camelContext;
+    private McpServerInfo info;
+    private McpJsonMapper jsonMapper;
+    private StdioServerTransportProvider transport;
+    private McpSyncServer server;
+    private InputStream inputStream;
+    private OutputStream outputStream;
+    private final List<McpServerFeatures.SyncToolSpecification> pendingTools = 
new ArrayList<>();
+
+    @Override
+    public CamelContext getCamelContext() {
+        return camelContext;
+    }
+
+    @Override
+    public void setCamelContext(CamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
+    @Override
+    public void initialize(McpServerInfo info) {
+        this.info = info;
+        this.jsonMapper = McpJsonDefaults.getMapper();
+    }
+
+    /**
+     * Optional test hook: when both streams are set, the engine uses them 
instead of {@link System#in} and
+     * {@link System#out}.
+     */
+    public void setTransportStreams(InputStream inputStream, OutputStream 
outputStream) {
+        this.inputStream = inputStream;
+        this.outputStream = outputStream;
+    }
+
+    @Override
+    public boolean consumesServingConfiguration() {
+        return true;
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        if (jsonMapper == null) {
+            jsonMapper = McpJsonDefaults.getMapper();
+        }
+        if (inputStream != null && outputStream != null) {
+            transport = new StdioServerTransportProvider(jsonMapper, 
inputStream, outputStream);
+        } else {
+            transport = new StdioServerTransportProvider(jsonMapper);
+        }
+        server = McpServer.sync(transport)
+                .serverInfo(info.serverName(), info.version())
+                
.capabilities(McpSchema.ServerCapabilities.builder().tools(true).build())
+                .immediateExecution(true)
+                .build();
+        for (McpServerFeatures.SyncToolSpecification spec : pendingTools) {
+            server.addTool(spec);
+        }
+        pendingTools.clear();
+        LOG.info("MCP server '{}' serving ai-tool routes over stdio", 
info.serverName());
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        if (server != null) {
+            // closeGracefully() shuts down the MCP sync server and its stdio 
transport provider
+            // (including custom stdin/stdout streams when setTransportStreams 
was used).
+            server.closeGracefully();
+            server = null;
+        }
+        pendingTools.clear();
+        transport = null;
+    }
+
+    @Override
+    public void toolAdded(McpServerTool tool) {
+        McpServerFeatures.SyncToolSpecification spec = 
buildToolSpecification(tool);
+        if (server == null) {
+            pendingTools.add(spec);
+            LOG.debug("MCP tool queued until stdio server starts: {}", 
tool.name());
+            return;
+        }
+        server.addTool(spec);
+        LOG.debug("MCP tool added: {}", tool.name());
+    }
+
+    @Override
+    public void toolRemoved(String toolName) {
+        removePendingTool(toolName);
+        if (server == null) {
+            return;
+        }
+        try {
+            server.removeTool(toolName);
+            LOG.debug("MCP tool removed: {}", toolName);
+        } catch (Exception e) {
+            LOG.debug("Failed to remove MCP tool {}: {}", toolName, 
e.getMessage());
+        }
+    }
+
+    private void removePendingTool(String toolName) {
+        for (Iterator<McpServerFeatures.SyncToolSpecification> iterator = 
pendingTools.iterator(); iterator.hasNext();) {
+            if (toolName.equals(iterator.next().tool().name())) {
+                iterator.remove();
+            }
+        }
+    }
+
+    private McpServerFeatures.SyncToolSpecification 
buildToolSpecification(McpServerTool tool) {
+        McpSchema.Tool mcpTool = buildMcpTool(tool);
+        return McpServerFeatures.SyncToolSpecification.builder()
+                .tool(mcpTool)
+                .callHandler((exchange, request) -> {
+                    Map<String, Object> arguments = request.arguments() != 
null ? request.arguments() : Map.of();
+                    McpToolCallResult result = tool.handler().call(arguments);
+                    return McpSchema.CallToolResult.builder()
+                            .addTextContent(result.text())
+                            .isError(result.isError())
+                            .build();
+                })
+                .build();
+    }
+
+    private McpSchema.Tool buildMcpTool(McpServerTool tool) {
+        String schema = tool.inputSchemaJson() != null ? 
tool.inputSchemaJson() : EMPTY_OBJECT_SCHEMA;
+        McpSchema.Tool.Builder builder = McpSchema.Tool.builder(tool.name(), 
jsonMapper, schema)
+                .description(tool.description());
+        applyAnnotations(builder, tool.annotations());
+        return builder.build();
+    }
+
+    private static void applyAnnotations(McpSchema.Tool.Builder builder, 
AiToolAnnotations annotations) {
+        if (annotations == null) {
+            return;
+        }
+        if (annotations.title() != null) {
+            builder.title(annotations.title());
+        }
+        McpSchema.ToolAnnotations.Builder hintBuilder = 
McpSchema.ToolAnnotations.builder();
+        boolean hasHints = false;
+        if (annotations.readOnlyHint() != null) {
+            hintBuilder.readOnlyHint(annotations.readOnlyHint());
+            hasHints = true;
+        }
+        if (annotations.destructiveHint() != null) {
+            hintBuilder.destructiveHint(annotations.destructiveHint());
+            hasHints = true;
+        }
+        if (annotations.idempotentHint() != null) {
+            hintBuilder.idempotentHint(annotations.idempotentHint());
+            hasHints = true;
+        }
+        if (annotations.openWorldHint() != null) {
+            hintBuilder.openWorldHint(annotations.openWorldHint());
+            hasHints = true;
+        }
+        if (hasHints) {
+            builder.annotations(hintBuilder.build());
+        }
+    }
+}
diff --git 
a/components/camel-ai/camel-mcp-server/src/main/resources/log4j2-mcp-stdio.properties
 
b/components/camel-ai/camel-mcp-server/src/main/resources/log4j2-mcp-stdio.properties
new file mode 100644
index 000000000000..ab66151a1d2a
--- /dev/null
+++ 
b/components/camel-ai/camel-mcp-server/src/main/resources/log4j2-mcp-stdio.properties
@@ -0,0 +1,38 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+status = warn
+name = McpStdioLog4jConfiguration
+packages = org.apache.camel
+
+appender.console.type = Console
+appender.console.name = console
+appender.console.target = SYSTEM_ERR
+appender.console.layout.type = PatternLayout
+appender.console.layout.pattern = %d{HH:mm:ss.SSS} %-5p [%t] %c{1} - %m%n
+
+rootLogger.level = warn
+rootLogger.appenderRef.console.ref = console
+
+logger.camel.name = org.apache.camel
+logger.camel.level = info
+logger.camel.additivity = false
+logger.camel.appenderRef.console.ref = console
+
+logger.mcp.name = org.apache.camel.component.mcp.server
+logger.mcp.level = info
+logger.mcp.additivity = false
+logger.mcp.appenderRef.console.ref = console
diff --git 
a/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/main/DefaultMcpServerFactoryTest.java
 
b/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/main/DefaultMcpServerFactoryTest.java
new file mode 100644
index 000000000000..b2743dedd900
--- /dev/null
+++ 
b/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/main/DefaultMcpServerFactoryTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.component.mcp.server.main;
+
+import org.apache.camel.component.mcp.server.McpServerEngine;
+import org.apache.camel.component.mcp.server.stdio.StdioMcpServerEngine;
+import org.apache.camel.main.HttpServerConfigurationProperties;
+import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class DefaultMcpServerFactoryTest extends CamelTestSupport {
+
+    @Test
+    void registersStdioEngineWhenTransportIsStdio() throws Exception {
+        DefaultMcpServerFactory factory = new DefaultMcpServerFactory();
+        HttpServerConfigurationProperties server = new 
MainConfigurationProperties().httpServer();
+        server.setMcpEnabled(true);
+        server.setMcpTransport("stdio");
+        server.setMcpTags("agent");
+
+        factory.newMcpServer(context, server);
+
+        McpServerEngine engine = 
context.getRegistry().findSingleByType(McpServerEngine.class);
+        assertThat(engine).isInstanceOf(StdioMcpServerEngine.class);
+    }
+
+    @Test
+    void doesNotRegisterStdioEngineForHttpTransport() {
+        DefaultMcpServerFactory factory = new DefaultMcpServerFactory();
+        HttpServerConfigurationProperties server = new 
MainConfigurationProperties().httpServer();
+        server.setMcpEnabled(true);
+        server.setMcpTransport("http");
+
+        factory.newMcpServer(context, server);
+
+        
assertThat(context.getRegistry().findSingleByType(McpServerEngine.class)).isNull();
+    }
+
+    @Test
+    void isStdioTransportRecognizesCaseInsensitiveValues() {
+        assertThat(DefaultMcpServerFactory.isStdioTransport("stdio")).isTrue();
+        assertThat(DefaultMcpServerFactory.isStdioTransport(" STDIO 
")).isTrue();
+        assertThat(DefaultMcpServerFactory.isStdioTransport("http")).isFalse();
+        assertThat(DefaultMcpServerFactory.isStdioTransport(null)).isFalse();
+    }
+}
diff --git 
a/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/main/McpServerMainPropertiesTest.java
 
b/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/main/McpServerMainPropertiesTest.java
index 872d3bb7f93b..4d2f2ff13729 100644
--- 
a/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/main/McpServerMainPropertiesTest.java
+++ 
b/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/main/McpServerMainPropertiesTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.component.mcp.server.main;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.time.Duration;
 import java.util.Map;
 
@@ -24,6 +26,7 @@ import io.modelcontextprotocol.client.McpSyncClient;
 import 
io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport;
 import io.modelcontextprotocol.spec.McpSchema;
 import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mcp.server.stdio.StdioMcpServerEngine;
 import org.apache.camel.main.Main;
 import org.apache.camel.test.AvailablePortFinder;
 import org.junit.jupiter.api.Test;
@@ -88,9 +91,10 @@ class McpServerMainPropertiesTest {
     }
 
     @Test
-    void testMcpEnabledWithoutHttpServerFailsFast() {
+    void testMcpEnabledWithoutHttpServerFailsFastForHttpTransport() {
         Main main = new Main();
         main.addInitialProperty("camel.server.mcp-enabled", "true");
+        main.addInitialProperty("camel.server.mcp-transport", "http");
         main.addInitialProperty("camel.server.mcp-tags", "crm");
 
         try {
@@ -100,4 +104,35 @@ class McpServerMainPropertiesTest {
             main.stop();
         }
     }
+
+    @Test
+    void testMcpStdioStartsWithoutHttpServer() throws Exception {
+        Main main = new Main();
+        
main.configure().setStartupSummaryLevel(org.apache.camel.StartupSummaryLevel.Off);
+        main.configure().addRoutesBuilder(new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("ai-tool:say_hello?tags=crm&description=Say hello"
+                     + "&parameter.name=string&parameter.name.required=true")
+                        .setBody(simple("Hello ${header.name}"));
+            }
+        });
+        main.addInitialProperty("camel.server.mcp-enabled", "true");
+        main.addInitialProperty("camel.server.mcp-transport", "stdio");
+        main.addInitialProperty("camel.server.mcp-tags", "crm");
+        StdioMcpServerEngine engine = new StdioMcpServerEngine();
+        engine.setTransportStreams(new ByteArrayInputStream(new byte[0]), new 
ByteArrayOutputStream());
+        main.bind("mcpServerEngine", engine);
+
+        try {
+            main.start();
+            org.apache.camel.component.mcp.server.McpServerBridge bridge
+                    = 
main.getCamelContext().hasService(org.apache.camel.component.mcp.server.McpServerBridge.class);
+            assertThat(bridge).isNotNull();
+            assertThat(bridge.getEngine()).isInstanceOf(
+                    
org.apache.camel.component.mcp.server.stdio.StdioMcpServerEngine.class);
+        } finally {
+            main.stop();
+        }
+    }
 }
diff --git 
a/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/stdio/McpStdioHarnessMain.java
 
b/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/stdio/McpStdioHarnessMain.java
new file mode 100644
index 000000000000..6c09847fae04
--- /dev/null
+++ 
b/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/stdio/McpStdioHarnessMain.java
@@ -0,0 +1,55 @@
+/*
+ * 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.component.mcp.server.stdio;
+
+import org.apache.camel.StartupSummaryLevel;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.main.Main;
+
+/**
+ * Subprocess entry point for stdio MCP integration tests. Launched by {@link 
StdioMcpServerProcessIT} through the
+ * official MCP {@code StdioClientTransport}.
+ */
+public final class McpStdioHarnessMain {
+
+    private McpStdioHarnessMain() {
+    }
+
+    public static void main(String[] args) throws Exception {
+        System.setProperty("log4j2.configurationFile", 
"classpath:log4j2-mcp-stdio.properties");
+        String tags = args.length > 0 ? args[0] : "harness";
+
+        Main main = new Main();
+        main.configure().setStartupSummaryLevel(StartupSummaryLevel.Off);
+        main.addInitialProperty("camel.server.mcp-enabled", "true");
+        main.addInitialProperty("camel.server.mcp-transport", "stdio");
+        main.addInitialProperty("camel.server.mcp-tags", tags);
+        main.configure().addRoutesBuilder(new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("ai-tool:say_hello?tags=" + tags + "&description=Say 
hello"
+                     + "&parameter.name=string&parameter.name.description=Who 
to greet&parameter.name.required=true")
+                        .routeId("say-hello-route")
+                        .setBody(simple("Hello ${header.name}"));
+
+                from("ai-tool:hidden_tool?description=Untagged tool, must not 
be exposed")
+                        .setBody(constant("hidden"));
+            }
+        });
+        main.run();
+    }
+}
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
new file mode 100644
index 000000000000..4d7c61b21926
--- /dev/null
+++ 
b/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerEngineTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.component.mcp.server.stdio;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mcp.server.McpServerBridge;
+import org.apache.camel.component.mcp.server.McpServerConfiguration;
+import org.apache.camel.component.mcp.server.McpServerTool;
+import org.apache.camel.component.mcp.server.McpToolCallHandler;
+import org.apache.camel.component.mcp.server.McpToolCallResult;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+
+class StdioMcpServerEngineTest extends CamelTestSupport {
+
+    private McpServerBridge bridge;
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext camelContext = super.createCamelContext();
+        StdioMcpServerEngine engine = new StdioMcpServerEngine();
+        engine.setTransportStreams(new ByteArrayInputStream(new byte[0]), new 
ByteArrayOutputStream());
+        camelContext.getRegistry().bind("mcpServerEngine", engine);
+        McpServerConfiguration configuration = new McpServerConfiguration();
+        configuration.setTags("stdio");
+        configuration.setToolTimeout(5000);
+        bridge = new McpServerBridge(configuration);
+        camelContext.addService(bridge);
+        return camelContext;
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("ai-tool:say_hello?tags=stdio&description=Say hello"
+                     + "&parameter.name=string&parameter.name.required=true")
+                        .setBody(simple("Hello ${header.name}"));
+            }
+        };
+    }
+
+    @Test
+    void bridgeResolvesStdioEngineWithoutHttpServer() {
+        
assertThat(bridge.getEngine()).isInstanceOf(StdioMcpServerEngine.class);
+        assertThat(bridge.isStarted()).isTrue();
+        assertThat(bridge.getEngine().consumesServingConfiguration()).isTrue();
+    }
+
+    @Test
+    void toolAddedBeforeStartIsQueuedWithoutNpe() {
+        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));
+
+        assertThatCode(() -> engine.toolAdded(sampleTool("queued_tool", 
"Queued before start")))
+                .doesNotThrowAnyException();
+        assertThatCode(() -> 
engine.toolRemoved("queued_tool")).doesNotThrowAnyException();
+    }
+
+    @Test
+    void toolRemovedBeforeStartDoesNotThrow() {
+        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));
+
+        engine.toolAdded(sampleTool("ephemeral", "Removed before start"));
+        assertThatCode(() -> 
engine.toolRemoved("ephemeral")).doesNotThrowAnyException();
+    }
+
+    private static McpServerTool sampleTool(String name, String description) {
+        McpToolCallHandler handler = arguments -> new McpToolCallResult("ok", 
false);
+        return new McpServerTool() {
+            @Override
+            public String name() {
+                return name;
+            }
+
+            @Override
+            public String description() {
+                return description;
+            }
+
+            @Override
+            public String inputSchemaJson() {
+                return null;
+            }
+
+            @Override
+            public java.util.Map<String, 
org.apache.camel.component.ai.tool.AiToolParameterHelper.ParameterDef> 
parameters() {
+                return java.util.Map.of();
+            }
+
+            @Override
+            public McpToolCallHandler handler() {
+                return handler;
+            }
+
+            @Override
+            public org.apache.camel.component.ai.tool.AiToolAnnotations 
annotations() {
+                return null;
+            }
+
+            @Override
+            public String outputSchemaJson() {
+                return null;
+            }
+        };
+    }
+}
diff --git 
a/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerProcessIT.java
 
b/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerProcessIT.java
new file mode 100644
index 000000000000..261741a98c30
--- /dev/null
+++ 
b/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerProcessIT.java
@@ -0,0 +1,113 @@
+/*
+ * 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.component.mcp.server.stdio;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import io.modelcontextprotocol.client.McpClient;
+import io.modelcontextprotocol.client.McpSyncClient;
+import io.modelcontextprotocol.client.transport.ServerParameters;
+import io.modelcontextprotocol.client.transport.StdioClientTransport;
+import io.modelcontextprotocol.json.McpJsonDefaults;
+import io.modelcontextprotocol.spec.McpSchema;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * End-to-end stdio MCP test: the MCP Java SDK client launches {@link 
McpStdioHarnessMain} as a subprocess (the same
+ * shape IDE agents use) and verifies tool listing and execution.
+ */
+class StdioMcpServerProcessIT {
+
+    private static final String HARNESS_TAG = "harness";
+
+    @Test
+    void testStdioMcpServerListsAndCallsTaggedTools() {
+        McpSyncClient client = null;
+        try {
+            client = createClient();
+            client.initialize();
+
+            List<McpSchema.Tool> tools = client.listTools().tools();
+            assertThat(tools).extracting(McpSchema.Tool::name)
+                    .contains("say_hello")
+                    .doesNotContain("hidden_tool");
+
+            McpSchema.CallToolResult result
+                    = client.callTool(new 
McpSchema.CallToolRequest("say_hello", Map.of("name", "Camel")));
+            assertThat(result.isError()).isNotEqualTo(Boolean.TRUE);
+            assertThat(textOf(result)).isEqualTo("Hello Camel");
+        } finally {
+            if (client != null) {
+                client.closeGracefully();
+            }
+        }
+    }
+
+    @Test
+    void testStdioMcpServerSanitizesExecutionErrors() {
+        McpSyncClient client = null;
+        try {
+            client = createClient();
+            client.initialize();
+
+            // harness does not expose fail_tool; add a quick inline check via 
missing required arg
+            McpSchema.CallToolResult result = client.callTool(new 
McpSchema.CallToolRequest("say_hello", Map.of()));
+            assertThat(result.isError()).isEqualTo(Boolean.TRUE);
+            assertThat(textOf(result)).contains("name");
+        } finally {
+            if (client != null) {
+                client.closeGracefully();
+            }
+        }
+    }
+
+    private static McpSyncClient createClient() {
+        List<String> command = harnessCommand();
+        ServerParameters parameters = ServerParameters.builder(command.get(0))
+                .args(command.subList(1, command.size()))
+                .build();
+        return McpClient.sync(new StdioClientTransport(parameters, 
McpJsonDefaults.getMapper()))
+                .requestTimeout(Duration.ofSeconds(20))
+                .initializationTimeout(Duration.ofSeconds(20))
+                .build();
+    }
+
+    private static List<String> harnessCommand() {
+        String javaHome = System.getProperty("java.home");
+        String javaBin = javaHome + "/bin/java";
+        String classpath = System.getProperty("java.class.path");
+        return List.of(
+                javaBin,
+                
"-Dlog4j2.configurationFile=classpath:log4j2-mcp-stdio.properties",
+                "-cp",
+                classpath,
+                McpStdioHarnessMain.class.getName(),
+                HARNESS_TAG);
+    }
+
+    private static String textOf(McpSchema.CallToolResult result) {
+        return result.content().stream()
+                .filter(McpSchema.TextContent.class::isInstance)
+                .map(c -> ((McpSchema.TextContent) c).text())
+                .collect(Collectors.joining());
+    }
+}
diff --git 
a/core/camel-main/src/generated/java/org/apache/camel/main/HttpServerConfigurationPropertiesConfigurer.java
 
b/core/camel-main/src/generated/java/org/apache/camel/main/HttpServerConfigurationPropertiesConfigurer.java
index 9c37334dcc80..01112978cce5 100644
--- 
a/core/camel-main/src/generated/java/org/apache/camel/main/HttpServerConfigurationPropertiesConfigurer.java
+++ 
b/core/camel-main/src/generated/java/org/apache/camel/main/HttpServerConfigurationPropertiesConfigurer.java
@@ -50,6 +50,7 @@ public class HttpServerConfigurationPropertiesConfigurer 
extends org.apache.came
         map.put("McpSessionKeepAliveInterval", long.class);
         map.put("McpTags", java.lang.String.class);
         map.put("McpToolTimeout", long.class);
+        map.put("McpTransport", java.lang.String.class);
         map.put("Path", java.lang.String.class);
         map.put("Port", int.class);
         map.put("StaticContextPath", java.lang.String.class);
@@ -117,6 +118,8 @@ public class HttpServerConfigurationPropertiesConfigurer 
extends org.apache.came
         case "mcpTags": target.setMcpTags(property(camelContext, 
java.lang.String.class, value)); return true;
         case "mcptooltimeout":
         case "mcpToolTimeout": target.setMcpToolTimeout(property(camelContext, 
long.class, value)); return true;
+        case "mcptransport":
+        case "mcpTransport": target.setMcpTransport(property(camelContext, 
java.lang.String.class, value)); return true;
         case "path": target.setPath(property(camelContext, 
java.lang.String.class, value)); return true;
         case "port": target.setPort(property(camelContext, int.class, value)); 
return true;
         case "staticcontextpath":
@@ -193,6 +196,8 @@ public class HttpServerConfigurationPropertiesConfigurer 
extends org.apache.came
         case "mcpTags": return java.lang.String.class;
         case "mcptooltimeout":
         case "mcpToolTimeout": return long.class;
+        case "mcptransport":
+        case "mcpTransport": return java.lang.String.class;
         case "path": return java.lang.String.class;
         case "port": return int.class;
         case "staticcontextpath":
@@ -265,6 +270,8 @@ public class HttpServerConfigurationPropertiesConfigurer 
extends org.apache.came
         case "mcpTags": return target.getMcpTags();
         case "mcptooltimeout":
         case "mcpToolTimeout": return target.getMcpToolTimeout();
+        case "mcptransport":
+        case "mcpTransport": return target.getMcpTransport();
         case "path": return target.getPath();
         case "port": return target.getPort();
         case "staticcontextpath":
diff --git 
a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
 
b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
index 18b928ed1d37..b2548a1b6a2e 100644
--- 
a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
+++ 
b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
@@ -442,7 +442,7 @@
     { "name": "camel.server.jwtKeystorePath", "required": false, 
"description": "Path to the keystore file used for JWT tokens validation.", 
"sourceType": "org.apache.camel.main.HttpServerConfigurationProperties", 
"type": "string", "javaType": "java.lang.String", "secret": false },
     { "name": "camel.server.jwtKeystoreType", "required": false, 
"description": "Type of the keystore used for JWT tokens validation (jks, 
pkcs12, etc.).", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string", 
"javaType": "java.lang.String", "secret": false },
     { "name": "camel.server.maxBodySize", "required": false, "description": 
"Maximum HTTP body size the embedded HTTP server can accept.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "integer", 
"javaType": "java.lang.Long", "secret": false },
-    { "name": "camel.server.mcpEnabled", "required": false, "description": 
"Whether to expose ai-tool routes as MCP tools and ai-resource routes as MCP 
resources over streamable HTTP. Requires camel-mcp-server on the classpath. By 
default, the MCP server is not enabled.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "boolean", 
"javaType": "boolean", "defaultValue": false, "secret": false },
+    { "name": "camel.server.mcpEnabled", "required": false, "description": 
"Whether to expose ai-tool routes as MCP tools and ai-resource routes as MCP 
resources. Requires camel-mcp-server on the classpath. Use 
setMcpTransport(String) to choose streamable HTTP (default) or stdio for IDE 
subprocess integration. By default, the MCP server is not enabled.", 
"sourceType": "org.apache.camel.main.HttpServerConfigurationProperties", 
"type": "boolean", "javaType": "boolean", "defaultValue": fals [...]
     { "name": "camel.server.mcpInstructions", "required": false, 
"description": "Top-level MCP instructions returned to clients on initialize.", 
"sourceType": "org.apache.camel.main.HttpServerConfigurationProperties", 
"type": "string", "javaType": "java.lang.String", "secret": false },
     { "name": "camel.server.mcpPath", "required": false, "description": "HTTP 
path where the MCP endpoint is served.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string", 
"javaType": "java.lang.String", "defaultValue": "\/mcp", "secret": false },
     { "name": "camel.server.mcpResourceTimeout", "required": false, 
"description": "Per-read MCP resource execution timeout in milliseconds. A read 
exceeding the timeout returns an error to the MCP client; the underlying route 
keeps running until it completes on its own.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "integer", 
"javaType": "long", "defaultValue": 20000, "secret": false },
@@ -455,6 +455,7 @@
     { "name": "camel.server.mcpSessionKeepAliveInterval", "required": false, 
"description": "Keep-alive ping interval in milliseconds for MCP sessions on 
the Vert.x streamable transport. Dead sessions are evicted after consecutive 
ping failures. 0 disables keep-alive pings.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "integer", 
"javaType": "long", "defaultValue": 30000, "secret": false },
     { "name": "camel.server.mcpTags", "required": false, "description": 
"Comma-separated list of tag patterns selecting the ai-tool routes to expose as 
MCP tools and the ai-resource routes to expose as MCP resources. Matching is 
case-insensitive and supports exact match, wildcard prefix ( {code foo} ), and 
{code } to match all tags. Only routes registered under a matching tag are 
exposed; the untagged default pools are never exposed. When not set, nothing is 
exposed.", "sourceType": "org [...]
     { "name": "camel.server.mcpToolTimeout", "required": false, "description": 
"Per-call MCP tool execution timeout in milliseconds. A call exceeding the 
timeout returns an error result to the MCP client; the underlying route keeps 
running until it completes on its own.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "integer", 
"javaType": "long", "defaultValue": 20000, "secret": false },
+    { "name": "camel.server.mcpTransport", "required": false, "description": 
"MCP transport for ai-tool routes: http (streamable HTTP on the embedded 
server, default) or stdio (JSON-RPC on process stdin\/stdout for local agent 
subprocesses).", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "enum", 
"javaType": "java.lang.String", "defaultValue": "http", "secret": false, 
"enum": [ "http", "stdio" ] },
     { "name": "camel.server.path", "required": false, "description": 
"Context-path to use for embedded HTTP server", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string", 
"javaType": "java.lang.String", "defaultValue": "\/", "secret": false },
     { "name": "camel.server.port", "required": false, "description": "Port to 
use for binding embedded HTTP server. Use 0 to dynamic assign a free random 
port number.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "integer", 
"javaType": "int", "defaultValue": 8080, "secret": false },
     { "name": "camel.server.staticContextPath", "required": false, 
"description": "The context-path to use for serving static content. By default, 
the root path is used. And if there is an index.html page then this is 
automatically loaded.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string", 
"javaType": "java.lang.String", "defaultValue": "\/", "secret": false },
diff --git a/core/camel-main/src/main/docs/main.adoc 
b/core/camel-main/src/main/docs/main.adoc
index 6418250b5c3e..fbb30dd12cd3 100644
--- a/core/camel-main/src/main/docs/main.adoc
+++ b/core/camel-main/src/main/docs/main.adoc
@@ -195,7 +195,7 @@ The camel.routecontroller supports 12 options, which are 
listed below.
 
 
 === Camel Embedded HTTP Server (only for standalone; not Spring Boot or 
Quarkus) configurations
-The camel.server supports 34 options, which are listed below.
+The camel.server supports 35 options, which are listed below.
 
 [width="100%",cols="2,5,^1,2",options="header"]
 |===
@@ -215,7 +215,7 @@ The camel.server supports 34 options, which are listed 
below.
 | *camel.server.jwtKeystorePath* | Path to the keystore file used for JWT 
tokens validation. |  | String
 | *camel.server.jwtKeystoreType* | Type of the keystore used for JWT tokens 
validation (jks, pkcs12, etc.). |  | String
 | *camel.server.maxBodySize* | Maximum HTTP body size the embedded HTTP server 
can accept. |  | Long
-| *camel.server.mcpEnabled* | Whether to expose ai-tool routes as MCP tools 
and ai-resource routes as MCP resources over streamable HTTP. Requires 
camel-mcp-server on the classpath. By default, the MCP server is not enabled. | 
false | boolean
+| *camel.server.mcpEnabled* | Whether to expose ai-tool routes as MCP tools 
and ai-resource routes as MCP resources. Requires camel-mcp-server on the 
classpath. Use setMcpTransport(String) to choose streamable HTTP (default) or 
stdio for IDE subprocess integration. By default, the MCP server is not 
enabled. | false | boolean
 | *camel.server.mcpInstructions* | Top-level MCP instructions returned to 
clients on initialize. |  | String
 | *camel.server.mcpPath* | HTTP path where the MCP endpoint is served. | /mcp 
| String
 | *camel.server.mcpResourceTimeout* | Per-read MCP resource execution timeout 
in milliseconds. A read exceeding the timeout returns an error to the MCP 
client; the underlying route keeps running until it completes on its own. | 
20000 | long
@@ -228,6 +228,7 @@ The camel.server supports 34 options, which are listed 
below.
 | *camel.server.mcpSessionKeepAliveInterval* | Keep-alive ping interval in 
milliseconds for MCP sessions on the Vert.x streamable transport. Dead sessions 
are evicted after consecutive ping failures. 0 disables keep-alive pings. | 
30000 | long
 | *camel.server.mcpTags* | Comma-separated list of tag patterns selecting the 
ai-tool routes to expose as MCP tools and the ai-resource routes to expose as 
MCP resources. Matching is case-insensitive and supports exact match, wildcard 
prefix ( {code foo} ), and {code } to match all tags. Only routes registered 
under a matching tag are exposed; the untagged default pools are never exposed. 
When not set, nothing is exposed. |  | String
 | *camel.server.mcpToolTimeout* | Per-call MCP tool execution timeout in 
milliseconds. A call exceeding the timeout returns an error result to the MCP 
client; the underlying route keeps running until it completes on its own. | 
20000 | long
+| *camel.server.mcpTransport* | MCP transport for ai-tool routes: http 
(streamable HTTP on the embedded server, default) or stdio (JSON-RPC on process 
stdin/stdout for local agent subprocesses). | http | String
 | *camel.server.path* | Context-path to use for embedded HTTP server | / | 
String
 | *camel.server.port* | Port to use for binding embedded HTTP server. Use 0 to 
dynamic assign a free random port number. | 8080 | int
 | *camel.server.staticContextPath* | The context-path to use for serving 
static content. By default, the root path is used. And if there is an 
index.html page then this is automatically loaded. | / | String
diff --git 
a/core/camel-main/src/main/java/org/apache/camel/main/HttpServerConfigurationProperties.java
 
b/core/camel-main/src/main/java/org/apache/camel/main/HttpServerConfigurationProperties.java
index cae91c4bf558..c6bb81bf4d0b 100644
--- 
a/core/camel-main/src/main/java/org/apache/camel/main/HttpServerConfigurationProperties.java
+++ 
b/core/camel-main/src/main/java/org/apache/camel/main/HttpServerConfigurationProperties.java
@@ -73,6 +73,8 @@ public class HttpServerConfigurationProperties implements 
BootstrapCloseable {
 
     @Metadata
     private boolean mcpEnabled;
+    @Metadata(defaultValue = "http", enums = "http,stdio")
+    private String mcpTransport = "http";
     @Metadata
     private String mcpTags;
     @Metadata(defaultValue = "20000")
@@ -358,13 +360,26 @@ public class HttpServerConfigurationProperties implements 
BootstrapCloseable {
     }
 
     /**
-     * Whether to expose ai-tool routes as MCP tools and ai-resource routes as 
MCP resources over streamable HTTP.
-     * Requires camel-mcp-server on the classpath. By default, the MCP server 
is not enabled.
+     * Whether to expose ai-tool routes as MCP tools and ai-resource routes as 
MCP resources. Requires camel-mcp-server
+     * on the classpath. Use {@link #setMcpTransport(String)} to choose 
streamable HTTP (default) or stdio for IDE
+     * subprocess integration. By default, the MCP server is not enabled.
      */
     public void setMcpEnabled(boolean mcpEnabled) {
         this.mcpEnabled = mcpEnabled;
     }
 
+    public String getMcpTransport() {
+        return mcpTransport;
+    }
+
+    /**
+     * MCP transport for ai-tool routes: {@code http} (streamable HTTP on the 
embedded server, default) or {@code stdio}
+     * (JSON-RPC on process stdin/stdout for local agent subprocesses).
+     */
+    public void setMcpTransport(String mcpTransport) {
+        this.mcpTransport = mcpTransport;
+    }
+
     public String getMcpTags() {
         return mcpTags;
     }
@@ -686,14 +701,23 @@ public class HttpServerConfigurationProperties implements 
BootstrapCloseable {
     }
 
     /**
-     * Whether to expose ai-tool routes as MCP tools and ai-resource routes as 
MCP resources over streamable HTTP.
-     * Requires camel-mcp-server on the classpath. By default, the MCP server 
is not enabled.
+     * Whether to expose ai-tool routes as MCP tools and ai-resource routes as 
MCP resources. Requires camel-mcp-server
+     * on the classpath. Use {@link #setMcpTransport(String)} to choose 
streamable HTTP (default) or stdio for IDE
+     * subprocess integration. By default, the MCP server is not enabled.
      */
     public HttpServerConfigurationProperties withMcpEnabled(boolean 
mcpEnabled) {
         this.mcpEnabled = mcpEnabled;
         return this;
     }
 
+    /**
+     * MCP transport for ai-tool routes: {@code http} (default) or {@code 
stdio}.
+     */
+    public HttpServerConfigurationProperties withMcpTransport(String 
mcpTransport) {
+        this.mcpTransport = mcpTransport;
+        return this;
+    }
+
     /**
      * Comma-separated list of tag patterns selecting the ai-tool routes to 
expose as MCP tools and the ai-resource
      * routes to expose as MCP resources. Matching is case-insensitive and 
supports exact match, wildcard prefix
diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
index 1a7f77f79cef..32e40809f634 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
@@ -62,6 +62,8 @@ camel debug [options]
 | `--max-messages` | Max number of messages to process before stopping | 0 | 
int
 | `--max-seconds` | Max seconds to run before stopping | 0 | int
 | `--mcp` | Embed dev/diagnostics MCP tools on the local HTTP management 
server (/mcp by default). When management shares the main HTTP port, MCP is 
served on the main server bind address. Also binds the management server to 
127.0.0.1 (affecting health/metrics when --observe is used). | false | boolean
+| `--mcp-stdio` | Expose tagged ai-tool routes as MCP tools over process 
stdin/stdout for IDE subprocess integration (no HTTP port). Requires --mcp-tags 
or camel.server.mcp-tags. Logging and startup summaries are routed to stderr so 
stdout carries MCP protocol only. | false | boolean
+| `--mcp-tags` | Comma-separated ai-tool tags to expose on the ai-tool MCP 
server (maps to camel.server.mcp-tags). Use with --mcp-stdio or when 
camel.server.mcp-enabled is configured for HTTP transport. Not used by --mcp 
(dev/diagnostics MCP). |  | String
 | `--metrics` _(deprecated)_ | Deprecated: use --observe instead. Metrics 
(Micrometer and Prometheus) at /q/metrics on local HTTP server (port 8080 by 
default) | false | boolean
 | `--modeline` | Whether to support JBang style //DEPS to specify additional 
dependencies | true | boolean
 | `--name` | The name of the Camel application | CamelJBang | String
diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dev.adoc 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dev.adoc
index 4d8ce342f534..9d57cdbcb7d7 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dev.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dev.adoc
@@ -58,6 +58,8 @@ camel dev [options]
 | `--max-messages` | Max number of messages to process before stopping | 0 | 
int
 | `--max-seconds` | Max seconds to run before stopping | 0 | int
 | `--mcp` | Embed dev/diagnostics MCP tools on the local HTTP management 
server (/mcp by default). When management shares the main HTTP port, MCP is 
served on the main server bind address. Also binds the management server to 
127.0.0.1 (affecting health/metrics when --observe is used). | false | boolean
+| `--mcp-stdio` | Expose tagged ai-tool routes as MCP tools over process 
stdin/stdout for IDE subprocess integration (no HTTP port). Requires --mcp-tags 
or camel.server.mcp-tags. Logging and startup summaries are routed to stderr so 
stdout carries MCP protocol only. | false | boolean
+| `--mcp-tags` | Comma-separated ai-tool tags to expose on the ai-tool MCP 
server (maps to camel.server.mcp-tags). Use with --mcp-stdio or when 
camel.server.mcp-enabled is configured for HTTP transport. Not used by --mcp 
(dev/diagnostics MCP). |  | String
 | `--metrics` _(deprecated)_ | Deprecated: use --observe instead. Metrics 
(Micrometer and Prometheus) at /q/metrics on local HTTP server (port 8080 by 
default) | false | boolean
 | `--modeline` | Whether to support JBang style //DEPS to specify additional 
dependencies | true | boolean
 | `--name` | The name of the Camel application | CamelJBang | String
diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
index 864a7fad313a..953c7735a5dc 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
@@ -58,6 +58,8 @@ camel run [options]
 | `--max-messages` | Max number of messages to process before stopping | 0 | 
int
 | `--max-seconds` | Max seconds to run before stopping | 0 | int
 | `--mcp` | Embed dev/diagnostics MCP tools on the local HTTP management 
server (/mcp by default). When management shares the main HTTP port, MCP is 
served on the main server bind address. Also binds the management server to 
127.0.0.1 (affecting health/metrics when --observe is used). | false | boolean
+| `--mcp-stdio` | Expose tagged ai-tool routes as MCP tools over process 
stdin/stdout for IDE subprocess integration (no HTTP port). Requires --mcp-tags 
or camel.server.mcp-tags. Logging and startup summaries are routed to stderr so 
stdout carries MCP protocol only. | false | boolean
+| `--mcp-tags` | Comma-separated ai-tool tags to expose on the ai-tool MCP 
server (maps to camel.server.mcp-tags). Use with --mcp-stdio or when 
camel.server.mcp-enabled is configured for HTTP transport. Not used by --mcp 
(dev/diagnostics MCP). |  | String
 | `--metrics` _(deprecated)_ | Deprecated: use --observe instead. Metrics 
(Micrometer and Prometheus) at /q/metrics on local HTTP server (port 8080 by 
default) | false | boolean
 | `--modeline` | Whether to support JBang style //DEPS to specify additional 
dependencies | true | boolean
 | `--name` | The name of the Camel application | CamelJBang | String
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 e155bf6a47e2..30ea3af76e38 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
@@ -6,9 +6,9 @@
     { "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  [...]
+    { "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  [...]
     { "name": "dependency", "fullName": "dependency", "description": "Displays 
all Camel dependencies required to run", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.DependencyCommand", "options": [ { 
"names": "-h,--help", "description": "Display the help and sub-commands", 
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": 
"copy", "fullName": "dependency copy", "description": "Copies all Camel 
dependencies required to run to a specific directory", "sourc [...]
-    { "name": "dev", "fullName": "dev", "description": "Run in dev mode with 
live reload", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Dev", 
"options": [ { "names": "--background", "description": "Run in the background", 
"defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": 
"--background-wait", "description": "To wait for run in background to startup 
successfully, before returning", "defaultValue": "true", "javaType": "boolean", 
"type": "boolean" }, [...]
+    { "name": "dev", "fullName": "dev", "description": "Run in dev mode with 
live reload", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Dev", 
"options": [ { "names": "--background", "description": "Run in the background", 
"defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": 
"--background-wait", "description": "To wait for run in background to startup 
successfully, before returning", "defaultValue": "true", "javaType": "boolean", 
"type": "boolean" }, [...]
     { "name": "dirty", "fullName": "dirty", "description": "Check if there are 
dirty files from previous Camel runs that did not terminate gracefully", 
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.Dirty", 
"options": [ { "names": "--clean", "description": "Clean dirty files which are 
no longer in use", "defaultValue": "false", "javaType": "boolean", "type": 
"boolean" }, { "names": "-h,--help", "description": "Display the help and 
sub-commands", "javaType": "boolean", " [...]
     { "name": "doc", "fullName": "doc", "description": "Shows documentation 
for kamelet, component, and other Camel resources", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.catalog.CatalogDoc", "options": [ { 
"names": "--camel-version", "description": "To use a different Camel version 
than the default version", "javaType": "java.lang.String", "type": "string" }, 
{ "names": "--download", "description": "Whether to allow automatic downloading 
JAR dependencies (over the internet [...]
     { "name": "doctor", "fullName": "doctor", "description": "Checks the 
environment and reports potential issues", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.Doctor", "options": [ { "names": 
"-h,--help", "description": "Display the help and sub-commands", "javaType": 
"boolean", "type": "boolean" } ] },
@@ -26,7 +26,7 @@
     { "name": "plugin", "fullName": "plugin", "description": "Manage plugins 
that add sub-commands to this CLI", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.plugin.PluginCommand", "options": [ { 
"names": "-h,--help", "description": "Display the help and sub-commands", 
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "add", 
"fullName": "plugin add", "description": "Add new plugin", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.plugin.PluginA [...]
     { "name": "ps", "fullName": "ps", "description": "List running Camel 
integrations", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.process.ListProcess", "options": [ { 
"names": "--json", "description": "Output in JSON Format", "javaType": 
"boolean", "type": "boolean" }, { "names": "--pid", "description": "List only 
pid in the output", "javaType": "boolean", "type": "boolean" }, { "names": 
"--remote", "description": "Break down counters into remote\/total pairs", 
"javaType": [...]
     { "name": "restart", "fullName": "restart", "description": "Restarts 
running Camel integrations (stop + re-launch)", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.process.RestartProcess", "options": [ 
{ "names": "-h,--help", "description": "Display the help and sub-commands", 
"javaType": "boolean", "type": "boolean" } ] },
-    { "name": "run", "fullName": "run", "description": "Run as local Camel 
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Run", 
"options": [ { "names": "--background", "description": "Run in the background", 
"defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": 
"--background-wait", "description": "To wait for run in background to startup 
successfully, before returning", "defaultValue": "true", "javaType": "boolean", 
"type": "boolean" }, { [...]
+    { "name": "run", "fullName": "run", "description": "Run as local Camel 
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Run", 
"options": [ { "names": "--background", "description": "Run in the background", 
"defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": 
"--background-wait", "description": "To wait for run in background to startup 
successfully, before returning", "defaultValue": "true", "javaType": "boolean", 
"type": "boolean" }, { [...]
     { "name": "sbom", "fullName": "sbom", "description": "Generate a CycloneDX 
or SPDX SBOM for a specific project", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.SBOMGenerator", "options": [ { 
"names": "--build-property", "description": "Maven build properties, ex. 
--build-property=prop1=foo", "javaType": "java.util.List", "type": "array" }, { 
"names": "--camel-spring-boot-version", "description": "Camel version to use 
with Spring Boot", "javaType": "java.lang.String", "type" [...]
     { "name": "script", "fullName": "script", "description": "Run Camel 
integration as shell script for terminal scripting", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.Script", "options": [ { "names": 
"--logging", "description": "Can be used to turn on logging (logs to file in 
<user home>\/.camel directory)", "defaultValue": "false", "javaType": 
"boolean", "type": "boolean" }, { "names": "--logging-level", "description": 
"Logging level (ERROR, WARN, INFO, DEBUG, TRACE)", "d [...]
     { "name": "shell", "fullName": "shell", "description": "Interactive Camel 
CLI shell.", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Shell", 
"options": [ { "names": "-h,--help", "description": "Display the help and 
sub-commands", "javaType": "boolean", "type": "boolean" } ] },
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
index 266ac4925357..fcae254d0d0b 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
@@ -167,6 +167,7 @@ public class Run extends CamelCommand {
     public long spawnPid;
 
     private Printer quietPrinter;
+    private boolean mcpStdioMode;
 
     @Parameters(description = "The Camel file(s) to run. If no files specified 
then application.properties is used as source for which files to run.",
                 arity = "0..9", paramLabel = "<files>", parameterConsumer = 
FilesConsumer.class)
@@ -610,6 +611,28 @@ public class Run extends CamelCommand {
         return "true".equals(val);
     }
 
+    private boolean isMcpStdioEnabled(Properties profileProperties) {
+        if (serverOptions.mcpStdio) {
+            return true;
+        }
+        return profileProperties != null
+                && 
"stdio".equalsIgnoreCase(profileProperties.getProperty("camel.server.mcp-transport",
 "").trim());
+    }
+
+    boolean isAiToolMcpServerEnabled(Properties profileProperties) {
+        if (isMcpStdioEnabled(profileProperties)) {
+            return true;
+        }
+        return profileProperties != null
+                && 
"true".equalsIgnoreCase(profileProperties.getProperty("camel.server.mcp-enabled",
 "").trim());
+    }
+
+    void applyMcpTagsOverride(KameletMain main) {
+        if (serverOptions.mcpTags != null && !serverOptions.mcpTags.isBlank()) 
{
+            main.addOverrideProperty("camel.server.mcp-tags", 
serverOptions.mcpTags);
+        }
+    }
+
     private void writeSetting(KameletMain main, Properties existing, String 
key, Supplier<String> value) {
         String val = existing != null ? existing.getProperty(key, value.get()) 
: value.get();
         if (val != null) {
@@ -697,7 +720,8 @@ public class Run extends CamelCommand {
         }
 
         Properties profileProperties = !empty ? loadProfileProperties(baseDir) 
: null;
-        configureLogging(baseDir);
+        mcpStdioMode = isMcpStdioEnabled(profileProperties);
+        configureLogging(baseDir, profileProperties);
         if (openapi != null) {
             generateOpenApi();
         }
@@ -1268,7 +1292,14 @@ public class Run extends CamelCommand {
             dependencies.add("camel:openapi-java");
             applyOpenApiUiRuntimeOptions(main);
         }
-        if (isMcpEnabled(profileProperties)) {
+        if (isMcpStdioEnabled(profileProperties)) {
+            dependencies.add("camel:mcp-server");
+            applyMcpStdioRuntimeOptions(main, profileProperties);
+        } else if (isAiToolMcpServerEnabled(profileProperties)) {
+            dependencies.add("camel:platform-http-main");
+            dependencies.add("camel:mcp-server");
+            applyMcpTagsOverride(main);
+        } else if (isMcpEnabled(profileProperties)) {
             dependencies.add("camel:platform-http-main");
             dependencies.add("camel:mcp-server");
         }
@@ -2671,7 +2702,7 @@ public class Run extends CamelCommand {
         return main;
     }
 
-    private void configureLogging(Path baseDir) throws Exception {
+    private void configureLogging(Path baseDir, Properties profileProperties) 
throws Exception {
         if (loggingOptions.logging) {
             // allow to configure individual logging levels in 
application.properties
             Properties prop = loadProfileProperties(baseDir);
@@ -2700,7 +2731,7 @@ public class Run extends CamelCommand {
             }
             RuntimeUtil.configureLog(loggingOptions.loggingLevel, 
loggingOptions.loggingColor,
                     loggingOptions.loggingJson, scriptRun, false, 
loggingOptions.loggingConfigPath,
-                    loggingOptions.loggingCategory);
+                    loggingOptions.loggingCategory, mcpStdioMode);
             writeSettings("loggingLevel", loggingOptions.loggingLevel);
             writeSettings("loggingColor", loggingOptions.loggingColor ? "true" 
: "false");
             writeSettings("loggingJson", loggingOptions.loggingJson ? "true" : 
"false");
@@ -3109,6 +3140,18 @@ public class Run extends CamelCommand {
                               + "Also binds the management server to 127.0.0.1 
(affecting health/metrics when --observe is used).")
         boolean mcp;
 
+        @Option(names = { "--mcp-stdio" }, defaultValue = "false",
+                description = "Expose tagged ai-tool routes as MCP tools over 
process stdin/stdout for IDE subprocess "
+                              + "integration (no HTTP port). Requires 
--mcp-tags or camel.server.mcp-tags. "
+                              + "Logging and startup summaries are routed to 
stderr so stdout carries MCP protocol only.")
+        boolean mcpStdio;
+
+        @Option(names = { "--mcp-tags" },
+                description = "Comma-separated ai-tool tags to expose on the 
ai-tool MCP server (maps to "
+                              + "camel.server.mcp-tags). Use with --mcp-stdio 
or when camel.server.mcp-enabled is "
+                              + "configured for HTTP transport. Not used by 
--mcp (dev/diagnostics MCP).")
+        String mcpTags;
+
         @Option(names = { "--openapi-ui" }, defaultValue = "false",
                 description = "Swagger UI for REST OpenAPI at /q/openapi 
(OpenAPI document at /q/openapi.json; port 8080 by default)")
         boolean openapiUi;
@@ -3155,6 +3198,27 @@ public class Run extends CamelCommand {
         main.addOverrideProperty("camel.management.enabled", "true");
     }
 
+    void applyMcpStdioRuntimeOptions(KameletMain main, Properties 
profileProperties) {
+        if (!isMcpStdioEnabled(profileProperties)) {
+            return;
+        }
+        if (isMcpEnabled(profileProperties)) {
+            throw new IllegalArgumentException(
+                    "--mcp-stdio and --mcp cannot be used together: --mcp 
serves dev/diagnostics "
+                                               + "tools over HTTP management, 
while --mcp-stdio serves ai-tool routes over stdin/stdout.");
+        }
+        if (serverOptions.mcpStdio) {
+            main.addOverrideProperty("camel.server.mcp-enabled", "true");
+            main.addOverrideProperty("camel.server.mcp-transport", "stdio");
+            applyMcpTagsOverride(main);
+        } else {
+            writeSetting(main, profileProperties, "camel.server.mcp-enabled", 
"true");
+            writeSetting(main, profileProperties, 
"camel.server.mcp-transport", "stdio");
+        }
+        main.addOverrideProperty("camel.main.startupSummaryLevel", "Off");
+        main.setSilent(true);
+    }
+
     static class FilesConsumer extends ParameterConsumer<Run> {
         @Override
         protected void doConsumeParameters(Stack<String> args, Run cmd) {
@@ -3202,8 +3266,8 @@ public class Run extends CamelCommand {
 
     @Override
     protected Printer printer() {
-        if (exportRun && (!loggingOptions.logging && !verbose)) {
-            // Export run should be silent unless in logging or verbose mode
+        if (mcpStdioMode || (exportRun && (!loggingOptions.logging && 
!verbose))) {
+            // MCP stdio mode and export silent runs must not write 
diagnostics to stdout
             if (quietPrinter == null) {
                 quietPrinter = new Printer.QuietPrinter(super.printer());
             }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/Printer.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/Printer.java
index d61cc198c890..9d4eb6ecc334 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/Printer.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/Printer.java
@@ -101,8 +101,9 @@ public interface Printer {
         public void printf(String format, Object... args) {
         }
 
+        @Override
         public void printErr(String message) {
-            delegate.printErr(message);
+            System.err.printf("ERROR: %s%n", message);
         }
     }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/RuntimeUtil.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/RuntimeUtil.java
index 864a1d9547f7..8ccd3cdcef6b 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/RuntimeUtil.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/RuntimeUtil.java
@@ -43,6 +43,13 @@ public final class RuntimeUtil {
             String level, boolean color, boolean json, boolean script, boolean 
export, String loggingConfigPath,
             List<String> loggingCategories)
             throws Exception {
+        configureLog(level, color, json, script, export, loggingConfigPath, 
loggingCategories, false);
+    }
+
+    public static void configureLog(
+            String level, boolean color, boolean json, boolean script, boolean 
export, String loggingConfigPath,
+            List<String> loggingCategories, boolean mcpStdio)
+            throws Exception {
         if (INIT_DONE.compareAndSet(false, true)) {
             long pid = ProcessHandle.current().pid();
             System.setProperty("pid", Long.toString(pid));
@@ -52,6 +59,8 @@ public final class RuntimeUtil {
             if (loggingConfigPath != null) {
                 // ust custom logging configuration as-is
                 Configurator.initialize("CamelJBang", "file://" + 
Path.of(loggingConfigPath).toAbsolutePath());
+            } else if (mcpStdio) {
+                Configurator.initialize("CamelJBang", 
"log4j2-mcp-stdio.properties");
             } else if (loggingCategories != null && 
!loggingCategories.isEmpty()) {
                 // enrich logging file with custom logging categories
                 String name = "log4j2-no-color.properties";
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-mcp-stdio.properties
 
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-mcp-stdio.properties
new file mode 100644
index 000000000000..ab66151a1d2a
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-mcp-stdio.properties
@@ -0,0 +1,38 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+status = warn
+name = McpStdioLog4jConfiguration
+packages = org.apache.camel
+
+appender.console.type = Console
+appender.console.name = console
+appender.console.target = SYSTEM_ERR
+appender.console.layout.type = PatternLayout
+appender.console.layout.pattern = %d{HH:mm:ss.SSS} %-5p [%t] %c{1} - %m%n
+
+rootLogger.level = warn
+rootLogger.appenderRef.console.ref = console
+
+logger.camel.name = org.apache.camel
+logger.camel.level = info
+logger.camel.additivity = false
+logger.camel.appenderRef.console.ref = console
+
+logger.mcp.name = org.apache.camel.component.mcp.server
+logger.mcp.level = info
+logger.mcp.additivity = false
+logger.mcp.appenderRef.console.ref = console
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunMcpStdioOptionTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunMcpStdioOptionTest.java
new file mode 100644
index 000000000000..6cbef36908a3
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunMcpStdioOptionTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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;
+
+import java.util.Properties;
+
+import org.apache.camel.main.KameletMain;
+import org.junit.jupiter.api.Test;
+import picocli.CommandLine;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class RunMcpStdioOptionTest {
+
+    @Test
+    void parseMcpStdioFlag() {
+        Run run = new Run(new CamelJBangMain());
+        CommandLine cmd = new CommandLine(run);
+        cmd.parseArgs("tools.yaml", "--mcp-stdio", "--mcp-tags=agent,crm");
+        assertThat(run.serverOptions.mcpStdio).isTrue();
+        assertThat(run.serverOptions.mcpTags).isEqualTo("agent,crm");
+    }
+
+    @Test
+    void mcpStdioDefaultsToFalse() {
+        Run run = new Run(new CamelJBangMain());
+        CommandLine cmd = new CommandLine(run);
+        cmd.parseArgs("tools.yaml");
+        assertThat(run.serverOptions.mcpStdio).isFalse();
+    }
+
+    @Test
+    void applyMcpStdioOverridesProfileHttpTransport() {
+        Run run = new Run(new CamelJBangMain());
+        new CommandLine(run).parseArgs("--mcp-stdio", "--mcp-tags=agent", 
"tools.yaml");
+        KameletMain main = new KameletMain("jbang");
+        Properties profile = new Properties();
+        profile.setProperty("camel.server.mcp-transport", "http");
+        profile.setProperty("camel.server.mcp-enabled", "false");
+
+        run.applyMcpStdioRuntimeOptions(main, profile);
+
+        
assertThat(main.getOverrideProperties().getProperty("camel.server.mcp-transport")).isEqualTo("stdio");
+        
assertThat(main.getOverrideProperties().getProperty("camel.server.mcp-enabled")).isEqualTo("true");
+        
assertThat(main.getOverrideProperties().getProperty("camel.server.mcp-tags")).isEqualTo("agent");
+        
assertThat(main.getOverrideProperties().getProperty("camel.main.startupSummaryLevel")).isEqualTo("Off");
+    }
+
+    @Test
+    void mcpStdioIsExclusiveFromDevMcpFlag() {
+        Run run = new Run(new CamelJBangMain());
+        new CommandLine(run).parseArgs("--mcp-stdio", "--mcp", "tools.yaml");
+        KameletMain main = new KameletMain("jbang");
+
+        assertThatThrownBy(() -> run.applyMcpStdioRuntimeOptions(main, null))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("--mcp-stdio and --mcp cannot be used 
together");
+    }
+
+    @Test
+    void mcpStdioIsExclusiveFromProfileDevMcpFlag() {
+        Run run = new Run(new CamelJBangMain());
+        new CommandLine(run).parseArgs("--mcp-stdio", "tools.yaml");
+        KameletMain main = new KameletMain("jbang");
+        Properties profile = new Properties();
+        profile.setProperty("camel.jbang.mcp", "true");
+
+        assertThatThrownBy(() -> run.applyMcpStdioRuntimeOptions(main, 
profile))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("--mcp-stdio and --mcp cannot be used 
together");
+    }
+
+    @Test
+    void applyMcpTagsOverrideForHttpAiToolServer() {
+        Run run = new Run(new CamelJBangMain());
+        new CommandLine(run).parseArgs("--mcp-tags=crm,notify", "tools.yaml");
+        KameletMain main = new KameletMain("jbang");
+        Properties profile = new Properties();
+        profile.setProperty("camel.server.mcp-enabled", "true");
+
+        assertThat(run.isAiToolMcpServerEnabled(profile)).isTrue();
+        run.applyMcpTagsOverride(main);
+
+        
assertThat(main.getOverrideProperties().getProperty("camel.server.mcp-tags")).isEqualTo("crm,notify");
+    }
+}

Reply via email to