mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-03-19 00:43:07 +00:00
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>
This commit is contained in:
381
docs/MCP_REVISED_IMPLEMENTATION_STRATEGY.md
Normal file
381
docs/MCP_REVISED_IMPLEMENTATION_STRATEGY.md
Normal file
@@ -0,0 +1,381 @@
|
||||
# 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<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: [] };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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<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
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```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<ConfigurationSuggestion> {
|
||||
// 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.
|
||||
Reference in New Issue
Block a user