Federico Mariani created CAMEL-24308:
----------------------------------------

             Summary: 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


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



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to