
chrome-debug
This skill should be used when debugging web applications, diagnosing page errors, inspecting consol
Chrome Debug Skill for Claude Code
A Claude Code skill and standalone CLI tool for controlling Chrome via the Chrome DevTools Protocol (CDP). Enables automated web app debugging, console monitoring, and screenshot capture.
Built as a single-file Python script using uv inline dependencies — no requirements.txt, no manual venv, no setup required.
Use it as:
- 🤖 Claude Code Skill - Claude automatically debugs your web apps
- 🛠️ Standalone Tool - Command-line Chrome automation via
chromectl.py
Features
- Zero-setup: Just run the script, uv handles all dependencies automatically
- Browser automation: Open tabs, evaluate JavaScript, capture screenshots
- Console monitoring: Stream live console messages from any tab
- Full-page screenshots: Capture entire pages, not just the viewport
- Headless support: Run Chrome in the background without a visible window
Install as Claude Code Skill
Want Claude to help debug your web apps automatically? Install this as a skill for Claude Code.
Quick Install
cd ~/.claude/skills
git clone https://github.com/pengelbrecht/chrome-debug-skill.git chrome-debug
Then restart Claude Code.
What does the skill do?
Once installed, Claude will automatically use this skill when you ask for help with:
- "Debug why this page isn't loading correctly"
- "Take a screenshot of https://example.com"
- "Check the console for errors on my webapp"
- "Monitor console output while I test my app"
The skill enables collaborative debugging where Claude can:
- Launch Chrome in visible mode (so you can interact with the page)
- Monitor console errors in real-time
- Capture screenshots to identify visual issues
- Execute JavaScript to inspect page state
- All while you navigate and test your application
See SKILL.md for complete skill documentation and workflows.
Standalone Installation (Without Claude Code)
Clone the repository and use the script directly:
git clone https://github.com/pengelbrecht/chrome-debug-skill.git
cd chrome-debug-skill
chmod +x scripts/chromectl.py
That's it! On first run, uv will automatically install the required dependency (aiohttp).
Quick Start
-
Launch Chrome with remote debugging:
scripts/chromectl.py start --headless -
List all open tabs:
scripts/chromectl.py list -
Open a new tab:
scripts/chromectl.py open https://example.com # Output: {"id":"ABC123...","url":"https://example.com"} -
Run JavaScript:
scripts/chromectl.py eval --id ABC123 -e "document.title" # Output: "Example Domain" -
Stop Chrome when done (important!):
scripts/chromectl.py stop⚠️ Always run
stopwhen finished to clean up Chrome processes and allow normal Chrome to launch from Finder.
Commands
start - Launch Chrome with debugging enabled
scripts/chromectl.py start [--headless] [--port PORT] [--chrome-app NAME] [--user-data-dir PATH]
Options:
--headless- Run Chrome in headless mode (no visible window)--port- Remote debugging port (default: 9222)--chrome-app- macOS app name (default: "Google Chrome")--user-data-dir- Custom profile directory (default: ~/chromectl-profile)
Important: Each debugging instance uses a separate profile directory, allowing you to run multiple instances simultaneously without closing your regular Chrome browser.
Examples:
# Headless mode (recommended for automation)
scripts/chromectl.py start --headless
# Visible Chrome window
scripts/chromectl.py start
# Use Chrome Canary
scripts/chromectl.py start --chrome-app "Google Chrome Canary"
stop - Stop all chromectl-managed Chrome instances
scripts/chromectl.py stop
Finds and stops all Chrome instances launched by chromectl (identifies them by the chromectl profile directories).
Why use this:
- Cleans up background Chrome processes
- Frees up debugging ports (9222, etc.)
- Allows you to launch regular Chrome from macOS Finder
- Prevents interference with your normal Chrome usage
Output:
Stopping Chrome instance (PID: 5595)
Stopping Chrome instance (PID: 5602)
...
Stopped 8 Chrome instance(s)
Important: Always run stop when you're done debugging. Headless Chrome instances run invisibly in the background and can prevent normal Chrome from launching properly.
list - List all open tabs/targets
scripts/chromectl.py list
Shows all available targets (tabs, extensions, service workers) with their IDs, URLs, and titles.
Output format: One JSON object per line
{"id": "ABC123...", "type": "page", "title": "Example", "url": "https://example.com", "attached": null}
open - Open a new tab
scripts/chromectl.py open <url>
Opens a new tab at the specified URL and returns its target ID.
Examples:
# Open a website
scripts/chromectl.py open https://github.com
# Open a data URL with inline HTML/JS
scripts/chromectl.py open "data:text/html,<h1>Hello</h1>"
Tip: Save the target ID for use with other commands:
TARGET=$(scripts/chromectl.py open https://example.com | jq -r .id)
scripts/chromectl.py eval --id $TARGET -e "document.title"
eval - Evaluate JavaScript
scripts/chromectl.py eval --id <target-id> -e <expression>
Executes JavaScript in the specified tab and returns the result.
Features:
- Automatically awaits promises (
awaitPromise: true) - Returns JSON-serialized values
- REPL mode enabled for cleaner output
Examples:
# Get page title
scripts/chromectl.py eval --id ABC123 -e "document.title"
# Get current URL
scripts/chromectl.py eval --id ABC123 -e "window.location.href"
# Return an object
scripts/chromectl.py eval --id ABC123 -e "({title: document.title, url: location.href})"
# Async operations work automatically
scripts/chromectl.py eval --id ABC123 -e "fetch('https://api.github.com').then(r => r.json())"
# DOM manipulation
scripts/chromectl.py eval --id ABC123 -e "document.querySelector('h1').innerText"
screenshot - Capture a PNG screenshot
scripts/chromectl.py screenshot --id <target-id> [-o output.png] [--full-page]
Options:
-o, --output- Output file path (default:screenshot_<id>.png)--full-page- Capture entire page by resizing viewport to content height
Examples:
# Viewport screenshot (visible area only)
scripts/chromectl.py screenshot --id ABC123 -o page.png
# Full-page screenshot (entire scrollable content)
scripts/chromectl.py screenshot --id ABC123 -o fullpage.png --full-page
Output: Prints the filename when complete
page.png
console-tail - Stream console messages
scripts/chromectl.py console-tail --id <target-id> [--for SECONDS]
Streams console output from the specified tab in real-time.
Options:
--for- Duration in seconds to stream (default: 10)
Important: Only captures messages logged after the command starts. Historical console messages are not shown.
Output format: One JSON object per line
{"t": "+2.011s", "console": "log", "args": ["Test message"]}
{"t": "+2.015s", "console": "warning", "args": ["Warning text"]}
{"t": "+2.020s", "console": "error", "args": ["Error message"]}
Usage pattern:
# Start tailing in the background
scripts/chromectl.py console-tail --id ABC123 --for 30 &
# Then interact with the page
scripts/chromectl.py eval --id ABC123 -e "console.log('Hello from eval')"
Example: Monitor a page with active logging
# Open a page that logs continuously
scripts/chromectl.py open "data:text/html,<script>setInterval(() => console.log('tick', Date.now()), 1000)</script>"
# Start monitoring (captures new messages for 10 seconds)
scripts/chromectl.py console-tail --id <target-id> --for 10
How It Works
- Shebang magic:
#!/usr/bin/env -S uv runtells your shell to execute viauv run - Inline dependencies: The
# /// scriptblock (PEP 723) embeds dependency info directly in the file - Auto-caching: On first run, uv resolves and caches
aiohttp(~17ms after initial install) - Chrome DevTools Protocol: Communicates with Chrome over HTTP and WebSocket on port 9222
Tips & Tricks
Run multiple debugging instances
You can run multiple Chrome instances simultaneously without closing your regular browser:
# Instance 1 on port 9222
scripts/chromectl.py start --headless
# Instance 2 on port 9223 (doesn't interfere with your regular Chrome)
scripts/chromectl.py start --headless --port 9223 --user-data-dir ~/chromectl-test
# Use the second instance
scripts/chromectl.py --port 9223 list
Closing Chrome cleanly
# Use the built-in stop command (recommended)
scripts/chromectl.py stop
# Or manually close all Chrome instances
killall "Google Chrome"
# Or find and kill specific debugging instance by PID
ps aux | grep chromectl-profile
kill <PID>
Chain commands with jq
# Open, capture ID, and screenshot in one go
TARGET=$(scripts/chromectl.py open https://github.com | jq -r .id)
scripts/chromectl.py screenshot --id $TARGET -o github.png
Monitor console while running tests
# Terminal 1: Start console monitoring
scripts/chromectl.py console-tail --id ABC123 --for 60
# Terminal 2: Run your automation
scripts/chromectl.py eval --id ABC123 -e "runTests()"
Debug connection issues
# Check if Chrome is listening on the debug port
lsof -i :9222
# Test HTTP endpoint directly
curl http://localhost:9222/json | jq .
Troubleshooting
"Cannot connect to host 127.0.0.1:9222"
- No Chrome instance with remote debugging is running on that port
- Start one with:
./chromectl.py start --headless - If port is taken, use a different port:
./chromectl.py start --headless --port 9223
"Target not found"
- The tab was closed or the ID is incorrect
- Run
./chromectl.py listto get current target IDs
Port already in use
- Another Chrome instance is using port 9222
- Use
--port 9223to specify a different port
Requirements
- macOS (uses
open -ato launch Chrome; Linux/Windows need different launch commands) - uv (Install here)
- Google Chrome installed in
/Applications/
License
MIT