From b910664329e3139f1d9bb491a6868d86cff03679 Mon Sep 17 00:00:00 2001 From: sestet Date: Fri, 4 Sep 2026 17:36:35 +0800 Subject: [PATCH 1/2] feat: add Claude Code auto-registration Adds Claude Code to the agent selection popup and manual config docs. DebugMCP is a standard MCP server over Streamable HTTP, so Claude Code already works via `claude mcp add` / editing ~/.claude.json by hand - this just wires it into the same auto-detect flow the other JSON-based agents (Cline, Cursor, Windsurf, Roo Code, Antigravity) already use. - ~/.claude.json's top-level `mcpServers` field is the user-scope, cross-project location per https://code.claude.com/docs/en/mcp. - Claude Code's config schema only recognizes `http`/`streamable-http` as transport types, not the `streamableHttp` shape the other JSON agents use, so getDebugMCPConfig() special-cases it the same way it already does for copilot-cli. - Claude Desktop is intentionally left out of auto-registration: its official docs only show local stdio `command` servers in claude_desktop_config.json, with remote/local HTTP servers added through Settings -> Connectors -> Add custom connector instead of a static config file. The README's manual section documents that path. Verified: `npm run check-types` and `npm run lint` pass. `npm test` (vscode-test) could not complete in this sandbox - the bundled test host hits an unrelated environment issue unpacking VS Code under a path containing a space - so it wasn't able to run end-to-end here. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01WhAeRUTsesSRnTi4E6CAcR --- CHANGELOG.md | 5 +++++ README.md | 27 +++++++++++++++++++++++--- src/utils/agentConfigurationManager.ts | 23 ++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc55e09..cff045d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to DebugMCP will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/). +## [Unreleased] + +### Added +- **Claude Code auto-registration** - Claude Code is now offered in the agent selection popup and configured via `~/.claude.json`'s user-scope `mcpServers` field. Claude Desktop connects via its Custom Connector UI instead of a static config file; the README's manual configuration section covers both. + ## [2.3.4] - 2026-09-03 ### Added diff --git a/README.md b/README.md index 15a5bd8..d0153d4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # DebugMCP (MCP Server) - Empowering AI Agents with Operational Debugging Capabilities -Let AI agents debug your code inside VS Code - set breakpoints, step through execution, inspect variables, and evaluate expressions. Works with **Codex**, **GitHub Copilot**, **GitHub Copilot CLI**, **Cline**, **Cursor**, **Windsurf**, **Roo Code**, and any MCP-compatible assistant. Compatible with any VS Code supported coding language. +Let AI agents debug your code inside VS Code - set breakpoints, step through execution, inspect variables, and evaluate expressions. Works with **Codex**, **GitHub Copilot**, **GitHub Copilot CLI**, **Claude Code**, **Cline**, **Cursor**, **Windsurf**, **Roo Code**, and any MCP-compatible assistant. Compatible with any VS Code supported coding language. [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![VS Code](https://img.shields.io/badge/VS%20Code-1.104.0+-blue.svg)](https://code.visualstudio.com/) @@ -133,6 +133,7 @@ DebugMCP works with any MCP-compatible AI assistant. It auto-detects and offers |-----------|:-----------------:|:-------------:| | **GitHub Copilot** | ✅ | [See config](#github-copilot) | | **GitHub Copilot CLI** | ✅ | [See config](#github-copilot-cli) | +| **Claude Code** | ✅ | [See config](#claude-code) | | **Cline** | ✅ | [See config](#cline) | | **Cursor** | ✅ | [See config](#cursor) | | **Codex** | ✅ | [See config](#codex) | @@ -214,6 +215,26 @@ Add to `~/.copilot/mcp-config.json` (`${COPILOT_HOME}/mcp-config.json` if `COPIL } ``` +#### Claude Code +Register DebugMCP with Claude Code: +```bash +claude mcp add --transport http debugmcp http://localhost:3001/mcp +``` + +Or add the equivalent configuration to `~/.claude.json` (top-level `mcpServers`, for cross-project user-scope access — see the [Claude Code MCP docs](https://code.claude.com/docs/en/mcp)): +```json +{ + "mcpServers": { + "debugmcp": { + "type": "http", + "url": "http://localhost:3001/mcp" + } + } +} +``` + +> **Claude Desktop**: Claude Desktop connects to local/remote HTTP MCP servers through **Settings → Connectors → Add custom connector** rather than a static config file — paste `http://localhost:3001/mcp` there. DebugMCP doesn't auto-register with Desktop for this reason. + #### Cursor Add to Cursor's MCP settings: ```json @@ -315,7 +336,7 @@ DebugMCP exposes powerful debugger primitives (`evaluate_expression`, `start_deb
Which AI assistants are supported? -DebugMCP works with any MCP-compatible AI assistant, including **GitHub Copilot**, **GitHub Copilot CLI**, **Cline**, **Cursor**, **Codex**, **Windsurf**, **Roo Code**, **Antigravity**, and others. If your assistant supports the Model Context Protocol, it can use DebugMCP. +DebugMCP works with any MCP-compatible AI assistant, including **GitHub Copilot**, **GitHub Copilot CLI**, **Claude Code**, **Cline**, **Cursor**, **Codex**, **Windsurf**, **Roo Code**, **Antigravity**, and others. If your assistant supports the Model Context Protocol, it can use DebugMCP. Claude Desktop can also connect via its Custom Connector UI (see [Claude Code](#claude-code) in the manual configuration section).
@@ -429,7 +450,7 @@ The extension handles debug configurations intelligently: - **Rust**: [rust-analyzer extension](vscode:extension/rust-lang.rust-analyzer) - **PHP**: [PHP Debug extension](vscode:extension/xdebug.php-debug) - **Ruby**: [Ruby extension](vscode:extension/rebornix.ruby) with debug support -- MCP-compatible AI assistant (Copilot, Cline, Cursor, Codex, Windsurf, Roo Code, etc.) +- MCP-compatible AI assistant (Copilot, Claude Code, Cline, Cursor, Codex, Windsurf, Roo Code, etc.) ## Development diff --git a/src/utils/agentConfigurationManager.ts b/src/utils/agentConfigurationManager.ts index ba12127..04642d9 100644 --- a/src/utils/agentConfigurationManager.ts +++ b/src/utils/agentConfigurationManager.ts @@ -347,6 +347,18 @@ export class AgentConfigurationManager { configFormat: 'json', mcpServerFieldName: 'mcpServers' }, + { + id: 'claude-code', + name: 'claude-code', + displayName: 'Claude Code', + // User-scope MCP servers live under the top-level `mcpServers` field of + // ~/.claude.json (shared across projects), distinct from the per-project + // `projects..mcpServers` entries Claude Code also stores there. + // See https://code.claude.com/docs/en/mcp. + configPath: path.join(os.homedir(), '.claude.json'), + configFormat: 'json', + mcpServerFieldName: 'mcpServers' + }, { id: 'codex', name: 'codex', @@ -371,6 +383,17 @@ export class AgentConfigurationManager { }; } + if (agent?.id === 'claude-code') { + // Claude Code's mcpServers schema only recognizes `type`/`url`/`headers`; + // `streamableHttp` (the shape the other JSON agents below use) isn't one of + // its accepted type values. Its docs treat `http` and `streamable-http` as + // aliases for the same Streamable HTTP transport DebugMCP speaks. + return { + type: 'http', + url: this.getMCPServerUrl() + }; + } + return { autoApprove: [], disabled: false, From aabeb026804ec4f90c19c3401a21a0619f1c6d02 Mon Sep 17 00:00:00 2001 From: sestet Date: Thu, 10 Sep 2026 11:37:10 +0800 Subject: [PATCH 2/2] fix: preserve valid Claude Code HTTP configurations --- README.md | 4 +- .../architecture/agentConfigurationManager.md | 15 +- src/test/agentConfigurationManager.test.ts | 181 ++++++++++++++++++ src/utils/agentConfigurationManager.ts | 4 +- 4 files changed, 198 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d0153d4..71b1c91 100644 --- a/README.md +++ b/README.md @@ -218,7 +218,7 @@ Add to `~/.copilot/mcp-config.json` (`${COPILOT_HOME}/mcp-config.json` if `COPIL #### Claude Code Register DebugMCP with Claude Code: ```bash -claude mcp add --transport http debugmcp http://localhost:3001/mcp +claude mcp add --transport http --scope user debugmcp http://localhost:3001/mcp ``` Or add the equivalent configuration to `~/.claude.json` (top-level `mcpServers`, for cross-project user-scope access — see the [Claude Code MCP docs](https://code.claude.com/docs/en/mcp)): @@ -520,4 +520,4 @@ If DebugMCP has helped you debug faster, please consider giving it a star on Git MIT License - See [LICENSE](LICENSE.txt) for details -This extension was created by **Oz Zafar**, **Ori Bar-Ilan** and **Karin Brisker**. \ No newline at end of file +This extension was created by **Oz Zafar**, **Ori Bar-Ilan** and **Karin Brisker**. diff --git a/docs/architecture/agentConfigurationManager.md b/docs/architecture/agentConfigurationManager.md index 12c2510..b4a35d8 100644 --- a/docs/architecture/agentConfigurationManager.md +++ b/docs/architecture/agentConfigurationManager.md @@ -2,11 +2,11 @@ ## Purpose -Handles automatic configuration of AI coding agents (Cline, GitHub Copilot, GitHub Copilot CLI, Cursor, Codex) to connect to the DebugMCP server. Provides a seamless onboarding experience. +Handles automatic configuration of AI coding agents (Cline, GitHub Copilot, GitHub Copilot CLI, Claude Code, Cursor, Codex) to connect to the DebugMCP server. Provides a seamless onboarding experience. ## Motivation -For AI agents to use DebugMCP, they need MCP server configuration in their settings files. Rather than requiring users to manually edit JSON files, this manager auto-configures supported agents with the correct SSE endpoint. +For AI agents to use DebugMCP, they need MCP server configuration in their settings files. Rather than requiring users to manually edit JSON files, this manager auto-configures supported agents with the Streamable HTTP endpoint. ## Responsibility @@ -28,6 +28,7 @@ Popup** from the Command Palette after correcting the JSON. | Cline | `cline_mcp_settings.json` | `mcpServers` | | GitHub Copilot | `mcp.json` | `servers` | | GitHub Copilot CLI | `~/.copilot/mcp-config.json` or `${COPILOT_HOME}/mcp-config.json` | `mcpServers` | +| Claude Code | `~/.claude.json` | Top-level `mcpServers` (user scope, shared across projects) | | Cursor | `mcp_settings.json` | `mcpServers` | | Codex | `~/.codex/config.toml` or `${CODEX_HOME}/config.toml` | `mcp_servers.debugmcp` | @@ -68,6 +69,16 @@ GitHub Copilot CLI uses: } ``` +Claude Code uses `type: "http"` and `url` in the top-level `mcpServers.debugmcp` +entry of `~/.claude.json`. Setup preserves unrelated top-level settings, project +settings (including project-scoped MCP servers), and other user-scoped MCP servers. + +On extension activation, migration leaves existing Claude Code `http` and +`streamable-http` entries unchanged, including custom URLs and headers, unless +the URL still ends in `/sse`. Entries with `type: "sse"` or a legacy `/sse` URL +are migrated to the current HTTP configuration. Once migrated, subsequent runs +do not rewrite the configuration or report another migration. + Codex uses TOML: ```toml [mcp_servers.debugmcp] diff --git a/src/test/agentConfigurationManager.test.ts b/src/test/agentConfigurationManager.test.ts index 901f7da..c51accf 100644 --- a/src/test/agentConfigurationManager.test.ts +++ b/src/test/agentConfigurationManager.test.ts @@ -1,10 +1,13 @@ // Copyright (c) Microsoft Corporation. +import * as vscode from 'vscode'; import * as assert from 'assert'; import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; import { + AgentConfigurationManager, + JsonAgentInfo, upsertCodexDebugMCPConfig, upsertJsonDebugMCPConfigFile } from '../utils/agentConfigurationManager'; @@ -31,6 +34,184 @@ suite('AgentConfigurationManager JSON configuration', () => { }); }); +suite('AgentConfigurationManager Claude Code configuration', () => { + const serverPort = 4317; + const expectedEntry = { type: 'http', url: `http://localhost:${serverPort}/mcp` }; + const unrelatedConfig = { + theme: 'dark', + projects: { + '/sample/project': { + hasTrustDialogAccepted: true, + mcpServers: { + debugmcp: { type: 'http', url: 'http://localhost:9876/mcp' }, + projectServer: { command: 'sample', args: ['--project'] } + } + } + } + }; + const otherServer = { type: 'http', url: 'https://other.example/mcp' }; + const originalWriteFile = fs.promises.writeFile; + const originalShowInformationMessage = vscode.window.showInformationMessage; + let tempDir: string; + let manager: AgentConfigurationManager; + let supportedAgent: JsonAgentInfo; + let agent: JsonAgentInfo; + let writes: number; + let notifications: string[]; + + setup(async () => { + tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'debugmcp-claude-config-')); + manager = new AgentConfigurationManager({ extensionPath: tempDir } as vscode.ExtensionContext, 180, serverPort); + const agents = await manager['getSupportedAgents'](); + const claudeCode = agents.find(candidate => candidate.id === 'claude-code'); + assert.ok(claudeCode && claudeCode.configFormat === 'json'); + supportedAgent = claudeCode; + agent = { ...supportedAgent, configPath: path.join(tempDir, '.claude.json') }; + // Exercise the real setup and migration paths using only the temporary config. + manager['getSupportedAgents'] = async () => [agent]; + manager['installDebugMCPSkill'] = async () => null; + writes = 0; + notifications = []; + fs.promises.writeFile = async (...args: Parameters) => { + if (args[0] === agent.configPath) { + writes++; + } + return originalWriteFile(...args); + }; + vscode.window.showInformationMessage = async (message: string) => { + notifications.push(message); + return undefined; + }; + }); + + teardown(async () => { + fs.promises.writeFile = originalWriteFile; + vscode.window.showInformationMessage = originalShowInformationMessage; + await fs.promises.rm(tempDir, { recursive: true, force: true }); + }); + + test('uses the user-scoped Claude Code configuration location', () => { + assert.strictEqual(supportedAgent.configPath, path.join(os.homedir(), '.claude.json')); + assert.strictEqual(supportedAgent.mcpServerFieldName, 'mcpServers'); + }); + + test('setup generates only the Claude Code HTTP fields using the configured port', async () => { + const result = await manager['addDebugMCPToAgent'](agent); + + assert.strictEqual(result.success, true); + assert.deepStrictEqual(JSON.parse(await fs.promises.readFile(agent.configPath, 'utf8')), { + mcpServers: { debugmcp: expectedEntry } + }); + assert.strictEqual(writes, 1); + }); + + for (const hasUserServers of [false, true]) { + test(`setup preserves unrelated settings with ${hasUserServers ? 'existing' : 'no'} user-scoped servers`, async () => { + const existingServers = hasUserServers ? { other: otherServer } : {}; + const initial = { + ...unrelatedConfig, + ...(hasUserServers ? { mcpServers: existingServers } : {}) + }; + await originalWriteFile(agent.configPath, JSON.stringify(initial), 'utf8'); + + const result = await manager['addDebugMCPToAgent'](agent); + + assert.strictEqual(result.success, true); + assert.deepStrictEqual(JSON.parse(await fs.promises.readFile(agent.configPath, 'utf8')), { + ...unrelatedConfig, + mcpServers: { ...existingServers, debugmcp: expectedEntry } + }); + assert.strictEqual(writes, 1); + }); + } + + for (const type of ['http', 'streamable-http']) { + for (const url of [expectedEntry.url, 'https://custom.example:4443/debug/mcp']) { + test(`migration does not rewrite or notify for ${type} at ${url}`, async () => { + const content = JSON.stringify({ + ...unrelatedConfig, + mcpServers: { + other: otherServer, + debugmcp: { type, url, headers: { 'X-Test': 'synthetic-value' } } + } + }); + await originalWriteFile(agent.configPath, content, 'utf8'); + + for (let run = 0; run < 2; run++) { + await manager.migrateExistingConfigurations(); + + assert.strictEqual(await fs.promises.readFile(agent.configPath, 'utf8'), content); + assert.strictEqual(writes, 0); + assert.deepStrictEqual(notifications, []); + } + }); + } + } + + for (const legacyEntry of [ + { type: 'sse', url: 'http://localhost:3001/sse' }, + { type: 'sse', url: 'http://localhost:3001/mcp' }, + { type: 'http', url: 'http://localhost:3001/sse' }, + { type: 'streamable-http', url: 'http://localhost:3001/sse' } + ]) { + test(`migrates ${legacyEntry.type} at ${legacyEntry.url} only once and preserves unrelated settings`, async () => { + await originalWriteFile(agent.configPath, JSON.stringify({ + ...unrelatedConfig, + mcpServers: { other: otherServer, debugmcp: legacyEntry } + }), 'utf8'); + + await manager.migrateExistingConfigurations(); + + const migratedContent = await fs.promises.readFile(agent.configPath, 'utf8'); + assert.deepStrictEqual(JSON.parse(migratedContent), { + ...unrelatedConfig, + mcpServers: { other: otherServer, debugmcp: expectedEntry } + }); + assert.strictEqual(writes, 1); + assert.deepStrictEqual(notifications, [ + 'DebugMCP: Migrated 1 agent configuration(s) to use the new transport protocol.' + ]); + + await manager.migrateExistingConfigurations(); + + assert.strictEqual(await fs.promises.readFile(agent.configPath, 'utf8'), migratedContent); + assert.strictEqual(writes, 1); + assert.strictEqual(notifications.length, 1); + }); + } + + test('still leaves a valid Copilot CLI HTTP entry unchanged', async () => { + agent = { ...agent, id: 'copilot-cli' }; + const content = JSON.stringify({ mcpServers: { debugmcp: { ...expectedEntry, tools: ['*'] } } }); + await originalWriteFile(agent.configPath, content, 'utf8'); + + await manager.migrateExistingConfigurations(); + + assert.strictEqual(await fs.promises.readFile(agent.configPath, 'utf8'), content); + assert.strictEqual(writes, 0); + assert.deepStrictEqual(notifications, []); + }); + + test('still migrates a Cline HTTP entry and preserves custom autoApprove settings', async () => { + agent = { ...agent, id: 'cline' }; + const autoApprove = ['list_breakpoints']; + await originalWriteFile(agent.configPath, JSON.stringify({ + mcpServers: { debugmcp: { ...expectedEntry, autoApprove } } + }), 'utf8'); + + await manager.migrateExistingConfigurations(); + await manager.migrateExistingConfigurations(); + + assert.deepStrictEqual(JSON.parse(await fs.promises.readFile(agent.configPath, 'utf8')), { + mcpServers: { + debugmcp: { ...expectedEntry, type: 'streamableHttp', autoApprove, disabled: false, timeout: 180 } + } + }); + assert.strictEqual(writes, 1); + assert.strictEqual(notifications.length, 1); + }); +}); + suite('AgentConfigurationManager Codex TOML configuration', () => { const mcpServerUrl = 'http://localhost:3001/mcp'; diff --git a/src/utils/agentConfigurationManager.ts b/src/utils/agentConfigurationManager.ts index 04642d9..531bb08 100644 --- a/src/utils/agentConfigurationManager.ts +++ b/src/utils/agentConfigurationManager.ts @@ -459,11 +459,11 @@ export class AgentConfigurationManager { continue; // DebugMCP not configured for this agent } - // Check if it's using the old SSE configuration + // Claude Code's HTTP transport is current; still migrate legacy SSE endpoints. const needsMigration = agent.id === 'copilot-cli' ? debugmcpConfig.type !== 'http' || (debugmcpConfig.url && debugmcpConfig.url.endsWith('/sse')) : debugmcpConfig.type === 'sse' || - debugmcpConfig.type === 'http' || + (debugmcpConfig.type === 'http' && agent.id !== 'claude-code') || (debugmcpConfig.url && debugmcpConfig.url.endsWith('/sse')); if (needsMigration) {