import * as vscode from 'vscode';
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';
/**
* MCP Server that runs directly within the VSCode extension
* This has direct access to VSCode APIs without needing inter-process communication
*/
export class IntegratedMcpServer {
private server: Server;
private transport: StdioServerTransport | undefined;
constructor() {
this.server = new Server({
name: 'vscode-debugger-mcp',
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 addBreakpoint(args: any) {
const { filePath, lineNumber, condition } = args;
try {
const uri = vscode.Uri.file(filePath);
const position = new vscode.Position(lineNumber - 1, 0);
const location = new vscode.Location(uri, position);
const breakpoint = new vscode.SourceBreakpoint(location, true, condition);
vscode.debug.addBreakpoints([breakpoint]);
return {
content: [
{
type: 'text',
text: `Successfully added breakpoint at ${filePath}:${lineNumber}${condition ? ` with condition: ${condition}` : ''}`
}
]
};
} 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 uri = vscode.Uri.file(filePath);
const breakpoints = vscode.debug.breakpoints;
const toRemove = breakpoints.filter((bp: vscode.Breakpoint) => {
if (bp instanceof vscode.SourceBreakpoint) {
return bp.location.uri.fsPath === uri.fsPath &&
bp.location.range.start.line === lineNumber - 1;
}
return false;
});
if (toRemove.length > 0) {
vscode.debug.removeBreakpoints(toRemove);
return {
content: [
{
type: 'text',
text: `Successfully removed breakpoint at ${filePath}:${lineNumber}`
}
]
};
} else {
return {
content: [
{
type: 'text',
text: `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 {
let workspaceFolder: vscode.WorkspaceFolder | undefined;
if (workspaceFolderPath) {
const uri = vscode.Uri.file(workspaceFolderPath);
workspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
} else {
workspaceFolder = vscode.workspace.workspaceFolders?.[0];
}
if (!workspaceFolder) {
throw new Error('No workspace folder found');
}
// Get debug configurations
const configurations = vscode.workspace.getConfiguration('launch', workspaceFolder.uri);
const launchConfig = configurations.get('configurations') as any[];
let debugConfig: any;
if (configurationName) {
debugConfig = launchConfig?.find(config => config.name === configurationName);
if (!debugConfig) {
throw new Error(`Debug configuration '${configurationName}' not found`);
}
} else {
debugConfig = launchConfig?.[0];
if (!debugConfig) {
throw new Error('No debug configurations found');
}
}
const success = await vscode.debug.startDebugging(workspaceFolder, debugConfig);
if (success) {
return {
content: [
{
type: 'text',
text: `Successfully started debugging with configuration: ${debugConfig.name || 'default'}`
}
]
};
} else {
throw new Error('Failed to start debugging session');
}
} 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 {
if (sessionId) {
const session = vscode.debug.activeDebugSession;
if (session && session.id === sessionId) {
await vscode.debug.stopDebugging(session);
return {
content: [
{
type: 'text',
text: `Successfully stopped debugging session: ${sessionId}`
}
]
};
} else {
throw new Error(`Debug session with ID '${sessionId}' not found or not active`);
}
} else {
const session = vscode.debug.activeDebugSession;
if (session) {
await vscode.debug.stopDebugging(session);
return {
content: [
{
type: 'text',
text: 'Successfully stopped active debugging session'
}
]
};
} else {
return {
content: [
{
type: 'text',
text: 'No active debugging session to stop'
}
]
};
}
}
} catch (error) {
throw new Error(`Failed to stop debugging: ${error instanceof Error ? error.message : String(error)}`);
}
}
public start() {
this.transport = new StdioServerTransport();
this.server.connect(this.transport);
console.error('Integrated MCP Debugger Server started');
}
public stop() {
if (this.transport) {
this.transport.close();
this.transport = undefined;
}
console.error('Integrated MCP Debugger Server stopped');
}
}