demo-compressed.mp4
A browser extension that sends full element context (selector, dimensions, React component name, source file, and screenshot) straight to your active Claude Code session.
Instead of manually copying selectors, pasting screenshots, and describing layouts, you click once and Claude receives everything automatically. No copy-pasting, no context switching, no wasted tokens describing what Claude could just see.
This means faster fixes, more precise answers, and significantly fewer tokens spent on setup. Claude jumps straight to the solution.
Screenshots are never inlined into the conversation as text. Instead, the plugin stores them server-side and tells Claude a screenshot is available for a given message. Claude calls the get_screenshot MCP tool only when it actually needs to look, at which point the image is delivered as a native image block to Claude's vision encoder, not as a base64 string embedded in the prompt.
This means:
- Zero screenshot tokens when Claude can answer from the selector and component name alone
- Vision tokens only when needed (far cheaper per unit of visual information than text tokens)
- No context bloat (the conversation stays clean regardless of how many elements you inspect)
- Install the extension from the Chrome Web Store
- Install the plugin inside Claude Code — one-time only:
/plugin install claude-code-inspect@claude-plugins-official - Start Claude Code with the extension connected:
claude --channels plugin:claude-code-inspect@claude-plugins-official
- Open any webpage, click the Claude Code Inspect icon in your toolbar
- Click Inspect, then click any element on the page
- Type your question in the chat box and press Enter
Claude Code receives your question with full element context (selector, dimensions, React component name, source file, domain name, and a screenshot) and responds directly in the terminal.
If the extension shows a red "Disconnected" indicator, it will show both commands to copy.
The extension captures element details (selector, dimensions, React component info, screenshot) and sends them alongside your question to an active Claude Code session. Claude sees the full context and can help with styling, debugging, accessibility, or anything else about the element.
Browser Extension -> POST localhost:9999 -> MCP Channel Server -> Claude Code session
|
screenshot store
(keyed by message_id,
fetched on demand via
get_screenshot tool)
Claude Code spawns server.ts automatically as a subprocess when started with --channels. The server listens on localhost:9999 for messages from the extension and forwards them into the session via notifications/claude/channel.
claude-code-inspect/
├── apps/
│ ├── extension/ # Chrome extension (React + TypeScript + Vite)
│ └── plugin/ # MCP channel server (Node + TypeScript)
│ └── .claude-plugin/
│ └── plugin.json # Tells Claude Code how to spawn the server
└── package.json # npm workspace root
| Tool | Version | Required for |
|---|---|---|
| Node.js + npx | 18+ | Running the plugin |
| Claude Code | v2.1.80+ | Receiving messages |
| Chrome / Chromium | 120+ | Loading the extension |
Install from the Chrome Web Store.
Run this inside Claude Code:
/plugin install claude-code-inspect@claude-plugins-official
claude --channels plugin:claude-code-inspect@claude-plugins-officialClaude Code spawns server.ts as a subprocess and the HTTP listener starts automatically on port 9999. When the extension detects it, the header turns green.
- Open any webpage and click the Claude Code Inspect icon to open the side panel
- Click Inspect, then click any element on the page
- The element's details appear in the panel (selector, size, React component, screenshot)
- Type a question in the chat box and press Enter
- Your question, with element context attached, lands in your Claude Code session
The header shows the connection state at a glance:
| State | Meaning |
|---|---|
| 🟢 Connected | Ready to send |
| 🔴 Disconnected | Start Claude Code with the command shown |
npm run buildThen load apps/extension/dist/ as an unpacked extension in Chrome (chrome://extensions -> Developer mode -> Load unpacked).
Start the plugin standalone and verify the HTTP server is working before involving Claude Code:
# Terminal 1 - start the plugin
cd apps/plugin && npx tsx server.ts
# Terminal 2 - health check
curl http://localhost:9999/health
# Terminal 3 - send a test message
curl -X POST http://localhost:9999/message \
-H 'Content-Type: application/json' \
-d '{"content":"hello from test","context":{"tagName":"div","htmlPath":"#root > div","boundingRect":{"width":100,"height":50},"url":"https://example.com"}}'claude --plugin-dir /path/to/claude-code-inspect/apps/plugin --dangerously-load-development-channels plugin:claude-code-inspect@inlineThen open Chrome, inspect an element in the extension, type a message, and it should appear in the Claude Code terminal.
# Terminal 1 - rebuild extension on save
npm run dev
# Terminal 2 - Claude Code with channel server (auto-restarts server.ts on change)
claude --plugin-dir /path/to/claude-code-inspect/apps/plugin --dangerously-load-development-channels plugin:claude-code-inspect@inlineReload the unpacked extension in Chrome after the extension rebuilds (chrome://extensions -> refresh icon).
npm run devRebuilds apps/extension/dist/ on every file change. Reload the extension in Chrome after each build (chrome://extensions -> refresh icon).
cd apps/plugin
bun run devRestarts the bridge server on every change to server.ts.
npm run typecheck # extension
cd apps/plugin && bun run typecheck # pluginnpm run lint # check
npm run lint:fix # auto-fixThe plugin (apps/plugin/server.ts) is a Node process that does two things simultaneously:
- MCP server over stdio: Claude Code spawns it as a subprocess via
.claude-plugin/plugin.jsonand communicates over stdin/stdout. Theclaude/channelexperimental capability tells Claude Code to register a notification listener for incoming events. - HTTP server on
localhost:9999: acceptsPOST /messagefrom the extension and forwards to Claude Code vianotifications/claude/channel; exposesGET /healthfor the extension's connection polling.
The port defaults to 9999 and can be changed via the BRIDGE_PORT environment variable.
When a message arrives with a screenshot, the plugin:
- Strips the
data:URI prefix and stores the raw base64 in an in-memory map keyed bymessage_id - Sends a channel notification with a text hint instead of the image data:
screenshot: available, call get_screenshot('<message_id>') to view it - Exposes a
get_screenshotMCP tool that returns a nativeimagecontent block when called
Claude's vision encoder receives the image directly. It is never serialized into the text context. Screenshots that Claude doesn't need (answerable from selector/component info alone) cost zero tokens.
Channel notifications delivered to Claude Code look like:
<channel source="claude-code-inspect" chat_id="browser" ...>
Why is this button not aligning correctly?
---
url: https://example.com
html: #root > main > .actions > button
react: <SubmitButton />
src: src/components/SubmitButton.tsx:42
screenshot: available, call get_screenshot('abc-123') to view it
</channel>
| Command | What it does |
|---|---|
npm run build |
Build the extension for production |
npm run dev |
Build the extension in watch mode |
npm run typecheck |
Type-check the extension |
npm run lint |
Lint the extension |
npm run clean |
Remove apps/extension/dist/ |
cd apps/plugin && npx tsx server.ts |
Run the plugin once |
Extension – React 19, TypeScript, Tailwind CSS 4, Vite 8, Manifest V3
Plugin – Node.js, TypeScript, @modelcontextprotocol/sdk
Monorepo - npm workspaces
License – Elastic License 2.0 – free to use and contribute; redistribution as a competing product is not permitted