mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-03-28 13:13:08 +00:00
* chore: update n8n to 2.13.3 and bump version to 2.41.0 - Updated n8n from 2.12.3 to 2.13.3 - Updated n8n-core from 2.12.0 to 2.13.1 - Updated n8n-workflow from 2.12.0 to 2.13.1 - Updated @n8n/n8n-nodes-langchain from 2.12.0 to 2.13.1 - Rebuilt node database with 1,396 nodes (812 core + 584 community: 516 verified + 68 npm) - Refreshed community nodes with 581 AI-generated documentation summaries - Improved documentation generator: strip <think> tags, raw fetch for vLLM chat_template_kwargs - Incremental community updates: saveNode uses ON CONFLICT DO UPDATE preserving READMEs/AI summaries - fetch:community now upserts by default (use --rebuild for clean slate) - Updated README badge and node counts - Updated CHANGELOG and MEMORY_N8N_UPDATE.md Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en Co-Authored-By: Claude <noreply@anthropic.com> * chore: update MCP SDK from 1.27.1 to 1.28.0 - Pinned @modelcontextprotocol/sdk to 1.28.0 (was ^1.27.1) - Updated CI dependency check to expect 1.28.0 - SDK 1.28.0 includes: loopback port relaxation, inputSchema fix, timeout cleanup fix, OAuth scope improvements - All 15 MCP tool tests pass with no regressions Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en Co-Authored-By: Claude <noreply@anthropic.com> * fix: update test assertions for ON CONFLICT saveNode SQL Tests expected old INSERT OR REPLACE SQL, updated to match new INSERT INTO ... ON CONFLICT(node_type) DO UPDATE SET pattern. Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en Co-Authored-By: Claude <noreply@anthropic.com> * chore: remove documentation generator tests These tests mocked the OpenAI SDK which was replaced with raw fetch. Documentation generation is a local LLM utility, not core functionality. Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en Co-Authored-By: Claude <noreply@anthropic.com> * fix: relax SQL assertion in outputs test to match ON CONFLICT pattern Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en Co-Authored-By: Claude <noreply@anthropic.com> * fix: use INSERT OR REPLACE with docs preservation instead of ON CONFLICT ON CONFLICT DO UPDATE caused FTS5 trigger conflicts ("database disk image is malformed") in CI. Reverted to INSERT OR REPLACE but now reads existing npm_readme/ai_documentation_summary/ai_summary_generated_at before saving and carries them through the replace. Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en Co-Authored-By: Claude <noreply@anthropic.com> * fix: update saveNode test mocks for docs preservation pattern Tests now account for the SELECT query that reads existing docs before INSERT OR REPLACE, and the 3 extra params (npm_readme, ai_documentation_summary, ai_summary_generated_at). Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en Co-Authored-By: Claude <noreply@anthropic.com> * fix: update community integration test mock for INSERT OR REPLACE The mock SQL matching used 'INSERT INTO nodes' which doesn't match 'INSERT OR REPLACE INTO nodes'. Also added handler for the new SELECT npm_readme query in saveNode. Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
166 lines
5.3 KiB
JavaScript
166 lines
5.3 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Fetch community nodes from n8n Strapi API and npm registry.
|
|
*
|
|
* Usage:
|
|
* npm run fetch:community # Upsert all (preserves READMEs and AI summaries)
|
|
* npm run fetch:community:verified # Verified nodes only (fast)
|
|
* npm run fetch:community:update # Incremental update (skip existing)
|
|
*
|
|
* Options:
|
|
* --verified-only Only fetch verified nodes from Strapi API
|
|
* --update Skip nodes that already exist in database
|
|
* --rebuild Delete all community nodes first (wipes READMEs/AI summaries!)
|
|
* --npm-limit=N Maximum number of npm packages to fetch (default: 100)
|
|
* --staging Use staging Strapi API instead of production
|
|
*/
|
|
|
|
import path from 'path';
|
|
import { CommunityNodeService, SyncOptions } from '../community';
|
|
import { NodeRepository } from '../database/node-repository';
|
|
import { createDatabaseAdapter } from '../database/database-adapter';
|
|
|
|
interface CliOptions {
|
|
verifiedOnly: boolean;
|
|
update: boolean;
|
|
rebuild: boolean;
|
|
npmLimit: number;
|
|
staging: boolean;
|
|
}
|
|
|
|
function parseArgs(): CliOptions {
|
|
const args = process.argv.slice(2);
|
|
|
|
const options: CliOptions = {
|
|
verifiedOnly: false,
|
|
update: false,
|
|
rebuild: false,
|
|
npmLimit: 100,
|
|
staging: false,
|
|
};
|
|
|
|
for (const arg of args) {
|
|
if (arg === '--verified-only') {
|
|
options.verifiedOnly = true;
|
|
} else if (arg === '--update') {
|
|
options.update = true;
|
|
} else if (arg === '--rebuild') {
|
|
options.rebuild = true;
|
|
} else if (arg === '--staging') {
|
|
options.staging = true;
|
|
} else if (arg.startsWith('--npm-limit=')) {
|
|
const value = parseInt(arg.split('=')[1], 10);
|
|
if (!isNaN(value) && value > 0) {
|
|
options.npmLimit = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
function printProgress(message: string, current: number, total: number): void {
|
|
const percent = total > 0 ? Math.round((current / total) * 100) : 0;
|
|
const bar = '='.repeat(Math.floor(percent / 2)) + ' '.repeat(50 - Math.floor(percent / 2));
|
|
process.stdout.write(`\r[${bar}] ${percent}% - ${message} (${current}/${total})`);
|
|
if (current === total) {
|
|
console.log(); // New line at completion
|
|
}
|
|
}
|
|
|
|
async function main(): Promise<void> {
|
|
const cliOptions = parseArgs();
|
|
|
|
console.log('='.repeat(60));
|
|
console.log(' n8n-mcp Community Node Fetcher');
|
|
console.log('='.repeat(60));
|
|
console.log();
|
|
|
|
// Print options
|
|
console.log('Options:');
|
|
console.log(` - Mode: ${cliOptions.rebuild ? 'Rebuild (clean slate)' : cliOptions.update ? 'Update (skip existing)' : 'Upsert (preserves docs)'}`);
|
|
console.log(` - Verified only: ${cliOptions.verifiedOnly ? 'Yes' : 'No'}`);
|
|
if (!cliOptions.verifiedOnly) {
|
|
console.log(` - npm package limit: ${cliOptions.npmLimit}`);
|
|
}
|
|
console.log(` - API environment: ${cliOptions.staging ? 'staging' : 'production'}`);
|
|
console.log();
|
|
|
|
// Initialize database
|
|
const dbPath = path.join(__dirname, '../../data/nodes.db');
|
|
console.log(`Database: ${dbPath}`);
|
|
|
|
const db = await createDatabaseAdapter(dbPath);
|
|
const repository = new NodeRepository(db);
|
|
|
|
// Create service
|
|
const environment = cliOptions.staging ? 'staging' : 'production';
|
|
const service = new CommunityNodeService(repository, environment);
|
|
|
|
// Only delete existing community nodes when --rebuild is explicitly requested
|
|
if (cliOptions.rebuild) {
|
|
console.log('\nClearing existing community nodes (--rebuild)...');
|
|
console.log(' WARNING: This wipes READMEs and AI summaries!');
|
|
const deleted = service.deleteCommunityNodes();
|
|
console.log(` Deleted ${deleted} existing community nodes`);
|
|
}
|
|
|
|
// Sync options
|
|
const syncOptions: SyncOptions = {
|
|
verifiedOnly: cliOptions.verifiedOnly,
|
|
npmLimit: cliOptions.npmLimit,
|
|
skipExisting: cliOptions.update,
|
|
environment,
|
|
};
|
|
|
|
// Run sync
|
|
console.log('\nFetching community nodes...\n');
|
|
|
|
const result = await service.syncCommunityNodes(syncOptions, printProgress);
|
|
|
|
// Print results
|
|
console.log('\n' + '='.repeat(60));
|
|
console.log(' Results');
|
|
console.log('='.repeat(60));
|
|
console.log();
|
|
|
|
console.log('Verified nodes (Strapi API):');
|
|
console.log(` - Fetched: ${result.verified.fetched}`);
|
|
console.log(` - Saved: ${result.verified.saved}`);
|
|
console.log(` - Skipped: ${result.verified.skipped}`);
|
|
if (result.verified.errors.length > 0) {
|
|
console.log(` - Errors: ${result.verified.errors.length}`);
|
|
result.verified.errors.forEach((e) => console.log(` ! ${e}`));
|
|
}
|
|
|
|
if (!cliOptions.verifiedOnly) {
|
|
console.log('\nnpm packages:');
|
|
console.log(` - Fetched: ${result.npm.fetched}`);
|
|
console.log(` - Saved: ${result.npm.saved}`);
|
|
console.log(` - Skipped: ${result.npm.skipped}`);
|
|
if (result.npm.errors.length > 0) {
|
|
console.log(` - Errors: ${result.npm.errors.length}`);
|
|
result.npm.errors.forEach((e) => console.log(` ! ${e}`));
|
|
}
|
|
}
|
|
|
|
// Get final stats
|
|
const stats = service.getCommunityStats();
|
|
console.log('\nDatabase statistics:');
|
|
console.log(` - Total community nodes: ${stats.total}`);
|
|
console.log(` - Verified: ${stats.verified}`);
|
|
console.log(` - Unverified: ${stats.unverified}`);
|
|
|
|
console.log(`\nCompleted in ${(result.duration / 1000).toFixed(1)} seconds`);
|
|
console.log('='.repeat(60));
|
|
|
|
// Close database
|
|
db.close();
|
|
}
|
|
|
|
// Run
|
|
main().catch((error) => {
|
|
console.error('Fatal error:', error);
|
|
process.exit(1);
|
|
});
|