@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
JavaScript
/**
* 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