UNPKG

@grebyn/toolflow-mcp-server

Version:

MCP server for managing other MCP servers - discover, install, organize into bundles, and automate with workflows. Uses StreamableHTTP transport with dual OAuth/API key authentication.

218 lines 9.75 kB
/** * Test MCP Server Tool * Test if an MCP server configuration will actually work using the toolflow CLI */ import { exec } from 'child_process'; import { promisify } from 'util'; const execAsync = promisify(exec); export const testMCPServerTool = { name: 'test_mcp_server', description: 'Test if an MCP server configuration will actually work before adding it to a client. Verifies the server can start and respond correctly. **CRITICAL WORKFLOW: Always test → adjust if needed → test again → only then use write_client_config.** If tests fail and config has env variables, likely cause is missing API keys/tokens.', inputSchema: { type: 'object', properties: { config: { type: 'object', description: 'MCP server configuration to test', properties: { command: { type: 'string', description: 'Command to run the MCP server' }, args: { type: 'array', items: { type: 'string' }, description: 'Command line arguments for the server' }, env: { type: 'object', additionalProperties: { type: 'string' }, description: 'Environment variables for the server' }, type: { type: 'string', enum: ['stdio', 'sse', 'http'], description: 'Transport type for the server' } }, required: ['command', 'args'] }, timeout: { type: 'number', description: 'How long to wait for server startup (milliseconds)', default: 10000, minimum: 1000, maximum: 60000 }, test_type: { type: 'string', enum: ['startup', 'protocol', 'full'], description: 'Type of test to perform: startup (process only), protocol (MCP handshake), full (comprehensive)', default: 'startup' } }, required: ['config'], }, async execute(args, context) { try { // Validate the config if (!args.config.command || !Array.isArray(args.config.args)) { throw new Error('Invalid MCP server configuration: command and args are required'); } const timeout = args.timeout || 10000; const testType = args.test_type || 'startup'; // Prepare the configuration JSON for CLI const configJson = JSON.stringify(args.config); const escapedConfig = configJson.replace(/"/g, '\\"').replace(/\$/g, '\\$'); // Build the CLI command const command = `npx @grebyn/toolflow-cli@latest test "${escapedConfig}" --type=${testType} --timeout=${timeout}`; // Execute the test const startTime = Date.now(); let testResult; try { const { stdout, stderr } = await execAsync(command, { timeout: timeout + 5000, // Add buffer to CLI timeout maxBuffer: 1024 * 1024, // 1MB buffer env: { ...process.env, ...args.config.env // Pass through any env vars needed for the test } }); // Parse the test result from CLI output try { testResult = JSON.parse(stdout); } catch (parseError) { // If not JSON, create a result from the output testResult = { success: !stderr || !stderr.includes('Error'), test_type: testType, startup_time: Date.now() - startTime, output: stdout, error: stderr }; } } catch (error) { const executionTime = Date.now() - startTime; // Check if it's a timeout (which might mean the server is running) if (error.killed && error.signal === 'SIGTERM' && testType === 'startup') { testResult = { success: true, test_type: testType, startup_time: executionTime, output: 'Server appears to be running (did not exit immediately)', recommendations: [ 'Server started successfully and continues running', 'This is normal behavior for MCP servers' ] }; } else { testResult = { success: false, test_type: testType, startup_time: executionTime, error: error.message, output: error.stdout || error.stderr || '' }; } } return { content: [ { type: 'text', text: formatTestResult(testResult, args.config) } ] }; } catch (error) { console.error(`MCP server test failed: ${error.message}`); return { content: [ { type: 'text', text: `**MCP Server Test Failed**\n\nError: ${error.message}\n\nThis indicates an issue with the server configuration or system setup.` } ] }; } } }; /** * Format test result for display */ function formatTestResult(result, config) { const icon = result.success ? '✅' : '❌'; const status = result.success ? 'Passed' : 'Failed'; let output = `${icon} **MCP Server Test ${status}**\n\n`; output += `**Test Type**: ${result.test_type}\n`; if (result.startup_time) { output += `**Test Duration**: ${result.startup_time}ms\n`; } output += `\n`; if (result.success) { output += `🎉 **Server configuration appears to be working correctly!**\n\n`; if (result.output && result.output.trim()) { output += `**Server Output:**\n\`\`\`\n${result.output.trim()}\n\`\`\`\n\n`; } if (result.recommendations && result.recommendations.length > 0) { output += `**Test Results:**\n`; result.recommendations.forEach((rec, index) => { output += `${index + 1}. ${rec}\n`; }); output += `\n`; } output += `**Next Steps:**\n`; output += `1. Use \`write_client_config\` to add this server to your MCP client\n`; output += `2. Restart your MCP client if required\n`; output += `3. Test the server functionality within your MCP client\n`; } else { output += `❌ **Server test failed with the following error:**\n\n`; if (result.error) { output += `**Error**: ${result.error}\n\n`; } if (result.output && result.output.trim()) { output += `**Command Output:**\n\`\`\`\n${result.output.trim()}\n\`\`\`\n\n`; } // Add intelligent hints based on the configuration output += `**Troubleshooting:**\n`; // Check for environment variables if (config.env && Object.keys(config.env).length > 0) { output += `🔑 **Note**: This server uses environment variables\n`; output += `- Ensure all API keys/tokens are properly configured\n`; output += `- Check that environment variables have valid values\n`; } // Common error patterns const errorText = (result.error || '').toLowerCase(); if (errorText.includes('not found') || errorText.includes('command not found')) { output += `- Command '${config.command}' not found in PATH\n`; output += `- Install the required software or package\n`; if (config.command === 'python' || config.command === 'python3') { output += `- Install Python from python.org\n`; } else if (config.command === 'node' || config.command === 'npm' || config.command === 'npx') { output += `- Install Node.js from nodejs.org\n`; } else if (config.command === 'uv') { output += `- Install uv: pip install uv\n`; } } else if (errorText.includes('permission') || errorText.includes('eacces')) { output += `- Permission denied - check file permissions\n`; output += `- Try running with administrator privileges if needed\n`; } else if (errorText.includes('module') || errorText.includes('package')) { output += `- Required dependencies are not installed\n`; output += `- Use run_system_command to install missing packages\n`; } output += `\n**Recovery Steps:**\n`; output += `1. Address the issues mentioned above\n`; output += `2. Use \`run_system_command\` to install missing dependencies\n`; output += `3. Re-test the server configuration after making fixes\n`; } return output; } //# sourceMappingURL=test-mcp-server.js.map