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/channelpushed 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 /stackon 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
- Open Chrome DevTools on any page
- Click the DevPanel tab
- A terminal opens with your shell (fish/bash/zsh) —
DEVPANEL_PORTandDEVPANEL_SESSIONenv vars are set - Your fish conf.d (see Shell setup below) wraps
claudewith--mcp-config,--settings, and--dangerously-load-development-channelswhen those env vars are present - Run
claude— it connects to the MCP server athttp://127.0.0.1:$DEVPANEL_PORT/mcpand registers for channel notifications - Click Select → pick elements → annotate → channel push fires immediately, Claude sees
<channel source="devpanel">in the conversation - (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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4e6e27e806ccb4d089998322044e48d23d86761e3dbbc0e5c173a6f59776500
|
|
| MD5 |
f7c398c8f5b199ac52df5f0f6a94b75e
|
|
| BLAKE2b-256 |
55a3b6736db6042544f0d7455d7902c26ceda3d6e5f8b5ecaa840cbdfd5aab64
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
26944e66b0ba8bb5848d599b62521633f0d8f75909066e44ab0dbe0b046350e5
|
|
| MD5 |
cc7e94ec6e2cba8194974060af3203f1
|
|
| BLAKE2b-256 |
44ba32e4d88362cd9c2d2369ffcf605624f3297511df9c78dd1d41c4ab648b7f
|