Skip to main content
Glama
README.md
# Claude Bridge

Claude Bridge is a VS Code extension that gives Claude native access to your editor and to a browser you can debug. Instead of grepping text and guessing, Claude uses what VS Code already knows: language servers, the debugger, terminals with exit codes, the Problems panel, tasks, tests, git, and a Chrome page driven over the Chrome DevTools Protocol (CDP).

The tools are written once and served two ways:

```mermaid
flowchart LR
  cc["Claude Code<br/>(CLI or the official extension)"]
  you["You, in VS Code's<br/>chat panel"]

  subgraph bridge["Claude Bridge (runs inside VS Code)"]
    mcp["MCP server<br/>on 127.0.0.1"]
    chat["@claude chat participant<br/>(Claude Agent SDK,<br/>in-process)"]
    registry["One tool registry<br/>(permissions, timeouts, audit)"]
    api["VS Code API<br/>(language servers, debugger,<br/>terminals, tasks, tests, git)"]
    debugger["js-debug"]
  end

  chrome["Chrome"]

  cc -- "MCP over HTTP" --> mcp
  you -- "@claude …" --> chat
  mcp --> registry
  chat --> registry
  registry --> api
  registry --> debugger
  debugger -- "CDP" --> chrome
```

- **MCP:** Claude Code (the terminal CLI, or the official Claude Code extension's panel) connects to the bridge as an MCP server called `vscode-bridge`.
- **`@claude` in VS Code's chat panel:** the same tools, run in-process through the Claude Agent SDK. Each turn automatically includes your current editor state.

## Contents

1. [Requirements](#requirements)
2. [Setup](#setup)
3. [Try it: a guided tour](#try-it-a-guided-tour)
4. [Commands](#commands)
5. [Settings](#settings)
6. [Tool reference](#tool-reference)
7. [Security model](#security-model)
8. [Development](#development)
9. [Troubleshooting](#troubleshooting)
10. [Known limitations](#known-limitations)

## Requirements

| Need | Why | Check |
|---|---|---|
| VS Code 1.144 or newer | The extension targets the 1.144 API | **Help → About** |
| Node.js 22 or newer and npm | Building and packaging (tested with Node 24) | `node -v` |
| The official Claude Code extension, signed in | Provides the `claude` binary and your login. Used to register the MCP server, to run `@claude`, and for the **Claude** chat model | Claude Code panel opens and answers |
| Google Chrome | Browser tools (`browser_*`, `cdp_*`) | `/Applications/Google Chrome.app` exists |
| Shell integration in the integrated terminal | `terminal_run` reads exit codes and output through it | On by default for zsh, bash, fish and pwsh |
| *Optional:* the `enable-proposed-api` runtime argument | The **Claude (Bridge)** harness in the chat panel | See [step 8](#8-optional-the-claude-bridge-harness) |
| *Optional:* VS Code Insiders | Other proposed (unreleased) APIs, e.g. structured test results | See [step 9](#9-optional-other-proposed-apis-on-vs-code-insiders) |

Only macOS has been tested.

## Setup

### 1. Build and install

```sh
git clone https://github.com/devadula-nandan/vscode-claude-bridge.git
cd vscode-claude-bridge
npm install
npm run dts:proposed   # downloads the proposed-API typings so vscode_api_docs can document them
npm run package        # builds dist/ and writes vscode-claude-bridge-0.1.0.vsix
code --install-extension vscode-claude-bridge-0.1.0.vsix
```

Then run **Developer: Reload Window** in every open VS Code window.

> Run `dts:proposed` before `package`: the build copies those typings into the package. If you skip it, everything works except that `vscode_api_docs` won't know about proposed APIs.

**Check:** open **View → Output** and choose **Claude Bridge**. You should see a line like `MCP server listening on http://127.0.0.1:41xxx/mcp (44 tools)` (on Stable; `test_results` is hidden without the `testObserver` proposed API).

### 2. Smoke test, without Claude

```sh
node scripts/smoke.mjs
```

This connects to the most recently started window's server and runs a few read-only tools. Expected output:

```
Workspace: /Users/you/Source/my-app
Server:    http://127.0.0.1:41xxx/mcp
Tools:     44 (22 read-only)
VS Code:   1.144.0, API docs: …
js-debug:  true   git: true   proposed testObserver: false
Editor:    /Users/you/Source/my-app/src/components/DatePicker/DatePicker.tsx
Problems:  0 errors, 3 warnings
OK
```

To test a specific window, pass its connection file: `node scripts/smoke.mjs ~/.claude-bridge/my-app-1a2b3c4d.json`.

### 3. Register with Claude Code, once per workspace

In the VS Code window for the workspace (e.g. my-app), open the Command Palette and run **Claude Bridge: Register with Claude Code**.

This runs `claude mcp add --scope local --transport http vscode-bridge <url> --header "Authorization: Bearer <token>"` directly, without a shell, so the token never lands in your shell history:
- It uses the binary inside the official Claude Code extension, so `claude` doesn't need to be on your PATH.
- `--scope local` stores the entry in your own Claude Code config for that folder (`~/.claude.json`), never in the repository.
- Each workspace gets a fixed port derived from its path, so you only register once per workspace.

**Check:** start a **new** Claude Code session in that workspace and run `/mcp`. `vscode-bridge` should be listed as connected. Then ask: *"Call bridge_capabilities and summarise what you can do."*

Prefer to do it by hand? **Claude Bridge: Copy `claude mcp add` Command** copies the full command, with the binary path if `claude` isn't on your PATH.

### 4. Decide who approves tool calls

There are two independent permission layers:

1. **Claude Code's own permissions** (or its auto mode) decide whether Claude may call an MCP tool at all.
2. **The bridge's tiers** decide again, per tool: `read` tools run, while `write` and `raw` tools show a VS Code dialog with **Allow** and **Allow for this session**.

To avoid being asked twice, let the bridge's tiers be the single gate by allowing the server in Claude Code. Add this to `~/.claude/settings.json`:

```json
{
  "permissions": {
    "allow": ["mcp__vscode-bridge"]
  }
}
```

The bridge still asks for `write` and `raw` tools under the default settings. If you'd rather Claude Code alone decide, set all three `claudeBridge.permissions.*` settings to `allow` instead.

### 5. Check terminal shell integration

Ask Claude to *"run `echo hello` with terminal_run"*. The result should include `"exitCode": 0` and the output. If it says shell integration is unavailable, see [Troubleshooting](#troubleshooting).

### 6. Browser setup

Nothing is needed if Chrome is installed. The bridge launches Chrome through VS Code's built-in JavaScript debugger, with its own profile stored in the extension's storage, never your normal Chrome profile. To use another browser build, set `claudeBridge.browser.executable` to a path, or to `stable`, `beta`, `dev` or `canary`.

**Check:** run **Claude Bridge: Launch Debug Browser**, enter any URL, then ask Claude *"show me the accessibility tree of the debug browser page"*.

### 7. `@claude` in the chat panel

If you set up the harness in [step 8](#8-optional-the-claude-bridge-harness), use it instead: it needs no `@claude` prefix. `@claude` is the fallback for VS Code started without the harness's runtime argument, and VS Code has deprecated chat participants like it.

1. Open the chat view (**View → Chat**). If it's hidden, check that `chat.disableAIFeatures` is off.
2. If the model picker under the input doesn't already show **Claude**, choose it. It's listed under **Claude Bridge**.
3. Type `@claude` followed by your request.

VS Code needs a chat model selected before it will hand a message to any participant, and without Copilot there is none. That is why the bridge registers its own model, **Claude**. `@claude` doesn't use the picked model's output: it runs Claude Code with the bridge tools. Without `@claude`, the **Claude** model answers as plain chat, with no tools, and ignores any tools VS Code offers it.

It uses the Claude Code binary from the official extension and the login you already have. If a turn fails with an authentication error, sign in from the Claude Code panel (`/login`) and try again. `claudeBridge.agent.executable` overrides which binary is used.

> `@claude` and the harness share one turn implementation. It has run against a live model through the harness in a test window, but `@claude` itself hasn't been run against a live model yet. If something fails, the **Claude Bridge** output channel has the error.

### 8. Optional: the Claude (Bridge) harness

VS Code's chat panel has a harness picker (**Local**, **Copilot**, **Claude**, …), at the bottom left of the chat input. The bridge can add **Claude (Bridge)** there. In that harness every message is a Claude Code turn with the bridge tools, and the session list shows this workspace's Claude Code sessions, so you can reopen one and continue it. This includes sessions you started in the CLI or the Claude Code panel.

Harnesses use the `chatSessionsProvider` and `chatProvider` proposed APIs. VS Code Stable allows them for extensions named in the `enable-proposed-api` runtime argument:

1. Run **Preferences: Configure Runtime Arguments** and add this line inside the braces:
   ```json
   "enable-proposed-api": ["devadula-nandan.vscode-claude-bridge"]
   ```
2. Quit VS Code completely (**Cmd+Q**) and reopen it. Reloading a window isn't enough.
3. In the chat panel, open the harness picker and choose **Claude (Bridge)**. Its model picker lists **Claude**, **Claude Opus**, **Claude Sonnet** and **Claude Haiku**; **Claude** uses `claudeBridge.agent.model`, else your Claude Code default. The permissions picker offers **Default**, **Accept edits** and **Plan**.

**Check:** the **Claude Bridge** output channel logs `Claude harness registered`, and `bridge_capabilities` reports `"chatSessionsProvider": true`. Without the argument it logs `Claude harness unavailable: …` instead, and everything else, including `@claude`, works as before.

> Proposed APIs can change in any VS Code release, so a VS Code update may break the harness until the extension catches up. Extensions that use them can't be published to the Marketplace.

### 9. Optional: other proposed APIs on VS Code Insiders

Other proposed APIs, such as `testObserver`, have only been tried in Insiders, and only work for extensions you allow:

1. Add the proposals you want to `enabledApiProposals` in `package.json`, e.g. `"testObserver"`, then rebuild and install into Insiders (`code-insiders --install-extension …`).
2. Allow the extension: run **Preferences: Configure Runtime Arguments** in Insiders and add
   ```json
   "enable-proposed-api": ["devadula-nandan.vscode-claude-bridge"]
   ```
   then restart Insiders.
3. **Check:** `bridge_capabilities` reports `"testObserver": true` and lists `test_results`.

Once allowed, every proposed API is also reachable through `vscode_eval`.

## Try it: a guided tour

Each item gives a prompt to type into Claude Code (or after `@claude`), the tools you'll see it call, and what to expect. The examples use a made-up React app, `my-app`, with `src/components/DatePicker/DatePicker.tsx` open; substitute your own files and symbols.

> Tip: run **Claude Bridge: Show Audit Log** beside the chat to watch every tool call, with its arguments and duration.

### Orientation

> *"What am I looking at right now?"*

Calls `vscode_context` and returns the active file, cursor and selected text, visible lines, problems in the file, open tabs, git branch, debug sessions and terminals. Select some code first and ask *"explain my selection"*: Claude reads the selection from VS Code instead of you pasting it.

### Code intelligence

> *"In DatePicker.tsx, where is `onSelect` declared (around line 40), and what uses it across the project?"*

Calls `code_definition`, `code_references` and `code_hover`. References come from the TypeScript server, grouped by file, so they include imports and re-exports and skip unrelated text matches that grep would return. Positions are given as file + line + symbol name, so Claude never has to count columns.

> *"Who calls `handleKeyDown` in DatePicker.tsx (line 120)?"*

Calls `code_call_hierarchy` (incoming calls, one level per call; ask for outgoing to see what it calls).

> *"Give me an outline of DatePicker.tsx"* · *"Find the symbol `CalendarGrid` in the workspace"*

Calls `code_symbols`: the file outline, or a workspace-wide symbol search.

### Problems and quick fixes

> *"Check DatePicker.tsx and DatePicker.stories.tsx for problems, and fix what the lightbulb can fix."*

Calls `diagnostics_get` (TypeScript, ESLint and anything else feeding the Problems panel), `code_actions` (the lightbulb menu) and `code_action_apply` (applies one by its exact title and saves the file).

### Safe edits

> *"Rename the local variable `isCalendarOpen` in DatePicker.tsx to `isOpen`, with a preview."*

Calls `edit_rename` with `preview: true`. VS Code's **Refactor Preview** panel opens with every change; nothing is written until you click **Apply**. Renames go through the language server, so they cover imports and re-exports across files. After applying, the tool saves the files and returns any new errors in them (`errorsAfterEdit`).

> *"Format DatePicker.tsx."*

Calls `edit_format`, using whichever formatter VS Code is configured to use for that file.

Try renames on a scratch branch; `git restore` undoes them.

### Terminals, tasks and tests

> *"Run the DatePicker tests in a terminal and summarise failures."*

Calls `terminal_run` with something like `npm test -- src/components/DatePicker`. A **Claude** terminal opens so you can watch it, and the tool returns the exit code and the end of the output (ANSI colors stripped). For dev servers, Claude passes `wait: false` and gets a `terminal.commandEnded` event in `bridge_events` when the command exits.

> *"List the tasks in this workspace and run the build task."*

Calls `task_list` (tasks.json plus auto-detected npm/yarn scripts) and `task_run`, which waits for the exit code.

> *"Run the tests for this file through the Test Explorer."*

Calls `test_run`. On Stable it starts the run in the Test Explorer, but the API can't report results there, so Claude falls back to `terminal_run`. On Insiders with `testObserver` it returns structured pass/fail results, and `test_results` becomes available.

### Debugger

> *"Debug the DatePicker tests: break inside `handleKeyDown` in DatePicker.tsx (line 120), run the tests under the debugger, and show me the locals when it stops."*

Calls:
- `debug_breakpoints` to add the breakpoint;
- `debug_start` with a configuration such as `{"type": "node-terminal", "command": "npm test -- src/components/DatePicker"}` and `waitForStop: true`, which returns the stop reason, call stack and top-frame locals;
- `debug_evaluate` / `debug_variables` to inspect values;
- `debug_control` to step (`next`, `stepIn`, `stepOut`), `continue` or `stop`.

You see the same session in VS Code's Run and Debug view. Program output goes to `bridge_events` (source `debug`).

### Browser (Storybook)

First start Storybook in a terminal: `npm run storybook` (port 6006). Or ask Claude to start it with `terminal_run` and `wait: false`.

> *"Open http://localhost:6006/iframe.html?id=components-datepicker--default&viewMode=story in the debug browser. Show me the accessibility tree for the date input, type '2026-01-15' into it, press Escape, and take a screenshot in dark mode."*

Calls:
- `browser_launch` to start Chrome under VS Code's debugger and connect CDP;
- `browser_ax_tree` for roles, accessible names and states as Chrome computes them;
- `browser_type` with `text` and `keys: ["Escape"]`;
- `browser_emulate` with `colorScheme: "dark"`;
- `browser_screenshot`, which returns the image and can also save a PNG.

If your stories render web components, selectors reach into shadow DOM automatically, and `my-date-input >>> input` steps into `my-date-input`'s shadow root explicitly.

> *"Compare the accessibility trees of the default and disabled DatePicker stories."*

Claude opens each story in turn and compares the `browser_ax_tree` output, e.g. to confirm the disabled state is exposed to assistive tech.

More to try:
- *"Click the next-month button and check the console for errors."* Calls `browser_click`, then reads console messages and exceptions from `bridge_events`; the bridge captures them automatically.
- *"Reload with reduced motion and a 375×667 mobile viewport."* Calls `browser_emulate` and `browser_navigate` (`reload: true`).
- *"Record the network requests while the story reloads."* Calls `cdp_subscribe` with `Network.*`, `browser_navigate`, then `bridge_events` (source `cdp`).
- *"What are the page's DOM node count and JS heap size?"* Calls `cdp_send` with `Performance.enable` and then `Performance.getMetrics`. Any CDP command works this way.

**Breakpoints in your source while the browser runs:** because Chrome runs under VS Code's debugger, a breakpoint set with `debug_breakpoints` in a `.tsx` file pauses the page when that code runs (through Storybook's source maps), and `debug_state` shows where. This hasn't been tested against a real Storybook yet; if the breakpoint stays hollow (unbound), the source maps aren't resolving to your workspace paths.

To attach to a browser you launched yourself from a launch.json Chrome or Edge configuration, ask Claude to use `browser_connect`. `browser_close` closes the bridge's browser.

### Anything else in the VS Code API

> *"Open DatePicker.tsx and DatePicker.stories.tsx side by side."*
> *"Which extensions are installed and active?"*
> *"Fold all regions in this file."*

For requests without a typed tool, Claude:
1. looks up the API with `vscode_api_docs`, which covers the installed VS Code version and proposed APIs, i.e. newer than Claude's training data;
2. runs code with `vscode_eval` (the live `vscode` API in the extension host), or a command with `vscode_execute_command`.

`vscode_commands` lists command IDs from VS Code and every installed extension.

> *"For the next few minutes, keep track of which files I save; tell me when I ask."*

Calls `vscode_subscribe` with `workspace.onDidSaveTextDocument`, and later `bridge_events`. Any `onDid…` / `onWill…` event in the API works. Events are buffered until Claude reads them; they aren't pushed into the conversation.

### Git (read-only)

> *"What have I changed on this branch?"*

Calls `git_status` (branch, ahead/behind, staged, unstaged, conflicts) and `git_diff`. Commits and pushes stay with Claude Code, where your hooks apply.

### `@claude` and triggers

> In the chat panel: *"@claude why does this fail?"* with a `#file` or selection attached.

Your current editor state is attached to every turn automatically. Edited files appear as references in the response, and follow-up messages resume the same session.

**Triggers.** These offer an **Ask Claude** button; nothing is sent until you click it. Enable them under **Settings → Claude Bridge**:

| Setting | Fires when | Try it |
|---|---|---|
| `claudeBridge.triggers.newErrorsOnSave` | saving a file adds errors to it | add `const x: number = 'a';` to a `.ts` file and save |
| `claudeBridge.triggers.testFailure` | a task in the **test** group exits non-zero (or, with `testObserver`, a Test Explorer run fails) | run a failing test task |
| `claudeBridge.triggers.debuggerPaused` | the debugger stops on an exception | debug code that throws, with "Uncaught Exceptions" breakpoints on |

Clicking **Ask Claude** opens the chat with an `@claude` prompt describing what happened.

## Commands

All are in the Command Palette under **Claude Bridge:**.

| Command | What it does |
|---|---|
| Show MCP Connection | Shows the server URL and connection-file path, with a button to copy the registration command |
| Register with Claude Code | Registers (or re-registers) `vscode-bridge` with Claude Code for this workspace folder, at local scope |
| Copy `claude mcp add` Command | Copies the registration command to run yourself |
| Rotate Token | Generates a new token, restarts the server and offers to re-register |
| Show Audit Log | Opens the output channel listing every tool call |
| Launch Debug Browser | Prompts for a URL and opens it in the debug browser |

## Settings

| Setting | Default | Meaning |
|---|---|---|
| `claudeBridge.mcp.enabled` | `true` | Run the MCP server |
| `claudeBridge.mcp.port` | `0` | `0` derives a stable port from the workspace path (41000–42999, moving up if that port is taken); otherwise a fixed port |
| `claudeBridge.permissions.readTools` | `allow` | `allow` or `ask` for read-only tools |
| `claudeBridge.permissions.writeTools` | `ask` | `allow`, `ask` or `deny` for tools that change files or run things |
| `claudeBridge.permissions.rawTools` | `ask` | `allow`, `ask` or `deny` for `vscode_eval`, `vscode_execute_command`, `browser_eval`, `cdp_send` |
| `claudeBridge.output.maxChars` | `20000` | Longer tool results are truncated with a marker |
| `claudeBridge.tools.timeoutMs` | `60000` | Default per-call timeout (long-running tools set their own) |
| `claudeBridge.browser.executable` | `""` | Chrome path or `stable`/`beta`/`dev`/`canary`; empty uses Chrome stable |
| `claudeBridge.agent.executable` | `""` | Claude Code binary for `@claude` and registration; empty tries the official extension's binary, then `claude` on PATH, then the Agent SDK's own copy |
| `claudeBridge.agent.model` | `""` | Model for `@claude` and the **Claude** chat model; empty uses your Claude Code default |
| `claudeBridge.agent.permissionMode` | `""` | Permission mode for `@claude`; empty follows your Claude Code settings |
| `claudeBridge.triggers.testFailure` | `false` | See [Triggers](#claude-and-triggers) |
| `claudeBridge.triggers.newErrorsOnSave` | `false` | 〃 |
| `claudeBridge.triggers.debuggerPaused` | `false` | 〃 |

## Tool reference

Tiers: **read** observes state, **write** changes files or runs things, **raw** is arbitrary code or protocol access. Lines and columns are 1-based everywhere except inside `vscode_eval`, which uses the raw 0-based API. Paths in results are absolute.

### Raw API and events

| Tool | Tier | Purpose |
|---|---|---|
| `vscode_api_docs` | read | Search the VS Code API (`query`) or print a full declaration (`name`, e.g. `window.createTerminal`), including proposed APIs |
| `vscode_eval` | raw | Run an async function body with `vscode`, `context`, `state` (persists across calls), `emit(name, data)` and `require` in scope |
| `vscode_commands` | read | List command IDs matching a filter |
| `vscode_execute_command` | raw | Execute any command; `{"$uri"}`, `{"$position"}` and `{"$range"}` arguments become API objects |
| `vscode_subscribe` | read | Buffer any `onDid…`/`onWill…` event; `unsubscribe` with an id or `"all"` |
| `bridge_events` | read | Read buffered events after a cursor; sources `vscode`, `cdp`, `console`, `debug`, `custom` |
| `bridge_capabilities` | read | VS Code version, proposed APIs, js-debug, git, browser state and the tool list |

### Editor and code

| Tool | Tier | Purpose |
|---|---|---|
| `vscode_context` | read | Active file, selection, visible lines, problems, tabs, git, debug, terminals, browser |
| `code_definition` | read | Definition, type definition, implementation or declaration |
| `code_references` | read | References grouped by file (`includeDeclaration` optional) |
| `code_hover` | read | Type, signature and docs at a position |
| `code_symbols` | read | File outline (`path`) or workspace symbol search (`query`) |
| `code_call_hierarchy` | read | Incoming or outgoing calls |
| `code_actions` | read | Quick fixes and refactorings for a range |
| `code_action_apply` | write | Apply one by title; saves only the files it changed |
| `edit_rename` | write | Language-server rename across files, optional refactor preview |
| `edit_apply` | write | Multi-file text edits as one undoable change, optional preview |
| `edit_format` | write | Format files with the configured formatter |
| `diagnostics_get` | read | Problems for files or the whole workspace; `waitForSettle` after edits |

Edit tools save to disk and return `errorsAfterEdit` by default (`save`, `checkDiagnostics` and `preview` options).

### Running things

| Tool | Tier | Purpose |
|---|---|---|
| `terminal_run` | write | Run a command in a visible terminal; returns exit code and output, or starts it in the background with `wait: false` |
| `terminal_list` | read | Terminals, shell-integration state, cwd, busy |
| `task_list` | read | Tasks from tasks.json and auto-detection |
| `task_run` | write | Run a task and wait for its exit code |
| `test_run` | write | Run tests through the Test Explorer (`all`, `file`, `failed`) |
| `test_results` | read | Last Test Explorer results (Insiders + `testObserver` only) |

### Debugger

| Tool | Tier | Purpose |
|---|---|---|
| `debug_start` | write | Start a launch.json configuration by name or an inline one; `waitForStop` returns the paused state |
| `debug_breakpoints` | write | Add (with condition, hit count or log message), remove or clear breakpoints |
| `debug_state` | read | Sessions; for paused ones, the reason, stack and locals |
| `debug_control` | write | `continue`, `next`, `stepIn`, `stepOut`, `pause`, `restart`, `stop` |
| `debug_evaluate` | write | Evaluate in a paused frame (runs code in the program being debugged) |
| `debug_variables` | read | Expand a variable by its `expand` reference |

### Browser

| Tool | Tier | Purpose |
|---|---|---|
| `browser_launch` | write | Launch Chrome under the JavaScript debugger with a dedicated profile (`headless`, `viewport` optional) |
| `browser_connect` | write | Attach to a Chrome or Edge debug session you started |
| `browser_close` | write | Disconnect and close the bridge's browser |
| `browser_navigate` | write | Navigate or reload and wait for load |
| `browser_screenshot` | read | Viewport, full page or one element; optional `savePath` |
| `browser_ax_tree` | read | Accessibility tree, whole page or one element, including shadow DOM |
| `browser_click` | write | Real mouse events on an element |
| `browser_type` | write | Focus, insert text, press keys (`Enter`, `Tab`, `Shift+Tab`, `ArrowDown`, `Escape`, …) |
| `browser_eval` | raw | Evaluate JavaScript in the page |
| `browser_emulate` | write | Color scheme, reduced motion, forced colors, viewport, CPU throttling, offline; `reset` |
| `cdp_send` | raw | Any CDP command |
| `cdp_subscribe` | read | Buffer CDP events (`Network.responseReceived`, `Network.*`, …) into `bridge_events` |

Selectors search open shadow roots; `host >>> inner` steps into a host's shadow root explicitly.

### Git

| Tool | Tier | Purpose |
|---|---|---|
| `git_status` | read | Branch, upstream, ahead/behind, staged, unstaged and conflicting files |
| `git_diff` | read | Diff of all changes or one file; `staged` for the index |

## Security model

- **Network:** the MCP server listens on `127.0.0.1` only. Every request needs `Authorization: Bearer <token>`. Requests with an `Origin` header (anything from a web page) or a `Host` other than localhost are refused, which blocks cross-site requests and DNS rebinding.
- **Token:** the token is random, stored in VS Code's SecretStorage, and written only to `~/.claude-bridge/<workspace>.json` (mode `0600`) and your Claude Code config. **Rotate Token** replaces it.
- **Permission tiers:** one gate covers both front ends, and **Allow for this session** lasts until the window reloads. `raw` tools run arbitrary code with your user's permissions (the same trust as Claude's Bash tool), so they default to `ask`.
- **Browser:** the debug browser uses its own profile in the extension's storage, so CDP never sees your real cookies or logins.
- **Git:** read-only by design.
- **Audit:** every call is logged with its caller (`mcp`, `chat` or `command`), arguments, outcome and duration.

## Development

| Task | Command |
|---|---|
| Run from source | Open this folder in VS Code and press **F5** (**Run Claude Bridge**) |
| Rebuild on change | `npm run watch` |
| Typecheck | `npm run typecheck` |
| Integration tests | `npm test` (uses the installed VS Code; set `VSCODE_PATH` for another) |
| Package | `npm run package` |
| Smoke test a running window | `node scripts/smoke.mjs [connection file]` |

**Tests.** `npm test` starts a real VS Code window on `test/fixture`, with one suite per phase. The code-intelligence suite registers a small fake language (`*.bridge` files) so it doesn't depend on any language server. A second suite repeats the checks against the real TypeScript server and skips itself if tsserver can't start, as happens in some sandboxes. The browser suite runs headless Chrome and skips if Chrome isn't installed.

**After VS Code updates:**
1. `npm install -D @types/vscode@<new version>` and raise `engines.vscode` to match.
2. `npm run dts:proposed`, which defaults to the `@types/vscode` version (or pass a git tag).
3. `npm run package` and reinstall.

**Layout:**
- `src/core/`: registry, permissions, serialization, events, positions
- `src/tools/`: VS Code tools
- `src/browser/`: CDP client and browser tools
- `src/transports/`: MCP server, `@claude`, the **Claude** chat model, and the **Claude (Bridge)** harness
- `src/triggers.ts`: opt-in triggers

## Troubleshooting

| Symptom | Fix |
|---|---|
| `/mcp` doesn't list `vscode-bridge` | Run **Register with Claude Code** from that workspace's window, then start a **new** Claude Code session there. The registration is per folder. |
| `vscode-bridge` shows as failed, or calls return 401 | The token changed (Rotate Token) or the window isn't open. Open the workspace in VS Code and run **Register with Claude Code** again. |
| Server didn't start (error notification, or no "listening" line in the output) | The port is taken. Set `claudeBridge.mcp.port` to a free port and re-register. |
| `terminal_run` says shell integration is unavailable | Enable `terminal.integrated.shellIntegration.enabled`. If you use a custom shell setup, see VS Code's *Terminal Shell Integration* docs for manual installation. Check with `terminal_list`. |
| Code tools return nothing | The language server is still starting (large repos take a while) or isn't installed for that file type. Hover in the editor to confirm VS Code itself has the information. |
| `browser_launch` fails | Install Chrome or set `claudeBridge.browser.executable`. Check the Debug Console for js-debug errors. |
| Browser breakpoints stay hollow (unbound) | Source maps aren't mapping to workspace paths; check the dev server serves source maps. |
| `@claude` says "Language model unavailable", or the picker says "No models available" | VS Code found no chat model. Choose **Claude** (under **Claude Bridge**) in the model picker. If it isn't listed, check that the extension is installed and the window was reloaded, and look under **Manage Models** in the picker in case it's hidden. |
| **Claude (Bridge)** isn't in the harness picker | Check the **Claude Bridge** output channel. `Claude harness unavailable` means VS Code wasn't started with the runtime argument from [step 8](#8-optional-the-claude-bridge-harness): add it, then quit and reopen VS Code. |
| `@claude` errors immediately | Check the **Claude Bridge** output channel. Usually it's the binary (set `claudeBridge.agent.executable`) or login (`/login` in the Claude Code panel). |
| `vscode_api_docs` doesn't know proposed APIs | Run `npm run dts:proposed` before `npm run package`, then reinstall. |
| Tool asks for permission every time | Choose **Allow for this session**, or change `claudeBridge.permissions.*`. |

## Known limitations

- **Task output:** VS Code's API exposes a task's exit code but not its output. Use `terminal_run` when you need output.
- **Test results on Stable:** `test_run` starts a Test Explorer run but can't read the results without `testObserver` (Insiders). Use `terminal_run` with the test command.
- **Events are pulled, not pushed:** Claude sees buffered events when it calls `bridge_events`. Only `@claude` triggers start a conversation.
- **Not yet tested end to end:** `@claude` turns against a live model (the harness, which shares their code, has been), the harness picker UI in a normal VS Code window (tests drive it by command), source-map breakpoints in a real Storybook, proposed APIs on Insiders, and the TypeScript-backed tests inside a sandbox (they're designed for your normal environment).
- **Platform:** macOS only.