debug-mcp-client.jsโข8.81 kB
#!/usr/bin/env node
/**
* Debug client for testing stdio adapter functionality
* This script tests the stdio-to-SSE adapter bridge
*
* Flow: Client -> stdio -> StdioAdapter -> HTTP/SSE -> MCP Server
*
* Usage:
*
* # Start HTTP/SSE server first (Terminal 1)
* codebase mcp-server --port=3001
*
* # Test stdio adapter (Terminal 2)
* node src/examples/debug-mcp-client.js
* node src/examples/debug-mcp-client.js --server-url=http://localhost:3001/sse
* node src/examples/debug-mcp-client.js --timeout=30000
*
* Arguments:
* --server-url=<url> HTTP server URL (default: http://localhost:3001/sse)
* --timeout=<ms> Request timeout in milliseconds (default: 30000)
* --help, -h Show help message
*/
import { spawn } from 'child_process';
import { EventEmitter } from 'events';
class StdioAdapterTestClient extends EventEmitter {
constructor(options = {}) {
super();
this.requests = new Map();
this.requestId = 0;
this.serverUrl = options.serverUrl || 'http://localhost:3001/sse';
this.timeout = options.timeout || 30000;
}
async startAdapter() {
console.log('๐ Starting Stdio Adapter...');
console.log(`๐ Server URL: ${this.serverUrl}`);
console.log(`โฑ๏ธ Timeout: ${this.timeout}ms`);
console.log('๐ Note: Make sure HTTP/SSE server is running separately');
// Start stdio adapter
this.adapterProcess = spawn('npx', [
'tsx',
'src/index.ts',
'stdio-adapter',
`--server-url=${this.serverUrl}`,
`--timeout=${this.timeout}`
], {
stdio: ['pipe', 'pipe', 'pipe'],
env: { ...process.env }
});
this.adapterProcess.stderr.on('data', (data) => {
console.log('๐ Adapter Log:', data.toString());
});
this.adapterProcess.stdout.on('data', (data) => {
this.handleAdapterMessage(data.toString());
});
this.adapterProcess.on('error', (error) => {
console.error('โ Adapter Error:', error);
});
this.adapterProcess.on('exit', (code) => {
console.log(`๐ Adapter exited with code ${code}`);
});
// Wait a bit for the adapter to start
console.log('โณ Waiting 2000ms for adapter to initialize...');
await new Promise(resolve => setTimeout(resolve, 2000));
return this;
}
handleAdapterMessage(data) {
const lines = data.trim().split('\n');
for (const line of lines) {
try {
// Skip non-JSON lines (logs)
if (!line.startsWith('{')) {
console.log('๐ Adapter Output:', line);
continue;
}
const message = JSON.parse(line);
console.log('๐จ Received:', JSON.stringify(message, null, 2));
if (message.id && this.requests.has(message.id)) {
const { resolve } = this.requests.get(message.id);
this.requests.delete(message.id);
resolve(message);
}
} catch (error) {
console.log('๐ Adapter Output:', line);
}
}
}
async sendRequest(method, params = {}) {
const id = ++this.requestId;
const request = {
jsonrpc: '2.0',
id,
method,
params
};
console.log('๐ค Sending:', JSON.stringify(request, null, 2));
return new Promise((resolve, reject) => {
this.requests.set(id, { resolve, reject });
// Set timeout
setTimeout(() => {
if (this.requests.has(id)) {
this.requests.delete(id);
reject(new Error(`Request ${id} timed out`));
}
}, this.timeout);
this.adapterProcess.stdin.write(JSON.stringify(request) + '\n');
});
}
async testInitialize() {
console.log('\n๐ง Testing initialize...');
try {
const response = await this.sendRequest('initialize', {
protocolVersion: '2024-11-05',
capabilities: {
roots: {
listChanged: true
},
sampling: {}
},
clientInfo: {
name: 'stdio-adapter-test-client',
version: '1.0.0'
}
});
console.log('โ
Initialize response:', response);
return response;
} catch (error) {
console.error('โ Initialize error:', error);
throw error;
}
}
async testListTools() {
console.log('\n๐ ๏ธ Testing tools/list...');
try {
const response = await this.sendRequest('tools/list');
console.log('โ
Tools list:', response);
return response;
} catch (error) {
console.error('โ List tools error:', error);
throw error;
}
}
async testSearchCodebase() {
console.log('\n๐ Testing search_codebase tool...');
try {
const response = await this.sendRequest('tools/call', {
name: 'search_codebase',
arguments: {
query: 'CodeIndexManager',
limit: 3
}
});
console.log('โ
Search result:', response);
return response;
} catch (error) {
console.error('โ Search error:', error);
throw error;
}
}
async testGetStats() {
console.log('\n๐ Testing get_search_stats tool...');
try {
const response = await this.sendRequest('tools/call', {
name: 'get_search_stats',
arguments: {}
});
console.log('โ
Stats result:', response);
return response;
} catch (error) {
console.error('โ Stats error:', error);
throw error;
}
}
async runFullTest() {
try {
await this.testInitialize();
await this.testListTools();
await this.testGetStats();
await this.testSearchCodebase();
console.log('\nโ
All tests completed successfully!');
} catch (error) {
console.error('\nโ Test failed:', error);
}
}
stop() {
if (this.adapterProcess) {
console.log('๐ Stopping adapter...');
this.adapterProcess.kill('SIGTERM');
}
}
}
// Main execution
async function main() {
// Parse command line arguments
const args = process.argv.slice(2);
// Show help if requested
if (args.includes('--help') || args.includes('-h')) {
console.log(`
๐งช Stdio Adapter Test Client
This client tests the stdio-to-SSE adapter functionality.
Flow: Client -> stdio -> StdioAdapter -> HTTP/SSE -> MCP Server
Usage:
node src/examples/debug-mcp-client.js [options]
Options:
--server-url=<url> Full SSE endpoint URL (default: http://localhost:3001/sse)
--timeout=<ms> Request timeout in milliseconds (default: 30000)
--help, -h Show this help message
Setup:
1. Start HTTP/SSE server:
codebase mcp-server --port=3001
2. Test stdio adapter:
node src/examples/debug-mcp-client.js
node src/examples/debug-mcp-client.js --server-url=http://localhost:3001/sse
node src/examples/debug-mcp-client.js --timeout=30000
`);
process.exit(0);
}
console.log('๐งช Stdio Adapter Test Client Starting...');
const serverUrlArg = args.find(arg => arg.startsWith('--server-url='));
const serverUrl = serverUrlArg ? serverUrlArg.split('=')[1] : 'http://localhost:3001/sse';
const timeoutArg = args.find(arg => arg.startsWith('--timeout='));
const timeout = timeoutArg ? parseInt(timeoutArg.split('=')[1], 10) : 30000;
console.log(`๐ Configuration:`);
console.log(` Server URL: ${serverUrl}`);
console.log(` Timeout: ${timeout}ms`);
const client = new StdioAdapterTestClient({ serverUrl, timeout });
// Handle graceful shutdown
process.on('SIGINT', () => {
console.log('\n๐ Shutting down...');
client.stop();
process.exit(0);
});
try {
await client.startAdapter();
await client.runFullTest();
} catch (error) {
console.error('โ Test session failed:', error);
} finally {
client.stop();
process.exit(0);
}
}
main().catch(console.error);