Tthanh2k6/turbo-browser-mcp: High-speed Playwright MCP server with multi-tab batching, compact delta DOM snapshots, React/Shadow DOM/iframe form filling, disk file uploads, and submit guards · GitHub

turbo-browser-mcp is a Playwright-based Model Context Protocol (MCP) server built to cut LLM token waste and multi-turn latency in browser automation. It consolidates 25-33 fine-grained browser primitives into 10 batched tools featuring delta DOM snapshots, single-turn React/Shadow DOM/iframe form prefill, direct disk file uploads (no Base64 in context), parallel tab sessions, network blocking presets, and a default human-in-the-loop Submit guard.
All figures below come from benchmarks/benchmark.mjs (npm run benchmark) running headless Chromium locally with exact OpenAI o200k_base tokenization (tiktoken 0.14.0):
| Capability | Typical Baseline | turbo-browser-mcp |
Speed / Turn Gain | Token Savings (o200k_base) |
|---|---|---|---|---|
| Dense UI Snapshot (140-btn SaaS console) | 18,654 tok HTML / 3,568 tok full ARIA |
1,135 tok compact (28 ms) |
1 call |
93.9% vs HTML (68.2% vs ARIA) |
Post-Click Delta Snapshot (onlyChanged: true) |
3,582 tok full ARIA rescan |
56 tok changed lines (18 ms) |
1 call |
98.4% vs ARIA (99.7% vs HTML) |
10-Field Complex Form + Snapshot (smart_fill_form) |
11 turns (574 ms, 12,197 dir / 72,614 cum tok) |
1 turn (82 ms, 811 tok) |
7x faster, 11 -> 1 turns |
93.4% direct / 98.9% cumulative |
48 KiB PDF Upload (49,152 bytes) |
44,728 tok inline Base64 tool arg |
85 tok disk path (68 ms) |
Zero Base64 generation | 99.8% saved |
6-Step Interactive UI Flow (batch_actions) |
6 turns (411 ms, 955 dir / 3,162 cum tok) |
1 turn (357 ms, 406 tok) |
1.15x faster, 6 -> 1 turns |
57.5% direct / 87.2% cumulative |
Open 6 Tabs Concurrently (open_many) |
6 turns sequential (998 ms) |
1 turn batched (420 ms) |
2.38x faster, 6x fewer turns |
5 fewer prompt replays |
Heavy Media Load (preset: "turbo", 304 KiB assets) |
252 ms, 9 media requests (304 KiB) |
165 ms, 0 media requests (0 KiB) |
1.53x faster |
100% media bandwidth cut |
Article Markdown Scraping (extract_clean_text) |
3,595 tok raw HTML (10,844 chars) |
156 tok clean Markdown (641 chars) |
Clean reader output | 95.7% saved |
Diagnostics Triage (page_diagnostics errors-only) |
3,460 tok unfiltered (32 req / 26 console) |
601 tok default errors (2 req / 6 console) |
Zero noise scrolling | 82.6% saved |
| Static MCP Tool Schema (every turn overhead) | 3,917 tok Playwright / 6,737 tok DevTools |
3,010 tok stripped (3,314 full w/ annotations) |
10 tools vs 25 / 33 |
23.2% vs Playwright / 55.3% vs DevTools |
Note on latency vs LLM inference turns: Eliminating
10round-trips on a 10-field form saves both7xin browser execution (574 ms -> 82 ms) and10separate LLM inference round-trips (2-5sper turn in agent loops) plus~71,800replayed history tokens.
Standard MCP servers charge an LLM round-trip for every snapshot, field fill, choice, upload, and verification check. Each turn re-sends the growing conversation history back into the model.
sequenceDiagram
autonumber
participant Agent as LLM Agent
participant Std as Standard MCP (Playwright / DevTools)
participant Turbo as turbo-browser-mcp
Note over Agent,Std: Standard MCP (12+ LLM turns, full ARIA tree + history replay each step)
Agent->>Std: 1. snapshot() -> full ARIA (~3,568 tok)
loop Repeats 10+ times across fields, selects, checkboxes, upload
Agent->>Std: fill / select_option / click
Std-->>Agent: full ARIA or confirmation re-added to history
end
Agent->>Std: 12. upload inline or click chooser
Note over Agent,Turbo: turbo-browser-mcp (2 LLM turns total, delta & inline snapshot)
Agent->>Turbo: 1. inspect_compact(mode: "forms") -> compact refs (1,135 tok)
Agent->>Turbo: 2. smart_fill_form(fields: [...10 fields + disk PDF...], returnSnapshot: true)
Turbo-->>Agent: Filled 10 fields + guarded before Submit + inline snapshot (811 tok)
Loading
- 4 connection modes (
--mode auto|cdp|persistent|launch):auto(default): probes--cdp http://127.0.0.1:9222first so agents can reuse an existing signed-in browser session, then falls back to launching Chromium.cdp: attach directly to a running Chromium/Chrome/Edge remote debugging port.persistent: launch with a reusable--user-data-dirprofile across runs.launch: start a clean browser session (--headlessby default, or--headed).
- Auto binary discovery (
src/playwright-loader.mjs): checksplaywright-corecache revisions across Windows, macOS, and Linux, system installs of Google Chrome, Microsoft Edge, Brave, and Chromium, or an explicit--executable. - Per-tab isolation (
tabs_batch): open tabs inside namedisolatedContextsandboxes with separate cookies and storage, batch-open up to 10 concurrent URLs (open_many), activate, reload, goback/forward,resizeviewport, and emulatecolorScheme,media,reducedMotion,contrast,forcedColors, or customuserAgent.
- Snapshot modes:
interactive,forms,article, andall. - Cross-boundary traversal: flattens main frame DOM, child iframes (
includeFrames,frameIdx), and open Shadow DOM. Injects an init script hookingElement.prototype.attachShadow(WeakMap+WeakRef/FinalizationRegistry+window.__tbmGetShadowRoot) to keep post-init closed Shadow roots inspectable and guarded, traversesassignedNodes({ flatten: true }), and supports Form-Associated Custom Elements. - Token controls:
onlyChanged: trueemits only elements added or changed since the prior snapshot;queryfilters by regex or substring;boxes: trueadds[box=x,y,w,h]only when needed;maxCharscaps payload size;savePathwrites full output to disk.
Prefills an entire form in one MCP call while flushing microtasks and macrotasks between controlled inputs:
- Standard & framework inputs: text, email, password, number, date, textarea,
, checkboxes, radio groups (check,not,overwrite). Triggers React-controlled state updates via native property setters and synthetic events. - Selects & custom widgets:
and(option/values), ARIA comboboxes, ARIA switches,contenteditablerich editors, and Gmail compose dialogs (gmail: { to, subject, body }). - Frame & wildcard targeting: target specific iframes via
frameIdx, or setall: trueto populate matching controls across frames. - Direct disk uploads: pass
fileorfilespaths directly to PlaywrightsetInputFileswithout loading binary files or Base64 strings into LLM context. - Atomic verification: pass
returnSnapshot: trueto inspect the updated form state in the same tool response.
act_fastactions:click,dblclick,hover,focus,clear,fill,type(slowly,delayMs,submit),select,check,uncheck,drag,drop(file,files, or custom MIMEdropData),press(key+modifiers:Alt,Control,ControlOrMeta,Meta,Shift),scroll(dx,dy),wait(ms,state,loadState,text,textGone,url), andupload.- Targeting options: compact
[eN]ref, CSSselector, visibletext,label, or viewport(x, y)coordinates, withbutton(left,right,middle) andframeIdx. batch_actions: runs an ordered list of DOM actions and nestedsmart_fill_formsteps in one MCP call, stopping safely if blocked by the Submit guard or optionally continuing on step errors (continueOnError).
5. Clean Extraction, Evaluation & Media Capture (extract_clean_text, evaluate_js, capture_page)
extract_clean_text: extracts readable plain text or compact Markdown (format: "text" | "markdown") from main/article containers and iframes, stripped of script and style noise.evaluate_js: runs JS expressions or(el, arg)element functions in any frame (frameIdx) with JSON-serialized results and optionalsavePathexport.capture_page: full-page or element-scoped screenshots (pngorjpegwithquality,cssordevicescale, optionalreturnImage) and PDF generation saved to a confinedsavePath.
network_sessionpresets:turbo,no-images,no-media, andclean(unblock all), alongside customblockResourcesandblockUrlspatterns.- Session & storage management: read/write/clear browser
cookies,localStorage/sessionStorage, and export or reload full PlaywrightstorageStateJSON files (saveStorageStatePath,loadStorageStatePath). page_diagnostics: defaults to errors-only (level: "errors",statusFilter: "errors") to surface failures cleanly without flooding context, while supportingall,includeStatic,urlFilter, deep request/response inspection (reqId+part), completed downloads, and dialog auto-handling (dialogPolicy:accept/dismiss).
Run the benchmark suite yourself with Chromium and Playwright installed:
Environment & methodology: Headless Chromium via playwright-core, counting exact OpenAI o200k_base tokens via Python tiktoken (approx-bpe fallback if tiktoken is unavailable). Cumulative workflow tokens model standard chat-history replay across
1. Snapshot & Reader Extraction (Chars & Exact o200k_base Tokens)
| Scenario | Raw HTML | Full Playwright ARIA | turbo-browser-mcp Output |
Saved vs ARIA | Saved vs HTML |
|---|---|---|---|---|---|
140-Button SaaS Console (inspect_compact) |
61,010 chars (18,654 tok) |
12,771 chars (3,568 tok) |
3,968 chars (1,135 tok) |
68.2% |
93.9% |
Post-Click Delta (onlyChanged: true) |
61,010 chars (18,654 tok) |
12,825 chars (3,582 tok) |
168 chars (56 tok) |
98.4% |
99.7% |
Article Markdown Extraction (extract_clean_text) |
10,844 chars (3,595 tok) |
n/a | 641 chars (156 tok) |
n/a | 95.7% |
Diagnostics Filter (32 reqs, 26 console logs) |
9,651 chars (3,460 tok, all) |
n/a | 1,761 chars (601 tok, errors) |
n/a | 82.6% |
| Scenario | Step-by-Step Baseline | turbo-browser-mcp |
Execution Speedup | Token Savings |
|---|---|---|---|---|
10-Field Complex Form + Verify (smart_fill_form) |
11 turns, 574 ms, 12,197 direct / 72,614 cum tok |
1 turn, 82 ms, 811 tok (filled=10) |
7x faster |
93.4% dir / 98.9% cum |
48 KiB PDF Upload (49,152 bytes via file) |
65,679 chars (44,728 tok) Base64 payload |
263 chars (85 tok) disk path (68 ms) |
Zero LLM Base64 encode | 99.8% |
6-Step UI Flow (batch_actions) |
6 turns, 411 ms, 955 dir / 3,162 cum tok |
1 turn, 357 ms, 406 tok |
1.15x faster |
57.5% dir / 87.2% cum |
Open 6 Tabs (tabs_batch open_many, 60 ms TTFB) |
6 turns sequential (998 ms) |
1 turn batched (420 ms) |
2.38x faster (6 -> 1 turns) |
Eliminates 5 turns |
Heavy Media Load (8x32 KiB imgs + 48 KiB font) |
252 ms, 9 media hits (304 KiB) |
165 ms, 0 media hits (0 KiB) |
1.53x faster |
100% media saved |
3. Static MCP Tool Definition Footprint
Measured against installed MCP JSON schemas on disk (compact parameter schemas without annotations):
| MCP Server | Tool Count | Compact Schema Chars | Exact o200k_base Tokens |
Savings with turbo-browser-mcp |
|---|---|---|---|---|
turbo-browser-mcp |
10 (14,186 full / 12,796 compact) |
12,796 |
3,010 (3,314 full w/ annotations) |
Baseline (10 tools) |
@playwright/mcp |
25 |
18,457 |
3,917 |
23.2% fewer static tokens |
chrome-devtools-mcp |
33 |
30,252 |
6,737 |
55.3% fewer static tokens |
| Feature | turbo-browser-mcp |
@playwright/mcp |
chrome-devtools-mcp |
|---|---|---|---|
| Tool Surface Size | 10 batched tools | 25 granular tools | 33 granular tools |
Delta Snapshots (onlyChanged) |
Built-in | Full tree every snapshot | Full tree every snapshot |
| Closed Shadow DOM Hook | Post-init attachShadow hook + flattening |
Open Shadow DOM | CDP / standard DOM access |
| Single-Call Smart Form + Contenteditable | Yes (React, Shadow DOM, iframe, Gmail, ranges) | Multi-turn fill + separate select calls | Basic fill_form |
| Disk File Upload (Zero Base64) | Native file/files path in smart_fill_form & act_fast |
Dedicated browser_file_upload turn |
Dedicated upload_file turn |
| Default Human-in-the-Loop Submit Guard | Yes (allowSubmit=false, form/iframe/Enter hooks) |
No | No |
Parallel open_many + isolatedContext |
Single batched call | Separate sequential tab calls | Separate page calls |
One-Call turbo Media Blocker |
Built-in (turbo, no-images, no-media) |
Manual JS / routing | Network emulation |
| Performance / Heap Profiling | Not included | Not included | Trace, Lighthouse, Heap Snapshot |
When to keep Chrome DevTools MCP alongside
turbo-browser-mcp:chrome-devtools-mcpprovides Lighthouse audits, performance trace analysis, CSS computed style panels, and V8 heap snapshots.turbo-browser-mcpfocuses on fast multi-step UI automation, forms, and scraping with minimal token overhead.
| Capability | turbo-browser-mcp |
@playwright/mcp |
chrome-devtools-mcp |
|---|---|---|---|
| Multi-tab open, list, switch, resize, emulate | tabs_batch |
browser_tabs, browser_navigate, browser_resize, … |
new_page, list_pages, select_page, resize_page, … |
| DOM / accessibility inspection | inspect_compact |
browser_snapshot |
take_snapshot |
| Multi-field form & upload | smart_fill_form |
browser_fill_form + browser_select_option + browser_file_upload |
fill_form + upload_file |
| Click, type, drag, press, scroll, wait | act_fast |
browser_click, browser_type, browser_drag, … |
click, type_text, drag, press_key, wait_for |
| Multi-step sequential macro | batch_actions |
browser_run_code_unsafe or N tool calls |
N separate tool calls |
| Clean article extraction | extract_clean_text |
browser_evaluate custom JS |
evaluate_script custom JS |
| Frame/element JS evaluation | evaluate_js |
browser_evaluate |
evaluate_script |
| Errors, network & console triage | page_diagnostics |
browser_console_messages, browser_network_requests |
list_console_messages, list_network_requests, … |
| Screenshots & PDF export | capture_page |
browser_take_screenshot |
take_screenshot |
| Blocking presets, cookies & storage | network_session |
browser_evaluate + context config |
Partial via evaluate_script / emulate |
- Human-in-the-loop Submit Guard (
allowSubmit=falseby default):smart_fill_form(stopBeforeSubmit: true),act_fast, andbatch_actionsinspect matching elements, W3C AccName labels (isSafeSubmitter), implicit HTML form Enter submissions, key chords (Shift+Enter,Enter+Shift),HTMLFormElement.prototype.submit/requestSubmit, and same-origin iframecontentWindow/contentDocumentproxies.- Final actions resembling
Submit,Publish,Send,Post,Apply,Confirm, or payment triggers are blocked unlessallowSubmit: trueis explicitly passed or--allow-submitis enabled at startup. - Trade-off: pre-existing closed Shadow roots on already-open CDP tabs (attached before the init script ran) or strict cross-origin sandboxed frames fall back to Playwright frame handles, coordinate actions, and keyboard input.
- Workspace Path Confinement (
TURBO_BROWSER_SAVE_ROOT):- File outputs (
savePath,saveStorageStatePath) and state inputs (loadStorageStatePath) are strictly confined to the current working directory orTURBO_BROWSER_SAVE_ROOT. - Lexical checks,
lstatSync/readlinkSyncsymlink verification, andrealpathSyncvalidation block..traversal, UNC network paths, dangling symlinks, cyclic symlinks, and Windows junctions before reading or writing.
- File outputs (
| Tool | Purpose | Key Parameters |
|---|---|---|
tabs_batch |
Ordered tab & browser lifecycle | operations[] (list, open, open_many, navigate, activate, resize, emulate, reload, back, forward, close, close_browser), isolatedContext, concurrency (1-10) |
inspect_compact |
Token-capped element/form/article snapshot | mode (interactive, forms, article, all), onlyChanged, query, boxes, maxChars, includeFrames, savePath |
smart_fill_form |
Batch form, editor & disk file prefill | fields[] (ref, selector, value, option, values, check, not, keys, file, files, frameIdx, all), gmail, overwrite, stopBeforeSubmit, returnSnapshot |
act_fast |
Single targeted page action | action (16 actions), ref/selector/text/label/(x, y), modifiers, dropData, allowSubmit, returnSnapshot |
batch_actions |
Ordered chain of actions + form steps | steps[], continueOnError, allowSubmit, returnSnapshot, snapshotMaxChars |
extract_clean_text |
Clean text or compact Markdown reader | format (text, markdown), selector, includeFrames, maxChars, savePath |
evaluate_js |
Scoped JS expression or (el, arg) call |
expression, arg, frameIdx, ref/selector/text, savePath |
page_diagnostics |
Console, network, downloads & dialogs | level (errors, all), statusFilter (errors, all), includeStatic, urlFilter, reqId, part, dialogPolicy, clear, savePath |
capture_page |
PNG/JPEG screenshots & PDF export | mode (screenshot, pdf), type, quality, fullPage, scale (css, device), ref/selector/text, returnImage, savePath |
network_session |
Resource blockers, cookies & storage | preset (turbo, no-images, no-media, clean), blockResources, blockUrls, cookieAction, storageAction, loadStorageStatePath, saveStorageStatePath |
{
"name": "smart_fill_form",
"arguments": {
"fields": [
{ "selector": "#full_name", "value": "Alex Nguyen" },
{ "selector": "#email", "value": "[email protected]" },
{ "selector": "#country", "option": "United States" },
{ "selector": "#skills", "values": ["TypeScript", "Playwright"] },
{ "selector": "#agree_terms", "check": true },
{ "selector": "#resume_pdf", "file": "./assets/resume.pdf" }
],
"stopBeforeSubmit": true,
"returnSnapshot": true
}
}
{
"name": "inspect_compact",
"arguments": {
"mode": "interactive",
"onlyChanged": true,
"maxChars": 3000
}
}
{
"name": "tabs_batch",
"arguments": {
"operations": [
{ "action": "open", "url": "https://example.com/app", "isolatedContext": "acct-a" },
{ "action": "open_many", "urls": ["https://example.com/docs", "https://example.com/pricing"], "concurrency": 4 }
]
}
}
git clone https://github.com/Tthanh2k6/turbo-browser-mcp.git
cd turbo-browser-mcp
npm install
npm run doctor
If you need Playwright’s bundled Chromium (or have no system Chrome/Edge/Brave installed):
npx playwright-core install chromium
Using local clone:
{
"mcpServers": {
"turbo-browser": {
"command": "node",
"args": ["/absolute/path/to/turbo-browser-mcp/src/index.mjs"],
"env": {
"TURBO_BROWSER_SAVE_ROOT": "/absolute/path/to/allowed/workspace"
}
}
}
}
Using npx / binary entrypoint:
{
"mcpServers": {
"turbo-browser": {
"command": "npx",
"args": ["-y", "turbo-browser-mcp"]
}
}
}
| CLI Flag | Environment Variable | Default | Description |
|---|---|---|---|
--mode |
TURBO_BROWSER_MODE |
auto |
Connection strategy (cdp first then launch in auto) |
--cdp |
TURBO_BROWSER_CDP_URL |
http://127.0.0.1:9222 |
Chrome DevTools Protocol HTTP endpoint |
--user-data-dir |
TURBO_BROWSER_USER_DATA_DIR |
"" |
Persistent profile directory path |
--executable |
– | auto-detected | Custom Chromium, Chrome, Edge, or Brave executable |
--timeout |
TURBO_BROWSER_TIMEOUT_MS |
15000 |
Default Playwright action/navigation timeout in ms |
--headless / --headed |
TURBO_BROWSER_HEADLESS |
true (headless) |
Run headless or visible browser window ("false" for headed) |
--allow-submit |
– | false |
Disable default Submit/Publish confirmation guard |
--doctor |
– | – | Print Node, Playwright, Chromium, and CDP diagnostics as JSON |
--bridge |
TURBO_BROWSER_BRIDGE_PORT |
9224 |
Start/verify the persistent CDP Keep-Alive Bridge daemon for Chrome 144+ DevToolsActivePort (approve once, reconnect unlimited times AFK) |
--launch-chrome |
– | ~/.turbo-browser-mcp/chrome-profile |
Launch Chrome with a dedicated profile & --remote-debugging-port=9222 (0 approval prompts ever) |
--version / -v |
– | – | Print server version (0.4.0) |
--help / -h |
– | – | Print CLI usage summary |
| – | TURBO_BROWSER_SAVE_ROOT |
process.cwd() |
Root directory confining savePath and storageState files |
| – | PLAYWRIGHT_CORE_PATH |
auto-resolved | Explicit path to playwright-core/index.mjs |
In Chrome 136+/144+, enabling chrome://inspect/#remote-debugging on the default User Data directory exposes a WebSocket in DevToolsActivePort that displays an “Allow remote debugging?” prompt whenever a new WebSocket client connects. turbo-browser-mcp provides two built-in solutions so overnight AFK multi-agent runs never hang on a prompt:
- Automatic CDP Keep-Alive Bridge Daemon (
npm run bridge/--bridge, enabled automatically onDevToolsActivePort):- Spawns a detached background multiplexer (
src/cdp-bridge.mjsonhttp://127.0.0.1:9224) that holds one persistent upstream WebSocket to Chrome open indefinitely and interceptsBrowser.closeso downstream scripts never terminate your browser. - You click Allow once when starting your session; afterwards, any number of MCP servers, CLI scripts, or subagents can connect and disconnect all night with 0 additional prompts.
- Spawns a detached background multiplexer (
- Zero-Prompt Dedicated Profile Launcher (
npm run launch-chrome/--launch-chrome):- Launches Chrome with
--remote-debugging-port=9222and a dedicated--user-data-dir(~/.turbo-browser-mcp/chrome-profile), where Chrome exposes standard HTTP/WS CDP with 0 confirmation dialogs even on the very first connection.
- Launches Chrome with
npm test # 407 passing tests across 86 suites (26 test files under test/*.test.mjs)
npm run doctor # Environment check for Node, Playwright, Chromium, and CDP
npm run bridge # Start/verify persistent CDP Keep-Alive Bridge daemon
npm run benchmark # Local Chromium speed & exact o200k_base token benchmark
Tested automatically on GitHub Actions (ubuntu-latest) across Node 20 and Node 22.
