# 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: ```typescript // 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`: ```typescript 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`: ```typescript export class PropertyFilter { // Curated lists of essential properties per node type private static ESSENTIAL_PROPERTIES: Record = { '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: [] }; } } ``` ### 1.3 Smart Property Search Enhance the existing structure with property search: ```typescript 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`: ```typescript export class TaskConfigurator { private static TASK_TEMPLATES: Record = { '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 ```typescript export class ConfigurationValidator { static async validate(nodeType: string, config: any): Promise { 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 ```typescript 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 ```typescript export class WorkflowPatternAnalyzer { // Analyze common patterns from existing workflows static async suggestConfiguration(context: { previousNode?: string; nextNode?: string; workflowObjective?: string; }): Promise { // Use patterns to suggest optimal configuration } } ``` ## Implementation Priority & Timeline ### Week 1: Core Filtering - [x] Implement PropertyFilter service - [x] Create get_node_essentials tool - [x] Add curated essential lists for top 20 nodes - [x] 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.