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

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.
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.

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