An MCP (Model Context Protocol) server that exposes the full 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 project by Luigi Agosti and the Pinchtab team. Pinchtab is a lightweight 12MB Go binary that provides browser automation via HTTP API with accessibility-first snapshots.
When AI agents need to browse the web, efficiency matters. Pinchtab delivers 5-13x cost savings compared to traditional browser automation.
| 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 |
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=truereturns 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)
curl -fsSL https://raw.githubusercontent.com/BDuba/pinchtab-mcp-wrapper/main/install.sh | bashRequirements: Docker, Node.js 18+, npm
Then restart your AI agent (OpenCode, Claude Code, etc.)
Open https://example.com and take a screenshot
Or use commands:
/browse https://example.com
/screenshot
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:
// 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)
Problem: Monitor competitor prices or news updates
// 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
Problem: Gather information from 10+ documentation sites simultaneously
// 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
Problem: Automate login/checkout flows that break on website updates
Traditional selectors break:
// Brittle - breaks when CSS changes
await page.click('#login-btn') // ❌Pinchtab stable refs:
// Stable - survives redesigns
pinchtab_action({
tabId: "...",
kind: "click",
ref: "e5" // ✅ Persistent reference
})Built-in accessibility tree:
- Element roles (button, link, heading)
- Accessible names and labels
- Focus states and visibility
- Screen reader compatible
// Get only interactive elements
interactives = pinchtab_list_interactives()
// Returns: buttons, links, inputs with labels| 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 |
Add to ~/.config/opencode/opencode.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:
docker ps | grep pinchtab # Check if running
docker start pinchtab # Start if stoppedAdd to .mcp.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 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.
Add to Cursor's MCP settings (Settings → Features → MCP):
{
"mcpServers": {
"pinchtab": {
"type": "stdio",
"command": "bash",
"args": ["~/.pinchtab-mcp-wrapper/run-mcp.sh"]
}
}
}Add to ~/.config/zed/settings.json:
{
"assistant": {
"version": "2",
"enabled": true
},
"context_servers": {
"pinchtab": {
"command": "bash",
"args": ["~/.pinchtab-mcp-wrapper/run-mcp.sh"]
}
}
}The installer now automatically detects and configures pinchtab for multiple AI agents:
- OpenCode (
~/.config/opencode/opencode.json) - Claude Code (
~/.mcp.jsonor./.mcp.json) - Cursor (
~/.config/Cursor/User/settings.json) - Zed (
~/.config/zed/settings.json)
If a config file already exists, the installer will:
- Create a backup (
.backup.YYYYMMDD_HHMMSS) - Automatically merge pinchtab configuration using Node.js
- Preserve all existing settings
No manual editing required - just run the installer and restart your AI agent!
The install script now supports automatic mode detection and graceful fallback:
The installer automatically detects your environment and chooses the best mode:
curl -fsSL https://raw.githubusercontent.com/BDuba/pinchtab-mcp-wrapper/main/install.sh | bashWhat happens:
- Checks for Docker (including macOS-specific paths: Homebrew, OrbStack, Docker Desktop)
- If Docker is available → builds Docker image and uses
dockermode - If Docker is unavailable → automatically downloads Pinchtab binary and uses
externalmode
Recommended for most users. Requires Docker to be installed.
export PINCHTAB_MODE=docker
curl -fsSL https://raw.githubusercontent.com/BDuba/pinchtab-mcp-wrapper/main/install.sh | bashFor systems without Docker. The installer automatically downloads the Pinchtab binary for your architecture.
export PINCHTAB_MODE=external
curl -fsSL https://raw.githubusercontent.com/BDuba/pinchtab-mcp-wrapper/main/install.sh | bashThe 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:
export DOCKER_PATH=/opt/homebrew/bin/docker
curl -fsSL https://raw.githubusercontent.com/BDuba/pinchtab-mcp-wrapper/main/install.sh | 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.shWhen using delivery: "file", you can now use relative paths and auto-naming:
{
"tool": "screenshot",
"params": {
"delivery": "file"
// Auto-saved to: ~/Pictures/Screenshots/2026-02-21T12-34-56-tab-123.jpg
}
}{
"tool": "screenshot",
"params": {
"delivery": "file",
"path": "google/homepage.jpg"
// Saved to: ~/Pictures/Screenshots/google/homepage.jpg
}
}{
"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 |
# 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 | filePinchtab 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:
# 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/healthHTTP 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:
- Always use authentication in production (
MCP_AUTH_TYPE=bearer) - Restrict CORS origins (
MCP_ALLOWED_ORIGINS=https://yourdomain.com) - Use HTTPS with a reverse proxy (nginx, traefik)
- 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 | |
http://host:port/status |
GET | Status endpoint (alias for health) |
For LobeChat v2 (and other HTTP-based MCP clients):
- Start MCP in HTTP mode (listening on all interfaces for Docker network access):
./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-
Configure in LobeChat:
- Go to Settings → Plugins → MCP
- Add new MCP server:
- Name:
pinchtab - Type:
HTTP(orStreamable HTTP) - URL:
http://172.21.0.1:3001/mcp(adjust IP based on your Docker network) - Auth Type:
None
- Name:
- Click "Test Connection"
-
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 bridge172.21.0.1- custom networkshost.docker.internal- Docker Desktop (Mac/Windows)
- Check your Docker network:
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:
// 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:
# 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:
{
"mcp": {
"pinchtab": {
"type": "http",
"url": "http://your-server:3000/mcp",
"headers": {
"Authorization": "Bearer your-secret-token"
}
}
}
}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:
- Start Pinchtab Docker container:
docker run -d \
--name pinchtab \
-p 127.0.0.1:9867:9867 \
-e PINCHTAB_TOKEN=opencode-browser-token-secure \
pinchtab:local- Start HTTP MCP server for server-based agents (LobeChat, etc.):
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-
Configure CLI agents (OpenCode, Claude Code, Cursor, Zed) to use stdio:
- OpenCode:
~/.config/opencode/opencode.json(see OpenCode section) - Claude Code:
.mcp.json(see Claude Code section) - Cursor: MCP settings (see Cursor section)
- Zed:
~/.config/zed/settings.json(see Zed section)
- OpenCode:
-
Configure server-based agents (LobeChat) to use HTTP:
- URL:
http://172.21.0.1:3001/mcp - Type:
Streamable HTTP - Auth:
None
- URL:
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:
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-mcpPINCHTAB_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 | fileBRIDGE_STEALTH=full # Bypass bot detection
BRIDGE_BLOCK_IMAGES=true # Faster loading
BRIDGE_NO_ANIMATIONS=true # Consistent snapshots
BRIDGE_TIMEOUT=30 # Action timeout (seconds)| 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 |
Best practices for minimizing costs:
- Use
/textfor reading - 800 tokens vs 10,000 for DOM - Filter interactives - 3,600 tokens vs 10,500 for full page
- Use
diff=true- Only changed elements (50-200 tokens) - Block images/media - Faster loading, less noise
- Lock tabs - Prevent conflicts, avoid re-work
npm install
npm run build
npm testSee 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
This MCP wrapper is built on Pinchtab by Luigi Agosti and contributors.
- Pinchtab: github.com/pinchtab/pinchtab
- MCP Protocol: modelcontextprotocol.io
MIT License