feat: add n8n workflow templates as MCP tools

- Add 4 new MCP tools for workflow templates
- Integrate with n8n.io API to fetch community templates
- Filter templates to last 6 months only
- Store templates in SQLite with full workflow JSON
- Manual fetch system (not part of regular rebuild)
- Support search by nodes, keywords, and task categories
- Add fetch:templates and test:templates npm scripts
- Update to v2.4.1

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
czlonkowski
2025-06-20 00:02:09 +02:00
parent 98b7e83739
commit 08f9d1ad30
13 changed files with 1068 additions and 33 deletions

View File

@@ -0,0 +1,75 @@
#!/usr/bin/env node
import { createDatabaseAdapter } from '../database/database-adapter';
import { TemplateService } from '../templates/template-service';
import * as fs from 'fs';
import * as path from 'path';
async function fetchTemplates() {
console.log('🌐 Fetching n8n workflow templates...\n');
// Ensure data directory exists
const dataDir = './data';
if (!fs.existsSync(dataDir)) {
fs.mkdirSync(dataDir, { recursive: true });
}
// Initialize database
const db = await createDatabaseAdapter('./data/nodes.db');
// Apply schema if needed
const schema = fs.readFileSync(path.join(__dirname, '../../src/database/schema.sql'), 'utf8');
db.exec(schema);
// Create service
const service = new TemplateService(db);
// Progress tracking
let lastMessage = '';
const startTime = Date.now();
try {
await service.fetchAndUpdateTemplates((message, current, total) => {
// Clear previous line
if (lastMessage) {
process.stdout.write('\r' + ' '.repeat(lastMessage.length) + '\r');
}
const progress = Math.round((current / total) * 100);
lastMessage = `📊 ${message}: ${current}/${total} (${progress}%)`;
process.stdout.write(lastMessage);
});
console.log('\n'); // New line after progress
// Get stats
const stats = await service.getTemplateStats();
const elapsed = Math.round((Date.now() - startTime) / 1000);
console.log('✅ Template fetch complete!\n');
console.log('📈 Statistics:');
console.log(` - Total templates: ${stats.totalTemplates}`);
console.log(` - Average views: ${stats.averageViews}`);
console.log(` - Time elapsed: ${elapsed} seconds`);
console.log('\n🔝 Top used nodes:');
stats.topUsedNodes.forEach((node: any, index: number) => {
console.log(` ${index + 1}. ${node.node} (${node.count} templates)`);
});
} catch (error) {
console.error('\n❌ Error fetching templates:', error);
process.exit(1);
}
// Close database
if ('close' in db && typeof db.close === 'function') {
db.close();
}
}
// Run if called directly
if (require.main === module) {
fetchTemplates().catch(console.error);
}
export { fetchTemplates };

View File

@@ -0,0 +1,88 @@
#!/usr/bin/env node
import { createDatabaseAdapter } from '../database/database-adapter';
import { TemplateService } from '../templates/template-service';
import * as fs from 'fs';
import * as path from 'path';
async function testTemplates() {
console.log('🧪 Testing template functionality...\n');
// Initialize database
const db = await createDatabaseAdapter('./data/nodes.db');
// Apply schema if needed
const schema = fs.readFileSync(path.join(__dirname, '../../src/database/schema.sql'), 'utf8');
db.exec(schema);
// Create service
const service = new TemplateService(db);
try {
// Get statistics
const stats = await service.getTemplateStats();
console.log('📊 Template Database Stats:');
console.log(` Total templates: ${stats.totalTemplates}`);
if (stats.totalTemplates === 0) {
console.log('\n⚠ No templates found in database!');
console.log(' Run "npm run fetch:templates" to populate the database.\n');
return;
}
console.log(` Average views: ${stats.averageViews}`);
console.log('\n🔝 Most used nodes in templates:');
stats.topUsedNodes.forEach((node: any, i: number) => {
console.log(` ${i + 1}. ${node.node} (${node.count} templates)`);
});
// Test search
console.log('\n🔍 Testing search for "webhook":');
const searchResults = await service.searchTemplates('webhook', 3);
searchResults.forEach((t: any) => {
console.log(` - ${t.name} (${t.views} views)`);
});
// Test node-based search
console.log('\n🔍 Testing templates with HTTP Request node:');
const httpTemplates = await service.listNodeTemplates(['n8n-nodes-base.httpRequest'], 3);
httpTemplates.forEach((t: any) => {
console.log(` - ${t.name} (${t.nodes.length} nodes)`);
});
// Test task-based search
console.log('\n🔍 Testing AI automation templates:');
const aiTemplates = await service.getTemplatesForTask('ai_automation');
aiTemplates.forEach((t: any) => {
console.log(` - ${t.name} by @${t.author.username}`);
});
// Get a specific template
if (searchResults.length > 0) {
const templateId = searchResults[0].id;
console.log(`\n📄 Getting template ${templateId} details...`);
const template = await service.getTemplate(templateId);
if (template) {
console.log(` Name: ${template.name}`);
console.log(` Nodes: ${template.nodes.join(', ')}`);
console.log(` Workflow has ${template.workflow.nodes.length} nodes`);
}
}
console.log('\n✅ All template tests passed!');
} catch (error) {
console.error('❌ Error during testing:', error);
}
// Close database
if ('close' in db && typeof db.close === 'function') {
db.close();
}
}
// Run if called directly
if (require.main === module) {
testTemplates().catch(console.error);
}
export { testTemplates };