diff --git a/CLAUDE.md b/CLAUDE.md index fb957ae..741cde2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,15 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co n8n-mcp is a comprehensive documentation and knowledge server that provides AI assistants with complete access to n8n node information through the Model Context Protocol (MCP). It serves as a bridge between n8n's workflow automation platform and AI models, enabling them to understand and work with n8n nodes effectively. -## ✅ Latest Updates (v2.6.0) +## ✅ Latest Updates (v2.6.1) + +### Update (v2.6.1) - Enhanced typeVersion Validation: +- ✅ **NEW: typeVersion validation** - Workflow validator now enforces typeVersion on all versioned nodes +- ✅ **Catches missing typeVersion** - Returns error with correct version to use +- ✅ **Warns on outdated versions** - Alerts when using older node versions +- ✅ **Prevents invalid versions** - Errors on versions that exceed maximum supported +- ✅ Helps AI agents avoid common workflow creation mistakes +- ✅ Ensures workflows use compatible node versions before deployment ### Update (v2.6.0) - n8n Management Tools Integration: - ✅ **NEW: 14 n8n management tools** - Create, update, execute workflows via API @@ -159,6 +167,7 @@ src/ │ ├── test-workflow-validation.ts # Test workflow validation (NEW in v2.5.0) │ ├── test-ai-workflow-validation.ts # Test AI workflow validation (NEW in v2.5.1) │ ├── test-mcp-tools.ts # Test MCP tool enhancements (NEW in v2.5.1) +│ ├── test-typeversion-validation.ts # Test typeVersion validation (NEW in v2.6.1) │ ├── fetch-templates.ts # Fetch workflow templates from n8n.io (NEW in v2.4.1) │ └── test-templates.ts # Test template functionality (NEW in v2.4.1) ├── mcp/ @@ -211,6 +220,7 @@ npm run test:mcp-tools # Test MCP tool enhancements npm run test:single-session # Test single session HTTP npm run test:template-validation # Test template validation npm run test:n8n-manager # Test n8n management tools integration +npm run test:typeversion-validation # Test typeVersion validation # Workflow Validation Commands: npm run test:workflow-validation # Test workflow validation features diff --git a/package.json b/package.json index f07385c..0336371 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n-mcp", - "version": "2.5.1", + "version": "2.6.1", "description": "Integration between n8n workflow automation and Model Context Protocol (MCP)", "main": "dist/index.js", "scripts": { @@ -32,6 +32,7 @@ "test:ai-workflow-validation": "node dist/scripts/test-ai-workflow-validation.js", "test:mcp-tools": "node dist/scripts/test-mcp-tools.js", "test:n8n-manager": "node dist/scripts/test-n8n-manager-integration.js", + "test:typeversion-validation": "node dist/scripts/test-typeversion-validation.js", "db:rebuild": "node dist/scripts/rebuild-database.js", "db:init": "node -e \"new (require('./dist/services/sqlite-storage-service').SQLiteStorageService)(); console.log('Database initialized')\"", "docs:rebuild": "ts-node src/scripts/rebuild-database.ts" diff --git a/scripts/test-typeversion-validation.ts b/scripts/test-typeversion-validation.ts new file mode 100644 index 0000000..b9c2e43 --- /dev/null +++ b/scripts/test-typeversion-validation.ts @@ -0,0 +1,207 @@ +#!/usr/bin/env ts-node + +/** + * Test script for typeVersion validation in workflow validator + */ + +import { NodeRepository } from '../src/database/node-repository'; +import { createDatabaseAdapter } from '../src/database/database-adapter'; +import { WorkflowValidator } from '../src/services/workflow-validator'; +import { EnhancedConfigValidator } from '../src/services/enhanced-config-validator'; +import { Logger } from '../src/utils/logger'; + +const logger = new Logger({ prefix: '[test-typeversion]' }); + +// Test workflows with various typeVersion scenarios +const testWorkflows = { + // Workflow with missing typeVersion on versioned nodes + missingTypeVersion: { + name: 'Missing typeVersion Test', + nodes: [ + { + id: 'webhook_1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + position: [250, 300], + parameters: { + path: '/test', + httpMethod: 'POST' + } + // Missing typeVersion - should error + }, + { + id: 'execute_1', + name: 'Execute Command', + type: 'n8n-nodes-base.executeCommand', + position: [450, 300], + parameters: { + command: 'echo "test"' + } + // Missing typeVersion - should error + } + ], + connections: { + 'Webhook': { + main: [[{ node: 'Execute Command', type: 'main', index: 0 }]] + } + } + }, + + // Workflow with outdated typeVersion + outdatedTypeVersion: { + name: 'Outdated typeVersion Test', + nodes: [ + { + id: 'http_1', + name: 'HTTP Request', + type: 'n8n-nodes-base.httpRequest', + typeVersion: 1, // Outdated - latest is likely 4+ + position: [250, 300], + parameters: { + url: 'https://example.com', + method: 'GET' + } + }, + { + id: 'code_1', + name: 'Code', + type: 'n8n-nodes-base.code', + typeVersion: 1, // Outdated - latest is likely 2 + position: [450, 300], + parameters: { + jsCode: 'return items;' + } + } + ], + connections: { + 'HTTP Request': { + main: [[{ node: 'Code', type: 'main', index: 0 }]] + } + } + }, + + // Workflow with correct typeVersion + correctTypeVersion: { + name: 'Correct typeVersion Test', + nodes: [ + { + id: 'webhook_1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 2, + position: [250, 300], + parameters: { + path: '/test', + httpMethod: 'POST' + } + }, + { + id: 'http_1', + name: 'HTTP Request', + type: 'n8n-nodes-base.httpRequest', + typeVersion: 4, + position: [450, 300], + parameters: { + url: 'https://example.com', + method: 'GET' + } + } + ], + connections: { + 'Webhook': { + main: [[{ node: 'HTTP Request', type: 'main', index: 0 }]] + } + } + }, + + // Workflow with invalid typeVersion + invalidTypeVersion: { + name: 'Invalid typeVersion Test', + nodes: [ + { + id: 'webhook_1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 0, // Invalid - must be positive + position: [250, 300], + parameters: { + path: '/test' + } + }, + { + id: 'http_1', + name: 'HTTP Request', + type: 'n8n-nodes-base.httpRequest', + typeVersion: 999, // Too high - exceeds maximum + position: [450, 300], + parameters: { + url: 'https://example.com' + } + } + ], + connections: { + 'Webhook': { + main: [[{ node: 'HTTP Request', type: 'main', index: 0 }]] + } + } + } +}; + +async function testTypeVersionValidation() { + const dbAdapter = await createDatabaseAdapter('./data/nodes.db'); + const repository = new NodeRepository(dbAdapter); + const validator = new WorkflowValidator(repository, EnhancedConfigValidator); + + console.log('\n===================================='); + console.log('Testing typeVersion Validation'); + console.log('====================================\n'); + + // Check some versioned nodes to show their versions + console.log('📊 Checking versioned nodes in database:'); + const versionedNodes = ['nodes-base.webhook', 'nodes-base.httpRequest', 'nodes-base.code', 'nodes-base.executeCommand']; + + for (const nodeType of versionedNodes) { + const nodeInfo = repository.getNode(nodeType); + if (nodeInfo) { + console.log(`- ${nodeType}: isVersioned=${nodeInfo.isVersioned}, maxVersion=${nodeInfo.version || 'N/A'}`); + } + } + + console.log('\n'); + + // Test each workflow + for (const [testName, workflow] of Object.entries(testWorkflows)) { + console.log(`\n🧪 Testing: ${testName}`); + console.log('─'.repeat(50)); + + const result = await validator.validateWorkflow(workflow as any); + + console.log(`\n✅ Valid: ${result.valid}`); + + if (result.errors.length > 0) { + console.log('\n❌ Errors:'); + result.errors.forEach(error => { + console.log(` - [${error.nodeName || 'Workflow'}] ${error.message}`); + }); + } + + if (result.warnings.length > 0) { + console.log('\n⚠️ Warnings:'); + result.warnings.forEach(warning => { + console.log(` - [${warning.nodeName || 'Workflow'}] ${warning.message}`); + }); + } + + if (result.suggestions.length > 0) { + console.log('\n💡 Suggestions:'); + result.suggestions.forEach(suggestion => { + console.log(` - ${suggestion}`); + }); + } + } + + console.log('\n\n✅ typeVersion validation test completed!'); +} + +// Run the test +testTypeVersionValidation().catch(console.error); \ No newline at end of file diff --git a/src/services/workflow-validator.ts b/src/services/workflow-validator.ts index 251a19f..e6786a8 100644 --- a/src/services/workflow-validator.ts +++ b/src/services/workflow-validator.ts @@ -259,6 +259,46 @@ export class WorkflowValidator { continue; } + // Validate typeVersion for versioned nodes + if (nodeInfo.isVersioned) { + // Check if typeVersion is missing + if (!node.typeVersion) { + result.errors.push({ + type: 'error', + nodeId: node.id, + nodeName: node.name, + message: `Missing required property 'typeVersion'. Add typeVersion: ${nodeInfo.version || 1}` + }); + } + // Check if typeVersion is invalid + else if (typeof node.typeVersion !== 'number' || node.typeVersion < 1) { + result.errors.push({ + type: 'error', + nodeId: node.id, + nodeName: node.name, + message: `Invalid typeVersion: ${node.typeVersion}. Must be a positive number` + }); + } + // Check if typeVersion is outdated (less than latest) + else if (nodeInfo.version && node.typeVersion < nodeInfo.version) { + result.warnings.push({ + type: 'warning', + nodeId: node.id, + nodeName: node.name, + message: `Outdated typeVersion: ${node.typeVersion}. Latest is ${nodeInfo.version}` + }); + } + // Check if typeVersion exceeds maximum supported + else if (nodeInfo.version && node.typeVersion > nodeInfo.version) { + result.errors.push({ + type: 'error', + nodeId: node.id, + nodeName: node.name, + message: `typeVersion ${node.typeVersion} exceeds maximum supported version ${nodeInfo.version}` + }); + } + } + // Validate node configuration const nodeValidation = this.nodeValidator.validateWithMode( node.type,