chrome-mcp

Security

Deny-all by default. Domain allowlist, mutation and eval gates, redaction, the audit log, and the pairing token.

Deny-all safe mode. With no policy configured: empty domain allowlist, eval off, downloads off, mutating tools off. Opt in explicitly:

chrome-mcp --allow-domain example.com --enable-mutations
chrome-mcp --policy ./policy.json          # see policy.example.json
chrome-mcp --unsafe-all-domains            # loud footgun
chrome-mcp --enable-observers              # console/network/dialog capture (patches page globals)
chrome-mcp --redact                        # scrub secret-shaped strings out of page reads

What comes back is gated too. The allowlist decides which pages may be read; it says nothing about what is on them. A logged-in page routinely renders a session token into a script tag or an API key onto a settings screen.

  • Password field values are always suppressed — in get_html, and in snapshot, where the field still appears (so you can type into it) flagged secret: true with no value. No flag, no opt-in: nobody wants those characters.
  • --redact additionally scrubs secret-shaped strings — JWTs, AWS/GitHub/Slack/ Google keys, Bearer headers, private-key blocks — out of get_text, get_html, read_as_markdown and eval. It is opt-in because a pattern will eventually fire on something you actually wanted. --redact-pattern <regex> adds your own (and implies --redact); an invalid one fails at startup rather than silently never matching.
  • Redaction runs before the output cap, so a truncated read cannot leak what a full one would have hidden.

Every call is recorded to the task's history.jsonl with the URL it touched, the policy verdict (allowed/denied), how long it took, how many bytes came back, and how many secrets were scrubbed — so "what did the agent do in my browser" has an answer after the fact.

The per-boot 256-bit token in ~/.chrome-mcp/handshake.json (mode 0600) is the only trust boundary; it is never written to stdout/stderr. On POSIX the mode is re-verified after every write and the server fails closed if the file ends up group/other-accessible. Windows has no such bits — chmod there only toggles the read-only attribute — so the check is skipped and the token's confidentiality rests on the per-user ACL of %USERPROFILE%\.chrome-mcp.

Telemetry

The chrome-mcp server sends anonymous usage statistics to PostHog, so the project can see how many installs are active, which versions and platforms are in use, and which tools fail most. A notice is printed the first time it runs.

What is sent: a random install id (kept in ~/.chrome-mcp/telemetry.json), the chrome-mcp version, OS, CPU architecture and Node major version, whether the session owns the bridge port or shares it, how many browsers are paired, and per-tool call and error counts with error codes — batched every 10 minutes.

What is never sent: URLs, domains, tool arguments, page content, screenshots, cookies, profile names, tokens, file paths, or anything you type. Events are personless and GeoIP lookup is disabled.

The browser extension sends nothing — it only ever talks to 127.0.0.1.

Turn it off with any of:

CHROME_MCP_TELEMETRY=0     # or false / off
DO_NOT_TRACK=1
--no-telemetry             # server flag

Policy file

Pass --policy ./policy.json to configure everything in one place. The example shipped with the package:

{
  "allowDomains": ["example.com", "*.wikipedia.org"],
  "allowEval": false,
  "allowDownloads": false,
  "allowAllTabs": false,
  "enableMutations": true
}

On this page