Skip to main content
Glama
mcpServer.ts12.5 kB
#!/usr/bin/env node /** * Standalone MCP server for VSCode debugging tools * This server runs as a separate process and communicates with VSCode via IPC */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, CallToolRequest, } from '@modelcontextprotocol/sdk/types.js'; import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; class StandaloneMcpServer { private server: Server; constructor() { this.server = new Server({ name: 'vscode-mcp-debugger', version: '1.0.0', capabilities: { tools: {}, }, }); this.setupToolHandlers(); } private setupToolHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'add_breakpoint', description: 'Add a breakpoint to a specific file and line number', inputSchema: { type: 'object', properties: { filePath: { type: 'string', description: 'Absolute path to the file where breakpoint should be added' }, lineNumber: { type: 'number', description: 'Line number where breakpoint should be added (1-based)' }, condition: { type: 'string', description: 'Optional condition for the breakpoint' } }, required: ['filePath', 'lineNumber'] } }, { name: 'remove_breakpoint', description: 'Remove a breakpoint from a specific file and line number', inputSchema: { type: 'object', properties: { filePath: { type: 'string', description: 'Absolute path to the file where breakpoint should be removed' }, lineNumber: { type: 'number', description: 'Line number where breakpoint should be removed (1-based)' } }, required: ['filePath', 'lineNumber'] } }, { name: 'start_debugging', description: 'Start debugging with a specific configuration', inputSchema: { type: 'object', properties: { configurationName: { type: 'string', description: 'Name of the debug configuration to use' }, workspaceFolderPath: { type: 'string', description: 'Path to the workspace folder' } }, required: [] } }, { name: 'stop_debugging', description: 'Stop the current debugging session', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Optional session ID to stop specific session' } }, required: [] } } ] }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => { const { name, arguments: args } = request.params; try { switch (name) { case 'add_breakpoint': return await this.addBreakpoint(args); case 'remove_breakpoint': return await this.removeBreakpoint(args); case 'start_debugging': return await this.startDebugging(args); case 'stop_debugging': return await this.stopDebugging(args); default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { return { content: [ { type: 'text', text: `Error executing ${name}: ${error instanceof Error ? error.message : String(error)}` } ] }; } }); } private async sendCommandToExtension(command: string, args: any): Promise<any> { // Create a communication mechanism with the VSCode extension // We'll use a simple file-based IPC for now const tempDir = os.tmpdir(); const requestId = `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const requestFile = path.join(tempDir, `vscode_mcp_${requestId}.json`); const responseFile = path.join(tempDir, `vscode_mcp_response_${requestId}.json`); try { // Write the command request const request = { command, args, responseFile, timestamp: Date.now() }; fs.writeFileSync(requestFile, JSON.stringify(request, null, 2)); // Wait for response (with timeout) const maxWaitTime = 10000; // 10 seconds const startTime = Date.now(); while (Date.now() - startTime < maxWaitTime) { if (fs.existsSync(responseFile)) { const responseData = fs.readFileSync(responseFile, 'utf8'); const response = JSON.parse(responseData); // Clean up files try { fs.unlinkSync(requestFile); fs.unlinkSync(responseFile); } catch (e) { // Ignore cleanup errors } return response; } // Wait a bit before checking again await new Promise(resolve => setTimeout(resolve, 100)); } throw new Error('Timeout waiting for VSCode extension response'); } catch (error) { // Clean up request file on error try { if (fs.existsSync(requestFile)) { fs.unlinkSync(requestFile); } } catch (e) { // Ignore cleanup errors } throw error; } } private async addBreakpoint(args: any) { const { filePath, lineNumber, condition } = args; try { const result = await this.sendCommandToExtension('addBreakpoint', { filePath, lineNumber, condition }); if (result.success) { return { content: [ { type: 'text', text: `Successfully added breakpoint at ${filePath}:${lineNumber}${condition ? ` with condition: ${condition}` : ''}` } ] }; } else { throw new Error(result.error || 'Failed to add breakpoint'); } } catch (error) { throw new Error(`Failed to add breakpoint: ${error instanceof Error ? error.message : String(error)}`); } } private async removeBreakpoint(args: any) { const { filePath, lineNumber } = args; try { const result = await this.sendCommandToExtension('removeBreakpoint', { filePath, lineNumber }); if (result.success) { return { content: [ { type: 'text', text: `Successfully removed breakpoint at ${filePath}:${lineNumber}` } ] }; } else { return { content: [ { type: 'text', text: result.error || `No breakpoint found at ${filePath}:${lineNumber}` } ] }; } } catch (error) { throw new Error(`Failed to remove breakpoint: ${error instanceof Error ? error.message : String(error)}`); } } private async startDebugging(args: any) { const { configurationName, workspaceFolderPath } = args; try { const result = await this.sendCommandToExtension('startDebugging', { configurationName, workspaceFolderPath }); if (result.success) { return { content: [ { type: 'text', text: `Successfully started debugging${configurationName ? ` with configuration: ${configurationName}` : ''}` } ] }; } else { throw new Error(result.error || 'Failed to start debugging'); } } catch (error) { throw new Error(`Failed to start debugging: ${error instanceof Error ? error.message : String(error)}`); } } private async stopDebugging(args: any) { const { sessionId } = args; try { const result = await this.sendCommandToExtension('stopDebugging', { sessionId }); if (result.success) { return { content: [ { type: 'text', text: sessionId ? `Successfully stopped debugging session: ${sessionId}` : 'Successfully stopped active debugging session' } ] }; } else { return { content: [ { type: 'text', text: result.error || 'No active debugging session to stop' } ] }; } } catch (error) { throw new Error(`Failed to stop debugging: ${error instanceof Error ? error.message : String(error)}`); } } public async start() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('MCP Debugger Tools Server started'); // Use stderr for logging } } // Start the server when run as a standalone script if (require.main === module) { const server = new StandaloneMcpServer(); server.start().catch((error) => { console.error('Failed to start MCP server:', 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/davidfirst/vscode-mcp-debugger'

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