Skip to main content
A browser extension is hard to debug because the interesting state lives in places you can’t easily reach: an MV3 service worker that goes idle, an isolated content-script world, a popup that closes the moment it loses focus. Extension.js opens a small local control channel into your running dev session so you (or an AI agent, or a CI job) can reach those contexts directly — read what they logged, inspect what they rendered, call into them, and fire the events a user would. It’s local, it needs no account, and observation is always free. Anything that changes state is opt-in per session.

Turn it on

Start a dev session with the control flags you need:
Every operation below targets that session and addresses a context the same way, whether you’re reading or acting.

What you can do

Address a context

Reading and acting share one vocabulary — you name the surface, Extension.js resolves it against the session it’s already tracking.

Example: did my background handler actually run?

Any console.log your expression triggers flows out through extension logs at the same time, correlated by sequence — so you see the return value and the side effects.

Cross-browser support

Extension.js debugs through an in-browser companion, not the Chrome DevTools Protocol, so the core loop reaches your own surfaces on both Chrome and Firefox — the old “Firefox uses RDP, not supported” wall is gone for these tools. (extension_list_extensions is an MCP tool rather than a CLI verb — it connects over the DevTools Protocol, so it’s Chromium-only.)

Safety

The gates are intentional, not bureaucratic:
  • Observation needs nothing. Reading logs and DOM is always available.
  • Bounded operations need --allow-control. storage, reload, and open change state, so you opt in per session.
  • eval needs --allow-eval and a per-session token. The token is written to a 0600 file outside dist/ so it never ships in a build — a random local process can’t quietly drive your service worker.
  • Nothing reaches production. The control channel exists only during dev/preview; it’s gated on a port that isn’t present in a built bundle.

With an AI agent

The same operations are exposed as MCP tools through @extension.dev/mcp (extension_logs, extension_eval, extension_storage, extension_reload, extension_open, extension_list_extensions). The gates are identical — an assistant observes freely but only acts when you’ve enabled it for the session.

Next steps