Files
n8n-mcp/docs/MCP_REVISED_IMPLEMENTATION_STRATEGY.md
czlonkowski 1884d5babf feat: implement AI-optimized MCP tools with 95% size reduction
- Add get_node_essentials tool for 10-20 essential properties only
- Add search_node_properties for targeted property search
- Add get_node_for_task with 14 pre-configured templates
- Add validate_node_config for comprehensive validation
- Add get_property_dependencies for visibility analysis
- Implement PropertyFilter service with curated essentials
- Implement ExampleGenerator with working examples
- Implement TaskTemplates for common workflows
- Implement ConfigValidator with security checks
- Implement PropertyDependencies for dependency analysis
- Enhance property descriptions to 100% coverage
- Add version information to essentials response
- Update documentation with new tools

Response sizes reduced from 100KB+ to <5KB for better AI agent usability.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-16 12:37:45 +02:00

12 KiB

MCP Tools Implementation Strategy (Revised)

Executive Summary

After analyzing the actual n8n-mcp implementation, the core issue isn't data extraction or storage - you already have excellent property extraction with complete schemas stored as JSON. The real problem is information presentation - returning all 200+ properties at once overwhelms AI agents. This revised strategy focuses on intelligent filtering and presentation layers on top of your existing data.

Current System Strengths

  1. Comprehensive property extraction - All properties with types, options, displayOptions, etc.
  2. Efficient storage - JSON columns allow flexibility while maintaining query performance
  3. Complete metadata - Operations, credentials, documentation all properly extracted
  4. Version handling - Supports versioned nodes like HTTPRequest v1/v2/v3

Revised Implementation Approach

Core Principle: Filter, Don't Restructure

Instead of changing how data is stored, we'll add intelligent filtering layers:

// Your current data flow:
n8n source  PropertyExtractor  JSON properties  Database  get_node_info  100KB response

// New data flow:
n8n source  PropertyExtractor  JSON properties  Database  PropertyFilter  Smart Tools  5KB response

Phase 1: Intelligent Property Filtering (Week 1)

1.1 Enhanced get_node_essentials

Implementation - Add to src/mcp/server.ts:

case "get_node_essentials": {
  const { nodeType } = request.params.arguments as { nodeType: string };
  
  const node = await service.getNodeByType(nodeType);
  if (!node) throw new Error(`Node type ${nodeType} not found`);
  
  // Parse existing properties
  const allProperties = JSON.parse(node.properties_schema || '[]');
  
  // Filter to essentials using smart rules
  const essentials = PropertyFilter.getEssentials(allProperties, nodeType);
  
  return {
    nodeType: node.node_type,
    displayName: node.display_name,
    description: node.description,
    requiredProperties: essentials.required,
    commonProperties: essentials.common,
    examples: ExampleGenerator.getExamples(nodeType, essentials),
    totalPropertiesAvailable: allProperties.length,
    operations: JSON.parse(node.operations || '[]')
  };
}

1.2 Create PropertyFilter Service

Create src/services/property-filter.ts:

export class PropertyFilter {
  // Curated lists of essential properties per node type
  private static ESSENTIAL_PROPERTIES: Record<string, EssentialConfig> = {
    'nodes-base.httpRequest': {
      required: ['url'],
      common: ['method', 'authentication', 'sendBody', 'contentType', 'sendHeaders'],
      categoryPriority: ['basic', 'authentication', 'request', 'response', 'advanced']
    },
    'nodes-base.webhook': {
      required: [],
      common: ['httpMethod', 'path', 'responseMode', 'responseData'],
      categoryPriority: ['basic', 'response', 'advanced']
    }
    // Add more nodes...
  };
  
  static getEssentials(properties: any[], nodeType: string): FilteredProperties {
    const config = this.ESSENTIAL_PROPERTIES[nodeType] || this.inferEssentials(properties);
    
    const required = properties.filter(p => 
      config.required.includes(p.name) || p.required === true
    );
    
    const common = properties.filter(p => 
      config.common.includes(p.name) && !required.find(r => r.name === p.name)
    );
    
    // Simplify property structure for AI consumption
    return {
      required: required.map(p => this.simplifyProperty(p)),
      common: common.map(p => this.simplifyProperty(p))
    };
  }
  
  private static simplifyProperty(prop: any): SimplifiedProperty {
    return {
      name: prop.name,
      displayName: prop.displayName,
      type: prop.type,
      description: prop.description || '',
      required: prop.required || false,
      default: prop.default,
      options: prop.options?.map((opt: any) => ({
        value: typeof opt === 'string' ? opt : opt.value,
        label: typeof opt === 'string' ? opt : opt.name
      })),
      // Only include display conditions if simple
      showWhen: this.simplifyDisplayConditions(prop.displayOptions?.show),
      // Add usage hint
      usageHint: this.getUsageHint(prop)
    };
  }
  
  private static inferEssentials(properties: any[]): EssentialConfig {
    // Fallback logic for nodes without curated lists
    const required = properties.filter(p => p.required).map(p => p.name);
    const common = properties
      .filter(p => !p.displayOptions && !p.required)
      .slice(0, 5)
      .map(p => p.name);
    
    return { required, common, categoryPriority: [] };
  }
}

Enhance the existing structure with property search:

case "search_node_properties": {
  const { nodeType, query, category } = request.params.arguments as {
    nodeType: string;
    query: string;
    category?: string;
  };
  
  const node = await service.getNodeByType(nodeType);
  if (!node) throw new Error(`Node type ${nodeType} not found`);
  
  const allProperties = JSON.parse(node.properties_schema || '[]');
  const matches = PropertySearch.search(allProperties, query, category);
  
  return {
    query,
    category,
    matches: matches.map(match => ({
      ...PropertyFilter.simplifyProperty(match.property),
      path: match.path,
      relevanceScore: match.score,
      context: match.context
    })),
    totalMatches: matches.length
  };
}

Phase 2: Configuration Intelligence (Week 2)

2.1 Task-Based Configuration

Create src/services/task-configurator.ts:

export class TaskConfigurator {
  private static TASK_TEMPLATES: Record<string, TaskTemplate> = {
    'post_json_request': {
      nodeType: 'nodes-base.httpRequest',
      description: 'Make a POST request with JSON data',
      configuration: {
        method: 'POST',
        sendBody: true,
        contentType: 'json',
        specifyBody: 'json'
      },
      userMustProvide: ['url', 'jsonBody'],
      conditionalProperties: {
        'sendBody=true': ['contentType', 'specifyBody'],
        'contentType=json': ['jsonBody']
      }
    }
    // More templates...
  };
  
  static getTaskConfiguration(task: string): TaskConfiguration {
    const template = this.TASK_TEMPLATES[task];
    if (!template) throw new Error(`Unknown task: ${task}`);
    
    // Resolve all properties needed for this configuration
    const node = await service.getNodeByType(template.nodeType);
    const allProperties = JSON.parse(node.properties_schema || '[]');
    
    // Get properties mentioned in template
    const relevantProperties = this.extractRelevantProperties(
      allProperties, 
      template.configuration,
      template.conditionalProperties
    );
    
    return {
      task,
      nodeType: template.nodeType,
      description: template.description,
      configuration: template.configuration,
      properties: relevantProperties,
      userMustProvide: template.userMustProvide,
      propertyChain: this.buildPropertyChain(template.conditionalProperties)
    };
  }
}

2.2 Configuration Validator

export class ConfigurationValidator {
  static async validate(nodeType: string, config: any): Promise<ValidationResult> {
    const node = await service.getNodeByType(nodeType);
    const properties = JSON.parse(node.properties_schema || '[]');
    
    const errors: ValidationError[] = [];
    const warnings: ValidationWarning[] = [];
    const suggestions: string[] = [];
    
    // Check required properties
    const requiredProps = properties.filter(p => p.required);
    for (const prop of requiredProps) {
      if (!(prop.name in config)) {
        errors.push({
          type: 'missing_required',
          property: prop.name,
          message: `Required property '${prop.displayName}' is missing`
        });
      }
    }
    
    // Check property visibility
    const visibleProps = this.getVisibleProperties(properties, config);
    const configuredButHidden = Object.keys(config).filter(
      key => !visibleProps.find(p => p.name === key)
    );
    
    if (configuredButHidden.length > 0) {
      warnings.push({
        type: 'hidden_properties',
        message: `Properties ${configuredButHidden.join(', ')} won't be used with current configuration`,
        properties: configuredButHidden
      });
    }
    
    // Smart suggestions based on config
    if (config.method === 'POST' && !config.sendBody) {
      suggestions.push('POST requests typically send a body - consider setting sendBody=true');
    }
    
    return {
      valid: errors.length === 0,
      errors,
      warnings,
      suggestions,
      visibleProperties: visibleProps.map(p => p.name),
      hiddenProperties: properties
        .filter(p => !visibleProps.includes(p))
        .map(p => p.name)
    };
  }
  
  private static getVisibleProperties(properties: any[], config: any): any[] {
    return properties.filter(prop => {
      if (!prop.displayOptions) return true;
      
      // Check show conditions
      if (prop.displayOptions.show) {
        return this.evaluateConditions(prop.displayOptions.show, config);
      }
      
      // Check hide conditions
      if (prop.displayOptions.hide) {
        return !this.evaluateConditions(prop.displayOptions.hide, config);
      }
      
      return true;
    });
  }
}

Phase 3: Advanced Features (Week 3-4)

3.1 Property Resolution Helper

case "resolve_property_visibility": {
  const { nodeType, currentConfig, targetProperty } = request.params.arguments;
  
  const resolver = new PropertyResolver();
  const path = resolver.getPathToProperty(nodeType, currentConfig, targetProperty);
  
  return {
    targetProperty,
    currentlyVisible: path.isVisible,
    requiredChanges: path.changes,
    steps: path.steps,
    alternatives: path.alternatives
  };
}

3.2 Workflow Pattern Analyzer

export class WorkflowPatternAnalyzer {
  // Analyze common patterns from existing workflows
  static async suggestConfiguration(context: {
    previousNode?: string;
    nextNode?: string;
    workflowObjective?: string;
  }): Promise<ConfigurationSuggestion> {
    // Use patterns to suggest optimal configuration
  }
}

Implementation Priority & Timeline

Week 1: Core Filtering

  • Implement PropertyFilter service
  • Create get_node_essentials tool
  • Add curated essential lists for top 20 nodes
  • Implement property search within nodes

Week 2: Intelligence Layer

  • Build TaskConfigurator with 10 common templates
  • Implement ConfigurationValidator
  • Add property visibility resolver
  • Create example generator

Week 3: Testing & Refinement

  • Test with all 525 nodes
  • Refine essential property lists
  • Add more task templates
  • Performance optimization

Week 4: Advanced Features

  • Workflow pattern analysis
  • Context-aware suggestions
  • Property dependency graphs
  • Auto-completion support

Key Differences from Original Strategy

  1. No database schema changes needed - Work with existing JSON structure
  2. Focus on filtering, not restructuring - Properties are already well-structured
  3. Build intelligence layers - Add smart filtering and validation on top
  4. Leverage existing extraction - Don't duplicate the excellent work already done
  5. Progressive enhancement - Each tool adds value independently

Success Metrics

Metric Current Target How to Measure
Properties returned 200+ 10-20 get_node_essentials response
Response size 100KB+ <5KB JSON.stringify().length
Time to find property 30+ seconds <5 seconds Property search tool
Configuration errors 40% <10% Validation success rate
AI success rate Low >90% Successful workflow creation

Next Steps

  1. Implement PropertyFilter with hardcoded essentials for HTTP Request node
  2. Test size reduction with real AI agents
  3. Iterate on essential property lists based on usage
  4. Add task templates for common use cases
  5. Build validation layer to catch errors early

This revised strategy works WITH your existing architecture rather than against it, delivering immediate value while building toward a comprehensive solution.