Skip to main content

Chrome DevTools panel with terminal and browser context injection for Claude Code

Project description

devpanel

Chrome DevTools panel with an embedded terminal and browser context injection for Claude Code.

What it does

Opens a terminal inside Chrome DevTools. Claude Code runs in that terminal connected to a local MCP server. When you select an element, capture network/console, or take a screenshot in DevTools, it goes into a delivery queue. In instant mode (default), items are flushed to the agent immediately. In staging mode, items accumulate until you explicitly push them.

Select elements — click Select (or Ctrl+K → Select element), Chrome's native crosshair picker activates. A modal describes the gesture; outside-click cancels. Pick an element, type an annotation in the prompt, repeat.

Capture network — snapshot recent requests from devtools.network.getHAR(). Full HAR spills to a JSON file the agent can grep. network() MCP tool also supports filters (url, method, status, since).

Capture console — snapshot recent console messages (log/warn/error + exceptions). Spills to JSON. Errors are also auto-pushed as channel notifications (unless channels are muted).

Screenshot — viewport via the toolbar / screenshot() MCP tool, or element-clipped via Ctrl+K → "Screenshot element…" / screenshot(selector="…"). Element clips use captureBeyondViewport: true so off-screen targets work without scrolling. PNG spills to disk.

Eval JSjs(code) MCP tool runs in the inspected page (sync via inspectedWindow.eval, async via Runtime.evaluate({awaitPromise: true}) when await is present). defer=true returns a task_id immediately and pushes the result via channel. every=N runs on a timer until cancelled. Inside eval, sendChannel(msg) pushes ad-hoc channel notifications.

Console + watchersconsole() returns recent log/warn/error entries; errors are also auto-pushed as channels. watch(url_pattern) registers a network observer; matching requests push channel notifications until cancelled.

Page navigation events — auto-pushed as channel notifications when the page navigates to a new URL. Same-URL navigations (HMR reconnects, sleep/wake reloads) are suppressed. Suppressed during click/type/navigate MCP calls (tool result already has the URL).

Delivery model

The stack is a delivery queue. Items are pushed by the panel (POST /stack) and removed on delivery. Two modes, toggled via Ctrl+K command palette:

  • Instant (default) — auto-flushes after each capture. Items appear briefly in the queue then clear. The agent sees each item immediately via channel notification.
  • Staging — items accumulate. StackChip shows pending count. Flush explicitly via Ctrl+K → "Push staged to agent", or let the UserPromptSubmit hook drain them on your next prompt.

Three delivery paths, all of which remove items from the queue:

  • POST /stack/flush — batch channel push via SSE
  • GET /stack (hook drain) — returns markdown for prompt injection
  • MCP drain tool — returns markdown in tool result

Direct channel pushes (nav events, console errors, watch hits, eval results) bypass the queue — they're ephemeral notifications. These can be muted via Ctrl+K → "Channels: mute" for quiet browsing.

Architecture

Chrome DevTools (DevPanel tab)                       PTY daemon (Python)
  ├── Terminal (xterm.js) ──ws ─────────────────── /ws (attach/detach)
  ├── cdp.ts ── chrome.debugger ─── all CDP work
  │   ├── ensureAttached / detach (with onDetach reset)
  │   ├── startInspect (Overlay.setInspectMode)
  │   ├── captureScreenshot (clip + captureBeyondViewport)
  │   ├── captureTree (Accessibility.getFullAXTree)
  │   ├── resolveSelector / focusElement / dispatchClick / dispatchType
  │   └── settle + networkDelta
  ├── commands.ts dispatch ────────────────────── POST /stack
  │   eval / screenshot / network / console / tree / click / type / navigate
  ├── actions.svelte.ts (Actions class)
  │   toggleSelect / captureScreenshot / captureNetwork / captureConsole / selectAndScreenshot
  ├── network.onNavigated ───────────────────── POST /channel (dedupe: same URL suppressed)
  ├── CommandPalette (Ctrl+K) / SelectionDialog / StackChip / ThemeToggle
  └── Service Worker ── NMH-relay only (sendNativeMessage to spawn daemon)

PTY daemon endpoints
  ├── /ws?tab=N              → terminal I/O (per-mount uuid)
  ├── POST /stack?session=   → stage context item (spills large data to disk)
  ├── POST /stack/flush?session= → deliver all queued items via channel
  ├── GET  /stack?session=   → drain (clears queue) / ?peek=true (raw JSON)
  ├── POST /channel?session= → ad-hoc channel push
  ├── POST /mcp?session=     → MCP JSON-RPC
  ├── GET  /mcp?session=     → SSE stream (channel notifications)
  ├── DELETE /mcp?session=   → terminate MCP session
  ├── GET  /spill/{name}     → serve spill files (screenshots, HAR)
  └── GET  /controls         → dev endpoint (port, pid, queue size, MCP sessions, uptime)

Claude Code (in PTY terminal)
  ├── MCP client ──POST/GET──→ /mcp (tools + SSE channel notifications)
  └── UserPromptSubmit hook ──→ GET /stack (drain fallback for non-channel clients)

The service worker is NMH-relay-only — it spawns the daemon via sendNativeMessage and returns the port to the panel. All CDP work runs in the panel via chrome.debugger.

Install

Two steps — Chrome assigns the extension ID on load, and the NMH manifest needs that ID.

Step 1: Load the extension

# Production
uv tool install devpanel
devpanel install    # prints the extension path

# Dev (source checkout — enables repld introspection)
uv sync --dev
uv run devpanel install --dev    # prints the extension path

Go to chrome://extensions → Enable Developer Mode → Load unpacked → point to the printed path. Copy the extension ID shown on the card.

Step 2: Register the NMH manifest with the ID

# Production
devpanel install --extension-id=PASTE_ID_HERE

# Dev
uv run devpanel install --extension-id=PASTE_ID_HERE --dev

The --dev flag stores the project root in ~/.config/devpanel/config.json so the NMH launcher spawns the daemon inside repld for live introspection. Without --dev, the daemon spawns directly via devpanel start.

Usage

  1. Open Chrome DevTools on any page
  2. Click the DevPanel tab
  3. A terminal opens with your shell — DEVPANEL_PORT, DEVPANEL_SESSION, DEVPANEL_SPILL_DIR, and DEVPANEL_REPLD_SOCKET env vars are set
  4. Your shell wrapper (see Shell setup) passes --mcp-config, --settings, and --dangerously-load-development-channels to claude
  5. Run claude — it connects to the MCP server and registers for channel notifications
  6. Select elements, capture network/console, take screenshots — context flows to Claude automatically
  7. Ctrl+K opens the command palette: all capture actions, staging toggle, channel mute, flush

Command palette (Ctrl+K)

Command What it does
Select element Activate crosshair picker, push to queue
Capture network Snapshot recent HAR entries to queue
Capture console Snapshot recent console messages to queue
Screenshot Capture viewport to queue
Screenshot element… Pick element, capture clipped screenshot
Channels: mute/unmute Toggle automatic channel notifications (nav, errors, watchers)
Stage mode: ON/OFF Toggle instant vs staging delivery
Push staged to agent Flush queued items via channel (visible in staging mode)
Eval JS… Run JavaScript in the inspected page

Dev

# Extension
cd extension-src
npm install
npm run build          # required — hot reload doesn't work with CRXJS service workers
npm run check          # svelte-check
npm run lint           # prettier + eslint
npm run format         # prettier --write

# Daemon (Python)
uv sync --dev          # installs aiohttp + repld (for live introspection)
uv run devpanel start  # standalone daemon for testing
ruff format src/       # format
ruff check src/        # lint
# Then: curl localhost:PORT/controls, curl POST/GET /stack, /mcp

# Full build (extension + Python wheel)
make build

Live introspection via repld

When the NMH spawns the daemon, it goes through repld so you can inspect daemon state from your main Claude Code session:

# .mcp.json registers repld bridge (gitignored — socket path is per-user)
{"mcpServers": {"repld": {"type": "stdio", "command": "uv",
  "args": ["run", "repld", "bridge", "--socket", "/run/user/$UID/devpanel/repld.sock"]}}}

Then in your Claude Code session: mcp__repld__exec("list(_sessions.keys())"), exec("[s.stack for s in _sessions.values()]"), push test channel notifications, etc.

Shell setup

DevPanel does not write any shell config files. The daemon sets env vars on the PTY: DEVPANEL_PORT, DEVPANEL_SESSION, DEVPANEL_SPILL_DIR, DEVPANEL_REPLD_SOCKET. Your shell needs to wrap claude with three flags when those vars are present:

What Claude Code needs:

  1. --mcp-config — connects to the devpanel MCP server:

    {"mcpServers":{"devpanel":{"type":"http","url":"http://127.0.0.1:$DEVPANEL_PORT/mcp?session=$DEVPANEL_SESSION"}}}
    
  2. --settings — registers the UserPromptSubmit hook (drain fallback):

    {"hooks":{"UserPromptSubmit":[{"matcher":"","hooks":[{"type":"command","command":"curl -s http://127.0.0.1:$DEVPANEL_PORT/stack?session=$DEVPANEL_SESSION"}]}]}}
    
  3. --dangerously-load-development-channels server:devpanel — enables real-time channel notifications

How to wire it up:

Create a shell wrapper that only activates inside DevPanel PTYs (when DEVPANEL_PORT is set). The wrapper should:

  • Pass all three flags to claude
  • Expand $DEVPANEL_PORT and $DEVPANEL_SESSION into the --mcp-config URL at call time
  • Keep the $DEVPANEL_PORT in the hook command literal — Claude's hook executor expands it at fire time
  • Forward all other arguments ($@ / $argv)

If you use tmux inside the DevPanel terminal, ensure env vars propagate:

set -ga update-environment " DEVPANEL_PORT DEVPANEL_SESSION DEVPANEL_SPILL_DIR DEVPANEL_REPLD_SOCKET"

Drain format

When the hook fires (or the agent calls drain), it sees:

## Browser Context

### Selected elements
- `div.central-textlogo` — this is the logo
- `nav.central-featured` — language links

### Network
- 8 requests, 1 errors → /run/user/1000/devpanel/network-def.json

### Console
- 15 entries, 3 errors/warnings → /run/user/1000/devpanel/console-abc.json

### Screenshots
- /run/user/1000/devpanel/screenshot-ghi.png

Large data (HAR, console, screenshots) spills to disk. The agent uses Read/Grep on the file paths.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

devpanel-0.5.0.tar.gz (381.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

devpanel-0.5.0-py3-none-any.whl (387.1 kB view details)

Uploaded Python 3

File details

Details for the file devpanel-0.5.0.tar.gz.

File metadata

  • Download URL: devpanel-0.5.0.tar.gz
  • Upload date:
  • Size: 381.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"EndeavourOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for devpanel-0.5.0.tar.gz
Algorithm Hash digest
SHA256 7573cba47c518bd59064cb7ac985dca089193e540ec710d0ddce62fed2b9a4c0
MD5 1f5dd6279cb59faeea0354ed7728eb13
BLAKE2b-256 da92ca8bbb3629b0b28ff7737e48a683d6640fbf2b95d532d95ceab53dee3b5c

See more details on using hashes here.

File details

Details for the file devpanel-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: devpanel-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 387.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"EndeavourOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for devpanel-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 110f1b3a2ae1888d4101f0c53609350aa778d418aba37c73e592306d0c1d6030
MD5 d118892486991b89d539421feeb5673d
BLAKE2b-256 a1da7b4357d477ef01e6d123f62e0fd2b11cdf79fc36e1a38868529488829855

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page