[ 
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)

Reply via email to