Skip to main content
Glama
tcsenpai

Universal Documentation MCP Server

by tcsenpai
stdioTester.js7.41 kB
#!/usr/bin/env node const { spawn } = require('child_process'); const readline = require('readline'); /** * Simple stdio MCP Tester * Tests MCP servers that use stdio transport */ class StdioMCPTester { constructor() { this.serverProcess = null; this.messageId = 1; this.rl = readline.createInterface({ input: process.stdin, output: process.stdout }); } async startServer(command, args = []) { console.log(`🔗 Starting MCP server: ${command} ${args.join(' ')}`); this.serverProcess = spawn(command, args, { stdio: ['pipe', 'pipe', 'inherit'] }); this.serverProcess.stdout.on('data', (data) => { const lines = data.toString().split('\n').filter(line => line.trim()); lines.forEach(line => { try { const response = JSON.parse(line); this.handleResponse(response); } catch (error) { console.log('📥 Raw output:', line); } }); }); this.serverProcess.on('error', (error) => { console.error('❌ Server process error:', error); }); this.serverProcess.on('exit', (code) => { console.log(`🔚 Server process exited with code ${code}`); }); // Initialize connection await this.sendMessage({ jsonrpc: '2.0', id: this.messageId++, method: 'initialize', params: { protocolVersion: '2024-11-05', capabilities: { tools: {} }, clientInfo: { name: 'stdio-tester', version: '1.0.0' } } }); // Wait a bit for initialization await this.sleep(1000); await this.listTools(); } async sendMessage(message) { if (!this.serverProcess) { console.error('❌ Server not started'); return; } const jsonString = JSON.stringify(message); console.log(`📤 Sending: ${jsonString}`); this.serverProcess.stdin.write(jsonString + '\n'); } handleResponse(response) { console.log('📥 Response:', JSON.stringify(response, null, 2)); // Calculate token estimate const responseText = JSON.stringify(response); const estimatedTokens = Math.ceil(responseText.length / 4); const responseSize = Buffer.byteLength(responseText, 'utf8'); console.log(`📊 Response size: ${responseSize} bytes`); console.log(`🔢 Estimated tokens: ${estimatedTokens}`); console.log(`⚠️ ${estimatedTokens > 25000 ? 'WARNING: Exceeds 25k token limit!' : 'Within token limit'}`); console.log('---'); } async listTools() { await this.sendMessage({ jsonrpc: '2.0', id: this.messageId++, method: 'tools/list' }); } async callTool(name, args = {}) { await this.sendMessage({ jsonrpc: '2.0', id: this.messageId++, method: 'tools/call', params: { name, arguments: args } }); } async interactive() { console.log('\n🎮 Interactive mode - Type commands:'); console.log('Commands:'); console.log(' list - List available tools'); console.log(' call <tool_name> [json_args] - Call a tool'); console.log(' search <query> - Quick search'); console.log(' test-pagination - Test pagination'); console.log(' test-tokens - Test token limits'); console.log(' quit - Exit'); console.log(); while (true) { const input = await this.prompt('stdio-tester> '); const [command, ...args] = input.trim().split(' '); try { switch (command.toLowerCase()) { case 'list': await this.listTools(); break; case 'call': if (args.length < 1) { console.log('Usage: call <tool_name> [json_args]'); break; } const toolName = args[0]; const jsonArgs = args.slice(1).join(' '); let parsedArgs = {}; if (jsonArgs) { try { parsedArgs = JSON.parse(jsonArgs); } catch { console.log('Invalid JSON arguments. Using empty args.'); } } await this.callTool(toolName, parsedArgs); break; case 'search': if (args.length === 0) { console.log('Usage: search <query>'); break; } await this.callTool('search_content', { query: args.join(' ') }); break; case 'test-pagination': await this.testPagination(); break; case 'test-tokens': await this.testTokenLimits(); break; case 'quit': case 'exit': console.log('👋 Goodbye!'); return; default: console.log('Unknown command. Type "quit" to exit.'); } } catch (error) { console.error('❌ Command failed:', error); } console.log(); // Add spacing } } async testPagination() { console.log('🧪 Testing pagination...'); // First call with small limit console.log('\n1️⃣ Making initial search call...'); await this.callTool('search_content', { query: 'api', limit: 5 }); // Wait for response, then manually continue console.log('\n💡 If you see a continuation_token in the response above,'); console.log(' copy it and run: call search_content {"continuation_token":"<token>"}'); } async testTokenLimits() { console.log('🧪 Testing token limits...'); const testCases = [ { name: 'Small search', tool: 'search_content', args: { query: 'test', limit: 5 } }, { name: 'Large search', tool: 'search_content', args: { query: 'documentation', limit: 50 } }, { name: 'Get page outline', tool: 'get_page_outline', args: { path: '/' } }, { name: 'List sections', tool: 'list_sections', args: {} } ]; for (const testCase of testCases) { console.log(`\n🔍 Testing: ${testCase.name}`); await this.callTool(testCase.tool, testCase.args); await this.sleep(2000); // Wait between tests } } prompt(question) { return new Promise((resolve) => { this.rl.question(question, resolve); }); } sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async cleanup() { if (this.serverProcess) { this.serverProcess.kill(); } this.rl.close(); } } // CLI usage async function main() { const args = process.argv.slice(2); if (args.length === 0) { console.log('🧪 stdio MCP Tester'); console.log('Usage: node stdioTester.js <server_command> [server_args...]'); console.log('Example: node stdioTester.js node dist/index.js'); console.log('Example: node stdioTester.js npm start'); process.exit(1); } const tester = new StdioMCPTester(); // Handle cleanup on exit process.on('SIGINT', async () => { console.log('\n🛑 Cleaning up...'); await tester.cleanup(); process.exit(0); }); try { await tester.startServer(args[0], args.slice(1)); await tester.interactive(); } catch (error) { console.error('💥 Tester failed:', error); process.exit(1); } finally { await tester.cleanup(); } } if (require.main === module) { main().catch(console.error); }

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/tcsenpai/mcpbook'

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