Featured

Deploy OpenClaw in 60 seconds — 20% off logoDeploy OpenClaw in 60 seconds — 20% off

Launch OpenClaw on Hostinger in about 60 seconds and keep your agent live 24/7. Our referral link gives you 20% off, no coupon code needed.

Launch on Hostinger
Run your Hermes agent on Hostinger, fully managed logoRun your Hermes agent on Hostinger, fully managed

Launch Hermes on Hostinger in one click, fully managed, no VPS knowledge needed. Use code ZACAARON10 for 10% off.

Launch on Hostinger
Crawl and scrape any site into clean data, 10% off logoCrawl and scrape any site into clean data, 10% off

Firecrawl crawls and scrapes any site into clean markdown for your agent. Get 1,000 free credits, and new users get 10% off their first purchase.

Try Firecrawl free
Your own AI agent, running 24/7 with QwikClaw logoYour own AI agent, running 24/7 with QwikClaw

QwikClaw sets up and runs an always-on OpenClaw agent for you. One click, no config files, no server setup.

Deploy now
One API to scrape, enrich, and extract the internet. logoOne API to scrape, enrich, and extract the internet.

Context.dev gives your agents a single API to scrape, enrich, and extract live web data — no proxies, no parsers, no maintenance.

Start building free
SetupClaw: done-for-you OpenClaw for founders & exec teams logoSetupClaw: done-for-you OpenClaw for founders & exec teams

White-glove OpenClaw for founders and exec teams (4–50+ employees): we install, harden, integrate your tools, and maintain it — secured from day one.

Get it set up for you
SEO data APIs for your agent, $1 free credit logoSEO data APIs for your agent, $1 free credit

DataForSEO gives your agent live access to SERP results, keyword data, backlinks, and on-page SEO data through one API. New accounts get a $1 credit, good for up to 20,000 keyword or backlink lookups.

Try DataForSEO free
Reach 47,000+ AI builders

A flat monthly placement in front of developers actively installing AI tools. No lock-in, cancel anytime.

Advertise here

Works with

Claude CodeClaude DesktopCursorVS CodeClineCodex CLIOpenClaw+ any MCP client

Install to Claude Code

This server doesn't publish a one-line install command. Follow the setup in the source repository.

Summary

Enables Claude Code to inspect network traffic, query the DOM, read console logs, and capture WebSocket frames in Firefox.

README.md

Firefox Browser Bridge

Note: This entire project — all code, tests, documentation, and configuration — was generated by Claude (Anthropic). No human-written code.

MCP server + Firefox extension that gives Claude Code real-time browser debugging tools.

What It Does

Connects Firefox to Claude Code via the Model Context Protocol, enabling Claude to inspect network traffic, query the DOM, read console logs, and capture WebSocket frames -- all from the CLI.

Available Tools

Network

| Tool | Description | |------|-------------| | get_network_requests | List captured HTTP requests, filterable by URL pattern, method, status code, and content type | | get_request_details | Get full headers, request body, response body, and timing for a specific request | | search_network | Full-text search across all captured request URLs, headers, and bodies |

Page

| Tool | Description | |------|-------------| | get_page_info | Get the active tab's URL, title, and tab ID | | query_dom | Query DOM elements by CSS selector (returns tag, text, attributes, outerHTML) | | get_page_html | Get full page HTML or a specific element's HTML (truncated at 500 KB) | | get_screenshot | Capture a PNG/JPEG screenshot of the monitored tab | | get_storage | Get localStorage, sessionStorage, and cookies (including HttpOnly) |

Interaction (requires the interact capability, off by default)

| Tool | Description | |------|-------------| | navigate | Navigate the monitored tab to an http(s) URL | | reload | Reload the monitored tab (optional cache bypass) | | click | Click the first element matching a CSS selector | | fill | Set an input/textarea/select/contenteditable value and fire input/change events |

Diagnostics

| Tool | Description | |------|-------------| | get_capture_status | Report what the extension is actually capturing: connection, monitored tab, capability toggles, per-tab hook presence (e.g. CSP-blocked), and buffer counts |

Console

| Tool | Description | |------|-------------| | get_console_logs | Get recent console log entries, filterable by level (log, warn, error, info, debug) |

WebSocket

| Tool | Description | |------|-------------| | start_ws_capture | Start capturing WebSocket frames matching a URL pattern | | stop_ws_capture | Stop capturing WebSocket frames for a URL pattern | | get_ws_frames | Retrieve captured frames, filterable by URL pattern and direction (sent/received) |

Architecture

Claude Code <── MCP stdio ──> Python Server <── WebSocket ──> Firefox Extension
                               (port 7865)

The system has two components:

  1. Python MCP Server (src/mcp_server/) -- runs two async tasks: an MCP stdio server that exposes tools to Claude Code, and a WebSocket server on port 7865 that communicates with the extension.
  1. Firefox Extension (extension/) -- a Manifest V2 background script that connects as a WebSocket client to the MCP server. It captures HTTP traffic passively via the webRequest API, with page-level XHR/fetch hooking as a fallback for response bodies that filterResponseData fails to capture (common with POST responses on servers that use connection: close). It performs DOM queries, console log capture, and WebSocket frame interception on demand by injecting content scripts.

Data flow: Claude calls an MCP tool, the server either queries its local in-memory stores (for network data) or sends a command to the extension via WebSocket (for DOM, console, and WS operations). The extension executes the command and responds. Request/response correlation uses UUID-based message IDs with a 5-second timeout.

Prerequisites

  • Python >= 3.12
  • uv (Python package manager)
  • Firefox >= 109

Quick Start

1. Clone the repository

git clone <repo-url>
cd firefox-extension

2. Install dependencies

uv sync

3. Load the Firefox extension

  1. Open Firefox and navigate to about:debugging#/runtime/this-firefox
  2. Click Load Temporary Add-on...
  3. Select extension/manifest.json from the cloned repository
  4. The extension badge shows ON (green) when connected to the MCP server, OFF (red) when enabled but not connected, and OFF (grey) when switched off with the popup's master toggle

4. Configure MCP in your project

Add a .mcp.json file to your project root (or merge into an existing one):

{
  "mcpServers": {
    "browser-bridge": {
      "command": "uv",
      "args": ["run", "--directory", "/absolute/path/to/firefox-extension", "python", "-m", "mcp_server.server"]
    }
  }
}

Replace /absolute/path/to/firefox-extension with the actual path to the cloned repository.

5. Restart Claude Code

The MCP tools appear automatically. You can verify by asking Claude to list its available tools.

Extension Controls

Click the extension icon in the Firefox toolbar to open the control panel. From here you can:

  • See the connection status (green dot = connected to MCP server, red = disconnected)
  • Flip the master on/off switch (labelled Extension) to fully activate or deactivate the extension. When off, it disconnects from the server and removes all traffic listeners and page hooks, so it has zero effect on browsing until switched back on. The setting persists across restarts.
  • Toggle individual capabilities on or off (only available while the extension is on and connected):

| Toggle | Controls | MCP Tools Affected | |--------|----------|--------------------| | Network Requests | HTTP traffic capture and storage | get_network_requests, get_request_details, search_network | | DOM / HTML | Page queries, HTML, screenshots, storage | get_page_info, query_dom, get_page_html, get_screenshot, get_storage | | Console Logs | Console output capture | get_console_logs | | WebSocket Frames | WS message interception | start_ws_capture, stop_ws_capture, get_ws_frames | | Interact | Click, type, and navigate (mutates the page; off by default) | navigate, reload, click, fill |

When a capability is disabled, the corresponding MCP tools return a "capability disabled" error. Settings persist across browser restarts via browser.storage.local. get_capture_status is never gated, so diagnostics work even when everything else is off.

This is useful for limiting the amount of data flowing through the bridge -- for example, disable network capture when you only need DOM queries, or turn off everything except WebSocket frames during a targeted debugging session.

Usage Examples

Once everything is connected, you can ask Claude Code things like:

  • "Check the network requests for any failed API calls on this page"
  • "Query the DOM for all form input elements"
  • "Start capturing WebSocket frames for wss://api.example.com and show me what comes through"
  • "Show me the console errors from the current page"
  • "Search the captured network traffic for any requests containing 'auth'"
  • "Get the full response body of that 500 error request"
  • "Take a screenshot of the page" / "Show me the cookies and localStorage"
  • "Navigate to the login page, fill in the email field, and click submit" (needs the Interact toggle on)
  • "Why am I not seeing any network requests?" (uses get_capture_status)

Tool Reference

get_network_requests

List captured HTTP requests, newest first.

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | url_pattern | string | no | Regex pattern to match against URLs | | method | string | no | HTTP method filter (GET, POST, etc.) | | status_code | integer | no | HTTP status code filter | | content_type | string | no | Content type substring filter (e.g. "json", "html") | | limit | integer | no | Max results to return (default 50) |

get_request_details

Get full details of a specific request.

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | request_id | string | yes | Request ID from get_network_requests |

search_network

Full-text search across captured request URLs, headers, and bodies.

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | query | string | yes | Search string | | limit | integer | no | Max results (default 50) |

get_page_info

Get the active tab's URL, title, and tab ID. No parameters.

query_dom

Query DOM elements by CSS selector.

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | selector | string | yes | CSS selector to query |

get_page_html

Get full page HTML or a specific element's HTML.

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | selector | string | no | CSS selector. If omitted, returns full page HTML |

get_console_logs

Get recent console log entries from the current page.

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | level | string | no | Filter by level: log, warn, error, info, debug | | limit | integer | no | Max entries (default 100) |

get_screenshot

Capture a screenshot of the monitored tab. Returns an image (PNG by default).

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | format | string | no | "png" (default) or "jpeg" | | quality | integer | no | JPEG quality 0-100 (default 80; ignored for png) |

get_storage

Get the current page's localStorage, sessionStorage, and cookies (including HttpOnly cookies, which document.cookie cannot see). Large values are truncated. No parameters.

get_capture_status

Diagnose what the extension is capturing: connection state, monitored tab, capability toggles, whether the network/console/XHR hooks are actually present on the current tab (surfacing CSP-blocked injection), buffer counts, and server-side store totals. No parameters. Use this when a capture tool unexpectedly returns nothing.

navigate

Navigate the monitored tab to a URL. Only http/https URLs are allowed. Requires the interact capability.

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | url | string | yes | The http(s) URL to load |

reload

Reload the monitored tab. Requires the interact capability.

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | bypass_cache | boolean | no | Hard reload bypassing the cache (default false) |

click

Click the first element matching a CSS selector. Fires a real DOM click event. Requires the interact capability.

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | selector | string | yes | CSS selector of the element to click |

fill

Set the value of an input, textarea, select, or contenteditable element and fire input/change events (using the native value setter so frameworks like React register the change). Requires the interact capability.

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | selector | string | yes | CSS selector of the field | | value | string | yes | Value to set |

start_ws_capture

Start capturing WebSocket frames for connections matching a URL pattern. Frames are not captured by default -- you must call this first.

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | url_pattern | string | yes | Pattern to match WebSocket connection URLs |

stop_ws_capture

Stop capturing WebSocket frames.

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | url_pattern | string | yes | The same pattern passed to start_ws_capture |

get_ws_frames

Retrieve captured WebSocket frames.

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | url_pattern | string | no | Filter by connection URL pattern | | direction | string | no | Filter by direction: "sent" or "received" | | limit | integer | no | Max frames (default 100) |

How It Works

The MCP server (src/mcp_server/server.py) launches two concurrent async tasks:

  • MCP stdio server -- communicates with Claude Code over stdin/stdout using the MCP protocol. Exposes 17 tools.
  • WebSocket server -- listens on ws://127.0.0.1:7865 for the Firefox extension to connect. Connections whose Origin is not a moz-extension:// origin are rejected, so a web page cannot open the port and impersonate the extension.

The Firefox extension runs a persistent background script that:

  • Connects as a WebSocket client and auto-reconnects with exponential backoff on disconnection.
  • Passively captures all HTTP traffic using Firefox's webRequest API (listeners are registered dynamically and only active when network capture is enabled). Response bodies are captured via filterResponseData (primary) with two fallback mechanisms: cache-based re-fetch for GET requests, and page-level XHR/fetch hooking via dynamically registered content script (document_start) for POST/PUT/DELETE/PATCH responses where filterResponseData produces no data. Body capture (filterResponseData) is restricted to xmlhttprequest resource types (XHR/fetch API calls) -- document loads, scripts, stylesheets, images, fonts, and other non-API traffic skip body capture entirely, which dramatically reduces IPC overhead. Monitoring is scoped to a single tab (the active tab, re-selected if the monitored tab closes). The hook communicates with the content script relay via synchronous DOM attribute + dispatchEvent, avoiding async postMessage races with page navigation. URLs are resolved to absolute before correlation. Captured data is stored in in-memory ring buffers -- 500 requests per tab, up to 20 tabs.
  • On demand, injects into the active tab to perform DOM queries, console log interception, and WebSocket frame capture. Console and WebSocket hooks run in the page world via injected <script> tags (a content-script console/window.WebSocket override does not intercept the page's own calls under Firefox Xray isolation); console logs are read back through window.wrappedJSObject.
  • Correlates requests and responses using UUID-based message IDs with asyncio Futures (5-second timeout on the server side).

Only a single extension connection is allowed at a time.

Key Files

| File | Purpose | |------|---------| | src/mcp_server/server.py | Entry point; wires up MCP + WebSocket servers | | src/mcp_server/tools.py | MCP tool definitions and dispatch logic | | src/mcp_server/ws_bridge.py | WebSocket server, connection manager, message routing | | src/mcp_server/request_store.py | In-memory ring buffer stores for requests and WS frames | | extension/background.js | All extension logic: WS client, network capture, DOM tools, WS frame capture | | extension/xhr_hook_content.js | Content script for XHR/fetch response body capture (registered at document_start) |

Troubleshooting

Extension badge shows "OFF" The MCP server is not running. Start it manually to verify: ``bash uv run python -m mcp_server.server ``

Tools return timeout errors The extension may have disconnected. Check the Firefox browser console (Ctrl+Shift+J) for WebSocket connection errors. Reload the extension from about:debugging.

No network data appears Make sure the extension is loaded and the badge shows "ON". Check that the Network Requests toggle is enabled in the extension popup. Network capture begins automatically when the extension connects -- navigate to a page or refresh to generate traffic.

DOM queries fail on certain pages Content script injection is blocked on privileged pages (about:, moz-extension:, and other restricted URLs). This is a Firefox security restriction.

Console logs are empty on first call Console log capture requires injecting a page-world hook, which happens when the console capability is toggled on, when the monitored tab is selected/navigates, or on the first get_console_logs call. Logs generated before the hook is installed are not captured. It is also injected as an inline <script>, so a strict Content-Security-Policy (script-src without 'unsafe-inline') blocks it, same as the XHR/fetch hook.

POST response bodies are missing on pages with strict CSP The XHR/fetch hook fallback injects an inline <script> tag into the page. Pages with a strict Content-Security-Policy that blocks inline scripts (script-src without 'unsafe-inline') will prevent the hook from running. In that case, POST response bodies may be missing if Firefox's filterResponseData also fails (common with servers that send connection: close + gzip). GET responses are unaffected as they use a separate cache-based fallback.

Response bodies show garbled text This can happen if the response uses brotli (br) content-encoding and Firefox's filterResponseData delivers raw compressed bytes instead of decompressed data. This is rare in practice. The XHR/fetch hook handles JSON, XML (responseType: "document"), and plain text responses correctly; binary formats (arraybuffer, blob) are skipped.

Security Notes

  • The WebSocket server binds to 127.0.0.1 only -- not accessible from the network. It also rejects connections whose Origin header is not moz-extension://..., so a malicious web page cannot open the port, impersonate the extension, and feed fabricated data to Claude. Note there is no shared-secret handshake, and captured XHR/fetch bodies relayed from the page world should be treated as page-controlled data.
  • No data is persisted to disk. All captured data lives in memory and is lost when the server stops.
  • The extension requires broad permissions (<all_urls>, webRequest, webRequestBlocking, tabs, storage) to capture network traffic across all sites. This is inherent to the functionality. However, all webRequest listeners and the XHR/fetch content script are only active when the corresponding capability is enabled — when disabled, the extension has near-zero overhead.
  • Use the extension popup toggles to limit data exposure -- disable capabilities you don't need.
  • The extension is loaded as a temporary add-on and must be re-loaded after each Firefox restart.

License

MIT

See related servers & alternatives →

Related MCP servers

Browse all →

Related guides

Hand-picked reading to help you choose and use Browser & Scraping servers.