# Browser Automation

> Browser automation for AI agents using PinchTab. Control Chrome programmatically for testing, scraping, and interaction. Features token-efficient text extraction, multi-instance orchestration, headless/headed modes, and MCP integration. Use when automating browser tasks, extracting web data, testing web apps, or validating sites in real browsers.

- Skill: `lord1egypt/browser-automation` (Agent Skill)
- Install (CLI): `npx skillmds@latest add lord1egypt/browser-automation`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lord1egypt/browser-automation/raw
- Safety review: pending (external: skill-scanner WARNING, skillspector WARNING)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: Lord1Egypt (https://skillmd.com/u/lord1egypt)
- Updated: 2026-09-08
- Page: https://skillmd.com/skills/lord1egypt/browser-automation

---


# Browser Automation

Browser automation for AI agents using [PinchTab](https://pinchtab.com) — a high-performance Chrome bridge with HTTP API.

## What is PinchTab?

- **Standalone HTTP server** — Control Chrome via HTTP API
- **Token-efficient** — 800 tokens/page with text extraction (vs 10,000+ for screenshots)
- **Multi-instance** — Run multiple parallel Chrome processes with isolated profiles
- **Headless or Headed** — Run without window or with visible Chrome
- **Self-contained** — 12MB binary, no external dependencies
- **MCP integration** — Native SMCP plugin for Claude Code

## Quick Start

### Installation

```bash
# macOS / Linux
curl -fsSL https://pinchtab.com/install.sh | bash

# npm
npm install -g pinchtab

# Docker
docker run -d -p 9867:9867 pinchtab/pinchtab
```

### Start Server

```bash
# Terminal 1: Start PinchTab server
pinchtab
# Server runs on http://localhost:9867
```

### Basic Commands

```bash
# Navigate
pinchtab nav https://pinchtab.com

# Wait 3 seconds for accessibility tree
sleep 3

# Get interactive elements
pinchtab snap -i -c

# Extract text (token-efficient)
pinchtab text

# Click element by ref
pinchtab click e5

# Fill input
pinchtab fill e3 "user@example.com"
```

---

## Core Concepts

### Instance

A running Chrome process. Each instance has isolated state.

```bash
# Create headless instance
pinchtab instances create --mode=headless

# Create headed instance (visible window)
pinchtab instances create --mode=headed

# List instances
pinchtab instances list

# Stop instance
pinchtab instances stop <instance-id>
```

### Profile

Browser state (cookies, history, localStorage). Log in once, stay logged in.

```bash
# Create instance with profile
pinchtab instances create --profile=work

# Profile persists across restarts
```

### Tab

A single webpage. Each instance can have multiple tabs.

```bash
# Open new tab
pinchtab tabs open https://example.com

# List tabs
pinchtab tabs list

# Close tab
pinchtab tabs close <tab-id>
```

---

## Token-Efficient Patterns

### The 3-Second Wait Rule

**Critical:** Chrome's accessibility tree takes ~3 seconds to populate after navigation.

```bash
# ❌ Too fast - empty tree
pinchtab nav https://example.com
pinchtab snap
# Returns: {"count": 1, "nodes": [{"ref": "e0"}]}

# ✅ Wait 3 seconds
pinchtab nav https://example.com
sleep 3
pinchtab snap
# Returns: {"count": 2645, "nodes": [...]}
```

### Optimal Extraction Pattern

```bash
# Navigate + wait + filter (14x token savings)
curl -X POST http://localhost:9867/navigate \
  -d '{"url": "https://example.com"}' && \
sleep 3 && \
curl http://localhost:9867/snapshot | \
jq '.nodes[] | select(.name | length > 15) | .name' | \
head -30
```

**Why this works:**
1. Navigate + wait ensures full accessibility tree
2. jq filter extracts text nodes only
3. `length > 15` filters buttons/labels
4. `head -30` limits output

**Token comparison:**
- Exploratory approach: ~3,800 tokens
- Pattern-driven: ~270 tokens
- **Savings: 14x**

---

## HTTP API Reference

### Base URL

```
http://localhost:9867
```

### Instances

```bash
# Create instance
TAB=$(curl -s -X POST http://localhost:9867/instances \
  -d '{"profile":"work","mode":"headless"}' | jq -r '.id')

# List instances
curl http://localhost:9867/instances

# Stop instance
curl -X POST "http://localhost:9867/instances/$TAB/stop"
```

### Navigation

```bash
# Navigate to URL
curl -X POST "http://localhost:9867/instances/$TAB/tabs/open" \
  -d '{"url":"https://example.com"}'

# Wait for load
sleep 3
```

### Snapshot

```bash
# Full snapshot
curl "http://localhost:9867/instances/$TAB/snapshot"

# Interactive elements only
curl "http://localhost:9867/instances/$TAB/snapshot?filter=interactive"

# With coordinates
curl "http://localhost:9867/instances/$TAB/snapshot?includeCoords=true"
```

### Actions

```bash
# Click element
curl -X POST "http://localhost:9867/instances/$TAB/action" \
  -d '{"kind":"click","ref":"e5"}'

# Type text
curl -X POST "http://localhost:9867/instances/$TAB/action" \
  -d '{"kind":"type","ref":"e12","text":"hello"}'

# Press key
curl -X POST "http://localhost:9867/instances/$TAB/action" \
  -d '{"kind":"key","ref":"e12","key":"Enter"}'

# Scroll
curl -X POST "http://localhost:9867/instances/$TAB/action" \
  -d '{"kind":"scroll","direction":"down"}'
```

### Extraction

```bash
# Extract text (token-efficient)
curl "http://localhost:9867/instances/$TAB/text"

# Take screenshot
curl "http://localhost:9867/instances/$TAB/screenshot" \
  --output screenshot.png

# Generate PDF
curl "http://localhost:9867/instances/$TAB/pdf" \
  --output page.pdf

# Evaluate JavaScript
curl -X POST "http://localhost:9867/instances/$TAB/evaluate" \
  -d '{"script": "document.title"}'
```

---

## Common Patterns

### Pattern 1: Web Scraping

```bash
#!/bin/bash
# scrape-headlines.sh

URL=$1
INST=$(curl -s -X POST http://localhost:9867/instances \
  -d '{"mode":"headless"}' | jq -r '.id')

# Navigate and wait
curl -s -X POST "http://localhost:9867/instances/$INST/tabs/open" \
  -d "{\"url\":\"$URL\"}"
sleep 3

# Extract headlines (filter by length)
curl -s "http://localhost:9867/instances/$INST/snapshot" | \
  jq '.nodes[] | select(.name | length > 20) | .name' | \
  head -20

# Cleanup
curl -s -X POST "http://localhost:9867/instances/$INST/stop"
```

### Pattern 2: Form Interaction

```bash
#!/bin/bash
# fill-form.sh

INST=$(curl -s -X POST http://localhost:9867/instances \
  -d '{"mode":"headless"}' | jq -r '.id')

# Navigate to form
curl -s -X POST "http://localhost:9867/instances/$INST/tabs/open" \
  -d '{"url":"https://example.com/login"}'
sleep 3

# Get snapshot to find element refs
SNAPSHOT=$(curl -s "http://localhost:9867/instances/$INST/snapshot?filter=interactive")

# Extract refs (example: e5=email, e7=password, e9=submit)
EMAIL_REF=$(echo $SNAPSHOT | jq -r '.nodes[] | select(.name | contains("email")) | .ref')
PASS_REF=$(echo $SNAPSHOT | jq -r '.nodes[] | select(.name | contains("password")) | .ref')
SUBMIT_REF=$(echo $SNAPSHOT | jq -r '.nodes[] | select(.role == "button") | .ref')

# Fill form
curl -s -X POST "http://localhost:9867/instances/$INST/action" \
  -d "{\"kind\":\"type\",\"ref\":\"$EMAIL_REF\",\"text\":\"user@example.com\"}"
curl -s -X POST "http://localhost:9867/instances/$INST/action" \
  -d "{\"kind\":\"type\",\"ref\":\"$PASS_REF\",\"text\":\"password123\"}"

# Submit
curl -s -X POST "http://localhost:9867/instances/$INST/action" \
  -d "{\"kind\":\"click\",\"ref\":\"$SUBMIT_REF\"}"

# Wait for navigation
sleep 3

# Verify login
curl -s "http://localhost:9867/instances/$INST/text" | jq -r '.title'

# Cleanup
curl -s -X POST "http://localhost:9867/instances/$INST/stop"
```

### Pattern 3: Multi-Instance Parallel Processing

```bash
#!/bin/bash
# parallel-scrape.sh

URLS=("https://site1.com" "https://site2.com" "https://site3.com")
INSTANCES=()

# Create instances
for i in {0..2}; do
  INST=$(curl -s -X POST http://localhost:9867/instances \
    -d '{"mode":"headless"}' | jq -r '.id')
  INSTANCES[$i]=$INST
done

# Launch parallel jobs
for i in {0..2}; do
  (
    curl -s -X POST "http://localhost:9867/instances/${INSTANCES[$i]}/tabs/open" \
      -d "{\"url\":\"${URLS[$i]}\"}"
    sleep 3
    TITLE=$(curl -s "http://localhost:9867/instances/${INSTANCES[$i]}/text" | jq -r '.title')
    echo "Result $i: $TITLE"
    curl -s -X POST "http://localhost:9867/instances/${INSTANCES[$i]}/stop"
  ) &
done

wait
echo "All complete"
```

### Pattern 4: Visual Regression Testing

```bash
#!/bin/bash
# visual-regression.sh

URLS=("https://staging.example.com" "https://production.example.com")
INST=$(curl -s -X POST http://localhost:9867/instances \
  -d '{"mode":"headless"}' | jq -r '.id')

for URL in "${URLS[@]}"; do
  curl -s -X POST "http://localhost:9867/instances/$INST/tabs/open" \
    -d "{\"url\":\"$URL\"}"
  sleep 3

  # Take screenshot
  FILENAME=$(echo $URL | sed 's/[^a-zA-Z0-9]/_/g').png
  curl -s "http://localhost:9867/instances/$INST/screenshot" \
    --output "$FILENAME"
  echo "Saved: $FILENAME"
done

curl -s -X POST "http://localhost:9867/instances/$INST/stop"
```

### Pattern 5: Session Persistence

```bash
#!/bin/bash
# persistent-session.sh

# Create instance with named profile
INST=$(curl -s -X POST http://localhost:9867/instances \
  -d '{"profile":"myaccount","mode":"headless"}' | jq -r '.id')

# Login once
curl -s -X POST "http://localhost:9867/instances/$INST/tabs/open" \
  -d '{"url":"https://example.com/login"}'
sleep 3
# ... perform login ...

# Stop (cookies saved to profile)
curl -s -X POST "http://localhost:9867/instances/$INST/stop"

# Later: Resume with same profile
INST2=$(curl -s -X POST http://localhost:9867/instances \
  -d '{"profile":"myaccount","mode":"headless"}' | jq -r '.id')

# Already logged in!
curl -s -X POST "http://localhost:9867/instances/$INST2/tabs/open" \
  -d '{"url":"https://example.com/dashboard"}'
```

---

## MCP Integration

PinchTab provides an SMCP plugin for native Claude Code integration.

### Setup

```bash
# Set plugin directory
export MCP_PLUGINS_DIR=/path/to/pinchtab/plugins

# Restart Claude Code to load plugin
```

### Available Tools

| Tool | Description |
|------|-------------|
| `pinchtab__navigate` | Navigate to URL |
| `pinchtab__snapshot` | Get page structure |
| `pinchtab__action` | Click, type, press keys |
| `pinchtab__text` | Extract text content |
| `pinchtab__screenshot` | Capture screenshot |
| `pinchtab__pdf` | Generate PDF |
| `pinchtab__evaluate` | Run JavaScript |
| `pinchtab__cookies_get` | Get cookies |
| `pinchtab__stealth_status` | Check stealth mode |

### Usage in Claude Code

```
Use pinchtab to navigate to example.com and extract the main headlines.
```

Claude will:
1. Call `pinchtab__navigate` with URL
2. Wait 3 seconds
3. Call `pinchtab__snapshot` with filter
4. Extract headlines from result

---

## Headless vs Headed

| Aspect | Headless | Headed |
|--------|----------|--------|
| **Window** | No visible UI | Chrome window visible |
| **Speed** | ~20% faster | Slower (rendering overhead) |
| **Memory** | ~50-80 MB | ~100-150 MB |
| **Use Case** | CI/CD, scraping, batch | Debugging, visual QA |
| **Interaction** | API only | API + manual |

```bash
# Headless for production
pinchtab instances create --mode=headless

# Headed for debugging
pinchtab instances create --mode=headed
```

---

## Best Practices

### DO

- ✅ Wait 3+ seconds after navigation
- ✅ Use text extraction over screenshots (token-efficient)
- ✅ Filter snapshots to reduce tokens
- ✅ Use profiles for persistent sessions
- ✅ Run headless in production
- ✅ Clean up instances after use
- ✅ Handle errors gracefully

### DON'T

- ❌ Skip the 3-second wait
- ❌ Take screenshots for text extraction
- ❌ Parse full snapshots without filtering
- ❌ Use headed mode in CI/CD
- ❌ Leave instances running indefinitely
- ❌ Hardcode element refs (they change)

---

## Troubleshooting

### Only getting 1 node in snapshot

**Cause:** Accessibility tree not ready
**Fix:** Increase wait time to 3+ seconds

```bash
pinchtab nav https://example.com
sleep 3  # Increase if needed
pinchtab snap
```

### Timeouts

**Cause:** Page too slow or Chrome overloaded
**Fix:** Increase sleep or use headless mode

```bash
# Increase wait
sleep 5

# Or use headless for faster rendering
pinchtab instances create --mode=headless
```

### Element not found

**Cause:** Refs change between snapshots
**Fix:** Re-snapshot before each action

```bash
# Get fresh refs before each action
REF=$(pinchtab snap -i | jq -r '.nodes[] | select(.name == "Submit") | .ref')
pinchtab click "$REF"
```

### Connection refused

**Cause:** PinchTab server not running
**Fix:** Start server first

```bash
pinchtab  # In separate terminal
```

---

## References

- [PinchTab Documentation](https://pinchtab.com/docs)
- [PinchTab GitHub](https://github.com/pinchtab/pinchtab)
- [Agent Optimization Guide](https://pinchtab.com/docs/guides/agent-optimization)
- [Common Patterns](https://pinchtab.com/docs/guides/common-patterns)
- [SMCP Plugin](https://github.com/pinchtab/pinchtab/tree/main/plugins)

---

*Token-efficient browser automation for AI agents.*

