Skip to content

Commit 05b83de

Browse files
Host MCP server in-process with auto-config and release workflow
- Embed MCP server directly in the extension using Streamable HTTP transport at /mcp, eliminating the need for a separate stdio proxy process - Auto-update ~/.claude.json on activation for zero-config Claude Code setup - Add sidebar panel showing server status, port, and Claude config state - Add IDebugClient interface so tools work with both in-process and stdio paths - Add GitHub Actions workflow to build .vsix and create releases on tag push - Bump version to 0.2.0
1 parent bbe63ca commit 05b83de

19 files changed

+853
-75
lines changed

.github/workflows/release.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags: ['v*']
6+
7+
permissions:
8+
contents: write
9+
10+
jobs:
11+
release:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
- uses: actions/setup-node@v4
16+
with:
17+
node-version: 20
18+
cache: npm
19+
- run: npm ci
20+
- run: npm run package
21+
- name: Create GitHub Release
22+
uses: softprops/action-gh-release@v2
23+
with:
24+
files: '*.vsix'
25+
generate_release_notes: true

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
node_modules/
22
dist/
33
.vscode-test/
4+
*.vsix

README.md

Lines changed: 37 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,38 @@ A VS Code extension that exposes an MCP (Model Context Protocol) server, allowin
66

77
- **22 MCP tools** for full debugger control
88
- **Works with any debugger** that implements the Debug Adapter Protocol
9+
- **Zero-config for Claude Code** — auto-updates `~/.claude.json` on activation
910
- **Execution commands block until the debugger stops** and return the stop location + local variables in a single response
1011
- **Captures program output** (stdout/stderr) for inspection
11-
- **Multi-window support** — each VS Code window runs its own bridge
1212

1313
## Architecture
1414

1515
```
16-
Claude Code CLI / Desktop
17-
| (stdio: JSON-RPC)
16+
Claude Code / Desktop / any MCP client
17+
| (HTTP: Streamable HTTP transport)
1818
v
19-
MCP Server Process (dist/mcp-server.js)
20-
| (HTTP to localhost:<port>)
21-
v
22-
VS Code Extension (dist/extension.js)
19+
VS Code Extension (MCP server on localhost:<port>/mcp)
2320
| (vscode.debug.* API)
2421
v
2522
Debug Adapter Protocol → Any Debugger
2623
```
2724

28-
The extension starts a localhost HTTP server on a random port with a random auth token, and writes both to a temporary port file. The MCP server (spawned by the AI client via stdio) reads this file to communicate with the extension.
25+
The extension hosts the MCP server directly inside the VS Code process — no separate server or sidecar needed. On activation it starts a localhost HTTP server (default port `45557`) and automatically writes the connection URL to `~/.claude.json`.
2926

3027
## Installation
3128

3229
This extension is not yet published to the VS Code Marketplace. Install it manually from the `.vsix` file.
3330

3431
### 1. Download the `.vsix`
3532

36-
Download `vscode-debugger-mcp-0.1.0.vsix` from this repository (it's in the repo root).
33+
Download `vscode-debugger-mcp-0.2.0.vsix` from this repository (it's in the repo root).
3734

3835
### 2. Install in VS Code
3936

4037
**Option A — Command line:**
4138

4239
```bash
43-
code --install-extension vscode-debugger-mcp-0.1.0.vsix
40+
code --install-extension vscode-debugger-mcp-0.2.0.vsix
4441
```
4542

4643
**Option B — VS Code UI:**
@@ -53,7 +50,7 @@ code --install-extension vscode-debugger-mcp-0.1.0.vsix
5350

5451
### 3. Reload VS Code
5552

56-
After installation, reload the window (`Ctrl+Shift+P` / `Cmd+Shift+P`**Developer: Reload Window**). The extension activates automatically on startup.
53+
After installation, reload the window (`Ctrl+Shift+P` / `Cmd+Shift+P`**Developer: Reload Window**). The extension activates automatically on startup and the MCP server begins listening.
5754

5855
### From source (development)
5956

@@ -68,30 +65,22 @@ Then press `F5` in VS Code to launch the Extension Development Host.
6865

6966
## MCP Client Configuration
7067

71-
After installing the `.vsix`, the MCP server script is located inside the installed extension directory. The path depends on your setup:
68+
### Claude Code (zero-config)
7269

73-
| Setup | Extensions path |
74-
|-------|----------------|
75-
| VS Code (local) | `~/.vscode/extensions/` |
76-
| VS Code Remote / WSL | `~/.vscode-server/extensions/` |
77-
| Windows (local) | `%USERPROFILE%\.vscode\extensions\` |
70+
The extension automatically registers itself in `~/.claude.json` on activation. **No manual setup needed** — just install the extension, reload VS Code, and start using Claude Code.
7871

79-
The full path to the MCP server will be:
72+
To verify it's configured, run:
8073

81-
```
82-
<extensions-path>/debugger-mcp.vscode-debugger-mcp-0.1.0/dist/mcp-server.js
74+
```bash
75+
claude mcp list
8376
```
8477

85-
Use this path when configuring your MCP client below.
78+
You should see `vscode-debugger` listed as an HTTP server.
8679

87-
### Claude Code
80+
If you prefer to configure manually, or if auto-config is disabled:
8881

8982
```bash
90-
# Local VS Code
91-
claude mcp add vscode-debugger node ~/.vscode/extensions/debugger-mcp.vscode-debugger-mcp-0.1.0/dist/mcp-server.js
92-
93-
# VS Code Remote / WSL
94-
claude mcp add vscode-debugger node ~/.vscode-server/extensions/debugger-mcp.vscode-debugger-mcp-0.1.0/dist/mcp-server.js
83+
claude mcp add --transport http vscode-debugger http://localhost:45557/mcp
9584
```
9685

9786
### Claude Desktop
@@ -102,10 +91,8 @@ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_
10291
{
10392
"mcpServers": {
10493
"vscode-debugger": {
105-
"command": "node",
106-
"args": [
107-
"~/.vscode/extensions/debugger-mcp.vscode-debugger-mcp-0.1.0/dist/mcp-server.js"
108-
]
94+
"type": "http",
95+
"url": "http://localhost:45557/mcp"
10996
}
11097
}
11198
}
@@ -119,15 +106,24 @@ Add to `.vscode/mcp.json` in your workspace:
119106
{
120107
"servers": {
121108
"vscode-debugger": {
122-
"command": "node",
123-
"args": [
124-
"~/.vscode/extensions/debugger-mcp.vscode-debugger-mcp-0.1.0/dist/mcp-server.js"
125-
]
109+
"type": "http",
110+
"url": "http://localhost:45557/mcp"
126111
}
127112
}
128113
}
129114
```
130115

116+
### Alternative: stdio transport
117+
118+
A standalone MCP server process is also available for clients that only support stdio transport. See the extension's `dist/mcp-server.js` — it discovers the running extension via a port file and proxies MCP requests over HTTP.
119+
120+
## Settings
121+
122+
| Setting | Default | Description |
123+
|---------|---------|-------------|
124+
| `debuggerMcp.port` | `45557` | Preferred port for the MCP server. Falls back to a random port if taken. |
125+
| `debuggerMcp.autoConfigureClaude` | `true` | Automatically update `~/.claude.json` with the MCP server URL on activation. |
126+
131127
## Tools
132128

133129
### Session Management
@@ -189,19 +185,18 @@ Add to `.vscode/mcp.json` in your workspace:
189185

190186
## How It Works
191187

192-
1. **Extension activates** on VS Code startup and starts a localhost HTTP server on a random port
193-
2. **Port file** is written to the OS temp directory containing `{ port, authToken, pid, workspaceFolder, timestamp }`
194-
3. **MCP server** (spawned by the AI client) reads the port file, validates it (PID check, timestamp freshness), and sends authenticated HTTP requests
195-
4. **Debug commands** are forwarded to the VS Code debug API
188+
1. **Extension activates** on VS Code startup and starts an HTTP server on `localhost:45557`
189+
2. **MCP endpoint** at `/mcp` uses the Streamable HTTP transport protocol — each client gets its own session
190+
3. **Auto-config** writes the server URL to `~/.claude.json` so Claude Code discovers it automatically
191+
4. **Debug commands** are forwarded to the VS Code debug API via an in-process `DirectClient`
196192
5. **DAP message interception** captures stopped events, program output, and exit codes in real-time
197193
6. **Execution commands** (continue, step) block until the debugger pauses, then automatically gather the stop location and local variables
198194

199195
## Security
200196

201197
- HTTP server binds to `127.0.0.1` only (no network exposure)
202-
- Random UUID auth token required on every request
203-
- Token is shared only through a local temp file
204-
- Port file includes PID for stale-file detection
198+
- MCP endpoint uses the standard Streamable HTTP transport protocol
199+
- Port file (for legacy stdio transport) includes random auth token and PID for stale-file detection
205200

206201
## License
207202

package.json

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "vscode-debugger-mcp",
33
"displayName": "Debugger MCP",
44
"description": "MCP server for AI-agent-controlled debugging via VS Code's Debug Adapter Protocol",
5-
"version": "0.1.0",
5+
"version": "0.2.0",
66
"publisher": "debugger-mcp",
77
"license": "MIT",
88
"engines": {
@@ -21,8 +21,45 @@
2121
{
2222
"command": "vscode-debugger-mcp.showPort",
2323
"title": "Debugger MCP: Show Server Port"
24+
},
25+
{
26+
"command": "vscode-debugger-mcp.refreshClaudeConfig",
27+
"title": "Debugger MCP: Update Claude Config"
28+
}
29+
],
30+
"viewsContainers": {
31+
"activitybar": [
32+
{
33+
"id": "debuggerMcp",
34+
"title": "Debugger MCP",
35+
"icon": "$(bug)"
36+
}
37+
]
38+
},
39+
"views": {
40+
"debuggerMcp": [
41+
{
42+
"type": "webview",
43+
"id": "debuggerMcp.statusView",
44+
"name": "Status"
45+
}
46+
]
47+
},
48+
"configuration": {
49+
"title": "Debugger MCP",
50+
"properties": {
51+
"debuggerMcp.port": {
52+
"type": "number",
53+
"default": 45557,
54+
"description": "Preferred port for the MCP server. Falls back to a random port if taken."
55+
},
56+
"debuggerMcp.autoConfigureClaude": {
57+
"type": "boolean",
58+
"default": true,
59+
"description": "Automatically update ~/.claude.json with the MCP server URL on activation."
60+
}
2461
}
25-
]
62+
}
2663
},
2764
"scripts": {
2865
"compile": "npm run check-types && node esbuild.js",

src/extension/claude-config.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import * as fs from 'fs';
2+
import * as path from 'path';
3+
import * as os from 'os';
4+
5+
const CLAUDE_CONFIG_PATH = path.join(os.homedir(), '.claude.json');
6+
const MCP_SERVER_KEY = 'vscode-debugger';
7+
8+
interface ClaudeConfig {
9+
mcpServers?: Record<string, any>;
10+
projects?: Record<string, { mcpServers?: Record<string, any>; [key: string]: unknown }>;
11+
[key: string]: unknown;
12+
}
13+
14+
export interface ClaudeConfigResult {
15+
success: boolean;
16+
error?: string;
17+
details?: string;
18+
}
19+
20+
export function updateClaudeConfig(port: number): ClaudeConfigResult {
21+
let config: ClaudeConfig = {};
22+
23+
try {
24+
if (fs.existsSync(CLAUDE_CONFIG_PATH)) {
25+
const raw = fs.readFileSync(CLAUDE_CONFIG_PATH, 'utf-8');
26+
config = JSON.parse(raw);
27+
}
28+
} catch {
29+
config = {};
30+
}
31+
32+
const mcpUrl = `http://localhost:${port}/mcp`;
33+
const httpEntry = { type: 'http', url: mcpUrl };
34+
let changed = false;
35+
36+
// Update global config
37+
if (!config.mcpServers) {
38+
config.mcpServers = {};
39+
}
40+
const existing = config.mcpServers[MCP_SERVER_KEY];
41+
if (existing?.type !== 'http' || existing?.url !== mcpUrl) {
42+
config.mcpServers[MCP_SERVER_KEY] = httpEntry;
43+
changed = true;
44+
}
45+
46+
// Update project-level configs that have old entries for this server
47+
let projectsUpdated = 0;
48+
if (config.projects) {
49+
for (const [, project] of Object.entries(config.projects)) {
50+
const projectServer = project.mcpServers?.[MCP_SERVER_KEY];
51+
if (projectServer && (projectServer.type !== 'http' || projectServer.url !== mcpUrl)) {
52+
project.mcpServers![MCP_SERVER_KEY] = httpEntry;
53+
projectsUpdated++;
54+
changed = true;
55+
}
56+
}
57+
}
58+
59+
if (!changed) {
60+
return { success: true, details: 'Already up to date' };
61+
}
62+
63+
try {
64+
fs.writeFileSync(CLAUDE_CONFIG_PATH, JSON.stringify(config, null, 2) + '\n', 'utf-8');
65+
} catch (err) {
66+
return {
67+
success: false,
68+
error: `Failed to write ${CLAUDE_CONFIG_PATH}: ${err instanceof Error ? err.message : String(err)}`,
69+
};
70+
}
71+
72+
const parts: string[] = ['Updated ~/.claude.json'];
73+
if (projectsUpdated > 0) {
74+
parts.push(`Migrated ${projectsUpdated} project config(s) from stdio to HTTP`);
75+
}
76+
77+
return { success: true, details: parts.join('. ') };
78+
}

0 commit comments

Comments
 (0)