-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Enable session roaming across multiple server instances #1519
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
davidroberts-merlyn
wants to merge
9
commits into
modelcontextprotocol:main
Choose a base branch
from
davidroberts-merlyn:session-roaming
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+2,595
−42
Draft
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
9faef55
Enable session roaming across multiple server instances
davidroberts-merlyn 5e75bec
Fix prettier formatting in docker-compose.yml
davidroberts-merlyn 07cb1dd
Fix markdownlint issues in example documentation
davidroberts-merlyn 550fe88
Fix race condition in session roaming
davidroberts-merlyn 7df72c1
Fix test mock setup for async context managers
davidroberts-merlyn ea7813f
Add missing contextlib import for async context manager decorator
davidroberts-merlyn 33f14f0
Fix pyright type errors for asynccontextmanager decorators
davidroberts-merlyn ce114b2
Fix test failures by making mock app.run block until cancelled
davidroberts-merlyn 64d7f4e
Merge branch 'main' into session-roaming
davidroberts-merlyn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| # Python | ||
| __pycache__/ | ||
| *.py[cod] | ||
| *$py.class | ||
| *.so | ||
| .Python | ||
| build/ | ||
| develop-eggs/ | ||
| dist/ | ||
| downloads/ | ||
| eggs/ | ||
| .eggs/ | ||
| lib/ | ||
| lib64/ | ||
| parts/ | ||
| sdist/ | ||
| var/ | ||
| wheels/ | ||
| *.egg-info/ | ||
| .installed.cfg | ||
| *.egg | ||
|
|
||
| # Virtual environments | ||
| .venv/ | ||
| venv/ | ||
| ENV/ | ||
| env/ | ||
|
|
||
| # IDEs | ||
| .vscode/ | ||
| .idea/ | ||
| *.swp | ||
| *.swo | ||
| *~ | ||
|
|
||
| # Testing | ||
| .pytest_cache/ | ||
| .coverage | ||
| htmlcov/ | ||
|
|
||
| # Ruff | ||
| .ruff_cache/ | ||
|
|
||
| # OS | ||
| .DS_Store | ||
| Thumbs.db |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| FROM python:3.12-slim | ||
|
|
||
| # Install uv | ||
| COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ | ||
|
|
||
| # Set working directory | ||
| WORKDIR /app | ||
|
|
||
| # Copy project files | ||
| COPY pyproject.toml ./ | ||
| COPY mcp_simple_streamablehttp_roaming ./mcp_simple_streamablehttp_roaming | ||
|
|
||
| # Install dependencies | ||
| RUN uv sync --frozen | ||
|
|
||
| # Expose port | ||
| EXPOSE 3001 | ||
|
|
||
| # Default command (can be overridden in docker-compose) | ||
| CMD ["uv", "run", "mcp-streamablehttp-roaming", "--port", "3001"] |
235 changes: 235 additions & 0 deletions
235
examples/servers/simple-streamablehttp-roaming/FILES.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,235 @@ | ||
| # File Structure | ||
|
|
||
| This example demonstrates session roaming across multiple MCP server instances. | ||
|
|
||
| ## Directory Structure | ||
|
|
||
| ```text | ||
| simple-streamablehttp-roaming/ | ||
| ├── README.md # Comprehensive documentation | ||
| ├── QUICKSTART.md # 5-minute getting started guide | ||
| ├── FILES.md # This file | ||
| ├── pyproject.toml # Project configuration | ||
| ├── Dockerfile # Docker container definition | ||
| ├── docker-compose.yml # Multi-instance deployment | ||
| ├── nginx.conf # Load balancer configuration | ||
| ├── test_roaming.sh # Automated test script | ||
| ├── .gitignore # Git ignore patterns | ||
| └── mcp_simple_streamablehttp_roaming/ | ||
| ├── __init__.py # Package initialization | ||
| ├── __main__.py # Module entry point | ||
| ├── server.py # Main server implementation | ||
| └── redis_event_store.py # Redis EventStore implementation | ||
|
|
||
| ``` | ||
|
|
||
| ## File Purposes | ||
|
|
||
| ### Documentation | ||
|
|
||
| - **README.md** (486 lines) | ||
| - Comprehensive guide to session roaming | ||
| - Architecture diagrams and explanations | ||
| - Production deployment examples (Kubernetes, Docker Compose) | ||
| - Testing instructions | ||
| - Implementation details | ||
|
|
||
| - **QUICKSTART.md** (381 lines) | ||
| - Get started in 5 minutes | ||
| - Step-by-step local development setup | ||
| - Docker Compose deployment guide | ||
| - Manual testing examples | ||
| - Common issues and solutions | ||
|
|
||
| - **FILES.md** (This file) | ||
| - Overview of file structure | ||
| - Purpose of each file | ||
|
|
||
| ### Python Package | ||
|
|
||
| - **mcp_simple_streamablehttp_roaming/**init**.py** (3 lines) | ||
| - Package version information | ||
|
|
||
| - **mcp_simple_streamablehttp_roaming/**main**.py** (5 lines) | ||
| - Entry point for running as module | ||
|
|
||
| - **mcp_simple_streamablehttp_roaming/server.py** (169 lines) | ||
| - Main MCP server implementation | ||
| - Command-line interface | ||
| - Tool: `get-instance-info` (shows which instance handles request) | ||
| - Session manager configuration with EventStore | ||
| - Starlette ASGI application | ||
|
|
||
| - **mcp_simple_streamablehttp_roaming/redis_event_store.py** (154 lines) | ||
| - Production-ready Redis EventStore implementation | ||
| - Persistent event storage | ||
| - Event replay functionality | ||
| - Shared across all instances | ||
|
|
||
| ### Configuration | ||
|
|
||
| - **pyproject.toml** (44 lines) | ||
| - Project metadata | ||
| - Dependencies (mcp, redis, starlette, uvicorn, etc.) | ||
| - CLI script registration | ||
| - Build configuration | ||
| - Development tools (pyright, pytest, ruff) | ||
|
|
||
| - **.gitignore** (35 lines) | ||
| - Python artifacts | ||
| - Virtual environments | ||
| - IDE files | ||
| - Cache directories | ||
|
|
||
| ### Deployment | ||
|
|
||
| - **Dockerfile** (20 lines) | ||
| - Multi-stage Python container | ||
| - Uses uv for dependency management | ||
| - Optimized for production | ||
|
|
||
| - **docker-compose.yml** (85 lines) | ||
| - Redis service (persistent event store) | ||
| - 3 MCP server instances (ports 3001, 3002, 3003) | ||
| - NGINX load balancer (port 80) | ||
| - Health checks and dependencies | ||
| - Volume management | ||
|
|
||
| - **nginx.conf** (60 lines) | ||
| - Round-robin load balancing (NO sticky sessions!) | ||
| - SSE support configuration | ||
| - CORS headers | ||
| - MCP-Session-ID header pass-through | ||
| - Health check endpoint | ||
|
|
||
| ### Testing | ||
|
|
||
| - **test_roaming.sh** (100 lines) | ||
| - Automated test script | ||
| - Creates session on Instance 1 | ||
| - Calls tool on Instance 1 | ||
| - Uses same session on Instance 2 | ||
| - Verifies session roaming works | ||
| - Detailed success/failure reporting | ||
|
|
||
| ## Key Features Demonstrated | ||
|
|
||
| ### 1. Session Roaming | ||
|
|
||
| - Sessions move freely between instances | ||
| - No sticky sessions required | ||
| - EventStore provides continuity | ||
|
|
||
| ### 2. Production Deployment | ||
|
|
||
| - Docker Compose for local testing | ||
| - Kubernetes manifests in README | ||
| - NGINX load balancing example | ||
| - Redis persistence configuration | ||
|
|
||
| ### 3. Developer Experience | ||
|
|
||
| - Automated testing script | ||
| - Comprehensive documentation | ||
| - Quick start guide | ||
| - Clear error messages | ||
| - Detailed logging | ||
|
|
||
| ### 4. Code Quality | ||
|
|
||
| - Type hints throughout | ||
| - Comprehensive docstrings | ||
| - Configuration via CLI arguments | ||
| - Environment-based configuration | ||
| - Proper error handling | ||
|
|
||
| ## Usage Examples | ||
|
|
||
| ### Local Development | ||
|
|
||
| ```bash | ||
| # Terminal 1 | ||
| uv run mcp-streamablehttp-roaming --port 3001 --instance-id instance-1 | ||
|
|
||
| # Terminal 2 | ||
| uv run mcp-streamablehttp-roaming --port 3002 --instance-id instance-2 | ||
|
|
||
| # Terminal 3 | ||
| ./test_roaming.sh | ||
| ``` | ||
|
|
||
| ### Docker Compose | ||
|
|
||
| ```bash | ||
| docker-compose up -d | ||
| # Access via http://localhost/mcp (load balanced) | ||
| # or directly via http://localhost:3001/mcp, :3002/mcp, :3003/mcp | ||
| ``` | ||
|
|
||
| ### Manual Testing | ||
|
|
||
| ```bash | ||
| # Create session on Instance 1 | ||
| curl -X POST http://localhost:3001/mcp -H "Content-Type: application/json" ... | ||
|
|
||
| # Use session on Instance 2 | ||
| curl -X POST http://localhost:3002/mcp -H "MCP-Session-ID: <session-id>" ... | ||
| ``` | ||
|
|
||
| ## Total Lines of Code | ||
|
|
||
| - Python code: ~331 lines | ||
| - Configuration: ~149 lines | ||
| - Documentation: ~867 lines | ||
| - Testing: ~100 lines | ||
| - **Total: ~1,447 lines** | ||
|
|
||
| ## Implementation Highlights | ||
|
|
||
| ### Minimal Code for Maximum Impact | ||
|
|
||
| **Enable session roaming with just:** | ||
|
|
||
| ```python | ||
| event_store = RedisEventStore(redis_url="redis://localhost:6379") | ||
| manager = StreamableHTTPSessionManager(app=app, event_store=event_store) | ||
| ``` | ||
|
|
||
| ### No Special Session Store Needed | ||
|
|
||
| The EventStore alone enables: | ||
|
|
||
| - ✅ Event replay (resumability) | ||
| - ✅ Session roaming (distributed sessions) | ||
| - ✅ Horizontal scaling | ||
| - ✅ High availability | ||
|
|
||
| ### Production-Ready Patterns | ||
|
|
||
| - Redis persistence (AOF enabled) | ||
| - Health checks | ||
| - Graceful shutdown | ||
| - Comprehensive logging | ||
| - Environment-based configuration | ||
| - CORS support | ||
|
|
||
| ## Related Files in SDK | ||
|
|
||
| The example uses these SDK components: | ||
|
|
||
| - `mcp.server.streamable_http_manager.StreamableHTTPSessionManager` - Session management | ||
| - `mcp.server.streamable_http.EventStore` - Interface for event storage | ||
| - `mcp.server.lowlevel.Server` - Core MCP server | ||
| - `mcp.types` - MCP protocol types | ||
|
|
||
| ## Contributing | ||
|
|
||
| To extend this example: | ||
|
|
||
| 1. **Add new tools** - Modify `server.py` to add more tool handlers | ||
| 2. **Custom EventStore** - Implement EventStore for other databases | ||
| 3. **Monitoring** - Add Prometheus metrics or OpenTelemetry | ||
| 4. **Authentication** - Add auth middleware to Starlette app | ||
| 5. **Rate limiting** - Add rate limiting middleware | ||
|
|
||
| See README.md for more details on each approach. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this file? It's nice information, but none of the other examples have it.