Skip to content

Commit ec7f6a0

Browse files
authored
Merge pull request #78 from microsoft/ozzafar/resolve_icms
Harden MCP HTTP endpoint + fix start_debugging hangs for Testing-API runners
2 parents 75ef015 + b94a21b commit ec7f6a0

12 files changed

Lines changed: 1222 additions & 476 deletions

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Let AI agents debug your code inside VS Code - set breakpoints, step through exe
44

55
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
66
[![VS Code](https://img.shields.io/badge/VS%20Code-1.104.0+-blue.svg)](https://code.visualstudio.com/)
7-
[![Version](https://img.shields.io/badge/version-1.1.4-green.svg)](https://github.com/microsoft/DebugMCP)
7+
[![Version](https://img.shields.io/badge/version-1.2.0-green.svg)](https://github.com/microsoft/DebugMCP)
88
[![VS Marketplace](https://img.shields.io/badge/VS%20Marketplace-Install-blue.svg)](https://marketplace.visualstudio.com/items?itemName=ozzafar.debugmcpextension)
99

1010
> **If you find DebugMCP useful, please [star the repo on GitHub](https://github.com/microsoft/DebugMCP)!** It helps others discover the project and motivates continued development.
@@ -274,14 +274,23 @@ Configure DebugMCP behavior in VSCode settings:
274274
```json
275275
{
276276
"debugmcp.serverPort": 3001,
277-
"debugmcp.timeoutInSeconds": 180
277+
"debugmcp.timeoutInSeconds": 180,
278+
"debugmcp.bindHost": ["127.0.0.1", "::1"]
278279
}
279280
```
280281

281282
| Setting | Default | Description |
282283
|---------|---------|-------------|
283284
| `debugmcp.serverPort` | `3001` | Port number for the MCP server |
284285
| `debugmcp.timeoutInSeconds` | `180` | Timeout for debugging operations |
286+
| `debugmcp.bindHost` | `["127.0.0.1", "::1"]` | Network interface(s) the HTTP server binds to. Accepts a string or array of strings. See [Security model](#security-model) before changing. |
287+
288+
### Security model
289+
290+
DebugMCP exposes powerful debugger primitives (`evaluate_expression`, `start_debugging`, …) over an unauthenticated local HTTP endpoint. To keep that surface safe, the server enforces two controls:
291+
292+
1. **Loopback-only bind.** The HTTP server binds to the IPv4 and IPv6 loopback addresses (`127.0.0.1` and `::1`) by default, so other hosts on your network cannot reach `http://<your-ip>:3001/mcp`. Binding both families ensures clients that resolve `localhost` to either family connect successfully. The `debugmcp.bindHost` setting (string or array of strings) lets you opt into a different interface (for example, when forwarding the port into a remote container), but doing so exposes the unauthenticated debugger to anything that can route to that address — do not point it at `0.0.0.0` or a LAN address on an untrusted network.
293+
2. **Host / Origin header validation.** Every request must carry a `Host` header naming a loopback address (`localhost`, `127.0.0.1`, or `[::1]`); any port suffix in the `Host` must also match the server's listening port. Requests with any other `Host` — including those that arrive via DNS rebinding from a malicious webpage — are rejected with HTTP 403. The same loopback check is applied to the `Origin` header when present.
285294

286295

287296
## FAQ

docs/agent-resources/troubleshooting/python.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,29 @@
2727
}
2828
```
2929

30+
## Debugging a Specific Test (pytest / unittest):
31+
32+
DebugMCP dispatches single-test debugging through VS Code's Test Explorer, which requires the Python extension to have **discovered** the test. If discovery hasn't run, `start_debugging` with a `testName` will appear to do nothing (the file opens, the cursor jumps to the test, but no debug session starts).
33+
34+
Discovery requires the test framework to be enabled in workspace settings. Add **one** of the following to `.vscode/settings.json`:
35+
36+
```jsonc
37+
// For pytest:
38+
{
39+
"python.testing.pytestEnabled": true
40+
}
41+
42+
// For unittest:
43+
{
44+
"python.testing.unittestEnabled": true,
45+
"python.testing.unittestArgs": ["-v", "-s", ".", "-p", "test_*.py"]
46+
}
47+
```
48+
49+
Alternatively, run **"Python: Configure Tests"** from the VS Code command palette once — it will write the appropriate settings for you.
50+
51+
Verify discovery worked by opening the Testing view (beaker icon in the sidebar): your tests should appear in the tree. If the tree is empty, the framework isn't configured correctly.
52+
3053
## Debugging Tips:
3154
- Use `print()` statements for quick debugging
3255
- Leverage Python's `pdb` module for command-line debugging

docs/architecture/debugConfigurationManager.md

Lines changed: 30 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,75 +2,63 @@
22

33
## Purpose
44

5-
Manages debug launch configurations by reading from `launch.json` for explicitly named configs, creating defaults when needed, and supporting test-specific debugging across multiple languages and test frameworks.
5+
Produces the argument passed to `vscode.debug.startDebugging()` — either a launch.json configuration name or a minimal `DebugConfiguration` stub.
66

77
## Motivation
88

9-
Different languages and test frameworks require different debug configurations. Rather than forcing AI agents to understand these details, `DebugConfigurationManager` auto-detects the appropriate configuration based on file extension and test framework conventions.
9+
Earlier versions of this class manually parsed `launch.json`, scored configurations, and assembled fully populated per-language config objects. That duplicated work VS Code and the language debug extensions already do better:
10+
11+
- **VS Code** resolves launch.json configurations by name when you pass a string to `startDebugging`.
12+
- **Language extensions** (Python, JS/TS, Java, .NET, Go, …) each register a `DebugConfigurationProvider` whose `resolveDebugConfiguration` hook fills in `cwd`, `console`, `env`, `stopOnEntry`, and other sensible defaults for a minimal stub.
13+
14+
Delegating to those mechanisms keeps this class small and ensures defaults stay aligned with whatever the installed language extensions consider current.
1015

1116
## Responsibility
1217

13-
- Read and parse `.vscode/launch.json` configurations
14-
- Auto-select the most relevant launch configuration for the target file/test
15-
- Respect an explicitly provided `configurationName` when supplied by the agent
16-
- Create default configurations when none exist
17-
- Detect programming language from file extensions
18-
- Generate test-specific configurations for various frameworks
19-
- Validate workspace setup for debugging
18+
- Return a launch.json configuration name when the caller provides one — VS Code looks it up itself.
19+
- Otherwise, return a minimal launch stub (`type`, `request`, `name`, `program`) for the file's language and let the language extension resolve the rest.
20+
- For `.NET` (`coreclr`), locate the project's built DLL since `program` cannot be a `.cs` source file.
21+
- Detect the debugger `type` from a file extension.
22+
23+
**Test debugging is not handled here.** It is routed through `DebuggingExecutor.debugTestAtCursor`, which uses VS Code's built-in `testing.debugAtCursor` command to dispatch to whichever `TestController` owns the test under the cursor. That path supports any language whose extension registers a Test Explorer integration and correctly handles parent/child process attach (e.g. `dotnet test`'s testhost).
2024

2125
## Key Concepts
2226

23-
### Configuration Sources
27+
### Return type
2428

25-
1. **User's launch.json**: Preferred if available
26-
2. **Default Configuration**: Auto-generated based on file extension
27-
3. **Test Configuration**: Special handling for unit test files
29+
`getDebugConfig()` returns `string | vscode.DebugConfiguration`. Both forms are accepted by `vscode.debug.startDebugging(folder, nameOrConfiguration)`.
2830

29-
### Language Detection
31+
### Language detection
3032

31-
Maps file extensions to debug types:
33+
Maps file extensions to debugger `type` values:
3234
- `.py``python`
33-
- `.js/.ts/.jsx/.tsx``node` (pwa-node)
35+
- `.js/.ts/.jsx/.tsx``pwa-node`
3436
- `.java``java`
35-
- `.cs``coreclr`
37+
- `.cs/.csproj``coreclr`
3638
- `.cpp/.cc/.c``cppdbg`
3739
- `.go``go`
3840
- `.rs``lldb`
3941
- `.php``php`
4042
- `.rb``ruby`
4143

42-
### Test Framework Support
44+
### Test framework support
4345

44-
| Language | Frameworks |
45-
|----------|------------|
46-
| Python | unittest |
47-
| Node.js | Jest, Mocha (auto-detected) |
48-
| Java | JUnit |
49-
| .NET | xUnit, NUnit, MSTest |
46+
Test launches are dispatched via `DebuggingExecutor.debugTestAtCursor`, not via this class. Any language with a registered `TestController` is supported (Python unittest/pytest, Jest, Mocha, JUnit, C# Dev Kit, Go, Rust, ...).
5047

51-
### Configuration Selection Flow
48+
### Selection flow
5249

53-
When starting debugging, the manager:
54-
1. Loads available launch.json configurations
55-
2. Scores configurations based on language/type/request/test relevance
56-
3. Selects the best match automatically
57-
4. Falls back to an auto-detected default configuration when needed
50+
1. If `configurationName` is provided and is not the sentinel `Default Configuration`, return that name verbatim.
51+
2. Otherwise, if the file is C# (`coreclr`), walk up to find the `.csproj`, locate its built DLL under `bin/{Debug,Release}/<tfm>/`, and return a coreclr config pointing at that assembly.
52+
3. Otherwise, return `{ type, request: 'launch', name: 'DebugMCP Launch', program: fileFullPath }`.
5853

59-
## Key Code Locations
54+
## Key code locations
6055

6156
- Class definition: `src/utils/debugConfigurationManager.ts`
6257
- Interface: `IDebugConfigurationManager`
63-
- Default configs: `createDefaultDebugConfig()`
64-
- Test configs: `createTestDebugConfig()`
58+
- .NET assembly lookup: `findNearestCsproj()`, `findBuiltAssembly()`, `createDotNetLaunchConfig()`
6559
- Language detection: `detectLanguageFromFilePath()`
66-
- Configuration selection: `selectBestLaunchConfiguration()`
67-
68-
## JSON Parsing
69-
70-
Handles common launch.json quirks:
71-
- Strips comments (`//` and `/* */`)
72-
- Removes trailing commas before `}` or `]`
60+
- Test launches: see `DebuggingExecutor.debugTestAtCursor` in `src/debuggingExecutor.ts`
7361

74-
## Python Test Name Formatting
62+
## Python test name formatting
7563

76-
For Python tests, the manager auto-detects the class name from the test file to build the full test path (`module.ClassName.test_method`). This allows AI agents to specify just the test method name.
64+
Python test name handling now lives in the Python extension's `TestController`; we no longer format `module.ClassName.test_method` ourselves.

package-lock.json

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

package.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "debugmcpextension",
33
"displayName": "DebugMCP",
44
"description": "Let AI agents debug your code inside VS Code — breakpoints, step-through execution, variable inspection, and expression evaluation. Automatically exposes itself as an MCP (Model Context Protocol) server for seamless integration with AI assistants.",
5-
"version": "1.1.4",
5+
"version": "1.2.0",
66
"publisher": "ozzafar",
77
"author": {
88
"name": "Oz Zafar",
@@ -15,6 +15,7 @@
1515
},
1616
"keywords": [
1717
"debug",
18+
"debugmcp",
1819
"mcp",
1920
"debugging",
2021
"ai",
@@ -69,13 +70,19 @@
6970
"properties": {
7071
"debugmcp.timeoutInSeconds": {
7172
"type": "number",
72-
"default": 180,
73+
"default": 300,
7374
"description": "Timeout in seconds"
7475
},
7576
"debugmcp.serverPort": {
7677
"type": "number",
7778
"default": 3001,
7879
"description": "Port number for the DebugMCP server"
80+
},
81+
"debugmcp.bindHost": {
82+
"type": ["string", "array"],
83+
"items": { "type": "string" },
84+
"default": ["127.0.0.1", "::1"],
85+
"markdownDescription": "Network interface(s) the DebugMCP HTTP server binds to. **Defaults to `[\"127.0.0.1\", \"::1\"]` (IPv4 + IPv6 loopback only).** Accepts a single string or an array of strings. ⚠️ **Security warning:** changing this to `0.0.0.0` or a LAN address exposes the unauthenticated MCP debugger — including arbitrary code execution via `evaluate_expression` and `start_debugging` — to every host on the network. Only change this if you fully understand the risk."
7986
}
8087
}
8188
}
@@ -92,7 +99,6 @@
9299
"@modelcontextprotocol/sdk": "^1.26.0",
93100
"@types/express": "^5.0.3",
94101
"express": "^5.2.1",
95-
"jsonc-parser": "^3.3.1",
96102
"zod": "^3.25.76"
97103
},
98104
"devDependencies": {

0 commit comments

Comments
 (0)