Browser Bridge: your Chrome as a tool
A Manifest V3 extension, a native messaging host, and Chrome DevTools input let the agent search, read, click, type, upload, and screenshot in your own browser, visibly and within limits.
Checked against the source on 17 September 2026
On this page
How the bridge works
- Tool callitem/tool/call
Codex asks the desktop to run use_user_browser. The desktop checks upload paths, queues the command, and waits 60 seconds, or 5 seconds for status. Without a result in time, Codex gets a tool error that says to install the extension and keep Chrome open.
- Long-pollplantocode.next
The extension asks for one command at a time. The desktop answers command: null after 25 seconds, and the extension asks again at once. A 20-second keep-alive and a 30-second reconnect alarm keep it asking, so the desktop never listens on a network port.
- Native hostplantocode-browser-bridge-host
Chrome starts it for com.plantocode.browser_bridge. It refuses an extension origin outside the published and development IDs and PLANTOCODE_CHROME_EXTENSION_ORIGINS in its own environment, adds the per-install secret to every message, and opens one socket or pipe connection per message.
- Resultplantocode.result
Frames from Chrome may reach 64 MiB and frames to Chrome stop at 1 MiB, so large results fit on the way up and commands stay small on the way down. The desktop writes a screenshot to a file and gives Codex text, Markdown, and the path.
The desktop allows the published Chrome Web Store extension ID and the fixed development ID from the unpacked manifest, and PLANTOCODE_CHROME_EXTENSION_ORIGINS adds further trusted origins to that manifest. The host accepts them only when the same variable is set in its own environment. The per-install IPC secret lives in a file created with mode 0600. The host is registered when the Browser Bridge setting is on, which it is not by default, and PLANTOCODE_DISABLE_BROWSER_BRIDGE=1 forces it off. Chrome 120 is the minimum, and the extension spans incognito windows so research can run there.
| Deadline | Value |
|---|---|
| Command | 60 seconds; status 5 seconds. |
| Extension long-poll for the next command | Returns empty after 25 seconds. |
| IPC connect / response | 5 seconds / 35 seconds. |
| Owned tab record | 30 minutes, refreshed on each use, swept by a 30-second alarm. |
Every DevTools action attaches chrome.debugger (protocol 1.3) and detaches when it is done, so Chrome shows its debugging banner per command. A click first runs an injected locate script to find a document point and then dispatches a DevTools click there; typing focuses the target first and inserts text through the protocol. That is why they work on pages that ignore synthetic DOM events; inputMode=dom exists for the sites that need the legacy path, and select, check, and scroll stay injected DOM actions. Extraction waits for the page to load and for the DOM to go quiet, by default 800 milliseconds of quiet within an 8 second readiness window that can stretch to 30 seconds, then reads the rendered visible DOM including open shadow roots and frame content, and returns the title, URL, text, a reader view, Markdown, links, headings, frame metadata, and warnings.
Enable it
- Install the PlanToCode Browser Bridge extension from the Chrome Web Store. For development, load the desktop/browser-extension folder unpacked instead.
- In PlanToCode, open Settings → Connections → Browser Bridge and enable it. Saving starts the IPC server and registers the native messaging host at once; the restart hint in the UI is stale.
What the agent can do
| Action | Behavior |
|---|---|
| status | Returns the active tab and an inventory of every open tab in every window with URL and title, plus owned browser workspaces and windows, so later calls can target known IDs. Without a connected extension it returns bridge diagnostics instead: host name, IPC endpoint, allowed extension IDs, resolved host binary, and manifest install targets. |
| search, open, extract | Search opens a visible Google results page and returns classified result entries. Open and extract accept a URL and can use a new tab, the current tab, or an explicit tab ID, and extract can wait for a selector, text, or URL fragment before reading. |
| click, type, press, select, check | Locators accept a CSS selector, visible text, role plus accessible name, label, placeholder, a frame ID from extract or inspect, or coordinates in viewport or document space. Check is idempotent and reports the final checked state. |
| upload | Assigns up to 20 absolute local file paths to a located file input through the DevTools Protocol. The desktop first checks that each path is absolute, not a symlink, and a readable regular file, canonicalizes it, and rejects components such as .ssh, .gnupg, keychains, cookies, id_rsa, .env, .pem, .key, private_key, secret, and token. There is deliberately no byte limit, no file content enters the transcript, and protected Chrome pages can block it. |
| scroll, wait, inspect | Wait covers load, selector, text, role, label, placeholder, and URL conditions. Inspect lists interactive elements before a click when the page shape is unclear. |
| screenshot | Captures the full page through the DevTools Protocol by default, or the viewport or a clip rectangle, as JPEG at quality 70. A protected page returns full_page_screenshot_unavailable instead of a silent viewport image. The desktop writes the decoded image with mode 0600 under the cache directory in plantocode/browser-screenshots and returns the path; nothing deletes these files automatically. |
| close | Closes only extension-owned tabs and, with closeWorkspace, the owned windows; a window that still holds a tab the extension did not open is left alone. The extension has no history permission, so nothing is removed from Chrome history. |
Workspaces, windows, and incognito
- WorkspacebrowserWorkspaceId
Open, search, and extract with a URL put new tabs in the window of an explicit browserWorkspaceId, else run:<runId>, else session:<sessionId>, unless the call asks for the current window or tab. That window is created unfocused at 1280×900.
- Profileincognito
Only search asks for incognito, and open and extract join the workspace’s window whatever its profile. An explicit incognito value that the window does not match opens a new window that takes over the workspace, and incognito needs Allow in Incognito for the extension.
- Page actionstabId
Click, type, press, select, check, upload, scroll, wait, inspect, screenshot, and extract without a URL use tabId, else the active tab of an explicit browserWorkspaceId, else the active tab of Chrome’s current window. The run and session IDs do not steer them.
Research runs in a dedicated, unfocused 1280×900 Chrome window per browser workspace ID, so parallel research streams stay isolated. The ID comes from an explicit browserWorkspaceId, else the run, else the session, else the command. Only search asks for incognito, which keeps a search spree out of your history and requires the extension to be allowed in incognito windows. Open and extract ask for no profile and join the workspace’s existing window, so after a search they run incognito too. Pass incognito: false for signed-in work. When the workspace window is incognito, that opens a normal-profile window that takes over the workspace. Page actions ignore the run and the session. They use tabId, then an explicit browserWorkspaceId, and otherwise Chrome’s current window. Without an explicit wait condition, a readiness timeout does not fail the extraction. It returns the current content with a warning.