Run beside Chrome DevTools MCP
chrome-devtools-mcp gives an agent raw control of a live Chrome instance, and recent versions added extension tools (install, list, reload, trigger and uninstall, plus service worker logs). @extension.dev/mcp covers the same extension capabilities build-aware and across browsers, because it wraps an Extension.js dev session instead of a bare browser.
They are complementary, not competing. Run both: let chrome-devtools-mcp drive Chrome itself, and let the extension.dev server own everything that depends on the project: the build, hot reload, the generated manifest, storage, logs across every context, and the same flow on Firefox, Edge and Safari.
When to use which
| Task | Use |
|---|---|
| Build, dev server, hot reload, production packaging | extension.dev (extension_dev, extension_build) |
| Read the generated, cross-browser manifest | extension.dev (extension_manifest_validate) |
| Replay the toolbar action handler, scriptably, no popup | extension.dev (extension_open with surface: "action") |
| Replay a keyboard shortcut command handler | extension.dev (extension_open with surface: "command") |
| List extensions with a live context in the dev browser | extension.dev (extension_list_extensions) |
| Logs across service worker, content scripts, popup, options | extension.dev (extension_logs) |
Read or write chrome.storage, eval in any context | extension.dev (extension_storage, extension_eval) |
| Same flow on Firefox, Edge or Safari | extension.dev (cross-browser by design) |
| Drive an arbitrary web page, click, scroll, fill forms | chrome-devtools-mcp |
| Performance traces, Lighthouse audits, network inspection | chrome-devtools-mcp |
| Inspect an extension you did not build (no project) | chrome-devtools-mcp |
Trigger the action with a real gesture (grants activeTab) | chrome-devtools-mcp (trigger_extension_action) |
Rule of thumb: if the task touches the project (source, build, manifest, reload), reach for the extension.dev server. If it touches the browser generically (pages, network, performance), reach for chrome-devtools-mcp.
Install both
The extension.dev tools are namespaced extension_*, so they never collide with chrome-devtools-mcp tools, and an agent fluent in one transfers to the other.
# extension.dev, build-aware extension control
claude mcp add extension-dev npx @extension.dev/mcp
# chrome-devtools-mcp, raw Chrome debugging (extension tools opt-in)
# `--` hands the flag to the server; without it `claude` parses it as its own
claude mcp add chrome-devtools -- npx chrome-devtools-mcp@latest --categoryExtensions{
"mcpServers": {
"extension-dev": {
"command": "npx",
"args": ["@extension.dev/mcp"]
},
"chrome-devtools": {
"command": "npx",
"args": ["chrome-devtools-mcp@latest", "--categoryExtensions"]
}
}
}{
"mcpServers": {
"extension-dev": {
"command": "npx",
"args": ["@extension.dev/mcp"]
},
"chrome-devtools": {
"command": "npx",
"args": ["chrome-devtools-mcp@latest", "--categoryExtensions"]
}
}
}Pipe transport
The chrome-devtools-mcp extension tools (--categoryExtensions) need a pipe connection. Attaching to an already running browser over a WebSocket endpoint (browserUrl or wsEndpoint) is not supported for that category before Chrome 149. The extension.dev server connects to the browser that the dev session launched, so its extension tools work today.
Capability map
Every chrome-devtools-mcp extension verb has an extension.dev counterpart, plus a build platform around it.
| chrome-devtools-mcp | extension.dev | Notes |
|---|---|---|
install_extension | extension_dev, extension_start | Loading is tied to a real build and hot reload, not a static folder |
list_extensions | extension_list_extensions | Lists extensions with a live context, read-only through the Extensions domain, Chromium only |
reload_extension | extension_reload | Rebuild-aware, reloads the background or a tab |
trigger_extension_action | extension_open (surface: "action") | Portable replay of the onClicked handler, cross-browser, no gesture. For a genuine click that grants activeTab, use the left |
| none | extension_open (surface: "command") | Replays a chrome.commands.onCommand keyboard shortcut handler, no chrome-devtools-mcp equivalent |
uninstall_extension | managed by the dev session lifecycle | The dev session owns load and unload through dev and preview |
| service worker logs | extension_logs | One ordered timeline across every context, with follow |
Action and command triggers, and their one caveat
chrome-devtools-mcp triggers an action by clicking the real toolbar button, which needs a visible window and a genuine user gesture. Extension.js takes a different approach: it captures the extension's chrome.action.onClicked and chrome.commands.onCommand listeners at build time and replays them on demand. That makes triggering scriptable and reproducible, the natural mode for agentic testing, and because it only touches addListener it works on both Chromium and Firefox. A single background.service_worker source works on Firefox too: the framework translates it to a background.scripts event page for the Firefox build.
No gesture on replay
Replay invokes the handler without a user gesture. A real toolbar click grants temporary activeTab permission and satisfies gesture-gated APIs (chrome.permissions.request, interactive identity.getAuthToken); a replay does not. A handler that relies on activeTab (for example chrome.scripting.executeScript on the active tab, or captureVisibleTab) behaves differently than it does on a real click. The result includes gesture: false, and a warning when the manifest declares activeTab. When you need click fidelity, use chrome-devtools-mcp's trigger_extension_action.
When you need a genuine gesture
extension_open(surface: "action"): the everyday path. Replays the handler, cross-browser, headless, no gesture (activeTabnot granted). Use it for most handler testing.- chrome-devtools-mcp
trigger_extension_action: a real action invocation through Puppeteer over the pipe transport, soactiveTabis granted exactly like a user click. Chromium only. Reach for it only when the handler depends on the gesture.
Safe by construction
The replay wraps are injected only into the dev build. The agent bridge is gated on a control port that exists solely during extension dev and extension preview, and nothing is added to a production build. The addListener wraps delegate transparently to the originals, so the extension behaves identically with or without the bridge. The framework side of this is documented at extension.js.org/docs/debugging/trigger-actions-and-commands.
Next
- MCP server is the tool reference
- Agent skill teaches the agent which server to reach for
- Inspect and act covers the observe and act gates
- Agent bridge is the local channel both the CLI and the server use