Conversation
…routing to wrong clients A single global Server instance caused `_transport` to be overwritten on each new connection, routing responses to the most recently connected client instead of the requesting one. This resulted in clients hanging indefinitely in multi-session deployments. Introduces SessionManager that creates isolated Server instances per session with automatic idle timeout cleanup (30min default), disconnect-based cleanup for SSE, and broadcast notifications across all active sessions. Also fixes ERR_HTTP_HEADERS_SENT by checking res.headersSent before error responses. Closes #138
📊 Test Coverage ReportOverall Coverage: 91.57% Coverage Details
Coverage Report: View detailed coverage report
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
This pull request fixes a critical bug where a single global MCP Server instance caused response routing failures when multiple clients connected concurrently. The fix replaces the global server with per-session Server instances managed by a new SessionManager class.
Changes:
- Introduces SessionManager class to manage per-session Server instances, preventing transport routing conflicts
- Adds automatic session cleanup with configurable idle timeout (30 minutes default) and disconnect-based cleanup for SSE
- Improves error handling by checking
res.headersSentbefore sending error responses to prevent ERR_HTTP_HEADERS_SENT errors - Updates sendToolsListChangedNotification to broadcast to all active sessions via SessionManager
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/session-manager.ts | New SessionManager class that creates isolated Server instances per session, manages session lifecycle, cleanup, and broadcasts notifications |
| src/server.ts | Removes global Server instance, integrates SessionManager, adds session cleanup on disconnect, improves error handling with headersSent checks, updates graceful shutdown |
| tests/unit/session-manager.test.ts | Comprehensive unit tests (19 tests) for SessionManager covering isolation, cleanup, broadcast, and edge cases |
| tests/unit/server.test.ts | Updated tests (30 tests) to verify SessionManager integration, mocks sessionManager instead of global server |
- Pre-generate session ID and await createSession before handleWithContext to prevent race condition in StreamableHTTP - Remove redundant handlersInitialized if-else in SessionManager - Wrap sendToolsListChangedNotification in try/catch to prevent error propagation to callers - Add tests for onsessioninitialized/closed callbacks, OAuth session association, graceful shutdown, SSE disconnect cleanup, and headersSent guards
…heck - Wrap SSE createSession calls in try/catch with proper error response - Add schemaModeDetected flag to prevent setDetectedSchemaMode race condition - Add duplicate sessionId guard in createSession (warns and closes existing) - Add tests for all new behaviors
Test Coverage ReportOverall Coverage: 93.18%
|
…t-single-server-instance-causes-respons
… transport cases determineTransportMode() only returns "stdio" or "dual", making the standalone "sse" and "streamable-http" switch cases dead code that cannot be tested. The "dual" mode already provides both transports.
Move the duplicate session guard to the beginning of createSession(), before creating a new Server instance and registering handlers. This avoids wasteful resource allocation when a duplicate is detected.
|
🎉 This PR is included in version 6.31.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Summary
Serverinstance with per-sessionServerinstances via newSessionManagerclassServer, preventing_transportoverwrite that caused responses to be routed to wrong clientsERR_HTTP_HEADERS_SENTby checkingres.headersSentbefore sending error responsessendToolsListChangedNotification()now broadcasts to all active sessionsTest plan
SessionManagerunit tests (19 tests): per-session isolation, cleanup, broadcast, shutdownserver.test.ts(30 tests): verifies session manager integrationCloses #138