From 36eb8e3864bfe0804d16847bef5a705dfb79f01e Mon Sep 17 00:00:00 2001 From: czlonkowski <56956555+czlonkowski@users.noreply.github.com> Date: Thu, 9 Oct 2025 22:24:40 +0200 Subject: [PATCH] fix: resolve sql.js adapter bypass in NodeRepository constructor (Issue #296) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes duck typing ('db' in object) to instanceof check for precise type discrimination. Only unwraps SQLiteStorageService instances, preserving DatabaseAdapter wrappers intact. Fixes MCP tool failures (get_node_essentials, get_node_info, validate_node_operation) on systems using sql.js fallback (Node.js version mismatches, ARM architectures). - Changed: NodeRepository constructor to use instanceof SQLiteStorageService - Fixed: sql.js queries now flow through SQLJSAdapter wrapper properly - Impact: Empty object returns eliminated, proper data normalization restored Closes #296 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- CHANGELOG.md | 76 +++++++++++++++++++++++++++++++++ package.json | 2 +- src/database/node-repository.ts | 7 +-- 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19db5ef..d1916e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,82 @@ 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.18.4] - 2025-10-09 + +### 🐛 Bug Fixes + +**Issue #296: sql.js Adapter Bypass Causing MCP Tool Failures** + +This release fixes a critical constructor bug in `NodeRepository` that caused the sql.js database adapter to be bypassed, resulting in empty object returns and MCP tool failures. + +#### Problem + +When using the sql.js fallback adapter (pure JavaScript implementation without native dependencies), three critical MCP tools were failing with "Cannot read properties of undefined" errors: +- `get_node_essentials` +- `get_node_info` +- `validate_node_operation` + +**Root Cause:** +The `NodeRepository` constructor used duck typing (`'db' in object`) to determine whether to unwrap the database adapter. This check incorrectly matched BOTH `SQLiteStorageService` AND `DatabaseAdapter` instances because both have a `.db` property. + +When sql.js was used: +1. `createDatabaseAdapter()` returned a `SQLJSAdapter` instance (wrapped) +2. `NodeRepository` constructor saw `'db' in adapter` was true +3. Constructor unwrapped it: `this.db = adapter.db` +4. This exposed the raw sql.js `Database` object, bypassing all wrapper logic +5. Raw sql.js API has completely different behavior (returns typed arrays instead of objects) +6. Result: Empty objects `{}` with no properties, causing undefined property access errors + +#### Fixed + +**NodeRepository Constructor Type Discrimination** +- Changed from duck typing (`'db' in object`) to precise instanceof check +- Only unwrap `SQLiteStorageService` instances (intended behavior) +- Keep `DatabaseAdapter` instances intact (preserves wrapper logic) +- File: `src/database/node-repository.ts` + +#### Technical Details + +**Before (Broken):** +```typescript +constructor(dbOrService: DatabaseAdapter | SQLiteStorageService) { + if ('db' in dbOrService) { // ❌ Matches EVERYTHING with .db property + this.db = dbOrService.db; // Unwraps both SQLiteStorageService AND DatabaseAdapter + } else { + this.db = dbOrService; + } +} +``` + +**After (Fixed):** +```typescript +constructor(dbOrService: DatabaseAdapter | SQLiteStorageService) { + if (dbOrService instanceof SQLiteStorageService) { // ✅ Only matches SQLiteStorageService + this.db = dbOrService.db; + return; + } + + this.db = dbOrService; // ✅ Keep DatabaseAdapter intact +} +``` + +**Why instanceof is Critical:** +- `'db' in object` is property checking (duck typing) - too permissive +- `instanceof` is class hierarchy checking - precise type discrimination +- With instanceof, sql.js queries flow through `SQLJSAdapter` → `SQLJSStatement` wrapper chain +- Wrapper normalizes sql.js behavior to match better-sqlite3 API (object returns) + +**Impact:** +- Fixes MCP tool failures on systems where better-sqlite3 cannot compile (Node.js version mismatches, ARM architectures) +- Ensures sql.js fallback works correctly with proper data normalization +- No performance impact (same code path, just preserved wrapper) + +#### Related + +- Closes issue #296 +- Affects environments where better-sqlite3 falls back to sql.js +- Common in Docker containers, CI environments, and ARM-based systems + ## [2.18.3] - 2025-10-09 ### 🔒 Critical Safety Fixes diff --git a/package.json b/package.json index d9a032a..cb43e19 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n-mcp", - "version": "2.18.3", + "version": "2.18.4", "description": "Integration between n8n workflow automation and Model Context Protocol (MCP)", "main": "dist/index.js", "bin": { diff --git a/src/database/node-repository.ts b/src/database/node-repository.ts index 85b7205..7ba8797 100644 --- a/src/database/node-repository.ts +++ b/src/database/node-repository.ts @@ -7,11 +7,12 @@ export class NodeRepository { private db: DatabaseAdapter; constructor(dbOrService: DatabaseAdapter | SQLiteStorageService) { - if ('db' in dbOrService) { + if (dbOrService instanceof SQLiteStorageService) { this.db = dbOrService.db; - } else { - this.db = dbOrService; + return; } + + this.db = dbOrService; } /**