Skip to main content
Glama
mcp-endpoint-test.cjs13.8 kB
#!/usr/bin/env node /** * MCP Endpoint Test Script * Tests all MCP server endpoints with various parameter combinations * Uses the existing built MCP server via stdio transport */ const { spawn } = require('child_process'); const path = require('path'); const fs = require('fs'); // Test account configurations (you need to set the passwords) const ACCOUNTS = { SAOLA: { name: 'SAOLA', username: 'david+saola@umbrellacost.com', password: process.env.SAOLA_PASSWORD || '' }, ALLCLOUD: { name: 'AllCloud', username: 'david+allcloud@umbrellacost.com', password: process.env.ALLCLOUD_PASSWORD || '' } }; // MSP customer variations const MSP_CUSTOMERS = [ { name: 'Bank Leumi', accountKey: '22676', divisionId: '139' }, { name: 'Bank Hapoalim', accountKey: '16185', divisionId: '1' } ]; // Endpoint test variations const ENDPOINT_TESTS = [ // Cost Analysis { tool: 'api__invoices_caui', name: 'Basic costs - unblended', params: { groupBy: 'none', periodGranLevel: 'month', costType: 'cost,discount', isUnblended: 'true', startDate: '2024-01-01', endDate: '2024-12-31' } }, { tool: 'api__invoices_caui', name: 'Service breakdown - amortized', params: { groupBy: 'service', periodGranLevel: 'month', costType: 'cost,discount', isAmortized: 'true', startDate: '2024-01-01', endDate: '2024-12-31' } }, { tool: 'api__invoices_caui', name: 'EC2 costs only', params: { service: 'Amazon Elastic Compute Cloud', groupBy: 'region', periodGranLevel: 'day', costType: 'cost', isUnblended: 'true', startDate: '2024-11-01', endDate: '2024-11-30' } }, // Budget Management { tool: 'api__budgets_v2_i_', name: 'All budgets metadata', params: { only_metadata: 'true' } }, { tool: 'api__budgets_v2_i_', name: 'AWS budgets only', params: { cloud_context: 'aws', only_metadata: 'true' } }, // Recommendations { tool: 'api__recommendationsNew_heatmap_summary', name: 'Recommendations summary', params: {} }, // User Management { tool: 'api__users', name: 'User information', params: {} }, { tool: 'api__users_plain_sub_users', name: 'Customer divisions', params: {} }, // Anomaly Detection { tool: 'api__anomaly_detection', name: 'Recent anomalies', params: { startDate: '2024-11-01', endDate: '2024-12-31', isFull: 'true' } }, // Service Discovery { tool: 'api__invoices_service_names_distinct', name: 'Available services (limited)', params: { limit: '50' } } ]; class McpTester { constructor() { this.results = []; this.totalTests = 0; this.successCount = 0; this.failCount = 0; this.startTime = Date.now(); } log(message, level = 'info') { const timestamp = new Date().toISOString().split('.')[0]; const emoji = { info: '📋', success: '✅', error: '❌', test: '🧪', warning: '⚠️' }[level] || '📋'; console.log(`${timestamp} ${emoji} ${message}`); } async createMcpClient() { return new Promise((resolve, reject) => { // Start MCP server const serverProcess = spawn('node', ['dist/index.js'], { stdio: ['pipe', 'pipe', 'pipe'], cwd: process.cwd() }); let buffer = ''; const pendingRequests = new Map(); let requestId = 1; let initialized = false; // Handle server responses serverProcess.stdout.on('data', (data) => { buffer += data.toString(); const lines = buffer.split('\n'); buffer = lines.pop() || ''; lines.forEach(line => { if (line.trim()) { try { const response = JSON.parse(line); if (response.id && pendingRequests.has(response.id)) { const { resolve: resolveReq, reject: rejectReq } = pendingRequests.get(response.id); pendingRequests.delete(response.id); if (response.error) { rejectReq(new Error(response.error.message || JSON.stringify(response.error))); } else { resolveReq(response.result); } } } catch (e) { // Ignore malformed JSON } } }); }); // Handle server errors serverProcess.stderr.on('data', (data) => { if (process.env.DEBUG) { console.error('[MCP-SERVER]', data.toString()); } }); const sendRequest = (method, params = {}) => { return new Promise((resolveReq, rejectReq) => { const id = requestId++; const request = { jsonrpc: '2.0', id, method, params }; pendingRequests.set(id, { resolve: resolveReq, reject: rejectReq }); serverProcess.stdin.write(JSON.stringify(request) + '\n'); // Timeout after 30 seconds setTimeout(() => { if (pendingRequests.has(id)) { pendingRequests.delete(id); rejectReq(new Error(`Request timeout for ${method}`)); } }, 30000); }); }; // Initialize connection setTimeout(async () => { try { await sendRequest('initialize', { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'test-client', version: '1.0.0' } }); await sendRequest('initialized'); initialized = true; const client = { callTool: async (name, args) => sendRequest('tools/call', { name, arguments: args }), listTools: async () => sendRequest('tools/list'), close: () => serverProcess.kill() }; resolve(client); } catch (error) { reject(error); } }, 1000); // Handle process exit serverProcess.on('exit', (code) => { if (!initialized) { reject(new Error(`MCP server exited with code ${code}`)); } }); }); } async testAccount(accountName, username, password) { this.log(`\n🏢 Testing ${accountName} account...`); try { // Create MCP client const client = await this.createMcpClient(); // Authenticate this.log(`Authenticating ${accountName}...`); const authResult = await client.callTool('authenticate_user', { username, password }); if (!authResult.content?.[0]?.text?.includes('✅')) { this.log(`Authentication failed for ${accountName}: ${authResult.content?.[0]?.text}`, 'error'); client.close(); return; } this.log(`Authentication successful for ${accountName}`, 'success'); // Test endpoints for (const test of ENDPOINT_TESTS) { await this.testEndpoint(client, accountName, test); // If this is an MSP account (AllCloud), test with different customers if (accountName === 'AllCloud') { for (const customer of MSP_CUSTOMERS) { const customerTest = { ...test, name: `${test.name} (${customer.name})`, params: { ...test.params, customer_account_key: customer.accountKey, customer_division_id: customer.divisionId, userQuery: `Show me ${customer.name} data` } }; await this.testEndpoint(client, accountName, customerTest, customer.name); } } } // Logout await client.callTool('logout', {}); client.close(); } catch (error) { this.log(`Failed to test ${accountName}: ${error.message}`, 'error'); } } async testEndpoint(client, accountName, test, customerName = null) { const testName = `${accountName} - ${test.name}${customerName ? ` (${customerName})` : ''}`; this.log(`Testing: ${testName}`, 'test'); this.totalTests++; try { const startTime = Date.now(); const result = await client.callTool(test.tool, test.params); const duration = Date.now() - startTime; const responseText = result.content?.[0]?.text || ''; const success = !responseText.includes('❌'); const hasData = responseText.includes('```json') || responseText.includes('Results:') || responseText.match(/\d+\s+(items?|results?|recommendations?|budgets?|accounts?)/i); if (success) { this.successCount++; this.log(`✅ ${testName} - ${duration}ms ${hasData ? '(with data)' : '(empty)'}`, 'success'); } else { this.failCount++; const errorMsg = responseText.substring(0, 200); this.log(`❌ ${testName} - ${duration}ms - ${errorMsg}...`, 'error'); } // Store result this.results.push({ account: accountName, customer: customerName || 'Direct', testName: test.name, tool: test.tool, params: test.params, success, hasData, duration, responseLength: responseText.length, error: success ? null : errorMsg, timestamp: new Date().toISOString() }); // Brief pause between tests await new Promise(resolve => setTimeout(resolve, 100)); } catch (error) { this.failCount++; this.log(`❌ ${testName} - Error: ${error.message}`, 'error'); this.results.push({ account: accountName, customer: customerName || 'Direct', testName: test.name, tool: test.tool, params: test.params, success: false, hasData: false, duration: 0, responseLength: 0, error: error.message, timestamp: new Date().toISOString() }); } } async runAllTests() { this.log('🚀 Starting MCP Endpoint Tests', 'info'); // Check for passwords let canTest = false; for (const [key, account] of Object.entries(ACCOUNTS)) { if (account.password) { canTest = true; await this.testAccount(account.name, account.username, account.password); } else { this.log(`Skipping ${account.name} - no password set`, 'warning'); } } if (!canTest) { this.log('No passwords provided. Set environment variables:', 'warning'); this.log(' export SAOLA_PASSWORD="your_password"', 'info'); this.log(' export ALLCLOUD_PASSWORD="your_password"', 'info'); } this.generateReport(); } generateReport() { const duration = Date.now() - this.startTime; this.log('\n📊 TEST SUMMARY', 'info'); this.log(`Total Tests: ${this.totalTests}`, 'info'); this.log(`Successful: ${this.successCount} (${((this.successCount/this.totalTests)*100).toFixed(1)}%)`, 'success'); this.log(`Failed: ${this.failCount} (${((this.failCount/this.totalTests)*100).toFixed(1)}%)`, 'error'); this.log(`Duration: ${(duration/1000).toFixed(1)}s`, 'info'); // Account breakdown this.log('\n📋 ACCOUNT RESULTS:', 'info'); const accountGroups = {}; this.results.forEach(r => { if (!accountGroups[r.account]) { accountGroups[r.account] = { total: 0, success: 0, withData: 0 }; } accountGroups[r.account].total++; if (r.success) accountGroups[r.account].success++; if (r.hasData) accountGroups[r.account].withData++; }); Object.entries(accountGroups).forEach(([account, stats]) => { this.log(`${account}: ${stats.success}/${stats.total} successful (${((stats.success/stats.total)*100).toFixed(1)}%), ${stats.withData} with data`, 'info'); }); // Top successful tests const successful = this.results.filter(r => r.success && r.hasData); if (successful.length > 0) { this.log('\n✅ WORKING ENDPOINTS:', 'success'); successful.slice(0, 10).forEach(r => { this.log(` ${r.tool} (${r.account}${r.customer !== 'Direct' ? ` - ${r.customer}` : ''})`, 'info'); }); } // Failed tests const failed = this.results.filter(r => !r.success); if (failed.length > 0) { this.log('\n❌ FAILED ENDPOINTS:', 'error'); failed.slice(0, 10).forEach(r => { this.log(` ${r.tool} (${r.account}): ${r.error?.substring(0, 100)}`, 'warning'); }); } // Save detailed results const resultsPath = path.join(__dirname, `mcp-test-results-${Date.now()}.json`); fs.writeFileSync(resultsPath, JSON.stringify(this.results, null, 2)); this.log(`\nDetailed results: ${resultsPath}`, 'success'); // Generate CSV const csvHeaders = ['Account', 'Customer', 'Test', 'Tool', 'Success', 'HasData', 'Duration(ms)', 'Error']; const csvRows = this.results.map(r => [ r.account, r.customer, r.testName, r.tool, r.success, r.hasData, r.duration, r.error || '' ]); const csvContent = [csvHeaders.join(','), ...csvRows.map(row => row.map(cell => `"${cell}"`).join(','))].join('\n'); const csvPath = path.join(__dirname, `mcp-test-results-${Date.now()}.csv`); fs.writeFileSync(csvPath, csvContent); this.log(`CSV results: ${csvPath}`, 'success'); } } // Main execution async function main() { console.log('🧪 MCP Endpoint Comprehensive Tester'); console.log('===================================='); const tester = new McpTester(); await tester.runAllTests(); console.log('\n🎉 Testing completed!'); } if (require.main === module) { main().catch(error => { console.error('❌ Test failed:', error); process.exit(1); }); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/daviddraiumbrella/invoice-monitoring'

If you have feedback or need assistance with the MCP directory API, please join our Discord server