Created by Matteo Benedetto
A standalone Model Context Protocol (MCP) server for controlling an already-open Firefox instance through WebDriver BiDi.
This project is designed for workflows where Firefox is started manually with remote debugging enabled and an MCP-compatible client, such as GitHub Copilot in VS Code, needs to inspect existing tabs instead of launching a new browser process.
- Connects to an existing Firefox instance via WebDriver BiDi
- Lists currently open top-level tabs
- Activates a specific tab by browsing context id
- Navigates the active tab or a chosen tab to a URL
- Evaluates JavaScript in the selected tab
- Captures screenshots from the selected tab
- Keeps a BiDi session open across multiple tool calls
- Closes the session cleanly with an explicit tool
Firefox remote debugging is not Chrome CDP.
When Firefox is started with:
firefox --remote-debugging-port=9222it exposes a WebDriver BiDi endpoint, typically:
ws://127.0.0.1:9222/session
Standard reconnect flows used by Chrome-based DevTools clients are often not suitable here. This server provides a small MCP wrapper around BiDi so an MCP client can work with the already-open Firefox session directly.
- Node.js 18+
- Firefox started manually with remote debugging enabled
- An MCP-compatible client
npm installnpm startStart Firefox manually before using the MCP server:
firefox --remote-debugging-port=9222If your Firefox instance is exposed on a different host or port, configure the MCP server with the FIREFOX_BIDI_WS_URL environment variable.
Example:
FIREFOX_BIDI_WS_URL=ws://127.0.0.1:9223/session npm startOpen and keep a Firefox WebDriver BiDi session alive for subsequent tool calls.
Close the current Firefox WebDriver BiDi session.
Return the top-level tabs visible in the already-open Firefox window.
Activate a tab using its browsing context id.
Input:
{ "context": "<context-id>" }Navigate the selected tab or a provided context to a URL.
Input:
{ "url": "https://example.com", "context": "<optional-context-id>" }Run JavaScript inside the selected tab.
Input:
{ "expression": "document.title", "context": "<optional-context-id>" }Capture a screenshot from the selected tab.
Input:
{ "context": "<optional-context-id>" }For most MCP clients, use this sequence:
firefox_bidi_open_sessionfirefox_bidi_list_tabsfirefox_bidi_select_tab- One or more of:
firefox_bidi_navigatefirefox_bidi_evaluatefirefox_bidi_screenshot
firefox_bidi_close_session
This is important because Firefox often allows only one active BiDi session at a time.
Example mcp.json entry:
{
"servers": {
"firefox-bidi": {
"command": "node",
"args": [
"/absolute/path/to/firefox-bidi-mcp/src/index.mjs"
],
"type": "stdio",
"env": {
"FIREFOX_BIDI_WS_URL": "ws://127.0.0.1:9222/session"
}
}
}
}firefox-bidi-mcp/
├── package.json
├── README.md
├── LICENSE
├── .gitignore
└── src/
└── index.mjs
This project implements a lightweight WebSocket client directly over Node's net module instead of adding a heavier dependency. It:
- performs the HTTP upgrade handshake
- encodes masked client frames
- decodes server frames
- sends BiDi commands such as:
session.newsession.endbrowsingContext.getTreebrowsingContext.activatebrowsingContext.navigatebrowsingContext.captureScreenshotscript.evaluate
The server keeps one BiDi session open across tool calls and expects the client to manage the lifecycle explicitly through:
firefox_bidi_open_sessionfirefox_bidi_close_session
This avoids browsing context instability that can happen if a brand-new session is created for every individual tool call.
- Firefox may reject connections if another BiDi client is already attached
- Context ids are valid only within the active session
- The server currently focuses on tab listing, tab activation, evaluation, navigation, and screenshots
- More advanced actions like DOM click/fill helpers are intentionally left to higher-level automation layers or custom scripts executed with
firefox_bidi_evaluate
Suggested steps:
- Create a new repository named
firefox-bidi-mcp - Copy this folder into that repository root
- Commit and push
- Optionally publish to npm
Example:
git init
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin git@github.com:<your-user>/firefox-bidi-mcp.git
git push -u origin mainIf you decide to publish:
npm login
npm publishIf the package name is already taken, update the name field in package.json first.
MIT
