MCP Agent Integration
The Sa2web MCP server exposes the remote browser as standard MCP tools for clients such as Claude Code, Claude Desktop, Cursor, Windsurf, VS Code Copilot, Cline, Roo Code, opencode, Gemini CLI, Zed, Hermes Agent, and OpenClaw.
The MCP project is open source at sa2web/sa2web-mcp and published on npm as @sa2web/mcp. For normal use, install the npm package; clone the source only for development or debugging.
For direct terminal operation, see Remote Browser CLI.
The target site is rendered inside iframe#rbi-frame. The server controls that frame through Playwright; the agent does not access the target site directly.
Prerequisites
- Node.js 18 or later.
- A global
@sa2web/mcpinstallation, or a cloned source checkout for development. - A working agent login URL for the remote browser. It normally contains
clientIdandclientSecretand must be treated as a secret. - Access to the required SaaS sites, workspaces, internal sites, and proxies assigned to the agent account.
Install
Install the published package globally. This exposes sa2, sa2-browser, and sa2-mcp on your PATH:
npm install @sa2web/mcp -g
npx playwright install --with-depssa2-mcp is the stdio server entry for MCP clients. sa2 and sa2-browser are CLI entries for terminal debugging and automation.
For source development, use the open-source repository:
git clone https://github.com/sa2web/sa2web-mcp.git
cd sa2web-mcp
npm install
npx playwright install --with-deps
npm run buildThe source build creates dist/server.js. Run npm start to check that the server starts. MCP uses stdio, so no interactive interface is expected.
Configure an MCP client
Most clients accept the mcpServers format below. With the global npm package, call sa2-mcp directly:
{
"mcpServers": {
"sa2-remote-browser": {
"command": "sa2-mcp",
"env": {
"SA2_LOGIN_URL": "https://<APP_DOMAIN>/agent/login?clientId=...&clientSecret=...",
"SA2_LOGIN_REDIRECT_PATH": "/app/login",
"SA2_HEADLESS": "false",
"SA2_IGNORE_HTTPS_ERRORS": "true"
}
}
}
}If the client cannot resolve global commands, use the full path to sa2-mcp, or point to a source build:
{
"mcpServers": {
"sa2-remote-browser": {
"command": "node",
"args": ["/absolute/path/to/sa2web-mcp/dist/server.js"],
"env": {
"SA2_LOGIN_URL": "https://<APP_DOMAIN>/agent/login?clientId=...&clientSecret=...",
"SA2_LOGIN_REDIRECT_PATH": "/app/login",
"SA2_HEADLESS": "false",
"SA2_IGNORE_HTTPS_ERRORS": "true"
}
}
}
}On Windows, use an absolute path and escape backslashes in JSON, for example E:\\sa2web-mcp\\dist\\server.js.
| Client | Configuration note |
|---|---|
| Claude Code, Claude Desktop, Cursor, Windsurf, Cline, Roo Code | Use mcpServers |
| VS Code Copilot | The root field is servers |
| opencode | The root field is mcp |
| Zed | The root field is context_servers |
| Gemini CLI | Add the server to Gemini CLI settings.json |
| Hermes Agent | Merge it into mcp_servers in ~/.hermes/config.yaml |
| OpenClaw | Merge it into Gateway mcp.servers; allow the MCP tools when sandboxing is enabled |
Restart the client after saving. The connection is ready when its tool list contains sa2_help, sa2_open_target, and browser_snapshot.
Protect credentials
Never commit a real clientSecret to Git or paste it into documentation. Inject production secrets through the MCP client or deployment platform and rotate any exposed credential.
Main environment variables
| Variable | Default | Purpose |
|---|---|---|
SA2_LOGIN_URL | none | Required agent login URL; the remote-browser origin is inferred from it |
SA2_LOGIN_REDIRECT_PATH | /app/login | Expected path after login |
SA2_AUTO_LOGIN_BEFORE_NAVIGATE | true | Log in before opening a target |
SA2_HEADLESS | false | Run the local Playwright browser headlessly |
SA2_BROWSER | chromium | chromium, firefox, or webkit |
SA2_IGNORE_HTTPS_ERRORS | true | Ignore HTTPS certificate errors; set to false to require valid certificates |
SA2_RBI_FRAME_SELECTOR | #rbi-frame | Target-page iframe selector |
SA2_RBI_FRAME_CONTENT_WAIT_MS | 15000 | Wait time for target-frame content |
SA2_NAVIGATION_TIMEOUT_MS | 45000 | Navigation timeout |
SA2_ACTION_TIMEOUT_MS | 15000 | Action and element timeout |
SA2_LOG_LEVEL | info | trace, debug, info, warn, error, fatal, or off |
SA2_LOG_STDERR | true | Write logs to stderr, leaving stdout for MCP stdio |
SA2_LOG_FILE | none | Optional append-only log file |
Do not configure the obsolete SA2_REMOTE_BROWSER_PREFIX or SA2_REMOTE_BROWSER_BASE_URL. Secrets, passwords, cookies, tokens, and authorization fields are redacted from logs.
Recommended workflow
sa2_help
-> sa2_list_available_targets # only for SaaS, workspace, or internal targets
-> sa2_open_target
-> browser_snapshot or browser_extract_text
-> browser_click / browser_type / browser_press / browser_waitOpen a public URL:
{ "type": "cloud", "url": "https://example.com" }For a saved SaaS site, workspace, or internal site, call sa2_list_available_targets, select a stable ID, and pass it to sa2_open_target:
{ "targetId": "workspace:123" }After opening the page, prefer browser_snapshot. It assigns actionable references such as ref=e3. Pass those refs to click, type, select, check, upload, hover, or drag tools. Snapshot values such as id=t3 and context=[t3] are text references, not DOM IDs, CSS selectors, or actionable refs. A selector argument must always be a real CSS selector.
Tool groups
| Group | Tools |
|---|---|
| Recommended entry points | sa2_help, sa2_list_available_targets, sa2_open_target |
| Inspection | browser_snapshot, browser_extract_text |
| Interaction | browser_click, browser_type, browser_press, browser_wait, browser_scroll |
| Forms and pointer | browser_fill_form, browser_select_option, browser_check, browser_uncheck, browser_hover, browser_drag |
| Files and clipboard | browser_file_upload, browser_paste |
| Browser controls | browser_navigate_back, browser_navigate_forward, browser_reload, browser_resize, browser_list_devices, browser_toggle_device, browser_handle_dialog |
| Output and debugging | browser_screenshot, browser_take_screenshot, browser_evaluate, browser_run_code, browser_close |
| Advanced compatibility | browser_open_login, browser_navigate, browser_list_proxies, browser_list_*, browser_enter_* |
Complete tool reference
The table below covers every tool registered by the current server. Arguments refer to the MCP arguments object.
| Tool | Purpose, key arguments, and result |
|---|---|
sa2_help | No arguments. Returns the recommended workflow, examples, and compatibility-tool list. Use it as an agent's first call. |
sa2_list_available_targets | No arguments. Lists accessible proxies, SaaS sites, workspace accounts, and internal sites with stable targetId values. |
sa2_open_target | Preferred navigation tool. Open a public URL with type=cloud, url, optional surf; or a saved target with targetId, type plus id/name, and optional waitUntil. Returns the matched target and load/login state. |
browser_open_login | Opens SA2_LOGIN_URL; optional waitUntil. Intended for login diagnostics because sa2_open_target normally logs in automatically. |
browser_navigate | Compatibility public-URL navigation. Requires url; accepts surf and waitUntil; returns the remote URL, selected proxy, and frame/login state. |
browser_navigate_back | No arguments. Clicks the remote-browser toolbar back button. Take a new snapshot afterward. |
browser_navigate_forward | No arguments. Clicks the toolbar forward button. Take a new snapshot afterward. |
browser_reload | No arguments. Reloads through the toolbar. Old snapshot refs must be discarded. |
browser_list_proxies | No arguments. Returns free-browse settings, raw proxy data, and options including direct. |
browser_list_saas_sites | No arguments. Returns raw SaaS categories plus a flattened sites list. Prefer sa2_list_available_targets for normal use. |
browser_enter_saas_site | Opens a SaaS site by optional id or name, with waitUntil. Returns the matched site and open result. |
browser_list_workspaces | No arguments. Returns raw workspace data and flattened account-level targets. |
browser_enter_workspace | Opens an account by accountId, or siteId plus username; optional waitUntil. Returns the matched account. |
browser_list_inner_sites | No arguments. Returns internal sites available to the current account. |
browser_enter_inner_site | Opens an internal site by id or name; optional waitUntil. Returns the matched site. |
browser_snapshot | Optional real CSS filter. Returns a flat semantic snapshot across the target iframe tree. Use actionable ref=eN; id=tN and context=[tN] are text references only. Raw URLs are omitted. |
browser_extract_text | Optional real CSS selector. Returns visible text from the target iframe tree. Snapshot text IDs are not selectors. |
browser_click | Click by ref, CSS selector, role/name, visible text, or paired x/y coordinates. May report a pending JavaScript dialog. |
browser_type | Requires text; locates by ref, selector, or textbox/searchbox/combobox role and name, otherwise uses the focused editor. append preserves existing content; pressEnter submits after typing. |
browser_press | Requires a Playwright key, for example Enter, Escape, Tab, or Control+A. |
browser_press_key | Compatibility alias of browser_press with identical arguments and behavior. |
browser_wait | Wait by selector, text, urlIncludes, or positive ms (default 1000). Conditions are checked in that priority order. |
browser_scroll | Scroll the root or a located element. Supports mode=by/to/intoView, directions, amount, coordinates, smooth behavior, and alignment. With no arguments, scrolls down 600 px. |
browser_hover | Hover an element located by ref, selector, role/name, or text. |
browser_drag | Drag from startRef/startSelector to endRef/endSelector; both endpoints are required. |
browser_fill_form | Requires a fields array. Each field has locator data plus required value; fills fields sequentially and replaces existing values. |
browser_select_option | Locate a <select> by ref or selector, then select by values, value, label, or zero-based index. Returns actual selected values. |
browser_check | Checks a checkbox or radio located by ref, selector, role/name, or text. |
browser_uncheck | Unchecks a checkbox using the same locator forms; radio is not supported for unchecking. |
browser_file_upload | Locate a file input by ref or selector; provide single path or paths. Paths must be readable on the MCP server machine. |
browser_paste | Paste any combination of text, html, rtf, path, or paths into a located/focused target. File inputs use direct upload mode. Returns paste mode, types, and file count. |
browser_handle_dialog | Accept or dismiss the current/pending alert, confirm, or prompt. accept defaults true; optional promptText and timeoutMs. Returns dialog details. |
browser_resize | Requires positive integer width and height. Resizes desktop viewport or reapplies the active device emulation with new dimensions. |
browser_list_devices | No arguments and does not open a page. Lists Desktop plus every Playwright device preset with viewport, screen, DPR, mobile/touch flags, and default browser type. |
browser_toggle_device | Toggle device emulation without rebuilding the context. Accepts enabled, Playwright device, dimensions, deviceScaleFactor, hasTouch, userAgent, orientation, and reload. Desktop restores desktop mode. Returns expected state, outer observed, iframe targetObserved, and warnings. |
browser_screenshot | Optional fullPage, default false. Returns a base64 PNG as MCP image content. |
browser_take_screenshot | Screenshot compatibility alias; unlike the previous tool, fullPage defaults to true. |
browser_evaluate | Executes an expression in iframe#rbi-frame, with optional serializable arg, and returns JSON. High privilege; prefer dedicated tools for routine work. |
browser_run_code | Runs async JavaScript with Playwright page, rootFrame, and locator helpers. Requires code; returns its JSON result or null. Use only trusted code. |
browser_close | No arguments. Closes browser/context and clears refs, login, dialog, CDP, and device-emulation state. |
Device switching examples
List presets before selecting one:
{ "name": "browser_list_devices", "arguments": {} }Enable a preset without losing cookies or login state:
{
"name": "browser_toggle_device",
"arguments": {
"device": "Pixel 7",
"orientation": "portrait"
}
}Use reload: true when subsequent requests must carry the new user agent. Chromium uses CDP for viewport, screen, DPR, touch, orientation, and user-agent overrides. Firefox and WebKit can only resize the viewport dynamically and report that limitation in warnings. The observed and targetObserved fields let you compare the outer shell with the target iframe. Restore the previous desktop viewport with { "device": "Desktop" }, then take a new snapshot after every switch.
Suggested agent instructions
Use the sa2-remote-browser MCP whenever web access is required.
For a public URL, call sa2_open_target with {"type":"cloud","url":"https://example.com"}.
For SaaS, workspace, or internal targets, call sa2_list_available_targets first and pass its targetId to sa2_open_target.
After opening, call browser_snapshot or browser_extract_text. Prefer snapshot refs for clicks and typing.
Obtain explicit user confirmation before publishing, submitting, deleting, purchasing, authorizing, transferring money, or sending messages.Troubleshooting
| Symptom | Resolution |
|---|---|
| No MCP tools appear | Build the project; verify absolute paths to node and dist/server.js; restart the client |
| Login fails | Verify SA2_LOGIN_URL, credentials, and account status; call browser_open_login once if needed |
| Only the toolbar appears | Call browser_wait or increase SA2_RBI_FRAME_CONTENT_WAIT_MS and SA2_LOGIN_SETTLE_MS |
| A saved target is missing | Check the agent account's group, site, workspace, internal-site, and proxy permissions |
| An action cannot find an element | Take a new snapshot and use the latest ref=eN; never use tN as a selector |
| stdio connection errors | Reserve stdout for MCP; send logs to stderr or SA2_LOG_FILE |