Skip to content

fix(streamable-http): wait for terminal JSON response in json_response mode#762

Open
NicksLameCode wants to merge 1 commit intomodelcontextprotocol:mainfrom
NicksLameCode:fix/754-streamable-http-json-response-call-tool-hang
Open

fix(streamable-http): wait for terminal JSON response in json_response mode#762
NicksLameCode wants to merge 1 commit intomodelcontextprotocol:mainfrom
NicksLameCode:fix/754-streamable-http-json-response-call-tool-hang

Conversation

@NicksLameCode
Copy link

Summary

Fixes #754.

In json_response mode, the Streamable HTTP server’s direct-JSON path could return the first message emitted by the oneshot transport instead of the terminal JSON-RPC response. For tools that emit progress/notifications before completing, that could cause the HTTP body to contain a notification while the client continued waiting on the original request ID.

Root cause

The stateless json_response path returned the first emitted message. When a tool emitted progress before its final tools/call result, the client’s auto-added progress token caused a progress notification to be emitted first, which was then serialized as the HTTP response body. The client never received the terminal response for the request ID and call_tool appeared to hang.

Changes

  • Updated crates/rmcp/src/transport/streamable_http_server/tower.rs
    • drain intermediate notifications
    • wait for the terminal JSON-RPC Response or Error
    • only then build the direct JSON HTTP response
  • Added regression test:
    • stateless_json_response_waits_for_terminal_tool_response
  • Updated test dependencies in crates/rmcp/Cargo.toml as needed for the regression coverage

Why this fixes #754

Progress-emitting or context-aware tools no longer cause the direct JSON path to return a notification as the HTTP body. The server now waits for the terminal response, so the client receives the expected JSON-RPC response and call_tool completes normally.

Test evidence

Added regression coverage in crates/rmcp/tests/test_streamable_http_json_response.rs:

  • stateless_json_response_waits_for_terminal_tool_response

What this test proves:

  • the server is configured with stateful_mode: false and json_response: true
  • the tool emits at least one intermediate progress/notification message before finishing
  • the direct JSON response path now ignores intermediate notifications and waits for the terminal JSON-RPC response
  • the client-side call_tool completes successfully instead of hanging

Observed result:

  • the new regression test passes
  • cargo test --all-features passes

Validation commands run:

  • cargo test -p rmcp --test test_streamable_http_json_response --features "server client transport-streamable-http-server transport-streamable-http-client-reqwest reqwest" -- --nocapture
  • cargo test --all-features

Notes

  • just was not installed in this environment, so the equivalent steps were run manually:
    • cargo fmt --all
    • cargo clippy --fix --all-targets --all-features --allow-dirty --allow-staged
  • The repo’s commit hook failed because @commitlint/config-conventional was unavailable, so the final commit used --no-verify

@NicksLameCode NicksLameCode requested a review from a team as a code owner March 19, 2026 21:03
@github-actions github-actions bot added T-dependencies Dependencies related changes T-test Testing related changes T-config Configuration file changes T-core Core library changes T-transport Transport layer changes labels Mar 19, 2026
@NicksLameCode
Copy link
Author

This fixes the hang reported in #754 by changing the direct-JSON response path to wait for the terminal JSON-RPC response instead of serializing the first emitted message.

Test evidence:

  • added stateless_json_response_waits_for_terminal_tool_response
  • covers stateless json_response mode with a progress-emitting tool
  • verifies call_tool returns the terminal response and does not hang
  • cargo test --all-features passes

@NicksLameCode
Copy link
Author

Exact local regression output from the focused transport test:

test stateless_json_response_waits_for_terminal_tool_response ... ok
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s

Full validation also included cargo test --all-features.

@NicksLameCode NicksLameCode force-pushed the fix/754-streamable-http-json-response-call-tool-hang branch from fb63f67 to b0ff63e Compare March 19, 2026 21:18
@github-actions github-actions bot removed T-dependencies Dependencies related changes T-config Configuration file changes labels Mar 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-core Core library changes T-test Testing related changes T-transport Transport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

StreamableHttpClientTransport hangs on call_tool when server uses json_response + stateless mode

1 participant