Title: fix(streamable_http): expose session_idle_timeout and don't reap active sessions by BuildWithAbid · Pull Request #2510 · modelcontextprotocol/python-sdk · GitHub
Open Graph Title: fix(streamable_http): expose session_idle_timeout and don't reap active sessions by BuildWithAbid · Pull Request #2510 · modelcontextprotocol/python-sdk
X Title: fix(streamable_http): expose session_idle_timeout and don't reap active sessions by BuildWithAbid · Pull Request #2510 · modelcontextprotocol/python-sdk
Description: Closes #2455 Fixes both problems described in the issue: Problem 1: session_idle_timeout not exposed by streamable_http_app() StreamableHTTPSessionManager(...) accepted session_idle_timeout, but neither Server.streamable_http_app(...) nor MCPServer.streamable_http_app(...) did. So the canonical high-level API could not configure a supported session-manager feature. This PR threads session_idle_timeout: float | None = None through both wrappers and forwards it to the underlying StreamableHTTPSessionManager. Problem 2: active requests cancelled by the idle timeout The previous logic reset the deadline only at the start of each incoming request. A handler whose work outlived session_idle_timeout would be cancelled mid-flight; the client waited until session termination instead of receiving the tool result. Replaced the deadline-bumping logic with a per-transport in-flight counter and a _suspend_idle_timeout() async context manager: on entry: idle_active_requests += 1. If this is the first concurrent request, set idle_scope.deadline = math.inf. on exit: idle_active_requests -= 1. If this was the last in-flight request, restore now + session_idle_timeout. Both the new-session and existing-session code paths now wrap transport.handle_request(...) with _suspend_idle_timeout, so the deadline is suspended for the lifetime of every request. Test plan All in tests/server/test_streamable_http_manager.py: test_session_idle_timeout_passthrough_lowlevel — verifies the kwarg propagates from Server.streamable_http_app() to the manager. test_session_idle_timeout_passthrough_mcpserver — same for MCPServer.streamable_http_app(). test_suspend_idle_timeout_sets_deadline_inf_then_restores — direct unit test of the helper: deadline is inf during the request, finite (≈ now + timeout) after. test_suspend_idle_timeout_only_restores_after_last_concurrent_request — nested suspensions keep the deadline at inf until the outermost exit; counter logic correctness. test_suspend_idle_timeout_no_op_without_timeout — helper is inert when no timeout is configured. test_long_running_request_outlives_idle_timeout — regression for problem 2: end-to-end via httpx.ASGITransport + Client, a tool whose handler sleeps 4× the timeout still completes successfully. test_idle_session_reaped_after_request_completes — the session is still reaped once the request finishes, so existing reaping behavior is preserved. Local results: ``` tests/server/test_streamable_http_manager.py 18 passed tests/server/ + tests/shared/test_streamable_http.py 542 passed ruff check + ruff format clean pyright 0 errors, 0 warnings ``` Notes No public API removed — session_idle_timeout is a new kwarg with the same None default everywhere. The validation guards (session_idle_timeout > 0, mutually exclusive with stateless) still live on StreamableHTTPSessionManager.__init__ and continue to raise correctly when invoked through either wrapper.
Open Graph Description: Closes #2455 Fixes both problems described in the issue: Problem 1: session_idle_timeout not exposed by streamable_http_app() StreamableHTTPSessionManager(...) accepted session_idle_timeout, but ne...
X Description: Closes #2455 Fixes both problems described in the issue: Problem 1: session_idle_timeout not exposed by streamable_http_app() StreamableHTTPSessionManager(...) accepted session_idle_timeout, but ne...
Opengraph URL: https://github.com/modelcontextprotocol/python-sdk/pull/2510
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:38929514-38b9-7adc-40c0-885b84116b8b |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | D08A:20DA71:1E8C33B:283A2CA:6A5C97D1 |
| html-safe-nonce | 205bf0391ac6c22f0f63716858003415caec58fa5ac9e1e8b00be0fe9a0ba857 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJEMDhBOjIwREE3MToxRThDMzNCOjI4M0EyQ0E6NkE1Qzk3RDEiLCJ2aXNpdG9yX2lkIjoiNzI5NzEwNjU1MjQ0MDAwMjUxMyIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | 1b455469b0b24c2cc26a29fe93a3012a85c6ee5ca5f47028941025a71be2d70f |
| hovercard-subject-tag | pull_request:3587581957 |
| 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/2510/files |
| twitter:image | https://avatars.githubusercontent.com/u/43534342?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/43534342?s=400&v=4 |
| og:image:alt | Closes #2455 Fixes both problems described in the issue: Problem 1: session_idle_timeout not exposed by streamable_http_app() StreamableHTTPSessionManager(...) accepted session_idle_timeout, but ne... |
| 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