chore: update n8n to v1.101.1

- Updated n8n from 1.100.1 to 1.101.1
- Updated n8n-core from 1.99.0 to 1.100.0
- Updated n8n-workflow from 1.97.0 to 1.98.0
- Updated @n8n/n8n-nodes-langchain from 1.99.0 to 1.100.1
- Rebuilt node database with 528 nodes
- All validation tests passing
- Bumped version to 2.7.12

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
czlonkowski
2025-07-11 00:48:43 +02:00
parent 53d8c8452f
commit f525303748
16 changed files with 5231 additions and 1009 deletions

48
scripts/debug-fuzzy.ts Normal file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env node
import { N8NDocumentationMCPServer } from '../src/mcp/server';
async function debugFuzzy() {
const server = new N8NDocumentationMCPServer();
await new Promise(resolve => setTimeout(resolve, 1000));
// Get the actual implementation
const serverAny = server as any;
// Test nodes we expect to find
const testNodes = [
{ node_type: 'nodes-base.slack', display_name: 'Slack', description: 'Consume Slack API' },
{ node_type: 'nodes-base.webhook', display_name: 'Webhook', description: 'Handle webhooks' },
{ node_type: 'nodes-base.httpRequest', display_name: 'HTTP Request', description: 'Make HTTP requests' },
{ node_type: 'nodes-base.emailSend', display_name: 'Send Email', description: 'Send emails' }
];
const testQueries = ['slak', 'webook', 'htpp', 'emial'];
console.log('Testing fuzzy scoring...\n');
for (const query of testQueries) {
console.log(`\nQuery: "${query}"`);
console.log('-'.repeat(40));
for (const node of testNodes) {
const score = serverAny.calculateFuzzyScore(node, query);
const distance = serverAny.getEditDistance(query, node.display_name.toLowerCase());
console.log(`${node.display_name.padEnd(15)} - Score: ${score.toFixed(0).padStart(4)}, Distance: ${distance}`);
}
// Test actual search
console.log('\nActual search result:');
const result = await server.executeTool('search_nodes', {
query: query,
mode: 'FUZZY',
limit: 5
});
console.log(`Found ${result.results.length} results`);
if (result.results.length > 0) {
console.log('Top result:', result.results[0].displayName);
}
}
}
debugFuzzy().catch(console.error);

View File

@@ -0,0 +1,114 @@
#!/usr/bin/env npx tsx
/**
* Debug template search issues
*/
import { createDatabaseAdapter } from '../src/database/database-adapter';
import { TemplateRepository } from '../src/templates/template-repository';
async function debug() {
console.log('🔍 Debugging template search...\n');
const db = await createDatabaseAdapter('./data/nodes.db');
// Check FTS5 support
const hasFTS5 = db.checkFTS5Support();
console.log(`FTS5 support: ${hasFTS5}`);
// Check template count
const templateCount = db.prepare('SELECT COUNT(*) as count FROM templates').get() as { count: number };
console.log(`Total templates: ${templateCount.count}`);
// Check FTS5 tables
const ftsTables = db.prepare(`
SELECT name FROM sqlite_master
WHERE type IN ('table', 'virtual') AND name LIKE 'templates_fts%'
ORDER BY name
`).all() as { name: string }[];
console.log('\nFTS5 tables:');
ftsTables.forEach(t => console.log(` - ${t.name}`));
// Check FTS5 content
if (hasFTS5) {
try {
const ftsCount = db.prepare('SELECT COUNT(*) as count FROM templates_fts').get() as { count: number };
console.log(`\nFTS5 entries: ${ftsCount.count}`);
} catch (error) {
console.log('\nFTS5 query error:', error);
}
}
// Test template repository
console.log('\n📋 Testing TemplateRepository...');
const repo = new TemplateRepository(db);
// Test different searches
const searches = ['webhook', 'api', 'automation'];
for (const query of searches) {
console.log(`\n🔎 Searching for "${query}"...`);
// Direct SQL LIKE search
const likeResults = db.prepare(`
SELECT COUNT(*) as count FROM templates
WHERE name LIKE ? OR description LIKE ?
`).get(`%${query}%`, `%${query}%`) as { count: number };
console.log(` LIKE search matches: ${likeResults.count}`);
// Repository search
try {
const repoResults = repo.searchTemplates(query, 5);
console.log(` Repository search returned: ${repoResults.length} results`);
if (repoResults.length > 0) {
console.log(` First result: ${repoResults[0].name}`);
}
} catch (error) {
console.log(` Repository search error:`, error);
}
// Direct FTS5 search if available
if (hasFTS5) {
try {
const ftsQuery = `"${query}"`;
const ftsResults = db.prepare(`
SELECT COUNT(*) as count
FROM templates t
JOIN templates_fts ON t.id = templates_fts.rowid
WHERE templates_fts MATCH ?
`).get(ftsQuery) as { count: number };
console.log(` Direct FTS5 matches: ${ftsResults.count}`);
} catch (error) {
console.log(` Direct FTS5 error:`, error);
}
}
}
// Check if templates_fts is properly synced
if (hasFTS5) {
console.log('\n🔄 Checking FTS5 sync...');
try {
// Get a few template IDs and check if they're in FTS
const templates = db.prepare('SELECT id, name FROM templates LIMIT 5').all() as { id: number, name: string }[];
for (const template of templates) {
try {
const inFTS = db.prepare('SELECT rowid FROM templates_fts WHERE rowid = ?').get(template.id);
console.log(` Template ${template.id} "${template.name.substring(0, 30)}...": ${inFTS ? 'IN FTS' : 'NOT IN FTS'}`);
} catch (error) {
console.log(` Error checking template ${template.id}:`, error);
}
}
} catch (error) {
console.log(' FTS sync check error:', error);
}
}
db.close();
}
// Run if called directly
if (require.main === module) {
debug().catch(console.error);
}
export { debug };

View File

@@ -0,0 +1,130 @@
#!/usr/bin/env node
import * as path from 'path';
import { createDatabaseAdapter } from '../src/database/database-adapter';
import { logger } from '../src/utils/logger';
/**
* Migrate existing database to add FTS5 support for nodes
*/
async function migrateNodesFTS() {
logger.info('Starting nodes FTS5 migration...');
const dbPath = path.join(process.cwd(), 'data', 'nodes.db');
const db = await createDatabaseAdapter(dbPath);
try {
// Check if nodes_fts already exists
const tableExists = db.prepare(`
SELECT name FROM sqlite_master
WHERE type='table' AND name='nodes_fts'
`).get();
if (tableExists) {
logger.info('nodes_fts table already exists, skipping migration');
return;
}
logger.info('Creating nodes_fts virtual table...');
// Create the FTS5 virtual table
db.prepare(`
CREATE VIRTUAL TABLE IF NOT EXISTS nodes_fts USING fts5(
node_type,
display_name,
description,
documentation,
operations,
content=nodes,
content_rowid=rowid,
tokenize='porter'
)
`).run();
// Populate the FTS table with existing data
logger.info('Populating nodes_fts with existing data...');
const nodes = db.prepare('SELECT rowid, * FROM nodes').all() as any[];
logger.info(`Migrating ${nodes.length} nodes to FTS index...`);
const insertStmt = db.prepare(`
INSERT INTO nodes_fts(rowid, node_type, display_name, description, documentation, operations)
VALUES (?, ?, ?, ?, ?, ?)
`);
for (const node of nodes) {
insertStmt.run(
node.rowid,
node.node_type,
node.display_name,
node.description || '',
node.documentation || '',
node.operations || ''
);
}
// Create triggers to keep FTS in sync
logger.info('Creating synchronization triggers...');
db.prepare(`
CREATE TRIGGER IF NOT EXISTS nodes_fts_insert AFTER INSERT ON nodes
BEGIN
INSERT INTO nodes_fts(rowid, node_type, display_name, description, documentation, operations)
VALUES (new.rowid, new.node_type, new.display_name, new.description, new.documentation, new.operations);
END
`).run();
db.prepare(`
CREATE TRIGGER IF NOT EXISTS nodes_fts_update AFTER UPDATE ON nodes
BEGIN
UPDATE nodes_fts
SET node_type = new.node_type,
display_name = new.display_name,
description = new.description,
documentation = new.documentation,
operations = new.operations
WHERE rowid = new.rowid;
END
`).run();
db.prepare(`
CREATE TRIGGER IF NOT EXISTS nodes_fts_delete AFTER DELETE ON nodes
BEGIN
DELETE FROM nodes_fts WHERE rowid = old.rowid;
END
`).run();
// Test the FTS search
logger.info('Testing FTS search...');
const testResults = db.prepare(`
SELECT n.* FROM nodes n
JOIN nodes_fts ON n.rowid = nodes_fts.rowid
WHERE nodes_fts MATCH 'webhook'
ORDER BY rank
LIMIT 5
`).all();
logger.info(`FTS test search found ${testResults.length} results for 'webhook'`);
// Persist if using sql.js
if ('persist' in db) {
logger.info('Persisting database changes...');
(db as any).persist();
}
logger.info('✅ FTS5 migration completed successfully!');
} catch (error) {
logger.error('Migration failed:', error);
throw error;
} finally {
db.close();
}
}
// Run migration
migrateNodesFTS().catch(error => {
logger.error('Migration error:', error);
process.exit(1);
});

162
scripts/test-fts5-search.ts Normal file
View File

@@ -0,0 +1,162 @@
#!/usr/bin/env node
import { N8NDocumentationMCPServer } from '../src/mcp/server';
interface SearchTest {
query: string;
mode?: 'OR' | 'AND' | 'FUZZY';
description: string;
expectedTop?: string[];
}
async function testFTS5Search() {
console.log('Testing FTS5 Search Implementation\n');
console.log('='.repeat(50));
const server = new N8NDocumentationMCPServer();
// Wait for initialization
await new Promise(resolve => setTimeout(resolve, 1000));
const tests: SearchTest[] = [
{
query: 'webhook',
description: 'Basic search - should return Webhook node first',
expectedTop: ['nodes-base.webhook']
},
{
query: 'http call',
description: 'Multi-word OR search - should return HTTP Request node first',
expectedTop: ['nodes-base.httpRequest']
},
{
query: 'send message',
mode: 'AND',
description: 'AND mode - only nodes with both "send" AND "message"',
},
{
query: 'slak',
mode: 'FUZZY',
description: 'FUZZY mode - should find Slack despite typo',
expectedTop: ['nodes-base.slack']
},
{
query: '"email trigger"',
description: 'Exact phrase search with quotes',
},
{
query: 'http',
mode: 'FUZZY',
description: 'FUZZY mode with common term',
expectedTop: ['nodes-base.httpRequest']
},
{
query: 'google sheets',
mode: 'AND',
description: 'AND mode - find Google Sheets node',
expectedTop: ['nodes-base.googleSheets']
},
{
query: 'webhook trigger',
mode: 'OR',
description: 'OR mode - should return nodes with either word',
}
];
let passedTests = 0;
let failedTests = 0;
for (const test of tests) {
console.log(`\n${test.description}`);
console.log(`Query: "${test.query}" (Mode: ${test.mode || 'OR'})`);
console.log('-'.repeat(40));
try {
const results = await server.executeTool('search_nodes', {
query: test.query,
mode: test.mode,
limit: 5
});
if (!results.results || results.results.length === 0) {
console.log('❌ No results found');
if (test.expectedTop) {
failedTests++;
}
continue;
}
console.log(`Found ${results.results.length} results:`);
results.results.forEach((node: any, index: number) => {
const marker = test.expectedTop && index === 0 && test.expectedTop.includes(node.nodeType) ? ' ✅' : '';
console.log(` ${index + 1}. ${node.nodeType} - ${node.displayName}${marker}`);
});
// Verify search mode is returned
if (results.mode) {
console.log(`\nSearch mode used: ${results.mode}`);
}
// Check expected results
if (test.expectedTop) {
const firstResult = results.results[0];
if (test.expectedTop.includes(firstResult.nodeType)) {
console.log('✅ Test passed: Expected node found at top');
passedTests++;
} else {
console.log('❌ Test failed: Expected node not at top');
console.log(` Expected: ${test.expectedTop.join(' or ')}`);
console.log(` Got: ${firstResult.nodeType}`);
failedTests++;
}
} else {
// Test without specific expectations
console.log('✅ Search completed successfully');
passedTests++;
}
} catch (error) {
console.log(`❌ Error: ${error}`);
failedTests++;
}
}
console.log('\n' + '='.repeat(50));
console.log('FTS5 Feature Tests');
console.log('='.repeat(50));
// Test FTS5-specific features
console.log('\n1. Testing relevance ranking...');
const webhookResult = await server.executeTool('search_nodes', {
query: 'webhook',
limit: 10
});
console.log(` Primary "Webhook" node position: #${webhookResult.results.findIndex((r: any) => r.nodeType === 'nodes-base.webhook') + 1}`);
console.log('\n2. Testing fuzzy matching with various typos...');
const typoTests = ['webook', 'htpp', 'slck', 'googl sheet'];
for (const typo of typoTests) {
const result = await server.executeTool('search_nodes', {
query: typo,
mode: 'FUZZY',
limit: 1
});
if (result.results.length > 0) {
console.log(` "${typo}" → ${result.results[0].displayName}`);
} else {
console.log(` "${typo}" → No results ❌`);
}
}
console.log('\n' + '='.repeat(50));
console.log(`Test Summary: ${passedTests} passed, ${failedTests} failed`);
console.log('='.repeat(50));
process.exit(failedTests > 0 ? 1 : 0);
}
// Run tests
testFTS5Search().catch(error => {
console.error('Test execution failed:', error);
process.exit(1);
});

76
scripts/test-fuzzy-fix.ts Normal file
View File

@@ -0,0 +1,76 @@
#!/usr/bin/env node
import { N8NDocumentationMCPServer } from '../src/mcp/server';
async function testFuzzyFix() {
console.log('Testing FUZZY mode fix...\n');
const server = new N8NDocumentationMCPServer();
// Wait for initialization
await new Promise(resolve => setTimeout(resolve, 1000));
// Test 1: FUZZY mode with typo
console.log('Test 1: FUZZY mode with "slak" (typo for "slack")');
const fuzzyResult = await server.executeTool('search_nodes', {
query: 'slak',
mode: 'FUZZY',
limit: 5
});
console.log(`Results: ${fuzzyResult.results.length} found`);
if (fuzzyResult.results.length > 0) {
console.log('✅ FUZZY mode now finds results!');
fuzzyResult.results.forEach((node: any, i: number) => {
console.log(` ${i + 1}. ${node.nodeType} - ${node.displayName}`);
});
} else {
console.log('❌ FUZZY mode still not working');
}
// Test 2: AND mode with explanation
console.log('\n\nTest 2: AND mode with "send message"');
const andResult = await server.executeTool('search_nodes', {
query: 'send message',
mode: 'AND',
limit: 5
});
console.log(`Results: ${andResult.results.length} found`);
if (andResult.searchInfo) {
console.log('✅ AND mode now includes search info:');
console.log(` ${andResult.searchInfo.message}`);
console.log(` Tip: ${andResult.searchInfo.tip}`);
}
console.log('\nFirst 5 results:');
andResult.results.slice(0, 5).forEach((node: any, i: number) => {
console.log(` ${i + 1}. ${node.nodeType} - ${node.displayName}`);
});
// Test 3: More typos
console.log('\n\nTest 3: More FUZZY tests');
const typos = ['htpp', 'webook', 'slck', 'emial'];
for (const typo of typos) {
const result = await server.executeTool('search_nodes', {
query: typo,
mode: 'FUZZY',
limit: 1
});
if (result.results.length > 0) {
console.log(`✅ "${typo}" → ${result.results[0].displayName}`);
} else {
console.log(`❌ "${typo}" → No results`);
}
}
process.exit(0);
}
// Run tests
testFuzzyFix().catch(error => {
console.error('Test failed:', error);
process.exit(1);
});

View File

@@ -0,0 +1,38 @@
#!/usr/bin/env node
import { N8NDocumentationMCPServer } from '../src/mcp/server';
async function testSimple() {
const server = new N8NDocumentationMCPServer();
await new Promise(resolve => setTimeout(resolve, 1000));
// Just test one query
const result = await server.executeTool('search_nodes', {
query: 'slak',
mode: 'FUZZY',
limit: 5
});
console.log('Query: "slak" (FUZZY mode)');
console.log(`Results: ${result.results.length}`);
if (result.results.length === 0) {
// Let's check with a lower threshold
const serverAny = server as any;
const slackNode = {
node_type: 'nodes-base.slack',
display_name: 'Slack',
description: 'Consume Slack API'
};
const score = serverAny.calculateFuzzyScore(slackNode, 'slak');
console.log(`\nSlack node score for "slak": ${score}`);
console.log('Current threshold: 400');
console.log('Should it match?', score >= 400 ? 'YES' : 'NO');
} else {
result.results.forEach((r: any, i: number) => {
console.log(`${i + 1}. ${r.displayName}`);
});
}
}
testSimple().catch(console.error);

View File

@@ -0,0 +1,46 @@
#\!/usr/bin/env node
import { N8NDocumentationMCPServer } from '../src/mcp/server';
async function testHttpSearch() {
const server = new N8NDocumentationMCPServer();
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('Testing search for "http"...\n');
const result = await server.executeTool('search_nodes', {
query: 'http',
limit: 50 // Get more results to see where HTTP Request is
});
console.log(`Total results: ${result.results.length}\n`);
// Find HTTP Request node in results
const httpRequestIndex = result.results.findIndex((r: any) =>
r.nodeType === 'nodes-base.httpRequest'
);
if (httpRequestIndex === -1) {
console.log('❌ HTTP Request node NOT FOUND in results\!');
} else {
console.log(`✅ HTTP Request found at position ${httpRequestIndex + 1}`);
}
console.log('\nTop 10 results:');
result.results.slice(0, 10).forEach((r: any, i: number) => {
console.log(`${i + 1}. ${r.nodeType} - ${r.displayName}`);
});
// Also check LIKE search directly
console.log('\n\nTesting LIKE search fallback:');
const serverAny = server as any;
const likeResult = await serverAny.searchNodesLIKE('http', 20);
console.log(`LIKE search found ${likeResult.results.length} results`);
console.log('Top 5 LIKE results:');
likeResult.results.slice(0, 5).forEach((r: any, i: number) => {
console.log(`${i + 1}. ${r.nodeType} - ${r.displayName}`);
});
}
testHttpSearch().catch(console.error);

113
scripts/test-mcp-search.ts Normal file
View File

@@ -0,0 +1,113 @@
#!/usr/bin/env npx tsx
/**
* Test MCP search behavior
*/
import { createDatabaseAdapter } from '../src/database/database-adapter';
import { TemplateService } from '../src/templates/template-service';
import { TemplateRepository } from '../src/templates/template-repository';
async function testMCPSearch() {
console.log('🔍 Testing MCP search behavior...\n');
// Set MCP_MODE to simulate Docker environment
process.env.MCP_MODE = 'stdio';
console.log('Environment: MCP_MODE =', process.env.MCP_MODE);
const db = await createDatabaseAdapter('./data/nodes.db');
// Test 1: Direct repository search
console.log('\n1⃣ Testing TemplateRepository directly:');
const repo = new TemplateRepository(db);
try {
const repoResults = repo.searchTemplates('webhook', 5);
console.log(` Repository search returned: ${repoResults.length} results`);
if (repoResults.length > 0) {
console.log(` First result: ${repoResults[0].name}`);
}
} catch (error) {
console.log(' Repository search error:', error);
}
// Test 2: Service layer search (what MCP uses)
console.log('\n2⃣ Testing TemplateService (MCP layer):');
const service = new TemplateService(db);
try {
const serviceResults = await service.searchTemplates('webhook', 5);
console.log(` Service search returned: ${serviceResults.length} results`);
if (serviceResults.length > 0) {
console.log(` First result: ${serviceResults[0].name}`);
}
} catch (error) {
console.log(' Service search error:', error);
}
// Test 3: Test with empty query
console.log('\n3⃣ Testing with empty query:');
try {
const emptyResults = await service.searchTemplates('', 5);
console.log(` Empty query returned: ${emptyResults.length} results`);
} catch (error) {
console.log(' Empty query error:', error);
}
// Test 4: Test getTemplatesForTask (which works)
console.log('\n4⃣ Testing getTemplatesForTask (control):');
try {
const taskResults = await service.getTemplatesForTask('webhook_processing');
console.log(` Task search returned: ${taskResults.length} results`);
if (taskResults.length > 0) {
console.log(` First result: ${taskResults[0].name}`);
}
} catch (error) {
console.log(' Task search error:', error);
}
// Test 5: Direct SQL queries
console.log('\n5⃣ Testing direct SQL queries:');
try {
// Count templates
const count = db.prepare('SELECT COUNT(*) as count FROM templates').get() as { count: number };
console.log(` Total templates: ${count.count}`);
// Test LIKE search
const likeResults = db.prepare(`
SELECT COUNT(*) as count FROM templates
WHERE name LIKE '%webhook%' OR description LIKE '%webhook%'
`).get() as { count: number };
console.log(` LIKE search for 'webhook': ${likeResults.count} results`);
// Check if FTS5 table exists
const ftsExists = db.prepare(`
SELECT name FROM sqlite_master
WHERE type='table' AND name='templates_fts'
`).get() as { name: string } | undefined;
console.log(` FTS5 table exists: ${ftsExists ? 'Yes' : 'No'}`);
if (ftsExists) {
// Test FTS5 search
try {
const ftsResults = db.prepare(`
SELECT COUNT(*) as count FROM templates t
JOIN templates_fts ON t.id = templates_fts.rowid
WHERE templates_fts MATCH 'webhook'
`).get() as { count: number };
console.log(` FTS5 search for 'webhook': ${ftsResults.count} results`);
} catch (ftsError) {
console.log(` FTS5 search error:`, ftsError);
}
}
} catch (error) {
console.log(' Direct SQL error:', error);
}
db.close();
}
// Run if called directly
if (require.main === module) {
testMCPSearch().catch(console.error);
}
export { testMCPSearch };

View File

@@ -0,0 +1,136 @@
#!/usr/bin/env node
import { N8NDocumentationMCPServer } from '../src/mcp/server';
interface SearchTestCase {
query: string;
expectedTop: string[];
description: string;
}
async function testSearchImprovements() {
console.log('Testing search improvements...\n');
const server = new N8NDocumentationMCPServer();
// Wait for initialization
await new Promise(resolve => setTimeout(resolve, 1000));
const testCases: SearchTestCase[] = [
{
query: 'webhook',
expectedTop: ['nodes-base.webhook'],
description: 'Primary webhook node should appear first'
},
{
query: 'http',
expectedTop: ['nodes-base.httpRequest'],
description: 'HTTP Request node should appear first'
},
{
query: 'http call',
expectedTop: ['nodes-base.httpRequest'],
description: 'HTTP Request node should appear first for "http call"'
},
{
query: 'slack',
expectedTop: ['nodes-base.slack'],
description: 'Slack node should appear first'
},
{
query: 'email',
expectedTop: ['nodes-base.emailSend', 'nodes-base.gmail', 'nodes-base.emailReadImap'],
description: 'Email-related nodes should appear first'
},
{
query: 'http request',
expectedTop: ['nodes-base.httpRequest'],
description: 'HTTP Request node should appear first for exact name'
}
];
let passedTests = 0;
let failedTests = 0;
for (const testCase of testCases) {
try {
console.log(`\nTest: ${testCase.description}`);
console.log(`Query: "${testCase.query}"`);
const results = await server.executeTool('search_nodes', {
query: testCase.query,
limit: 10
});
if (!results.results || results.results.length === 0) {
console.log('❌ No results found');
failedTests++;
continue;
}
console.log(`Found ${results.results.length} results`);
console.log('Top 5 results:');
const top5 = results.results.slice(0, 5);
top5.forEach((node: any, index: number) => {
const isExpected = testCase.expectedTop.includes(node.nodeType);
const marker = index === 0 && isExpected ? '✅' : index === 0 && !isExpected ? '❌' : '';
console.log(` ${index + 1}. ${node.nodeType} - ${node.displayName} ${marker}`);
});
// Check if any expected node appears in top position
const firstResult = results.results[0];
if (testCase.expectedTop.includes(firstResult.nodeType)) {
console.log('✅ Test passed: Expected node found at top position');
passedTests++;
} else {
console.log('❌ Test failed: Expected nodes not at top position');
console.log(` Expected one of: ${testCase.expectedTop.join(', ')}`);
console.log(` Got: ${firstResult.nodeType}`);
failedTests++;
}
} catch (error) {
console.log(`❌ Test failed with error: ${error}`);
failedTests++;
}
}
console.log('\n' + '='.repeat(50));
console.log(`Test Summary: ${passedTests} passed, ${failedTests} failed`);
console.log('='.repeat(50));
// Test the old problematic queries to ensure improvement
console.log('\n\nTesting Original Problem Scenarios:');
console.log('=====================================\n');
// Test webhook query that was problematic
console.log('1. Testing "webhook" query (was returning service-specific webhooks first):');
const webhookResult = await server.executeTool('search_nodes', { query: 'webhook', limit: 10 });
const webhookFirst = webhookResult.results[0];
if (webhookFirst.nodeType === 'nodes-base.webhook') {
console.log(' ✅ SUCCESS: Primary Webhook node now appears first!');
} else {
console.log(` ❌ FAILED: Got ${webhookFirst.nodeType} instead of nodes-base.webhook`);
console.log(` First 3 results: ${webhookResult.results.slice(0, 3).map((r: any) => r.nodeType).join(', ')}`);
}
// Test http call query
console.log('\n2. Testing "http call" query (was not finding HTTP Request easily):');
const httpCallResult = await server.executeTool('search_nodes', { query: 'http call', limit: 10 });
const httpCallFirst = httpCallResult.results[0];
if (httpCallFirst.nodeType === 'nodes-base.httpRequest') {
console.log(' ✅ SUCCESS: HTTP Request node now appears first!');
} else {
console.log(` ❌ FAILED: Got ${httpCallFirst.nodeType} instead of nodes-base.httpRequest`);
console.log(` First 3 results: ${httpCallResult.results.slice(0, 3).map((r: any) => r.nodeType).join(', ')}`);
}
process.exit(failedTests > 0 ? 1 : 0);
}
// Run tests
testSearchImprovements().catch(error => {
console.error('Test execution failed:', error);
process.exit(1);
});