-
Notifications
You must be signed in to change notification settings - Fork 9
Implement streamable HTTP transport with Redis-based session management #2
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
Merged
Conversation
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
- Add new /mcp endpoint supporting GET, POST, DELETE methods - Implement SessionManager for Redis-based session state management - Add MessageDelivery for message buffering when SSE disconnected - Support horizontal scaling via Redis session storage - Maintain backwards compatibility with existing /sse and /message endpoints - Add comprehensive tests for SessionManager functionality - Session TTL of 5 minutes prevents Redis bloat - Message buffering for resilient connection handling 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Add detailed logging to Redis transport lifecycle - Log session validation and message flow - Track MCP server request handling - Add diagnostics for streamable HTTP transport connections 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Implement new ServerRedisTransport for server->client messages using request-specific channels - Add redisRelayToMcpServer function for dynamic request-id based subscriptions - Update shttp handler to use new transport return types with cleanup functions - Remove old relayTransports and helper functions in favor of new architecture - Add comprehensive test suites for Redis transport and shttp handler components - Fix linting issues and ensure all tests pass The new system routes server responses to channels like: - mcp:shttp:toclient:${sessionId}:${requestId} for request responses - mcp:shttp:toclient:${sessionId}:__GET_stream for notifications This enables efficient scaling across multiple shttp instances as each client only subscribes to channels relevant to their specific requests. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Fix timing issue by awaiting startServerListeningToRedis - Add request-id based response channel routing - Implement control message system for proper server shutdown - Add comprehensive integration tests - Ensure response subscription is established before message sending - Fix MCP server cleanup integration to prevent async hanging 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Removed verbose logging from Redis transport operations - Cleaned up HTTP handler debug statements - Simplified Redis client logging - Removed request/response logging middleware - Maintains error logging for troubleshooting 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Add explicit DELETE request handling in handleStreamableHTTP - Validate session existence before termination - Send shutdown control messages to clean up Redis resources - Return proper HTTP status codes (200 for success, 404 for not found) - Fix TypeScript issue with JSONRPCMessage id access in redisTransport - Add missing semicolon in startServerListeningToRedis call Follows MCP specification for explicit session termination. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Test Redis subscription cleanup after response completion - Test DELETE request session termination with control messages - Test error handling for invalid session IDs - Test user session isolation scenarios (documents expected behavior) - Use MockRedisClient directly instead of mocking the mock - Verify actual Redis channel creation and cleanup behavior - Add proper Express Response mock with all required methods Tests cover the three key areas: 1. Redis subscription cleanup after shttp responses 2. MCP server shutdown on DELETE requests 3. Session isolation between authenticated users 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Add userId: string field to distinguish individual users from OAuth clients - Prepares for user session isolation implementation - clientId represents OAuth application, userId represents individual user This separation allows multiple users to use the same OAuth client while maintaining session isolation for security. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
**MCP Server Auth Page (src/auth/provider.ts):** - Black background with white card design - Official MCP logo and branding - Clear client information display - Elegant "Continue to Authentication" flow - Professional MCP server identification **Upstream Fake Auth Page (src/handlers/fakeauth.ts):** - Updated branding to distinguish from MCP server - Lock icon and "Upstream Authentication" title - Clear user ID management with localStorage - Generate/edit user ID functionality for testing - Multi-user testing instructions **Technical Updates:** - Permissive CSP headers for both auth pages - Logo serving route at /mcp-logo.png - Responsive design with modern CSS - Clear visual distinction between MCP server auth vs upstream auth - Enhanced userId integration in OAuth authorization flow Both pages now provide professional user experience while maintaining clear distinction between the MCP server's auth and the fake upstream provider auth for testing purposes. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Add userId extraction from auth info - Implement session ownership validation - Remove DELETE endpoint handling (moved elsewhere) - Simplify transport lifecycle management - Add proper 401 responses for unauthorized access 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Implement setSessionOwner, getSessionOwner, and validation functions - Add isSessionOwnedBy check combining liveness and ownership - Simplify getShttpTransport by removing cleanup return - Move cleanup logic to transport onclose handler 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Add cleanup function to mcp server creation - Export new Redis transport functions from index 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Update tests to handle async session initialization - Fix session ID retrieval from SSE/JSON responses - Update DELETE request tests to match new behavior - Remove references to deprecated startServerListeningToRedis - Add proper waits for async operations 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Add Accept header to mock requests per MCP spec requirement - Fix TypeScript build errors in integration tests - Add shutdownSession calls to properly clean up MCP server intervals - Update afterEach to ensure proper test cleanup - Add debug configurations for Jest in VS Code launch.json All tests now pass without hanging processes. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Add 5-minute inactivity timeout to prevent memory leaks - Reset timer on each message received from client - Clear timer on transport close - Add comprehensive tests for timeout behavior - Fix async/await in redisRelayToMcpServer calls Sessions will now automatically shutdown after 5 minutes of no client activity. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Add afterEach hook to properly close transport instances - Prevents hanging tests due to uncleaned inactivity timers - All 85 tests now pass successfully 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Add structured logger utility with Cloud Run trace context support - Uses AsyncLocalStorage for request context propagation - Replace all console.log/error/warn statements with structured logger - Add logging middleware to capture request/response metadata - Support Google Cloud Logging severity levels and trace correlation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Remove sensitive data (auth tokens, cookies) from request logging - Add detailed logging throughout streamable HTTP request lifecycle: - Session creation/reuse - Session ownership validation - Transport initialization - Message flow through Redis channels - Session shutdown and cleanup - Add logging for inactivity timeouts and control messages - Use appropriate log levels (info for lifecycle, debug for details, error for failures) All logs now include contextual information like sessionId, userId, and channel names for easier debugging and monitoring. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1b59b8d
to
c78aa11
Compare
- Add overrides for @types/express to resolve version conflicts - Remove SDK build step from CI as we're now using published version - Move mcp.png to src/static folder and update serving path - Add copy-static script to build process 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Remove unused imports and variables - Replace 'any' types with proper type guards - Add comments to empty catch blocks - Fix Response/Request type imports in logger - Use 'unknown' instead of 'any' for generic types - Remove unused createMcpServer and setSessionOwner imports All ESLint checks now pass. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Add MCPInitResponse interface for test responses - Replace 'any' types with proper interface - Remove unused JSONRPCResponse import - Maintain type safety while fixing build errors 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
This is me trying to add shttp to the example server deployment, it's multi instance so I need to relay mcp messages across redis
Context
SSE
This is how the SSE works in this repo
SHTTP
This server, for demonstration purposes, implements a stateful mcp server with shttp using redis and background tasks. it works like this:
When you initialize a session the container that handles the request starts up the MCP server (and runs it async), and wires it up to a transport that runs over redis.
Then for every request to that session we instantiate a streamable http transport object (to handle the http behaviour) and relay all MCP messages via the redis transport to the MCP server
it requires mcp messages being passed back and forward through redis, hence the redis MCP transport. I implemented many proxies like this for the claudeai MCP functionality, so I believe this shouldn't be an out-of-distribution task for the transports to implement.
I ran into a few issues:
extra
andoptions
on the send and onmessage functions making them asymmetrical (meaning chaining transports is non-trivial)