Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions bom/camel-bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1667,6 +1667,16 @@
<artifactId>camel-master</artifactId>
<version>4.22.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-mcp-server</artifactId>
<version>4.22.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-mcp-server-api</artifactId>
<version>4.22.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-mdc</artifactId>
Expand Down
10 changes: 10 additions & 0 deletions catalog/camel-allcomponents/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1457,6 +1457,16 @@
<artifactId>camel-master</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-mcp-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-mcp-server-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-mdc</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ main
mapstruct-component
marshal-eip
master-component
mcp-server
mdc
message
message-broker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
|===
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
= MCP Server Component
:doctitle: MCP Server
:shortname: mcp-server
:artifactid: camel-mcp-server
:description: Expose ai-tool routes as MCP tools over streamable HTTP
:since: 4.22
:supportlevel: Preview
:tabs-sync-option:

*Since Camel {since}*

The camel-mcp-server module exposes Camel routes registered via the
xref:ROOT:ai-tool-component.adoc[ai-tool] component as tools 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 that any MCP client (another Camel application, an IDE, a
coding agent) can discover and call.

Maven users will need to add the following dependency to their `pom.xml`:

[source,xml]
----
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-mcp-server</artifactId>
<version>x.x.x</version>
<!-- use the same version as your Camel core version -->
</dependency>
----

== Architecture

The module is split in two artifacts:

* `camel-mcp-server-api` — the runtime-agnostic _bridge_ and the small
`McpServerEngine` SPI. The bridge owns tool selection (tags), execution via
the shared `AiToolExecutor` (per-call timeout, error sanitization) and reacts
to `AiToolRegistry` 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.

Engine resolution mirrors the platform-http engine: a bean of type
`McpServerEngine` in the Camel registry wins; otherwise the engine is
discovered on the classpath. Other runtimes plug native engines through the
same SPI: on Quarkus the `camel-quarkus-mcp-server` extension serves through
the Quarkiverse `quarkus-mcp-server` (configured via `quarkus.mcp.server.*`),
and on Spring Boot the starter serves through the Spring AI MCP server
(configured via `spring.ai.mcp.server.*`). Bridge behavior — tag selection,
timeout, sanitization — is identical on every runtime and verified by a shared
conformance test kit.

== Usage

Define tools as regular `ai-tool` routes and give them tags:

[source,yaml]
----
- route:
from:
uri: "ai-tool:query_db"
parameters:
description: "Query customer database"
tags: "crm"
parameter.customerId: string
parameter.customerId.description: "The customer id"
parameter.customerId.required: "true"
steps:
- to: "jdbc:dataSource"
----

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]
----
McpServerConfiguration configuration = new McpServerConfiguration();
configuration.setTags("crm,notify");
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
xref:ROOT:openai-component.adoc[camel-openai] MCP client:

[source,java]
----
from("direct:agent")
.to("openai:chat-completion"
+ "?model={{llm.model}}"
+ "&autoToolExecution=true"
+ "&mcpServer.myCamelTools.transportType=streamableHttp"
+ "&mcpServer.myCamelTools.url=http://localhost:8080/mcp");
----

== 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

| `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
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.

== Protocol

The Vert.x engine implements the MCP streamable HTTP transport:

* `POST /mcp` answering `application/json` or `text/event-stream` depending on
the request,
* a long-lived `GET /mcp` SSE channel for server notifications, with
`Last-Event-ID` replay,
* session management via the `Mcp-Session-Id` header and `DELETE /mcp` for
session termination.

Tools appearing or disappearing (routes starting and stopping) emit
`notifications/tools/list_changed` to connected clients.

== Security

External MCP clients are *untrusted senders* under the
xref:manual::security-model.adoc[Camel security model]. The module applies the
following rules:

* *Explicit opt-in per tool*: only tools whose tags intersect the configured
`tags` are exposed. The untagged default pool is never exposed implicitly.
* *Flat namespace protection*: a tool whose name collides with an already
exposed tool is refused with an ERROR log — never silently replaced.
* *Error sanitization*: route exceptions are mapped to a generic error
message; the cause is logged server-side and never sent to the client.
Argument validation messages (missing or invalid parameters) are returned
as-is.
* *Bounded execution*: every call is subject to the `toolTimeout`. 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
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.

== Runtime notes

* *Camel Main / JBang*: 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.
* *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
Spring AI MCP server).
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,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 },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ lra
mail-microsoft-oauth
main
management
mcp-server
mdc
micrometer-observability
micrometer-prometheus
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"other": {
"kind": "other",
"name": "mcp-server",
"title": "MCP Server",
"description": "Expose ai-tool routes as MCP tools over streamable HTTP",
"deprecated": false,
"firstVersion": "4.22.0",
"label": "ai",
"supportLevel": "Preview",
"groupId": "org.apache.camel",
"artifactId": "camel-mcp-server",
"version": "4.22.0-SNAPSHOT"
}
}
Loading