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, or take a screenshot in DevTools, it appears immediately in Claude's conversation as a channel notification — no need to type "look at this," no need to drain on prompt.

Select elements — click Select, Chrome's native crosshair picker activates. Click an element, type an annotation in the prompt, repeat. Each selection pushes a channel notification.

Capture network — snapshot recent requests from devtools.network.getHAR(). Full HAR spills to a JSON file the agent can grep.

Screenshot — CDP Page.captureScreenshot with clip from the selected element. PNG spills to disk, agent can read it.

Page HTML — captured automatically on first element selection. Full HTML spills to a file for grep/read.

Two delivery channels:

  • MCP channel notifications (primary) — notifications/claude/channel pushed over Streamable HTTP/SSE the moment context is captured. Claude sees <channel source="devpanel">...</channel> in real time.
  • UserPromptSubmit hook (fallback) — drains the stack via curl /stack on every prompt. Works for clients that don't enable channels.

Architecture

Chrome DevTools (DevPanel tab)
  ├── Terminal (xterm.js) ──ws──→ PTY daemon (:random)
  ├── Select button ──CDP──→ Overlay.setInspectMode → selector + annotation
  ├── Network button ──→ devtools.network.getHAR()
  └── Screenshot button ──→ chrome.debugger → Page.captureScreenshot

Service Worker
  ├── Native messaging → spawn PTY daemon
  ├── CDP element picker (Overlay domain)
  └── Screenshot relay (chrome.debugger, not available in panel)

PTY daemon (Python, aiohttp)
  ├── WebSocket /ws?tab=N      → terminal I/O (per-tab session)
  ├── POST /stack?session=N    → push context (spills large data to disk)
  ├── GET /stack?session=N     → drain + clear (formatted text for hook)
  ├── GET /stack?peek=true     → raw JSON (for panel UI)
  ├── POST /mcp?session=N      → MCP JSON-RPC (initialize, tools/call)
  ├── GET /mcp?session=N       → SSE stream (channel notifications)
  ├── DELETE /mcp?session=N    → terminate MCP session
  └── GET /controls            → dev endpoint (port, pid, stack size, MCP sessions)

Claude Code (in terminal)
  ├── MCP client → /mcp (channel notifications: element selected, network captured, ...)
  └── UserPromptSubmit hook → curl /stack (drain — fallback for non-MCP clients)

Install

pip install devpanel  # or: uv tool install devpanel
devpanel install --extension-id=EXTENSION_ID

Then load the extension in chrome://extensions → Load unpacked → point to the printed path.

The --extension-id is shown on the extensions page after loading. Re-run devpanel install with the correct ID.

Usage

  1. Open Chrome DevTools on any page
  2. Click the DevPanel tab
  3. A terminal opens with your shell (fish/bash/zsh) — DEVPANEL_PORT and DEVPANEL_SESSION env vars are set
  4. Your fish conf.d (see Shell setup below) wraps claude with --mcp-config, --settings, and --dangerously-load-development-channels when those env vars are present
  5. Run claude — it connects to the MCP server at http://127.0.0.1:$DEVPANEL_PORT/mcp and registers for channel notifications
  6. Click Select → pick elements → annotate → channel push fires immediately, Claude sees <channel source="devpanel"> in the conversation
  7. (Fallback) Type a prompt — hook drains any unsent context

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 only sets DEVPANEL_PORT and DEVPANEL_SESSION as env vars on the PTY. You configure your dotfiles to use those vars:

~/.config/fish/conf.d/devpanel.fish — guarded function that activates only inside DevPanel PTYs:

if set -q DEVPANEL_PORT
    function claude
        command claude \
            --mcp-config '{"mcpServers":{"devpanel":{"type":"http","url":"http://127.0.0.1:'$DEVPANEL_PORT'/mcp?session='$DEVPANEL_SESSION'"}}}' \
            --settings '{"hooks":{"UserPromptSubmit":[{"matcher":"","hooks":[{"type":"command","command":"curl -s http://127.0.0.1:$DEVPANEL_PORT/stack?session=$DEVPANEL_SESSION"}]}]}}' \
            --dangerously-load-development-channels server:devpanel \
            $argv
    end
end

~/.tmux.conf — pass env vars through tmux:

set -ga update-environment " DEVPANEL_PORT DEVPANEL_SESSION"

The fish function is harmless outside DevPanel (the if set -q guard skips it). The single-quote breaks ('...:'$DEVPANEL_PORT'/...') make fish expand the URL at function-call time; the hook's $DEVPANEL_PORT stays literal so Claude's hook executor expands it at hook-fire time.

How delivery works

Two paths, both wired up by the function above:

MCP channel notifications — when you click Select / Network / Screenshot, the panel POSTs to /stack. The daemon stores the item, then pushes notifications/claude/channel to all initialized MCP sessions for that tab. Claude Code receives it instantly via the SSE stream and renders <channel source="devpanel">...</channel> in the conversation.

UserPromptSubmit hook — on every prompt, Claude's hook executor curls GET /stack?session=.... The daemon returns formatted markdown of any pending items and clears the stack. This is the fallback for clients that don't have --dangerously-load-development-channels enabled.

Both fire by default. With channels active, items are typically already in the conversation by the time the hook drains; the hook just sees an empty stack.

Drain format

When the hook fires, the agent sees:

## Browser Context

### Page HTML
- https://www.wikipedia.org/ → /run/user/1000/devpanel/page-abc.html

### 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

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

Large data (HTML, HAR, 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.4.0.tar.gz (377.9 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.4.0-py3-none-any.whl (383.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: devpanel-0.4.0.tar.gz
  • Upload date:
  • Size: 377.9 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.4.0.tar.gz
Algorithm Hash digest
SHA256 a4e6e27e806ccb4d089998322044e48d23d86761e3dbbc0e5c173a6f59776500
MD5 f7c398c8f5b199ac52df5f0f6a94b75e
BLAKE2b-256 55a3b6736db6042544f0d7455d7902c26ceda3d6e5f8b5ecaa840cbdfd5aab64

See more details on using hashes here.

File details

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

File metadata

  • Download URL: devpanel-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 383.7 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.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 26944e66b0ba8bb5848d599b62521633f0d8f75909066e44ab0dbe0b046350e5
MD5 cc7e94ec6e2cba8194974060af3203f1
BLAKE2b-256 44ba32e4d88362cd9c2d2369ffcf605624f3297511df9c78dd1d41c4ab648b7f

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