Skip to content

Add camel-quarkus-mcp-server extension - #8950

Merged
jamesnetherton merged 9 commits into
apache:camel-mainfrom
Croway:mcp-server-extension
Aug 6, 2026
Merged

Add camel-quarkus-mcp-server extension#8950
jamesnetherton merged 9 commits into
apache:camel-mainfrom
Croway:mcp-server-extension

Conversation

@Croway

@Croway Croway commented Aug 3, 2026

Copy link
Copy Markdown

Fixes #8949. The Quarkus runtime of CAMEL-24308 (camel-mcp-server).

Targets the camel-main branch: camel-mcp-server-api is new in Camel 4.22. The camel side (apache/camel#25306) is merged; CI needs the 4.22.0-SNAPSHOT artifacts published to the Apache snapshots repository — draft until then. Verified locally against the installed 4.22.0-SNAPSHOT artifacts (JVM and native).

What this adds

A camel-quarkus-mcp-server extension (JVM and native) exposing ai-tool routes as MCP tools over streamable HTTP, served through the quarkiverse quarkus-mcp-server (1.13.1) — per the design recorded on CAMEL-24308: Quarkus reuses its native MCP ecosystem (dev UI, guardrails, CDI, config) instead of the MCP Java SDK.

  • Runtime: depends on camel-mcp-server-api (the runtime-agnostic bridge + McpServerEngine SPI) + quarkus-mcp-server-http. QuarkusMcpServerEngine maps toolAdded/toolRemoved onto the programmatic ToolManager API (newTool(...).addArgument(...).setHandler(...).register() / removeTool), with tool arguments derived from the ai-tool ParameterDef metadata and sanitized results mapped to ToolResponse.success/error. The MCP Java SDK and Reactor never reach the classpath.
  • Deployment: RuntimeCamelContextCustomizerBuildItem (gated on quarkus.camel.mcp-server.enabled) binds the engine into the Camel registry (the bridge resolves registry-first) and adds the McpServerBridge as a CamelContext-managed service.
  • Native support: the extension lives under extensions/ with nativeSince declared — the engine needed no reflection or resource registration, the native image builds clean and all conformance scenarios pass against the native binary (McpServerIT).
  • Config: quarkus.camel.mcp-server.enabled (default true) / .tags (unset ⇒ nothing exposed — the security default) / .tool-timeout (20s). Serving concerns (path, transports, dev UI, auth, guardrails) stay fully owned by quarkus.mcp.server.*, per the CAMEL-24311 precedence rule.
  • Docs: generated extension page with hand-written intro/usage — combined quarkus.camel.mcp-server.* / quarkus.mcp.server.* configuration example with the ownership split, a link to the quarkus-mcp-server configuration reference, YAML DSL tool definition with the full parameter syntax, MCP client configuration examples (camel-openai agent + mcp.json-style), a Serving over stdio section (add quarkus-mcp-server-stdio, disable the default server's HTTP transport with quarkus.mcp.server."<default>".http.enabled=false, launch as a subprocess — typically a native executable), dynamic tools and error-handling semantics, and Camel Main/Spring Boot pointers.
  • @Tool coexistence proven by test: quarkus-mcp-server annotated business methods and Camel ai-tool routes serve from the same MCP server and the same tools/list (annotated tools register at build time, Camel tools follow the route lifecycle).
  • Housekeeping done: cq:update-quarkus-metadata, docs regen, process-resources -Pformat, BOM entries for quarkus-mcp-server-http(-deployment) under quarkiverse-mcp-server.version, mcp-server assigned to the group-12 test category, dependabot entries.

Testing

integration-tests/mcp-server runs the engine conformance scenarios (CAMEL-24313) driven by McpAssured from quarkus-mcp-server-test over streamable HTTP (no MCP Java SDK on the test classpath): tag-filtered tools/list (untagged default pool excluded), call success, execution error sanitized to a generic message, per-call timeout, the tools list following route stop/start (tools/list_changed path), and @Tool coexistence. Route control goes through a JAX-RS resource so the same tests run in JVM mode (McpServerTest) and against the native binary (McpServerIT). All 6 scenarios green in both modes.


This PR was written by Claude Code on behalf of Federico Mariani (@Croway).

🤖 Generated with Claude Code

@jamesnetherton jamesnetherton left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial feedback before the Camel PR is merged. Otherwise it all looks good to me.

<description>Integration tests for Camel Quarkus MCP Server extension</description>

<properties>
<mcp-java-sdk.version>2.0.0</mcp-java-sdk.version>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this property to the root pom.xml with the other test version properties:

https://github.com/apache/camel-quarkus/blob/main/pom.xml#L187

Comment on lines +51 to +62
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-core</artifactId>
<version>${mcp-java-sdk.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-json-jackson2</artifactId>
<version>${mcp-java-sdk.version}</version>
<scope>test</scope>
</dependency>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add these to the dependabot config with the other test dependencies:

https://github.com/apache/camel-quarkus/blob/main/.github/dependabot.yml#L49

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use McpAssured from quarkus-mcp-server-test instead of the raw MCP SDK? It would replace both mcp-core and mcp-json-jackson2 test dependencies and their dependabot/BOM entries.

Comment on lines +46 to +47
@Inject
CamelContext camelContext;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of @Inject into the test class, can we add a JAX-RS resource class that interacts with the Camel context for the tests to interrogate the route status etc. It'll make life easier if we promote the extension to native mode support. You can get examples from other integration tests.

Comment thread pom.xml Outdated
<quarkiverse-freemarker.version>1.3.0</quarkiverse-freemarker.version><!-- https://repo1.maven.org/maven2/io/quarkiverse/freemarker/quarkus-freemarker-parent/ -->
<quarkiverse-fory.version>1.4.0</quarkiverse-fory.version><!-- https://repo1.maven.org/maven2/io/quarkiverse/fory/quarkus-fory-parent/ -->
<quarkiverse-groovy.version>3.38.0</quarkiverse-groovy.version><!-- https://repo1.maven.org/maven2/io/quarkiverse/groovy/quarkus-groovy-parent/ -->
<quarkiverse-mcp-server.version>1.13.1</quarkiverse-mcp-server.version><!-- https://repo1.maven.org/maven2/io/quarkiverse/mcp/quarkus-mcp-server-parent/ -->

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick - can we keep alphabetical order and move it above the quarkiverse-micrometer.version property.

Comment on lines +44 to +46
<!-- the runtime-agnostic MCP bridge and engine SPI; the serving engine is provided
by the quarkiverse quarkus-mcp-server extension below (NOT the camel-mcp-server
Vert.x engine used on Camel Main / JBang) -->

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can probably remove this.

@JiriOndrusek JiriOndrusek left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far looks good to me - but I need to review this PR in more details. Here is 1 small test issue (which caught my eye)

private McpSyncClient client() {
if (client == null) {
client = McpClient
.sync(HttpClientStreamableHttpTransport.builder("http://localhost:8081").build())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded 8081 can cause troubles if user changes port. Better is to get value from the property (quarkus.http.test-port.)

@JiriOndrusek

Copy link
Copy Markdown
Contributor

@Croway I'm curious, what is blocking the native support? to my understanding the camel-quarkus-mcp-servre worksin native (https://docs.quarkiverse.io/quarkus-mcp-server/dev/)

@Croway

Croway commented Aug 4, 2026

Copy link
Copy Markdown
Author

@Croway I'm curious, what is blocking the native support? to my understanding the camel-quarkus-mcp-servre worksin native (https://docs.quarkiverse.io/quarkus-mcp-server/dev/)

good point, in theory nothing, I am adding the native profile and I'm going to test it.

@zbendhiba

Copy link
Copy Markdown
Contributor

The engine only uses ToolManager APIs from the core module. Would it be possible to depend on just the core and let users choose the transport (quarkus-mcp-server-http or quarkus-mcp-server-stdio) in their own pom? That would support both HTTP and stdio use cases without hardcoding the transport.

@JiriOndrusek

Copy link
Copy Markdown
Contributor

@Croway, FYI the recent camel-main branch's CI is almost green (only 1 example fails)therefore if you plan to push something, consider rebase

@Croway
Croway force-pushed the mcp-server-extension branch from 1a9f898 to 5042d09 Compare August 4, 2026 11:23
@Croway

Croway commented Aug 4, 2026

Copy link
Copy Markdown
Author

The engine only uses ToolManager APIs from the core module. Would it be possible to depend on just the core and let users choose the transport (quarkus-mcp-server-http or quarkus-mcp-server-stdio) in their own pom? That would support both HTTP and stdio use cases without hardcoding the transport.

mhhhh I would like to achieve the following, OOB Camel MCP will use the http transport, but somehow, the user can configure stdio if needed. As of now this is not possible, I'll do a research if this is feasible somehow on all the runtimes. WDYT @zbendhiba ?

@zbendhiba

Copy link
Copy Markdown
Contributor

The engine only uses ToolManager APIs from the core module. Would it be possible to depend on just the core and let users choose the transport (quarkus-mcp-server-http or quarkus-mcp-server-stdio) in their own pom? That would support both HTTP and stdio use cases without hardcoding the transport.

mhhhh I would like to achieve the following, OOB Camel MCP will use the http transport, but somehow, the user can configure stdio if needed. As of now this is not possible, I'll do a research if this is feasible somehow on all the runtimes. WDYT @zbendhiba ?

What I suggest is: instead of depending on quarkus-mcp-server-http, depend only on quarkus-mcp-server-core-parent. Then on the tests, users can pull either quarkus-mcp-server-http or quarkus-mcp-server-stdio (which probably means 2 flavor of integration tests)
Or have 2 camel quarkus mcp server.

I don't understand what's limiting us to HTTP.

I don't know. WDYT @jamesnetherton @JiriOndrusek

@Croway

Croway commented Aug 4, 2026

Copy link
Copy Markdown
Author

The engine only uses ToolManager APIs from the core module. Would it be possible to depend on just the core and let users choose the transport (quarkus-mcp-server-http or quarkus-mcp-server-stdio) in their own pom? That would support both HTTP and stdio use cases without hardcoding the transport.

mhhhh I would like to achieve the following, OOB Camel MCP will use the http transport, but somehow, the user can configure stdio if needed. As of now this is not possible, I'll do a research if this is feasible somehow on all the runtimes. WDYT @zbendhiba ?

What I suggest is: instead of depending on quarkus-mcp-server-http, depend only on quarkus-mcp-server-core-parent. Then on the tests, users can pull either quarkus-mcp-server-http or quarkus-mcp-server-stdio (which probably means 2 flavor of integration tests) Or have 2 camel quarkus mcp server.

I don't understand what's limiting us to HTTP.

I don't know. WDYT @jamesnetherton @JiriOndrusek

but this way the user should configure the http or the stdio when using quarkus, right? it won't work by adding the extension only

@jamesnetherton

Copy link
Copy Markdown
Contributor

WDYT @jamesnetherton @JiriOndrusek

If it is just literally a case of switching between quarkus-mcp-server-http & quarkus-mcp-server-stdio, then yes, we could just have the extension depend on quarkus-mcp-server-core and users choose the server impl.

If it's not that simple, then maybe we just go with HTTP initially?

@Croway

Croway commented Aug 4, 2026

Copy link
Copy Markdown
Author

WDYT @jamesnetherton @JiriOndrusek

If it is just literally a case of switching between quarkus-mcp-server-http & quarkus-mcp-server-stdio, then yes, we could just have the extension depend on quarkus-mcp-server-core and users choose the server impl.

If it's not that simple, then maybe we just go with HTTP initially?

I'd go with HTTP, in particular, right now the following is possible:

  • add the quarkus-mcp-server-stdio on the pom.xml
  • enable stdio via properties quarkus.mcp.server.stdio.enabled=true
  • disable http quarkus.mcp.server."<default>".http.enabled=false

This works with the current code, and can be documented, it might be a bit hacky, but this way the component is battery included, OOB, http works.

@jamesnetherton

Copy link
Copy Markdown
Contributor

This works with the current code, and can be documented, it might be a bit hacky, but this way the component is battery included, OOB, http works

I'm ok with that. Maybe @zbendhiba has more thoughts about it?

I guess we can always adjust things as needed later.

@zbendhiba

Copy link
Copy Markdown
Contributor

ok

Thanks for the discussion

@Croway
Croway marked this pull request as ready for review August 4, 2026 17:03
@Croway
Croway force-pushed the mcp-server-extension branch from fe0ecac to 36f6209 Compare August 6, 2026 10:43
Croway and others added 9 commits August 6, 2026 12:45
Expose ai-tool routes as MCP tools over streamable HTTP through the
quarkiverse quarkus-mcp-server extension (CAMEL-24308):

- JVM-only extension depending on camel-mcp-server-api (the
  runtime-agnostic bridge + McpServerEngine SPI) - deliberately NOT the
  camel-mcp-server Vert.x engine used on Camel Main/JBang, so the MCP
  Java SDK and Reactor never reach the Quarkus classpath.
- QuarkusMcpServerEngine publishes tools through the programmatic
  ToolManager API (list_changed emitted by quarkus-mcp-server); tool
  arguments are mapped from the ai-tool ParameterDef metadata.
- quarkus.camel.mcp-server.* config (enabled default true, tags opt-in,
  tool-timeout 20s); serving concerns (path, transports, dev UI, auth,
  guardrails) stay owned by quarkus.mcp.server.*.
- Integration tests run the engine conformance scenarios with the
  official MCP SDK client over streamable HTTP: tag filtering (untagged
  pool excluded), call success, sanitized execution errors, timeout,
  tools list following route stop/start.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ge docs

- Prove @Tool-annotated business methods coexist with Camel ai-tool
  routes on the same MCP server.
- Usage docs: combined quarkus.camel.mcp-server.* / quarkus.mcp.server.*
  configuration example with ownership split, link to the
  quarkus-mcp-server configuration reference, YAML DSL tool definition,
  MCP client configuration examples, dynamic tools and error handling.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erver extension

- Move the MCP Java SDK test dependency version to the root pom.xml test
  version properties, manage the artifacts in camel-quarkus-bom-test and
  drop the explicit versions from the integration test pom
- Register io.modelcontextprotocol.sdk:mcp-core and mcp-json-jackson2 with
  the dependabot test dependencies
- Replace the CamelContext injection in McpServerTest with an McpServerResource
  JAX-RS resource driven via RestAssured, so the test also works if the
  extension is promoted to native mode support
- Keep quarkiverse-mcp-server.version in alphabetical order
- Remove a redundant comment from the runtime pom
- Regenerate the flattened BOMs

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
….test-port

The hardcoded 8081 broke the test whenever the HTTP test port was
configured to something else.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…pport

The engine needed no changes to work in native mode: the native image
builds without any reflection or resource registration and all engine
conformance scenarios pass against the native binary.

- Move the extension to extensions/ and the tests to integration-tests/,
  drop the JVM only warning build step and declare nativeSince
- Add a native profile and McpServerIT to the test module
- Assign mcp-server to the group-12 test category
- Use McpAssured from quarkus-mcp-server-test to drive the server instead
  of the MCP Java SDK client, which removes the mcp-core and
  mcp-json-jackson2 test dependencies together with their version
  property, test BOM and dependabot entries
- Regenerate the extension metadata, documentation and flattened BOMs

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The extension keeps streamable HTTP out of the box. Adding the
quarkus-mcp-server-stdio transport serves the same Camel tools over
stdin/stdout, and the HTTP transport of the default MCP server can be turned
off to serve over stdio only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…rver HTTP transport

The servers map is annotated @WithUnnamedKey("<default>"), so
quarkus.mcp.server.http.enabled addresses the default server directly —
the quoted "<default>" key works but is not required, and the flat form
is what the quarkus-mcp-server configuration reference documents.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t MCP server HTTP transport"

This reverts commit 9f6fdd4.
…gation

docs/modules/ROOT/nav.adoc is generated by the check-extension-pages goal
of the catalog module and was missed when the extension was added, leaving
the reference page unreachable from the navigation tree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Croway
Croway force-pushed the mcp-server-extension branch from 36f6209 to afe210d Compare August 6, 2026 10:46
@jamesnetherton

Copy link
Copy Markdown
Contributor

This looks good to me. To avoid PR builds failing endlessly due to changes on the Camel main branch, I can merge this manually onto camel-main. I tested this locally and the tests pass fine.

@jamesnetherton
jamesnetherton merged commit fdaa5a5 into apache:camel-main Aug 6, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants