Skip to main content

sbrs-mcp exposes a browser to any Model Context Protocol client (Claude Desktop, Claude Code, …) over stdio. It serves one of three toolsets, the same tools as the Python seleniumbase-mcp project, and adds tools of its own for developing, debugging and testing:

--serverEngineTools
cdpPure CDP (no WebDriver)24 + 9 developer tools + get_devtools_endpoint
driverWebDriver, Driver() toolset26
sb (default)WebDriver, SB() toolset, plus this crate’s stealth tools88 + 7 stealth + 9 developer tools
Info

All three keep one browser session for the whole conversation: call start_browser once, use the other tools, then close_browser.

Install and configure#

Install the binary

Terminal

Or, without a checkout: cargo install --git https://github.com/MustCodeAl/seleniumbase-rs seleniumbase-rs-cli --locked (both binaries; the mcp-server feature is on by default in that package).

Register it with your client

For example in .mcp.json:

.mcp.json

Use ["--server", "driver"] or ["--server", "sb"] for the others.

To run a built-in plugin in every browser the server starts, add "--plugin", "shield" to args (and "--plugin-opt", "shield.level=\"strict\"" for an option, a TOML value). The operator chooses the plugins; no tool argument adds one, and a mistake in them stops the server before it listens. See Choosing plugins.

Note

Logs go to stderr; stdout carries only the protocol.

Which server?#

cdp is the lightest and the hardest to detect: no chromedriver, and it can pass common CAPTCHA widgets. Chrome or Chromium only Needs Chrome. Its tools group related actions behind an action/mode/state argument, so there are few of them (click_element, type_text, wait_for_condition, manage_tabs, …).

How tools behave#

  • Errors are results. A failed action (missing element, timeout, bad argument) is returned to the model as an error result it can read and act on, not as a protocol failure. See When a call fails.
  • Waits are bounded. Every timeout or pause is capped at one hour.
  • Files stay in one folder. Screenshots, saved pages, PDFs, downloads and cookie files are written under SB_MCP_OUTPUT_DIR (default ./mcp_output). Absolute paths and .. are refused. Cookie files go to saved_cookies/ there.
  • Annotations are honest. Each tool declares whether it reads, navigates or overwrites files, so a client can decide what to confirm. A tool whose actions differ in kind (for example manage_cookies) declares only its title.
  • Defaults. start_browser runs headless on Linux and headed elsewhere unless you set headless.

Where the files go, under SB_MCP_OUTPUT_DIR:

mcp_outputSB_MCP_OUTPUT_DIR, default ./mcp_output; screenshots, saved pages, PDFs and downloads are written here
saved_cookiescookie files
debug_bundlescapture_debug_bundle writes one folder here
<name>a folder labelled by the name argument
screenshot.png
page.htmlvalues of password and hidden inputs replaced
bundle.jsonURL, title, viewport, user agent, console messages, failed requests, cookie and storage names, the last tool calls
exported_tests
<test_name>.rsexport_session_as_test writes the session as a Rust tokio test that uses BaseCase

When a call fails#

Every server, driver included, answers a failed call with a report: a stable kind (the error’s category, or invalid_argument, no_browser, refused), a one-line summary, the call that was tried, the hint (likely cause and fix), and up to three next steps that name tools this server has. For a failure the page can explain (an element, a wait, an assertion, a navigation or a script) the server first reads the page: URL, title, ready state, recent JavaScript errors, and how many elements the selector matched and how many were visible. Reading it takes at most two seconds; a page that does not answer is reported as not read, and an argument error never touches the page.

The same fields come as structured content for clients that read JSON. A mistyped choice or tool name is answered with the nearest valid one ("bakc": did you mean "back"?).

Developer tools#

cdp and sb add nine tools that help a model write tests, find out why something fails and check a page. They read the page with one self-contained script per call, which leaves nothing behind (except the console recorder, on request) and never sends Runtime.enable.

inspect_page#

An outline of the current page: URL, title, ready state, viewport, headings, forms, iframes, shadow-DOM hosts, counts, and every visible control with its role, accessible name, a selector that finds it, and whether it is enabled or off screen.

scopestring#

A selector to look inside.

limitinteger#

Controls to list, 1 to 500.

Default: "50"

include_hiddenboolean#

Also list controls that are not visible.

Default: "false"

The call and its result:

When to use it: first, on a page you have not seen, instead of reading the HTML.

suggest_selectors#

Finds the element meant by text, role or a rough selector (at least one), and returns ranked selectors for it: test attributes (data-testid and similar), id, name, aria-label, placeholder, link text, a text XPath, classes and a short CSS path. Each is checked against the page: matches, unique, finds_element, and stable (false for ids and classes that look generated).

textstring#

Text the element shows or is labelled with.

rolestring#

ARIA role such as button, link, textbox or checkbox.

selectorstring#

A rough selector that finds it.

limitinteger#

The number of selectors per element, 1 to 20.

Default: "5"

When to use it: before writing a test step, or when a selector breaks.

diagnose_selector#

Explains why selector finds nothing or cannot be used: invalid syntax (with the browser’s message), no match, matches that are hidden (display, visibility, opacity, no size), off screen, disabled or covered by another element (which one), matches inside a same-origin iframe or an open shadow root, and near misses (a similar id or class, the same text in different case or spacing, a data-test* variant). It ends with a one-line verdict, advice about the selector’s text (lnk= for link=, XPath without //, :contains(), which is not CSS) and up to three next_steps.

selectorstringrequired#

CSS selector, XPath, or link=text.

When to use it: right after a click, type or wait failed on a selector.

get_console_messages#

The page’s console messages and uncaught errors. Recording starts when you pass start: true: it replaces the page’s console functions, which a page script can notice, and lasts until the page navigates. The Pure CDP engine also reports, as page_errors, the uncaught errors since the page loaded, with no recording.

levelstring#

The least serious level to include: all, error, warning, info or log.

Default: "all"

startboolean#

Start recording console messages in this page.

Default: "false"

clearboolean#

Forget the messages after reading them.

Default: "false"

limitinteger#

Most messages to return, 1 to 500.

Default: "100"

When to use it: when a page behaves oddly, or a failure report lists script errors.

get_failed_requests#

Requests that failed. Always: responses of status 400 and up from the browser’s performance timeline (it knows the status of same-origin and CORS-enabled resources only). On the Pure CDP engine, watch: true also records every failed, blocked or refused request of the active tab from then on (through the Network domain), with its error, such as net::ERR_NAME_NOT_RESOLVED.

watchboolean#

Start recording failed requests (Pure CDP engine).

Default: "false"

clearboolean#

Forget the recorded failures after reading them.

Default: "false"

limitinteger#

Most requests to return, 1 to 500.

Default: "100"

When to use it: when a page is blank or half-loaded, or a navigation failed.

audit_page#

Accessibility and HTML checks on the page as it is now, after its scripts ran, with the rules of seleniumbase_rs::api::html_inspector::HtmlInspector: images without alt text, controls without a label or name, duplicate ids, empty links and buttons, heading order, missing lang or title, and more. Each finding has its rule, severity, message and a selector. Static checks only: no colour contrast or focus order.

severitystring#

all (warnings too) or error (errors only).

Default: "all"

limitinteger#

Most findings to return, 1 to 500.

Default: "50"

capture_debug_bundle#

Writes a folder under debug_bundles/ in the output directory with screenshot.png, page.html (values of password and hidden inputs replaced) and bundle.json: URL, title, viewport, user agent, console messages, failed requests, cookie and storage names (never values), and the last tool calls. In those calls the arguments that can hold a secret (text, value, password, secret, token, totp_key, expression, script and proxy) are replaced by their length, and so is the result of every call that succeeded: it reads [redacted, N characters], so what get_cookies, manage_cookies or a storage tool returned is not saved with the report. A call that failed keeps its error text, since that says what went wrong.

namestring#

A label for the folder; only letters, digits, - and _ are kept.

callsinteger#

How many recent tool calls to include, 1 to 200.

Default: "20"

When to use it: to attach to a bug report, or to hand a failure to a person.

export_session_as_test#

Writes the successful steps of the browser session since the last start_browser as a Rust #[tokio::test] that uses BaseCase, to exported_tests/<test_name>.rs in the output directory. Reads are left out; waits and assertions are kept; a change with no BaseCase equivalent (a script, a fixed pause, a click by position) becomes a // skipped: comment in its place and is listed in the result.

test_namestring#

Name of the test function and file, such as checkout_works. It must be a Rust identifier.

Default: "exported_session"

When to use it: once a session does what a test should check.

diagnose_environment#

Needs no browser. Reports the platform, whether Chrome and chromedriver are found, their major versions and whether they fit (a mismatch names the fix: sbrs install, then sbrs doctor), whether the output directory exists and is writable and how much disk is free there (from df; null where there is none), CHROMEDRIVER_PATH, every SB_* setting in the environment (a value whose name says it is secret, such as SB_API_TOKEN, and the user and password in a proxy URL are redacted), whether a session is running, and a list of problems with the fix for each.

When to use it: before the first start_browser, or when one fails.

get_devtools_endpoint#

cdp server only

Only in the cdp server. Reports the DevTools endpoint of the running browser (http_url such as http://127.0.0.1:53017, websocket_url, version_url) and mcp_configs that attach Google’s chrome-devtools-mcp (--browserUrl) or Microsoft’s Playwright MCP (--cdp-endpoint) to the same browser, plus the claude mcp add commands for Claude Code. Without a browser it answers running: false.

When to use it: to hand the session to a second DevTools client, for example the performance traces of chrome-devtools-mcp, while this server keeps driving the page. DevTools MCP servers covers those servers and sbrs devtools, which starts such a browser from the command line.

Prompts to try#

Once the server is registered, these prompts exercise the developer tools.

Find a selector that survives a redesign

Open https://example.com with the seleniumbase-cdp server, call inspect_page, then use suggest_selectors to give me a stable selector for the main link. Prefer test attributes and tell me which selectors are marked unstable.

Debug a click that does nothing

The click on the checkout button failed. Run diagnose_selector on it, then get_console_messages with start set to true, repeat the click, and report any covering element, script error or failed request from get_failed_requests.

Turn a session into a test

Walk through the sign-in flow on the site I name, check it with audit_page, and when it works call export_session_as_test with the test name sign_in_works. Show me the file it wrote and list the steps it skipped.

Security#

Danger

These tools let a model drive a real browser as you. Only connect clients you trust, and prefer a throwaway browser profile.

  • run_javascript, execute_script and evaluate run arbitrary script in the current page.
  • Cookies and web storage can hold logins. manage_cookies, get_cookies and the storage tools return them to the model.
  • choose_file uploads a file from this machine; download_file fetches a URL from this machine’s network.
  • patch_chromedriver (in sb) modifies a binary on disk.
  • get_devtools_endpoint (in cdp) gives out the address of the browser’s DevTools endpoint. It listens on 127.0.0.1 only, but any program on this machine that knows it can drive the browser.
  • capture_debug_bundle saves a screenshot and the page’s markup, which can show personal data; values of password and hidden inputs, cookies and storage, and the results of earlier tool calls are left out. export_session_as_test writes the text the model typed into the test, since the test must type it again.

Using the servers from Rust#

mcp::cdp::host, mcp::driver::host and mcp::sb::host build a Host you can call directly with Host::call(name, arguments), which is how the test suite exercises every tool against a mocked browser. To add a tool, see “Adding an MCP tool” in the developer guide.

Next steps#