Skip to content

Commit a10dabd

Browse files
committed
2 parents 5be7e21 + 7510464 commit a10dabd

10 files changed

Lines changed: 325 additions & 150 deletions

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,4 +420,4 @@ FodyWeavers.xsd
420420

421421
node_modules/*
422422
out/*
423-
.vscode-test/*
423+
.vscode-test/*

README.md

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Let AI agents debug your code inside VS Code — set breakpoints, step through e
66
77
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
88
[![VS Code](https://img.shields.io/badge/VS%20Code-1.104.0+-blue.svg)](https://code.visualstudio.com/)
9-
[![Version](https://img.shields.io/badge/version-1.0.2-green.svg)](https://github.com/microsoft/DebugMCP)
9+
[![Version](https://img.shields.io/badge/version-1.0.8-green.svg)](https://github.com/microsoft/DebugMCP)
1010
[![VS Marketplace](https://img.shields.io/badge/VS%20Marketplace-Install-blue.svg)](https://marketplace.visualstudio.com/items?itemName=ozzafar.debugmcpextension)
1111

1212
> Watch DebugMCP in action — your AI assistant autonomously sets breakpoints, steps through code, and inspects variables directly in VS Code.
@@ -38,21 +38,24 @@ DebugMCP is an MCP server that gives AI coding agents full control over the VS C
3838

3939
### 🔧 Tools
4040

41-
| Tool | Description | Parameters | Example Usage |
42-
|------|-------------|------------|---------------|
43-
| **start_debugging** | Start a debug session for a source code file | `filePath` (required)<br>`workingDirectory` (optional)<br>`configurationName` (optional) | Start debugging a Python script |
44-
| **stop_debugging** | Stop the current debug session | None | End the current debugging session |
45-
| **step_over** | Execute the next line (step over function calls) | None | Move to next line without entering functions |
46-
| **step_into** | Step into function calls | None | Enter function calls to debug them |
47-
| **step_out** | Step out of the current function | None | Exit current function and return to caller |
48-
| **continue_execution** | Continue until next breakpoint | None | Resume execution until hitting a breakpoint |
49-
| **restart_debugging** | Restart the current debug session | None | Restart debugging from the beginning |
50-
| **add_breakpoint** | Add a breakpoint at a specific line | `filePath` (required)<br>`line` (required) | Set breakpoint at line 25 of main.py |
51-
| **remove_breakpoint** | Remove a breakpoint from a specific line | `filePath` (required)<br>`line` (required) | Remove breakpoint from line 25 |
52-
| **list_breakpoints** | List all active breakpoints | None | Show all currently set breakpoints |
53-
| **get_debug_status** | Get current debug session status | None | Check if debugging session is active |
54-
| **get_variables** | Get variables and their values | `scope` (optional: 'local', 'global', 'all') | Inspect local variables |
55-
| **evaluate_expression** | Evaluate an expression in debug context | `expression` (required) | Evaluate `user.name` or `len(items)` |
41+
| Tool | Description | Parameters |
42+
|------|-------------|------------|
43+
| **get_debug_instructions** | Get the debugging guide with best practices and workflow instructions | None |
44+
| **start_debugging** | Start a debug session for a source code file | `fileFullPath` (required)<br>`workingDirectory` (required)<br>`testName` (optional)<br>`configurationName` (optional) |
45+
| **stop_debugging** | Stop the current debug session | None |
46+
| **step_over** | Execute the next line (step over function calls) | None |
47+
| **step_into** | Step into function calls | None |
48+
| **step_out** | Step out of the current function | None |
49+
| **continue_execution** | Continue until next breakpoint | None |
50+
| **restart_debugging** | Restart the current debug session | None |
51+
| **add_breakpoint** | Add a breakpoint at a specific line | `fileFullPath` (required)<br>`lineContent` (required) |
52+
| **remove_breakpoint** | Remove a breakpoint from a specific line | `fileFullPath` (required)<br>`line` (required) |
53+
| **clear_all_breakpoints** | Remove all breakpoints at once | None |
54+
| **list_breakpoints** | List all active breakpoints | None |
55+
| **get_variables_values** | Get variables and their values at current execution point | `scope` (optional: 'local', 'global', 'all') |
56+
| **evaluate_expression** | Evaluate an expression in debug context | `expression` (required) |
57+
58+
> **Note:** The `get_debug_instructions` tool is particularly useful for AI clients like GitHub Copilot that don't support MCP resources. It provides the same debugging guide content that is also available as an MCP resource.
5659
5760
### 🎯 Debugging Best Practices
5861

@@ -111,7 +114,7 @@ DebugMCP supports debugging for the following languages with their respective VS
111114
| **Python** | [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python) | `.py` | ✅ Fully Supported |
112115
| **JavaScript/TypeScript** | Built-in / [JS Debugger](https://marketplace.visualstudio.com/items?itemName=ms-vscode.js-debug) | `.js`, `.ts`, `.jsx`, `.tsx` | ✅ Fully Supported |
113116
| **Java** | [Extension Pack for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack) | `.java` | ✅ Fully Supported |
114-
| **C/C++** | [C/C++](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) | `.c`, `.cpp`, `.h`, `.hpp` | ✅ Fully Supported |
117+
| **C/C++** | [C/C++](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) | `.c`, `.cpp`, `.cc` | ✅ Fully Supported |
115118
| **Go** | [Go](https://marketplace.visualstudio.com/items?itemName=golang.Go) | `.go` | ✅ Fully Supported |
116119
| **Rust** | [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) | `.rs` | ✅ Fully Supported |
117120
| **PHP** | [PHP Debug](https://marketplace.visualstudio.com/items?itemName=xdebug.php-debug) | `.php` | ✅ Fully Supported |
@@ -126,45 +129,47 @@ The extension runs an MCP server automatically. It will pop up a message to auto
126129

127130
### Manual MCP Server Registration (Optional)
128131

132+
> **🔄 Auto-Migration**: If you previously configured DebugMCP with SSE transport, the extension will automatically migrate your configuration to the new Streamable HTTP transport on activation.
133+
129134
#### Cline
130135
Add to your Cline settings or `cline_mcp_settings.json`:
131136
```json
132137
{
133138
"mcpServers": {
134139
"debugmcp": {
135-
"transport": "sse",
136-
"url": "http://localhost:3001/sse",
140+
"type": "streamableHttp",
141+
"url": "http://localhost:3001/mcp",
137142
"description": "DebugMCP - AI-powered debugging assistant"
138143
}
139144
}
140145
}
141146
```
142147

143148
#### GitHub Copilot
144-
Add to your Copilot workspace settings (`.vscode/settings.json`):
149+
Add to your VS Code settings (`settings.json`):
145150
```json
146151
{
147-
"github.copilot.mcp.servers": {
148-
"debugmcp": {
149-
"type": "sse",
150-
"url": "http://localhost:3001/sse",
151-
"description": "DebugMCP - Multi-language debugging support"
152+
"mcp": {
153+
"servers": {
154+
"debugmcp": {
155+
"type": "http",
156+
"url": "http://localhost:3001/mcp",
157+
"description": "DebugMCP - Multi-language debugging support"
158+
}
152159
}
153160
}
154161
}
155162
```
156163

157-
#### Roo Code
158-
Add to Roo's MCP settings:
164+
#### Cursor
165+
Add to Cursor's MCP settings:
159166
```json
160167
{
161-
"mcp": {
162-
"servers": {
163-
"debugmcp": {
164-
"type": "sse",
165-
"url": "http://localhost:3001/sse",
166-
"description": "DebugMCP - Debugging tools for AI assistants"
167-
}
168+
"mcpServers": {
169+
"debugmcp": {
170+
"type": "streamableHttp",
171+
"url": "http://localhost:3001/mcp",
172+
"description": "DebugMCP - Debugging tools for AI assistants"
168173
}
169174
}
170175
}

docs/architecture/debugMCPServer.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,25 @@ The MCP server component that exposes VS Code debugging capabilities to AI agent
66

77
## Motivation
88

9-
AI coding agents need a standardized way to control debuggers programmatically. MCP provides this standard, and `DebugMCPServer` implements it using the official `@modelcontextprotocol/sdk` with Server-Sent Events (SSE) transport over an express HTTP server for real-time bidirectional communication.
9+
AI coding agents need a standardized way to control debuggers programmatically. MCP provides this standard, and `DebugMCPServer` implements it using the official `@modelcontextprotocol/sdk` with Streamable HTTP transport over an express HTTP server.
1010

1111
## Responsibility
1212

1313
- Initialize and manage the MCP server lifecycle (using `McpServer` from `@modelcontextprotocol/sdk`)
1414
- Register debugging tools that AI agents can invoke
1515
- Register documentation resources for agent guidance
1616
- Delegate all debugging operations to `DebuggingHandler`
17-
- Manage SSE transport connections via `SSEServerTransport` on configurable port (default: 3001)
17+
- Manage Streamable HTTP transport via `StreamableHTTPServerTransport` on configurable port (default: 3001)
1818

1919
## Architecture Position
2020

2121
```
2222
AI Agent (MCP Client)
2323
24-
SSE Connection (GET /sse + POST /messages)
24+
HTTP POST /mcp
2525
┌───────────────────┐
2626
│ DebugMCPServer │ ◄── You are here
27-
(express + SSE) │
27+
│ (express + HTTP) │
2828
└───────────────────┘
2929
3030
▼ Delegates to
@@ -38,15 +38,14 @@ AI Agent (MCP Client)
3838
### Tools vs Resources
3939

4040
- **Tools**: Actions the AI can perform (start debugging, step over, etc.)
41-
- **Resources**: Documentation the AI can read for guidance
41+
- **Resources**: Documentation the AI can read for guidance (note: some clients like GitHub Copilot don't support resources, so the `get_debug_instructions` tool is also provided)
4242

43-
### SSE Transport
43+
### Streamable HTTP Transport
4444

45-
Uses HTTP with Server-Sent Events for persistent connections. The express server exposes two endpoints:
46-
- `GET /sse` — Establishes the SSE stream and creates a session
47-
- `POST /messages?sessionId=...` — Receives JSON-RPC messages from the client
45+
Uses stateless HTTP POST requests for MCP communication. The express server exposes:
46+
- `POST /mcp` — Handles all MCP protocol messages (JSON-RPC over HTTP)
4847

49-
Each client connection creates an `SSEServerTransport` instance that is connected to the `McpServer`. The server tracks active transports by session ID and cleans them up on disconnect.
48+
Each request creates a new `StreamableHTTPServerTransport` instance in stateless mode, which is cleaned up when the response closes. This approach is simpler than session-based transports and works well with standard HTTP clients.
5049

5150
## Key Code Locations
5251

@@ -59,6 +58,7 @@ Each client connection creates an `SSEServerTransport` instance that is connecte
5958

6059
| Tool | Description |
6160
|------|-------------|
61+
| `get_debug_instructions` | Get debugging guide (for clients that don't support resources) |
6262
| `start_debugging` | Start a debug session |
6363
| `stop_debugging` | Stop current session |
6464
| `step_over/into/out` | Stepping commands |

package-lock.json

Lines changed: 11 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/debugMCPServer.ts

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
} from '.';
1414
import { logger } from './utils/logger';
1515
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
16-
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
16+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
1717

1818
/**
1919
* Main MCP server class that exposes debugging functionality as tools and resources.
@@ -25,7 +25,7 @@ export class DebugMCPServer {
2525
private port: number;
2626
private initialized: boolean = false;
2727
private debuggingHandler: IDebuggingHandler;
28-
private transports: Map<string, SSEServerTransport> = new Map();
28+
private transports: Map<string, StreamableHTTPServerTransport> = new Map();
2929

3030
constructor(port: number, timeoutInSeconds: number) {
3131
// Initialize the debugging components with dependency injection
@@ -57,6 +57,16 @@ export class DebugMCPServer {
5757
* Setup MCP tools that delegate to the debugging handler
5858
*/
5959
private setupTools() {
60+
// Get debug instructions tool (for clients that don't support MCP resources like GitHub Copilot)
61+
this.mcpServer!.registerTool('get_debug_instructions', {
62+
description: 'Get the debugging guide with step-by-step instructions for effective debugging. ' +
63+
'Returns comprehensive guidance including breakpoint strategies, root cause analysis framework, ' +
64+
'and best practices. Call this before starting a debug session.',
65+
}, async () => {
66+
const content = await this.loadMarkdownFile('agent-resources/debug_instructions.md');
67+
return { content: [{ type: 'text' as const, text: content }] };
68+
});
69+
6070
// Start debugging tool
6171
this.mcpServer!.registerTool('start_debugging', {
6272
description: 'IMPORTANT DEBUGGING TOOL - Start a debug session for a code file' +
@@ -67,13 +77,21 @@ export class DebugMCPServer {
6777
'\n• Functions return incorrect results' +
6878
'\n• Code behaves differently than expected' +
6979
'\n• User reports "it doesn\'t work"' +
70-
'\n\n⚠️ CRITICAL: Before using this tool, first read debugmcp://docs/debug_instructions resource!',
80+
'\n\n⚠️ CRITICAL: Before using this tool, first call get_debug_instructions or read debugmcp://docs/debug_instructions resource!',
7181
inputSchema: {
7282
fileFullPath: z.string().describe('Full path to the source code file to debug'),
7383
workingDirectory: z.string().describe('Working directory for the debug session'),
74-
testName: z.string().optional().describe('Name of the specific test name to debug.'),
84+
testName: z.string().optional().describe(
85+
'Name of a specific test name to debug. ' +
86+
'Only provide this when debugging a single test method. ' +
87+
'Leave empty to debug the entire file or test class.'
88+
),
89+
configurationName: z.string().optional().describe(
90+
'Name of a specific debug configuration from launch.json to use. ' +
91+
'Leave empty to be prompted to select a configuration interactively.'
92+
),
7593
},
76-
}, async (args: { fileFullPath: string; workingDirectory: string; testName?: string }) => {
94+
}, async (args: { fileFullPath: string; workingDirectory: string; testName?: string; configurationName?: string }) => {
7795
const result = await this.debuggingHandler.handleStartDebugging(args);
7896
return { content: [{ type: 'text' as const, text: result }] };
7997
});
@@ -308,29 +326,39 @@ export class DebugMCPServer {
308326
const express = expressModule.default;
309327
const app = express();
310328

311-
// SSE endpoint — clients connect here to establish the MCP session
312-
app.get('/sse', async (req: any, res: any) => {
313-
logger.info('New SSE connection established');
314-
const transport = new SSEServerTransport('/messages', res);
315-
this.transports.set(transport.sessionId, transport);
329+
// Parse JSON body for incoming requests
330+
app.use(express.json());
331+
332+
// Streamable HTTP endpoint — handles MCP protocol messages
333+
app.post('/mcp', async (req: any, res: any) => {
334+
logger.info('New MCP request received');
335+
336+
// Create a new transport for each request (stateless mode)
337+
const transport = new StreamableHTTPServerTransport({
338+
sessionIdGenerator: undefined, // Stateless mode - no session management
339+
});
316340

317-
transport.onclose = () => {
318-
this.transports.delete(transport.sessionId);
319-
logger.info(`SSE transport closed: ${transport.sessionId}`);
320-
};
341+
// Clean up transport when response closes
342+
res.on('close', () => {
343+
transport.close();
344+
logger.info('MCP transport closed');
345+
});
321346

347+
// Connect the MCP server to this transport
322348
await this.mcpServer!.connect(transport);
349+
350+
// Handle the incoming request
351+
await transport.handleRequest(req, res, req.body);
323352
});
324353

325-
// Message endpoint — clients POST JSON-RPC messages here
326-
app.post('/messages', async (req: any, res: any) => {
327-
const sessionId = req.query.sessionId as string;
328-
const transport = this.transports.get(sessionId);
329-
if (!transport) {
330-
res.status(404).json({ error: 'Session not found' });
331-
return;
332-
}
333-
await transport.handlePostMessage(req, res);
354+
// Legacy SSE endpoint for backward compatibility
355+
// Redirects to the new /mcp endpoint with appropriate headers
356+
app.get('/sse', async (req: any, res: any) => {
357+
res.status(410).json({
358+
error: 'SSE endpoint deprecated',
359+
message: 'Please use POST /mcp endpoint instead',
360+
newEndpoint: '/mcp'
361+
});
334362
});
335363

336364
// Start HTTP server
@@ -353,14 +381,8 @@ export class DebugMCPServer {
353381
* Stop the MCP server
354382
*/
355383
async stop() {
356-
// Close all active transports
357-
for (const [sessionId, transport] of this.transports) {
358-
try {
359-
await transport.close();
360-
} catch (error) {
361-
logger.error(`Error closing transport ${sessionId}`, error);
362-
}
363-
}
384+
// Note: With stateless StreamableHTTPServerTransport, transports are closed per-request
385+
// No need to track and close them manually
364386
this.transports.clear();
365387

366388
// Close the HTTP server

0 commit comments

Comments
 (0)