# Bug Fix: onSessionCreated Event Not Firing (v2.19.0) ## Summary Fixed critical bug where `onSessionCreated` lifecycle event was never emitted for sessions created during the standard MCP initialize flow, completely breaking session persistence functionality. ## Impact - **Severity**: Critical - **Affected Version**: v2.19.0 - **Component**: Session Persistence (Phase 3) - **Status**: ✅ Fixed ## Root Cause The `handleRequest()` method in `http-server-single-session.ts` had two different paths for session creation: 1. **Standard initialize flow** (lines 868-943): Created session inline but **did not emit** `onSessionCreated` event 2. **Manual restoration flow** (line 1048): Called `createSession()` which **correctly emitted** the event This inconsistency meant that: - New sessions during normal operation were **never saved to database** - Only manually restored sessions triggered the save event - Session persistence was completely broken for new sessions - Container restarts caused all sessions to be lost ## The Fix ### Location - **File**: `src/http-server-single-session.ts` - **Method**: `handleRequest()` - **Line**: After line 943 (`await server.connect(transport);`) ### Code Change Added event emission after successfully connecting server to transport during initialize flow: ```typescript // Connect the server to the transport BEFORE handling the request logger.info('handleRequest: Connecting server to new transport'); await server.connect(transport); // Phase 3: Emit onSessionCreated event (REQ-4) // Fire-and-forget: don't await or block session creation this.emitEvent('onSessionCreated', sessionIdToUse, instanceContext).catch(eventErr => { logger.error('Failed to emit onSessionCreated event (non-blocking)', { sessionId: sessionIdToUse, error: eventErr instanceof Error ? eventErr.message : String(eventErr) }); }); ``` ### Why This Works 1. **Consistent with existing pattern**: Matches the `createSession()` method pattern (line 664) 2. **Non-blocking**: Uses `.catch()` to ensure event handler errors don't break session creation 3. **Correct timing**: Fires after `server.connect(transport)` succeeds, ensuring session is fully initialized 4. **Same parameters**: Passes `sessionId` and `instanceContext` just like the restoration flow ## Verification ### Test Results Created comprehensive test suite to verify the fix: **Test File**: `tests/unit/session/onSessionCreated-event.test.ts` **Test Results**: ``` ✓ onSessionCreated Event - Initialize Flow ✓ should emit onSessionCreated event when session is created during initialize flow (1594ms) Test Files 5 passed (5) Tests 78 passed (78) ``` **Manual Testing**: ```typescript const server = new SingleSessionHTTPServer({ sessionEvents: { onSessionCreated: async (sessionId, context) => { console.log('✅ Event fired:', sessionId); await saveSessionToDatabase(sessionId, context); } } }); // Result: Event fires successfully on initialize! // ✅ Event fired: 40dcc123-46bd-4994-945e-f2dbe60e54c2 ``` ### Behavior After Fix 1. **Initialize request** → Session created → `onSessionCreated` event fired → Session saved to database ✅ 2. **Session restoration** → `createSession()` called → `onSessionCreated` event fired → Session saved to database ✅ 3. **Manual restoration** → `manuallyRestoreSession()` → Session created → Event fired ✅ All three paths now correctly emit the event! ## Backward Compatibility ✅ **Fully backward compatible**: - No breaking changes to API - Event handler is optional (defaults to no-op) - Non-blocking implementation ensures session creation succeeds even if handler fails - Matches existing behavior of `createSession()` method - All existing tests pass ## Related Code ### Event Emission Points 1. ✅ **Standard initialize flow**: `handleRequest()` at line ~947 (NEW - fixed) 2. ✅ **Manual restoration**: `createSession()` at line 664 (EXISTING - working) 3. ✅ **Session restoration**: calls `createSession()` indirectly (EXISTING - working) ### Other Lifecycle Events The following events are working correctly: - `onSessionRestored`: Fires when session is restored from database - `onSessionAccessed`: Fires on every request (with throttling recommended) - `onSessionExpired`: Fires before expired session cleanup - `onSessionDeleted`: Fires on manual session deletion ## Testing Recommendations After applying this fix, verify session persistence works: ```typescript // 1. Start server with session events const engine = new N8NMCPEngine({ sessionEvents: { onSessionCreated: async (sessionId, context) => { await database.upsertSession({ sessionId, ...context }); } } }); // 2. Client connects and initializes // 3. Verify session saved to database const sessions = await database.query('SELECT * FROM mcp_sessions'); expect(sessions.length).toBeGreaterThan(0); // 4. Restart server await engine.shutdown(); await engine.start(); // 5. Client reconnects with old session ID // 6. Verify session restored from database ``` ## Impact on n8n-mcp-backend This fix **unblocks** the multi-tenant n8n-mcp-backend service that depends on session persistence: - ✅ Sessions now persist across container restarts - ✅ Users no longer need to restart Claude Desktop after backend updates - ✅ Session continuity maintained for all users - ✅ Production deployment viable ## Lessons Learned 1. **Consistency is critical**: Session creation should follow the same pattern everywhere 2. **Event-driven architecture**: Events must fire at all creation points, not just some 3. **Testing lifecycle events**: Need integration tests that verify events fire, not just that code runs 4. **Documentation**: Clearly document when events should fire and where ## Files Changed - `src/http-server-single-session.ts`: Added event emission (lines 945-952) - `tests/unit/session/onSessionCreated-event.test.ts`: New test file - `tests/integration/session/test-onSessionCreated-event.ts`: Manual verification test ## Build Status - ✅ TypeScript compilation: Success - ✅ Type checking: Success - ✅ All unit tests: 78 passed - ✅ Integration tests: Pass - ✅ Backward compatibility: Verified