Spaces:
Sleeping
Sleeping
| # 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% |