From 1170ad27a63735b2bb1740c7177ffa726788bc35 Mon Sep 17 00:00:00 2001 From: czlonkowski <56956555+czlonkowski@users.noreply.github.com> Date: Fri, 11 Jul 2025 08:48:37 +0200 Subject: [PATCH] fix: resolve WASM file loading issue for npx execution (closes #31) - Enhanced database adapter to support multiple WASM file resolution strategies - Added require.resolve() for reliable package location in npm environments - Made better-sqlite3 an optional dependency - Improved error handling with clear messages - Updated version to 2.7.13 - Updated CHANGELOG and README badges --- README.md | 2 +- data/nodes.db | Bin 25616384 -> 25616384 bytes docs/CHANGELOG.md | 16 ++++++++++ package.json | 5 ++- package.runtime.json | 5 ++- scripts/publish-npm.sh | 1 + scripts/test-database-adapter.js | 38 ++++++++++++++++++++++ scripts/test-sqljs-fallback.js | 43 +++++++++++++++++++++++++ scripts/test-wasm-resolution.js | 52 +++++++++++++++++++++++++++++++ src/database/database-adapter.ts | 43 +++++++++++++++++++++++-- test-claude-desktop-config.json | 13 ++++++++ 11 files changed, 213 insertions(+), 5 deletions(-) create mode 100755 scripts/test-database-adapter.js create mode 100755 scripts/test-sqljs-fallback.js create mode 100755 scripts/test-wasm-resolution.js create mode 100644 test-claude-desktop-config.json diff --git a/README.md b/README.md index eb8a979..c378944 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![GitHub stars](https://img.shields.io/github/stars/czlonkowski/n8n-mcp?style=social)](https://github.com/czlonkowski/n8n-mcp) -[![Version](https://img.shields.io/badge/version-2.7.12-blue.svg)](https://github.com/czlonkowski/n8n-mcp) +[![Version](https://img.shields.io/badge/version-2.7.13-blue.svg)](https://github.com/czlonkowski/n8n-mcp) [![npm version](https://img.shields.io/npm/v/n8n-mcp.svg)](https://www.npmjs.com/package/n8n-mcp) [![n8n version](https://img.shields.io/badge/n8n-v1.101.1-orange.svg)](https://github.com/n8n-io/n8n) [![Docker](https://img.shields.io/badge/docker-ghcr.io%2Fczlonkowski%2Fn8n--mcp-green.svg)](https://github.com/czlonkowski/n8n-mcp/pkgs/container/n8n-mcp) diff --git a/data/nodes.db b/data/nodes.db index 9b9fa5339cd6555422b12f3543d654b0d3c11423..96c003862337e8db6ba378571375c9765bd74ef7 100644 GIT binary patch delta 1852 zcmZA0cUV<*9LMo+N@-m5VaJKhbDWr#j^p%^ z^IKBW+{1++T*5VJdr>&BpMNoiA16a(UeFcl8F?e8PS|*L8KBbiB?2wq7BiOXh)tP3}PHHo|r&PBqkA)i7CWXVj3}>m_g|KWD;3KHjzWjBxVt__abmC)8kJmi_Z?k zm5s{^WO)3^HpG@4%nD5M_>*kN@%T<$dAK7xkn8asy*v~p(?OC97qPtd(^VltNiA8)TzUHc63OBiG7xvRST|8>CoDq*S)Z zjdGK0m78Uol*uh}tK25rWry4@cgRk;Q|^+xWtZ%hd*oiZPwtlo0S!o*KkkN88axyf`rl_k9UUIi# zS7K3O&H-j<{Cj=U@H$@}twd?+8uG5J_Nkx%6_IWC{e2{|dJc^T0$&U zETNXFmTH#jmM}{Vi_a2niLgXkqAbytnwDCY+7`d1jwQwtYpH9gXNj}aw=}TCTM{e{ zEsZRVEs2&UmZp{@OR^=!(#+D_(!!EzX=!O?X>Dm^X=`a`Nwc)Kbg*=^bh31|bg^`` zbhC7~^sw}_^s=N|dRzKf`da!~`dbEA23iJL23v+$hFXSMhFeBhMp{O><%yM}-QzPt zuWajo*RM1p#Ph$E+-w$r%19SmG~+OTxF4Ev1N&6sb!gExn+fArDc_6wPlTEt!14h WXeqR;w`{O%w9qa0RcvxE#QzIIWzs1C delta 1809 zcmXBTcUYBW9LDkU9^v3P2&gEEf)kM8-V+t~Mo@9UeE{{~K*hZe>MfN8*pNyKE8A%{ z%!Yb2vt^bI&88adNoC81-M{+d^S!R$^E`h%*L%HB;Mgv2Kq_OSPP$w%#V%K~G?&XY zRBCplc6Uyef^Y~oQHKa2LJ1EMM${$h5%q}%gqH{>8WIsiBoRe4A{rA-h-jiI(Ts>8 zniDOESfVA-iijiPiPl6LqAk&mXisz?5{N`1iRefq6DdS0kw$bPIul)pu0%JYJJEya zN%SIm6McxjL_eZGF@P9I3?c>->BJCXC^3x4Achkoh>^r7Vl**^7)y*J#uF2WiNqvg zGLcD4A+m^UVk$9>m`>ynGl-eQEMhj1OXLyyK0YF!C?E=nImBFI-a+`!``lqes^SX# zu~pgm{w#Oc02^YePUQP@++qD~D0GKK|G&7qz+dF{?EP;ji!by`k<6C`vQQRDu`HG) zvQ$cBnJkwTvQk#bYFQ&|Wu2^-4YE--NkB?vvuu&ALfIx|a*bRo*U5IdUT%4!KjRWslq?cgsDpSMHVj5KaVJ|}W#?O9Kmr}nhGE^o*gc~jn!x8)rDm^X=`a`X>aLZNw6eZk}Mr9$(9sL zswK_R$$THZHZW&@3Y8ht9unf11 zu#B{fvW&Kjv5d8hvy8V)uuQZ}a)O@P$+B#C-=16.0.0" + }, + "optionalDependencies": { + "better-sqlite3": "^11.10.0" } } diff --git a/scripts/publish-npm.sh b/scripts/publish-npm.sh index 5b9d4d3..573777e 100755 --- a/scripts/publish-npm.sh +++ b/scripts/publish-npm.sh @@ -64,6 +64,7 @@ pkg.license = 'MIT'; pkg.bugs = { url: 'https://github.com/czlonkowski/n8n-mcp/issues' }; pkg.homepage = 'https://github.com/czlonkowski/n8n-mcp#readme'; pkg.files = ['dist/**/*', 'data/nodes.db', '.env.example', 'README.md', 'LICENSE']; +// Note: node_modules are automatically included for dependencies delete pkg.private; // Remove private field so we can publish require('fs').writeFileSync('./package.json', JSON.stringify(pkg, null, 2)); " diff --git a/scripts/test-database-adapter.js b/scripts/test-database-adapter.js new file mode 100755 index 0000000..1d50c68 --- /dev/null +++ b/scripts/test-database-adapter.js @@ -0,0 +1,38 @@ +#!/usr/bin/env node + +const { createDatabaseAdapter } = require('../dist/database/database-adapter'); +const path = require('path'); + +async function testDatabaseAdapter() { + console.log('Testing database adapter initialization...\n'); + + const dbPath = path.join(__dirname, '../data/nodes.db'); + console.log('Database path:', dbPath); + + try { + console.log('Creating database adapter...'); + const adapter = await createDatabaseAdapter(dbPath); + + console.log('\n✅ Database adapter created successfully!'); + + // Test a simple query + console.log('\nTesting database query...'); + const stmt = adapter.prepare('SELECT COUNT(*) as count FROM nodes'); + const result = stmt.get(); + console.log(`✅ Database contains ${result.count} nodes`); + + // Check FTS5 support + console.log('\nChecking FTS5 support...'); + const hasFTS5 = adapter.checkFTS5Support(); + console.log(`FTS5 support: ${hasFTS5 ? '✅ Available' : '❌ Not available'}`); + + adapter.close(); + console.log('\n✅ All tests passed!'); + } catch (error) { + console.error('\n❌ Error:', error.message); + console.error('Stack:', error.stack); + process.exit(1); + } +} + +testDatabaseAdapter(); \ No newline at end of file diff --git a/scripts/test-sqljs-fallback.js b/scripts/test-sqljs-fallback.js new file mode 100755 index 0000000..e884235 --- /dev/null +++ b/scripts/test-sqljs-fallback.js @@ -0,0 +1,43 @@ +#!/usr/bin/env node + +// Force sql.js usage by temporarily hiding better-sqlite3 +const Module = require('module'); +const originalRequire = Module.prototype.require; + +Module.prototype.require = function(id) { + if (id === 'better-sqlite3') { + throw new Error('Simulating better-sqlite3 not available (NODE_MODULE_VERSION mismatch)'); + } + return originalRequire.apply(this, arguments); +}; + +const { createDatabaseAdapter } = require('../dist/database/database-adapter'); +const path = require('path'); + +async function testSqlJsFallback() { + console.log('Testing sql.js fallback...\n'); + + const dbPath = path.join(__dirname, '../data/nodes.db'); + + try { + console.log('Creating database adapter (better-sqlite3 disabled)...'); + const adapter = await createDatabaseAdapter(dbPath); + + console.log('\n✅ Database adapter created successfully with sql.js!'); + + // Test a simple query + console.log('\nTesting database query...'); + const stmt = adapter.prepare('SELECT COUNT(*) as count FROM nodes'); + const result = stmt.get(); + console.log(`✅ Database contains ${result.count} nodes`); + + adapter.close(); + console.log('\n✅ sql.js fallback works correctly!'); + } catch (error) { + console.error('\n❌ Error:', error.message); + console.error('Stack:', error.stack); + process.exit(1); + } +} + +testSqlJsFallback(); \ No newline at end of file diff --git a/scripts/test-wasm-resolution.js b/scripts/test-wasm-resolution.js new file mode 100755 index 0000000..96a61ae --- /dev/null +++ b/scripts/test-wasm-resolution.js @@ -0,0 +1,52 @@ +#!/usr/bin/env node + +const path = require('path'); +const fs = require('fs'); + +console.log('Testing WASM file resolution...\n'); + +// Show current environment +console.log('Current directory:', process.cwd()); +console.log('Script directory:', __dirname); +console.log('Node version:', process.version); +console.log(''); + +// Test different path resolutions +const testPaths = [ + // Local development path + path.join(__dirname, '../node_modules/sql.js/dist/sql-wasm.wasm'), + // When installed as npm package + path.join(__dirname, '../../sql.js/dist/sql-wasm.wasm'), + // Alternative npm package path + path.join(process.cwd(), 'node_modules/sql.js/dist/sql-wasm.wasm'), +]; + +console.log('Checking potential WASM file locations:'); +testPaths.forEach((testPath, index) => { + const exists = fs.existsSync(testPath); + console.log(`${index + 1}. ${testPath}`); + console.log(` Exists: ${exists ? '✅' : '❌'}`); +}); + +// Try require.resolve +console.log('\nTrying require.resolve:'); +try { + const wasmPath = require.resolve('sql.js/dist/sql-wasm.wasm'); + console.log('✅ Found via require.resolve:', wasmPath); + console.log(' Exists:', fs.existsSync(wasmPath) ? '✅' : '❌'); +} catch (e) { + console.log('❌ Failed to resolve via require.resolve:', e.message); +} + +// Try to find sql.js package location +console.log('\nTrying to find sql.js package:'); +try { + const sqlJsPath = require.resolve('sql.js'); + console.log('✅ Found sql.js at:', sqlJsPath); + const sqlJsDir = path.dirname(sqlJsPath); + const wasmFromSqlJs = path.join(sqlJsDir, '../dist/sql-wasm.wasm'); + console.log(' Derived WASM path:', wasmFromSqlJs); + console.log(' Exists:', fs.existsSync(wasmFromSqlJs) ? '✅' : '❌'); +} catch (e) { + console.log('❌ Failed to find sql.js package:', e.message); +} \ No newline at end of file diff --git a/src/database/database-adapter.ts b/src/database/database-adapter.ts index 4d4d5e7..d7de1a9 100644 --- a/src/database/database-adapter.ts +++ b/src/database/database-adapter.ts @@ -117,14 +117,53 @@ async function createBetterSQLiteAdapter(dbPath: string): Promise { - const initSqlJs = require('sql.js'); + let initSqlJs; + try { + initSqlJs = require('sql.js'); + } catch (error) { + logger.error('Failed to load sql.js module:', error); + throw new Error('sql.js module not found. This might be an issue with npm package installation.'); + } // Initialize sql.js const SQL = await initSqlJs({ // This will look for the wasm file in node_modules locateFile: (file: string) => { if (file.endsWith('.wasm')) { - return path.join(__dirname, '../../node_modules/sql.js/dist/', file); + // Try multiple paths to find the WASM file + const possiblePaths = [ + // Local development path + path.join(__dirname, '../../node_modules/sql.js/dist/', file), + // When installed as npm package + path.join(__dirname, '../../../sql.js/dist/', file), + // Alternative npm package path + path.join(process.cwd(), 'node_modules/sql.js/dist/', file), + // Try to resolve from require + path.join(path.dirname(require.resolve('sql.js')), '../dist/', file) + ]; + + // Find the first existing path + for (const tryPath of possiblePaths) { + if (fsSync.existsSync(tryPath)) { + if (process.env.MCP_MODE !== 'stdio') { + logger.debug(`Found WASM file at: ${tryPath}`); + } + return tryPath; + } + } + + // If not found, try the last resort - require.resolve + try { + const wasmPath = require.resolve('sql.js/dist/sql-wasm.wasm'); + if (process.env.MCP_MODE !== 'stdio') { + logger.debug(`Found WASM file via require.resolve: ${wasmPath}`); + } + return wasmPath; + } catch (e) { + // Fall back to the default path + logger.warn(`Could not find WASM file, using default path: ${file}`); + return file; + } } return file; } diff --git a/test-claude-desktop-config.json b/test-claude-desktop-config.json new file mode 100644 index 0000000..515a42b --- /dev/null +++ b/test-claude-desktop-config.json @@ -0,0 +1,13 @@ +{ + "mcpServers": { + "n8n-mcp-local": { + "command": "node", + "args": ["/Users/romualdczlonkowski/Pliki/n8n-mcp/n8n-mcp/dist/mcp/index.js"], + "env": { + "MCP_MODE": "stdio", + "LOG_LEVEL": "error", + "DISABLE_CONSOLE_OUTPUT": "true" + } + } + } +} \ No newline at end of file