Skip to content

WebClient Streamable HTTP support #292

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

Draft
wants to merge 18 commits into
base: main
Choose a base branch
from
Draft

WebClient Streamable HTTP support #292

wants to merge 18 commits into from

Conversation

chemicL
Copy link
Member

@chemicL chemicL commented Jun 4, 2025

Introducing WebClient support for Streamable HTTP transport.

Motivation and Context

The 2025-03-26 specification of MCP introduces Streamable HTTP transport that replaces SSE transport.
This change introduces more resilient primitives that support the initial, WebClient-based client transport for Streamable HTTP.

Various failure modes at either the transport or logical layer have been addressed and are now fully supported by the new WebClientStreamableHttpTransport class. The surrounding McpAsyncClient and McpClientSession have been modified to handle possible issues according to the adequate layer's responsibility.

Orientation

The existing APIs are organized quite logically with regards to various responsibilities using layers. Below is the status quo description and the changes required for Streamable HTTP to be implemented.

Rationale for the API

The client types are layered in the following way:

McpAsyncClient
  McpClientSession
    McpClientTransport
      McpTransportSession
      McpTransportStream

Here are the distinguishing features of these APIs:

McpAsyncClient (and the facade, McpSyncClient)

  • user facing class exposing capabilities like tools, etc.
  • maintains state for the initialization (and an inner class was added to encapsulate this state)
  • maintains state for capabilities handlers, these translate to lower-level handlers at JSON-RPC level in the session
  • (change) used to have a single session, now dynamically allocates sessions upon reconnects/invalidations
  • (change) used to delegate to session to manage the transport, now it owns the transport and its lifecycle

Mcp(Client)Session

  • internal implementation providing JSON-RPC mapping of requests with responses
  • maintains state for the requests pending responses
  • allows defining req/resp/notification handlers and keeps a state of them
  • dispatches messages to their handlers
  • (change) used to own the transport and manage its lifecycle, now it's independent

Mcp(Client)Transport

  • specific implementation for the transport layer of serialized JSON-RPC
  • STDIO, SSE, Streamable HTTP
  • maintains open streams in case of streamable connections (SSE, Streamable HTTP, STDIO)
  • maintains particular state of the transport type, e.g. message endpoint in case of SSE transport
  • (new) spawns transport-specific sessions (e.g. HTTP status-driven session management from streamable HTTP)
  • (new) spawns transport-specific streams that can come and go

McpTransportSession

  • new abstraction over transport-driven session concept
  • allows enriching the communication with transport-level session identifier to communicate with stateful servers

McpTransportStream

  • new abstraction for a transport-level stream (particularly SSE stream) with message id tracking for resumability purposes

How Has This Been Tested?

  • Regular integration tests using the everything MCP server.
  • Resilience tests validating reconnections and session invalidation handling.

Breaking Changes

The client initialization became lazy and more error-prone - connections happen upon first request and initialization is performed implicitly if it has not been issued before.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

The JDK HttpClient will be implemented next.

Remaining tasks

  • Builder
  • Javadoc
  • Supporting DELETE call

Related #72

@chemicL chemicL mentioned this pull request Jun 4, 2025
7 tasks
@tzolov tzolov self-assigned this Jun 4, 2025
@chemicL
Copy link
Member Author

chemicL commented Jun 4, 2025

The CI fails for the Stdio tests. I can't reproduce it on my M1 Mac, but will try a linux box and investigate.

@chemicL
Copy link
Member Author

chemicL commented Jun 4, 2025

There is the missing bit for issuing a DELETE upon client-side session termination, I'll investigate how it could be achieved.

I will review the spec once more and see what else is missing.

@bugzyz
Copy link

bugzyz commented Jun 6, 2025

Hi @chemicL thanks for the excellent work! I can run the unit test on our mcp streamable http service.

But one issue is that our service is running on springframework 5.3.28. And I notice that your WebClientStreamableHttpTransport only work compatible with springframework 6.2.1.

So when I integrate with your code in our service. The below error popped up.

java.lang.NoClassDefFoundError: org/springframework/http/HttpStatusCode

this is from spring-web-6.2.1.jar

We integrated with HttpClientSseClientTransport before to take care of sse, and the springframework 5.3.28 works fine with the class. Now we would like to upgrade to WebClientStreamableHttpTransport and has this incompatible issue. Is it possible to make it compatible with the old spring version? Thanks!

@chemicL
Copy link
Member Author

chemicL commented Jun 6, 2025

@bugzyz thank you! We are targetting Spring Framework gen 6, unfortunately. However, in your circumstances, once we introduce the JDK HttpClient support for Streamable HTTP, you should be able to use that in combination with older Spring Framework-based code. That work is coming once we finalize the architectural changes covered here. And then you wouldn't need the mcp-spring module dependency. Hope this helps.

@chemicL
Copy link
Member Author

chemicL commented Jun 6, 2025

I updated the description in the PR to reflect the architectural overview and decisions to improve the review. @tzolov I'll continue looking into the DELETE aspect and come back either here or via a follow-up PR.

@noear
Copy link

noear commented Jun 7, 2025

Streamable is finally here:) Thanks!

@noear
Copy link

noear commented Jun 7, 2025

@bugzyz java8, java11, spring5, etc., can use the solon-ai project

https://github.com/opensolon/solon-ai


We sync a java8 code and provide a simple experience.

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