This is an automated email from the ASF dual-hosted git repository.
Croway 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 996f8ce4c7bb CAMEL-24311: camel.server.mcp-* configuration properties
and camel-main autowiring (#25309)
996f8ce4c7bb is described below
commit 996f8ce4c7bb3b24faf2602071d400d2fc568d81
Author: Federico Mariani <[email protected]>
AuthorDate: Tue Aug 4 12:47:01 2026 +0200
CAMEL-24311: camel.server.mcp-* configuration properties and camel-main
autowiring (#25309)
* CAMEL-24311: camel.server.mcp-* configuration properties and camel-main
autowiring
The MCP server now starts from properties alone, like Jolokia or
Prometheus - no code and no route for the server itself:
camel.server.enabled = true
camel.server.mcp-enabled = true
camel.server.mcp-tags = crm,notify
- HttpServerConfigurationProperties: mcpEnabled, mcpTags,
mcpToolTimeout (20s), mcpPath (/mcp), mcpServerName options with
regenerated configurer, main metadata and main.adoc.
- McpServerFactory SPI in camel-main (mirrors MainHttpServerFactory);
BaseMainSupport resolves it via bootstrap FactoryFinder when
mcp-enabled=true and adds the bridge after the HTTP server so the
engine finds the running router. With the HTTP server disabled the
bridge still starts and fails fast with an actionable message.
- DefaultMcpServerFactory in camel-mcp-server maps the options onto
McpServerConfiguration.
- Docs (CAMEL-24314): properties-based quick start replaces the
programmatic example as primary; options table now names the
camel.server.mcp-* properties.
- Catalog harvest for the new modules (others.properties,
others/mcp-server.json, docs copies).
Co-Authored-By: Claude Fable 5 <[email protected]>
* CAMEL-24311: camel-mcp-server - regen catalog docs copy and
known-dependencies
Co-Authored-By: Claude Fable 5 <[email protected]>
* CAMEL-24311: add @since to McpServerFactory
Co-Authored-By: Claude Fable 5 <[email protected]>
---------
Co-authored-by: Claude Fable 5 <[email protected]>
---
.../org/apache/camel/catalog/docs/main.adoc | 7 +-
.../org/apache/camel/catalog/docs/mcp-server.adoc | 44 +++++---
.../main/camel-main-configuration-metadata.json | 5 +
components/camel-ai/camel-mcp-server/pom.xml | 4 +
.../META-INF/services/org/apache/camel/mcp-server | 2 +
.../camel-mcp-server/src/main/docs/mcp-server.adoc | 44 +++++---
.../mcp/server/main/DefaultMcpServerFactory.java | 44 ++++++++
.../server/main/McpServerMainPropertiesTest.java | 103 +++++++++++++++++++
...ttpServerConfigurationPropertiesConfigurer.java | 35 +++++++
.../camel-main-configuration-metadata.json | 5 +
core/camel-main/src/main/docs/main.adoc | 7 +-
.../org/apache/camel/main/BaseMainSupport.java | 28 +++++-
.../main/HttpServerConfigurationProperties.java | 112 +++++++++++++++++++++
.../java/org/apache/camel/main/MainConstants.java | 1 +
.../org/apache/camel/main/McpServerFactory.java | 39 +++++++
...mel-factoryfinder-known-dependencies.properties | 1 +
16 files changed, 450 insertions(+), 31 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 352d5a8e8fd5..ffa14c8dfd9d 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 21 options, which are listed below.
+The camel.server supports 26 options, which are listed below.
[width="100%",cols="2,5,^1,2",options="header"]
|===
@@ -215,6 +215,11 @@ The camel.server supports 21 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
over streamable HTTP. Requires camel-mcp-server on the classpath. By default,
the MCP server is not enabled. | false | boolean
+| *camel.server.mcpPath* | HTTP path where the MCP endpoint is served. | /mcp
| String
+| *camel.server.mcpServerName* | MCP server name advertised to clients.
Defaults to the CamelContext name. | | String
+| *camel.server.mcpTags* | Comma-separated list of ai-tool tags to expose as
MCP tools. Only tools registered under one of these tags are exposed; the
untagged default pool is never exposed. When not set, no tools are 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.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 6b28142ca981..17e3b436e191 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
@@ -101,8 +101,19 @@ YAML::
----
====
-Start the MCP server by adding the `McpServerBridge` service to the
-CamelContext, selecting the tags to expose:
+On Camel Main and Camel JBang no code is needed — like Jolokia or
+Prometheus, the server starts from configuration properties alone:
+
+[source,properties]
+----
+camel.server.enabled = true
+camel.server.mcp-enabled = true
+camel.server.mcp-tags = crm,notify
+camel.server.mcp-server-name = my-integration-app
+----
+
+On other runtimes, or when wiring programmatically, add the
+`McpServerBridge` service to the CamelContext instead:
[source,java]
----
@@ -128,23 +139,28 @@ from("direct:agent")
== Options
-The `McpServerConfiguration` options:
+The options, configurable as `camel.server.mcp-*` properties on Camel Main /
+JBang (see the xref:main.adoc[camel-main] options) or on
+`McpServerConfiguration` programmatically:
[width="100%",cols="2,5,2,1",options="header"]
|===
| Option | Description | Default | Owner
-| `tags` | Comma-separated list of ai-tool tags to expose as MCP tools. Only
- tools registered under one of these tags are published; the untagged
- default pool is never exposed. When not set, no tools are published. | |
- bridge
-| `toolTimeout` | Per-call 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` |
- bridge
-| `path` | HTTP path where the MCP endpoint is served. | `/mcp` | engine
-| `serverName` | MCP server name advertised to clients. | CamelContext name |
- engine
+| `camel.server.mcp-enabled` | Whether to expose ai-tool routes as MCP tools
+ over streamable HTTP. | `false` | bridge
+| `camel.server.mcp-tags` | Comma-separated list of ai-tool tags to expose as
+ MCP tools. Only tools registered under one of these tags are published; the
+ untagged default pool is never exposed. When not set, no tools are
+ published. | | bridge
+| `camel.server.mcp-tool-timeout` | Per-call 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` | bridge
+| `camel.server.mcp-path` | HTTP path where the MCP endpoint is served. |
+ `/mcp` | engine
+| `camel.server.mcp-server-name` | MCP server name advertised to clients. |
+ CamelContext name | engine
|===
Bridge-owned options are honored identically on every runtime. Engine-owned
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 4c5a8c43a5d7..5630025faa29 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
@@ -437,6 +437,11 @@
{ "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 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.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.mcpServerName", "required": false, "description":
"MCP server name advertised to clients. Defaults to the CamelContext name.",
"sourceType": "org.apache.camel.main.HttpServerConfigurationProperties",
"type": "string", "javaType": "java.lang.String", "secret": false },
+ { "name": "camel.server.mcpTags", "required": false, "description":
"Comma-separated list of ai-tool tags to expose as MCP tools. Only tools
registered under one of these tags are exposed; the untagged default pool is
never exposed. When not set, no tools are exposed.", "sourceType":
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string",
"javaType": "java.lang.String", "secret": false },
+ { "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.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/pom.xml
b/components/camel-ai/camel-mcp-server/pom.xml
index 40db5a9dc091..2adf333c000d 100644
--- a/components/camel-ai/camel-mcp-server/pom.xml
+++ b/components/camel-ai/camel-mcp-server/pom.xml
@@ -45,6 +45,10 @@
<groupId>org.apache.camel</groupId>
<artifactId>camel-mcp-server-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-main</artifactId>
+ </dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-platform-http-vertx</artifactId>
diff --git
a/components/camel-ai/camel-mcp-server/src/generated/resources/META-INF/services/org/apache/camel/mcp-server
b/components/camel-ai/camel-mcp-server/src/generated/resources/META-INF/services/org/apache/camel/mcp-server
new file mode 100644
index 000000000000..240ad12e3b06
--- /dev/null
+++
b/components/camel-ai/camel-mcp-server/src/generated/resources/META-INF/services/org/apache/camel/mcp-server
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.mcp.server.main.DefaultMcpServerFactory
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 6b28142ca981..17e3b436e191 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
@@ -101,8 +101,19 @@ YAML::
----
====
-Start the MCP server by adding the `McpServerBridge` service to the
-CamelContext, selecting the tags to expose:
+On Camel Main and Camel JBang no code is needed — like Jolokia or
+Prometheus, the server starts from configuration properties alone:
+
+[source,properties]
+----
+camel.server.enabled = true
+camel.server.mcp-enabled = true
+camel.server.mcp-tags = crm,notify
+camel.server.mcp-server-name = my-integration-app
+----
+
+On other runtimes, or when wiring programmatically, add the
+`McpServerBridge` service to the CamelContext instead:
[source,java]
----
@@ -128,23 +139,28 @@ from("direct:agent")
== Options
-The `McpServerConfiguration` options:
+The options, configurable as `camel.server.mcp-*` properties on Camel Main /
+JBang (see the xref:main.adoc[camel-main] options) or on
+`McpServerConfiguration` programmatically:
[width="100%",cols="2,5,2,1",options="header"]
|===
| Option | Description | Default | Owner
-| `tags` | Comma-separated list of ai-tool tags to expose as MCP tools. Only
- tools registered under one of these tags are published; the untagged
- default pool is never exposed. When not set, no tools are published. | |
- bridge
-| `toolTimeout` | Per-call 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` |
- bridge
-| `path` | HTTP path where the MCP endpoint is served. | `/mcp` | engine
-| `serverName` | MCP server name advertised to clients. | CamelContext name |
- engine
+| `camel.server.mcp-enabled` | Whether to expose ai-tool routes as MCP tools
+ over streamable HTTP. | `false` | bridge
+| `camel.server.mcp-tags` | Comma-separated list of ai-tool tags to expose as
+ MCP tools. Only tools registered under one of these tags are published; the
+ untagged default pool is never exposed. When not set, no tools are
+ published. | | bridge
+| `camel.server.mcp-tool-timeout` | Per-call 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` | bridge
+| `camel.server.mcp-path` | HTTP path where the MCP endpoint is served. |
+ `/mcp` | engine
+| `camel.server.mcp-server-name` | MCP server name advertised to clients. |
+ CamelContext name | engine
|===
Bridge-owned options are honored identically on every runtime. Engine-owned
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
new file mode 100644
index 000000000000..d524fa46fa20
--- /dev/null
+++
b/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/main/DefaultMcpServerFactory.java
@@ -0,0 +1,44 @@
+/*
+ * 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.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.main.HttpServerConfigurationProperties;
+import org.apache.camel.main.MainConstants;
+import org.apache.camel.main.McpServerFactory;
+import org.apache.camel.spi.annotations.JdkService;
+
+/**
+ * {@link McpServerFactory} creating the {@link McpServerBridge} from the
{@code camel.server.mcp-*} options, so the MCP
+ * server starts from properties alone on Camel Main / JBang.
+ */
+@JdkService(MainConstants.MCP_SERVER)
+public class DefaultMcpServerFactory implements McpServerFactory {
+
+ @Override
+ public Service newMcpServer(CamelContext camelContext,
HttpServerConfigurationProperties configuration) {
+ McpServerConfiguration mcpConfiguration = new McpServerConfiguration();
+ mcpConfiguration.setTags(configuration.getMcpTags());
+ mcpConfiguration.setToolTimeout(configuration.getMcpToolTimeout());
+ mcpConfiguration.setPath(configuration.getMcpPath());
+ mcpConfiguration.setServerName(configuration.getMcpServerName());
+ return new McpServerBridge(mcpConfiguration);
+ }
+}
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
new file mode 100644
index 000000000000..872d3bb7f93b
--- /dev/null
+++
b/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/main/McpServerMainPropertiesTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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 java.time.Duration;
+import java.util.Map;
+
+import io.modelcontextprotocol.client.McpClient;
+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.main.Main;
+import org.apache.camel.test.AvailablePortFinder;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * Verifies the camel-main autowiring (CAMEL-24311): the MCP server starts
from {@code camel.server.mcp-*} properties
+ * alone — no code and no route for the server itself.
+ */
+class McpServerMainPropertiesTest {
+
+ @Test
+ void testMcpServerStartsFromPropertiesAlone() throws Exception {
+ int port = AvailablePortFinder.getNextAvailable();
+
+ Main main = new Main();
+ main.configure().addRoutesBuilder(new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("ai-tool:say_hello?tags=crm&description=Say hello"
+ + "¶meter.name=string¶meter.name.required=true")
+ .setBody(simple("Hello ${header.name}"));
+
+ from("ai-tool:hidden_tool?description=Untagged, not exposed")
+ .setBody(constant("hidden"));
+ }
+ });
+ main.addInitialProperty("camel.server.enabled", "true");
+ main.addInitialProperty("camel.server.port", String.valueOf(port));
+ main.addInitialProperty("camel.server.mcp-enabled", "true");
+ main.addInitialProperty("camel.server.mcp-tags", "crm");
+ main.addInitialProperty("camel.server.mcp-server-name",
"my-integration-app");
+ main.start();
+
+ McpSyncClient client = null;
+ try {
+ client =
McpClient.sync(HttpClientStreamableHttpTransport.builder("http://localhost:" +
port).build())
+ .requestTimeout(Duration.ofSeconds(10))
+ .initializationTimeout(Duration.ofSeconds(10))
+ .build();
+
+ McpSchema.InitializeResult init = client.initialize();
+
assertThat(init.serverInfo().name()).isEqualTo("my-integration-app");
+
+ assertThat(client.listTools().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(result.content().toString()).contains("Hello Camel");
+ } finally {
+ if (client != null) {
+ client.closeGracefully();
+ }
+ main.stop();
+ }
+ }
+
+ @Test
+ void testMcpEnabledWithoutHttpServerFailsFast() {
+ Main main = new Main();
+ main.addInitialProperty("camel.server.mcp-enabled", "true");
+ main.addInitialProperty("camel.server.mcp-tags", "crm");
+
+ try {
+ assertThatThrownBy(main::start)
+ .hasStackTraceContaining("Vert.x platform HTTP server");
+ } finally {
+ main.stop();
+ }
+ }
+}
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 7c1d68afe45d..9129b2b45a81 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
@@ -37,6 +37,11 @@ public class HttpServerConfigurationPropertiesConfigurer
extends org.apache.came
map.put("JwtKeystorePath", java.lang.String.class);
map.put("JwtKeystoreType", java.lang.String.class);
map.put("MaxBodySize", java.lang.Long.class);
+ map.put("McpEnabled", boolean.class);
+ map.put("McpPath", java.lang.String.class);
+ map.put("McpServerName", java.lang.String.class);
+ map.put("McpTags", java.lang.String.class);
+ map.put("McpToolTimeout", long.class);
map.put("Path", java.lang.String.class);
map.put("Port", int.class);
map.put("StaticContextPath", java.lang.String.class);
@@ -78,6 +83,16 @@ public class HttpServerConfigurationPropertiesConfigurer
extends org.apache.came
case "jwtKeystoreType":
target.setJwtKeystoreType(property(camelContext, java.lang.String.class,
value)); return true;
case "maxbodysize":
case "maxBodySize": target.setMaxBodySize(property(camelContext,
java.lang.Long.class, value)); return true;
+ case "mcpenabled":
+ case "mcpEnabled": target.setMcpEnabled(property(camelContext,
boolean.class, value)); return true;
+ case "mcppath":
+ case "mcpPath": target.setMcpPath(property(camelContext,
java.lang.String.class, value)); return true;
+ case "mcpservername":
+ case "mcpServerName": target.setMcpServerName(property(camelContext,
java.lang.String.class, value)); return true;
+ case "mcptags":
+ 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 "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":
@@ -128,6 +143,16 @@ public class HttpServerConfigurationPropertiesConfigurer
extends org.apache.came
case "jwtKeystoreType": return java.lang.String.class;
case "maxbodysize":
case "maxBodySize": return java.lang.Long.class;
+ case "mcpenabled":
+ case "mcpEnabled": return boolean.class;
+ case "mcppath":
+ case "mcpPath": return java.lang.String.class;
+ case "mcpservername":
+ case "mcpServerName": return java.lang.String.class;
+ case "mcptags":
+ case "mcpTags": return java.lang.String.class;
+ case "mcptooltimeout":
+ case "mcpToolTimeout": return long.class;
case "path": return java.lang.String.class;
case "port": return int.class;
case "staticcontextpath":
@@ -174,6 +199,16 @@ public class HttpServerConfigurationPropertiesConfigurer
extends org.apache.came
case "jwtKeystoreType": return target.getJwtKeystoreType();
case "maxbodysize":
case "maxBodySize": return target.getMaxBodySize();
+ case "mcpenabled":
+ case "mcpEnabled": return target.isMcpEnabled();
+ case "mcppath":
+ case "mcpPath": return target.getMcpPath();
+ case "mcpservername":
+ case "mcpServerName": return target.getMcpServerName();
+ case "mcptags":
+ case "mcpTags": return target.getMcpTags();
+ case "mcptooltimeout":
+ case "mcpToolTimeout": return target.getMcpToolTimeout();
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 4c5a8c43a5d7..5630025faa29 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
@@ -437,6 +437,11 @@
{ "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 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.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.mcpServerName", "required": false, "description":
"MCP server name advertised to clients. Defaults to the CamelContext name.",
"sourceType": "org.apache.camel.main.HttpServerConfigurationProperties",
"type": "string", "javaType": "java.lang.String", "secret": false },
+ { "name": "camel.server.mcpTags", "required": false, "description":
"Comma-separated list of ai-tool tags to expose as MCP tools. Only tools
registered under one of these tags are exposed; the untagged default pool is
never exposed. When not set, no tools are exposed.", "sourceType":
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string",
"javaType": "java.lang.String", "secret": false },
+ { "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.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 352d5a8e8fd5..ffa14c8dfd9d 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 21 options, which are listed below.
+The camel.server supports 26 options, which are listed below.
[width="100%",cols="2,5,^1,2",options="header"]
|===
@@ -215,6 +215,11 @@ The camel.server supports 21 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
over streamable HTTP. Requires camel-mcp-server on the classpath. By default,
the MCP server is not enabled. | false | boolean
+| *camel.server.mcpPath* | HTTP path where the MCP endpoint is served. | /mcp
| String
+| *camel.server.mcpServerName* | MCP server name advertised to clients.
Defaults to the CamelContext name. | | String
+| *camel.server.mcpTags* | Comma-separated list of ai-tool tags to expose as
MCP tools. Only tools registered under one of these tags are exposed; the
untagged default pool is never exposed. When not set, no tools are 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.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/BaseMainSupport.java
b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
index d9560638622f..b9205a0a73b0 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
@@ -2188,7 +2188,9 @@ public abstract class BaseMainSupport extends BaseService
{
mainConfigurationProperties.isAutoConfigurationFailFast(),
true, autoConfiguredProperties);
if (!server.isEnabled()) {
- // http server is disabled
+ // http server is disabled; the mcp server (if enabled) is still
set up so it can
+ // fail-fast with an actionable message about the missing http
server
+ setupMcpServer(camelContext, server);
return;
}
@@ -2207,6 +2209,20 @@ public abstract class BaseMainSupport extends
BaseService {
// force eager starting as embedded http server is used for
// container platform to check readiness and need to be started eager
camelContext.addService(http, true, true);
+
+ // the mcp server serves through the http server, so it is set up (and
started) after it
+ setupMcpServer(camelContext, server);
+ }
+
+ private void setupMcpServer(CamelContext camelContext,
HttpServerConfigurationProperties server) throws Exception {
+ if (!server.isMcpEnabled()) {
+ return;
+ }
+ // auto-detect camel-mcp-server on classpath
+ McpServerFactory factory = resolveMcpServerFactory(camelContext);
+ Service mcp = factory.newMcpServer(camelContext, server);
+ // force eager starting so the mcp endpoint is served as soon as the
http server is up
+ camelContext.addService(mcp, true, true);
}
private void setHttpManagementServerProperties(
@@ -3179,6 +3195,16 @@ public abstract class BaseMainSupport extends
BaseService {
return CamelContextAware.trySetCamelContext(answer, camelContext);
}
+ private static McpServerFactory resolveMcpServerFactory(CamelContext
camelContext) {
+ // lookup in service registry first
+ McpServerFactory answer =
camelContext.getRegistry().findSingleByType(McpServerFactory.class);
+ if (answer == null) {
+ answer =
ResolverHelper.resolveMandatoryBootstrapService(camelContext,
MainConstants.MCP_SERVER,
+ McpServerFactory.class, "camel-mcp-server");
+ }
+ return CamelContextAware.trySetCamelContext(answer, camelContext);
+ }
+
private static final class PropertyPlaceholderListener implements
PropertiesLookupListener {
private final OrderedLocationProperties olp;
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 c153b60d1d3f..da2603c3cce3 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
@@ -71,6 +71,17 @@ public class HttpServerConfigurationProperties implements
BootstrapCloseable {
@Metadata(label = "security", defaultValue = "false", security =
"insecure:dev")
private boolean jwtAllowMissingIssuerAndAudience;
+ @Metadata
+ private boolean mcpEnabled;
+ @Metadata
+ private String mcpTags;
+ @Metadata(defaultValue = "20000")
+ private long mcpToolTimeout = 20000;
+ @Metadata(defaultValue = "/mcp")
+ private String mcpPath = "/mcp";
+ @Metadata
+ private String mcpServerName;
+
public HttpServerConfigurationProperties(MainConfigurationProperties
parent) {
this.parent = parent;
}
@@ -326,6 +337,64 @@ public class HttpServerConfigurationProperties implements
BootstrapCloseable {
this.jwtAllowMissingIssuerAndAudience =
jwtAllowMissingIssuerAndAudience;
}
+ public boolean isMcpEnabled() {
+ return mcpEnabled;
+ }
+
+ /**
+ * Whether to expose ai-tool routes as MCP tools over streamable HTTP.
Requires camel-mcp-server on the classpath.
+ * By default, the MCP server is not enabled.
+ */
+ public void setMcpEnabled(boolean mcpEnabled) {
+ this.mcpEnabled = mcpEnabled;
+ }
+
+ public String getMcpTags() {
+ return mcpTags;
+ }
+
+ /**
+ * Comma-separated list of ai-tool tags to expose as MCP tools. Only tools
registered under one of these tags are
+ * exposed; the untagged default pool is never exposed. When not set, no
tools are exposed.
+ */
+ public void setMcpTags(String mcpTags) {
+ this.mcpTags = mcpTags;
+ }
+
+ public long getMcpToolTimeout() {
+ return 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.
+ */
+ public void setMcpToolTimeout(long mcpToolTimeout) {
+ this.mcpToolTimeout = mcpToolTimeout;
+ }
+
+ public String getMcpPath() {
+ return mcpPath;
+ }
+
+ /**
+ * HTTP path where the MCP endpoint is served.
+ */
+ public void setMcpPath(String mcpPath) {
+ this.mcpPath = mcpPath;
+ }
+
+ public String getMcpServerName() {
+ return mcpServerName;
+ }
+
+ /**
+ * MCP server name advertised to clients. Defaults to the CamelContext
name.
+ */
+ public void setMcpServerName(String mcpServerName) {
+ this.mcpServerName = mcpServerName;
+ }
+
/**
* Whether embedded HTTP server is enabled. By default, the server is not
enabled.
*/
@@ -506,4 +575,47 @@ public class HttpServerConfigurationProperties implements
BootstrapCloseable {
return this;
}
+ /**
+ * Whether to expose ai-tool routes as MCP tools over streamable HTTP.
Requires camel-mcp-server on the classpath.
+ * By default, the MCP server is not enabled.
+ */
+ public HttpServerConfigurationProperties withMcpEnabled(boolean
mcpEnabled) {
+ this.mcpEnabled = mcpEnabled;
+ return this;
+ }
+
+ /**
+ * Comma-separated list of ai-tool tags to expose as MCP tools. Only tools
registered under one of these tags are
+ * exposed; the untagged default pool is never exposed. When not set, no
tools are exposed.
+ */
+ public HttpServerConfigurationProperties withMcpTags(String mcpTags) {
+ this.mcpTags = mcpTags;
+ return this;
+ }
+
+ /**
+ * 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.
+ */
+ public HttpServerConfigurationProperties withMcpToolTimeout(long
mcpToolTimeout) {
+ this.mcpToolTimeout = mcpToolTimeout;
+ return this;
+ }
+
+ /**
+ * HTTP path where the MCP endpoint is served.
+ */
+ public HttpServerConfigurationProperties withMcpPath(String mcpPath) {
+ this.mcpPath = mcpPath;
+ return this;
+ }
+
+ /**
+ * MCP server name advertised to clients. Defaults to the CamelContext
name.
+ */
+ public HttpServerConfigurationProperties withMcpServerName(String
mcpServerName) {
+ this.mcpServerName = mcpServerName;
+ return this;
+ }
+
}
diff --git
a/core/camel-main/src/main/java/org/apache/camel/main/MainConstants.java
b/core/camel-main/src/main/java/org/apache/camel/main/MainConstants.java
index 0304faa0acef..e0130140a264 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/MainConstants.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/MainConstants.java
@@ -24,6 +24,7 @@ public final class MainConstants {
public static final String CLOUD_PROPERTIES_LOCATION =
"camel.main.cloud-properties-location";
public static final String PROPERTY_PLACEHOLDER_LOCATION =
"camel.main.property-placeholder-location";
public static final String PLATFORM_HTTP_SERVER = "platform-http-server";
+ public static final String MCP_SERVER = "mcp-server";
public static final String PROFILE = "camel.main.profile";
private MainConstants() {
diff --git
a/core/camel-main/src/main/java/org/apache/camel/main/McpServerFactory.java
b/core/camel-main/src/main/java/org/apache/camel/main/McpServerFactory.java
new file mode 100644
index 000000000000..0216483ac157
--- /dev/null
+++ b/core/camel-main/src/main/java/org/apache/camel/main/McpServerFactory.java
@@ -0,0 +1,39 @@
+/*
+ * 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.main;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Service;
+
+/**
+ * Factory for creating the MCP server exposing ai-tool routes as MCP tools,
for standalone Camel (not Spring Boot or
+ * Quarkus). Provided by camel-mcp-server.
+ *
+ * @since 4.22
+ */
+public interface McpServerFactory {
+
+ /**
+ * Creates the MCP server bridge configured from the {@code
camel.server.mcp-*} options.
+ *
+ * @param camelContext the camel context
+ * @param configuration server configuration carrying the mcp options
+ * @return the bridge as a {@link Service} to be managed by
{@link org.apache.camel.CamelContext}.
+ */
+ Service newMcpServer(CamelContext camelContext,
HttpServerConfigurationProperties configuration);
+
+}
diff --git
a/dsl/camel-kamelet-main/src/generated/resources/camel-factoryfinder-known-dependencies.properties
b/dsl/camel-kamelet-main/src/generated/resources/camel-factoryfinder-known-dependencies.properties
index 1ce0ccf39027..e97aa071a654 100644
---
a/dsl/camel-kamelet-main/src/generated/resources/camel-factoryfinder-known-dependencies.properties
+++
b/dsl/camel-kamelet-main/src/generated/resources/camel-factoryfinder-known-dependencies.properties
@@ -36,6 +36,7 @@
META-INF/services/org/apache/camel/kafka-resume-strategy=camel:kafka
META-INF/services/org/apache/camel/kinesis-resume-strategy=camel:aws2-kinesis
META-INF/services/org/apache/camel/lra-saga-service=camel:lra
META-INF/services/org/apache/camel/mcp-server-engine=camel:mcp-server
+META-INF/services/org/apache/camel/mcp-server=camel:mcp-server
META-INF/services/org/apache/camel/mdc-service=camel:mdc
META-INF/services/org/apache/camel/micrometer-observability-tracer=camel:micrometer-observability
META-INF/services/org/apache/camel/micrometer-prometheus=camel:micrometer-prometheus