Skip to main content
Glama
bbauersjt

chrome-bridge

by bbauersjt

chrome-bridge

Install as a Claude Code plugin

/plugin marketplace add bbauersjt/sjt-chrome-bridge
/plugin install chrome-bridge@sjt-chrome-bridge

Then two manual steps the plugin can't do for you: pip install -r requirements.txt, and load the extension/ folder as an unpacked extension in Chrome (chrome://extensions → Developer mode → Load unpacked). Details below.


A direct line from Claude Cowork into your authenticated Chrome session — no linked tab, no file relay, no tab-group sprawl. Cowork calls MCP verbs; a local relay forwards them over a localhost WebSocket to an MV3 extension, which acts inside the live page (reads it, injects JS, calls the backend with the page's own cookies/CSRF) and returns the result.

Cowork ──MCP stdio──► server.py (relay) ──localhost WS (token)──► extension ──► Axcess / Suralink page

This is the generic transport. Site-specific JS (Axcess, Suralink) lives in the skill, not here.

Related MCP server: Chrome MCP Server

Pieces

File

Role

extension/

MV3 extension. Holds the outbound WS, runs fetch/JS in the page. Load unpacked into Chrome.

server.py

Local stdio MCP server. Registers the chrome_* verbs; pure relay.

bridge_core.py

The WebSocket hub (background thread, request/response correlation).

register_server.ps1

Registers server.py with the Claude desktop app (auto-detects Python + config path, BOM-less).

requirements.txt

mcp, websockets.

selftest.py

Proves the server half with a mock extension — no Chrome/Claude needed.

Verbs

  • chrome_bridge_status() — is the server up and the extension connected? Start here.

  • chrome_list_tabs() — open tabs (id/url/title); use an id as target.

  • chrome_page_info(target="active") — url/title/readyState.

  • chrome_fetch(url, method, headers, body, target="active") — runs fetch() inside the page, so it carries that page's cookies/auth/CSRF. The backend-call primitive.

  • chrome_eval(code, target="active", world="MAIN") — arbitrary JS, JSON result. MAIN world is subject to the page's CSP (may block eval); prefer chrome_fetch for backend calls.

Setup (Windows)

  1. Install depspip install -r requirements.txt

  2. Load the extension — Chrome → chrome://extensions → enable Developer modeLoad unpacked → select the extension/ folder. The toolbar badge reads on once it connects to the server (which must be running — see step 4/5).

  3. Register the serverpowershell -ExecutionPolicy Bypass -File ".\register_server.ps1"

  4. Fully quit and reopen the Claude desktop app (not just close the window) so it loads the new MCP server and launches server.py.

  5. Test — open an Axcess tab and log in, then in Cowork:

    • chrome_bridge_statusextension_connected: true

    • chrome_list_tabs → your tabs

    • chrome_page_info → the Axcess page's url/title

    • chrome_fetch an Axcess backend endpoint → JSON, authenticated.

Verify the server logic anytime without Chrome: python selftest.pySELFTEST OK.

Security model

  • Server binds 127.0.0.1 only and requires a shared token (BRIDGE_TOKEN, must match in bridge_core.py and extension/background.js).

  • The relay is capability-by-omission: no filesystem/shell/code-exec verbs. Arbitrary JS (chrome_eval) runs in the browser page, contained by Chrome's sandbox — never on the host.

  • You stay logged in as yourself; the extension reuses your live session (correct for an attest tool).

Known limits / next

  • DEV token is hardcoded. Before sharing with coworkers, move it to an extension options page (per-user secret), not source.

  • Multiple instances self-organize. The desktop app and the Cowork runtime each spawn server.py. The first to bind 8765 is the daemon (owns the extension); any other becomes a controller that proxies its calls through the daemon. So whichever process a call lands on, it reaches the one extension. chrome_bridge_status shows role and (for the daemon) the controller count.

  • MV3 keepalive. While connected, the worker pings every 20s so it isn't suspended (WebSocket activity resets Chrome's idle timer). A 30s alarm + on-demand connect cover cold starts, so a dropped socket self-heals within ~30s.

  • One extension at a time. The daemon tracks a single extension socket. If the extension is installed in multiple Chrome profiles, disable all but one (the daemon would otherwise bind to whichever connected last). Multi-profile routing is a future enhancement.

  • chrome_eval vs page CSP. If a page blocks eval, use chrome_fetch or world="ISOLATED".

  • Distribution. One-click handoff later = publish the extension (Web Store / enterprise CRX) + bundle server.py as a Cowork plugin (which registers the MCP server) + freeze Python to an exe so coworkers need no toolchain.

  • Port is 8765 (change in both bridge_core.py and background.js if it collides).

F
license - not found
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bbauersjt/sjt-chrome-bridge'

If you have feedback or need assistance with the MCP directory API, please join our Discord server