René's URL Explorer Experiment


Title: Enable session roaming across multiple server instances by davidroberts-merlyn · Pull Request #1519 · modelcontextprotocol/python-sdk · GitHub

Open Graph Title: Enable session roaming across multiple server instances by davidroberts-merlyn · Pull Request #1519 · modelcontextprotocol/python-sdk

X Title: Enable session roaming across multiple server instances by davidroberts-merlyn · Pull Request #1519 · modelcontextprotocol/python-sdk

Description: Motivation and Context Problem When deploying MCP servers across multiple instances (Kubernetes pods, Docker containers, worker processes), sessions are tied to the specific instance that created them. This requires sticky sessions at the load balancer level and prevents true horizontal scaling. Users are currently forced to choose between: Sticky sessions - Suboptimal load distribution, sessions lost on pod failure Single worker - Wastes resources, limits throughput Stateless mode - Loses session continuity and event replay This limitation is documented in multiple issues: #520 (multi-worker sessions), #692 (session reuse across instances), #880 (horizontal scalability), and #1350 (sticky session problems). Solution This PR enables session roaming - allowing sessions to seamlessly move between server instances without requiring sticky sessions. The key insight is that EventStore already serves as proof of session existence. When a request arrives with a session ID that's not in an instance's local memory, if an EventStore is configured, the instance can safely: Create a transport for that session ID (session roaming) Let EventStore replay any missed events (continuity) Handle the request seamlessly What Changed Modified streamable_http_manager.py (~50 lines): Added session roaming logic in _handle_stateful_request() When unknown session ID + EventStore exists → create transport (roaming!) Extracted duplicate server task code into reusable methods Updated docstrings to document session roaming capability Added comprehensive tests (test_session_roaming.py, 510 lines): Session roaming with EventStore Rejection without EventStore Concurrent request handling Exception cleanup behavior Fast path verification Logging verification Added production-ready example (simple-streamablehttp-roaming/, 13 files): Complete working example with Redis EventStore Multi-instance deployment support Docker Compose configuration (3 instances + Redis + NGINX) Kubernetes deployment example Automated test script demonstrating roaming Comprehensive documentation (README, QUICKSTART, implementation details) Why This Approach Previous Attempts Eplored two other approaches before arriving at this solution: Custom Session Store (outside SDK) - Created own session validation in the application layer, but this didn't solve the core problem and required every user to implement their own solution, it also meant that as the dict that contained session in the sdk was unchanged it still required sticky sessions. SessionStore ABC (in SDK) - Added a new SessionStore interface requiring both EventStore + SessionStore parameters. While functional, this approach required two separate storage backends and was more complex than necessary. It also meant that if you did not supply one of the stores it was not really stateful. Current Approach: EventStore-Only The key insight: EventStore already proves sessions existed. If events exist for a session ID, that session must have existed to create those events. No separate SessionStore needed. Benefits: ✅ One store instead of two (simpler) ✅ Reuses existing EventStore interface (no new APIs) ✅ Impossible to misconfigure (EventStore = both events + proof) ✅ Aligns with SEP-1359 (sessions are conversation context, not auth) ✅ Minimal code changes (~50 lines) ✅ 100% backward compatible (behavior enhancement only) Usage Before (Requires Sticky Sessions) # Without EventStore - sessions in memory only manager = StreamableHTTPSessionManager(app=app) # Deployment: requires sticky sessions for multi-instance After (No Sticky Sessions Needed) # With EventStore - sessions roam freely event_store = RedisEventStore(redis_url="redis://redis:6379") manager = StreamableHTTPSessionManager( app=app, event_store=event_store # Enables session roaming! ) # Deployment: load balancer can route freely, no sticky sessions How It Works Client → Instance 1 (creates session "abc123", stores events in Redis) Client → Instance 2 (with session "abc123") ↓ Instance 2 checks memory → not found Instance 2 sees EventStore exists Instance 2 creates transport for "abc123" (roaming!) EventStore replays events from Redis Session continues seamlessly ✅ How Has This Been Tested? ✅ Existing test suite (no regressions) ✅ 8 new tests for session roaming ✅ Automated roaming test script in example ✅ Testing within our existing infrastructure The included example also demonstrates: Multi-instance deployment with Docker Compose Kubernetes manifests (3 replicas, no sessionAffinity needed) NGINX load balancing without sticky sessions Redis EventStore for shared state Automated testing and verification with provided test script Breaking Changes None. This is a pure behavior enhancement: ✅ Existing code works unchanged ✅ No API changes ✅ No new required parameters ✅ Backward compatible Types of changes Bug fix (non-breaking change which fixes an issue) New feature (non-breaking change which adds functionality) Breaking change (fix or feature that would cause existing functionality to change) Documentation update Checklist I have read the MCP Documentation My code follows the repository's style guidelines New and existing tests pass locally I have added appropriate error handling I have added or updated documentation as needed Additional context Related Issues Closes #520, #692, #880, #1350 This implementation addresses the core limitation described in all these issues: the inability to run stateful MCP servers across multiple instances without sticky sessions.

Open Graph Description: Motivation and Context Problem When deploying MCP servers across multiple instances (Kubernetes pods, Docker containers, worker processes), sessions are tied to the specific instance that created t...

X Description: Motivation and Context Problem When deploying MCP servers across multiple instances (Kubernetes pods, Docker containers, worker processes), sessions are tied to the specific instance that created t...

Opengraph URL: https://github.com/modelcontextprotocol/python-sdk/pull/1519

X: @github

direct link

Domain: github.com

route-pattern/:user_id/:repository/pull/:id/files(.:format)
route-controllerpull_requests
route-actionfiles
fetch-noncev2:0e7b37ae-0c49-6837-4995-7287bb329c4e
current-catalog-service-hashae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b
request-idA1EA:1E71B6:D8C8A1:1339626:6A5A1F86
html-safe-nonce14f57d6c0ebfc9ce6655c2277ea8f3706d2ff75d5dd9802f226a48e517d53d27
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBMUVBOjFFNzFCNjpEOEM4QTE6MTMzOTYyNjo2QTVBMUY4NiIsInZpc2l0b3JfaWQiOiI1NDczMTM2MDUwMzgxMTM1NzUwIiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0=
visitor-hmac13336e986eeb33a92a812190a685a2af210ecd8af3927f6d02ae26808bde0a65
hovercard-subject-tagpull_request:2950652435
github-keyboard-shortcutsrepository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///pull_requests/show/files
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/modelcontextprotocol/python-sdk/pull/1519/files
twitter:imagehttps://avatars.githubusercontent.com/u/121063480?s=400&v=4
twitter:cardsummary_large_image
og:imagehttps://avatars.githubusercontent.com/u/121063480?s=400&v=4
og:image:altMotivation and Context Problem When deploying MCP servers across multiple instances (Kubernetes pods, Docker containers, worker processes), sessions are tied to the specific instance that created t...
og:site_nameGitHub
og:typeobject
hostnamegithub.com
expected-hostnamegithub.com
Nonee31742e80bbd077fd5679c13ed870ce4e0c13803a2fa5beaae557b6769de2c8a
turbo-cache-controlno-preview
diff-viewunified
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 full-width
disable-turbotrue
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
releasec9fcdbf98c619d1561ed843fa01d9766a32b5cd9
ui-targetfull
theme-color#1e2327
color-schemelight dark

Links:

Skip to contenthttps://github.com/modelcontextprotocol/python-sdk/pull/1519/files#start-of-content
https://github.com/
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fmodelcontextprotocol%2Fpython-sdk%2Fpull%2F1519%2Ffiles
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%2Fpull%2F1519%2Ffiles
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%2Fpull_requests%2Fshow%2Ffiles&source=header-repo&source_repo=modelcontextprotocol%2Fpython-sdk
Reloadhttps://github.com/modelcontextprotocol/python-sdk/pull/1519/files
Reloadhttps://github.com/modelcontextprotocol/python-sdk/pull/1519/files
Reloadhttps://github.com/modelcontextprotocol/python-sdk/pull/1519/files
Please reload this pagehttps://github.com/modelcontextprotocol/python-sdk/pull/1519/files
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
Sign up for GitHub https://github.com/signup?return_to=%2Fmodelcontextprotocol%2Fpython-sdk%2Fissues%2Fnew%2Fchoose
terms of servicehttps://docs.github.com/terms
privacy statementhttps://docs.github.com/privacy
Sign inhttps://github.com/login?return_to=%2Fmodelcontextprotocol%2Fpython-sdk%2Fissues%2Fnew%2Fchoose
davidroberts-merlynhttps://github.com/davidroberts-merlyn
modelcontextprotocol:mainhttps://github.com/modelcontextprotocol/python-sdk/tree/main
davidroberts-merlyn:session-roaminghttps://github.com/davidroberts-merlyn/python-sdk/tree/session-roaming
Conversation 5 https://github.com/modelcontextprotocol/python-sdk/pull/1519
Commits 9 https://github.com/modelcontextprotocol/python-sdk/pull/1519/commits
Checks 18 https://github.com/modelcontextprotocol/python-sdk/pull/1519/checks
Files changed https://github.com/modelcontextprotocol/python-sdk/pull/1519/files
Please reload this pagehttps://github.com/modelcontextprotocol/python-sdk/pull/1519/files
Enable session roaming across multiple server instances https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#top
Show all changes 9 commits https://github.com/modelcontextprotocol/python-sdk/pull/1519/files
9faef55 Enable session roaming across multiple server instances davidroberts-merlyn Oct 27, 2025 https://github.com/modelcontextprotocol/python-sdk/pull/1519/commits/9faef55c91e81ce33d2acc3c01a5646802e5ce28
5e75bec Fix prettier formatting in docker-compose.yml davidroberts-merlyn Oct 27, 2025 https://github.com/modelcontextprotocol/python-sdk/pull/1519/commits/5e75bec68be70cd30936e3ca56cdf9c90e73c316
07cb1dd Fix markdownlint issues in example documentation davidroberts-merlyn Oct 27, 2025 https://github.com/modelcontextprotocol/python-sdk/pull/1519/commits/07cb1dd601c8127c2377dbef9887e3f9914d8c36
550fe88 Fix race condition in session roaming davidroberts-merlyn Oct 27, 2025 https://github.com/modelcontextprotocol/python-sdk/pull/1519/commits/550fe88bbab5617b8b61966c72a9e05380fb259c
7df72c1 Fix test mock setup for async context managers davidroberts-merlyn Oct 27, 2025 https://github.com/modelcontextprotocol/python-sdk/pull/1519/commits/7df72c1299555cdaf4d74e5493473dc3fad3236a
ea7813f Add missing contextlib import for async context manager decorator davidroberts-merlyn Oct 27, 2025 https://github.com/modelcontextprotocol/python-sdk/pull/1519/commits/ea7813fc3ff90f134213533376e8d3674d5c2747
33f14f0 Fix pyright type errors for asynccontextmanager decorators davidroberts-merlyn Oct 27, 2025 https://github.com/modelcontextprotocol/python-sdk/pull/1519/commits/33f14f09b1bceb4df42ca224d55a15f2acb0fa68
ce114b2 Fix test failures by making mock app.run block until cancelled davidroberts-merlyn Oct 27, 2025 https://github.com/modelcontextprotocol/python-sdk/pull/1519/commits/ce114b28995d9787c57a833dee44e3f62359e377
64d7f4e Merge branch 'main' into session-roaming davidroberts-merlyn Oct 28, 2025 https://github.com/modelcontextprotocol/python-sdk/pull/1519/commits/64d7f4e40a234ac2db01a80e8845eeb808826310
Clear filters https://github.com/modelcontextprotocol/python-sdk/pull/1519/files
Please reload this pagehttps://github.com/modelcontextprotocol/python-sdk/pull/1519/files
Please reload this pagehttps://github.com/modelcontextprotocol/python-sdk/pull/1519/files
.gitignore https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-4096843b8d52c05d1f240d59956e418654927c970b1cfded8f6949ef8b8f3a8e
Dockerfile https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-12f10218a6951c48ea611d69ecbd7efe8602c613b6fad9de593245a91689beee
FILES.md https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-a052ade6ad5d3dc3b280ba25f2d835d2a643a9d4d8973220b41b7373d36b80ac
QUICKSTART.md https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-143fd26e8cf90152ac9735d7559e9b961603b89a628e57249f7cc7afa97d1ee9
README.md https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-ac8a3c82f570e446ef953e31ab2081661373162007b42489e2f78f127d4ddf84
docker-compose.yml https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-6056deabaf07be3bb165525da64a2ea40a6c0cb388468fe35998ab02d4c43c96
__init__.py https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-71889c347e17766275f10a3752868370bb5162930fc54e909374e8a751c0fb98
__main__.py https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-ba7e50d4bc2353b7cdc978352d8d625e178ce54f6907d6f42b7144686791d9f0
redis_event_store.py https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-d1472ec9762e296e0505eb23d23d20b9c9bc3452029a7e95de334df9509bf3aa
server.py https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-05513e4beea9ef7465a03b1dde553480e771be273d5713fd513f4310ea8c13fb
nginx.conf https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-b74e66e178b5de3c809b5740ada9eb251b07454241e10403e5a3549e0b921ab9
pyproject.toml https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-4fe1778bbb2ae777352c8049dbc0b8f9b018e8723c2347cb5817dcd4c3ec70fe
test_roaming.sh https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-ef9847b1f7b2609338114481c60c2881e8b62e213b175ad34691d363e031d011
streamable_http_manager.py https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-1071ee05567116cb77322c376307b9071c6212871068ccf41911fc337ed473af
test_session_roaming.py https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-958a98b78cdaa2aecdc7e08f6ab1ebdf84e2a1f432224723880bd8197029c073
uv.lock https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-84321598744d84dbee2318e634c74c9aae39a1c253f1c4bd17ebf9ef2f807b11
examples/servers/simple-streamablehttp-roaming/.gitignorehttps://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-4096843b8d52c05d1f240d59956e418654927c970b1cfded8f6949ef8b8f3a8e
View file https://github.com/davidroberts-merlyn/python-sdk/blob/64d7f4e40a234ac2db01a80e8845eeb808826310/examples/servers/simple-streamablehttp-roaming/.gitignore
Open in desktop https://desktop.github.com
https://github.co/hiddenchars
https://github.com/modelcontextprotocol/python-sdk/pull/1519/{{ revealButtonHref }}
examples/servers/simple-streamablehttp-roaming/Dockerfilehttps://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-12f10218a6951c48ea611d69ecbd7efe8602c613b6fad9de593245a91689beee
View file https://github.com/davidroberts-merlyn/python-sdk/blob/64d7f4e40a234ac2db01a80e8845eeb808826310/examples/servers/simple-streamablehttp-roaming/Dockerfile
Open in desktop https://desktop.github.com
https://github.co/hiddenchars
https://github.com/modelcontextprotocol/python-sdk/pull/1519/{{ revealButtonHref }}
examples/servers/simple-streamablehttp-roaming/FILES.mdhttps://github.com/modelcontextprotocol/python-sdk/pull/1519/files#diff-a052ade6ad5d3dc3b280ba25f2d835d2a643a9d4d8973220b41b7373d36b80ac
View file https://github.com/davidroberts-merlyn/python-sdk/blob/64d7f4e40a234ac2db01a80e8845eeb808826310/examples/servers/simple-streamablehttp-roaming/FILES.md
Open in desktop https://desktop.github.com
jacksteamdevhttps://github.com/jacksteamdev
Oct 28, 2025https://github.com/modelcontextprotocol/python-sdk/pull/1519/files#r2470016917
Learn morehttps://docs.github.com/articles/managing-disruptive-comments/#hiding-a-comment
Please reload this pagehttps://github.com/modelcontextprotocol/python-sdk/pull/1519/files
https://github.co/hiddenchars
https://github.com/modelcontextprotocol/python-sdk/pull/1519/{{ revealButtonHref }}
Please reload this pagehttps://github.com/modelcontextprotocol/python-sdk/pull/1519/files
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.