Skip to content

fix(client): improve performance of large SSE tool responses#1052

Open
arnabnandy7 wants to merge 1 commit into
modelcontextprotocol:mainfrom
arnabnandy7:fix/issue-1042-sse-performance
Open

fix(client): improve performance of large SSE tool responses#1052
arnabnandy7 wants to merge 1 commit into
modelcontextprotocol:mainfrom
arnabnandy7:fix/issue-1042-sse-performance

Conversation

@arnabnandy7

@arnabnandy7 arnabnandy7 commented Jul 2, 2026

Copy link
Copy Markdown

Replaced the JDK's slow fromLineSubscriber line-assembly mechanism with a custom byte-level streaming SSE parser (SseByteSubscriber) in ResponseSubscribers.java. This resolves an O(n²) performance bottleneck when receiving and parsing large compact JSON payload events (which are sent on a single line), without breaking streaming event delivery for notifications/progress.

Motivation and Context

When a tool returns a large payload (~4 MB compact JSON on a single line) over the Streamable HTTP client transport, the Java MCP SDK takes ~5 seconds to receive and parse it. This is roughly 10–13× slower than a raw HttpClient.ofString() read (~0.4s).

The bottleneck is the JDK HTTP Client's HttpResponse.BodySubscribers.fromLineSubscriber() used in ResponseSubscribers.sseToBodySubscriber(). It scans the incoming bytes to find newline boundaries, assembling the entire 4 MB JSON payload into a single string. The internal CharSequence/String assembly in fromLineSubscriber is extremely slow when processing one very long line from many small ByteBuffer chunks.

This PR replaces the line subscriber with a streaming byte-level SSE parser (SseByteSubscriber), which processes raw byte chunks (List<ByteBuffer>) using BodySubscribers.fromSubscriber(...). This bypasses the JDK's line-assembly entirely while preserving the streaming delivery of events.

How Has This Been Tested?

Tested locally via new unit tests in SseByteSubscriberTests.java and running the entire mcp-core test suite:

  • Unit Tests: Added coverage for standard SSE events, multi-line data concatenation, SSE comments skipping (:), split boundaries across chunks, empty data, and standard line endings (\n, \r\n, \r).
  • Performance Verification: Tested with a ~4 MB compact JSON payload split into 16 KB chunks. The parsing completed in 0.112 seconds, compared to ~5 seconds originally, confirming a ~45× throughput speedup.
  • Regression Testing: All 351 unit tests in mcp-core pass successfully.

Breaking Changes

None. This change is fully internal to the package-private subscriber in the transport layer and does not modify the public APIs or the ResponseEvent contract.

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

  • Bypassing the JDK's line reader requires custom byte buffering and shifting. We implemented SseByteBuffer (a fast rolling byte buffer) inside ResponseSubscribers.java to handle raw chunk aggregation and shifting at the primitive byte array level.
  • UTF-8 decoding is done exactly once per complete SSE line, avoiding incremental string/builder copying.
  • Backpressure and streaming demand are correctly managed: the custom subscriber requests Long.MAX_VALUE on subscription to receive the raw byte chunks as they arrive from the network, maintaining standard push-model streaming semantics.

Resolves #1042

Replaced the JDK's slow fromLineSubscriber line-assembly mechanism
with a custom byte-level streaming SSE parser SseByteSubscriber.
This resolves an O(n^2) bottleneck when parsing large compact JSON
payloads that are returned on a single line, improving throughput by
~45x.

Closes modelcontextprotocol#1042
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.

Streamable HTTP client: slow reception of large SSE tool responses (fromLineSubscriber line-assembly bottleneck)

1 participant