Skip to main content
Glama
satura-browser-mcp

satura-browser-mcp

Official
by saturadev

satura-browser-mcp

MCP server for Claude Code that drives a pool of named, persistent Google Chrome browsers. Each pool browser is a real Google Chrome process with its own profile under ~/.satura-browser/pool/<name>/profile, so logins, cookies and tabs survive between sessions.

  • One tool, browser_run: the agent writes JavaScript that runs against the leased Chrome with the Playwright API (page, context, request) plus a few helpers. One call can do a whole step (navigate, fill, click, extract) and return only the data it needs. Playwright is playwright-core@1.63.0, vendored verbatim at vendor/playwright/playwright-core (nothing is pulled from npm) and attached to Chrome over CDP; src/ uses only its public API. The snapshot diff comes from BetterWright, copied into vendor/betterwright/ (MIT).

  • Saved scripts per site: working code is saved as a parametrised function under the site's host and offered back in later responses on that site, so a repeated extraction or flow runs the cached code instead of being written again.

  • Never takes focus: the MCP server launches Chrome without a startup window and opens its window in the background, newTab() and context.newPage() open background tabs (a new foreground tab would restore a minimized window and activate Chrome), and tab(i) only changes the tab the code works on; nothing calls bringToFront. Background tabs still take input, screenshots and dialogs. Only satura-browser login brings its window forward, since you sign in there.

  • Pool: each MCP session leases one browser exclusively. The lease is released when the session exits; a crashed session's lease is ignored (dead pid).

  • State is copied once, at creation. A new browser is a copy of an existing profile (default: the most recently used one), plus a one-time copy of cookies from the running source because Chrome writes cookies to disk lazily. After that nothing syncs.

  • Real Chrome, no automation flags: launched via LaunchServices with --disable-blink-features=AutomationControlled, so navigator.webdriver is false and Google sign-in works.

  • Dock: every browser gets its own Dock icon with its name and color (Browser.setDockTile). The hover label stays "Google Chrome"; it comes from the signed app bundle, and a renamed copy of the bundle would break Keychain access (logins) and could redirect Chrome's auto-updates.

Agent workflow

The server exposes a single tool:

browser_run { code: string, timeout?: seconds (default 60, max 600), changes?: boolean }

code is the body of an async function; return gives a value back. Until a browser is chosen, page throws and the server instructions tell the agent to run return await pool.status(), show you the browsers, ask which one to open, then run await pool.use(name). A name that does not exist creates a new browser.

Global

page, context, request

Playwright Page of the current tab, its BrowserContext, and an HTTP client sharing the browser's cookies

pool.status(), pool.use(name?, {cloneFrom, steal}), pool.release()

the pool (cloneFrom: 'none' = empty profile)

snap({root, depth, boxes}), ref('e12')

aria snapshot with refs; Locator for a ref

read(target?)

visible text of the page or an element as compact markdown (headings, links, lists, tables, form values)

shot(target?, {fullPage, save})

screenshot attached to the response (JPEG, CSS pixels)

tabs(), tab(i), newTab(url?)

tabs; tab(i) makes tab i current

settle(ms?)

wait until the tab's network is quiet

net.list(filter?), net.body(id), net.clear()

recorded requests of the tab (document/fetch/xhr by default) and their bodies

logs({level})

console messages and page errors of the tab

save(name, data)

write data to .satura-browser/data/ in the project, returns the path

scripts.list/run/save/get/remove

saved per-site scripts (below)

state

object kept between calls (top-level variables are not)

console.log, help(topic)

output in the response; longer docs (scripts, net, examples)

The code runs in a node:vm context that holds only these globals (no require, process or fs), with a timeout. That keeps a script from breaking the server; it is not a security boundary, same as Playwright MCP's browser_run_code_unsafe.

Response

Every response is a few short sections, each present only when it has content:

  • ### Browser: the pool picked or created a browser, or relaunched a crashed one.

  • ### Error: the message plus the line of the agent's code (or saved script) that threw. Console output and page state up to the error are still reported.

  • ### Result and ### Console: the returned value (JSON, arrays one item per line) and logs. Over 20,000 / 8,000 characters, the full text goes to .satura-browser/output/ and the response keeps the start and the path.

  • ### Events: dialogs answered automatically (alerts and beforeunload accepted, confirms and prompts dismissed, unless the code registered its own dialog handler), downloads saved to .satura-browser/downloads/, new tabs and popups, new console errors.

  • ### Page: the current tab and the other tabs.

  • ### Changes: after the network settles, the aria snapshot is compared with the previous one of the same tab (diff from BetterWright) and only the +/- lines are shown. Playwright keeps a ref for as long as its element keeps its role and name, so refs from earlier responses stay valid. A tab seen for the first time, or one that changed almost entirely, is shown in full as ### Snapshot when it is under 4,000 characters, otherwise only its size is given and the agent calls snap() or read().

  • ### Saved scripts for <host>: shown when the current site has saved scripts and this session has not seen that list yet.

ref() fails at once for a ref that is not in the latest snapshot of the tab instead of waiting for the action timeout. Actions wait up to 10 s, navigations 30 s.

Saved scripts

await scripts.save('orders', async ({ from = 1 }) => {
  await page.goto(`https://shop.example/orders?page=${from}`);
  return page.locator('tr.order').evaluateAll(rows => rows.map(r => r.innerText));
}, 'Orders table -> row texts; args {from}');

return await scripts.run('orders', { from: 2 });
  • A script is a self-contained async (args) => result function that runs with the same globals, so scripts can call scripts.run too.

  • It is stored under the host of the current tab (www. dropped) in ~/.satura-browser/scripts/<host>/<name>.js, with <name>.json holding the description and run statistics. {site: '*'} stores it for any site; scripts.run falls back to those. Scripts are shared by every pool browser and project.

  • A failing script is reported with the line inside it, counted, and listed as failing until it is saved again under the same name.

  • satura-browser scripts [SITE] lists them; satura-browser scripts rm SITE NAME deletes one.

Every call is also appended to ~/.satura-browser/runs/<date>.jsonl (code, browser, URL, time, error), which helps when debugging a session or turning a good sequence into a saved script.

Files the tool writes in the project go to .satura-browser/; add it to the project's .gitignore.

Related MCP server: Chrome MCP Bridge

CLI

satura-browser status                          every browser: free/busy, running, open tabs
satura-browser create NAME [--from SRC|--empty]
satura-browser rename OLD NEW                  (free browsers only; closes the window)
satura-browser login [URL] [--name NAME]       open a window to sign in by hand
satura-browser stop [NAME...] [--all] [--force]
satura-browser rm NAME [--force]
satura-browser chrome-profiles                 profiles of your everyday Chrome
satura-browser import-chrome PROFILE [--name NAME]
satura-browser scripts [SITE]                  saved per-site scripts
satura-browser scripts rm SITE NAME
satura-browser doctor

Setup

macOS or Linux with Google Chrome installed, Node.js 20 or newer:

git clone https://github.com/saturadev/satura-browser-mcp.git
cd satura-browser-mcp
npm install && npm run build && npm link
claude mcp add satura-browser -s user -- node "$PWD/dist/server.js"

Server flags: --browser NAME pins the session to one browser (no question asked), --headless. Config: ~/.satura-browser/config.json with chromePath, maxInstances (default 8), headless, extraArgs. Env: SATURA_BROWSER_HOME, SATURA_BROWSER_NAME, SATURA_BROWSER_HEADLESS, SATURA_BROWSER_CHROME.

Security: each running browser listens on a random 127.0.0.1 DevTools port; any local process can drive it, same as Playwright MCP with a persistent profile. browser_run executes the agent's JavaScript in the server process (see above).

Tests

npm test runs end-to-end tests against real Chrome (headless; SATURA_TEST_HEADED=1 for windows). Run them after updating vendor/playwright/ too: they check the snapshot and ref format the tool relies on.

License

MIT, see LICENSE. Third-party code in vendor/ keeps its own license: vendor/playwright/playwright-core and the modules in vendor/playwright/copied come from Playwright (Apache-2.0, see vendor/playwright/playwright-core/LICENSE and NOTICE), and vendor/betterwright comes from BetterWright (MIT, see vendor/betterwright/LICENSE).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables browser automation and web interaction control through Playwright, allowing Claude Code to navigate, click, fill forms, take screenshots, and manage sessions.
      68 npm
      9
      MIT
    • F
      license
      Not graded
      quality
      D
      maintenance
      AI-driven browser automation for real Chrome with cookies, login sessions, and extensions intact, enabling Claude Code to control a visible Chrome browser via the Chrome DevTools Protocol.
      1
      -
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables Claude Code to control a browser via Playwright for web scraping, automation, and research, with features like navigation, screenshots, form filling, and PDF export.
      24 npm
      MIT
    • F
      license
      Not graded
      quality
      C
      maintenance
      Enables Claude to directly control a persistent, real logged-in Chrome browser via 17 browser tools and 9 trading tools, with natural-language selectors, self-healing element resolution, and risk-limited trading operations.
      -