| name | firefox-devtools-mcp-automation |
| description | Automate and control Firefox browser through MCP using WebDriver BiDi for AI-assisted web testing, scraping, and interaction |
| triggers | ["automate Firefox with MCP","control Firefox browser from Claude","take screenshots and interact with web pages","use Firefox DevTools MCP server","inspect web pages with AI assistance","automate web testing in Firefox","control browser through Model Context Protocol","use WebDriver BiDi with MCP"] |
Firefox DevTools MCP Automation
Skill by ara.so — Devtools Skills collection.
What It Does
Firefox DevTools MCP is a Model Context Protocol server that enables AI assistants to inspect and control Firefox browser through the Remote Debugging Protocol (WebDriver BiDi). It provides tools for:
- Page navigation and management (open, close, switch tabs)
- Element interaction (click, fill forms, hover, drag)
- Visual inspection (screenshots, snapshots with UIDs)
- Network monitoring (capture requests/responses)
- Console log access
- JavaScript execution
- Firefox management and preferences
- WebExtension installation
Security Considerations
Always use a dedicated Firefox profile — the agent has access to cookies, sessions, and everything the browser can reach.
- Avoid visiting untrusted sites (risk of prompt injection)
- Only enable
--enable-script and --enable-privileged-context when necessary
- Never run against your regular browsing profile
Installation
Quick Start with npx (Recommended)
claude mcp add firefox-devtools npx firefox-devtools-mcp@latest
claude mcp add firefox-devtools npx firefox-devtools-mcp@latest -- --headless --viewport 1280x720
Manual Configuration
Add to MCP settings JSON (~/Library/Application Support/Claude/Code/mcp_settings.json on macOS):
{
"mcpServers": {
"firefox-devtools": {
"command": "npx",
"args": ["-y", "firefox-devtools-mcp@latest", "--headless"],
"env": {
"START_URL": "about:home",
"FIREFOX_HEADLESS": "true"
}
}
}
}
Test with MCP Inspector
npx @modelcontextprotocol/inspector npx firefox-devtools-mcp@latest --start-url https://example.com --headless
Configuration Options
CLI Flags
--firefox-path /path/to/firefox
--headless
--viewport 1280x720
--profile-path /path/to/profile
--start-url https://example.com
--accept-insecure-certs
--enable-script
--enable-privileged-context
--connect-existing
--marionette-port 2828
--firefox-arg --arg-name
--pref name=value
Environment Variables
FIREFOX_HEADLESS=true
START_URL=https://example.com
ACCEPT_INSECURE_CERTS=true
CONNECT_EXISTING=true
MARIONETTE_PORT=2828
ENABLE_SCRIPT=true
ENABLE_PRIVILEGED_CONTEXT=true
Core Workflow
1. Page Navigation and Management
list_pages()
new_page({ url: "https://example.com" })
navigate_page({
pageId: "page-id-123",
url: "https://example.com/login"
})
select_page({ pageId: "page-id-123" })
close_page({ pageId: "page-id-123" })
2. Snapshot and UID-Based Interaction
The snapshot system assigns unique UIDs to interactive elements, enabling precise interaction without CSS selectors.
take_snapshot()
click_by_uid({ uid: "abc123" })
fill_by_uid({
uid: "def456",
value: "user@example.com"
})
hover_by_uid({ uid: "ghi789" })
drag_by_uid({
sourceUid: "drag-me",
targetUid: "drop-here"
})
upload_by_uid({
uid: "file-input",
filePath: "/path/to/file.pdf"
})
fill_form_by_uids({
fields: [
{ uid: "email-field", value: "user@example.com" },
{ uid: "password-field", value: "secure-password" },
{ uid: "submit-button", action: "click" }
]
})
clear_uids()
3. Visual Capture
screenshot_page()
screenshot_page({
saveTo: "/tmp/page.png"
})
screenshot_by_uid({
uid: "abc123",
saveTo: "/tmp/element.png"
})
4. Network Monitoring
Network capture is always-on. Use ID-first approach to inspect requests:
list_network_requests()
list_network_requests({
filter: {
method: "POST",
status: 200,
urlPattern: "api.example.com"
}
})
get_network_request({ requestId: "req-123" })
5. Console Messages
list_console_messages()
clear_console_messages()
6. JavaScript Execution
Requires --enable-script flag
evaluate_script({
script: "document.title"
})
evaluate_script({
script: `
const button = document.querySelector('.submit-btn');
return button ? button.textContent : null;
`
})
evaluate_script({
script: `
document.body.style.backgroundColor = 'lightblue';
return 'Background changed';
`
})
7. Browser Utilities
history_back()
history_forward()
set_viewport({ width: 1920, height: 1080 })
accept_dialog({ promptText: "optional text for prompts" })
dismiss_dialog()
get_firefox_info()
get_firefox_output()
restart_firefox()
Common Patterns
Login Flow
navigate_page({
pageId: "page-1",
url: "https://app.example.com/login"
})
const snap = take_snapshot()
fill_form_by_uids({
fields: [
{ uid: "email-uid", value: process.env.USER_EMAIL },
{ uid: "password-uid", value: process.env.USER_PASSWORD },
{ uid: "submit-uid", action: "click" }
]
})
Web Scraping with Network Monitoring
navigate_page({
pageId: "page-1",
url: "https://api-site.example.com/dashboard"
})
const snap = take_snapshot()
click_by_uid({ uid: "load-more-button-uid" })
const requests = list_network_requests({
filter: {
urlPattern: "api.example.com/v1/data",
method: "GET"
}
})
const apiData = get_network_request({
requestId: requests.requests[0].id
})
Visual Regression Testing
navigate_page({
pageId: "page-1",
url: "https://example.com/component"
})
screenshot_page({ saveTo: "/tmp/baseline.png" })
evaluate_script({
script: `document.querySelector('.hero').style.fontSize = '24px'`
})
screenshot_page({ saveTo: "/tmp/comparison.png" })
Form Automation
const pages = list_pages()
const pageId = pages.pages[0].id
let snap = take_snapshot()
fill_form_by_uids({
fields: [
{ uid: "first-name-uid", value: "John" },
{ uid: "last-name-uid", value: "Doe" },
{ uid: "next-btn-uid", action: "click" }
]
})
snap = take_snapshot()
fill_form_by_uids({
fields: [
{ uid: "address-uid", value: "123 Main St" },
{ uid: "city-uid", value: "Anytown" },
{ uid: "submit-uid", action: "click" }
]
})
Advanced Features
Privileged Context (Firefox Internals)
Requires --enable-privileged-context flag and MOZ_REMOTE_ALLOW_SYSTEM_ACCESS=1 environment variable
list_privileged_contexts()
select_privileged_context({ contextId: "chrome-123" })
evaluate_privileged_script({
script: `
Services.prefs.setIntPref("browser.cache.disk.capacity", 1024000);
return "Pref set";
`
})
get_firefox_prefs({ prefNames: ["browser.cache.disk.capacity"] })
set_firefox_prefs({
prefs: {
"browser.cache.disk.capacity": 2048000,
"privacy.trackingprotection.enabled": true
}
})
WebExtension Management
install_extension({
xpiPath: "/path/to/extension.xpi"
})
uninstall_extension({ extensionId: "extension-uuid" })
list_extensions()
Connect to Existing Firefox
Automate your real browsing session with cookies and logins intact:
firefox --marionette
npx firefox-devtools-mcp@latest --connect-existing --marionette-port 2828
Note: BiDi-dependent features (console/network events) are not available in connect-existing mode.
Troubleshooting
Firefox Not Found
npx firefox-devtools-mcp@latest --firefox-path "/Applications/Firefox.app/Contents/MacOS/firefox"
npx firefox-devtools-mcp@latest --firefox-path "/usr/bin/firefox"
npx firefox-devtools-mcp@latest --firefox-path "C:\Program Files\Mozilla Firefox\firefox.exe"
Stale UIDs After Navigation
Always take a fresh snapshot after page changes:
navigate_page({ pageId: "page-1", url: "https://new-page.com" })
clear_uids()
const snap = take_snapshot()
Windows 10 Connection Issues
Wrap command with cmd /c:
{
"mcpServers": {
"firefox-devtools": {
"command": "cmd",
"args": ["/c", "npx", "-y", "firefox-devtools-mcp@latest"]
}
}
}
Or use absolute path to npx:
{
"mcpServers": {
"firefox-devtools": {
"command": "C:\\Program Files\\nodejs\\npx.cmd",
"args": ["-y", "firefox-devtools-mcp@latest"]
}
}
}
Debugging Connection Issues
npx @modelcontextprotocol/inspector npx firefox-devtools-mcp@latest --start-url about:home
ps aux | grep firefox
lsof -i :2828
Example: Complete E2E Test
const { pageId } = new_page({ url: "https://example.com" })
navigate_page({ pageId, url: "https://example.com/search" })
const snap = take_snapshot()
fill_form_by_uids({
fields: [
{ uid: "search-input-uid", value: "test query" },
{ uid: "search-button-uid", action: "click" }
]
})
const screenshot = screenshot_page({ saveTo: "/tmp/results.png" })
const requests = list_network_requests({
filter: { urlPattern: "example.com/api/search" }
})
const searchData = get_network_request({
requestId: requests.requests[0].id
})
const logs = list_console_messages()
const errors = logs.messages.filter( => m. === )
({ pageId })
Resources