Skip to content

CAMEL-26360 : Add MCP server parity to Spring Boot - #2094

Open
ramu11 wants to merge 4 commits into
apache:mainfrom
ramu11:CAMEL-26360-spring-boot-quarkus-parity
Open

ramu11 wants to merge 4 commits into
apache:mainfrom
ramu11:CAMEL-26360-spring-boot-quarkus-parity

Conversation

@ramu11

@ramu11 ramu11 commented Nov 14, 2026

Copy link
Copy Markdown
Contributor

CAMEL-26360: Add MCP Server parity to Spring Boot

Description

This PR brings the Spring Boot MCP Server engine closer to feature parity with the Camel Main MCP Server engine by integrating Camel MCP server capabilities with Spring AI's MCP Streamable HTTP transport.

It enables Camel MCP server routes to participate in the Spring Boot MCP server alongside Spring-defined MCP tools, while preserving caller identity and supporting Spring Security integration.

Key changes

1. Spring Boot MCP transport integration

  • Add Spring Boot auto-configuration for the Camel MCP Server transport.
  • Integrate with Spring AI's WebMVC Streamable HTTP transport.
  • Ensure transport auto-configuration follows the explicitly configured STREAMABLE protocol.
  • Support custom Spring AI MCP transport provider implementations.

2. Caller identity and Spring Security integration

  • Extract the authenticated principal and authorities from the Spring Security request context.
  • Propagate caller identity, principal name, and roles into Camel MCP tool execution through McpToolCallContext.
  • Provide a reusable SpringAiMcpTransportContext utility for custom transport providers.
  • Enable Camel routes to apply native Camel AuthorizationPolicy using the propagated caller roles.

3. MCP server capabilities

  • Support Camel MCP tools alongside Spring AI annotation-based tools.
  • Support MCP resources and resource templates.
  • Support MCP prompts, including multiple user and assistant messages.
  • Support structured tool output and tool annotations.
  • Cover resource, prompt, and tool lifecycle changes and notifications.

4. Origin validation and transport security

  • Validate Origin headers for MCP Streamable HTTP requests.
  • Cover POST, GET, and DELETE request validation.
  • Verify loopback Origin handling and configured remote Origin allowlists.
  • Verify that rejected Origin requests do not invoke Camel tool routes.

5. Documentation

  • Document Spring Boot MCP server configuration and usage.
  • Add examples for ai-tool, ai-resource, and ai-prompt.
  • Document resource templates and prompt safety considerations.
  • Document Spring Security identity propagation and custom transport provider integration.
  • Clarify OAuth resource metadata configuration, including the use of canonical externally configured resource and metadata URLs.

Testing

The following focused test suite completed successfully:

mvn -pl components-starter/camel-mcp-server-starter -am \
  -Dtest=SpringBootMcpServerTest,SpringBootMcpServerOriginConfigurationTest,SpringBootMcpServerCustomTransportTest \
  -Dsurefire.failIfNoSpecifiedTests=false \
  test

Additional focused verification:

Scope

This PR focuses on the Spring Boot MCP Server engine.

Quarkus parity is intentionally deferred until the Spring Boot integration and common API changes have been reviewed.

@davsclaus davsclaus 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.

Thanks, this is a solid step towards parity. The engine follows the Vert.x engine almost line for line: resources, blobs, templates with the explicit remove notification, prompts, structured output and annotations. The Origin check reuses McpServerOrigins with the same loopback defaults as Camel Main. The identity is taken only from the servlet principal, and with no principal it fails closed. The Origin tests for POST, GET and DELETE are well done, including the one that shows a rejected request does not run the route.

Please address these before merging:

  1. Generated files. docs/spring-boot/modules/ROOT/pages/starters/mcp-server.adoc is generated from intro.adoc, usage.adoc and src/main/docs/mcp-server.json, but the committed page does not match them:

    • It has no Resources, Prompts or custom-provider sections.
    • It still has the old OAuth example that builds resource_metadata from the request URL (UrlUtils.buildFullRequestUrl(request), page line 84). The new intro.adoc:124-129 warns against exactly that.
    • The enabled row is the old one.

    mcp-server.json is generated from the Javadoc. Its new enabled description does not match McpServerConfigurationProperties.java:34-37, and its allowed-origins text differs from that option's Javadoc. Could you update the Javadoc, rebuild the starter and commit the regenerated JSON and page? Otherwise the sync bot overwrites them after the merge.

  2. camel.mcp-server.enabled=false. CamelMcpServerTransportAutoConfiguration (lines 49-66) does not check this option, so the Camel transport still replaces the Spring AI one. @ConditionalOnProperty(value = "camel.mcp-server.enabled", matchIfMissing = true) fixes that. A few back-off tests with WebApplicationContextRunner would help too:

    • with enabled=false, the Spring AI provider is the one used;
    • a WebMvcStreamableServerTransportProvider the user provides is the one actually used;
    • allowed-origins=* fails the startup.

    SpringBootMcpServerCustomTransportTest would pass with the Camel bean too, since both install the same extractor.

  3. Upgrade guide. The Spring AI default validator is NOOP, so Spring Boot apps had no Origin check until now. After this PR, a browser client that is not on loopback gets a 403. The 4.24 upgrade guide in apache/camel (the entry on the Origin header check on /mcp) currently says the Spring Boot engine is not affected. Could you open a small companion PR there that mentions camel.mcp-server.allowed-origins?

  4. Clashes with Spring-defined resources and prompts. The MCP SDK's addResource, addResourceTemplate and addPrompt (engine:166, 226, 255) silently replace an existing entry with the same key; they only log "Replace existing ...". When the Camel route stops, the Spring-defined @McpResource or prompt is then removed for good. The engine SPI expects McpServerRefusedException for this case. Checking server.listResources(), listResourceTemplates() and listPrompts() first, and throwing it, lets the bridge log a clear refusal. A check on listTools() would close the same, older gap for tools.

Suggestions:

  • Role format (SpringAiMcpTransportContext.java:92-130): CamelMcpSecurityPrincipalRoles holds the raw Spring authorities. That means ROLE_*, SCOPE_* for a JWT, and with Spring Security 7.1 also FACTOR_*. On Camel Main, the roles are the unprefixed roles/realm_access.roles/groups claims, and oauthSubject/oauthScopes/oauthClaims are filled in. So an AuthorizationPolicy written for Main does not match on Spring Boot. At least document the format, with an example such as ROLE_MCP_ADMIN (intro.adoc:150-155). Better still: strip ROLE_, drop FACTOR_*, map SCOPE_* into oauthScopes, and fill the OAuth fields for a JwtAuthenticationToken. The reflective lookups could also be cached instead of repeated on every request.
  • Notification test (SpringBootMcpServerTest.java:682-719): the client opens its SSE stream asynchronously after initialize(), so the first stopRoute notification can be dropped. Sending the notification again inside the await (for example McpSyncServer.notifyToolsListChanged() on each poll) makes the test deterministic. Stopping customer-template-route would also cover the explicit notification in resourceTemplateRemoved.
  • supportsResources() / supportsResourceTemplates() / supportsPrompts() always return true. They could return server.getServerCapabilities().resources() (or prompts()) != null. With a capability turned off, the bridge then logs its single "not served" warning instead of one IllegalStateException per route.
  • Host allow-list: Main has an opt-in camel.server.mcp-allowed-hosts as a DNS-rebinding defence. A matching camel.mcp-server.allowed-hosts, built on McpServerHosts and the SDK's allowedHosts, would complete the parity. Fine as a follow-up.
  • Nits:
    • Add a comment on why the deprecated ServerTransportSecurityValidator is needed: the Spring AI 2.0.2 builder accepts nothing else.
    • intro.adoc:25-27 says resources and prompts are "registered separately", but usage.adoc:83-85 says they use the same tags.
    • Mark the mcp.security.* names at intro.adoc:54-56 as application properties, so they don't read as options of the starter.
    • The test-scoped camel-ai-resource/camel-ai-prompt entries in the pom are redundant: camel-mcp-server-api brings them in.
    • The commit title needs a space after CAMEL-26360:.

Claude Code on behalf of Claus Ibsen (davsclaus). This review was generated by an AI agent and may contain inaccuracies; please verify all suggestions before applying.

@ramu11 ramu11 changed the title CAMEL-26360:Add MCP server parity to Spring Boot CAMEL-26360 : Add MCP server parity to Spring Boot Nov 14, 2026
@ramu11

ramu11 commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks @davsclaus for the review. I’ve addressed the feedback in commit 98e9611.

The updates include:

  • Ensuring camel.mcp-server.enabled=false backs off Camel’s MCP transport integration.
  • Preventing Camel routes from replacing Spring-defined tools, resources, resource templates, and prompts.
  • Reporting resource and prompt capabilities based on the actual Spring AI server configuration.
  • Improving Spring Security caller-context propagation, including role normalization, OAuth scopes, JWT subject, and claims.
  • Adding focused tests for the auto-configuration, caller context, capabilities, and registration-collision scenarios.
  • Updating the starter documentation.

Validation: the focused test suite passes (35 tests, 0 failures or errors), and the starter package build succeeds.

I’ll raise a separate PR against the main Camel repository for the Camel 4.24 upgrade-guide update covering the Spring Boot MCP Streamable HTTP Origin validation behavior.

@Croway Croway 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.

Review of the Spring Boot MCP parity changes, checked against Camel main (camel-mcp-server-api), Spring AI 2.0.2 and MCP Java SDK 2.1.0. The findings come from reading the code; I did not run the tests.

The inline comments are roughly in priority order. The main concern is that raw JWT claims are merged into the caller's roles in SpringAiMcpTransportContext. Next come the Origin default change for apps with only Spring-defined MCP endpoints, and the startup failure caused by an invalid outputSchema.

Claude Code (Opus 5.5) on behalf of Federico Mariani


addScopeClaim(scopes, claims.get("scope"));
addScopeClaim(scopes, claims.get("scp"));
addClaimRoles(claims, roles);

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.

Raw JWT claims are merged into the caller's roles. addClaimRoles adds the roles / realm_access.roles / groups claims to the principal roles, on top of the authorities produced by the application's own JwtAuthenticationConverter. In a Spring app the converter is where the application decides which token claims become authorities, so this bypasses that decision.

Example: an app maps only client roles (resource_access.myapp.roles) or only scopes to authorities. A Keycloak token whose realm_access.roles contains admin (granted for a different application) still ends up with admin in McpToolCallContext.SECURITY_PRINCIPAL_ROLES_PROPERTY. A route AuthorizationPolicy that requires admin then lets the call through, although Spring Security never granted ROLE_admin.

Suggestion: on Spring Boot, derive roles only from Authentication.getAuthorities() and keep the raw claims available through the OAuth claims for routes that want them. If claim-based roles are needed for parity with Camel Main, make them opt-in and document the behaviour.

Claude Code (Opus 5.5) on behalf of Federico Mariani

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for highlighting this security concern. Fixed by deriving Camel roles and OAuth scopes exclusively from the authorities granted by Spring Security's configured authentication provider or JWT authority converter.

Raw token claims, including roles, realm_access.roles, and groups, are still propagated through McpToolCallContext as OAuth claims, but they are no longer promoted to Camel authorization roles.

This preserves the application's Spring Security authority-mapping policy and prevents claims that were not converted into granted authorities from bypassing Spring Security authorization decisions.

Added regression tests verifying that realm_access.roles, groups, and raw roles claims do not independently grant Camel roles.


@Bean
@ConditionalOnMissingBean(WebMvcStreamableServerTransportProvider.class)
WebMvcStreamableServerTransportProvider webMvcStreamableServerTransportProvider(

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.

This changes Origin handling for apps that don't expose any Camel capability. With the starter on the classpath and spring.ai.mcp.server.protocol=STREAMABLE, this bean replaces Spring AI's default transport, which in 2.0.2 (McpServerStreamableHttpWebMvcAutoConfiguration) sets no security validator. The replacement is active even when camel.mcp-server.tags is unset and Camel exposes nothing, so Spring-defined @McpTool / @McpResource endpoints suddenly accept only loopback origins.

Example: a 4.23 app serving Spring @McpTool methods to a browser client at https://app.example.com gets HTTP 403 Invalid Origin header after upgrading. The only ways out are camel.mcp-server.allowed-origins, or camel.mcp-server.enabled=false, which also turns off the Camel bridge.

Suggestion: add an upgrade-guide entry for this, and consider a dedicated switch for replacing the transport.

Claude Code (Opus 5.5) on behalf of Federico Mariani

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for raising this. Addressed the transport-replacement concern by adding the separate camel.mcp-server.transport-enabled property.

When unset, Camel replaces Spring AI's default transport only when Camel MCP tags are configured. Setting it to true enables Camel's transport even without Camel routes, while setting it to false preserves Spring AI's provider without disabling the Camel bridge.

I also documented the behavior in the starter documentation. I'm clarifying the Camel 4.24 upgrade-guide entry to explain the default activation condition and make clear that Spring-only MCP applications are not affected by transport replacement when no Camel tags are configured.

.description(tool.description());

if (tool.outputSchemaJson() != null && !tool.outputSchemaJson().isBlank()) {
builder.outputSchema(jsonMapper, tool.outputSchemaJson());

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.

An invalid outputSchema fails startup instead of refusing this one tool. server.addTool validates both schemas with jsonSchemaValidator.assertConforms (MCP Java SDK 2.1.0, McpAsyncServer#addTool) and fails with IllegalArgumentException. McpServerBridge only catches McpServerRefusedException around engine.toolAdded, so the exception propagates.

Example: ai-tool:x?outputSchema={"type":"obj"} passes the ai-tool check (it is a JSON object), then fails CamelContext / route startup instead of logging an error and skipping the tool.

Suggestion: catch IllegalArgumentException from server.addTool and rethrow it as McpServerRefusedException, plus a test with an invalid output schema.

Claude Code (Opus 5.5) on behalf of Federico Mariani

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for catching this. Fixed by catching IllegalArgumentException during tool registration, including the SDK's server.addTool() validation, and wrapping it in McpServerRefusedException with the tool name and schema error details.

This allows the MCP bridge to refuse the invalid tool registration without propagating the exception and failing Camel startup.

Added a regression test verifying that an invalid output-schema registration error is translated into McpServerRefusedException, preserving the original cause.

@Override
public void resourceTemplateAdded(McpServerResourceTemplate resourceTemplate) {
if (server.listResourceTemplates().stream()
.anyMatch(existing -> existing.uriTemplate().equals(resourceTemplate.uriTemplate()))) {

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.

Only identical template strings are refused, not overlapping ones. The engine contract (McpServerEngine#resourceTemplateAdded: "throws McpServerRefusedException to refuse an overlapping or otherwise unsupported template") asks for overlap detection. McpServerBridge checks overlap only among Camel's own templates, so a clash with a Spring-defined template gets through.

Example: Spring defines camel:///customers/{id} and a Camel route defines camel:///customers/{customerId}. Both are registered. The SDK keeps templates in a ConcurrentHashMap and findResourceTemplateSpecification returns the first match, so which handler serves resources/read camel:///customers/42 is not defined.

Suggestion: refuse the template when it overlaps any template already registered on the server, using the same overlap rule the bridge applies to Camel templates.

Claude Code (Opus 5.5) on behalf of Federico Mariani

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for pointing this out. Fixed by checking each incoming resource template against all templates already registered with the Spring AI server, rather than checking only for identical template strings.

The implementation uses the same underlying AiResourceUriTemplate.overlaps() matching logic as Camel's McpServerBridge. Overlapping templates are rejected with McpServerRefusedException. It also fails closed if an existing Spring-defined template cannot be parsed safely.

Added a regression test covering the exact scenario from the review: camel:///customers/{id} already registered by Spring and camel:///customers/{customerId} being added by a Camel route. The test verifies that registration is refused and the SDK server is not modified.

@ConditionalOnProperty(
prefix = "camel.mcp-server",
name = "enabled",
havingValue = "true",

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.

havingValue = "true" doesn't match how CamelMcpServerAutoConfiguration reads the same property. That class uses @ConditionalOnProperty(value = "camel.mcp-server.enabled", matchIfMissing = true), which matches any value other than false. Here only true matches.

Example: with camel.mcp-server.enabled=on (which Spring binds to true), the bridge is active but this transport is not installed. There is no Origin validation, and every tool call gets McpToolCallContext.EMPTY, so role-based AuthorizationPolicy routes deny every caller. Nothing is logged.

Suggestion: drop havingValue so both auto-configurations use the same condition.

Claude Code (Opus 5.5) on behalf of Federico Mariani

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for catching this. Fixed by removing the restrictive havingValue = "true" condition so the transport auto-configuration uses the same matchIfMissing = true semantics as the MCP bridge.

I also added a regression test using camel.mcp-server.enabled=on to verify that both the bridge and transport are activated and that the authenticated caller context is propagated correctly.

"org.springframework.security.core.Authentication";
private static final String GRANTED_AUTHORITY_CLASS =
"org.springframework.security.core.GrantedAuthority";
private static final String JWT_AUTHENTICATION_CLASS =

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.

Opaque-token callers get no subject or claims. Claims are read only for JwtAuthenticationToken, but getTokenAttributes() is declared on AbstractOAuth2TokenAuthenticationToken, which BearerTokenAuthentication (token introspection, oauth2ResourceServer(o -> o.opaqueToken(...))) also extends. With opaque tokens the OAuth subject and claims are never set, so a route policy based on them denies every caller.

Suggestion: look up getTokenAttributes on AbstractOAuth2TokenAuthenticationToken instead.

Claude Code (Opus 5.5) on behalf of Federico Mariani

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for catching this. Fixed by resolving getTokenAttributes() from AbstractOAuth2TokenAuthenticationToken rather than relying on JwtAuthenticationToken specifically. This supports both JWT and opaque bearer-token authentication.

I also added a regression test using BearerTokenAuthentication to verify that the OAuth subject, token attributes, roles, and scopes are correctly propagated into the Camel tool call context.

// Spring AI 2.0.2 exposes only this deprecated validator type on the
// WebMVC transport builder.
@SuppressWarnings("deprecation")
private static ServerTransportSecurityValidator createOriginValidator(String configuredAllowedOrigins) {

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.

Apps with a custom transport can't reuse Camel's Origin policy. The docs ask apps that define their own WebMvcStreamableServerTransportProvider to install "an Origin validator equivalent to, or stricter than" this one, but createOriginValidator and DEFAULT_ALLOWED_ORIGINS are private, so they have to re-implement the normalization and the loopback defaults. SpringBootMcpServerCustomTransportTest already differs: it uses DefaultServerTransportSecurityValidator directly, without the McpServerOrigins normalization.

Similarly, SpringAiMcpTransportContext.CALL_CONTEXT_KEY is package-private, so an app that needs its own transport-context entries can't combine them with Camel's identity entry.

Suggestion: expose the validator factory and defaults as public API, and offer a way to compose the context extractor with an application's own entries.

Claude Code (Opus 5.5) on behalf of Federico Mariani

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for pointing this out. Addressed this by exposing the reusable Origin validation API and the default allowed-origin configuration. Both the current MCP SDK header validator and the Spring AI 2.0.2 compatibility adapter use Camel's shared Origin parsing and normalization.

I also made SpringAiMcpTransportContext.CALL_CONTEXT_KEY public and added an overload of extract() that composes Camel's authenticated caller context with application-defined transport-context entries, while preventing applications from overriding Camel's reserved entry.

Updated the custom transport integration test to use these public helpers and added coverage for context composition. The custom transport documentation now demonstrates how to reuse both helpers.

}
}

private record AuthenticationAccessors(

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.

Minor: the Conventions section of the repository CLAUDE.md says "Do NOT use Records or Lombok (unless already present in the file)". This file is new, so AuthenticationAccessors could be a small final class instead.

Claude Code (Opus 5.5) on behalf of Federico Mariani

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for noting the repository convention. Replaced the AuthenticationAccessors record with a private static final class, keeping the same accessor behavior without introducing a record into the new file.

* Whether to expose ai-tool routes as MCP tools through the Spring AI MCP server. Enabled by default when the
* starter is on the classpath.
* Whether to enable Camel MCP integration for ai-tool, ai-resource, and ai-prompt routes through the Spring AI
* MCP server. Enabled by default when the starter is on the classpath.

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.

The enabled description was updated, but the tags javadoc just below (lines 40-41) still says "Comma-separated list of ai-tool tags to expose as MCP tools ... When not set, no tools are exposed." Tags now also select ai-resource and ai-prompt routes. Updating that javadoc and regenerating also fixes mcp-server.json and the options table in mcp-server.adoc.

Claude Code (Opus 5.5) on behalf of Federico Mariani

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for pointing this out. Updated the tags Javadoc to clarify that tags select ai-tool, ai-resource, and ai-prompt routes for exposure through the Spring AI MCP server.

I also regenerated the MCP configuration metadata and starter documentation so the JSON metadata and options table reflect the updated description.

----
from("ai-resource:app_config?resourceUri=camel:///config/app.json"
+ "&tags=crm&description=Application configuration&mimeType=application/json")
.setBody(constant("{\\"env\\":\\"production\\"}"));

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.

AsciiDoc listing blocks show \\" literally, so this example (and the one at line 264) doesn't compile when copied: the Java string ends right after the first \\. These should be single-backslash escapes, for example constant("{\"env\":\"production\"}").

Claude Code (Opus 5.5) on behalf of Federico Mariani

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for catching this. Fixed both the resource and resource-template examples to use normal Java string escaping (a single backslash before embedded quotes).

I corrected the source usage document and regenerated the starter documentation page. I also verified that both examples in the generated AsciiDoc are copy-pasteable Java code.

The focused MCP test suite passes (45 tests, zero failures or errors), and the starter package build succeeds.

@ramu11

ramu11 commented Nov 15, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @Croway for the detailed review. I've addressed the outstanding feedback in the latest update.

The changes include Spring Security authority-only role mapping, explicit transport activation controls, invalid schema handling, resource-template overlap detection, notification capability guards, resource and prompt timeout configuration, opaque-token context extraction, and reusable Origin/context helpers.

I've also corrected the documentation examples, updated generated starter metadata, and addressed the remaining code convention feedback.

Validation: 45 focused tests pass with zero failures or errors, and the starter package build succeeds.

@ramu11
ramu11 requested a review from Croway November 15, 2026 05:36

@Croway Croway 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.

Follow-up on the latest commit: the earlier comments look addressed. One new issue on the resource-template overlap check, inline.

Claude Code (Opus 5.5) on behalf of Federico Mariani

try {
existingMatcher = new AiResourceUriTemplate(existing.uriTemplate());
} catch (IllegalArgumentException e) {
// Fail closed: Camel cannot prove that an unsupported Spring template does not overlap.

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.

A single Spring-defined template that Camel can't parse blocks every Camel resource template. The overlap check fails closed whenever an existing template is rejected by AiResourceUriTemplate. That parser intentionally accepts path-segment variables only, so it rejects the style Spring AI documents for @McpResource, where the variable follows the scheme directly: document://{id}, async-data://{id} and static://{path} in the Spring AI 2.0.2 mcp-annotations-examples.adoc all fail with "Resource uriTemplate must contain a path". Templates with query expressions such as {?q} are rejected too.

Example: an app defines @McpResource(uri = "document://{id}") and a Camel route defines ai-resource:customer?uriTemplate=camel:///customers/{customerId}. The two can never match the same URI because the schemes differ, but the Camel template is refused. So is every other Camel resource template in the app, as long as that one Spring template is registered.

Suggestion: only fail closed when the unparseable template has the same scheme as the candidate (case-insensitive). Templates with a different scheme can't overlap, so log a WARN and continue. A test with a Spring document://{id} template next to a Camel camel:///... template would cover it.

Claude Code (Opus 5.5) on behalf of Federico Mariani

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in commit d75d65d. Added URI scheme comparison, WARN logging for different schemes, and regression tests for different and matching schemes. All 6 SpringAiMcpServerEngineTest tests pass. git diff --check passes.

@ramu11
ramu11 requested a review from Croway November 15, 2026 11:30
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.

3 participants