zcode-for-chrome
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@zcode-for-chromeOpen a new tab, go to news.ycombinator.com, and summarize the top story"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ZCode for Chrome
A Chrome extension (MV3) plus an MCP server that give ZCode control of your real browser: tabs, page reading, snapshots with stable references, clicks, form filling, keyboard input, scrolling, screenshots, and JavaScript evaluation. It runs on the profile you actually browse with, sessions and extensions included.
Architecture
ZCode -> MCP over stdio -> Node server (packages/server)
server -> WebSocket on 127.0.0.1, ports 8765-8785 -> extension
an offscreen document in the extension keeps that WebSocket open
the offscreen document forwards each request to the service worker via chrome.runtime.sendMessage
the service worker runs it with chrome.tabs, chrome.scripting, or chrome.debugger
the response travels back the same path to ZCodeThe offscreen document only has access to chrome.runtime messaging: it has
no chrome.storage and no chrome.runtime.getManifest, so anything it needs
from those (the token, the extension version) it asks the service worker for
by message.
The repo is a monorepo with three packages: packages/shared (protocol types
and constants), packages/server (the MCP stdio server with the WebSocket
bridge), and packages/extension (the extension you load into Chrome).
Related MCP server: Chrome Browser Control
Install
You need Node 22+ and Chrome 120+.
Build:
npm install npm run buildOpen
chrome://extensions, turn on "Developer mode" (top right corner), click "Load unpacked", and select this repo'spackages/extension/distdirectory.Register the server with ZCode: in
~/.zcode/cli/config.json, undermcp.servers, add thezcode-for-chromeentry fromdocs/zcode-config.example.json, replacing<ABSOLUTE_PATH_TO_REPO>with the absolute path where you cloned the repo. If you're not using a shared token, drop the wholeenvblock.Restart your ZCode session and check under Settings -> MCP that
zcode-for-chromeshows as connected.
Once it's running, open the extension's popup to see the bridge status. That's where you set the shared token, the blocked-hosts list, and the two safety toggles described below.
Tools
The 20 tools ZCode sees through the server. tabId is optional wherever it
appears: omit it and the tool acts on the active tab of the most recently
focused window. Action tools accept ref or selector; refs like e1, e2, e3... come from snapshot or query_selector and expire once the page
navigates (STALE_REF error: take a new snapshot).
Tool | Parameters | What it does |
| (none) | Bridge status: extension connection, port, versions, and |
| (none) | Lists open tabs with URL, title, and protected-page flag |
|
| Opens a tab and waits for it to finish loading (45s) |
|
| Activates a tab |
|
| Closes a tab |
|
| Navigates to a URL or runs |
|
| Waits (up to 30s) for a selector, some text, or a URL change |
|
| Visible text of the page, or of the element matching selector |
|
| One |
|
| Finds matches for a CSS selector and assigns them refs |
|
| Captures the viewport or the full page, downscaled to |
|
| Clicks via the DOM; with |
|
| Trusted click at viewport coordinates (CDP) |
|
| Moves the mouse to the element's center (CDP) |
|
| Types into an input, textarea, contenteditable, or select, with events React picks up |
|
| Selects an option in a |
|
| Types text as real keystrokes (CDP |
|
| Presses a key with trusted events (Enter actually submits forms) |
|
| Scrolls the page in a direction, or centers an element |
|
| Evaluates a JS expression via |
Security model
The extension operates your real browser, so the safeguards are on by default and controlled from the popup:
Localhost only. The server listens on
127.0.0.1and the WebSocket only accepts connections whoseOriginstarts withchrome-extension://. There's no daemon: the process exits when ZCode closes its stdin.Optional shared token. Start the server with
ZCODE_CHROME_TOKENand the extension must send the same token (set it in the popup). Without a token, the only defense is theOriginheader, which has a known gap: any local process can spoof that header and connect to the bridge. Set a token if your machine runs code you don't trust.One bridge at a time. The server binds the first port of the range (8765) or, with
ZCODE_CHROME_PORTset, that exact port; if the port is taken by another live bridge, it waits for it to free up instead of silently sliding to the next port (which used to strand the second ZCode session with no extension, since the extension always picks the lowest healthy port). SetZCODE_CHROME_ALLOW_SCAN=1to restore the old scan-the-range behavior. While waiting,browser_statusreports theconflictand tool errors name the port holder.Protected pages. Page tools refuse to act on
chrome://,chrome-extension://,devtools:,about:,edge:,file:,view-source:, and the Chrome Web Store (PROTECTED_PAGE), plus any host in yourblockedHostslist, which accepts patterns like*.bank.com(BLOCKED_HOST).Global switch. The popup's
enabledtoggle cuts off every tool exceptbrowser_status(EXT_DISABLED) and detaches any attached debuggers.evaluate_jshas its own toggle.allowEvaluateJscan be turned off (EVAL_DISABLED); it runs with a 10s timeout, truncates results to 50,000 characters, and returns exceptions as errors with their message.Limits. One in-flight command per tab, four tabs in parallel; per-tool server timeouts (45s navigation, 20s screenshots and
evaluate_js, 15s everything else); screenshots capped at two per second.Data. Screenshots stay in memory and never touch disk. Server logs (stderr only) record the tool, tab, host, and duration, never
fillortype_textvalues or page content. The popup keeps a rolling log of the last 50 actions.Debug bar.
press_key,type_text,evaluate_js, trusted clicks, and full-page screenshots go throughchrome.debugger, so Chrome shows the "ZCode for Chrome started debugging this browser" bar while they run. The attach is lazy and detaches after 30s of inactivity.
Limitations
Only acts on each tab's main frame; iframes are out of reach.
screenshotcaptures the window's active tab: if the requested tab isn't active, it activates it first (a visible side effect).Chrome 137+ ignores
--load-extension, so loading is manual fromchrome://extensionsand there's no automated E2E from the command line.CDP-based tools fail with
DEBUGGER_UNAVAILABLEif DevTools is open on the target tab; close it and retry. DOM tools keep working.The extension connects to a single server: with two ZCode sessions open, the bridge belongs to the session that took the port first, and the other session's tools fail with a hint naming the port holder. Closing the first session (or killing its server) hands the bridge to the waiting one within seconds; the running extension reconnects on its own.
Verify
npm run smoke # headless: real server + a simulated extension over WebSocket
npm run smoke:live # against your real Chrome, with the extension loadedsmoke checks that the server exposes all 20 tools, rejects web origins and
wrong tokens, that screenshot returns an image, that errors carry their
code, and that the process exits when stdin closes. It also checks that
dist/offscreen.js doesn't call any chrome.* API outside what Chrome
exposes to offscreen documents.
smoke:live opens https://example.com/, waits for the page text, takes a
snapshot, clicks the link by ref, goes back, takes a screenshot, evaluates
1+1, presses End, and closes the tab. It needs the extension loaded and
Chrome open; it waits up to 60s for the connection.
License
MIT, see LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.
Automate cloud Chrome—navigate, click, type, screenshot, run code, record screen video
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Real Chrome for agents: start a browser, read pages as numbered markdown, click, type, hand off.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables MCP clients to drive a real, logged-in Chrome browser for web automation tasks like navigation, clicking, typing, and screenshotting.4 npm1MIT
- AlicenseBqualityAmaintenanceEnables controlling a local Chrome browser via MCP tools, using a Chrome extension and WebSocket broker for browser automation.30170 npm1MIT
- AlicenseNot gradedqualityDmaintenanceEnables browser automation over MCP using a real Chrome browser with existing profile, supporting real tabs, downloads, cookies, and RPA workflows.72 npmMIT
- FlicenseNot gradedqualityDmaintenanceDrive your real, signed-in Chrome browser from any MCP client, enabling browser automation such as navigation, clicking, typing, and screenshots through standard MCP tools.1-