[
https://issues.apache.org/jira/browse/CAMEL-24308?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Federico Mariani updated CAMEL-24308:
-------------------------------------
Description:
h2. Goal
Add a new {{camel-mcp-server}} module that exposes Camel routes registered via
the {{ai-tool}} component (CAMEL-23382) as MCP tools, served over MCP
streamable HTTP. No route is needed for the server itself — add the dependency,
configure via properties, done.
Tools are discovered from the shared {{AiToolRegistry}} by tag and invoked
through {{AiToolExecutor}}, the same contract used by the langchain4j-agent and
spring-ai-chat adapters. The MCP server is simply a third adapter over the same
registry.
h2. Example usage
The intended user experience: no code and no route for the server itself — it
behaves like Jolokia or Prometheus. Add the dependency, set a few properties,
and every {{ai-tool}} route with a matching tag becomes an MCP tool that any
MCP client (another Camel app, an IDE, a coding agent) can discover and call.
Add the dependency:
{code:xml}
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-mcp-server</artifactId>
</dependency>
{code}
Configure via properties:
{code}
camel.server.mcp-enabled=true
camel.server.mcp-path=/mcp
camel.server.mcp-server-name=my-integration-app
camel.server.mcp-tags=crm,notify
{code}
Define tools as regular {{ai-tool}} routes:
{code}
- 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"
- route:
from:
uri: "ai-tool:send_email"
parameters:
description: "Send email notification"
tags: "notify"
parameter.to: string
parameter.subject: string
steps:
- to: "smtp://mail.example.com"
{code}
Tools whose tags match {{mcp-tags}} are automatically exposed via streamable
HTTP at {{http://localhost:8080/mcp}}. Any MCP client can then connect, e.g.
another Camel integration using the camel-openai MCP client:
{code:java}
from("direct:agent")
.to("openai:chat-completion"
+ "?model={{llm.model}}"
+ "&autoToolExecution=true"
+ "&mcpServer.myCamelTools.transportType=streamableHttp"
+ "&mcpServer.myCamelTools.url=http://localhost:8080/mcp");
{code}
or a local coding agent / IDE configured against the same URL.
h2. Architecture: follow the camel-a2a pattern
camel-a2a already serves a JSON-RPC + SSE agent protocol through platform-http
and is the model to follow:
* Discover a {{RestConsumerFactory}} (typically platform-http) at startup and
register the MCP endpoint routes with {{useStreaming=true}} (see
{{A2AConsumer}}).
* Stream SSE responses by returning a queue-backed {{InputStream}} fed by an
emitter (see {{SseQueueInputStream}} / {{A2AStreamEmitter}}) — the exchange
completes quickly and hands back a live stream; events are pumped in afterwards.
* Real-time SSE delivery through platform-http is verified on Vert.x since
CAMEL-23804 ({{text/event-stream}} responses get eager per-chunk flush).
This gives one module for all runtimes:
* *Camel Main* — camel-platform-http-vertx: verified SSE streaming,
non-blocking.
* *Quarkus* — camel-quarkus-platform-http reuses camel-platform-http-vertx
wholesale, same code path, no extension-specific work needed.
* *Spring Boot* — servlet binding flushes per write
({{DefaultHttpBinding.copyStream}}) so SSE should work, but it is not on the
verified list: an IT asserting events arrive before stream completion is
required. Long-lived streams pin a servlet thread — document this.
Deliberately NOT using the {{PlatformHttpPlugin}} SPI (jolokia-style): its
handler is a Vert.x {{Handler<RoutingContext>}} in practice, which would
exclude the Spring Boot servlet runtime.
h2. Protocol layer
Use the official MCP Java SDK (io.modelcontextprotocol) for the protocol types
and server logic — already a dependency in the tree ({{camel-openai}} uses it
as MCP client). Implement the streamable HTTP transport on top of the
platform-http consumer as described above: POST answering {{application/json}}
or {{text/event-stream}}, long-lived GET SSE channel for server notifications,
{{Mcp-Session-Id}} session management, DELETE for session termination.
A stdio transport (SDK built-in) should follow as a separate issue for
camel-jbang local development (expose a Camel route as a tool for a local
coding agent).
h2. Tool semantics and security
* Tools whose tags intersect {{mcp-tags}} are exposed. The untagged default
pool is NOT exposed implicitly — external MCP clients are untrusted senders and
crossing that trust boundary must be an explicit opt-in per tool (tag it).
* MCP has a flat tool namespace: fail fast at startup (or refuse the tool with
a loud warning) on name collisions across the selected tags instead of silent
first-wins.
* {{tools/list_changed}}: requires adding a listener SPI to {{AiToolRegistry}}
(register/deregister callbacks) so the server can push notifications when
routes start/stop/suspend. Small prerequisite change in camel-ai-tool.
* Map {{AiToolResult}} to {{CallToolResult}}: {{ArgumentError}} -> isError=true
with the validation message; {{ExecutionError}} -> isError=true with a GENERIC
message only (per the {{AiToolResult}} security note, raw route exception
messages must not leak to remote clients; log the cause server-side).
* Per-call execution timeout ({{camel.server.mcp-tool-timeout}}, default e.g.
20s) — {{AiToolExecutor.execute}} is synchronous and unbounded; a hanging route
must not hold an MCP request open forever.
* Authentication: document wiring via platform-http authentication and
camel-oauth (MCP auth model is OAuth 2.1 resource server), mirroring the
{{oauthProfile}} idiom already used by the camel-openai MCP client.
h2. Out of scope (follow-up issues)
* stdio transport for camel-jbang.
* Raw JSON Schema tool input ({{argSchema}}) in camel-ai-tool — currently only
the flat {{parameter.NAME=type}} syntax exists; nested schemas are a common MCP
need. Note the executor argument allowlist must derive property names from the
raw schema (same bug class as CAMEL-24241).
* MCP tool annotations (readOnlyHint/destructiveHint/idempotentHint) as
optional ai-tool endpoint metadata.
* Structured content / outputSchema ({{AiToolResult}} is string-only today).
* Spring Boot / Quarkus native configuration layers if demand materializes —
the registry remains the SPI boundary.
The implementation is broken down into the attached sub-tasks.
----
_This issue was drafted by Claude Code on behalf of Federico Mariani._
was:
h2. Goal
Add a new {{camel-mcp-server}} module that exposes Camel routes registered via
the {{ai-tool}} component (CAMEL-23382) as MCP tools, served over MCP
streamable HTTP. No route is needed for the server itself — add the dependency,
configure via properties, done.
Tools are discovered from the shared {{AiToolRegistry}} by tag and invoked
through {{AiToolExecutor}}, the same contract used by the langchain4j-agent and
spring-ai-chat adapters. The MCP server is simply a third adapter over the same
registry.
h2. Example usage
The intended user experience: no code and no route for the server itself — it
behaves like Jolokia or Prometheus. Add the dependency, set a few properties,
and every {{ai-tool}} route with a matching tag becomes an MCP tool that any
MCP client (another Camel app, an IDE, a coding agent) can discover and call.
Add the dependency:
{code:xml}
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-mcp-server</artifactId>
</dependency>
{code}
Configure via properties:
{code}
camel.server.mcp-enabled=true
camel.server.mcp-path=/mcp
camel.server.mcp-server-name=my-integration-app
camel.server.mcp-tags=crm,notify
{code}
Define tools as regular {{ai-tool}} routes:
{code: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"
- route:
from:
uri: "ai-tool:send_email"
parameters:
description: "Send email notification"
tags: "notify"
parameter.to: string
parameter.subject: string
steps:
- to: "smtp://mail.example.com"
{code}
Tools whose tags match {{mcp-tags}} are automatically exposed via streamable
HTTP at {{http://localhost:8080/mcp}}. Any MCP client can then connect, e.g.
another Camel integration using the camel-openai MCP client:
{code:java}
from("direct:agent")
.to("openai:chat-completion"
+ "?model={{llm.model}}"
+ "&autoToolExecution=true"
+ "&mcpServer.myCamelTools.transportType=streamableHttp"
+ "&mcpServer.myCamelTools.url=http://localhost:8080/mcp");
{code}
or a local coding agent / IDE configured against the same URL.
h2. Architecture: follow the camel-a2a pattern
camel-a2a already serves a JSON-RPC + SSE agent protocol through platform-http
and is the model to follow:
* Discover a {{RestConsumerFactory}} (typically platform-http) at startup and
register the MCP endpoint routes with {{useStreaming=true}} (see
{{A2AConsumer}}).
* Stream SSE responses by returning a queue-backed {{InputStream}} fed by an
emitter (see {{SseQueueInputStream}} / {{A2AStreamEmitter}}) — the exchange
completes quickly and hands back a live stream; events are pumped in afterwards.
* Real-time SSE delivery through platform-http is verified on Vert.x since
CAMEL-23804 ({{text/event-stream}} responses get eager per-chunk flush).
This gives one module for all runtimes:
* *Camel Main* — camel-platform-http-vertx: verified SSE streaming,
non-blocking.
* *Quarkus* — camel-quarkus-platform-http reuses camel-platform-http-vertx
wholesale, same code path, no extension-specific work needed.
* *Spring Boot* — servlet binding flushes per write
({{DefaultHttpBinding.copyStream}}) so SSE should work, but it is not on the
verified list: an IT asserting events arrive before stream completion is
required. Long-lived streams pin a servlet thread — document this.
Deliberately NOT using the {{PlatformHttpPlugin}} SPI (jolokia-style): its
handler is a Vert.x {{Handler<RoutingContext>}} in practice, which would
exclude the Spring Boot servlet runtime.
h2. Protocol layer
Use the official MCP Java SDK (io.modelcontextprotocol) for the protocol types
and server logic — already a dependency in the tree ({{camel-openai}} uses it
as MCP client). Implement the streamable HTTP transport on top of the
platform-http consumer as described above: POST answering {{application/json}}
or {{text/event-stream}}, long-lived GET SSE channel for server notifications,
{{Mcp-Session-Id}} session management, DELETE for session termination.
A stdio transport (SDK built-in) should follow as a separate issue for
camel-jbang local development (expose a Camel route as a tool for a local
coding agent).
h2. Tool semantics and security
* Tools whose tags intersect {{mcp-tags}} are exposed. The untagged default
pool is NOT exposed implicitly — external MCP clients are untrusted senders and
crossing that trust boundary must be an explicit opt-in per tool (tag it).
* MCP has a flat tool namespace: fail fast at startup (or refuse the tool with
a loud warning) on name collisions across the selected tags instead of silent
first-wins.
* {{tools/list_changed}}: requires adding a listener SPI to {{AiToolRegistry}}
(register/deregister callbacks) so the server can push notifications when
routes start/stop/suspend. Small prerequisite change in camel-ai-tool.
* Map {{AiToolResult}} to {{CallToolResult}}: {{ArgumentError}} -> isError=true
with the validation message; {{ExecutionError}} -> isError=true with a GENERIC
message only (per the {{AiToolResult}} security note, raw route exception
messages must not leak to remote clients; log the cause server-side).
* Per-call execution timeout ({{camel.server.mcp-tool-timeout}}, default e.g.
20s) — {{AiToolExecutor.execute}} is synchronous and unbounded; a hanging route
must not hold an MCP request open forever.
* Authentication: document wiring via platform-http authentication and
camel-oauth (MCP auth model is OAuth 2.1 resource server), mirroring the
{{oauthProfile}} idiom already used by the camel-openai MCP client.
h2. Out of scope (follow-up issues)
* stdio transport for camel-jbang.
* Raw JSON Schema tool input ({{argSchema}}) in camel-ai-tool — currently only
the flat {{parameter.NAME=type}} syntax exists; nested schemas are a common MCP
need. Note the executor argument allowlist must derive property names from the
raw schema (same bug class as CAMEL-24241).
* MCP tool annotations (readOnlyHint/destructiveHint/idempotentHint) as
optional ai-tool endpoint metadata.
* Structured content / outputSchema ({{AiToolResult}} is string-only today).
* Spring Boot / Quarkus native configuration layers if demand materializes —
the registry remains the SPI boundary.
The implementation is broken down into the attached sub-tasks.
----
_This issue was drafted by Claude Code on behalf of Federico Mariani._
> camel-mcp-server - expose ai-tool routes as MCP tools over streamable HTTP
> --------------------------------------------------------------------------
>
> Key: CAMEL-24308
> URL: https://issues.apache.org/jira/browse/CAMEL-24308
> Project: Camel
> Issue Type: New Feature
> Components: camel-ai
> Reporter: Federico Mariani
> Priority: Major
> Labels: ai, mcp
>
> h2. Goal
> Add a new {{camel-mcp-server}} module that exposes Camel routes registered
> via the {{ai-tool}} component (CAMEL-23382) as MCP tools, served over MCP
> streamable HTTP. No route is needed for the server itself — add the
> dependency, configure via properties, done.
> Tools are discovered from the shared {{AiToolRegistry}} by tag and invoked
> through {{AiToolExecutor}}, the same contract used by the langchain4j-agent
> and spring-ai-chat adapters. The MCP server is simply a third adapter over
> the same registry.
> h2. Example usage
> The intended user experience: no code and no route for the server itself — it
> behaves like Jolokia or Prometheus. Add the dependency, set a few properties,
> and every {{ai-tool}} route with a matching tag becomes an MCP tool that any
> MCP client (another Camel app, an IDE, a coding agent) can discover and call.
> Add the dependency:
> {code:xml}
> <dependency>
> <groupId>org.apache.camel</groupId>
> <artifactId>camel-mcp-server</artifactId>
> </dependency>
> {code}
> Configure via properties:
> {code}
> camel.server.mcp-enabled=true
> camel.server.mcp-path=/mcp
> camel.server.mcp-server-name=my-integration-app
> camel.server.mcp-tags=crm,notify
> {code}
> Define tools as regular {{ai-tool}} routes:
> {code}
> - 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"
> - route:
> from:
> uri: "ai-tool:send_email"
> parameters:
> description: "Send email notification"
> tags: "notify"
> parameter.to: string
> parameter.subject: string
> steps:
> - to: "smtp://mail.example.com"
> {code}
> Tools whose tags match {{mcp-tags}} are automatically exposed via streamable
> HTTP at {{http://localhost:8080/mcp}}. Any MCP client can then connect, e.g.
> another Camel integration using the camel-openai MCP client:
> {code:java}
> from("direct:agent")
> .to("openai:chat-completion"
> + "?model={{llm.model}}"
> + "&autoToolExecution=true"
> + "&mcpServer.myCamelTools.transportType=streamableHttp"
> + "&mcpServer.myCamelTools.url=http://localhost:8080/mcp");
> {code}
> or a local coding agent / IDE configured against the same URL.
> h2. Architecture: follow the camel-a2a pattern
> camel-a2a already serves a JSON-RPC + SSE agent protocol through
> platform-http and is the model to follow:
> * Discover a {{RestConsumerFactory}} (typically platform-http) at startup and
> register the MCP endpoint routes with {{useStreaming=true}} (see
> {{A2AConsumer}}).
> * Stream SSE responses by returning a queue-backed {{InputStream}} fed by an
> emitter (see {{SseQueueInputStream}} / {{A2AStreamEmitter}}) — the exchange
> completes quickly and hands back a live stream; events are pumped in
> afterwards.
> * Real-time SSE delivery through platform-http is verified on Vert.x since
> CAMEL-23804 ({{text/event-stream}} responses get eager per-chunk flush).
> This gives one module for all runtimes:
> * *Camel Main* — camel-platform-http-vertx: verified SSE streaming,
> non-blocking.
> * *Quarkus* — camel-quarkus-platform-http reuses camel-platform-http-vertx
> wholesale, same code path, no extension-specific work needed.
> * *Spring Boot* — servlet binding flushes per write
> ({{DefaultHttpBinding.copyStream}}) so SSE should work, but it is not on the
> verified list: an IT asserting events arrive before stream completion is
> required. Long-lived streams pin a servlet thread — document this.
> Deliberately NOT using the {{PlatformHttpPlugin}} SPI (jolokia-style): its
> handler is a Vert.x {{Handler<RoutingContext>}} in practice, which would
> exclude the Spring Boot servlet runtime.
> h2. Protocol layer
> Use the official MCP Java SDK (io.modelcontextprotocol) for the protocol
> types and server logic — already a dependency in the tree ({{camel-openai}}
> uses it as MCP client). Implement the streamable HTTP transport on top of the
> platform-http consumer as described above: POST answering
> {{application/json}} or {{text/event-stream}}, long-lived GET SSE channel for
> server notifications, {{Mcp-Session-Id}} session management, DELETE for
> session termination.
> A stdio transport (SDK built-in) should follow as a separate issue for
> camel-jbang local development (expose a Camel route as a tool for a local
> coding agent).
> h2. Tool semantics and security
> * Tools whose tags intersect {{mcp-tags}} are exposed. The untagged default
> pool is NOT exposed implicitly — external MCP clients are untrusted senders
> and crossing that trust boundary must be an explicit opt-in per tool (tag it).
> * MCP has a flat tool namespace: fail fast at startup (or refuse the tool
> with a loud warning) on name collisions across the selected tags instead of
> silent first-wins.
> * {{tools/list_changed}}: requires adding a listener SPI to
> {{AiToolRegistry}} (register/deregister callbacks) so the server can push
> notifications when routes start/stop/suspend. Small prerequisite change in
> camel-ai-tool.
> * Map {{AiToolResult}} to {{CallToolResult}}: {{ArgumentError}} ->
> isError=true with the validation message; {{ExecutionError}} -> isError=true
> with a GENERIC message only (per the {{AiToolResult}} security note, raw
> route exception messages must not leak to remote clients; log the cause
> server-side).
> * Per-call execution timeout ({{camel.server.mcp-tool-timeout}}, default e.g.
> 20s) — {{AiToolExecutor.execute}} is synchronous and unbounded; a hanging
> route must not hold an MCP request open forever.
> * Authentication: document wiring via platform-http authentication and
> camel-oauth (MCP auth model is OAuth 2.1 resource server), mirroring the
> {{oauthProfile}} idiom already used by the camel-openai MCP client.
> h2. Out of scope (follow-up issues)
> * stdio transport for camel-jbang.
> * Raw JSON Schema tool input ({{argSchema}}) in camel-ai-tool — currently
> only the flat {{parameter.NAME=type}} syntax exists; nested schemas are a
> common MCP need. Note the executor argument allowlist must derive property
> names from the raw schema (same bug class as CAMEL-24241).
> * MCP tool annotations (readOnlyHint/destructiveHint/idempotentHint) as
> optional ai-tool endpoint metadata.
> * Structured content / outputSchema ({{AiToolResult}} is string-only today).
> * Spring Boot / Quarkus native configuration layers if demand materializes —
> the registry remains the SPI boundary.
> The implementation is broken down into the attached sub-tasks.
> ----
> _This issue was drafted by Claude Code on behalf of Federico Mariani._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)