From c2dc7c9c51b08a975fa4a54737c79a9634bcbf36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romuald=20Cz=C5=82onkowski?= <56956555+czlonkowski@users.noreply.github.com> Date: Fri, 5 Dec 2025 11:17:51 +0100 Subject: [PATCH] feat: configurable MAX_SESSIONS via N8N_MCP_MAX_SESSIONS env var (v2.28.4) (#469) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: n8n_test_workflow webhookId resolution and form handling (v2.28.2) ## Bug Fixes - **webhookId Resolution**: Fixed trigger handlers using `node.id` instead of `node.webhookId` for building webhook URLs. This caused 404 errors when nodes had custom IDs. - **Chat Trigger URL**: Fixed chat triggers using wrong URL pattern. Now correctly uses `/webhook//chat` endpoint. - **Form Content-Type**: Fixed form triggers failing with "Expected multipart/form-data" error by switching to proper multipart encoding. ## Enhancements - **Form Field Types**: Added support for all n8n form field types (text, textarea, email, number, password, date, dropdown, checkbox, file, hidden) - **Checkbox Arrays**: Automatically converts arrays to `field[]` format - **Helpful Warnings**: Reports missing required fields with names and labels - **Error Hints**: Provides complete field structure on failure Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * fix: address code review issues for form trigger improvements - Add form-data as direct dependency (was only in devDependencies) - Add TypeScript interfaces (FormFieldValue, FormFieldOption) replacing any types - Add FORM_FIELD_TYPES constants for type-safe switch statements - Add isValidBase64() validation for file uploads with size limits - Add MAX_FILE_SIZE_BYTES (10MB) constant with validation - Update form-handler.test.ts for FormData instead of JSON - Update trigger-detector.test.ts for chat URL /chat suffix Conceived by Romuald Członkowski - www.aiadvisors.pl/en 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * feat: configurable MAX_SESSIONS via N8N_MCP_MAX_SESSIONS env var (v2.28.3) (#468) Make MAX_SESSIONS limit configurable for multi-tenant SaaS deployments. - Add N8N_MCP_MAX_SESSIONS environment variable (default: 100) - Include safety floor with Math.max(1, ...) to prevent invalid configs - Update documentation in LIBRARY_USAGE.md, SESSION_PERSISTENCE.md - Update CLAUDE.md and CHANGELOG.md Fixes #468 Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * fix: add QEMU setup for multi-arch Docker builds Add docker/setup-qemu-action@v3 before Buildx setup to enable proper QEMU emulation for linux/arm64 builds on GitHub Actions. Fixes CI Docker build failure with "execve: No such file or directory" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --------- Co-authored-by: Claude Co-authored-by: Romuald Członkowski --- .github/workflows/docker-build-n8n.yml | 3 +++ CHANGELOG.md | 21 ++++++++++++++++- CLAUDE.md | 2 +- docs/LIBRARY_USAGE.md | 2 +- docs/SESSION_PERSISTENCE.md | 23 ++++++++++--------- package-lock.json | 4 ++-- package.json | 2 +- src/http-server-single-session.ts | 2 +- .../http-server/session-persistence.test.ts | 2 +- 9 files changed, 42 insertions(+), 19 deletions(-) diff --git a/.github/workflows/docker-build-n8n.yml b/.github/workflows/docker-build-n8n.yml index 1134bb4..d7c4b95 100644 --- a/.github/workflows/docker-build-n8n.yml +++ b/.github/workflows/docker-build-n8n.yml @@ -52,6 +52,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index 609de27..efe2a58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.28.4] - 2025-12-05 + +### Features + +**Configurable MAX_SESSIONS Limit (#468)** + +The `MAX_SESSIONS` limit is now configurable via the `N8N_MCP_MAX_SESSIONS` environment variable, addressing scalability issues for multi-tenant SaaS deployments. + +- **Problem**: Hardcoded limit of 100 concurrent sessions caused "Session limit reached" errors during peak usage +- **Solution**: `MAX_SESSIONS` now reads from `N8N_MCP_MAX_SESSIONS` env var (default: 100) +- **Usage**: Set `N8N_MCP_MAX_SESSIONS=1000` for higher capacity deployments +- **Safety**: Includes `Math.max(1, ...)` floor to prevent invalid configurations +- **Files**: `src/http-server-single-session.ts:44` + +```bash +# Example: Allow up to 1000 concurrent sessions +N8N_MCP_MAX_SESSIONS=1000 +``` + ## [2.28.3] - 2025-12-02 ### Changed @@ -596,7 +615,7 @@ Added export/restore functionality for MCP sessions to enable zero-downtime depl - `restoreSessionState(sessions)` method for session recovery - Validates session structure using existing `validateInstanceContext()` - Handles null/invalid sessions gracefully with warnings -- Enforces MAX_SESSIONS limit (100 concurrent sessions) +- Enforces MAX_SESSIONS limit (default 100, configurable via N8N_MCP_MAX_SESSIONS env var) - Skips expired sessions during restore **3. SessionState Type** diff --git a/CLAUDE.md b/CLAUDE.md index 6e4dc35..e3a58a4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -209,7 +209,7 @@ The MCP server exposes tools in several categories: - **Security-first**: API keys exported as plaintext - downstream MUST encrypt - **Dormant sessions**: Restored sessions recreate transports on first request - **Automatic expiration**: Respects `sessionTimeout` setting (default 30 min) -- **MAX_SESSIONS limit**: Caps at 100 concurrent sessions +- **MAX_SESSIONS limit**: Caps at 100 concurrent sessions (configurable via N8N_MCP_MAX_SESSIONS env var) **Important Implementation Notes:** - Only exports sessions with valid n8nApiUrl and n8nApiKey in context diff --git a/docs/LIBRARY_USAGE.md b/docs/LIBRARY_USAGE.md index f3fbebd..928c24d 100644 --- a/docs/LIBRARY_USAGE.md +++ b/docs/LIBRARY_USAGE.md @@ -558,7 +558,7 @@ DISABLE_CONSOLE_OUTPUT=false # Optional: Session configuration SESSION_TIMEOUT=1800000 # 30 minutes in milliseconds -MAX_SESSIONS=100 +N8N_MCP_MAX_SESSIONS=100 # Maximum concurrent sessions (default: 100) # Optional: Performance NODE_ENV=production diff --git a/docs/SESSION_PERSISTENCE.md b/docs/SESSION_PERSISTENCE.md index d31ffc0..bb99662 100644 --- a/docs/SESSION_PERSISTENCE.md +++ b/docs/SESSION_PERSISTENCE.md @@ -93,7 +93,7 @@ console.log(`Restored ${count} sessions`); - Validates session metadata (timestamps, required fields) - Skips expired sessions (age > sessionTimeout) - Skips duplicate sessions (idempotent) -- Respects MAX_SESSIONS limit (100 per container) +- Respects MAX_SESSIONS limit (default 100, configurable via N8N_MCP_MAX_SESSIONS env var) - Recreates transports/servers lazily on first request - Logs security events for restore success/failure @@ -595,19 +595,19 @@ console.log(`Export size: ${sizeKB.toFixed(2)} KB`); ### MAX_SESSIONS Limit -Hard limit: 100 sessions per container +Default limit: 100 sessions per container (configurable via `N8N_MCP_MAX_SESSIONS` env var) ```typescript // Restore respects limit const sessions = createSessions(150); // 150 sessions const restored = engine.restoreSessionState(sessions); -// restored = 100 (only first 100 restored) +// restored = 100 (only first 100 restored, or N8N_MCP_MAX_SESSIONS value) ``` -For >100 sessions per tenant: -- Deploy multiple containers -- Use session routing/sharding -- Implement session affinity +For higher session limits: +- Set `N8N_MCP_MAX_SESSIONS=1000` (or desired limit) +- Monitor memory usage as sessions consume resources +- Alternatively, deploy multiple containers with session routing/sharding ## Troubleshooting @@ -676,10 +676,11 @@ Reached MAX_SESSIONS limit (100), skipping remaining sessions **Solutions:** -1. Scale horizontally (more containers) -2. Implement session sharding -3. Reduce sessionTimeout -4. Clean up inactive sessions +1. Increase limit: Set `N8N_MCP_MAX_SESSIONS=1000` (or desired value) +2. Scale horizontally (more containers) +3. Implement session sharding +4. Reduce sessionTimeout +5. Clean up inactive sessions ```typescript // Pre-filter by activity diff --git a/package-lock.json b/package-lock.json index e47edf7..da68c4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "n8n-mcp", - "version": "2.28.2", + "version": "2.28.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "n8n-mcp", - "version": "2.28.2", + "version": "2.28.4", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "1.20.1", diff --git a/package.json b/package.json index 02a4ee3..1893e6c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n-mcp", - "version": "2.28.3", + "version": "2.28.4", "description": "Integration between n8n workflow automation and Model Context Protocol (MCP)", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/http-server-single-session.ts b/src/http-server-single-session.ts index 7400fc2..58d3406 100644 --- a/src/http-server-single-session.ts +++ b/src/http-server-single-session.ts @@ -41,7 +41,7 @@ interface MultiTenantHeaders { } // Session management constants -const MAX_SESSIONS = 100; +const MAX_SESSIONS = Math.max(1, parseInt(process.env.N8N_MCP_MAX_SESSIONS || '100', 10)); const SESSION_CLEANUP_INTERVAL = 5 * 60 * 1000; // 5 minutes interface Session { diff --git a/tests/unit/http-server/session-persistence.test.ts b/tests/unit/http-server/session-persistence.test.ts index e7ff177..143f8f4 100644 --- a/tests/unit/http-server/session-persistence.test.ts +++ b/tests/unit/http-server/session-persistence.test.ts @@ -427,7 +427,7 @@ describe('SingleSessionHTTPServer - Session Persistence', () => { }); it('should respect MAX_SESSIONS limit during restore', () => { - // Create 99 existing sessions (MAX_SESSIONS is 100) + // Create 99 existing sessions (MAX_SESSIONS defaults to 100, configurable via N8N_MCP_MAX_SESSIONS env var) const serverAny = server as any; const now = new Date(); for (let i = 0; i < 99; i++) {