🧭 TabPilot
Drive the real Chrome you are already logged into — on macOS, Windows, and Headless Ubuntu 24/7.
Most browser MCP servers launch a fresh, blank browser instance. That works for scraping static pages, but fails completely on tasks that matter:
- 🚫 Cloudflare & Bot Shields immediately flag fresh automation browsers.
- 🚫 Corporate SSO, Okta, & 2FA make authenticating from scratch painful or impossible.
- 🚫 Read-only tab viewers can only look at DOM text, not click or fill forms.
TabPilot bridges this gap. It attaches directly to your existing, logged-in Google Chrome. Your AI agents (Claude, Cursor, Antigravity, Cline) can read pages with up to 99% token savings, click elements with authentic mouse events (isTrusted: true), defeat React state caching traps, solve multi-row survey grids, and capture background screenshots without stealing window focus.
┌───────────────────────────────────────────────────────────────────────────────────┐
│ YOUR WORKSPACE TABPILOT MCP ENGINE REAL BROWSER │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────┐ │
│ │ AI Agents │ │ TabPilot MCP Server │ │ Real Chrome │ │
│ │ - Claude Desktop │ stdio / │ - Token Budget Slicer│ CDP │ - Active SSO │ │
│ │ - Cursor IDE │ ───────> │ - Native Form Setters│────> │ - Cookies │ │
│ │ - Antigravity/Gemini │ SSH │ - Matrix Grid Driver │<──── │ - 2FA Saved │ │
│ │ - Cline / Windsurf │ │ - Dual CDP/AppleScr. │ │ - Real Finger│ │
│ └──────────────────────┘ └──────────────────────┘ └──────────────┘ │
└───────────────────────────────────────────────────────────────────────────────────┘
⚡ Quick Start
1. Test your environment in one command
Run tabpilot doctor via uvx (no installation required):
uvx --from git+https://github.com/hunglp97/tabpilot-mcp tabpilot doctor
2. Configure your MCP Client
Add to claude_desktop_config.json or .cursor/mcp.json:
{
"mcpServers": {
"tabpilot": {
"command": "uvx",
"args": ["--from", "git+https://github.com/hunglp97/tabpilot-mcp", "tabpilot-mcp"]
}
}
}
3. Launch Chrome with Remote Debugging
Quit Chrome completely, then start it pointing to your persistent automation profile:
# macOS
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--remote-debugging-port=9222 --user-data-dir="$HOME/tabpilot-chrome"
# Linux
google-chrome --remote-debugging-port=9222 --user-data-dir="$HOME/tabpilot-chrome"
# Windows
taskkill /F /IM chrome.exe
"C:\Program Files\Google\Chrome\Application\chrome.exe" ^
--remote-debugging-port=9222 --user-data-dir="%USERPROFILE%\tabpilot-chrome"
💰 How TabPilot Saves 90%+ Tokens
Naive browser automation dumps document.body.innerText or raw HTML, wasting 15,000–45,000 tokens on navigation bars, cookies banners, and tracking scripts.
TabPilot provides Progressive Token Slicing:
Raw Page Dump (eval_js body) ██████████████████████████████████████ 18,000+ tokens
read_tab() (Link-Density) ███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ~1,200 tokens (93% saved)
read_tab(selector) █░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ~350 tokens (98% saved)
query_dom(selector) ▏ ~18 tokens (99.9% saved)
| Method | Tokens | Savings | Best For |
|---|---|---|---|
query_dom(selector) |
~15–30 | 99.9% | Checking button states (disabled), inputs, badges, or alerts |
read_tab(selector) |
~300–600 | 98.0% | Reading isolated articles, ticket cards, or form sections |
read_tab() (Link-Density) |
~1,200–1,800 | 93.0% | Full-page reads with nav, ads, headers, and footers stripped |
❌ Naive eval_js innerText |
15,000–35,000 | 0% | Context budget incinerator |
💡 Enforced Character Budget: Reads default to
--max-chars 20000. If truncated, TabPilot alerts the model with exact cut sizes, prompting it to narrow down withselectorinstead of hallucinating.
🥊 Driving Forms That Fight Back
Modern Single-Page Applications (React, Vue, Svelte) defeat standard automation scripts. TabPilot solves the 3 most infamous SPA traps:
┌───────────────────────────────────────────────────────────────────────────────────┐
│ TRAP 1: React Synthetic Cache │
│ Standard `.value = 'x'` is ignored by React's internal tracker. │
│ ✅ TabPilot calls native property setters + fires both `input` and `change`. │
├───────────────────────────────────────────────────────────────────────────────────┤
│ TRAP 2: Multi-Row Survey Grid Batching │
│ Clicking 25 matrix rows in one JS tick causes React to commit only the last row. │
│ ✅ `fill_matrix` clicks row-by-row across microtasks with delays, then confirms. │
├───────────────────────────────────────────────────────────────────────────────────┤
│ TRAP 3: Virtual Portals (React-Select, Headless UI) │
│ No `<select>` exists; options render only after clicking the trigger. │
│ ✅ `select_option_ui` clicks trigger, awaits portal mount, and selects option. │
└───────────────────────────────────────────────────────────────────────────────────┘
⚖️ Comparison
| Capability | Read-Only Tab Readers | Headless Bots (Puppeteer/Playwright) | 🧭 TabPilot |
|---|---|---|---|
| Uses Existing Sessions & Cookies | ✅ | ❌ Fresh empty profile | ✅ Real Chrome (No re-login) |
| Bypasses Cloudflare & Bot Shields | ✅ Human | ❌ Bot fingerprint | ✅ Human browser fingerprint |
| Form Driving & Clicking | ❌ Read-only | ✅ | ✅ Full Bidirectional Driving |
| React Synthetic Event Fix | ❌ | ⚠️ Often missed | ✅ Native prototype setters |
| Survey Matrix Handler | ❌ | ❌ Batched drops | ✅ fill_matrix task scheduling |
| Token-Optimized Extraction | ⚠️ Basic text | ❌ Raw DOM / costly vision | ✅ query_dom (18 tokens) |
| Background Tab Screenshots | ❌ Must be active | ✅ | ✅ Off-screen CDP captures |
| Headless Ubuntu 24/7 Daemon | ❌ macOS only | ⚠️ Complex Docker | ✅ Native systemd stack |
| External Dependencies | Minimal | ❌ Heavy Node/Playwright binaries | ✅ Zero dependencies beyond MCP |
🐧 24/7 Headless Ubuntu Server Stack
TabPilot is engineered to run permanently on cloud VPS servers (AWS, Hetzner, DigitalOcean) with zero exposed ports:
[ Laptop / Client ] ──( Encrypted SSH Pipe )──> [ Remote Ubuntu Server ]
│
▼
[ TabPilot MCP Server ]
│ (CDP 127.0.0.1:9222)
▼
┌───────────────────────────────────────────────────────────────────────────────────┐
│ Managed Systemd User Stack (Restart=always, Linger Enabled) │
│ tabpilot-xvfb.service ──> tabpilot-wm.service ──> tabpilot-chrome.service
│ (Virtual Framebuffer) (Openbox Window Mgr) (Real Chrome + SSO) │
└───────────────────────────────────────────────────────────────────────────────────┘
- One-Command Setup:
bash deploy/ubuntu/install.shconfigures Xvfb, Openbox, Chrome, and systemd services withRestart=always. - Font Protection (Anti-Tofu): Headless servers often render Vietnamese and CJK characters as empty square boxes.
install.shinstalls Noto and Liberation fonts;doctorvalidates text rendering. - Cgroup Memory Caps: Prevent Chrome from exhausting host RAM:
tabpilot install-stack --memory-high 4G
- Zero Remote Attack Surface: Chrome CDP stays locked to
127.0.0.1. Remote MCP connections run securely through SSH pipes (ssh you@server "tabpilot serve").
🧰 Tools & Resources Reference
TabPilot exposes 17 tools and 2 live resources:
🔍 Reading & DOM Inspection
query_dom(selector, attrs, limit, visible_only): Atomic element inspection (~18 tokens). Checks disabled, checked, values.read_tab(selector, mode, max_chars, url_pattern): Link-density markdown extraction with token caps.list_tabs(url_pattern): Lists open tabs with IDs, titles, and URLs (regex filterable).eval_js(expression, timeout_ms): Runs arbitrary JS in tab context; automatically awaits Promises in CDP.
🧭 Navigation & Tabs
open_tab(url, activate, wait_for_load): Opens a URL in a new tab, awaiting page completion.close_tab(url_pattern, tab_id): Closes tab and polls until process confirms destruction.navigate(url, wait_for_load): Points tab to a new URL and awaits document load.activate_tab(url_pattern, tab_id): Brings target tab to foreground focus.
🖱️ Interaction & Complex Forms
click(selector, text, nth): Scrolls element into view and emits trusted mouse event (isTrusted: true).fill(selector, value, clear, press_enter): Sets form inputs via native prototype setters to trigger React/Vue.select_option(selector, values, by): Selects options in native<select>or Select2 dropdowns.select_option_ui(control_selector, option_text): Clicks trigger, waits for popup portal, and selects item.wait_for(selector, state, text, timeout_ms): Polls until condition holds (visible,hidden,text,enabled).
📋 Matrix Surveys & Evidence
scan_matrix(selector): Discovers multi-row grid questions and flags unanswered rows.fill_matrix(column_index, rows, delay_ms): Answers matrix rows sequentially with task delays.screenshot(full_page, selector, label): Offscreen capture of any tab (local path or inline base64).browser_status(): Reports active backend, capabilities, CDP endpoint, and open tab count.
📦 Live MCP Resources
tab://active— Markdown stream of the currently focused tab.tab://{tab_id}— Markdown stream of any specific tab by ID.
🛠️ CLI Reference
tabpilot doctor # Run diagnostic suite (Chrome, CDP, fonts, permissions)
tabpilot tabs # List open browser tabs in terminal
tabpilot serve # Start MCP server on stdio
# Linux Managed Stack Commands
tabpilot install-stack # Install systemd user services
tabpilot up / tabpilot down # Start or stop the headless stack
tabpilot status # Check systemd stack status
tabpilot logs chrome -f # Follow live Chrome logs
🧪 Testing
git clone https://github.com/hunglp97/tabpilot-mcp.git
cd tabpilot-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
# Unit tests (Zero browser needed)
pytest -v
# Live integration tests (against Chrome on port 9222)
pytest -v -m live
📄 License
Distributed under the MIT License. See LICENSE for details.
Built with ❤️ for AI Engineers, QA Automators, and Power Users who demand real browser agency.
Star ⭐ TabPilot on GitHub if it saved your agents from bot shields and token burn!
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tabpilot_mcp-0.1.2.tar.gz.
File metadata
- Download URL: tabpilot_mcp-0.1.2.tar.gz
- Upload date:
- Size: 1.6 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
956bba7d134403cd6ca3721c776ae4b48cf30c54d0f4574307afc4f7db3cb0e2
|
|
| MD5 |
d905b80d8ac154250961c7d72001047d
|
|
| BLAKE2b-256 |
ce31227a954d1557213cda31f20873f817afb7fde8a7936827b80527a2b0d082
|
File details
Details for the file tabpilot_mcp-0.1.2-py3-none-any.whl.
File metadata
- Download URL: tabpilot_mcp-0.1.2-py3-none-any.whl
- Upload date:
- Size: 73.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9447e7e3d5ebf301678227db93f5c5598d21dbf508be3332d348237108476011
|
|
| MD5 |
be4211212a6833b0947526511371ab1e
|
|
| BLAKE2b-256 |
cf10bf5171c3c8ef29dba6cf9f6265d25cb745ec87a7b6cffc40996aee3d9f3e
|