Files
n8n-mcp/scripts/test-essentials-simple.js
czlonkowski 1884d5babf 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>
2025-06-16 12:37:45 +02:00

225 lines
7.0 KiB
JavaScript
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* Simple test script for validating the essentials implementation
* This version runs the MCP server as a subprocess to test real behavior
*/
const { spawn } = require('child_process');
const path = require('path');
const colors = {
reset: '\x1b[0m',
bright: '\x1b[1m',
green: '\x1b[32m',
red: '\x1b[31m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
cyan: '\x1b[36m'
};
function log(message, color = colors.reset) {
console.log(`${color}${message}${colors.reset}`);
}
function runMCPRequest(request) {
return new Promise((resolve, reject) => {
const mcp = spawn('npm', ['start'], {
cwd: path.join(__dirname, '..'),
stdio: ['pipe', 'pipe', 'pipe'],
env: { ...process.env, NODE_ENV: 'production' }
});
let output = '';
let error = '';
let timeout;
// Set timeout
timeout = setTimeout(() => {
mcp.kill();
reject(new Error('Request timed out after 10 seconds'));
}, 10000);
mcp.stdout.on('data', (data) => {
output += data.toString();
});
mcp.stderr.on('data', (data) => {
error += data.toString();
});
mcp.on('close', (code) => {
clearTimeout(timeout);
if (code !== 0) {
reject(new Error(`Process exited with code ${code}: ${error}`));
return;
}
try {
// Parse JSON-RPC response
const lines = output.split('\n');
for (const line of lines) {
if (line.trim() && line.includes('"jsonrpc"')) {
const response = JSON.parse(line);
if (response.result) {
const content = response.result.content[0].text;
resolve(JSON.parse(content));
return;
} else if (response.error) {
reject(new Error(response.error.message));
return;
}
}
}
reject(new Error('No valid response found in output:\n' + output));
} catch (err) {
reject(new Error(`Failed to parse response: ${err.message}\nOutput: ${output}`));
}
});
// Send request
mcp.stdin.write(JSON.stringify(request) + '\n');
mcp.stdin.end();
});
}
async function testEssentials() {
log('\n🚀 Testing n8n MCP Essentials Implementation\n', colors.bright + colors.cyan);
try {
// Test 1: Get node essentials
log('1⃣ Testing get_node_essentials for HTTP Request...', colors.yellow);
const essentialsRequest = {
jsonrpc: '2.0',
method: 'tools/call',
params: {
name: 'get_node_essentials',
arguments: {
nodeType: 'nodes-base.httpRequest'
}
},
id: 1
};
const essentials = await runMCPRequest(essentialsRequest);
log('✅ Success! Got essentials:', colors.green);
log(` Node Type: ${essentials.nodeType}`);
log(` Display Name: ${essentials.displayName}`);
log(` Required properties: ${essentials.requiredProperties?.map(p => p.name).join(', ') || 'None'}`);
log(` Common properties: ${essentials.commonProperties?.map(p => p.name).join(', ') || 'None'}`);
log(` Examples: ${Object.keys(essentials.examples || {}).join(', ')}`);
const essentialsSize = JSON.stringify(essentials).length;
log(` Response size: ${(essentialsSize / 1024).toFixed(1)} KB`, colors.green);
// Test 2: Compare with full node info
log('\n2⃣ Getting full node info for comparison...', colors.yellow);
const fullInfoRequest = {
jsonrpc: '2.0',
method: 'tools/call',
params: {
name: 'get_node_info',
arguments: {
nodeType: 'nodes-base.httpRequest'
}
},
id: 2
};
const fullInfo = await runMCPRequest(fullInfoRequest);
const fullSize = JSON.stringify(fullInfo).length;
log('✅ Got full node info:', colors.green);
log(` Properties count: ${fullInfo.properties?.length || 0}`);
log(` Response size: ${(fullSize / 1024).toFixed(1)} KB`);
const reduction = ((fullSize - essentialsSize) / fullSize * 100).toFixed(1);
log(`\n📊 Size Comparison:`, colors.bright);
log(` Full response: ${(fullSize / 1024).toFixed(1)} KB`);
log(` Essential response: ${(essentialsSize / 1024).toFixed(1)} KB`);
log(` Size reduction: ${reduction}% 🎉`, colors.bright + colors.green);
// Test 3: Search properties
log('\n3⃣ Testing search_node_properties...', colors.yellow);
const searchRequest = {
jsonrpc: '2.0',
method: 'tools/call',
params: {
name: 'search_node_properties',
arguments: {
nodeType: 'nodes-base.httpRequest',
query: 'auth'
}
},
id: 3
};
const searchResults = await runMCPRequest(searchRequest);
log('✅ Search completed:', colors.green);
log(` Query: "${searchResults.query}"`);
log(` Matches found: ${searchResults.totalMatches}`);
if (searchResults.matches && searchResults.matches.length > 0) {
log(' Top matches:');
searchResults.matches.slice(0, 3).forEach(match => {
log(` - ${match.name}: ${match.description || 'No description'}`);
});
}
// Summary
log('\n✨ All tests passed successfully!', colors.bright + colors.green);
log('\n📋 Summary:', colors.bright);
log(` - get_node_essentials works correctly`);
log(` - Size reduction achieved: ${reduction}%`);
log(` - Property search functioning`);
log(` - Examples included in response`);
// Test more nodes
log('\n4⃣ Testing additional nodes...', colors.yellow);
const additionalNodes = ['nodes-base.webhook', 'nodes-base.code', 'nodes-base.postgres'];
const results = [];
for (const nodeType of additionalNodes) {
try {
const req = {
jsonrpc: '2.0',
method: 'tools/call',
params: {
name: 'get_node_essentials',
arguments: { nodeType }
},
id: Math.random()
};
const result = await runMCPRequest(req);
const size = JSON.stringify(result).length;
results.push({
nodeType,
success: true,
propCount: (result.requiredProperties?.length || 0) + (result.commonProperties?.length || 0),
size: (size / 1024).toFixed(1)
});
log(`${nodeType}: ${results[results.length - 1].propCount} properties, ${results[results.length - 1].size} KB`);
} catch (error) {
results.push({ nodeType, success: false, error: error.message });
log(`${nodeType}: ${error.message}`, colors.red);
}
}
log('\n🎯 Implementation validated successfully!', colors.bright + colors.green);
} catch (error) {
log(`\n❌ Test failed: ${error.message}`, colors.red);
if (error.stack) {
log('Stack trace:', colors.red);
log(error.stack, colors.red);
}
process.exit(1);
}
}
// Run the test
testEssentials().catch(error => {
console.error('Unhandled error:', error);
process.exit(1);
});