Skip to content

refactor: restructure MCP server auto-configuraitons, adding streamable-http support #4179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

tzolov
Copy link
Contributor

@tzolov tzolov commented Aug 18, 2025

  • Extract MCP server functionality into specialized modules:
    • STDIO/SSE servers (existing functionality)
    • Streamable-HTTP servers with change notifications
    • Stateless servers for simplified deployments
  • Add new auto-configuration modules:
    • spring-ai-autoconfigure-mcp-streamable-server-common
    • spring-ai-autoconfigure-mcp-streamable-server-webflux/webmvc
    • spring-ai-autoconfigure-mcp-stateless-server-common
    • spring-ai-autoconfigure-mcp-stateless-server-webflux/webmvc
    • Add server-specific property prefixes for different server types
  • Extract ToolCallbackConverter into separate auto-configuration with conditional enablement via tool-callback-converter property
  • Enhance transport providers with builder patterns and new features:
    • Keep-alive interval support for connection health
    • Add keep-alive-interval and other transport-specific options
  • Add comprehensive integration tests for all server types
  • Update documentation to reflect new architecture and server options

…le-http support

- Extract MCP server functionality into specialized modules:
  * STDIO/SSE servers (existing functionality)
  * Streamable-HTTP servers with change notifications
  * Stateless servers for simplified deployments

- Add new auto-configuration modules:
  * spring-ai-autoconfigure-mcp-streamable-server-common
  * spring-ai-autoconfigure-mcp-streamable-server-webflux/webmvc
  * spring-ai-autoconfigure-mcp-stateless-server-common
  * spring-ai-autoconfigure-mcp-stateless-server-webflux/webmvc
  * Add server-specific property prefixes for different server types

- Extract ToolCallbackConverter into separate auto-configuration
  with conditional enablement via tool-callback-converter property

- Enhance transport providers with builder patterns and new features:
  * Keep-alive interval support for connection health
  * Add keep-alive-interval and other transport-specific options

- Add comprehensive integration tests for all server types

- Update documentation to reflect new architecture and server options

Signed-off-by: Christian Tzolov <[email protected]>
@markpollack
Copy link
Member

This looks great. I did some similar things for the IT testing in the integration tests for spring-ai-examples. i think there are some reusable patterns we can discuss.

// De-duplicate tools by their name, keeping the first occurrence of each tool
// name
return tools.stream() // Key: tool name
.collect(Collectors.toMap(tool -> tool.getToolDefinition().name(), tool -> tool, // Value:
Copy link
Member

Choose a reason for hiding this comment

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

this logic is duplicated in the sync version, can share a utility method

.withConfiguration(AutoConfigurations.of(McpToolCallbackAutoConfiguration.class,
McpClientAutoConfiguration.class, SseWebFluxTransportAutoConfiguration.class));

static AtomicReference<LoggingMessageNotification> loggingNotificationRef = new AtomicReference<>();
Copy link
Member

@markpollack markpollack Aug 19, 2025

Choose a reason for hiding this comment

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

static can introduce side effects, make them instance vars and reset state in @beforeeach.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As those are shared between the test and the spring configuration beens we can't use the beforeeach but we can make them a special TestContext been as part for the client test configuration.
I will fix it for the Sse and the Streamable its

tzolov added 2 commits August 19, 2025 07:48
Signed-off-by: Christian Tzolov <[email protected]>
Signed-off-by: Christian Tzolov <[email protected]>
@tzolov
Copy link
Contributor Author

tzolov commented Aug 19, 2025

Thanks @markpollack I've addressed the above recomendations

@Fottas
Copy link
Contributor

Fottas commented Aug 20, 2025

Hi tzolov,

I noticed that PR #4179 was merged recently to add streamable support for MCP server.
A bit of context: I had submitted PR #4053 over two weeks ago with a similar goal but a different approach, and there hasn’t been much follow-up on it yet. To be honest, I was a bit surprised and confused to see a parallel solution merged so quickly.

Looking at the current implementation, I do have some concerns:

Module proliferation – introducing 10+ modules mainly for configuration variations feels like overhead.

Maintainability – each new mode could add more modules in the future, which might lead to unnecessary growth.

Complexity – this design seems to increase cognitive load for both users and maintainers.

In my PR, I tried a different approach:

Using configuration properties to switch between modes

Keeping everything in a single autoconfigure module with conditional beans

Preserving backward compatibility while reducing module sprawl

I think this configuration-driven approach might be more sustainable in the long run.
Would it make sense to revisit the design direction here? I’d be happy to help align and refactor so that we can keep things both flexible and maintainable.

Thanks!

@tzolov tzolov added this to the 1.1.0.M1 milestone Aug 20, 2025
@tzolov
Copy link
Contributor Author

tzolov commented Aug 20, 2025

rebased and merged at 0b96f4c

@tzolov tzolov closed this Aug 20, 2025
@Fottas
Copy link
Contributor

Fottas commented Aug 20, 2025

Hi @tzolov,
I noticed this creates 6+ autoconfiguration modules and multiple Properties classes with duplicate code. This goes against Spring Boot's "convention over configuration" principle.
In my PR #4063, I used a single starter with configuration properties to switch between modes:
spring.ai.mcp.server.type=stateless|streamable|sse
This approach would reduce module count significantly while maintaining the same functionality. Would you consider consolidating these modules?
Happy to help with a refactor if you're interested.

@markpollack markpollack self-assigned this Aug 20, 2025
@tzolov
Copy link
Contributor Author

tzolov commented Aug 20, 2025

Hi @Fottas,

Thank you for reaching out, I missed your PR while on PTO and rushed to provide auto-configurations for the MCP 0.11.x (i've released recently).
#4179 is our first attempt, but I'm working, already, on a second iteration due to issues with user-facing configurations and code repetition. I'll submit a PR shortly.

I appreciate your points but disagree about module count and complexity. Better separation of concerns actually reduces complexity.

Unlike MCP client side (where one host may have multiple clients for different server types), server side always has a single server type.
Bundling unrelated dependencies into one jar and activating the right one via runtime properties/conditions increases complexity compared to simply replacing the starter dependency.

We already have 3 separate boot starters: spring-ai-starter-mcp-server (stdio), spring-ai-starter-mcp-server-webmvc (sse/webmvc), spring-ai-starter-mcp-server-webflux (sse/webflux)
Having these share a single auto-configuration was a design mistake that created a monolithic configuration we're now splitting. Boot starters understand dependencies, not properties.

Reducing modules isn't the goal - reducing overall complexity and simplifying user-facing configuration is.

Let me submit the new PR and we can continue the conversation there. I'll make you co-author due to your #4053 work and improvement suggestions.

P.S. #4053 lacks stateless server support. These are different beasts with their own spec and transport APIs. Spring AI doesn't support HttpServletStreamableServerTransportProvider - use WebMvc instead. Also your PR lacked any tests

@tzolov
Copy link
Contributor Author

tzolov commented Aug 20, 2025

@Fottas here is the new PR: #4196

@Fottas
Copy link
Contributor

Fottas commented Aug 21, 2025

Hi @tzolov,
Thank you for the detailed explanation and for addressing the code duplication issues! The shared properties module approach makes a lot of sense given your points about separation of concerns.
Thank you for including me as co-author - I really appreciate that recognition!

@tzolov
Copy link
Contributor Author

tzolov commented Aug 21, 2025

Here's a streamlined version:

Hi @Fottas,

After further exploration, I've decided to keep boot starter choices simple: webmvc vs. webflux. As you pointed out, having separate starters for sse, streamable, and stateless would be involving for the end users.

PR #4211 consolidates everything into 3 boot starters:

  • spring-ai-starter-mcp-server (stdio)
  • spring-ai-starter-mcp-server-webmvc (sse/webmvc)
  • spring-ai-starter-mcp-server-webflux (sse/webflux)

It adds spring.ai.mcp.server.protocol=(SSE|STREAMABLE|STATELESS) as you suggested (spring.ai.mcp.server.type was already taken).

However I've managed to refactor the auto-configuraiton modules to 3 common, webmvc and webflux (similar to the client) and so resolve the existing issue of bundling mvc and flux in single auto-conf.

You're co-author on the new PR. Thanks again for your contribution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants