Skip to content

Commit 13c1663

Browse files
committed
fix: address critical code review issues for validation improvements
- Fix type safety vulnerability in enhanced-config-validator.ts - Added proper type checking before string operations - Return early when nodeType is invalid instead of using empty string - Improve error handling robustness in MCP server - Wrapped validation in try-catch to handle unexpected errors - Properly re-throw ValidationError instances - Add user-friendly error messages for internal errors - Write comprehensive CHANGELOG entry for v2.10.3 - Document fixes for issues #58, #68, #70, #73 - Detail new validation system features - List all enhancements and test coverage Addressed HIGH priority issues from code review: - Type safety holes in config validator - Missing error handling for validation system failures - Consistent error types across validation tools
1 parent 4898626 commit 13c1663

12 files changed

+1106
-128
lines changed

CLAUDE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ The MCP server exposes tools in several categories:
180180
- Sub-agents are not allowed to spawn further sub-agents
181181
- When you use sub-agents, do not allow them to commit and push. That should be done by you
182182

183+
### Development Best Practices
184+
- Run typecheck and lint after every code change
185+
183186
# important-instruction-reminders
184187
Do what has been asked; nothing more, nothing less.
185188
NEVER create files unless they're absolutely necessary for achieving your goal.

data/nodes.db

0 Bytes
Binary file not shown.

docs/CHANGELOG.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,51 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [2.10.3] - 2025-08-07
11+
12+
### Fixed
13+
- **Validation System Robustness**: Fixed multiple critical validation issues affecting AI agents and workflow validation (fixes #58, #68, #70, #73)
14+
- **Issue #73**: Fixed `validate_node_minimal` crash when config is undefined
15+
- Added safe property access with optional chaining (`config?.resource`)
16+
- Tool now handles undefined, null, and malformed configs gracefully
17+
- **Issue #58**: Fixed `validate_node_operation` crash on invalid nodeType
18+
- Added type checking before calling string methods
19+
- Prevents "Cannot read properties of undefined (reading 'replace')" error
20+
- **Issue #70**: Fixed validation profile settings being ignored
21+
- Extended profile parameter to all validation phases (nodes, connections, expressions)
22+
- Added Sticky Notes filtering to reduce false positives
23+
- Enhanced cycle detection to allow legitimate loops (SplitInBatches)
24+
- **Issue #68**: Added error recovery suggestions for AI agents
25+
- New `addErrorRecoverySuggestions()` method provides actionable recovery steps
26+
- Categorizes errors and suggests specific fixes for each type
27+
- Helps AI agents self-correct when validation fails
28+
29+
### Added
30+
- **Input Validation System**: Comprehensive validation for all MCP tool inputs
31+
- Created `validation-schemas.ts` with custom validation utilities
32+
- No external dependencies - pure TypeScript implementation
33+
- Tool-specific validation schemas for all MCP tools
34+
- Clear error messages with field-level details
35+
- **Enhanced Cycle Detection**: Improved detection of legitimate loops vs actual cycles
36+
- Recognizes SplitInBatches loop patterns as valid
37+
- Reduces false positive cycle warnings
38+
- **Comprehensive Test Suite**: Added 16 tests covering all validation fixes
39+
- Tests for crash prevention with malformed inputs
40+
- Tests for profile behavior across validation phases
41+
- Tests for error recovery suggestions
42+
- Tests for legitimate loop patterns
43+
44+
### Enhanced
45+
- **Validation Profiles**: Now consistently applied across all validation phases
46+
- `minimal`: Reduces warnings for basic validation
47+
- `runtime`: Standard validation for production workflows
48+
- `ai-friendly`: Optimized for AI agent workflow creation
49+
- `strict`: Maximum validation for critical workflows
50+
- **Error Messages**: More helpful and actionable for both humans and AI agents
51+
- Specific recovery suggestions for common errors
52+
- Clear guidance on fixing validation issues
53+
- Examples of correct configurations
54+
1055
## [2.10.2] - 2025-08-05
1156

1257
### Updated

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "n8n-mcp",
3-
"version": "2.10.2",
3+
"version": "2.10.3",
44
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
55
"main": "dist/index.js",
66
"bin": {

src/mcp/server.ts

Lines changed: 91 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { handleUpdatePartialWorkflow } from './handlers-workflow-diff';
2828
import { getToolDocumentation, getToolsOverview } from './tools-documentation';
2929
import { PROJECT_VERSION } from '../utils/version';
3030
import { normalizeNodeType, getNodeTypeAlternatives, getWorkflowNodeType } from '../utils/node-utils';
31+
import { ToolValidation, Validator, ValidationError } from '../utils/validation-schemas';
3132
import {
3233
negotiateProtocolVersion,
3334
logProtocolNegotiation,
@@ -460,9 +461,77 @@ export class N8NDocumentationMCPServer {
460461
}
461462

462463
/**
463-
* Validate required parameters for tool execution
464+
* Enhanced parameter validation using schemas
464465
*/
465-
private validateToolParams(toolName: string, args: any, requiredParams: string[]): void {
466+
private validateToolParams(toolName: string, args: any, legacyRequiredParams?: string[]): void {
467+
try {
468+
// If legacy required params are provided, use the new validation but fall back to basic if needed
469+
let validationResult;
470+
471+
switch (toolName) {
472+
case 'validate_node_operation':
473+
validationResult = ToolValidation.validateNodeOperation(args);
474+
break;
475+
case 'validate_node_minimal':
476+
validationResult = ToolValidation.validateNodeMinimal(args);
477+
break;
478+
case 'validate_workflow':
479+
case 'validate_workflow_connections':
480+
case 'validate_workflow_expressions':
481+
validationResult = ToolValidation.validateWorkflow(args);
482+
break;
483+
case 'search_nodes':
484+
validationResult = ToolValidation.validateSearchNodes(args);
485+
break;
486+
case 'list_node_templates':
487+
validationResult = ToolValidation.validateListNodeTemplates(args);
488+
break;
489+
case 'n8n_create_workflow':
490+
validationResult = ToolValidation.validateCreateWorkflow(args);
491+
break;
492+
case 'n8n_get_workflow':
493+
case 'n8n_get_workflow_details':
494+
case 'n8n_get_workflow_structure':
495+
case 'n8n_get_workflow_minimal':
496+
case 'n8n_update_full_workflow':
497+
case 'n8n_delete_workflow':
498+
case 'n8n_validate_workflow':
499+
case 'n8n_get_execution':
500+
case 'n8n_delete_execution':
501+
validationResult = ToolValidation.validateWorkflowId(args);
502+
break;
503+
default:
504+
// For tools not yet migrated to schema validation, use basic validation
505+
return this.validateToolParamsBasic(toolName, args, legacyRequiredParams || []);
506+
}
507+
508+
if (!validationResult.valid) {
509+
const errorMessage = Validator.formatErrors(validationResult, toolName);
510+
logger.error(`Parameter validation failed for ${toolName}:`, errorMessage);
511+
throw new ValidationError(errorMessage);
512+
}
513+
} catch (error) {
514+
// Handle validation errors properly
515+
if (error instanceof ValidationError) {
516+
throw error; // Re-throw validation errors as-is
517+
}
518+
519+
// Handle unexpected errors from validation system
520+
logger.error(`Validation system error for ${toolName}:`, error);
521+
522+
// Provide a user-friendly error message
523+
const errorMessage = error instanceof Error
524+
? `Internal validation error: ${error.message}`
525+
: `Internal validation error while processing ${toolName}`;
526+
527+
throw new Error(errorMessage);
528+
}
529+
}
530+
531+
/**
532+
* Legacy parameter validation (fallback)
533+
*/
534+
private validateToolParamsBasic(toolName: string, args: any, requiredParams: string[]): void {
466535
const missing: string[] = [];
467536

468537
for (const param of requiredParams) {
@@ -619,12 +688,17 @@ export class N8NDocumentationMCPServer {
619688
fix: 'Provide config as an object with node properties'
620689
}],
621690
warnings: [],
622-
suggestions: [],
691+
suggestions: [
692+
'🔧 RECOVERY: Invalid config detected. Fix with:',
693+
' • Ensure config is an object: { "resource": "...", "operation": "..." }',
694+
' • Use get_node_essentials to see required fields for this node type',
695+
' • Check if the node type is correct before configuring it'
696+
],
623697
summary: {
624698
hasErrors: true,
625699
errorCount: 1,
626700
warningCount: 0,
627-
suggestionCount: 0
701+
suggestionCount: 3
628702
}
629703
};
630704
}
@@ -638,7 +712,10 @@ export class N8NDocumentationMCPServer {
638712
nodeType: args.nodeType || 'unknown',
639713
displayName: 'Unknown Node',
640714
valid: false,
641-
missingRequiredFields: ['Invalid config format - expected object']
715+
missingRequiredFields: [
716+
'Invalid config format - expected object',
717+
'🔧 RECOVERY: Use format { "resource": "...", "operation": "..." } or {} for empty config'
718+
]
642719
};
643720
}
644721
return this.validateNodeMinimal(args.nodeType, args.config);
@@ -2141,12 +2218,12 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
21412218
// Get properties
21422219
const properties = node.properties || [];
21432220

2144-
// Extract operation context
2221+
// Extract operation context (safely handle undefined config properties)
21452222
const operationContext = {
2146-
resource: config.resource,
2147-
operation: config.operation,
2148-
action: config.action,
2149-
mode: config.mode
2223+
resource: config?.resource,
2224+
operation: config?.operation,
2225+
action: config?.action,
2226+
mode: config?.mode
21502227
};
21512228

21522229
// Find missing required fields
@@ -2163,7 +2240,7 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
21632240
// Check show conditions
21642241
if (prop.displayOptions.show) {
21652242
for (const [key, values] of Object.entries(prop.displayOptions.show)) {
2166-
const configValue = config[key];
2243+
const configValue = config?.[key];
21672244
const expectedValues = Array.isArray(values) ? values : [values];
21682245

21692246
if (!expectedValues.includes(configValue)) {
@@ -2176,7 +2253,7 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
21762253
// Check hide conditions
21772254
if (isVisible && prop.displayOptions.hide) {
21782255
for (const [key, values] of Object.entries(prop.displayOptions.hide)) {
2179-
const configValue = config[key];
2256+
const configValue = config?.[key];
21802257
const expectedValues = Array.isArray(values) ? values : [values];
21812258

21822259
if (expectedValues.includes(configValue)) {
@@ -2189,8 +2266,8 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
21892266
if (!isVisible) continue;
21902267
}
21912268

2192-
// Check if field is missing
2193-
if (!(prop.name in config)) {
2269+
// Check if field is missing (safely handle null/undefined config)
2270+
if (!config || !(prop.name in config)) {
21942271
missingFields.push(prop.displayName || prop.name);
21952272
}
21962273
}

src/services/enhanced-config-validator.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,19 @@ export class EnhancedConfigValidator extends ConfigValidator {
4545
mode: ValidationMode = 'operation',
4646
profile: ValidationProfile = 'ai-friendly'
4747
): EnhancedValidationResult {
48+
// Input validation - ensure parameters are valid
49+
if (typeof nodeType !== 'string') {
50+
throw new Error(`Invalid nodeType: expected string, got ${typeof nodeType}`);
51+
}
52+
53+
if (!config || typeof config !== 'object') {
54+
throw new Error(`Invalid config: expected object, got ${typeof config}`);
55+
}
56+
57+
if (!Array.isArray(properties)) {
58+
throw new Error(`Invalid properties: expected array, got ${typeof properties}`);
59+
}
60+
4861
// Extract operation context from config
4962
const operationContext = this.extractOperationContext(config);
5063

@@ -190,6 +203,17 @@ export class EnhancedConfigValidator extends ConfigValidator {
190203
config: Record<string, any>,
191204
result: EnhancedValidationResult
192205
): void {
206+
// Type safety check - this should never happen with proper validation
207+
if (typeof nodeType !== 'string') {
208+
result.errors.push({
209+
type: 'invalid_type',
210+
property: 'nodeType',
211+
message: `Invalid nodeType: expected string, got ${typeof nodeType}`,
212+
fix: 'Provide a valid node type string (e.g., "nodes-base.webhook")'
213+
});
214+
return;
215+
}
216+
193217
// First, validate fixedCollection properties for known problematic nodes
194218
this.validateFixedCollectionStructures(nodeType, config, result);
195219

0 commit comments

Comments
 (0)