Skip to main content
Glama

SAP OData to MCP Server

by Raistlin82
test-server-tools-actual.js13.4 kB
#!/usr/bin/env node /** * Actual Server Tools Test * Tests the actual tools registered by the MCP server (based on server logs) * This test reflects what the server ACTUALLY registers, not what we expect */ import { spawn } from 'child_process'; import chalk from 'chalk'; export class ActualServerToolsTest { constructor(options = {}) { this.verbose = options.verbose || false; this.timeout = options.timeout || 15000; this.results = { passed: 0, failed: 0, tests: [] }; // ACTUAL tools registered by server (from logs analysis) this.actualTools = [ // ===== DISCOVERY TOOLS (4) ===== 'search-sap-services', 'discover-service-entities', 'get-entity-schema', 'execute-entity-operation', // ===== AUTH & ROUTING (2) ===== 'check-sap-authentication', 'sap-smart-query', // ===== AI PHASE 2 (4) ===== 'natural-query-builder', 'smart-data-analysis', 'query-performance-optimizer', 'business-process-insights', // ===== REALTIME PHASE 3 (4) ===== 'realtime-data-stream', 'kpi-dashboard-builder', 'predictive-analytics-engine', 'business-intelligence-insights' ]; this.serverLogMessages = [ '✅ Registered 4 hierarchical discovery tools successfully', '✅ Registered Authentication Check Tool: check-sap-authentication', '✅ Registered Smart Router Tool: sap-smart-query', '✅ Registered 8 AI-Enhanced tools: 4 Phase 2 + 4 Phase 3 Real-time Analytics tools', '🎯 TOTAL TOOLS REGISTERED: 12 (4 Discovery + 4 AI Phase 2 + 4 Real-time Phase 3)' ]; } log(message, type = 'info') { if (!this.verbose && type === 'debug') return; const prefix = { info: chalk.blue('ℹ'), success: chalk.green('✅'), error: chalk.red('❌'), debug: chalk.gray('🔍'), warning: chalk.yellow('⚠️') }[type] || ''; console.log(`${prefix} ${message}`); } async runTest() { this.log('🔍 Testing ACTUAL server tool registration (log-based analysis)', 'info'); this.log(`📊 Expected tools from server logs: ${this.actualTools.length}`, 'info'); try { // Test 1: Server startup and log analysis const startupResult = await this.testServerStartupLogs(); this.recordResult('Server Startup & Log Analysis', startupResult); // Test 2: Tools list extraction from server const toolsListResult = await this.testActualToolsList(); this.recordResult('Actual Tools List Extraction', toolsListResult); return this.results; } catch (error) { this.log(`Test suite failed: ${error.message}`, 'error'); return this.results; } } testServerStartupLogs() { return new Promise((resolve) => { this.log('🚀 Starting server and analyzing logs...', 'debug'); const child = spawn('node', ['dist/mcp-server.js'], { stdio: ['pipe', 'pipe', 'pipe'], env: { ...process.env, NODE_ENV: 'test' } }); let stdout = ''; let stderr = ''; let foundLogMessages = []; const timeout = setTimeout(() => { child.kill(); const foundCount = foundLogMessages.length; const expectedCount = this.serverLogMessages.length; resolve({ success: foundCount >= 3, // At least 3 out of 5 key messages foundLogMessages, stdout: stdout.substring(0, 2000), // Truncate for readability stderr: stderr.substring(0, 1000), foundCount, expectedCount, analysis: this.analyzeServerLogs(stdout, stderr) }); }, this.timeout); child.stdout.on('data', (data) => { stdout += data.toString(); }); child.stderr.on('data', (data) => { stderr += data.toString(); }); child.on('close', () => { clearTimeout(timeout); }); // Check for key log messages const checkLogs = () => { this.serverLogMessages.forEach(message => { if ((stdout + stderr).includes(message) && !foundLogMessages.includes(message)) { foundLogMessages.push(message); this.log(`Found: ${message}`, 'debug'); } }); }; const logCheckInterval = setInterval(checkLogs, 500); setTimeout(() => clearInterval(logCheckInterval), this.timeout); }); } testActualToolsList() { return new Promise((resolve) => { this.log('📋 Extracting actual tools list from server...', 'debug'); const child = spawn('node', ['dist/mcp-server.js'], { stdio: ['pipe', 'pipe', 'pipe'] }); let output = ''; let actualToolsList = []; const timeout = setTimeout(() => { child.kill(); resolve({ success: actualToolsList.length > 0, actualToolsList, expectedCount: this.actualTools.length, foundCount: actualToolsList.length, missingTools: this.actualTools.filter(name => !actualToolsList.includes(name)), extraTools: actualToolsList.filter(name => !this.actualTools.includes(name)) }); }, this.timeout); // Request tools list via MCP protocol const listToolsRequest = { jsonrpc: '2.0', id: 1, method: 'tools/list' }; child.stdin.write(JSON.stringify(listToolsRequest) + '\n'); child.stdout.on('data', (data) => { output += data.toString(); try { const lines = output.split('\n').filter(line => line.trim()); for (const line of lines) { const response = JSON.parse(line); if (response.result && response.result.tools) { actualToolsList = response.result.tools.map(t => t.name); this.log(`Found ${actualToolsList.length} tools in server response`, 'debug'); clearTimeout(timeout); child.kill(); resolve({ success: true, actualToolsList, expectedCount: this.actualTools.length, foundCount: actualToolsList.length, missingTools: this.actualTools.filter(name => !actualToolsList.includes(name)), extraTools: actualToolsList.filter(name => !this.actualTools.includes(name)) }); return; } } } catch (e) { // Continue parsing } }); child.on('close', () => { clearTimeout(timeout); }); }); } analyzeServerLogs(stdout, stderr) { const analysis = { serverStarted: false, toolsRegistered: false, authenticationMode: 'unknown', destinationService: 'unknown', toolCount: 0, errors: [], warnings: [] }; const combinedOutput = stdout + stderr; // Check server startup if (combinedOutput.includes('SAP MCP Server running on stdio')) { analysis.serverStarted = true; } // Check tools registration if (combinedOutput.includes('TOTAL TOOLS REGISTERED')) { analysis.toolsRegistered = true; const match = combinedOutput.match(/TOTAL TOOLS REGISTERED: (\d+)/); if (match) { analysis.toolCount = parseInt(match[1]); } } // Check authentication mode if (combinedOutput.includes('Authentication will be disabled')) { analysis.authenticationMode = 'disabled'; } else if (combinedOutput.includes('PRODUCTION authentication')) { analysis.authenticationMode = 'production'; } // Check destination service if (combinedOutput.includes('Could not find service binding of type \'destination\'')) { analysis.destinationService = 'missing'; } else if (combinedOutput.includes('destination service initialized')) { analysis.destinationService = 'available'; } // Extract errors const errorMatches = combinedOutput.match(/❌[^\\n]+/g); if (errorMatches) { analysis.errors = errorMatches.slice(0, 5); // Limit to 5 errors } // Extract warnings const warningMatches = combinedOutput.match(/⚠️[^\\n]+/g); if (warningMatches) { analysis.warnings = warningMatches.slice(0, 5); // Limit to 5 warnings } return analysis; } recordResult(testName, result) { this.results.tests.push({ name: testName, success: result.success, details: result }); if (result.success) { this.results.passed++; this.log(`✅ ${testName}: PASSED`, 'success'); } else { this.results.failed++; this.log(`❌ ${testName}: FAILED`, 'error'); } } // Generate comprehensive report generateReport() { const report = { summary: { totalTests: this.results.tests.length, passed: this.results.passed, failed: this.results.failed, successRate: Math.round((this.results.passed / this.results.tests.length) * 100) }, toolsAnalysis: { expectedTools: this.actualTools.length, expectedToolsList: this.actualTools }, recommendations: [] }; // Add recommendations based on results if (this.results.failed > 0) { report.recommendations.push('Fix server startup issues before proceeding with tool testing'); report.recommendations.push('Ensure local environment has minimal SAP configuration'); } if (this.results.passed === this.results.tests.length) { report.recommendations.push('Server tool registration is working correctly'); report.recommendations.push('Proceed with comprehensive tool execution testing'); } return report; } } // Main execution if (import.meta.url === `file://${process.argv[1]}`) { const test = new ActualServerToolsTest({ verbose: true }); console.log(chalk.blue.bold('\n🔍 ACTUAL SERVER TOOLS TEST')); console.log(chalk.gray('============================\n')); test.runTest().then(results => { console.log('\n📊 RESULTS SUMMARY:'); console.log(`✅ Passed: ${results.passed}`); console.log(`❌ Failed: ${results.failed}`); console.log(`📈 Total Tests: ${results.tests.length}`); const report = test.generateReport(); console.log(`\n🎯 Success Rate: ${report.summary.successRate}%`); console.log(`📋 Expected Tools: ${report.toolsAnalysis.expectedTools}`); if (report.recommendations.length > 0) { console.log('\n💡 RECOMMENDATIONS:'); report.recommendations.forEach(rec => { console.log(` • ${rec}`); }); } // Show detailed results for failed tests if (results.failed > 0) { console.log('\n❌ FAILED TEST DETAILS:'); results.tests.filter(t => !t.success).forEach(test => { console.log(`\n Test: ${test.name}`); if (test.details.analysis) { console.log(` Server Started: ${test.details.analysis.serverStarted}`); console.log(` Tools Registered: ${test.details.analysis.toolsRegistered}`); console.log(` Tool Count: ${test.details.analysis.toolCount}`); console.log(` Auth Mode: ${test.details.analysis.authenticationMode}`); if (test.details.analysis.errors.length > 0) { console.log(` Errors: ${test.details.analysis.errors.length}`); } } }); } process.exit(results.failed > 0 ? 1 : 0); }).catch(error => { console.error('\n💥 Test suite crashed:', error.message); process.exit(1); }); } export default ActualServerToolsTest;

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/Raistlin82/btp-sap-odata-to-mcp-server-optimized'

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