import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
import * as os from 'os';
import { DebugBridge } from './debugBridge';
export function activate(context: vscode.ExtensionContext) {
console.log('MCP Debugger Tools extension is now active!');
const debugBridge = DebugBridge.getInstance();
// Start monitoring for IPC files from the MCP server
startIPCMonitoring(debugBridge);
// Register commands that will be called by the MCP server
const addBreakpointCommand = vscode.commands.registerCommand(
'vscode-mcp-debugger.addBreakpoint',
async (args: { filePath: string; lineNumber: number; condition?: string }) => {
const success = await debugBridge.addBreakpoint(args.filePath, args.lineNumber, args.condition);
if (success) {
vscode.window.showInformationMessage(`Breakpoint added at ${args.filePath}:${args.lineNumber}`);
} else {
vscode.window.showErrorMessage(`Failed to add breakpoint at ${args.filePath}:${args.lineNumber}`);
}
return success;
}
);
const removeBreakpointCommand = vscode.commands.registerCommand(
'vscode-mcp-debugger.removeBreakpoint',
async (args: { filePath: string; lineNumber: number }) => {
const success = await debugBridge.removeBreakpoint(args.filePath, args.lineNumber);
if (success) {
vscode.window.showInformationMessage(`Breakpoint removed from ${args.filePath}:${args.lineNumber}`);
} else {
vscode.window.showWarningMessage(`No breakpoint found at ${args.filePath}:${args.lineNumber}`);
}
return success;
}
);
const startDebuggingCommand = vscode.commands.registerCommand(
'vscode-mcp-debugger.startDebugging',
async (args: { configurationName?: string; workspaceFolderPath?: string }) => {
const success = await debugBridge.startDebugging(args.configurationName, args.workspaceFolderPath);
if (success) {
vscode.window.showInformationMessage('Debugging started');
} else {
vscode.window.showErrorMessage('Failed to start debugging');
}
return success;
}
);
const stopDebuggingCommand = vscode.commands.registerCommand(
'vscode-mcp-debugger.stopDebugging',
async (args: { sessionId?: string }) => {
const success = await debugBridge.stopDebugging(args.sessionId);
if (success) {
vscode.window.showInformationMessage('Debugging stopped');
} else {
vscode.window.showWarningMessage('No active debugging session to stop');
}
return success;
}
);
// Register the MCP server definition provider
const provider = vscode.lm.registerMcpServerDefinitionProvider('debuggerMcpProvider', {
onDidChangeMcpServerDefinitions: new vscode.EventEmitter<void>().event,
provideMcpServerDefinitions(): vscode.McpServerDefinition[] {
const serverPath = path.join(context.extensionPath, 'out', 'mcpServer.js');
return [{
id: 'vscode-mcp-debugger',
label: 'MCP Debugger Tools',
type: 'stdio',
command: 'node',
args: [serverPath],
cwd: vscode.Uri.file(context.extensionPath),
env: {}
} as vscode.McpStdioServerDefinition];
},
async resolveMcpServerDefinition(definition: vscode.McpServerDefinition): Promise<vscode.McpServerDefinition> {
// No additional resolution needed for our basic server
return definition;
}
});
// Add all subscriptions
context.subscriptions.push(
addBreakpointCommand,
removeBreakpointCommand,
startDebuggingCommand,
stopDebuggingCommand,
provider
);
vscode.window.showInformationMessage('MCP Debugger Tools are now available!');
}
function startIPCMonitoring(debugBridge: DebugBridge) {
const tempDir = os.tmpdir();
// Check for IPC files every 500ms
const intervalId = setInterval(async () => {
try {
const files = fs.readdirSync(tempDir);
const mcpRequestFiles = files.filter(file => file.startsWith('vscode_mcp_') && file.endsWith('.json') && !file.includes('response'));
for (const requestFile of mcpRequestFiles) {
const requestPath = path.join(tempDir, requestFile);
try {
const requestData = fs.readFileSync(requestPath, 'utf8');
const request = JSON.parse(requestData);
// Process the request
const response = await processIPCRequest(request, debugBridge);
// Write the response
if (request.responseFile) {
fs.writeFileSync(request.responseFile, JSON.stringify(response, null, 2));
}
// Clean up the request file
fs.unlinkSync(requestPath);
} catch (error) {
console.error('Error processing IPC request:', error);
// Clean up the request file even on error
try {
fs.unlinkSync(requestPath);
} catch (e) {
// Ignore cleanup errors
}
}
}
} catch (error) {
// Ignore directory read errors
}
}, 500);
// Clean up interval when extension is deactivated
vscode.workspace.onDidChangeConfiguration(() => {
// This is just a placeholder to keep the interval alive
});
}
async function processIPCRequest(request: any, debugBridge: DebugBridge): Promise<any> {
const { command, args } = request;
try {
switch (command) {
case 'addBreakpoint':
const addSuccess = await debugBridge.addBreakpoint(args.filePath, args.lineNumber, args.condition);
return { success: addSuccess };
case 'removeBreakpoint':
const removeSuccess = await debugBridge.removeBreakpoint(args.filePath, args.lineNumber);
return { success: removeSuccess };
case 'startDebugging':
const startSuccess = await debugBridge.startDebugging(args.configurationName, args.workspaceFolderPath);
return { success: startSuccess };
case 'stopDebugging':
const stopSuccess = await debugBridge.stopDebugging(args.sessionId);
return { success: stopSuccess };
default:
return { success: false, error: `Unknown command: ${command}` };
}
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : String(error)
};
}
}
export function deactivate() {
console.log('MCP Debugger Tools extension deactivated');
}