MCP server
twin-browser-mcp exposes the execution engine as 35 native tools over stdio to any MCP client — Cursor, Claude Desktop, Claude Code, Cline, or your own. One API key, nothing to install or clone: the client launches the server via npx. Every target-bearing tool is authenticated with your key, scoped to your tenant, metered, and audited.
1 · Get a key
The hosted server needs a single Twin Browser API key from Keys & Secrets.
# 1. Create an API key at https://twin-browser.com/dashboard/keys
# (starts with ab_live_…). That's the only thing the hosted server needs.
# 2. Optional — smoke-test the server yourself before wiring it into a client:
TWIN_API_KEY=ab_live_… npx -y twin-browser-mcp
# → "transport: customer (web API at https://twin-browser.com …)"2 · Register the server
Pick the config for your client — the shape is the same everywhere.
// .cursor/mcp.json (project) or ~/.cursor/mcp.json (global)
// HOSTED setup — one variable, nothing to install. Every tool routes through the web API.
{
"mcpServers": {
"twin-browser": {
"command": "npx",
"args": ["-y", "twin-browser-mcp"],
"env": {
"TWIN_API_KEY": "ab_live_…"
}
}
}
}// claude_desktop_config.json → mcpServers
// HOSTED setup — one variable. WEB_BASE_URL defaults to https://twin-browser.com.
{
"mcpServers": {
"twin-browser": {
"command": "npx",
"args": ["-y", "twin-browser-mcp"],
"env": {
"TWIN_API_KEY": "ab_live_…"
}
}
}
}# Claude Code CLI — register the server in one line. Cline uses the same shape.
# HOSTED setup — one variable, nothing to install. (Operators running their own
# engine add EXEC_URL + EXEC_SHARED_SECRET instead.)
claude mcp add twin-browser \
--env TWIN_API_KEY=ab_live_… \
-- npx -y twin-browser-mcpEnvironment & modes
Hosted-only mode (recommended): set ONLY TWIN_API_KEY — WEB_BASE_URL defaults to https://twin-browser.com — and every tool routes through the hosted web API under your key. The startup log names the resolved mode: look for [twin-browser-mcp] transport: customer in your MCP client's logs; if it says operator, stale EXEC_URL/EXEC_SHARED_SECRET env is set.
Operator mode: running your own engine, set EXEC_URL (default http://localhost:7070) and EXEC_SHARED_SECRET instead of a key and the server talks to your fleet directly. The web-backed tools — search_library, dispatch, deep_search, etl/etl_query, the monitor tools, and the streamed-run branch of submit_verification — still need WEB_BASE_URL + TWIN_API_KEY.
3 · The tools
Lead with dispatch, run_goal, compile_skill and run_skill — the rest cover async jobs, observation, extraction, search, crawl, ETL, monitors, sessions, CAPTCHA, and recording.
Core — run, compile, replay, dispatch
| Tool | Description |
|---|---|
inspect_site | Ask what Twin Browser already knows about a host before spending a run on it — the anti-bot wall recorded from previous runs (class, vendor, hit count), whether you already hold a saved session or login for it, and a ready run configuration. Read-only, no browser, no credits. |
run_goal | Execute a goal on a target URL you provide — observe, plan, and act until a success condition is met. Takes free text ({ url, prompt, success }) or a one-word built-in goal ({ target, goal }). Full run control: egress country and tier, humanized timing, warm-up, stealth, file upload, credentials, recording. A sign-in run parks by default on a 2FA/approval wall it can’t auto-resolve, returning { status:"paused", sessionId } (resume with submit_verification); pass hitl:false to opt out. If the result carries code:"credential_rejected", the site REFUSED the identifiers the run typed (credentialRejected.secrets names them, never their values) — running the same goal again sends the same wrong ones, so supply different credentials or send the user a connect_account link instead of retrying. |
compile_skill | Discover a goal once with the planner, then minimize it into a reusable, deterministic skill. |
run_skill | Blind-replay a compiled skill with no LLM in the loop — the cheap, deterministic path. |
dispatch | The cheap path: fuzzy-match a goal to an existing compiled skill via the semantic cache — a hit replays deterministically (~5× cheaper), a miss compiles and caches it for next time (needs WEB_BASE_URL + TWIN_API_KEY). |
list_skills | List the compiled skills available for replay. |
list_catalog | The runnable-skill catalog: each compiled skill plus its run contract — the params to pass, the secrets it fills, and your auth readiness on the host (mode + ready + missing). Plan a run before spending a credit. |
search_library | Semantically search the cross-tenant shared skill library by intent and host (needs WEB_BASE_URL + TWIN_API_KEY). |
Async jobs
| Tool | Description |
|---|---|
submit_run | Submit a goal as an async background job; returns a job id immediately. Same two shapes and the same run controls as run_goal. Like run_goal, a sign-in job parks by default on a 2FA/approval wall — get_job then reports status:"paused" with a sessionId. |
get_job | Poll an async job by id for status, success, steps and result. |
cancel_job | Cancel a running async job by id (full credit refund). |
Observation & data
| Tool | Description |
|---|---|
observe_page | Serialize a page into token-efficient indexed DOM state without taking any action. |
screenshot | Capture a single screenshot of an authorized URL as PNG or JPEG — viewport or full scrollable page, at a laptop or mobile viewport (or a custom size), or one CSS selector. No action, no LLM cost. |
download_asset | Fetch an authorized asset's raw bytes — a logo, image, PDF, or other document — base64-encoded. Uses a saved session automatically when the asset sits behind a login. |
extract | Read an authorized page and return structured JSON matching the fields or JSON schema you request. |
search | Web search returning ranked results; set fetchContent to also fetch and clean the top results. |
deep_search | Async deep search — search, then scrape the top results (platform-aware) for cleaned content; returns a job id (needs WEB_BASE_URL + TWIN_API_KEY). |
map_site | Discover a site’s URLs fast from sitemap.xml + robots.txt + a shallow link scan. No page content. |
submit_crawl | Crawl a whole site asynchronously (BFS), bounded by page/depth limits and path globs; returns a job id. Optional per-page structured extraction. |
etl | Run the ETL pipeline: extract → transform → chunk → embed → load into the queryable content store (needs WEB_BASE_URL + TWIN_API_KEY). |
etl_query | Semantic search over content ingested with etl; returns the top-k matching chunks (needs WEB_BASE_URL + TWIN_API_KEY). |
Monitors
| Tool | Description |
|---|---|
create_monitor | Watch a page on a schedule and push an HMAC-signed webhook when the watched value changes (needs WEB_BASE_URL + TWIN_API_KEY). |
list_monitors | List this tenant’s monitors. |
delete_monitor | Delete a monitor by id. |
get_monitor_history | Get a monitor’s recent check history — changed/unchanged/error plus value excerpts. |
Sessions, credentials & HITL
| Tool | Description |
|---|---|
connect_account | Get a one-time link that lets a HUMAN sign into a site by hand, so Twin Browser captures the session and later runs are already logged in. Use it when a run returns code:"credential_missing", when it returns code:"credential_rejected" and you have no better credential to supply (the one stored is wrong — a retry re-types it), or when a site hard-challenges automated logins from datacenter IPs and simply cannot be signed into by an agent. Never ask the user to paste a password — send this link. Re-run the task afterwards and it restores the session (sessionRestored:true). |
list_connections | Check whether the user finished a connect link you sent them (pending | active | connected | failed | expired) before re-running the task. |
list_accounts | List the stored logins you already have as { label, host, emailPreview } — masked, never a credential. Pass a returned label as account on run_goal/run_skill and that login plus its own saved session are applied automatically. Nothing back for a host? Use connect_account to have the user sign in once. |
list_warnings | List open warnings — a run's login process failed (a missing stored credential, a stored credential the SITE rejected when it was typed, or a saved session that went stale). Names which account/host is broken, and for a rejected credential which vault NAMES were refused, so you replace the value instead of retrying blind. |
acknowledge_warning | Dismiss a warning by id once you've dealt with it; a later run that hits the same problem raises a fresh one. |
control_run | Drive a PAUSED run's browser directly — click, type, press a key, scroll, or navigate. Coordinates are 0..1 fractions of the viewport, read off a screenshot. The run continues by itself once the page no longer looks blocked; there is no "done" call. For a login a human must do, prefer connect_account; for a code, submit_verification. |
submit_verification | Resume a run waiting on a 2FA/approval wall. Two paths: a PARKED run (from run_goal/submit_run) → pass its { sessionId, code } (resumes the engine session directly); a LIVE streamed run → pass its { token, code } (needs WEB_BASE_URL + TWIN_API_KEY). Codes: sms/email, TOTP, or phone approval; or cancel. |
solve_captcha | Hand a live/paused session’s CAPTCHA to the solver and continue. |
Recording
| Tool | Description |
|---|---|
record_run | Run a goal and capture a video recording of the session. |
record_job | Submit an async job that records its run to video. |
Capture a session from your own browser
For sites whose sign-in is hard to automate remotely — popup-based “Continue with Google”, hardware-attested logins — sign in on your OWN browser (where OAuth just works) and hand the session to Twin Browser. Your password never leaves your browser; only the resulting session cookies are sent, encrypted at rest.
- Download the extension folder from the repo (
extension/) and openchrome://extensions. - Turn on Developer mode, click Load unpacked, select the folder.
- Sign into the site normally (e.g. linkedin.com → Continue with Google).
- Click the Twin Browser icon, paste your
ab_live_…key, add an optional account label, and Send this session.
From then on a run restores it (sessionRestored: true). Manage or forget connected sessions under Keys & secrets → Sessions. Works in any Chromium browser (Chrome, Edge, Brave, Arc). Under the hood it POSTs to /api/v1/sessions/import.
Enterprise / managed browsers: force-install by policy instead — a signed package with a stable ID and auto-update is hosted at /extension/twin-browser-extension.crx (update manifest /extension/updates.xml). No Chrome Web Store needed. See extension/SELF_HOSTING.md for the ExtensionInstallForcelist policy JSON.
Use it
# Once connected, ask your agent in natural language, e.g.:
# "Compile a skill that logs into app.acme.com and exports invoices,
# then replay it." → compile_skill → run_skill (no LLM on replay)
# "Search the library for 'export invoices as CSV' on app.acme.com"
# → search_library
# Every target-bearing call is authenticated with your key and acts on the
# url you pass — scoped to your tenant, metered, and written to the audit log.Replays run with no LLM: run_skill replays a compiled skill deterministically, and compile_skill discovers a path once and minimizes it for reuse — so repeated tasks avoid the per-run model cost entirely.