feat(streamable-http): add json_response option for stateless server mode#683
Conversation
DaleSeo
left a comment
There was a problem hiding this comment.
Thanks for your contribution, @thiagomendes! I really appreciate the detailed PR description. The implementation looks clean, and the test coverage addresses the important cases. I left a couple of comments to make sure the JSON path works consistently with the existing SSE path.
|
Thanks for the review @DaleSeo ! I have pushed a new commit addressing both points (cancellation awareness and observability parity). The local tests are passing and everything looks good. |
|
Thanks for addressing my feedback, @thiagomendes. The code looks good, but there's one linting error. We should be good to merge the PR once this is fixed. error[E0063]: missing field `json_response` in initializer of `rmcp::transport::StreamableHttpServerConfig`
--> crates/rmcp/tests/test_sse_concurrent_streams.rs:82:9
|
82 | StreamableHttpServerConfig {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `json_response`
For more information about this error, try `rustc --explain E0063`. |
…mode Adds `json_response: bool` field to `StreamableHttpServerConfig`. When true and `stateful_mode` is false, the server returns `Content-Type: application/json` directly instead of `text/event-stream`, eliminating SSE framing overhead for simple request-response patterns. This completes server-side JSON response support (client-side was added in modelcontextprotocol#540) and contributes to the stateless server goals of SEP-1442 (modelcontextprotocol#526). Backwards-compatible: `json_response: false` (default) preserves all existing SSE behaviour unchanged, and `stateful_mode: true` is unaffected. Benchmark evidence (50 VUs, 5min, 2 CPUs): - RPS: 770 → 1139 (+48%) - get_user_cart latency: 41ms → 0.76ms (-98%) - checkout latency: 41ms → 0.55ms (-99%) - Zero regressions, zero errors
8bfd1e2 to
93634fc
Compare
…rrent streams test Made-with: Cursor
5f171cf to
b6be330
Compare
@DaleSeo Fixed, thanks for catching it! Pushed the correction. CI should be green now. |
What this changes
Adds
json_response: boolfield toStreamableHttpServerConfig. Whentrueandstateful_modeis false, the server returnsContent-Type: application/jsondirectly instead oftext/event-stream.Why
While working on a multi-language MCP server benchmark comparing implementations across Rust, Go, Java and other runtimes under equivalent load, we noticed the Rust server (using this SDK) showed significantly lower throughput than the other runtimes, even when the business logic and external I/O were equivalent.
After profiling the request lifecycle, the bottleneck was not in the server logic itself but in the transport layer. In stateless mode, the SDK was wrapping every single response in a Server-Sent Events stream, even for straightforward request-response interactions that complete in a single round trip.
Looking at how other MCP SDKs handle this, the Go SDK already exposes this as a first-class option via
StreamableHTTPOptions:We looked for an equivalent in this SDK and found none. This PR adds it.
The MCP Streamable HTTP spec (2025-06-18) explicitly allows JSON responses as an alternative to SSE:
In stateless mode, each request is handled via
OneshotTransportwith exactly one message in and one message out. Wrapping this in an SSE stream adds unnecessary overhead:This change allows stateless servers to opt into the simpler and more efficient JSON response format that the spec already permits.
Benchmark evidence
Measurement setup: 50 concurrent virtual users, 5 minutes sustained, 2 CPUs, 2 GB RAM limit, I/O-bound workload with external HTTP calls and Redis operations, representative of typical MCP tool implementations.
The overhead was traced to SSE framing on responses from tools that are fully stateless and return a single JSON result, exactly the case this flag addresses.
Backward compatibility
json_response: false(default) keeps SSE behaviour unchangedstateful_mode: truepath is not touched at allRelation to existing work
Types of changes
Checklist