# Pinchtab MCP Wrapper

[![MCP](https://img.shields.io/badge/MCP-Compatible-green.svg)](https://modelcontextprotocol.io/)
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

An MCP (Model Context Protocol) server that exposes the full [Pinchtab](https://github.com/pinchtab/pinchtab) browser API to AI coding agents. Built with token efficiency in mind — perfect for data extraction, web scraping, and automated testing workflows.

**🙏 Credits:** This wrapper is built on top of the excellent [Pinchtab](https://github.com/pinchtab/pinchtab) project by [Luigi Agosti](https://github.com/luigi-agosti) and the Pinchtab team. Pinchtab is a lightweight 12MB Go binary that provides browser automation via HTTP API with accessibility-first snapshots.

---

## Why Use Pinchtab for AI Agents?

When AI agents need to browse the web, efficiency matters. Pinchtab delivers **5-13x cost savings** compared to traditional browser automation.

### Cost Comparison (Real Numbers)

| Task | Traditional Browser | Pinchtab | Savings |
|------|-------------------|----------|---------|
| Extract article text | ~10,000 tokens (full DOM) | **~800 tokens** | **12.5x cheaper** |
| Find interactive elements | ~10,500 tokens | **~3,600 tokens** | **3x cheaper** |
| 50-page monitoring task | ~$0.30 | **~$0.01** | **30x cheaper** |

### Key Advantages for AI Workflows

**1. Data Extraction & Web Scraping**
- Extract clean, structured text without HTML overhead
- Accessibility tree provides semantic structure (roles, labels, states)
- Filter to interactive elements only when needed

**2. Website Change Monitoring**
- `diff=true` returns only changed elements since last snapshot
- Track price changes, content updates, or availability
- Monitor 50+ pages without context overflow

**3. Async Multi-Tab Operations**
- Run 20+ tabs simultaneously (vs 3-5 with Playwright)
- Tab locking prevents conflicts between agents
- Each tab ~1MB RAM vs 50MB+ for full browser instances

**4. Stable Automation**
- Stable element references (`e0`, `e1`) survive CSS changes
- No brittle CSS selectors that break on redesigns
- Works with sites that block traditional automation (stealth mode)

---

## Quick Start

### One-Line Installation

```bash
curl -fsSL https://raw.githubusercontent.com/BDuba/pinchtab-mcp-wrapper/main/install.sh | bash
```

**Requirements:** Docker, Node.js 18+, npm

**Then restart your AI agent (OpenCode, Claude Code, etc.)**

### Test It

```
Open https://example.com and take a screenshot
```

Or use commands:
```
/browse https://example.com
/screenshot
```

---

## Use Cases & Examples

### 1. Web Scraping at Scale

**Problem:** Extract product prices from 100 e-commerce pages

**Traditional approach:**
- Load full DOM (~10,000 tokens per page)
- Parse HTML manually
- Cost: 1,000,000 tokens (~$3.00)

**Pinchtab approach:**
```javascript
// Extract structured text only
pinchtab_read_page({url: "https://store.com/product"})
// Returns clean text with prices, descriptions
```
- Cost: 80,000 tokens (~$0.24)
- **Savings: $2.76 (12.5x cheaper)**

### 2. Change Detection & Monitoring

**Problem:** Monitor competitor prices or news updates

```javascript
// First visit - establish baseline
snapshot1 = pinchtab_snapshot({url: "https://competitor.com/prices"})

// Later visits - get only changes
diff = pinchtab_snapshot({
  url: "https://competitor.com/prices", 
  diff: true
})
// Returns only modified elements (50-200 tokens vs 10,000)
```

**Benefits:**
- Track 50 sites without context overflow
- Instant detection of changes
- 200x less data transfer

### 3. Multi-Source Research

**Problem:** Gather information from 10+ documentation sites simultaneously

```javascript
// Open multiple tabs in parallel
tab1 = pinchtab_tab_open({url: "https://docs.api1.com"})
tab2 = pinchtab_tab_open({url: "https://docs.api2.com"})
tab3 = pinchtab_tab_open({url: "https://docs.api3.com"})

// Extract text from all tabs
text1 = pinchtab_read_page({tabId: tab1.tabId})
text2 = pinchtab_read_page({tabId: tab2.tabId})
text3 = pinchtab_read_page({tabId: tab3.tabId})
```

**Benefits:**
- 20+ concurrent tabs
- No memory issues (1MB per tab)
- Async operations

### 4. Form Automation That Lasts

**Problem:** Automate login/checkout flows that break on website updates

**Traditional selectors break:**
```javascript
// Brittle - breaks when CSS changes
await page.click('#login-btn')  // ❌
```

**Pinchtab stable refs:**
```javascript
// Stable - survives redesigns
pinchtab_action({
  tabId: "...",
  kind: "click",
  ref: "e5"  // ✅ Persistent reference
})
```

### 5. Accessibility-First Testing

**Built-in accessibility tree:**
- Element roles (button, link, heading)
- Accessible names and labels
- Focus states and visibility
- Screen reader compatible

```javascript
// Get only interactive elements
interactives = pinchtab_list_interactives()
// Returns: buttons, links, inputs with labels
```

---

## Available Tools

| Tool | Purpose |
|------|---------|
| `pinchtab_tab_open` | Open URL in new tab |
| `pinchtab_read_page` | Extract page text (token-efficient) |
| `pinchtab_list_interactives` | Get clickable elements |
| `pinchtab_snapshot` | Get accessibility tree |
| `pinchtab_action` | Click, type, fill forms |
| `pinchtab_screenshot` | Take JPEG screenshot |
| `pinchtab_evaluate` | Run JavaScript |
| `pinchtab_download` | Download files using browser session (preserves cookies, auth, stealth) |
| `pinchtab_upload` | Upload files to file inputs (local paths, base64, or data URLs) |
| `pinchtab_tab_lock` | Lock tab for exclusive access |

---

## CLI Integration

### OpenCode

Add to `~/.config/opencode/opencode.json`:

```json
{
  "\$schema": "https://opencode.ai/config.json",
  "mcp": {
    "pinchtab": {
      "type": "local",
      "command": [
        "/usr/bin/node",
        "~/.pinchtab-mcp-wrapper/dist/index.js"
      ],
      "environment": {
        "PINCHTAB_MODE": "external",
        "PINCHTAB_URL": "http://127.0.0.1:9867",
        "PINCHTAB_TOKEN": "opencode-browser-token-secure",
        "MCP_TRANSPORT": "stdio",
        "LOG_LEVEL": "info"
      },
      "enabled": true
    }
  },
  "agent": {
    "browser": {
      "description": "Browser automation specialist",
      "prompt": "Use pinchtab MCP tools for all web browsing tasks. Prefer text extraction over screenshots for efficiency.",
      "tools": {
        "pinchtab*": true,
        "playwright*": false
      }
    }
  },
  "default_agent": "browser"
}
```

**Prerequisites:** Ensure Pinchtab Docker container is running:
```bash
docker ps | grep pinchtab  # Check if running
docker start pinchtab       # Start if stopped
```

### Claude Code

Add to `.mcp.json`:

```json
{
  "mcpServers": {
    "pinchtab": {
      "command": "bash",
      "args": ["~/.pinchtab-mcp-wrapper/run-mcp.sh"]
    }
  }
}
```

**Claude Code Skill (Experimental):** We also provide a skill file for Claude Code CLI users. See [`CLAUDE_CODE_SKILL.md`](CLAUDE_CODE_SKILL.md) for detailed installation and usage instructions in Spanish. You can save this file to `~/.claude/commands/pinchtab.md` and use `/pinchtab` to access the full reference directly in your CLI.

### Cursor

Add to Cursor's MCP settings (Settings → Features → MCP):

```json
{
  "mcpServers": {
    "pinchtab": {
      "type": "stdio",
      "command": "bash",
      "args": ["~/.pinchtab-mcp-wrapper/run-mcp.sh"]
    }
  }
}
```

### Zed

Add to `~/.config/zed/settings.json`:

```json
{
  "assistant": {
    "version": "2",
    "enabled": true
  },
  "context_servers": {
    "pinchtab": {
      "command": "bash",
      "args": ["~/.pinchtab-mcp-wrapper/run-mcp.sh"]
    }
  }
}
```

---

---

## Automatic Configuration for Multiple AI Agents

The installer now automatically detects and configures pinchtab for multiple AI agents:

- **OpenCode** (`~/.config/opencode/opencode.json`)
- **Claude Code** (`~/.mcp.json` or `./.mcp.json`)
- **Cursor** (`~/.config/Cursor/User/settings.json`)
- **Zed** (`~/.config/zed/settings.json`)

If a config file already exists, the installer will:
1. Create a backup (`.backup.YYYYMMDD_HHMMSS`)
2. Automatically merge pinchtab configuration using Node.js
3. Preserve all existing settings

No manual editing required - just run the installer and restart your AI agent!

## Installation Options

The install script now supports automatic mode detection and graceful fallback:

### Automatic Mode (Default)

The installer automatically detects your environment and chooses the best mode:

```bash
curl -fsSL https://raw.githubusercontent.com/BDuba/pinchtab-mcp-wrapper/main/install.sh | bash
```

**What happens:**
1. Checks for Docker (including macOS-specific paths: Homebrew, OrbStack, Docker Desktop)
2. If Docker is available → builds Docker image and uses `docker` mode
3. If Docker is unavailable → automatically downloads Pinchtab binary and uses `external` mode

### Docker Mode

Recommended for most users. Requires Docker to be installed.

```bash
export PINCHTAB_MODE=docker
curl -fsSL https://raw.githubusercontent.com/BDuba/pinchtab-mcp-wrapper/main/install.sh | bash
```

### External Mode (Binary)

For systems without Docker. The installer automatically downloads the Pinchtab binary for your architecture.

```bash
export PINCHTAB_MODE=external
curl -fsSL https://raw.githubusercontent.com/BDuba/pinchtab-mcp-wrapper/main/install.sh | bash
```

### macOS Support

The installer automatically detects Docker in the following locations:
- `/opt/homebrew/bin/docker` (Homebrew on Apple Silicon)
- `/usr/local/bin/docker` (Homebrew on Intel)
- `/Applications/OrbStack.app/Contents/MacOS/../bin/docker` (OrbStack)
- `/Applications/Docker.app/Contents/Resources/bin/docker` (Docker Desktop)
- `~/.docker/bin/docker` (Docker Desktop alternative)

You can also manually specify the Docker path:

```bash
export DOCKER_PATH=/opt/homebrew/bin/docker
curl -fsSL https://raw.githubusercontent.com/BDuba/pinchtab-mcp-wrapper/main/install.sh | bash
```

## Manual Installation

```bash
# Clone
git clone https://github.com/BDuba/pinchtab-mcp-wrapper.git
cd pinchtab-mcp-wrapper

# Build
npm install && npm run build

# Build Docker image
docker build -f pinchtab.Dockerfile -t pinchtab:local .

# Create wrapper
cat > run-mcp.sh << 'EOF'
#!/bin/bash
export PINCHTAB_MODE=docker
export PINCHTAB_TOKEN=opencode-browser-token-secure
export PINCHTAB_DOCKER_IMAGE=pinchtab:local
exec node "$(dirname "$0")/dist/index.js"
EOF
chmod +x run-mcp.sh
```

---

## Configuration

### Screenshot File Delivery (New)

When using `delivery: "file"`, you can now use relative paths and auto-naming:

```json
{
  "tool": "screenshot",
  "params": {
    "delivery": "file"
    // Auto-saved to: ~/Pictures/Screenshots/2026-02-21T12-34-56-tab-123.jpg
  }
}
```

```json
{
  "tool": "screenshot",
  "params": {
    "delivery": "file",
    "path": "google/homepage.jpg"
    // Saved to: ~/Pictures/Screenshots/google/homepage.jpg
  }
}
```

```json
{
  "tool": "screenshot",
  "params": {
    "delivery": "file",
    "path": "/absolute/path/to/screenshot.png"
    // Absolute paths still work as before
  }
}
```

| Variable | Description | Default |
|----------|-------------|---------|
| `SCREENSHOTS_DIR` | Base directory for screenshots | OS-specific (~/Pictures/Screenshots) |
| `SCREENSHOTS_AUTO_CREATE` | Auto-create directories | `true` |
| `SCREENSHOTS_PATTERN` | Filename pattern | `{timestamp}-{tabId}.jpg` |


### Environment Variables

```bash
# Transport (v0.5.0+)
MCP_TRANSPORT=stdio               # stdio | streamable-http | sse
MCP_HTTP_PORT=3000                # HTTP server port
MCP_HTTP_HOST=0.0.0.0             # HTTP server host
MCP_AUTH_TYPE=none                # none | bearer | api-key
MCP_AUTH_TOKEN=secret-token       # Auth token for HTTP mode
MCP_ALLOWED_ORIGINS=*             # CORS origins

# Pinchtab connection
PINCHTAB_MODE=docker              # docker | external
PINCHTAB_TOKEN=secret-token       # Auth token
DEFAULT_SNAPSHOT_FORMAT=compact   # compact | text | json
DEFAULT_MAX_TOKENS=2500           # Token budget
SCREENSHOT_DEFAULT_DELIVERY=base64 # base64 | s3 | file
```

### HTTP Mode (v0.5.0+)

Pinchtab MCP Wrapper now supports Streamable HTTP transport for remote/cloud deployments.

**Use cases for HTTP mode:**
- Cloud deployments (AWS, GCP, Azure)
- Remote AI agent access
- Serverless architectures
- Load balancing across multiple instances

**Quick start with HTTP:**

```bash
# Terminal 1: Start MCP server in HTTP mode
export MCP_TRANSPORT=streamable-http
export MCP_HTTP_PORT=3000
export MCP_AUTH_TYPE=bearer
export MCP_AUTH_TOKEN=your-secret-token
npm start

# Terminal 2: Test the HTTP endpoint
curl http://localhost:3000/health
```

**HTTP Mode Configuration:**

| Variable | Description | Default |
|----------|-------------|---------|
| `MCP_TRANSPORT` | Transport type | `stdio` |
| `MCP_HTTP_PORT` | HTTP server port | `3000` |
| `MCP_HTTP_HOST` | HTTP server host | `0.0.0.0` |
| `MCP_HTTP_PATH` | MCP endpoint path | `/mcp` |
| `MCP_AUTH_TYPE` | Auth type: none, bearer, api-key | `none` |
| `MCP_AUTH_TOKEN` | Auth token (required for bearer/api-key) | - |
| `MCP_ALLOWED_ORIGINS` | CORS allowed origins (comma-separated) | `*` |
| `MCP_ENABLE_SESSIONS` | Enable stateful sessions | `true` |
| `MCP_SESSION_TIMEOUT` | Session timeout in seconds | `3600` |

**Security best practices for HTTP mode:**
1. Always use authentication in production (`MCP_AUTH_TYPE=bearer`)
2. Restrict CORS origins (`MCP_ALLOWED_ORIGINS=https://yourdomain.com`)
3. Use HTTPS with a reverse proxy (nginx, traefik)
4. Keep auth tokens secure and rotate regularly
**Streamable HTTP Endpoint Details:**

When running in HTTP mode, the MCP server exposes:

| Endpoint | Method | Description |
|----------|--------|-------------|
| `http://host:port/mcp` | POST/GET | Main MCP Streamable HTTP endpoint |
| `http://host:port/health` | GET | Health check endpoint |
|| `http://host:port/health` | GET | Health check endpoint |
| `http://host:port/status` | GET | Status endpoint (alias for health) |

#### LobeChat Integration

For **LobeChat v2** (and other HTTP-based MCP clients):

1. **Start MCP in HTTP mode** (listening on all interfaces for Docker network access):
```bash
./run-mcp-http-all.sh
# or
export MCP_TRANSPORT=streamable-http
export MCP_HTTP_HOST=0.0.0.0
export MCP_HTTP_PORT=3001
export MCP_AUTH_TYPE=none
npm start
```

2. **Configure in LobeChat:**
   - Go to **Settings** → **Plugins** → **MCP**
   - Add new MCP server:
     - **Name**: `pinchtab`
     - **Type**: `HTTP` (or `Streamable HTTP`)
     - **URL**: `http://172.21.0.1:3001/mcp` (adjust IP based on your Docker network)
     - **Auth Type**: `None`
   - Click **"Test Connection"**

3. **Finding the correct IP address:**
   - Check your Docker network: `docker network inspect bridge`
   - Find the gateway IP for your container network
   - Common values:
     - `172.17.0.1` - default Docker bridge
     - `172.21.0.1` - custom networks
     - `host.docker.internal` - Docker Desktop (Mac/Windows)

**Note:** The `run-mcp-http-all.sh` script listens on `0.0.0.0` to allow connections from any Docker container.
**Example MCP Client Configuration:**

```javascript
// MCP Client Streamable HTTP configuration
{
  "mcpServers": {
    "pinchtab": {
      "transport": "streamable-http",
      "url": "http://localhost:3000/mcp",
      "headers": {
        "Authorization": "Bearer your-secret-token"
      }
    }
  }
}
```

**HTTP Headers:**

| Header | Required | Description |
|--------|----------|-------------|
| `Authorization` | If auth enabled | `Bearer <token>` or raw token for api-key |
| `Content-Type` | Yes | `application/json` |
| `Mcp-Session-Id` | Optional | Session ID for stateful mode |

**Authentication Examples:**

```bash
# Bearer token auth
curl -H "Authorization: Bearer your-secret-token" \
     http://localhost:3000/mcp

# API key auth (header)
curl -H "Authorization: your-api-key" \
     http://localhost:3000/mcp

# API key auth (query param)
curl "http://localhost:3000/mcp?api_key=your-api-key"
```

**Connecting from AI Agents:**

Most MCP clients support Streamable HTTP natively. Configure your AI agent with:

```json
{
  "mcp": {
    "pinchtab": {
      "type": "http",
      "url": "http://your-server:3000/mcp",
      "headers": {
        "Authorization": "Bearer your-secret-token"
      }
    }
  }
}
```

#### Running Multiple AI Agents with One Pinchtab Instance

**Scenario:** You have multiple AI agents (OpenCode, LobeChat, Claude Code, Cursor, etc.) and want to use a single Pinchtab Docker container for all of them.

**Problem:** Each AI agent may require a different MCP transport (stdio for CLI agents, HTTP for server-based agents).

**Solution:** Use one Pinchtab container with multiple MCP servers:

```
┌─────────────────────────────────────────────────────────────┐
│                 Pinchtab Docker Container                   │
│                 (http://127.0.0.1:9867)                      │
└─────────────────────────┬───────────────────────────────────┘
                          │
┌─────────────────────────▼───────────────────────────────────┐
│              MCP Wrapper HTTP Server                        │
│              (http://0.0.0.0:3001/mcp)                      │
│   • LobeChat ───────┐  • Other HTTP clients ───┐            │
│   • Remote agents ──┘  • Cloud services ───────┘            │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│              MCP Wrapper stdio Servers                      │
│   • OpenCode ───────┐  • Claude Code ───────┐               │
│   • Cursor ─────────┘  • Zed ───────────────┘               │
└─────────────────────────────────────────────────────────────┘
```

**Setup Steps:**

1. **Start Pinchtab Docker container:**
```bash
docker run -d \
  --name pinchtab \
  -p 127.0.0.1:9867:9867 \
  -e PINCHTAB_TOKEN=opencode-browser-token-secure \
  pinchtab:local
```

2. **Start HTTP MCP server for server-based agents (LobeChat, etc.):**
```bash
export PINCHTAB_MODE=external
export PINCHTAB_URL=http://127.0.0.1:9867
export PINCHTAB_TOKEN=opencode-browser-token-secure
export MCP_TRANSPORT=streamable-http
export MCP_HTTP_PORT=3001
export MCP_HTTP_HOST=0.0.0.0
export MCP_AUTH_TYPE=none
export MCP_ENABLE_SESSIONS=true
node dist/index.js
```

3. **Configure CLI agents (OpenCode, Claude Code, Cursor, Zed) to use stdio:**
   - OpenCode: `~/.config/opencode/opencode.json` (see [OpenCode section](#opencode))
   - Claude Code: `.mcp.json` (see [Claude Code section](#claude-code))
   - Cursor: MCP settings (see [Cursor section](#cursor))
   - Zed: `~/.config/zed/settings.json` (see [Zed section](#zed))

4. **Configure server-based agents (LobeChat) to use HTTP:**
   - URL: `http://172.21.0.1:3001/mcp`
   - Type: `Streamable HTTP`
   - Auth: `None`

**Benefits:**
- **Single Pinchtab instance** for all AI agents
- **No port conflicts** (stdio for CLI, HTTP for servers)
- **Resource efficient** (one browser instance)
- **Consistent browser state** across all agents

**Making HTTP MCP server persistent:**

```bash
sudo tee /etc/systemd/system/pinchtab-mcp.service << 'EOF'
[Unit]
Description=Pinchtab MCP HTTP Server
After=network.target docker.service

[Service]
Type=simple
User=root
WorkingDirectory=/root/.pinchtab-mcp-wrapper
ExecStart=/bin/bash -c 'export PINCHTAB_MODE=external && export PINCHTAB_URL=http://127.0.0.1:9867 && export PINCHTAB_TOKEN=opencode-browser-token-secure && export MCP_TRANSPORT=streamable-http && export MCP_HTTP_PORT=3001 && export MCP_HTTP_HOST=0.0.0.0 && export MCP_AUTH_TYPE=none && export MCP_ENABLE_SESSIONS=true && node dist/index.js'
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable pinchtab-mcp
sudo systemctl start pinchtab-mcp
```

### Pinchtab Options (via Docker)
### Pinchtab Options (via Docker)

```bash
PINCHTAB_MODE=docker              # docker | external
PINCHTAB_TOKEN=secret-token       # Auth token
DEFAULT_SNAPSHOT_FORMAT=compact   # compact | text | json
DEFAULT_MAX_TOKENS=2500           # Token budget
SCREENSHOT_DEFAULT_DELIVERY=base64 # base64 | s3 | file
```

### Pinchtab Options (via Docker)

```bash
BRIDGE_STEALTH=full          # Bypass bot detection
BRIDGE_BLOCK_IMAGES=true     # Faster loading
BRIDGE_NO_ANIMATIONS=true    # Consistent snapshots
BRIDGE_TIMEOUT=30            # Action timeout (seconds)
```

---

## Troubleshooting

| Issue | Solution |
|-------|----------|
| "Connection closed" | Check `docker ps`, verify container running |
| "MCP error -32000" | Restart AI agent completely |
| Health check timeout | `docker logs pinchtab` for errors |
| 401 errors | Verify PINCHTAB_TOKEN matches |

---

## Token Efficiency Guide

**Best practices for minimizing costs:**

1. **Use `/text` for reading** - 800 tokens vs 10,000 for DOM
2. **Filter interactives** - 3,600 tokens vs 10,500 for full page
3. **Use `diff=true`** - Only changed elements (50-200 tokens)
4. **Block images/media** - Faster loading, less noise
5. **Lock tabs** - Prevent conflicts, avoid re-work

---

## Development

```bash
npm install
npm run build
npm test
```

---

## Release History

See [CHANGELOG.md](CHANGELOG.md) for detailed version history and changes.

**Latest release**: v0.6.1 (2026-03-19)
- **E2E HTTP tests stability** - Fixed intermittent timeouts in HTTP mode E2E tests
- All v0.6.0 features included

**Previous release (v0.6.0)**:
- **Pinchtab v0.7.8 support** - Updated to stable Pinchtab version
- **Fixed MCP Streamable HTTP transport** - Improved connection stability and error handling

**Previous releases**:
- v0.5.1 - Pinchtab v0.6.3 support, file upload tool
- v0.5.0 - Streamable HTTP transport, multi-transport architecture
- v0.4.0 - CI/CD pipeline, ESLint, comprehensive test suite
- v0.2.0 - Download tool, Pinchtab 0.6.1, multi-agent configuration

---

## Credits & License

This MCP wrapper is built on [Pinchtab](https://github.com/pinchtab/pinchtab) by Luigi Agosti and contributors.

- Pinchtab: [github.com/pinchtab/pinchtab](https://github.com/pinchtab/pinchtab)
- MCP Protocol: [modelcontextprotocol.io](https://modelcontextprotocol.io/)

MIT License
