René's URL Explorer Experiment


Title: Flaky streamable-HTTP/SSE tests: TOCTOU port race under pytest -n auto · Issue #2704 · modelcontextprotocol/python-sdk · GitHub

Open Graph Title: Flaky streamable-HTTP/SSE tests: TOCTOU port race under pytest -n auto · Issue #2704 · modelcontextprotocol/python-sdk

X Title: Flaky streamable-HTTP/SSE tests: TOCTOU port race under pytest -n auto · Issue #2704 · modelcontextprotocol/python-sdk

Description: Initial Checks I confirm that I'm using the latest version of MCP Python SDK I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue Description Summary Tests in tests...

Open Graph Description: Initial Checks I confirm that I'm using the latest version of MCP Python SDK I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this ...

X Description: Initial Checks I confirm that I'm using the latest version of MCP Python SDK I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening t...

Opengraph URL: https://github.com/modelcontextprotocol/python-sdk/issues/2704

X: @github

direct link

Domain: github.com


Hey, it has json ld scripts:
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Flaky streamable-HTTP/SSE tests: TOCTOU port race under pytest -n auto","articleBody":"### Initial Checks\n\n- [x] I confirm that I'm using the latest version of MCP Python SDK\n- [x] I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue\n\n### Description\n\n### Summary\n\n  Tests in `tests/shared/test_streamable_http.py` intermittently fail in CI (non-deterministically, across different Python versions). The failures are not real regressions, they're caused by a time-of-check/time-of-use (TOCTOU) race in how the test server fixtures allocate ports, which collides when tests run in parallel under pytest -n auto.\n\n### Evidence\n  \n  The flakiness is intermittent and non-deterministic: the same commit, re-run across the CI matrix, fails on different tests and different Python versions, while passing locally and on most matrix entries. That pattern, a failure that moves around rather than reproducing on a specific test/version, is the signature of a parallelism race, not a code defect.\n\n  Two failure signatures have been observed, and both reduce to \"the client connected to the wrong server instance\":\n\n  1. Server can't bind the port (`test_streamable_http_client_session_termination_204`)\n\n  ERROR: [Errno 98] error while attempting to bind on address ('127.0.0.1', 35105): address already in use\n  AssertionError: assert 2 == 10\n  The intended server (10 tools) loses the bind race, so the client reaches a different test's server: `len(tools.tools)` comes back as `2` (the `echo_headers/echo_context server`) instead of 10.\n\n  2. Crossed responses (`test_streamable_http_client_respects_retry_interval`)\n\n  pydantic_core.ValidationError: 1 validation error for CallToolResult\n  content\n    Field required [type=missing, input_value={'tools': [...]}, input_type=dict]\n  A call_tool request is answered with a `ListToolsResult` payload (`{'tools': [...]}`), which fails to validate as `CallToolResult`. The client is talking to a server/stream that belongs to another test.\n  \n  Both symptoms are downstream of two servers contending for the same ephemeral port — see Root cause below.\n\n\n### Root cause\n\nThe port fixtures pick a port, then close the socket before the real server binds it:\n\n```python\n# tests/shared/test_streamable_http.py:474\n@pytest.fixture\ndef basic_server_port() -\u003e int:\n    with socket.socket() as s:\n        s.bind((\"127.0.0.1\", 0))      # OS assigns a free port\n        return s.getsockname()[1]      # ...socket closes here, freeing the port\n```\n\nThe server is then started in a separate `multiprocessing.Process` (`run_server`, line 435) and binds that port later. In the gap, another xdist worker's fixture can be handed the same port by the OS, so two servers race for it - one fails with `Errno 98`, and clients can reach the wrong server.\n\n### Affected files (same pattern)\n\n  - tests/shared/test_streamable_http.py\n  - tests/shared/test_sse.py\n  - tests/server/test_sse_security.py\n  - tests/server/test_streamable_http_security.py\n  - tests/client/test_http_unicode.py\n\n### Proposed fix\n\nReuse the existing race-free helper `run_uvicorn_in_thread` in `tests/test_helpers.py:15`. It binds and `listen()`s a socket, then hands that same socket to uvicorn (`server.run(sockets=[sock])`), so there is no window where another worker can claim the port. This pattern is already proven in tests/shared/test_ws.py, and its docstring documents exactly this race.\n\nMigrate the racy fixtures, e.g.:\n\n```python\n# before: basic_server_port + basic_server + basic_server_url (3 fixtures, racy)\n# after:\n@pytest.fixture\ndef basic_server_url() -\u003e Generator[str, None, None]:\n    app = create_app()\n    with run_uvicorn_in_thread(app, limit_concurrency=10, timeout_keep_alive=5, access_log=False) as url:\n        yield url\n```\n\nThis also removes the need for the `wait_for_server(port)` poll (the helper's pre-listen()ed socket means connections are accepted as soon as the fixture yields).\n\n### Considerations\n\n  - This converts the test servers from a subprocess to a background thread (as `test_ws.py` already does). Need to confirm no test relies on subprocess semantics (e.g. `proc.kill()`); the streamable-HTTP tests appear to test HTTP-level behavior, not process lifecycle.\n  - A subprocess-preserving alternative (pass a pre-bound listening socket into the child via `server.run(sockets=[sock]))` is harder cross-platform, Windows spawn can't easily inherit/pickle sockets, so the thread helper is preferred for the CI matrix.\n\n### Scope\n\nPrimary scope: `tests/shared/test_streamable_http.py`. The 4 sibling files share the root cause and can be migrated in the same PR or as follow-ups.\n\n### Acceptance criteria\n\n  - Racy `*_port` fixtures + `run_server/wait_for_server` subprocess pattern removed from the affected file(s).\n  - Tests pass reliably under `uv run pytest -n auto` across the CI matrix (3.10–3.14, ubuntu + windows).\n\n\n### Example Code\n\n```Python\nimport socket\n\ndef pick_free_port() -\u003e int:\n    # Exact pattern used by basic_server_port / json_server_port / event_server_port\n    # in tests/shared/test_streamable_http.py\n    with socket.socket() as s:\n        s.bind((\"127.0.0.1\", 0))\n        return s.getsockname()[1]  # \u003c- socket closes here, so the port is free again\n\n\n# A fixture assigns this port to \"server A\"...\nport = pick_free_port()\n\n# ...but server A is started later (in a separate multiprocessing.Process), so the\n# port sits free in between. Under `pytest -n auto`, another worker can claim it.\n# Simulate that intruder grabbing the port during the window:\nintruder = socket.socket()\nintruder.bind((\"127.0.0.1\", port))\nintruder.listen()\n\n# Now server A finally tries to bind the port it was handed:\nserver_a = socket.socket()\nserver_a.bind((\"127.0.0.1\", port))  # OSError: [Errno 98] Address already in use\n```\n\n### Python \u0026 MCP Python SDK\n\n```Text\nPython: CPython 3.10 and 3.13 (failures captured on ubuntu-latest in CI).\n        Not version-specific, it's a test-parallelism race, so it can surface anywhere on the 3.10–3.14 matrix.\n\nMCP Python SDK: main branch (the unreleased v2 line) — 1.25.1.dev builds.\n```","author":{"url":"https://github.com/Ar-maan05","@type":"Person","name":"Ar-maan05"},"datePublished":"2026-05-28T08:10:33.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":5},"url":"https://github.com/2704/python-sdk/issues/2704"}

route-pattern/_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format)
route-controllervoltron_issues_fragments
route-actionissue_layout
fetch-noncev2:e450c009-ea43-32e5-1f4d-b8af86f1f97f
current-catalog-service-hash81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114
request-idAB52:38363:1A05840:252E21C:6A5A393C
html-safe-nonce38ea584daad35e212c89990d89bfbb88760a6e02821bdf55458130611f5a9db7
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBQjUyOjM4MzYzOjFBMDU4NDA6MjUyRTIxQzo2QTVBMzkzQyIsInZpc2l0b3JfaWQiOiI4NjE2NTkyNzI5OTQxMDk2NzY0IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0=
visitor-hmacaa5520cadf8be42f497263f8e905cb15ff98a9c7c620230546cd5c298d9b08b2
hovercard-subject-tagissue:4538807209
github-keyboard-shortcutsrepository,issues,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///voltron/issues_fragments/issue_layout
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/modelcontextprotocol/python-sdk/2704/issue_layout
twitter:imagehttps://opengraph.githubassets.com/3388fe83a32faa4c46ceca4820a3418ae343c43217144d45908d3a1448a58ef3/modelcontextprotocol/python-sdk/issues/2704
twitter:cardsummary_large_image
og:imagehttps://opengraph.githubassets.com/3388fe83a32faa4c46ceca4820a3418ae343c43217144d45908d3a1448a58ef3/modelcontextprotocol/python-sdk/issues/2704
og:image:altInitial Checks I confirm that I'm using the latest version of MCP Python SDK I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this ...
og:image:width1200
og:image:height600
og:site_nameGitHub
og:typeobject
og:author:usernameAr-maan05
hostnamegithub.com
expected-hostnamegithub.com
None19c67ad7579d2401ff06e88a32ef45460fbb40fab3191c7f7582d5e166f326fc
turbo-cache-controlno-preview
go-importgithub.com/modelcontextprotocol/python-sdk git https://github.com/modelcontextprotocol/python-sdk.git
octolytics-dimension-user_id182288589
octolytics-dimension-user_loginmodelcontextprotocol
octolytics-dimension-repository_id862584018
octolytics-dimension-repository_nwomodelcontextprotocol/python-sdk
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id862584018
octolytics-dimension-repository_network_root_nwomodelcontextprotocol/python-sdk
turbo-body-classeslogged-out env-production page-responsive
disable-turbofalse
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
releasee618485bff22dab2ef1ffb4e34e4e6626688df69
ui-targetfull
theme-color#1e2327
color-schemelight dark

Links:

Skip to contenthttps://github.com/modelcontextprotocol/python-sdk/issues/2704#start-of-content
https://github.com/
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fmodelcontextprotocol%2Fpython-sdk%2Fissues%2F2704
GitHub CopilotWrite better code with AIhttps://github.com/features/copilot
GitHub Copilot appDirect agents from issue to mergehttps://github.com/features/ai/github-app
MCP RegistryNewIntegrate external toolshttps://github.com/mcp
ActionsAutomate any workflowhttps://github.com/features/actions
CodespacesInstant dev environmentshttps://github.com/features/codespaces
IssuesPlan and track workhttps://github.com/features/issues
Code ReviewManage code changeshttps://github.com/features/code-review
GitHub Advanced SecurityFind and fix vulnerabilitieshttps://github.com/security/advanced-security
Code securitySecure your code as you buildhttps://github.com/security/advanced-security/code-security
Secret protectionStop leaks before they starthttps://github.com/security/advanced-security/secret-protection
Why GitHubhttps://github.com/why-github
Documentationhttps://docs.github.com
Bloghttps://github.blog
Changeloghttps://github.blog/changelog
Marketplacehttps://github.com/marketplace
View all featureshttps://github.com/features
Enterpriseshttps://github.com/enterprise
Small and medium teamshttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
App Modernizationhttps://github.com/solutions/use-case/app-modernization
DevSecOpshttps://github.com/solutions/use-case/devsecops
DevOpshttps://github.com/solutions/use-case/devops
CI/CDhttps://github.com/solutions/use-case/ci-cd
View all use caseshttps://github.com/solutions/use-case
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
View all industrieshttps://github.com/solutions/industry
View all solutionshttps://github.com/solutions
AIhttps://github.com/resources/articles?topic=ai
Software Developmenthttps://github.com/resources/articles?topic=software-development
DevOpshttps://github.com/resources/articles?topic=devops
Securityhttps://github.com/resources/articles?topic=security
View all topicshttps://github.com/resources/articles
Customer storieshttps://github.com/customer-stories
Events & webinarshttps://github.com/resources/events
Ebooks & reportshttps://github.com/resources/whitepapers
Business insightshttps://github.com/solutions/executive-insights
GitHub Skillshttps://skills.github.com
Documentationhttps://docs.github.com
Customer supporthttps://support.github.com
Community forumhttps://github.com/orgs/community/discussions
Trust centerhttps://github.com/trust-center
Partnershttps://github.com/partners
View all resourceshttps://github.com/resources
GitHub SponsorsFund open source developershttps://github.com/open-source/sponsors
Security Labhttps://securitylab.github.com
Maintainer Communityhttps://maintainers.github.com
Acceleratorhttps://github.com/open-source/accelerator
GitHub Starshttps://stars.github.com
Archive Programhttps://archiveprogram.github.com
Topicshttps://github.com/topics
Trendinghttps://github.com/trending
Collectionshttps://github.com/collections
Enterprise platformAI-powered developer platformhttps://github.com/enterprise
GitHub Advanced SecurityEnterprise-grade security featureshttps://github.com/security/advanced-security
Copilot for BusinessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/enterprise/premium-support
Pricinghttps://github.com/pricing
Search syntax tipshttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
documentationhttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fmodelcontextprotocol%2Fpython-sdk%2Fissues%2F2704
Sign up https://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fvoltron%2Fissues_fragments%2Fissue_layout&source=header-repo&source_repo=modelcontextprotocol%2Fpython-sdk
Reloadhttps://github.com/modelcontextprotocol/python-sdk/issues/2704
Reloadhttps://github.com/modelcontextprotocol/python-sdk/issues/2704
Reloadhttps://github.com/modelcontextprotocol/python-sdk/issues/2704
Please reload this pagehttps://github.com/modelcontextprotocol/python-sdk/issues/2704
modelcontextprotocol https://github.com/modelcontextprotocol
python-sdkhttps://github.com/modelcontextprotocol/python-sdk
Notifications https://github.com/login?return_to=%2Fmodelcontextprotocol%2Fpython-sdk
Fork 3.7k https://github.com/login?return_to=%2Fmodelcontextprotocol%2Fpython-sdk
Star 23.6k https://github.com/login?return_to=%2Fmodelcontextprotocol%2Fpython-sdk
Code https://github.com/modelcontextprotocol/python-sdk
Issues 255 https://github.com/modelcontextprotocol/python-sdk/issues
Pull requests 303 https://github.com/modelcontextprotocol/python-sdk/pulls
Actions https://github.com/modelcontextprotocol/python-sdk/actions
Projects https://github.com/modelcontextprotocol/python-sdk/projects
Models https://github.com/modelcontextprotocol/python-sdk/models
Security and quality 6 https://github.com/modelcontextprotocol/python-sdk/security
Insights https://github.com/modelcontextprotocol/python-sdk/pulse
Code https://github.com/modelcontextprotocol/python-sdk
Issues https://github.com/modelcontextprotocol/python-sdk/issues
Pull requests https://github.com/modelcontextprotocol/python-sdk/pulls
Actions https://github.com/modelcontextprotocol/python-sdk/actions
Projects https://github.com/modelcontextprotocol/python-sdk/projects
Models https://github.com/modelcontextprotocol/python-sdk/models
Security and quality https://github.com/modelcontextprotocol/python-sdk/security
Insights https://github.com/modelcontextprotocol/python-sdk/pulse
#2767https://github.com/modelcontextprotocol/python-sdk/pull/2767
Flaky streamable-HTTP/SSE tests: TOCTOU port race under pytest -n autohttps://github.com/modelcontextprotocol/python-sdk/issues/2704#top
#2767https://github.com/modelcontextprotocol/python-sdk/pull/2767
P2Moderate issues affecting some users, edge cases, potentially valuable featurehttps://github.com/modelcontextprotocol/python-sdk/issues?q=state%3Aopen%20label%3A%22P2%22
bugSomething isn't workinghttps://github.com/modelcontextprotocol/python-sdk/issues?q=state%3Aopen%20label%3A%22bug%22
fix proposedBot has a verified fix diff in the commenthttps://github.com/modelcontextprotocol/python-sdk/issues?q=state%3Aopen%20label%3A%22fix%20proposed%22
ready for workEnough information for someone to start working onhttps://github.com/modelcontextprotocol/python-sdk/issues?q=state%3Aopen%20label%3A%22ready%20for%20work%22
https://github.com/Ar-maan05
Ar-maan05https://github.com/Ar-maan05
on May 28, 2026https://github.com/modelcontextprotocol/python-sdk/issues/2704#issue-4538807209
https://github.com/modelcontextprotocol/python-sdk/issueshttps://github.com/modelcontextprotocol/python-sdk/issues
P2Moderate issues affecting some users, edge cases, potentially valuable featurehttps://github.com/modelcontextprotocol/python-sdk/issues?q=state%3Aopen%20label%3A%22P2%22
bugSomething isn't workinghttps://github.com/modelcontextprotocol/python-sdk/issues?q=state%3Aopen%20label%3A%22bug%22
fix proposedBot has a verified fix diff in the commenthttps://github.com/modelcontextprotocol/python-sdk/issues?q=state%3Aopen%20label%3A%22fix%20proposed%22
ready for workEnough information for someone to start working onhttps://github.com/modelcontextprotocol/python-sdk/issues?q=state%3Aopen%20label%3A%22ready%20for%20work%22
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.