Browse and visually inspect webpages using headless Chromium via Puppeteer MCP. Use when you need to see a page, interact with a web UI, extract information that WebFetch can't get (JS-rendered content, SPAs, authenticated pages), or when the user wants to show you something in a browser.
Scanned 9/12/2026
Install to Claude Code
npx -y skills add aibot88/sec_skill_store --skill browser-eyeballs --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Browser Eyeballs?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/aibot88-browser-eyeballs)More formats (shields.io, HTML) on the badges page.
---
name: browser-eyeballs
description: Browse and visually inspect webpages using headless Chromium via Puppeteer MCP. Use when you need to see a page, interact with a web UI, extract information that WebFetch can't get (JS-rendered content, SPAs, authenticated pages), or when the user wants to show you something in a browser.
---
# Browser Eyeballs
Visually browse webpages using a headless Chromium instance running in Docker via the Puppeteer MCP server.
## When to Use
- The user says "look at this page", "check this UI", "show you something"
- You need to see JS-rendered or SPA content that `WebFetch` returns as empty/useless
- You need to interact with a page (click, fill forms, scroll) to reach content
- You need to inspect visual layout, styling, or rendering
- You need to navigate an authenticated web app
- You need a screenshot of a page for reference or debugging
## Prerequisites
The Puppeteer MCP server must be configured in `~/.claude.json` under `mcpServers.puppeteer`. It runs headless Chromium inside Docker (`mcp/puppeteer-remote` image).
The recommended MCP config uses `bash -c` to automatically kill stale containers from previous sessions before starting a new one. Without this, a leftover container holding port 9222 will silently prevent MCP connection on the next session (and this cannot be fixed mid-session).
```json
"puppeteer": {
"type": "stdio",
"command": "bash",
"args": [
"-c",
"docker rm -f $(docker ps -aq --filter ancestor=mcp/puppeteer-remote) 2>/dev/null; exec docker run -i --rm --init -p 9222:9223 -e DOCKER_CONTAINER=true -e 'PUPPETEER_LAUNCH_OPTIONS={\"headless\":true,\"args\":[\"--remote-debugging-port=9222\",\"--remote-allow-origins=*\"]}' mcp/puppeteer-remote"
],
"env": {}
}
```
If the MCP tools (`mcp__puppeteer__puppeteer_navigate`, etc.) are not available, tell the user the Puppeteer MCP server is not configured or not running.
## Workflow
### Step 1: Navigate
The **first call in every session** must include `launchOptions`. Subsequent calls don't need them.
```
mcp__puppeteer__puppeteer_navigate({
url: "<target URL>",
allowDangerous: true,
launchOptions: {
headless: true,
args: ["--no-sandbox", "--remote-debugging-port=9222", "--remote-allow-origins=*"]
}
})
```
- `allowDangerous: true` is required for local/private network URLs (192.168.x.x, localhost, .local, etc.)
- For public HTTPS URLs, `allowDangerous` can be omitted but doesn't hurt
### Step 2: Wait and Screenshot
After navigation, take a screenshot to see the page:
```
mcp__puppeteer__puppeteer_screenshot()
```
The MCP tool returns the screenshot inline as an image — you can see and analyze it directly.
### Step 3: Interact (if needed)
Use the available MCP tools to interact with the page:
- **`puppeteer_click`** — click an element by CSS selector
- **`puppeteer_fill`** — type into an input field (selector + value)
- **`puppeteer_evaluate`** — run arbitrary JavaScript in the page context (for complex interactions, shadow DOM traversal, extracting data, scrolling, waiting)
After each interaction, take another screenshot to verify the result.
### Step 4: Extract Information
Two approaches depending on what's needed:
**Visual inspection** — just read the screenshot. Describe what you see.
**Data extraction** — use `puppeteer_evaluate` to pull structured data:
```js
// Example: extract all table rows
puppeteer_evaluate({
script: `JSON.stringify([...document.querySelectorAll('table tr')].map(r => [...r.cells].map(c => c.textContent.trim())))`
})
```
### Step 5: Save Screenshots (if needed)
The MCP screenshot tool shows images inline but doesn't save to disk. If the user needs a persistent screenshot, use `puppeteer_evaluate` to capture via CDP and save to `~/screenshots/`:
```js
puppeteer_evaluate({
script: `
const cdp = await page.target().createCDPSession();
const {data} = await cdp.send('Page.captureScreenshot', {format: 'png'});
require('fs').writeFileSync('/tmp/screenshot.png', Buffer.from(data, 'base64'));
`
})
```
Then copy from the container, or just rely on the inline screenshot for most use cases.
## Tips and Patterns
### Shadow DOM
Many web components (Home Assistant, Salesforce, etc.) use shadow DOM. Regular selectors won't reach inside. Use `puppeteer_evaluate` with manual traversal:
```js
document.querySelector('my-component').shadowRoot.querySelector('.inner-element')
```
### Waiting for Content
SPAs often load content asynchronously. If the screenshot shows a loading spinner, wait:
```js
puppeteer_evaluate({
script: `await new Promise(r => setTimeout(r, 2000))`
})
```
Or wait for a specific selector:
```js
puppeteer_evaluate({
script: `await document.querySelector('.content') || await new Promise(r => { const o = new MutationObserver(() => { if (document.querySelector('.content')) { o.disconnect(); r(); } }); o.observe(document.body, {childList: true, subtree: true}); setTimeout(r, 5000); })`
})
```
### Viewport Size
Default viewport is typically 1280x720. To change it:
```js
puppeteer_evaluate({ script: `await page.setViewport({width: 1920, height: 1080})` })
```
### Multiple Pages / Navigation
Just call `puppeteer_navigate` again with a new URL. No need to re-pass `launchOptions` after the first call.
### Network Considerations
The Chromium instance runs inside a Docker container on **bridge networking**. This means:
- `localhost` inside the container is the container itself, not the host
- To reach services on the host machine, use the host's LAN IP (e.g., `192.168.1.199`)
- Public URLs work normally
## Troubleshooting
### MCP tools not available
The Puppeteer MCP server connects at **session startup only**. If the `mcp__puppeteer__*` tools are not found (ToolSearch returns nothing), the server failed to start. This cannot be fixed mid-session.
If you're using the recommended MCP config (with `bash -c` cleanup), stale containers are cleaned up automatically. If tools are still missing after a fresh session start:
1. Check for stale containers manually: `docker ps -a --filter "ancestor=mcp/puppeteer-remote"`
2. Verify `~/.claude.json` has the `mcpServers.puppeteer` entry (see Prerequisites for the recommended config)
3. Check Docker is running: `docker ps`
### Container starts but tools still missing
Check `~/.claude.json` has `mcpServers.puppeteer` configured. The server must be defined at user level, not project level.
## What This is NOT
- Not a replacement for `WebFetch` — if you just need to read page content or an API response, `WebFetch` is faster and simpler
- Not a testing framework — this is for ad-hoc visual inspection and interaction
- Not persistent — the browser state (cookies, sessions) lives only as long as the MCP container runs
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!