@@ -28,6 +28,7 @@ import { handleUpdatePartialWorkflow } from './handlers-workflow-diff';
28
28
import { getToolDocumentation , getToolsOverview } from './tools-documentation' ;
29
29
import { PROJECT_VERSION } from '../utils/version' ;
30
30
import { normalizeNodeType , getNodeTypeAlternatives , getWorkflowNodeType } from '../utils/node-utils' ;
31
+ import { ToolValidation , Validator , ValidationError } from '../utils/validation-schemas' ;
31
32
import {
32
33
negotiateProtocolVersion ,
33
34
logProtocolNegotiation ,
@@ -460,9 +461,77 @@ export class N8NDocumentationMCPServer {
460
461
}
461
462
462
463
/**
463
- * Validate required parameters for tool execution
464
+ * Enhanced parameter validation using schemas
464
465
*/
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 {
466
535
const missing : string [ ] = [ ] ;
467
536
468
537
for ( const param of requiredParams ) {
@@ -619,12 +688,17 @@ export class N8NDocumentationMCPServer {
619
688
fix : 'Provide config as an object with node properties'
620
689
} ] ,
621
690
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
+ ] ,
623
697
summary : {
624
698
hasErrors : true ,
625
699
errorCount : 1 ,
626
700
warningCount : 0 ,
627
- suggestionCount : 0
701
+ suggestionCount : 3
628
702
}
629
703
} ;
630
704
}
@@ -638,7 +712,10 @@ export class N8NDocumentationMCPServer {
638
712
nodeType : args . nodeType || 'unknown' ,
639
713
displayName : 'Unknown Node' ,
640
714
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
+ ]
642
719
} ;
643
720
}
644
721
return this . validateNodeMinimal ( args . nodeType , args . config ) ;
@@ -2141,12 +2218,12 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
2141
2218
// Get properties
2142
2219
const properties = node . properties || [ ] ;
2143
2220
2144
- // Extract operation context
2221
+ // Extract operation context (safely handle undefined config properties)
2145
2222
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
2150
2227
} ;
2151
2228
2152
2229
// Find missing required fields
@@ -2163,7 +2240,7 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
2163
2240
// Check show conditions
2164
2241
if ( prop . displayOptions . show ) {
2165
2242
for ( const [ key , values ] of Object . entries ( prop . displayOptions . show ) ) {
2166
- const configValue = config [ key ] ;
2243
+ const configValue = config ?. [ key ] ;
2167
2244
const expectedValues = Array . isArray ( values ) ? values : [ values ] ;
2168
2245
2169
2246
if ( ! expectedValues . includes ( configValue ) ) {
@@ -2176,7 +2253,7 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
2176
2253
// Check hide conditions
2177
2254
if ( isVisible && prop . displayOptions . hide ) {
2178
2255
for ( const [ key , values ] of Object . entries ( prop . displayOptions . hide ) ) {
2179
- const configValue = config [ key ] ;
2256
+ const configValue = config ?. [ key ] ;
2180
2257
const expectedValues = Array . isArray ( values ) ? values : [ values ] ;
2181
2258
2182
2259
if ( expectedValues . includes ( configValue ) ) {
@@ -2189,8 +2266,8 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
2189
2266
if ( ! isVisible ) continue ;
2190
2267
}
2191
2268
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 ) ) {
2194
2271
missingFields . push ( prop . displayName || prop . name ) ;
2195
2272
}
2196
2273
}
0 commit comments