
Rust CDP 引擎上的 Playwright API — 适用于 Python 和 Node 的 Chromium 浏览器自动化,无驱动子
Rust CDP 引擎上的 Playwright API — 适用于 Python 和 Node 的 Chromium 浏览器自动化,无驱动子
Rustwright is a browser automation library for Python and Node.js that keeps the Playwright API you already know but drives Chromium from a native Rust engine speaking raw Chrome DevTools Protocol — no driver subprocess in the path.
playwright-python: your code ──pipe──► Node driver ──CDP──► Chromium
rustwright: your code ────────── raw CDP ──────────► Chromium
Rustwright is interoperable with Playwright — install it, change one import, and your existing code runs on the Rust engine.
Python
pip install rustwright
python -m rustwright install chromium
- from playwright.sync_api import sync_playwright
+ from rustwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("https://example.com")
print(page.title())
browser.close()
Node.js (experimental)
Install from npm:
npm install rustwright
The Node binding drives an existing Chromium/Chrome — point Rustwright at it with RUSTWRIGHT_CHROMIUM, CHROME, or CHROMIUM. Prefer to build from source? git clone the repo and run npm install && npm run build in node/.
- import { chromium } from 'playwright';
+ import { chromium } from 'rustwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
console.log(await page.title());
await browser.close();
See the network-independent examples/quickstart.js example. After building the Node binding from source, run it from the repository root with node examples/quickstart.js.
Only a subset of the API surface is bridged — see Limitations.
CLI (agent-focused)
Install the native rustwright-cli with one command:
curl -fsSL https://raw.githubusercontent.com/Skyvern-AI/rustwright/main/install.sh | sh
Then drive a persistent Chromium session from your shell or an agent loop:
rustwright-cli open https://example.com
rustwright-cli snapshot # compact page tree with @eN refs
rustwright-cli click @e1
rustwright-cli close
See cli/README.md for the full command surface. For a Model Context Protocol
server, use the standalone rustwright-mcp package.
playwright-python launches and pipes to a bundled Node driver. Rustwright's engine is native — the browser-control code runs in-process.Input.dispatchMouseEvent), not synthetic element.click() DOM calls.frame_locator() across origins.One Rust core — an async CDP client built on Tokio (WebSocket, with opt-in Unix-pipe transport) — talks to Chromium directly, and thin PyO3 (Python) and napi-rs (Node) bindings expose it in-process. The two-line diagram above is the entire architecture.
Already have a Chromium/Chrome binary? Point Rustwright at it with RUSTWRIGHT_CHROMIUM, CHROME, or CHROMIUM.
Give an agent or shell script a browser through compact accessibility snapshots with element refs (e1, e2, …), instead of raw HTML or screenshots. Refs are session-scoped, never reused, and best-effort rather than a security boundary; snapshots include page values but mask password fields.
rustwright-mcp is a native Rust MCP server (no Python or Node runtime in the
serving path) that gives any MCP client browser_* tools over stdio, with
compact accessibility snapshots and inline PNG screenshots. It lives in
mcp/ and is the canonical Rustwright MCP server.
From source (needs a Rust toolchain):
cargo install --git https://github.com/Skyvern-AI/rustwright rustwright-mcp
This installs the server binary as rustwright-mcp. An npm package (rustwright-mcp,
prebuilt binaries via npx rustwright-mcp) is on the way.
If Chrome or Chromium is already installed, point Rustwright at it with
RUSTWRIGHT_CHROMIUM (or CHROME / CHROMIUM). Otherwise download one with
pip install rustwright && python -m rustwright install chromium.
claude mcp add rustwright -- rustwright-mcp
Open ~/Library/Application Support/Claude/claude_desktop_config.json on macOS or %APPDATA%\Claude\claude_desktop_config.json on Windows, then use:
{
"mcpServers": {
"rustwright": {
"command": "rustwright-mcp"
}
}
}
{
"mcpServers": {
"rustwright": {
"command": "rustwright-mcp"
}
}
}
| If... | Do this |
|---|---|
| You already have Chrome/Chromium | Set RUSTWRIGHT_CHROMIUM (or CHROME / CHROMIUM) to its executable in the server's env. |
| You do not have a browser installed | pip install rustwright && python -m rustwright install chromium — the server finds the downloaded browser automatically. |
| Screenshots are too large to inline | Tune RUSTWRIGHT_MCP_SCREENSHOT_MAX_BYTES; oversized captures fall back to a temp-file path instead of inline image content. |
See mcp/README.md for the full tool list and
configuration. Setting up via an AI agent? Tell it to fetch
https://raw.githubusercontent.com/Skyvern-AI/rustwright/HEAD/mcp/README.md
and follow it.
The earlier Python MCP server is deprecated: it stays in place until the native server reaches full tool parity, but new capabilities land in
mcp/only.
Drive one persistent Chromium session straight from the rustwright command, with no application code.
pip install rustwright
python -m rustwright install chromium
rustwright open example.com
rustwright snapshot
rustwright click e1001
rustwright close
snapshot shows refs; act by ref, then use the fresh snapshot returned after the action.--json for one JSON object per command when scripting.--session NAME for named browser sessions.See the agent interface guide for every verb, flag, and security detail.
Rustwright drives browsers — but you still need somewhere to run them. Skyvern (the team behind Rustwright) offers hosted Browser Sessions as a paid service that funds this project.
Features:
Each session returns a browser_address CDP endpoint that Rustwright connects to like any remote Chromium (sessions bill while open).
Use chromium.connect_over_cdp() for every remote Chromium connection.
BrowserType.connect() is not a CDP alias and does not support Playwright
run-server endpoints. See the remote-browser guide
for migration steps, endpoint diagnostics, security guidance, and the pinned
Open WebUI compose recipe.
Get started:
pip install skyvernimport asyncio
from rustwright.async_api import async_playwright
from skyvern import Skyvern
async def main():
session = await Skyvern(api_key="").create_browser_session()
async with async_playwright() as p:
browser = await p.chromium.connect_over_cdp(session.browser_address)
page = await browser.new_page()
await page.goto("https://example.com")
asyncio.run(main())
Remote sessions are Python-only for now — Rustwright's Node binding doesn't support
connect_over_cdpyet (it's on the Roadmap).
Because Rustwright never loads Playwright's Node driver, it never emits the automation signatures that ship with it:
__playwright__binding__ / utility-world globals, no driver bootstrap. The backend reports playwright_driver: "none".Runtime.enable on the default path — a normal launch + navigate never enables the CDP Runtime domain, closing the Runtime.enable console-serialization leak behind isAutomatedWithCDP. (Console/page-error/binding opt-ins still enable it lazily — detectable by design.)--disable-blink-features=AutomationControlled, rewrites HeadlessChrome/ → Chrome/ in the UA and client hints, and installs a navigator.webdriver cleanup init script.Local fingerprint runs — default Playwright failed webdriver/headless checks that Rustwright passed; these are local diagnostics, not a guarantee:
| Probe | Result |
|---|---|
| SannySoft | ✅ Clean |
| BrowserScan | ✅ Clean |
| DeviceAndBrowserInfo | ✅ Clean (after the Runtime-domain cleanup) |
| CreepJS | ⚠️ Detects headless |
[!IMPORTANT] Rustwright is not "undetectable." It is not a CAPTCHA or Cloudflare bypass, and it is not fully CDP-invisible — it still uses CDP primitives (
Target.setAutoAttach, init scripts, and lazyRuntime.enablefor console event/pageerror event/binding opt-ins). The claim is narrow: no Playwright-specific automation fingerprint, plus baseline signal hygiene.
The headline numbers are local diagnostics, not yet capped-CI evidence. On speed, one dev-host run (warm browser, 5 iterations) won 16 of 17 case means:
| Run | Cases | Rustwright | playwright-python | Speedup |
|---|---|---|---|---|
| Local dev host (warm browser, 5 iterations) | 17 | 5,256 ms | 13,418 ms | 2.55× |
Treat it as a diagnostic, not a launch claim — it is not capped-Docker/CI evidence. Methodology: BENCHMARK.md.
On memory, a form-fill diagnostic recorded the client library's footprint at 133.5 MiB for playwright-python (Python + Node driver) versus 40.6 MiB for Rustwright (no driver) — about 70% less; a separate async-concurrency diagnostic measured ~66% less on the same client-stack basis. Both cover the part the library controls — Chromium-dominated whole-process memory is roughly equal — and both are demo-grade diagnostics, not capped-CI evidence.
| Rustwright | playwright-python | Puppeteer | Patchright | |
|---|---|---|---|---|
| API | Playwright-shaped (Py + Node) | Official Python Playwright | JS/TS Puppeteer | Playwright drop-in fork |
| Engine / transport | Rust core, raw CDP | Python → Node driver | Node over CDP | Patched PW driver |
| In-process engine (no driver subprocess) | ✅ | ❌ bundled Node driver | ✅ Node is the runtime | ❌ Playwright-style driver |
| Browsers | Chromium only | Chromium, Firefox, WebKit | Chrome, Firefox | Chromium-based |
| Default input | Trusted CDP events | Browser-level | Browser / CDP | Playwright + stealth |
| Cross-origin iframes | OOPIF (alpha) | Mature | Frame |
暂无开放 Issues,或尚未同步最近议题。