- 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>
381 lines
12 KiB
Markdown
381 lines
12 KiB
Markdown
# 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. |