- 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>
388 lines
11 KiB
Markdown
388 lines
11 KiB
Markdown
# MCP Implementation Quick Start Guide
|
|
|
|
## Immediate Actions (Day 1)
|
|
|
|
### 1. Create Essential Properties Configuration
|
|
|
|
Create `src/data/essential-properties.json`:
|
|
```json
|
|
{
|
|
"nodes-base.httpRequest": {
|
|
"required": ["url"],
|
|
"common": ["method", "authentication", "sendBody", "contentType", "sendHeaders"],
|
|
"examples": {
|
|
"minimal": {
|
|
"url": "https://api.example.com/data"
|
|
},
|
|
"getWithAuth": {
|
|
"method": "GET",
|
|
"url": "https://api.example.com/protected",
|
|
"authentication": "genericCredentialType",
|
|
"genericAuthType": "headerAuth"
|
|
},
|
|
"postJson": {
|
|
"method": "POST",
|
|
"url": "https://api.example.com/create",
|
|
"sendBody": true,
|
|
"contentType": "json",
|
|
"jsonBody": "{ \"name\": \"example\" }"
|
|
}
|
|
}
|
|
},
|
|
"nodes-base.webhook": {
|
|
"required": [],
|
|
"common": ["path", "method", "responseMode", "responseData"],
|
|
"examples": {
|
|
"minimal": {
|
|
"path": "webhook",
|
|
"method": "POST"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. Implement get_node_essentials Tool
|
|
|
|
Add to `src/mcp/server.ts`:
|
|
|
|
```typescript
|
|
// Add to tool implementations
|
|
case "get_node_essentials": {
|
|
const { nodeType } = request.params.arguments as { nodeType: string };
|
|
|
|
// Load essential properties config
|
|
const essentialsConfig = require('../data/essential-properties.json');
|
|
const nodeConfig = essentialsConfig[nodeType];
|
|
|
|
if (!nodeConfig) {
|
|
// Fallback: extract from existing data
|
|
const node = await service.getNodeByType(nodeType);
|
|
if (!node) {
|
|
return { error: `Node type ${nodeType} not found` };
|
|
}
|
|
|
|
// Parse properties to find required ones
|
|
const properties = JSON.parse(node.properties_schema || '[]');
|
|
const required = properties.filter((p: any) => p.required);
|
|
const common = properties.slice(0, 5); // Top 5 as fallback
|
|
|
|
return {
|
|
nodeType,
|
|
displayName: node.display_name,
|
|
description: node.description,
|
|
requiredProperties: required.map(simplifyProperty),
|
|
commonProperties: common.map(simplifyProperty),
|
|
examples: {
|
|
minimal: {},
|
|
common: {}
|
|
}
|
|
};
|
|
}
|
|
|
|
// Use configured essentials
|
|
const node = await service.getNodeByType(nodeType);
|
|
const properties = JSON.parse(node.properties_schema || '[]');
|
|
|
|
const requiredProps = nodeConfig.required.map((name: string) => {
|
|
const prop = findPropertyByName(properties, name);
|
|
return prop ? simplifyProperty(prop) : null;
|
|
}).filter(Boolean);
|
|
|
|
const commonProps = nodeConfig.common.map((name: string) => {
|
|
const prop = findPropertyByName(properties, name);
|
|
return prop ? simplifyProperty(prop) : null;
|
|
}).filter(Boolean);
|
|
|
|
return {
|
|
nodeType,
|
|
displayName: node.display_name,
|
|
description: node.description,
|
|
requiredProperties: requiredProps,
|
|
commonProperties: commonProps,
|
|
examples: nodeConfig.examples || {}
|
|
};
|
|
}
|
|
|
|
// Helper functions
|
|
function simplifyProperty(prop: any) {
|
|
return {
|
|
name: prop.name,
|
|
type: prop.type,
|
|
description: prop.description || prop.displayName || '',
|
|
default: prop.default,
|
|
options: prop.options?.map((opt: any) =>
|
|
typeof opt === 'string' ? opt : opt.value
|
|
),
|
|
placeholder: prop.placeholder
|
|
};
|
|
}
|
|
|
|
function findPropertyByName(properties: any[], name: string): any {
|
|
for (const prop of properties) {
|
|
if (prop.name === name) return prop;
|
|
// Check in nested collections
|
|
if (prop.type === 'collection' && prop.options) {
|
|
const found = findPropertyByName(prop.options, name);
|
|
if (found) return found;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
```
|
|
|
|
### 3. Add Tool Definition
|
|
|
|
Add to tool definitions:
|
|
|
|
```typescript
|
|
{
|
|
name: "get_node_essentials",
|
|
description: "Get only essential and commonly-used properties for a node - perfect for quick configuration",
|
|
inputSchema: {
|
|
type: "object",
|
|
properties: {
|
|
nodeType: {
|
|
type: "string",
|
|
description: "The node type (e.g., 'nodes-base.httpRequest')"
|
|
}
|
|
},
|
|
required: ["nodeType"]
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. Create Property Parser Service
|
|
|
|
Create `src/services/property-parser.ts`:
|
|
|
|
```typescript
|
|
export class PropertyParser {
|
|
/**
|
|
* Parse nested properties and flatten to searchable format
|
|
*/
|
|
static parseProperties(properties: any[], path = ''): ParsedProperty[] {
|
|
const results: ParsedProperty[] = [];
|
|
|
|
for (const prop of properties) {
|
|
const currentPath = path ? `${path}.${prop.name}` : prop.name;
|
|
|
|
// Add current property
|
|
results.push({
|
|
name: prop.name,
|
|
path: currentPath,
|
|
type: prop.type,
|
|
description: prop.description || prop.displayName || '',
|
|
required: prop.required || false,
|
|
displayConditions: prop.displayOptions,
|
|
default: prop.default,
|
|
options: prop.options?.filter((opt: any) => typeof opt === 'string' || opt.value)
|
|
});
|
|
|
|
// Recursively parse nested properties
|
|
if (prop.type === 'collection' && prop.options) {
|
|
results.push(...this.parseProperties(prop.options, currentPath));
|
|
} else if (prop.type === 'fixedCollection' && prop.options) {
|
|
for (const option of prop.options) {
|
|
if (option.values) {
|
|
results.push(...this.parseProperties(option.values, `${currentPath}.${option.name}`));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* Find properties matching a search query
|
|
*/
|
|
static searchProperties(properties: ParsedProperty[], query: string): ParsedProperty[] {
|
|
const lowerQuery = query.toLowerCase();
|
|
return properties.filter(prop =>
|
|
prop.name.toLowerCase().includes(lowerQuery) ||
|
|
prop.description.toLowerCase().includes(lowerQuery) ||
|
|
prop.path.toLowerCase().includes(lowerQuery)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Categorize properties
|
|
*/
|
|
static categorizeProperties(properties: ParsedProperty[]): CategorizedProperties {
|
|
const categories: CategorizedProperties = {
|
|
authentication: [],
|
|
request: [],
|
|
response: [],
|
|
advanced: [],
|
|
other: []
|
|
};
|
|
|
|
for (const prop of properties) {
|
|
if (prop.name.includes('auth') || prop.name.includes('credential')) {
|
|
categories.authentication.push(prop);
|
|
} else if (prop.name.includes('body') || prop.name.includes('header') ||
|
|
prop.name.includes('query') || prop.name.includes('url')) {
|
|
categories.request.push(prop);
|
|
} else if (prop.name.includes('response') || prop.name.includes('output')) {
|
|
categories.response.push(prop);
|
|
} else if (prop.path.includes('options.')) {
|
|
categories.advanced.push(prop);
|
|
} else {
|
|
categories.other.push(prop);
|
|
}
|
|
}
|
|
|
|
return categories;
|
|
}
|
|
}
|
|
|
|
interface ParsedProperty {
|
|
name: string;
|
|
path: string;
|
|
type: string;
|
|
description: string;
|
|
required: boolean;
|
|
displayConditions?: any;
|
|
default?: any;
|
|
options?: any[];
|
|
}
|
|
|
|
interface CategorizedProperties {
|
|
authentication: ParsedProperty[];
|
|
request: ParsedProperty[];
|
|
response: ParsedProperty[];
|
|
advanced: ParsedProperty[];
|
|
other: ParsedProperty[];
|
|
}
|
|
```
|
|
|
|
### 5. Quick Test Script
|
|
|
|
Create `scripts/test-essentials.ts`:
|
|
|
|
```typescript
|
|
import { MCPClient } from '../src/mcp/client';
|
|
|
|
async function testEssentials() {
|
|
const client = new MCPClient();
|
|
|
|
console.log('Testing get_node_essentials...\n');
|
|
|
|
// Test HTTP Request node
|
|
const httpEssentials = await client.call('get_node_essentials', {
|
|
nodeType: 'nodes-base.httpRequest'
|
|
});
|
|
|
|
console.log('HTTP Request Essentials:');
|
|
console.log(`- Required: ${httpEssentials.requiredProperties.map(p => p.name).join(', ')}`);
|
|
console.log(`- Common: ${httpEssentials.commonProperties.map(p => p.name).join(', ')}`);
|
|
console.log(`- Total properties: ${httpEssentials.requiredProperties.length + httpEssentials.commonProperties.length}`);
|
|
|
|
// Compare with full response
|
|
const fullInfo = await client.call('get_node_info', {
|
|
nodeType: 'nodes-base.httpRequest'
|
|
});
|
|
|
|
const fullSize = JSON.stringify(fullInfo).length;
|
|
const essentialSize = JSON.stringify(httpEssentials).length;
|
|
|
|
console.log(`\nSize comparison:`);
|
|
console.log(`- Full response: ${(fullSize / 1024).toFixed(1)}KB`);
|
|
console.log(`- Essential response: ${(essentialSize / 1024).toFixed(1)}KB`);
|
|
console.log(`- Reduction: ${((1 - essentialSize / fullSize) * 100).toFixed(1)}%`);
|
|
}
|
|
|
|
testEssentials().catch(console.error);
|
|
```
|
|
|
|
## Day 2-3: Implement search_node_properties
|
|
|
|
```typescript
|
|
case "search_node_properties": {
|
|
const { nodeType, query } = request.params.arguments as {
|
|
nodeType: string;
|
|
query: string;
|
|
};
|
|
|
|
const node = await service.getNodeByType(nodeType);
|
|
if (!node) {
|
|
return { error: `Node type ${nodeType} not found` };
|
|
}
|
|
|
|
const properties = JSON.parse(node.properties_schema || '[]');
|
|
const parsed = PropertyParser.parseProperties(properties);
|
|
const matches = PropertyParser.searchProperties(parsed, query);
|
|
|
|
return {
|
|
query,
|
|
matches: matches.map(prop => ({
|
|
name: prop.name,
|
|
type: prop.type,
|
|
path: prop.path,
|
|
description: prop.description,
|
|
visibleWhen: prop.displayConditions?.show
|
|
})),
|
|
totalMatches: matches.length
|
|
};
|
|
}
|
|
```
|
|
|
|
## Day 4-5: Implement get_node_for_task
|
|
|
|
Create `src/data/task-templates.json`:
|
|
|
|
```json
|
|
{
|
|
"post_json_request": {
|
|
"description": "Make a POST request with JSON data",
|
|
"nodeType": "nodes-base.httpRequest",
|
|
"configuration": {
|
|
"method": "POST",
|
|
"url": "",
|
|
"sendBody": true,
|
|
"contentType": "json",
|
|
"specifyBody": "json",
|
|
"jsonBody": ""
|
|
},
|
|
"userMustProvide": [
|
|
{ "property": "url", "description": "API endpoint URL" },
|
|
{ "property": "jsonBody", "description": "JSON data to send" }
|
|
],
|
|
"optionalEnhancements": [
|
|
{ "property": "authentication", "description": "Add authentication if required" },
|
|
{ "property": "sendHeaders", "description": "Add custom headers" }
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
## Testing Checklist
|
|
|
|
- [ ] Test get_node_essentials with HTTP Request node
|
|
- [ ] Verify size reduction is >90%
|
|
- [ ] Test with Webhook, Agent, and Code nodes
|
|
- [ ] Validate examples work correctly
|
|
- [ ] Test property search functionality
|
|
- [ ] Verify task templates are valid
|
|
- [ ] Check backward compatibility
|
|
- [ ] Measure response times (<100ms)
|
|
|
|
## Success Indicators
|
|
|
|
1. **Immediate (Day 1)**:
|
|
- get_node_essentials returns <5KB for HTTP Request
|
|
- Response includes working examples
|
|
- No errors with top 10 nodes
|
|
|
|
2. **Week 1**:
|
|
- 90% reduction in response size
|
|
- Property search working
|
|
- 5+ task templates created
|
|
- Positive AI agent feedback
|
|
|
|
3. **Month 1**:
|
|
- All tools implemented
|
|
- 50+ nodes optimized
|
|
- Configuration time <1 minute
|
|
- Error rate <10% |