mcp-firefox
Allows interaction with Firefox via its Remote Debugging Protocol, providing tools to evaluate JavaScript, navigate tabs, capture network traffic, take screenshots, and extract page content.
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., "@mcp-firefoxtake a full page screenshot of the current tab"
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.
mcp-firefox
An MCP server that connects to Firefox's Remote Debugging Protocol (RDP) over TCP, exposing browser DevTools capabilities as tools for LLMs.
How It Works
Firefox ships with a built-in Remote Debugging Protocol that speaks length-prefixed JSON over TCP. This server holds a persistent connection to that port, translates MCP tool calls into RDP messages, and returns the results:
LLM ↔ MCP (HTTP :8090) ↔ Firefox RDP (TCP :6000)A background reader task owns the socket and dispatches every inbound packet:
a packet without a
typefield is a response (or an error), and resolves the oldest pending request for the addressing actor;a packet with a
typefield is an unsolicited event.
That distinction is the only reliable correlation mechanism — Firefox does not echo a
requestId to this protocol version, and actors emit both responses and events on the same
connection. Getting it wrong is subtle: an event can resolve a request (hanging the caller), or
a response can be swallowed as an event (hanging it and losing the data).
Actor model
Firefox has two overlapping actor generations: the legacy surface (listTabs,
tabDescriptor.getTarget, navigateTo) that Mozilla is retiring, and the watcher/target
surface (getWatcher, watchTargets, target-available-form) used by its own DevTools
front-end.
This server uses the watcher/target surface as the single source of truth for per-tab actors, so resolution, navigation and target switching all go through one code path. Two legacy calls remain deliberately, because they are still the best API for what they do:
Call | Why it stays |
| The only clean tab enumeration. Watching frame targets from the parent process also returns extension/worker frames and reports |
| The unambiguous route to the parent-process (chrome) console that |
Sessions are keyed by the stable tab descriptor actor, not by browsingContextID — Firefox
allocates a new browsing context id when it switches targets on navigation, so a bcID-keyed
session would be orphaned mid-navigation and silently drop the replacement target. Target events
are correlated by their sender (the tab's watcher actor), which is stable across navigation.
Navigation is transactional: navigate waits for the replacement target before returning, so the
very next call works instead of racing a dead actor.
Requests are capped per actor (back-pressure), and a cancelled request is removed from its queue so it cannot consume the next response meant for someone else.
Promises are awaited for you, and a literal top-level await is automatically re-wrapped in an
async function, because Firefox's console rejects a bare top-level await in a plain script.
Related MCP server: Firefox DevTools MCP
Requirements
Python 3.11+
Firefox with the remote debugging server enabled (see below)
uv for dependency management
Installation
uv venv
uv pip install -e ".[dev]" # omit [dev] for a runtime-only installFirefox Setup
1. Enable Remote Debugging
In about:config, set:
Preference | Value | Purpose |
|
| Allow remote debug connections |
|
| Skip the "allow connection?" dialog |
|
| Enable chrome debugging (required for |
Without devtools.chrome.enabled, screenshot capture cannot reach the privileged APIs it needs.
2. Start the Debug Listener
# Native install
firefox --start-debugger-server 6000
# Flatpak
flatpak run org.mozilla.firefox --start-debugger-server 6000Firefox will not start a second instance against a profile that is already open, so launch it with the flag, or use a dedicated debug profile:
# Flatpak, isolated profile (recommended — avoids lock contention with your normal browser)
flatpak run org.mozilla.firefox --no-remote \
--profile ~/.var/app/org.mozilla.firefox/config/mozilla/firefox/debug \
--start-debugger-server 6000Flatpak path note: the
--profilepath is resolved outside the sandbox, so pass the host path (~/.var/app/org.mozilla.firefox/config/...). Flatpak maps the sandbox's~/.configonto that directory, so sandbox-style paths like~/.config/mozilla/firefox/debugdo not work.
Snap note: snap-packaged Firefox builds do not support
--start-debugger-server.
3. Verify
ss -tln | grep 6000You should see Firefox listening on 127.0.0.1:6000.
Running
.venv/bin/firefox-mcpStarts a streamable-HTTP MCP server. By default it binds 127.0.0.1:8090.
Variable | Default | Description |
|
| Firefox RDP host |
|
| Firefox RDP port |
|
| MCP server bind address |
|
| MCP server port |
Security: this server grants unauthenticated JavaScript execution, page reading and screenshots of a browser that is typically logged into everything. It binds to loopback only. Change
FIREFOX_MCP_HOSTonly if you fully understand the exposure.
MCP Client Configuration
{
"mcpServers": {
"firefox-devtools": {
"url": "http://127.0.0.1:8090/mcp"
}
}
}Tools
Tool | Description |
| Evaluate a JS expression in the current tab's console (promises and top-level |
| List open tabs with index, title, URL and actor id |
| Target a specific tab (by index) for subsequent calls |
| Navigate the current tab to a URL |
| Get the full HTML source of the current page |
| Get cached console messages (errors, logs) |
| Extract readable text content (strips nav, ads, boilerplate) |
| Screenshot the viewport, full page, or a CSS-selected element |
| Start capturing network requests |
| Read captured requests (optionally with response bodies, or errors only) |
| Stop network capture and clean up |
| Send arbitrary RDP JSON for experimentation |
| Drop and re-establish the RDP connection |
Most tools accept an optional tab_index to target a specific tab, using the index reported by
list_tabs.
Usage Examples
Page Content & Screenshots
# Read article text from a news site you're logged into
> read_page
# Screenshot a specific element
> screenshot(selector=".article-body")
# Full-page screenshot, written straight to disk (keeps the result small)
> screenshot(fullpage=True, save_path="/tmp/page.png")screenshot returns a data:image/...;base64,... URL by default. Passing save_path writes the
decoded image to disk and returns {path, bytes, width, height} instead, which avoids pushing
megabytes of base64 through the model context.
Tab Targeting
> list_tabs
> select_tab(index=2) # all later calls now target tab 2
> evaluate_js("document.title")Network Capture
Capture API traffic from web apps (SPAs, streaming services, etc.):
# Start recording network traffic (before triggering it, for full coverage)
> start_capture
# Navigate and trigger app behaviour
> navigate("https://www.example.com/home")
# See what requests were made
> read_capture
# Include response bodies
> read_capture(include_bodies=True)
# Only failures
> read_capture(only_errors=True)
# Done
> stop_captureRaw RDP Exploration
> raw_rdp_command({"to": "root", "type": "listTabs"})
> raw_rdp_command({"to": "<consoleActor>", "type": "getCachedMessages",
"messageTypes": ["PageError", "ConsoleAPI"]})Use list_tabs first to discover actor ids, then experiment with raw_rdp_command.
Troubleshooting
Symptom | Cause / Fix |
| Firefox isn't running with |
Connect hangs, Firefox shows an "allow connection?" prompt | Set |
| Set |
"profile already in use" on launch | Firefox is already running on that profile. Close it, or use a separate |
Tools fail right after Firefox restarts | Call |
| Was a real bug (see below); fixed — ensure you're on this version. |
Development
uv pip install -e ".[dev]"
uv run ruff check . # lint
uv run ruff format --check . # formatting
uv run mypy firefox_mcp # types (strict)
uv run pytest # testsThe test suite uses an in-process fake Firefox (tests/fake_firefox.py) that reproduces the
protocol's real quirks — nested resource batches, responses lacking a type key, interleaved
events, the watcher actor dying after unwatchResources, and Firefox allocating a new
browsing context id on navigation — so most behaviour is covered without needing a browser.
Notes & Limitations
Legacy actor protocol. Tab enumeration and the chrome console still use the older actor surface (
listTabs,processDescriptor.getTarget). Everything else goes through the watcher/target protocol and works on current Firefox (verified against 156), but Mozilla is gradually retiring parts of the legacy surface in favour of the WebDriver BiDi-based Remote Agent.One connection per server process. All tabs are reached through a single RDP connection; tab targeting is by actor, not by separate sessions.
Capture must start before the traffic. Network capture only sees requests made after
start_capture.Object results are grips. Evaluating an expression that returns a non-primitive yields an RDP object grip (a reference with a small preview), not the full serialised value. For a plain value, return a JSON string (
JSON.stringify(...)) or a primitive from your expression.No request pre-emption. Cancellation cleans up correctly (a cancelled request cannot steal the next response), but a request already sent to Firefox still runs to completion there.
No authentication. See the security note under Running.
This server cannot be deployed
Maintenance
Related MCP Connectors
Live browser debugging for AI assistants — DOM, console, network via MCP.
Official Browserless MCP server: browser automation, scraping, screenshots and PDFs for AI agents.
Control a real browser from AI agents: navigate, click, type, screenshot, read the page.
Hyperbrowser MCP — wraps the Hyperbrowser AI-agent browsing API
Related MCP Servers
- AlicenseCqualityDmaintenanceExposes a remote browser as MCP tools via Playwright, enabling AI agents to navigate and interact with web pages through DOM snapshots, clicks, typing, and form operations.405 npm14Apache 2.0
- FlicenseAqualityCmaintenanceModel Context Protocol server for automating Firefox via WebDriver BiDi, enabling page navigation, snapshots, input, network monitoring, and console debugging for AI agents in MCP clients.315,089 npm-
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to control live Zen Browser and Firefox tabs via MCP, performing actions like navigation, clicking, typing, scrolling, and running JavaScript through a local WebSocket bridge.MIT
- AlicenseNot gradedqualityAmaintenanceEnables MCP harnesses to control a live Firefox profile across all tabs and windows, exposing tools for browsing, navigation, page interaction, and browser state management. It includes an optional AI cursor that visually indicates actions being performed on pages.Mozilla Public 2.0