Skip to main content
chrome-devtools-mcp gives an AI agent raw control of a live Chrome instance, and recent versions added extension tools (install, list, reload, trigger, and uninstall, plus service-worker logs). The Extension.js MCP server (@extension.dev/mcp) covers the same extension capabilities, but build-aware and across browsers. They are complementary, not competing. Run both: let chrome-devtools-mcp drive Chrome itself, and let Extension.js own everything that depends on your project — the build, hot reload, the generated manifest, storage, logs across every context, and the same workflow on Firefox, Edge, and Safari.

When to use which

TaskUse
Build, dev server, hot reload, production packagingExtension.js (extension_dev, extension_build)
Read the generated, cross-browser manifestExtension.js (extension_manifest_validate)
Replay the toolbar-action handler, scriptably / no popupExtension.js (extension_open with surface: "action")
Replay a keyboard-shortcut command handlerExtension.js (extension_open with surface: "command")
List extensions with a live context in the dev browserExtension.js (extension_list_extensions)
Logs across SW, content scripts, popup, options, sidebarExtension.js (extension_logs)
Read/write chrome.storage, eval in any contextExtension.js (extension_storage, extension_eval)
Same flow on Firefox, Edge, or SafariExtension.js (cross-browser by design)
Drive an arbitrary web page, click/scroll, fill formschrome-devtools-mcp
Performance traces, Lighthouse audits, network inspectionchrome-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)
A useful rule of thumb: if the task touches your project (source, build, manifest, reload), reach for Extension.js. If it touches the browser generically (pages, network, performance), reach for chrome-devtools-mcp.

Install both servers

Extension.js tools are namespaced under extension_*, so they never collide with chrome-devtools-mcp tools. An agent fluent in one transfers to the other.
# Extension.js — build-aware extension control
claude mcp add extension-dev npx @extension.dev/mcp

# chrome-devtools-mcp — raw Chrome debugging (extension tools opt-in)
claude mcp add chrome-devtools npx chrome-devtools-mcp@latest --categoryExtensions
The chrome-devtools-mcp extension tools (--categoryExtensions) currently require a pipe connection. Attaching to an already-running browser over a WebSocket endpoint (browserUrl / wsEndpoint) is not supported for the extension category until Chrome 149. Extension.js connects to the browser it launched for your dev session, so its extension tools work today.

Capability map

Every chrome-devtools-mcp extension verb has an Extension.js counterpart, plus a build platform around it.
chrome-devtools-mcpExtension.jsNotes
install_extensionextension_dev, extension_previewLoading is tied to a real build + hot reload, not a static folder
list_extensionsextension_list_extensionsLists extensions with a live context; read-only via the Extensions domain
reload_extensionextension_reloadRebuild-aware; reloads background or a tab
trigger_extension_actionextension_open (surface: "action")Portable replay of the onClicked handler — cross-browser, no gesture (see caveat). For a genuine-gesture click (activeTab), use chrome-devtools-mcp’s trigger_extension_action
extension_open (surface: "command")Replays a chrome.commands.onCommand keyboard-shortcut handler — no chrome-devtools-mcp equivalent
uninstall_extensionmanaged by the dev session lifecycleExtension.js owns load/unload through dev / preview
service-worker logsextension_logsOne ordered timeline across every context, with follow

How the action / command triggers work — 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 (verified on both). A single background.service_worker source works on Firefox too — Extension.js translates it to a background.scripts event page for the Firefox target, since Firefox doesn’t run service-worker backgrounds.
Replay invokes your 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. So a handler that relies on activeTab (for example, chrome.scripting.executeScript on the active tab or captureVisibleTab) will behave differently than a real click. The result includes gesture: false, and a warning when your manifest declares activeTab. When you need true click fidelity, use chrome-devtools-mcp’s trigger_extension_action (below).

Need a genuine-gesture click? Use chrome-devtools-mcp

The two are complementary, not competing:
  • extension_open (surface: "action") — the everyday path. Replays the handler, cross-browser, headless, no gesture (activeTab not granted). Use it for the vast majority of handler testing.
  • chrome-devtools-mcp trigger_extension_action — a real action invocation through Puppeteer (over the supported pipe transport), so activeTab is granted exactly like a user click. Chromium only. Reach for it only when your handler genuinely depends on the gesture.
Since this page already recommends running both servers, let chrome-devtools-mcp own genuine-gesture browser driving and let Extension.js own the build-aware, cross-browser replay — no need to duplicate it. See Reload and HMR.

Safe by construction

The replay wraps are injected only into your dev build — the agent bridge is gated on a control port that exists solely during extension dev/preview, and nothing is added to a production build. The addListener wraps delegate transparently to the originals, so your extension behaves identically with or without the bridge.

Next steps