Agent Bridge for Firefox
Enables agents and MCP clients to control a running Firefox browser, including managing background tabs, navigating pages, reading page content, interacting with elements, capturing screenshots, running JavaScript, and using Firefox containers, tab groups, sessions, and profiles.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
Agent Bridge for Firefox
Lets coding agents and MCP clients drive the Firefox you're already signed into, in background tabs, without touching the tab you're looking at.
Two ways in, sharing one local bridge and one extension:
ffCLI for any agent with a shell (Claude Code, Codex, Gemini CLI). No MCP config.MCP server: stdio for Claude Desktop, Streamable HTTP at
/mcpfor Cowork and any HTTP MCP client.
Claude Desktop ──stdio/MCP──▶ bridge (:3002/:3003) ◀──ws── Firefox extension
Cowork ─────────HTTP /mcp───▶ │
Claude Code ──── ff … ──────────┤ /health + /dispatch (loopback, token)
codex / gemini ─ ff … ──────────┘Trust: anything that can run
ffor reach the MCP endpoint can act as you on every site you're signed into, including running JavaScript in your pages. Only connect agents you trust. Everything binds to localhost by default; see Security.
Requirements
Firefox 142 or newer
Node.js 20 or newer
Tested on macOS and Linux.
--coworksetup is macOS-only; Windows is untested.
Related MCP server: firefox-bridge
Install
git clone https://github.com/Jetemple/firefox-agent-bridge.git
cd firefox-agent-bridge
npm run setup # deps, and `ff` on your PATHnpm run setup -- --desktop also registers the MCP server with Claude Desktop;
--cowork does that plus LAN exposure for Cowork (see MCP clients).
Then load the extension: Firefox → about:debugging#/runtime/this-firefox →
Load Temporary Add-on… → firefox-extension/manifest.json. A temporary add-on goes away
when Firefox restarts; see Permanent install. Try it:
ff status # bridge + extension state
ff tabs # list your tabs
ff nav example.com # open a page in a background tab
ff text # read itThe bridge starts on demand (log: ~/.firefox-mcp/bridge.log); ff start / ff stop
control it by hand.
Using it from a coding agent
An agent working in this repo (to set it up for you or change it) should read
AGENTS.md. For day-to-day use, drop this in your CLAUDE.md / AGENTS.md:
## Firefox
You can drive my real, logged-in Firefox with the `ff` CLI (run `ff --help` once).
Read: `ff tabs`, `ff text`, `ff links`, `ff find <text>`, `ff screenshot -o x.png`
Act: `ff nav <url>`, `ff click <visible text>`, `ff type <field label> <text>`,
`ff key Enter`, `ff scroll --dy 600`, `ff js <code>`
Targeting is by visible text (add `--css <sel>` to scope, `--at X,Y` for raw
coordinates). `navigate`/`click` are fire-and-forget, so verify with `ff text`.
Commands run in your own background tab (🤖), never the one I'm viewing; use
`--here` only when I ask about the page I'm on.The agent tab
ff never switches the tab you're looking at or raises a Firefox window.
ff nav <url> opens a background tab the first time (stamped 🤖 in the tab
strip) and reuses it after that; ff text, ff click, ff screenshot and
the rest act on it by default. --here targets the tab you're viewing instead,
and ff tab activate brings the agent tab into view when you want to watch.
If you close the agent tab, commands error until the next ff nav rather than
falling through to your tab.
Some extensions reopen a tab under a new id as it loads; container extensions
do this to sites they assign to a container. ff follows the swap, and
ff open/ff nav report the id the page ended up in. When you're done,
ff tab close --mine closes every tab the agent opened except the one you're
viewing (--dry-run lists them first). It only knows about tabs opened since
the extension last started, and it covers every agent's tabs, not just yours.
Agents forget, so the bridge also sweeps: every 5 minutes it closes tabs an
agent opened (or a session owns) that no command has touched for 2 hours. It
never closes the tab you're viewing, a pinned tab, or the reused 🤖 tab, and it
drops session records whose tab is gone. FIREFOX_BRIDGE_AGENT_TAB_IDLE_MS
changes the cutoff; 0 turns the sweep off.
Containers
ff containers lists your Firefox containers. ff open <url> --container Gmail opens the page in one, matched by name (case-insensitive) or by its
firefox-container-N id; an unknown name fails without opening anything.
The reply names the container the tab actually landed in, since an "always
open in" rule can move it. ff tabs marks container tabs {Gmail}. An open
tab can't change containers, so to keep working in one, name a session:
ff open <url> --container Gmail --as mail, then ff -s mail <command>.
Tab groups
Firefox's native tab groups. ff open <url> --group "TV hunt"
opens the page straight into the group with that title (case-insensitive),
creating it if there's none; add --color to set its color (grey, blue, red,
yellow, green, pink, purple, cyan, orange). ff group <name> [TAB_ID...]
moves open tabs in (no ids: --tab N, --here, -s NAME, or the agent tab),
--collapse/--expand folds the group, and ff ungroup [TAB_ID...] takes
tabs out. ff groups lists them, and ff tabs marks grouped tabs ‹TV hunt›.
Grouping never changes which tab you're viewing. Pinned tabs can't join a group.
Sessions & profiles
Multiple agents (or one agent juggling tabs) can each pin a named session to a tab, so nothing ever depends on which tab you happen to be looking at:
ff open https://docs.example --as research # background tab, never steals focus
ff -s research text # every command can target the session
ff -s research tab close # closes the tab and the sessionSessions fail safe: if Firefox restarted or the tab is gone you get SESSION_STALE, never a command silently running against the wrong tab.
Run several Firefox profiles at once and name each one (once, per profile):
ff profiles name work # with one profile connected
ff --profile work nav https://mail.example
ff tabs # more than one profile? tabs come groupedWith one profile connected, nothing changes: name flags are only needed when two Firefoxes are up.
CLI reference
Run ff --help for the one-screen version. Highlights:
| bridge lifecycle & health |
| tab management |
| navigation |
| reading pages |
| capture (writes a file, prints the path) |
| pointer (query = visible text) |
| typing |
| input |
| forms |
| run JavaScript in the page |
Global flags: --tab N, --json, --timeout MS, --no-autostart.
Exit codes: 0 ok · 1 tool/bridge error · 2 usage.
MCP clients
Claude Desktop:
npm run setup -- --desktopregisters the stdio server; restart Desktop afterward.Cowork:
npm run setup -- --cowork, then upload the generatedfirefox-bridge.plugin(Settings → Upload local plugin). This exposes the bridge on your LAN behind a token; see Security.Any HTTP MCP client: point it at
http://127.0.0.1:3003/mcp. Multiple concurrent sessions are supported.
The first bridge instance owns both ports. Extra instances (a second MCP client, the CLI's
auto-start) find it through /health and forward through it. If the owner quits, a
follower takes over in-process and the extension reconnects.
Security
Localhost by default. The HTTP endpoint binds
127.0.0.1:3003; nothing on your network can reach it.LAN exposure always has a token.
--coworkgenerates a 256-bit token (.firefox-bridge-token, gitignored), binds0.0.0.0, requires the token on every request (Authorization: Bearer <token>or?token=), and bakes the header into the Cowork plugin. The setup script can't expose the endpoint without one. If you bind a non-loopback host by hand (FIREFOX_BRIDGE_SSE_HOST) withoutFIREFOX_BRIDGE_SSE_TOKEN, the bridge starts anyway and logs a warning. Don't.WebSocket relay (
127.0.0.1:3002, bridge ↔ extension) rejects connections whoseOriginis a web page, so a site you visit can't hijack it./dispatch(the CLI's endpoint) accepts only loopback connections, requires the local token (an owner-only file web pages can't read), rejects web-page origins, requires exactapplication/json, and validates every tool name and argument against the MCP schemas./healthexposes only name, version, pid, start time, and connection state.
Configuration
Variable | Default | Purpose |
|
| WebSocket port (Desktop + extension relay) |
|
| WebSocket bind address |
|
| Streamable HTTP port |
|
| HTTP bind address: set |
| (none) | Require this bearer/query token on HTTP requests |
|
| Path to local token file (0600, prevents web access) |
|
| Directory for bridge logs ( |
|
|
|
|
|
|
|
| Close agent tabs idle this long (ms); |
Caveats
navigateandclickare fire-and-forget: they return when the action is initiated, without waiting for the page to finish loading or settling. Add your own read-back (e.g.get_page_text) when you need to be sure a step landed.Mouse and keyboard tools dispatch synthetic DOM events: they drive page handlers, not native browser chrome.
find_element,get_page_text, the pointer tools,typeandpress_keyreach into iframes and shadow roots (open and closed).get_links,get_tables,javascript_tool,drag,scroll,select_optionandset_checkboxstay in the top frame's light DOM.A successful
clickmeans events were dispatched. A click aimed throughfind_element'sreffails if another element covers the target;tabHidden: trueflags a background tab, where some pages ignore clicks.
Permanent install
Release Firefox only installs signed extensions. Mozilla signs unlisted (self-distributed) add-ons automatically, usually within minutes:
Get API credentials at https://addons.mozilla.org/developers/addon/api/key/ and export them as
WEB_EXT_API_KEY(the JWT issuer) andWEB_EXT_API_SECRET.Change
browser_specific_settings.gecko.idinfirefox-extension/manifest.jsonto your own: an add-on id belongs to the account that first signed it.npm run sign, then open the.xpifromweb-ext-artifacts/in Firefox (or drag it onto a window) and approve the install. Remove the temporary copy fromabout:debuggingfirst.
Each upload needs a new version in the manifest, so bump it before signing again.
Development
npm install --prefix firefox-mcp-bridge
npm test # bridge, extension, and bench unit tests
npm run lint:extension # web-ext lintAdd a tool: define it in firefox-mcp-bridge/tools.mjs, add a handler under
firefox-extension/tools/, register it in the extension manifest, and add tests. A parity
test checks that the bridge and the extension expose the same tool names.
The extension's manifest.json version is the one that matters for releases; the
package.json versions are internal.
License
MIT, see LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Stealth web browser for agents: search, fetch, click, download and type in persistent MCP sessions.
Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Agent social posting via your logged-in Chrome. Remote MCP + OAuth. Not clawpost.dev.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables MCP-compatible agents to securely control the user's already authenticated Chrome browser via explicit tab authorization and DOM-based actions.1Apache 2.0
- AlicenseNot gradedqualityAmaintenanceEnables MCP-capable CLIs to operate real, already-logged-in Firefox tabs via a WebExtension and native messaging, without simulated input. Supports navigation, clicking, typing, reading, screenshots, and console/network capture with policy gating and frame awareness.2MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to create, read, and operate granted background tabs in Chrome via a local stdio MCP server, preserving the human's front tab. Supports tab groups, navigation, clicking/typing, screenshots, and human-priority permissions.MIT
- AlicenseNot gradedqualityBmaintenanceEnables an MCP client to drive a real, already-logged-in Firefox session through a WebExtension rather than a WebDriver connection, so tabs, cookies and logins work as-is without relaunching the browser under remote-control flags. Exposes tools for tab listing and selection, navigation, accessibility-style snapshots, clicking, filling, typing, key presses, file uploads, screenshots and waiting, with per-origin permission grants and opt-in script evaluation.MIT