mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-01-30 06:22:04 +00:00
Revert to v2.18.10 - Remove session persistence (v2.19.0-v2.19.5) (#322)
After 5 consecutive hotfix attempts, session persistence has proven architecturally incompatible with the MCP SDK. Rolling back to last known stable version. ## Removed - 16 new files (session types, docs, tests, planning docs) - 1,100+ lines of session persistence code - Session restoration hooks and lifecycle events - Retry policy and warm-start implementations ## Restored - Stable v2.18.10 codebase - Library export fields (from PR #310) - All core MCP functionality ## Breaking Changes - Session persistence APIs removed - onSessionNotFound hook removed - Session lifecycle events removed This reverts commitsfe13091through1d34ad8. Restores commit4566253(v2.18.10, PR #310). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
fe1309151a
commit
8d20c64f5c
559
CHANGELOG.md
559
CHANGELOG.md
@@ -5,432 +5,6 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [2.19.5] - 2025-10-13
|
||||
|
||||
### 🐛 Critical Bug Fixes
|
||||
|
||||
**Session Restoration Handshake (P0 - CRITICAL)**
|
||||
|
||||
Fixes critical bug in session restoration where synthetic MCP initialization had no HTTP connection to respond through, causing timeouts. Implements warm start pattern that handles the current request immediately.
|
||||
|
||||
#### Fixed
|
||||
|
||||
- **Synthetic MCP Initialization Failed Due to Missing HTTP Context**
|
||||
- **Issue**: v2.19.4's `initializeMCPServerForSession()` attempted to synthetically initialize restored MCP servers, but had no active HTTP req/res pair to send responses through, causing all restoration attempts to timeout
|
||||
- **Impact**: Session restoration completely broken - zero-downtime deployments non-functional
|
||||
- **Severity**: CRITICAL - v2.19.4 introduced a regression that broke session restoration
|
||||
- **Root Cause**:
|
||||
- `StreamableHTTPServerTransport` requires a live HTTP req/res pair to send responses
|
||||
- Synthetic initialization called `server.request()` but had no transport attached to current request
|
||||
- Transport's `_initialized` flag stayed false because no actual GET/POST went through it
|
||||
- Retrying with backoff didn't help - the transport had nothing to talk to
|
||||
- **Fix Applied**:
|
||||
- **Deleted broken synthetic initialization method** (`initializeMCPServerForSession()`)
|
||||
- **Implemented warm start pattern**:
|
||||
1. Restore session by calling existing `createSession()` with restored context
|
||||
2. Immediately handle current request through new transport: `transport.handleRequest(req, res, req.body)`
|
||||
3. Client receives standard MCP error `-32000` (Server not initialized)
|
||||
4. Client auto-retries with initialize on same connection (standard MCP behavior)
|
||||
5. Session fully restored and client continues normally
|
||||
- **Added idempotency guards** to prevent concurrent restoration from creating duplicate sessions
|
||||
- **Added cleanup on failure** to remove sessions when restoration fails
|
||||
- **Added early return** after handling request to prevent double processing
|
||||
- **Location**: `src/http-server-single-session.ts:1118-1247` (simplified restoration flow)
|
||||
- **Tests Added**: `tests/integration/session-restoration-warmstart.test.ts` (11 comprehensive tests)
|
||||
- **Documentation**: `docs/MULTI_APP_INTEGRATION.md` (warm start behavior explained)
|
||||
|
||||
#### Technical Details
|
||||
|
||||
**Warm Start Pattern Flow:**
|
||||
1. Client sends request with unknown session ID (after restart)
|
||||
2. Server detects unknown session, calls `onSessionNotFound` hook
|
||||
3. Hook loads session context from database
|
||||
4. Server creates session using existing `createSession()` flow
|
||||
5. Server immediately handles current request through new transport
|
||||
6. Client receives `-32000` error, auto-retries with initialize
|
||||
7. Session fully restored, client continues normally
|
||||
|
||||
**Benefits:**
|
||||
- **Zero client changes**: Standard MCP clients auto-retry on -32000
|
||||
- **Single HTTP round-trip**: No extra network requests needed
|
||||
- **Concurrent-safe**: Idempotency guards prevent race conditions
|
||||
- **Automatic cleanup**: Failed restorations clean up resources
|
||||
- **Standard MCP**: Uses official error code, not custom solutions
|
||||
|
||||
**Code Changes:**
|
||||
```typescript
|
||||
// Before (v2.19.4 - BROKEN):
|
||||
await server.connect(transport);
|
||||
await this.initializeMCPServerForSession(sessionId, server, context); // NO req/res to respond!
|
||||
|
||||
// After (v2.19.5 - WORKING):
|
||||
this.createSession(restoredContext, sessionId, false);
|
||||
transport = this.transports[sessionId];
|
||||
await transport.handleRequest(req, res, req.body); // Handle current request immediately
|
||||
return; // Early return prevents double processing
|
||||
```
|
||||
|
||||
#### Migration Notes
|
||||
|
||||
This is a **patch release** with no breaking changes:
|
||||
- No API changes to public interfaces
|
||||
- Existing session restoration hooks work unchanged
|
||||
- Internal implementation simplified (80 fewer lines of code)
|
||||
- Session restoration now works correctly with standard MCP protocol
|
||||
|
||||
#### Files Changed
|
||||
|
||||
- `src/http-server-single-session.ts`: Deleted synthetic init, implemented warm start (lines 1118-1247)
|
||||
- `tests/integration/session-restoration-warmstart.test.ts`: New integration tests (11 tests)
|
||||
- `docs/MULTI_APP_INTEGRATION.md`: Documentation for warm start pattern
|
||||
- `package.json`, `package.runtime.json`: Version bump to 2.19.5
|
||||
|
||||
## [2.19.4] - 2025-10-13
|
||||
|
||||
### 🐛 Critical Bug Fixes
|
||||
|
||||
**MCP Server Initialization for Restored Sessions (P0 - CRITICAL)**
|
||||
|
||||
Completes the session restoration feature by initializing MCP server instances for restored sessions, enabling tool calls to work after server restart.
|
||||
|
||||
#### Fixed
|
||||
|
||||
- **MCP Server Not Initialized During Session Restoration**
|
||||
- **Issue**: Session restoration successfully restored InstanceContext (v2.19.0) and transport layer (v2.19.3), but failed to initialize the MCP Server instance, causing all tool calls on restored sessions to fail with "Server not initialized" error
|
||||
- **Impact**: Zero-downtime deployments still broken - users cannot use tools after container restart without manually restarting their MCP client
|
||||
- **Severity**: CRITICAL - session persistence incomplete without MCP server initialization
|
||||
- **Root Cause**:
|
||||
- MCP protocol requires an `initialize` handshake before accepting tool calls
|
||||
- When restoring a session, we create a NEW MCP Server instance (uninitialized state)
|
||||
- Client thinks it already initialized (it did, with the old instance before restart)
|
||||
- Client sends tool call, new server rejects it: "Server not initialized"
|
||||
- The three layers of a session: (1) Data (InstanceContext) ✅, (2) Transport (HTTP) ✅ v2.19.3, (3) Protocol (MCP Server) ❌ not initialized
|
||||
- **Fix Applied**:
|
||||
- Created `initializeMCPServerForSession()` method that sends synthetic initialize request to new MCP server instance
|
||||
- Brings server into initialized state without requiring client to re-initialize
|
||||
- Called after `server.connect(transport)` during session restoration flow
|
||||
- Includes 5-second timeout and comprehensive error handling
|
||||
- **Location**: `src/http-server-single-session.ts:521-605` (new method), `src/http-server-single-session.ts:1321-1327` (application)
|
||||
- **Tests**: Compilation verified, ready for integration testing
|
||||
- **Verification**: Build successful, no TypeScript errors
|
||||
|
||||
#### Technical Details
|
||||
|
||||
**The Three Layers of Session State:**
|
||||
1. **Data Layer** (InstanceContext): Session configuration and state ✅ v2.19.0
|
||||
2. **Transport Layer** (HTTP Connection): Request/response binding ✅ v2.19.3
|
||||
3. **Protocol Layer** (MCP Server Instance): Initialize handshake ✅ v2.19.4
|
||||
|
||||
**Implementation:**
|
||||
```typescript
|
||||
// After connecting transport, initialize the MCP server
|
||||
await server.connect(transport);
|
||||
await this.initializeMCPServerForSession(sessionId, server, restoredContext);
|
||||
```
|
||||
|
||||
The synthetic initialize request:
|
||||
- Uses standard MCP protocol version
|
||||
- Includes client info: `n8n-mcp-restored-session`
|
||||
- Calls server's initialize handler directly
|
||||
- Waits for initialization to complete (5 second timeout)
|
||||
- Brings server into initialized state
|
||||
|
||||
#### Dependencies
|
||||
|
||||
- Requires: v2.19.3 (transport layer fix)
|
||||
- Completes: Session persistence feature (v2.19.0-v2.19.4)
|
||||
- Enables: True zero-downtime deployments for HTTP-based deployments
|
||||
|
||||
## [2.19.3] - 2025-10-13
|
||||
|
||||
### 🐛 Critical Bug Fixes
|
||||
|
||||
**Session Restoration Transport Layer (P0 - CRITICAL)**
|
||||
|
||||
Fixes critical bug where session restoration successfully restored InstanceContext but failed to reconnect the transport layer, causing all requests on restored sessions to hang indefinitely.
|
||||
|
||||
#### Fixed
|
||||
|
||||
- **Transport Layer Not Reconnected During Session Restoration**
|
||||
- **Issue**: Session restoration successfully restored InstanceContext (session state) but failed to connect transport layer (HTTP req/res binding), causing requests to hang indefinitely
|
||||
- **Impact**: Zero-downtime deployments broken - users cannot continue work after container restart without restarting their MCP client (Claude Desktop, Cursor, Windsurf)
|
||||
- **Severity**: CRITICAL - session persistence completely non-functional for production use
|
||||
- **Root Cause**:
|
||||
- The `handleRequest()` method's session restoration flow (lines 1119-1197) called `createSession()` which creates a NEW transport separate from the current HTTP request
|
||||
- This separate transport is not linked to the current req/res pair, so responses cannot be sent back through the active HTTP connection
|
||||
- The initialize flow (lines 946-1055) correctly creates transport inline for the current request, but restoration flow did not follow this pattern
|
||||
- **Fix Applied**:
|
||||
- Replace `createSession()` call with inline transport creation that mirrors the initialize flow
|
||||
- Create `StreamableHTTPServerTransport` directly for the current HTTP req/res context
|
||||
- Ensure transport is connected to server BEFORE handling request
|
||||
- This makes restored sessions work identically to fresh sessions
|
||||
- **Location**: `src/http-server-single-session.ts:1163-1244`
|
||||
- **Tests Added**:
|
||||
- Integration tests: `tests/integration/session-persistence.test.ts` (13 tests all passing)
|
||||
- **Verification**: All session persistence integration tests passing
|
||||
|
||||
#### Technical Details
|
||||
|
||||
**Before Fix (Broken):**
|
||||
```typescript
|
||||
// Session restoration (WRONG - creates separate transport)
|
||||
await this.createSession(restoredContext, sessionId, true);
|
||||
transport = this.transports[sessionId]; // Transport NOT linked to current req/res!
|
||||
```
|
||||
|
||||
**After Fix (Working):**
|
||||
```typescript
|
||||
// Session restoration (CORRECT - inline transport for current request)
|
||||
const server = new N8NDocumentationMCPServer(restoredContext);
|
||||
transport = new StreamableHTTPServerTransport({
|
||||
sessionIdGenerator: () => sessionId,
|
||||
onsessioninitialized: (id) => {
|
||||
this.transports[id] = transport; // Store for future requests
|
||||
this.servers[id] = server;
|
||||
// ... metadata storage
|
||||
}
|
||||
});
|
||||
await server.connect(transport); // Connect BEFORE handling request
|
||||
```
|
||||
|
||||
**Why This Matters:**
|
||||
- The `StreamableHTTPServerTransport` class from MCP SDK links a specific HTTP req/res pair to the MCP server
|
||||
- Creating transport in `createSession()` binds it to the wrong req/res (or no req/res at all)
|
||||
- Responses sent through the wrong transport never reach the client
|
||||
- The initialize flow got this right, but restoration flow did not
|
||||
|
||||
**Impact on Zero-Downtime Deployments:**
|
||||
- ✅ **After fix**: Container restart → Client reconnects with old session ID → Session restored → Requests work normally
|
||||
- ❌ **Before fix**: Container restart → Client reconnects with old session ID → Session restored → Requests hang forever
|
||||
|
||||
#### Migration Notes
|
||||
|
||||
This is a **patch release** with no breaking changes:
|
||||
- No API changes
|
||||
- No configuration changes required
|
||||
- Existing code continues to work
|
||||
- Session restoration now actually works as designed
|
||||
|
||||
#### Files Changed
|
||||
|
||||
- `src/http-server-single-session.ts`: Fixed session restoration to create transport inline (lines 1163-1244)
|
||||
- `package.json`, `package.runtime.json`, `src/mcp-engine.ts`: Version bump to 2.19.3
|
||||
- `tests/integration/session-persistence.test.ts`: Existing tests verify restoration works correctly
|
||||
|
||||
## [2.19.2] - 2025-10-13
|
||||
|
||||
### 🐛 Critical Bug Fixes
|
||||
|
||||
**Session Cleanup Stack Overflow (P0 - CRITICAL)**
|
||||
|
||||
Fixes critical stack overflow bug that caused service to become unresponsive after container restart.
|
||||
|
||||
#### Fixed
|
||||
|
||||
- **Stack Overflow During Session Cleanup**
|
||||
- **Issue**: Missing `await` in cleanup loop caused concurrent async operations and recursive cleanup cascade
|
||||
- **Impact**: Stack overflow errors during container restart, all subsequent tool calls hang indefinitely
|
||||
- **Severity**: CRITICAL - makes service unusable after restart for all users with session persistence
|
||||
- **Root Causes**:
|
||||
1. `cleanupExpiredSessions()` line 206 called `removeSession()` without `await`, causing overlapping cleanup attempts
|
||||
2. Transport event handlers (`onclose`, `onerror`) triggered recursive cleanup during shutdown
|
||||
3. No recursion guard to prevent concurrent cleanup of same session
|
||||
- **Fixes Applied**:
|
||||
1. Added `cleanupInProgress: Set<string>` recursion guard to prevent concurrent cleanup
|
||||
2. Added `isShuttingDown` flag to prevent recursive event handlers during shutdown
|
||||
3. Implemented `safeCloseTransport()` helper with timeout protection (3 seconds)
|
||||
4. Updated `removeSession()` to check recursion guard and use safe transport closing
|
||||
5. Fixed `cleanupExpiredSessions()` to properly `await` with error isolation
|
||||
6. Updated all transport event handlers to check shutdown flag before cleanup
|
||||
7. Enhanced `shutdown()` method to set flag and use proper sequential cleanup
|
||||
- **Location**: `src/http-server-single-session.ts`
|
||||
- **Verification**: All 77 session lifecycle tests passing
|
||||
|
||||
#### Technical Details
|
||||
|
||||
**Recursion Chain (Before Fix):**
|
||||
```
|
||||
cleanupExpiredSessions()
|
||||
└─> removeSession(session, 'expired') [NOT AWAITED]
|
||||
└─> transport.close()
|
||||
└─> transport.onclose handler
|
||||
└─> removeSession(session, 'transport_closed')
|
||||
└─> transport.close() [AGAIN!]
|
||||
└─> Stack overflow!
|
||||
```
|
||||
|
||||
**Protection Added:**
|
||||
- **Recursion Guard**: Prevents same session from being cleaned up concurrently
|
||||
- **Shutdown Flag**: Disables event handlers during shutdown to break recursion chain
|
||||
- **Safe Transport Close**: Removes event handlers before closing, uses timeout protection
|
||||
- **Error Isolation**: Each session cleanup failure doesn't affect others
|
||||
- **Sequential Cleanup**: Properly awaits each operation to prevent race conditions
|
||||
|
||||
#### Impact
|
||||
|
||||
- **Reliability**: Service survives container restarts without stack overflow
|
||||
- **Stability**: No more hanging requests after restart
|
||||
- **Resilience**: Individual session cleanup failures don't cascade
|
||||
- **Backward Compatible**: No breaking changes, all existing tests pass
|
||||
|
||||
## [2.19.1] - 2025-10-12
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
**Session Lifecycle Event Emission**
|
||||
|
||||
Fixes issue where `onSessionCreated` event was not being emitted during standard session initialization flow (when sessions are created directly without restoration).
|
||||
|
||||
#### Fixed
|
||||
|
||||
- **onSessionCreated Event Missing in Standard Flow**
|
||||
- **Issue**: `onSessionCreated` event was only emitted during restoration failure fallback, not during normal session creation
|
||||
- **Impact**: Applications relying on `onSessionCreated` for logging, monitoring, or persistence didn't receive events for directly created sessions
|
||||
- **Root Cause**: Event emission was only present in restoration error handler, not in standard `initialize()` flow
|
||||
- **Fix**: Added `onSessionCreated` event emission in `http-server-single-session.ts:436` during standard initialization
|
||||
- **Location**: `src/http-server-single-session.ts` (initialize method)
|
||||
- **Verification**: All session lifecycle tests passing (14 tests)
|
||||
|
||||
#### Impact
|
||||
|
||||
- **Event Consistency**: `onSessionCreated` now fires reliably for all new sessions (whether created directly or after restoration failure)
|
||||
- **Monitoring**: Complete session lifecycle visibility for logging and analytics systems
|
||||
- **Backward Compatible**: No breaking changes, only adds missing event emission
|
||||
|
||||
## [2.19.0] - 2025-10-12
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
**Session Lifecycle Events (Phase 3 - REQ-4)**
|
||||
|
||||
Adds optional callback-based event system for monitoring session lifecycle, enabling integration with logging, monitoring, and analytics systems.
|
||||
|
||||
#### Added
|
||||
|
||||
- **Session Lifecycle Event Handlers**
|
||||
- `onSessionCreated`: Called when new session is created (not restored)
|
||||
- `onSessionRestored`: Called when session is restored from external storage
|
||||
- `onSessionAccessed`: Called on every request using existing session
|
||||
- `onSessionExpired`: Called when session expires due to inactivity
|
||||
- `onSessionDeleted`: Called when session is manually deleted
|
||||
- **Implementation**: `src/types/session-restoration.ts` (SessionLifecycleEvents interface)
|
||||
- **Integration**: `src/http-server-single-session.ts` (event emission at 5 lifecycle points)
|
||||
- **API**: `src/mcp-engine.ts` (sessionEvents option)
|
||||
|
||||
- **Event Characteristics**
|
||||
- **Fire-and-forget**: Non-blocking, errors logged but don't affect operations
|
||||
- **Async Support**: Handlers can be sync or async
|
||||
- **Graceful Degradation**: Handler failures don't break session operations
|
||||
- **Metadata Support**: Events receive session ID and instance context
|
||||
|
||||
#### Use Cases
|
||||
|
||||
- **Logging & Monitoring**: Track session lifecycle for debugging and analytics
|
||||
- **Database Persistence**: Auto-save sessions on creation/restoration
|
||||
- **Metrics**: Track session activity and expiration patterns
|
||||
- **Cleanup**: Cascade delete related data when sessions expire
|
||||
- **Throttling**: Update lastAccess timestamps (with throttling for performance)
|
||||
|
||||
#### Example Usage
|
||||
|
||||
```typescript
|
||||
import { N8NMCPEngine } from 'n8n-mcp';
|
||||
import throttle from 'lodash.throttle';
|
||||
|
||||
const engine = new N8NMCPEngine({
|
||||
sessionEvents: {
|
||||
onSessionCreated: async (sessionId, context) => {
|
||||
await db.saveSession(sessionId, context);
|
||||
analytics.track('session_created', { sessionId });
|
||||
},
|
||||
onSessionRestored: async (sessionId, context) => {
|
||||
analytics.track('session_restored', { sessionId });
|
||||
},
|
||||
// Throttle high-frequency event to prevent DB overload
|
||||
onSessionAccessed: throttle(async (sessionId) => {
|
||||
await db.updateLastAccess(sessionId);
|
||||
}, 60000), // Max once per minute
|
||||
onSessionExpired: async (sessionId) => {
|
||||
await db.deleteSession(sessionId);
|
||||
await cleanup.removeRelatedData(sessionId);
|
||||
},
|
||||
onSessionDeleted: async (sessionId) => {
|
||||
await db.deleteSession(sessionId);
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Session Restoration Retry Policy (Phase 4 - REQ-7)**
|
||||
|
||||
Adds configurable retry logic for transient failures during session restoration, improving reliability for database-backed persistence.
|
||||
|
||||
#### Added
|
||||
|
||||
- **Retry Configuration Options**
|
||||
- `sessionRestorationRetries`: Number of retry attempts (default: 0, opt-in)
|
||||
- `sessionRestorationRetryDelay`: Delay between attempts in milliseconds (default: 100ms)
|
||||
- **Implementation**: `src/http-server-single-session.ts` (restoreSessionWithRetry method)
|
||||
- **API**: `src/mcp-engine.ts` (retry options)
|
||||
|
||||
- **Retry Behavior**
|
||||
- **Overall Timeout**: Applies to ALL attempts combined, not per attempt
|
||||
- **No Retry for Timeouts**: Timeout errors are never retried (already took too long)
|
||||
- **Exponential Backoff**: Optional via custom delay configuration
|
||||
- **Error Logging**: Logs each retry attempt with context
|
||||
|
||||
#### Use Cases
|
||||
|
||||
- **Database Retries**: Handle transient connection failures
|
||||
- **Network Resilience**: Retry on temporary network errors
|
||||
- **Rate Limit Handling**: Backoff and retry when hitting rate limits
|
||||
- **High Availability**: Improve reliability of external storage
|
||||
|
||||
#### Example Usage
|
||||
|
||||
```typescript
|
||||
const engine = new N8NMCPEngine({
|
||||
onSessionNotFound: async (sessionId) => {
|
||||
// May fail transiently due to database load
|
||||
return await database.loadSession(sessionId);
|
||||
},
|
||||
sessionRestorationRetries: 3, // Retry up to 3 times
|
||||
sessionRestorationRetryDelay: 100, // 100ms between retries
|
||||
sessionRestorationTimeout: 5000 // 5s total for all attempts
|
||||
});
|
||||
```
|
||||
|
||||
#### Error Handling
|
||||
|
||||
- **Retryable Errors**: Database connection failures, network errors, rate limits
|
||||
- **Non-Retryable**: Timeout errors (already exceeded time limit)
|
||||
- **Logging**: Each retry logged with attempt number and error details
|
||||
|
||||
#### Testing
|
||||
|
||||
- **Unit Tests**: 34 tests passing (14 lifecycle events + 20 retry policy)
|
||||
- `tests/unit/session-lifecycle-events.test.ts` (14 tests)
|
||||
- `tests/unit/session-restoration-retry.test.ts` (20 tests)
|
||||
- **Integration Tests**: 14 tests covering combined behavior
|
||||
- `tests/integration/session-lifecycle-retry.test.ts`
|
||||
- **Coverage**: Event emission, retry logic, timeout handling, backward compatibility
|
||||
|
||||
#### Documentation
|
||||
|
||||
- **Types**: Full JSDoc documentation in type definitions
|
||||
- **Examples**: Practical examples in CHANGELOG and type comments
|
||||
- **Migration**: Backward compatible - no breaking changes
|
||||
|
||||
#### Impact
|
||||
|
||||
- **Reliability**: Improved session restoration success rate
|
||||
- **Observability**: Complete visibility into session lifecycle
|
||||
- **Integration**: Easy integration with existing monitoring systems
|
||||
- **Performance**: Non-blocking event handlers prevent slowdowns
|
||||
- **Flexibility**: Opt-in retry policy with sensible defaults
|
||||
|
||||
## [2.18.8] - 2025-10-11
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
@@ -2934,139 +2508,6 @@ get_node_essentials({
|
||||
- Added telemetry configuration instructions to README
|
||||
- Updated CLAUDE.md with telemetry system architecture
|
||||
|
||||
## [2.19.0] - 2025-10-12
|
||||
|
||||
### Added
|
||||
|
||||
**Session Persistence for Multi-Tenant Deployments (Phase 1 + Phase 2)**
|
||||
|
||||
This release introduces production-ready session persistence enabling stateless multi-tenant deployments with session restoration and complete session lifecycle management.
|
||||
|
||||
#### Phase 1: Session Restoration Hook (REQ-1 to REQ-4)
|
||||
|
||||
- **Automatic Session Restoration**
|
||||
- New `onSessionNotFound` hook for session restoration from external storage
|
||||
- Async database lookup when client sends unknown session ID
|
||||
- Configurable restoration timeout (default 5 seconds)
|
||||
- Seamless integration with existing multi-tenant API
|
||||
|
||||
- **Core Capabilities**
|
||||
- Restore sessions from Redis, PostgreSQL, or any external storage
|
||||
- Support for session metadata and custom context
|
||||
- Timeout protection prevents hanging requests
|
||||
- Backward compatible - optional feature, zero breaking changes
|
||||
|
||||
- **Integration Points**
|
||||
- Hook called before session validation in handleRequest flow
|
||||
- Thread-safe session restoration with proper locking
|
||||
- Error handling with detailed logging
|
||||
- Production-tested with comprehensive test coverage
|
||||
|
||||
#### Phase 2: Session Management API (REQ-5)
|
||||
|
||||
- **Session Lifecycle Management**
|
||||
- `getActiveSessions()`: List all active session IDs
|
||||
- `getSessionState(sessionId)`: Get complete session state
|
||||
- `getAllSessionStates()`: Bulk export for periodic backups
|
||||
- `restoreSession(sessionId, context)`: Manual session restoration
|
||||
- `deleteSession(sessionId)`: Explicit session cleanup
|
||||
|
||||
- **Session State Information**
|
||||
- Session ID, instance context, metadata
|
||||
- Creation time, last access, expiration time
|
||||
- Serializable for database storage
|
||||
|
||||
- **Workflow Support**
|
||||
- Periodic backup: Export all sessions every N minutes
|
||||
- Bulk restore: Load sessions on server restart
|
||||
- Manual cleanup: Remove sessions from external trigger
|
||||
|
||||
#### Security Improvements
|
||||
|
||||
- **Session ID Validation**
|
||||
- Length validation (20-100 characters)
|
||||
- Character whitelist (alphanumeric, hyphens, underscores)
|
||||
- SQL injection prevention
|
||||
- Path traversal prevention
|
||||
- Early validation before restoration hook
|
||||
|
||||
- **Orphan Detection**
|
||||
- Comprehensive cleanup of orphaned session components
|
||||
- Detects and removes orphaned transports
|
||||
- Detects and removes orphaned servers
|
||||
- Prevents memory leaks from incomplete cleanup
|
||||
- Warning logs for orphaned resources
|
||||
|
||||
- **Rate Limiting Documentation**
|
||||
- Security notes in JSDoc for `onSessionNotFound`
|
||||
- Recommendations for preventing database lookup abuse
|
||||
- Guidance on implementing express-rate-limit
|
||||
|
||||
#### Technical Implementation
|
||||
|
||||
- **Files Changed**:
|
||||
- `src/types/session-restoration.ts`: New types for session restoration
|
||||
- `src/http-server-single-session.ts`: Hook integration and session management API
|
||||
- `src/mcp-engine.ts`: Public API methods for session lifecycle
|
||||
- `tests/unit/session-management-api.test.ts`: 21 unit tests
|
||||
- `tests/integration/session-persistence.test.ts`: 13 integration tests
|
||||
|
||||
- **Testing**:
|
||||
- ✅ 34 total tests (21 unit + 13 integration)
|
||||
- ✅ All edge cases covered (timeouts, errors, validation)
|
||||
- ✅ Thread safety verified
|
||||
- ✅ Memory leak prevention tested
|
||||
- ✅ Backward compatibility confirmed
|
||||
|
||||
#### Migration Guide
|
||||
|
||||
**For Existing Users (No Changes Required)**
|
||||
```typescript
|
||||
// Your existing code continues to work unchanged
|
||||
const engine = new N8NMCPEngine();
|
||||
await engine.processRequest(req, res, instanceContext);
|
||||
```
|
||||
|
||||
**For New Session Persistence Users**
|
||||
```typescript
|
||||
// 1. Implement restoration hook
|
||||
const engine = new N8NMCPEngine({
|
||||
onSessionNotFound: async (sessionId) => {
|
||||
// Load from your database
|
||||
const session = await db.loadSession(sessionId);
|
||||
return session ? session.instanceContext : null;
|
||||
},
|
||||
sessionRestorationTimeout: 5000
|
||||
});
|
||||
|
||||
// 2. Periodic backup (optional)
|
||||
setInterval(async () => {
|
||||
const states = engine.getAllSessionStates();
|
||||
for (const state of states) {
|
||||
await db.upsertSession(state);
|
||||
}
|
||||
}, 300000); // Every 5 minutes
|
||||
|
||||
// 3. Restore on server start (optional)
|
||||
const savedSessions = await db.loadAllSessions();
|
||||
for (const session of savedSessions) {
|
||||
engine.restoreSession(session.sessionId, session.instanceContext);
|
||||
}
|
||||
```
|
||||
|
||||
#### Benefits
|
||||
|
||||
- **Stateless Deployment**: No session state in memory, safe for container restarts
|
||||
- **Multi-Tenant Support**: Each tenant's sessions persist independently
|
||||
- **High Availability**: Sessions survive server crashes and deployments
|
||||
- **Scalability**: Share session state across multiple server instances
|
||||
- **Cost Efficient**: Use Redis, PostgreSQL, or any database for persistence
|
||||
|
||||
### Documentation
|
||||
- Added comprehensive session persistence documentation
|
||||
- Added migration guide and examples
|
||||
- Updated API documentation with session management methods
|
||||
|
||||
## Previous Versions
|
||||
|
||||
For changes in previous versions, please refer to the git history and release notes.
|
||||
Reference in New Issue
Block a user