diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e4bfb8..745faee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,95 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.17.4] - 2025-10-07 + +### 🔧 Validation + +**Fixed critical version extraction and typeVersion validation bugs.** + +This release fixes two critical bugs that caused incorrect version data and validation bypasses for langchain nodes. + +#### Fixed + +- **Version Extraction Bug (CRITICAL)** + - **Issue:** AI Agent node returned version "3" instead of "2.2" (the defaultVersion) + - **Impact:** + - MCP tools (`get_node_essentials`, `get_node_info`) returned incorrect version "3" + - Version "3" exists but n8n explicitly marks it as unstable ("Keep 2.2 until blocking bugs are fixed") + - AI agents created workflows with wrong typeVersion, causing runtime issues + - **Root Cause:** `extractVersion()` in node-parser.ts checked `instance.baseDescription.defaultVersion` which doesn't exist on VersionedNodeType instances + - **Fix:** Updated version extraction priority in `node-parser.ts:137-200` + 1. Priority 1: Check `currentVersion` property (what VersionedNodeType actually uses) + 2. Priority 2: Check `description.defaultVersion` (fixed property name from `baseDescription`) + 3. Priority 3: Fallback to max(nodeVersions) as last resort + - **Verification:** AI Agent node now correctly returns version "2.2" across all MCP tools + +- **typeVersion Validation Bypass (CRITICAL)** + - **Issue:** Langchain nodes with invalid typeVersion passed validation (even `typeVersion: 99999`) + - **Impact:** + - Invalid typeVersion values were never caught during validation + - Workflows with non-existent typeVersions passed validation but failed at runtime in n8n + - Validation was completely bypassed for all langchain nodes (AI Agent, Chat Trigger, OpenAI Chat Model, etc.) + - **Root Cause:** `workflow-validator.ts:400-405` skipped ALL validation for langchain nodes before typeVersion check + - **Fix:** Moved typeVersion validation BEFORE langchain skip in `workflow-validator.ts:447-493` + - typeVersion now validated for ALL nodes including langchain + - Validation runs before parameter validation skip + - Checks for missing, invalid, outdated, and exceeding-maximum typeVersion values + - **Verification:** Workflows with invalid typeVersion now correctly fail validation + +#### Technical Details + +**Version Extraction Fix:** +```typescript +// BEFORE (BROKEN): +if (instance?.baseDescription?.defaultVersion) { // Property doesn't exist! + return instance.baseDescription.defaultVersion.toString(); +} + +// AFTER (FIXED): +if (instance?.currentVersion !== undefined) { // What VersionedNodeType actually uses + return instance.currentVersion.toString(); +} +if (instance?.description?.defaultVersion) { // Correct property name + return instance.description.defaultVersion.toString(); +} +``` + +**typeVersion Validation Fix:** +```typescript +// BEFORE (BROKEN): +// Skip ALL node repository validation for langchain nodes +if (normalizedType.startsWith('nodes-langchain.')) { + continue; // typeVersion validation never runs! +} + +// AFTER (FIXED): +// Validate typeVersion for ALL versioned nodes (including langchain) +if (nodeInfo.isVersioned) { + // ... typeVersion validation ... +} + +// THEN skip parameter validation for langchain nodes +if (normalizedType.startsWith('nodes-langchain.')) { + continue; +} +``` + +#### Impact + +- **Version Accuracy:** AI Agent and all VersionedNodeType nodes now return correct version (2.2, not 3) +- **Validation Reliability:** Invalid typeVersion values are now caught for langchain nodes +- **Workflow Stability:** Prevents creation of workflows with non-existent typeVersions +- **Database Rebuilt:** 536 nodes reloaded with corrected version data + +#### Testing + +- **Unit Tests:** All existing tests passing +- **Integration Tests:** Verified with n8n-mcp-tester agent + - Version consistency between `get_node_essentials` and `get_node_info` ✅ + - typeVersion validation catches invalid values (99, 100000) ✅ + - AI Agent correctly reports version "2.2" ✅ + ## [2.17.3] - 2025-10-07 ### 🔧 Validation diff --git a/data/nodes.db b/data/nodes.db index 0152e62..6e4778f 100644 Binary files a/data/nodes.db and b/data/nodes.db differ diff --git a/package.json b/package.json index 254ec6a..883f1e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n-mcp", - "version": "2.17.3", + "version": "2.17.4", "description": "Integration between n8n workflow automation and Model Context Protocol (MCP)", "main": "dist/index.js", "bin": { diff --git a/src/parsers/node-parser.ts b/src/parsers/node-parser.ts index 02f09ad..a56ecf5 100644 --- a/src/parsers/node-parser.ts +++ b/src/parsers/node-parser.ts @@ -135,26 +135,32 @@ export class NodeParser { } private extractVersion(nodeClass: any): string { - // Check instance for baseDescription first + // Check instance properties first try { const instance = typeof nodeClass === 'function' ? new nodeClass() : nodeClass; - - // Handle instance-level baseDescription - if (instance?.baseDescription?.defaultVersion) { - return instance.baseDescription.defaultVersion.toString(); + + // PRIORITY 1: Check currentVersion (what VersionedNodeType actually uses) + // For VersionedNodeType, currentVersion = defaultVersion ?? max(nodeVersions) + if (instance?.currentVersion !== undefined) { + return instance.currentVersion.toString(); } - - // Handle instance-level nodeVersions + + // PRIORITY 2: Handle instance-level description.defaultVersion + // VersionedNodeType stores baseDescription as 'description', not 'baseDescription' + if (instance?.description?.defaultVersion) { + return instance.description.defaultVersion.toString(); + } + + // PRIORITY 3: Handle instance-level nodeVersions (fallback to max) if (instance?.nodeVersions) { const versions = Object.keys(instance.nodeVersions); return Math.max(...versions.map(Number)).toString(); } - + // Handle version array in description (e.g., [1, 1.1, 1.2]) if (instance?.description?.version) { const version = instance.description.version; if (Array.isArray(version)) { - // Find the maximum version from the array const maxVersion = Math.max(...version.map((v: any) => parseFloat(v.toString()))); return maxVersion.toString(); } else if (typeof version === 'number' || typeof version === 'string') { @@ -165,18 +171,19 @@ export class NodeParser { // Some nodes might require parameters to instantiate // Try class-level properties } - + // Handle class-level VersionedNodeType with defaultVersion - if (nodeClass.baseDescription?.defaultVersion) { - return nodeClass.baseDescription.defaultVersion.toString(); + // Note: Most VersionedNodeType classes don't have static properties + if (nodeClass.description?.defaultVersion) { + return nodeClass.description.defaultVersion.toString(); } - + // Handle class-level VersionedNodeType with nodeVersions if (nodeClass.nodeVersions) { const versions = Object.keys(nodeClass.nodeVersions); return Math.max(...versions.map(Number)).toString(); } - + // Also check class-level description for version array const description = this.getNodeDescription(nodeClass); if (description?.version) { @@ -187,7 +194,7 @@ export class NodeParser { return description.version.toString(); } } - + // Default to version 1 return '1'; } diff --git a/src/services/workflow-validator.ts b/src/services/workflow-validator.ts index 5242877..3bde273 100644 --- a/src/services/workflow-validator.ts +++ b/src/services/workflow-validator.ts @@ -397,14 +397,7 @@ export class WorkflowValidator { node.type = normalizedType; } - // Skip ALL node repository validation for langchain nodes - // They have dedicated AI-specific validators in validateAISpecificNodes() - // This prevents parameter validation conflicts and ensures proper AI validation - if (normalizedType.startsWith('nodes-langchain.')) { - continue; - } - - // Get node definition using normalized type + // Get node definition using normalized type (needed for typeVersion validation) const nodeInfo = this.nodeRepository.getNode(normalizedType); if (!nodeInfo) { @@ -451,7 +444,8 @@ export class WorkflowValidator { continue; } - // Validate typeVersion for versioned nodes + // Validate typeVersion for ALL versioned nodes (including langchain nodes) + // This validation runs BEFORE the langchain skip to ensure typeVersion is checked if (nodeInfo.isVersioned) { // Check if typeVersion is missing if (!node.typeVersion) { @@ -461,7 +455,7 @@ export class WorkflowValidator { 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({ @@ -491,6 +485,13 @@ export class WorkflowValidator { } } + // Skip parameter validation for langchain nodes + // They have dedicated AI-specific validators in validateAISpecificNodes() + // This prevents parameter validation conflicts and ensures proper AI validation + if (normalizedType.startsWith('nodes-langchain.')) { + continue; + } + // Validate node configuration const nodeValidation = this.nodeValidator.validateWithMode( node.type,