Title: fix: propagate SSE stream errors to waiting requests by heyhayes · Pull Request #2122 · modelcontextprotocol/python-sdk · GitHub
Open Graph Title: fix: propagate SSE stream errors to waiting requests by heyhayes · Pull Request #2122 · modelcontextprotocol/python-sdk
X Title: fix: propagate SSE stream errors to waiting requests by heyhayes · Pull Request #2122 · modelcontextprotocol/python-sdk
Description: Summary Fixes #1401. Also fixes #1789 (closed as duplicate). When an SSE read timeout occurs during a StreamableHTTP POST request, the pending send_request call hangs indefinitely. The transport catches the exception but never sends an error back through the read stream, leaving the caller blocked on response_stream_reader.receive() with nothing to receive. This PR fixes error propagation at the transport level so that SSE stream failures produce a JSONRPCError keyed to the original request ID. BaseSession._handle_response routes it to the correct per-request response stream, and send_request surfaces it as MCPError to the caller. This approach keeps failures isolated to the affected request rather than tearing down the entire session. What changed _handle_sse_response now sends a JSONRPCError(INTERNAL_ERROR, "SSE stream ended without a response") when the SSE stream ends without delivering a complete response, whether due to a read timeout, network error, or unexpected server close. If a last_event_id was received, reconnection is attempted first; the error is only sent after reconnection is exhausted. _handle_reconnection returns bool instead of None so callers can distinguish success (response delivered) from failure (attempts exhausted). The method also fixes an infinite recursion bug: the attempt counter was reset to 0 on every stream end (even when no complete response was delivered), which combined with httpx read timeouts causing graceful stream termination meant the reconnection loop could run forever. handle_get_stream applies the same fix to the GET stream's reconnection loop: the attempt counter only resets when events were actually received during the connection. Empty connections that close immediately count toward MAX_RECONNECTION_ATTEMPTS. _default_message_handler now logs a warning for exceptions instead of silently discarding them, providing observability for transport errors not tied to a specific request. Test plan New E2E test: client with 0.5s SSE read timeout reads a slow resource (2s server delay), asserts MCPError is raised instead of hanging New unit test: _handle_reconnection returns False when called at max attempts All 51 existing streamable HTTP tests pass (including reconnection, polling, and multi-reconnection tests) All 233 client tests pass Linting (ruff check .) and type checking (pyright) clean
Open Graph Description: Summary Fixes #1401. Also fixes #1789 (closed as duplicate). When an SSE read timeout occurs during a StreamableHTTP POST request, the pending send_request call hangs indefinitely. The transport ca...
X Description: Summary Fixes #1401. Also fixes #1789 (closed as duplicate). When an SSE read timeout occurs during a StreamableHTTP POST request, the pending send_request call hangs indefinitely. The transport ca...
Opengraph URL: https://github.com/modelcontextprotocol/python-sdk/pull/2122
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:a411dacb-4360-9b03-7eec-1daa0b62a9a7 |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | 8CE8:1F2D8B:1B686D2:2482604:6A5D43B8 |
| html-safe-nonce | 04d4d10e74316803b06c79462f9fff3fd459be839986b455118a0c2a9f218d0c |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI4Q0U4OjFGMkQ4QjoxQjY4NkQyOjI0ODI2MDQ6NkE1RDQzQjgiLCJ2aXNpdG9yX2lkIjoiNjY1MjI3NjUwOTI2NDEzNTA5NyIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | 657f56f713fffe58c1168a37e9068da2e2bd0e465457eabc0b90bf0d932ae7b3 |
| hovercard-subject-tag | pull_request:3310804750 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/modelcontextprotocol/python-sdk/pull/2122/files |
| twitter:image | https://avatars.githubusercontent.com/u/39026099?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/39026099?s=400&v=4 |
| og:image:alt | Summary Fixes #1401. Also fixes #1789 (closed as duplicate). When an SSE read timeout occurs during a StreamableHTTP POST request, the pending send_request call hangs indefinitely. The transport ca... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | 5290d7e14309ad1e76106a9c4237bd1041517e83ea182c8ab756752cb0c6940b |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/modelcontextprotocol/python-sdk git https://github.com/modelcontextprotocol/python-sdk.git |
| octolytics-dimension-user_id | 182288589 |
| octolytics-dimension-user_login | modelcontextprotocol |
| octolytics-dimension-repository_id | 862584018 |
| octolytics-dimension-repository_nwo | modelcontextprotocol/python-sdk |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 862584018 |
| octolytics-dimension-repository_network_root_nwo | modelcontextprotocol/python-sdk |
| turbo-body-classes | logged-out env-production page-responsive full-width |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 9c975978430e9ad293956f2bbdaf153b1bd84a99 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width