#!/usr/bin/env node
/**
* Simple MCP Client to test the Debugger MCP Server
* This demonstrates how to use the breakpoint management tools
*/
import { spawn } from 'child_process';
import { createInterface } from 'readline';
class MCPClient {
constructor() {
this.requestId = 1;
this.server = null;
this.rl = createInterface({
input: process.stdin,
output: process.stdout
});
}
async start() {
// Check if we're being called with command line arguments for direct tool testing
const args = process.argv.slice(2);
if (args.length >= 1) {
return this.testSpecificTool(args[0], args[1] ? JSON.parse(args[1]) : {});
}
console.log('π Starting MCP Debugger Server Test Client...\n');
// Start the MCP server
this.server = spawn('node', ['dist/index.js'], {
stdio: ['pipe', 'pipe', 'pipe'],
cwd: process.cwd()
});
this.server.stdout.on('data', (data) => {
const lines = data.toString().split('\n').filter(line => line.trim());
lines.forEach(line => {
try {
const message = JSON.parse(line);
this.handleServerMessage(message);
} catch (e) {
// Not JSON, probably a log message
if (line.includes('[INFO]') || line.includes('[ERROR]') || line.includes('[WARN]')) {
console.log(`π Server: ${line}`);
}
}
});
});
this.server.stderr.on('data', (data) => {
console.error(`β Server Error: ${data}`);
});
// Wait a bit for server to start
await new Promise(resolve => setTimeout(resolve, 2000));
// Initialize the MCP connection
await this.initialize();
// Show the interactive menu
this.showMenu();
}
async initialize() {
console.log('π Initializing MCP connection...');
const initRequest = {
jsonrpc: "2.0",
id: this.requestId++,
method: "initialize",
params: {
protocolVersion: "2024-11-05",
capabilities: {
tools: {}
},
clientInfo: {
name: "test-client",
version: "1.0.0"
}
}
};
this.sendMessage(initRequest);
// Send initialized notification
const initializedNotification = {
jsonrpc: "2.0",
method: "notifications/initialized"
};
this.sendMessage(initializedNotification);
}
sendMessage(message) {
const jsonMessage = JSON.stringify(message) + '\n';
this.server.stdin.write(jsonMessage);
}
handleServerMessage(message) {
if (message.method === 'notifications/tools/list_changed') {
console.log('π§ Tools list updated');
} else if (message.result) {
console.log('β
Response:', JSON.stringify(message.result, null, 2));
} else if (message.error) {
console.log('β Error:', JSON.stringify(message.error, null, 2));
}
}
showMenu() {
console.log('\nπ― Debugger MCP Test Menu:');
console.log('1. Get debug session info');
console.log('2. Set a simple breakpoint');
console.log('3. Set a conditional breakpoint');
console.log('4. Set a logpoint');
console.log('5. List all breakpoints');
console.log('6. Get breakpoint analytics');
console.log('7. Clear all breakpoints');
console.log('8. Toggle breakpoints on/off');
console.log('9. Get errors');
console.log('0. Exit');
console.log('\nChoose an option (0-9):');
this.rl.question('> ', (answer) => {
this.handleMenuChoice(answer.trim());
});
}
async handleMenuChoice(choice) {
switch (choice) {
case '1':
await this.getDebugSession();
break;
case '2':
await this.setSimpleBreakpoint();
break;
case '3':
await this.setConditionalBreakpoint();
break;
case '4':
await this.setLogpoint();
break;
case '5':
await this.listBreakpoints();
break;
case '6':
await this.getAnalytics();
break;
case '7':
await this.clearBreakpoints();
break;
case '8':
await this.toggleBreakpoints();
break;
case '9':
await this.getErrors();
break;
case '0':
console.log('π Goodbye!');
this.cleanup();
return;
default:
console.log('β Invalid choice. Please try again.');
}
setTimeout(() => this.showMenu(), 1000);
}
async getDebugSession() {
console.log('\nπ Getting debug session info...');
const request = {
jsonrpc: "2.0",
id: this.requestId++,
method: "tools/call",
params: {
name: "get-debug-session",
arguments: {}
}
};
this.sendMessage(request);
}
async setSimpleBreakpoint() {
console.log('\nπ΄ Setting simple breakpoint...');
const request = {
jsonrpc: "2.0",
id: this.requestId++,
method: "tools/call",
params: {
name: "manage-breakpoints",
arguments: {
action: "set",
location: {
url: "http://localhost:3000/",
lineNumber: 75
},
options: {
enabled: true
}
}
}
};
this.sendMessage(request);
}
async setConditionalBreakpoint() {
console.log('\nπ΄ Setting conditional breakpoint...');
const request = {
jsonrpc: "2.0",
id: this.requestId++,
method: "tools/call",
params: {
name: "manage-breakpoints",
arguments: {
action: "set",
location: {
url: "http://localhost:3000/",
lineNumber: 85
},
options: {
condition: "i > 5",
enabled: true
}
}
}
};
this.sendMessage(request);
}
async setLogpoint() {
console.log('\nπ Setting logpoint...');
const request = {
jsonrpc: "2.0",
id: this.requestId++,
method: "tools/call",
params: {
name: "manage-breakpoints",
arguments: {
action: "set",
location: {
url: "http://localhost:3000/",
lineNumber: 95
},
options: {
logMessage: "Counter value: ${counter}",
enabled: true
}
}
}
};
this.sendMessage(request);
}
async listBreakpoints() {
console.log('\nπ Listing all breakpoints...');
const request = {
jsonrpc: "2.0",
id: this.requestId++,
method: "tools/call",
params: {
name: "manage-breakpoints",
arguments: {
action: "list"
}
}
};
this.sendMessage(request);
}
async getAnalytics() {
console.log('\nπ Getting breakpoint analytics...');
const request = {
jsonrpc: "2.0",
id: this.requestId++,
method: "tools/call",
params: {
name: "manage-breakpoints",
arguments: {
action: "analytics"
}
}
};
this.sendMessage(request);
}
async clearBreakpoints() {
console.log('\nπ§Ή Clearing all breakpoints...');
const request = {
jsonrpc: "2.0",
id: this.requestId++,
method: "tools/call",
params: {
name: "manage-breakpoints",
arguments: {
action: "clear"
}
}
};
this.sendMessage(request);
}
async toggleBreakpoints() {
console.log('\nπ Toggling breakpoints off...');
const request = {
jsonrpc: "2.0",
id: this.requestId++,
method: "tools/call",
params: {
name: "manage-breakpoints",
arguments: {
action: "toggle",
active: false
}
}
};
this.sendMessage(request);
}
async getErrors() {
console.log('\nπ¨ Getting current errors...');
const request = {
jsonrpc: "2.0",
id: this.requestId++,
method: "tools/call",
params: {
name: "get-errors",
arguments: {}
}
};
this.sendMessage(request);
}
cleanup() {
if (this.server) {
this.server.kill();
}
this.rl.close();
process.exit(0);
}
async testSpecificTool(toolName, params) {
console.log(`π§ Testing tool: ${toolName}`);
// Start the MCP server
this.server = spawn('node', ['dist/index.js'], {
stdio: ['pipe', 'pipe', 'pipe'],
cwd: process.cwd()
});
let responseReceived = false;
let result = null;
this.server.stdout.on('data', (data) => {
const lines = data.toString().split('\n').filter(line => line.trim());
lines.forEach(line => {
try {
const message = JSON.parse(line);
if (message.id === 1 && message.result) {
result = message.result;
responseReceived = true;
}
} catch (e) {
// Not JSON, ignore
}
});
});
// Wait for server to start
await new Promise(resolve => setTimeout(resolve, 3000));
// Send initialization
this.sendMessage({
jsonrpc: "2.0",
id: 0,
method: "initialize",
params: {
protocolVersion: "2024-11-05",
capabilities: {},
clientInfo: { name: "test-client", version: "1.0.0" }
}
});
await new Promise(resolve => setTimeout(resolve, 500));
// Send tool call
this.sendMessage({
jsonrpc: "2.0",
id: 1,
method: "tools/call",
params: {
name: toolName,
arguments: params
}
});
// Wait for response
const timeout = 30000; // 30 seconds
const startTime = Date.now();
while (!responseReceived && (Date.now() - startTime) < timeout) {
await new Promise(resolve => setTimeout(resolve, 100));
}
if (responseReceived && result && result.content && result.content[0]) {
try {
const content = JSON.parse(result.content[0].text);
console.log(JSON.stringify(content, null, 2));
} catch (e) {
console.log(result.content[0].text);
}
} else {
console.log('No response received or invalid format');
}
this.server.kill();
process.exit(0);
}
}
// Handle process termination
process.on('SIGINT', () => {
console.log('\nπ Shutting down...');
process.exit(0);
});
// Start the client
const client = new MCPClient();
client.start().catch(console.error);