io.github.extensiondev/mcp
NPM · @EXTENSION.DEV/MCP
Build, run, inspect, and publish browser extensions from any MCP client. 37 tools, 13 browsers.
Available components
Last scanned (6 hours ago)
Scores higher than 48% of npm servers
Category Developer Tools
How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. How we score → Why this is hard to score →
Supply Chain Security90
- No malware found by supply-chain analysis.Pass
- Known CVEs were checked across the 400 of 545 dependencies we could resolve, so this covers what we could see, not the whole tree.Partial
- No install/post-install scripts declared.Pass
- Dependency health was assessed across the 400 of 545 dependencies we could resolve, so this covers what we could see, not the whole tree. View diagnostics → Partial
Provenance & Transparency45
- Source repository is publicly reachable at the declared URL. View diagnostics → Pass
- Provenance check failed: no build-provenance attestation is published. See how to fix → View diagnostics → Fail
- Clear OSI-approved license (Apache-2.0).Pass
- Actively maintained (last published 0 days ago).Pass
- Disclosure check failed: no security disclosure policy was found in the source repository. See how to fix → Fail
Schema Quality & AI Usability54
- AI-judged instruction clarity (excellent).Pass
- Context-footprint check failed: tool/resource definitions use about 13477 tokens (~561/item across 24 items; 24 tools + 0 resources), over budget; trim descriptions and params. See how to fix → Fail
- Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management71
- Stability check failed: the tool surface changed between 10.10.2 and 10.20.0: 9 tool removals, 0 breaking changes, 2 additions. See how to fix → Fail
Tool Coverage98
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 95% of tool parameters carry a description.Partial
Tool Safety100
- No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
- All 1 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.Pass
- An AI judge read all 25 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
- Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.
- 4 Nov 26 0
- Stability: fail → unverified ▼ security
- Tool safety: pass → unverified ▼ security
- Schema quality: 489 → 561 ▼ functional
- Security disclosure: unverified → fail ▼ functional
- Capabilities: pass → unverified ▼ functional
- Tool coverage: 100 → unverified ▼ functional
- Package version: 10.15.1 → 10.20.0 functional
- 3 Nov 26 0
- Security disclosure: fail → unverified ▼ functional
- 1 Nov 26 0
- Security disclosure: unverified → fail ▼ functional
- 30 Oct 26 0
- Security disclosure: fail → unverified ▼ functional
- 29 Oct 26 +15
- Malware scan: unverified → pass ▲ security
- 28 Oct 26 −15
- Malware scan: pass → unverified ▼ security
- Package version: 10.14.1 → 10.15.1 functional
- 19 Oct 26 +15
- Malware scan: unverified → pass ▲ security
- 18 Oct 26 −15
- Malware scan: pass → unverified ▼ security
- Package version: 10.14.0 → 10.14.1 functional
Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.
Captured 5 Nov 2026 · Analysed npm/@extension.dev/mcp@10.20.0
Provenance No attestation
The registry publishes no build provenance for this version, so there is nothing to verify.
| Result | No attestation |
|---|---|
| Ecosystem | npm |
Background: How many MCP packages publish verified provenance →
Dependencies 400 packages
| Packages resolved | 400 |
|---|---|
| Stale | 81 |
| Tree resolution | Partial |
The dependency tree was only partially resolved, so these counts may be incomplete.
Background: SBOMs and build attestations, explained →
The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →
extension_add_feature ~114
Plan the surface before adding an options page, a popup, a side panel, a content script, a devtools panel, a new tab page or a background script to an existing extension. It answers with the manifest additions, the files to create and the catalog template to copy from, and modifies nothing: write what the plan says, then validate the manifest and run extension_dev.
| Name | Type | Req | Description |
|---|---|---|---|
| feature | string | yes | Feature surface to add |
| framework | string | – | – |
| projectPath | string | yes | Extension project root |
No output schema declared.
No examples provided.
extension_analyze ~158
Analyze a BUILT extension on disk: sizes, entry points, permissions, bundle composition, store-readiness checks, and manifestByBrowser, the emitted manifest per built browser with the resolved keys (background, action, sidebar, permissions, host_permissions, chrome_url_overrides, browser_specific_settings) that differ; other dist folders are named, not read as browsers. Static: it reads dist/<browser> only. A dist extension_dev rewrote is a development build: buildType says so, store checks are skipped, extension_build makes the build to analyze. extension_inspect reads a running extension.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Browser build to analyze |
| format | string | – | – |
| projectPath | string | yes | Extension project root |
No output schema declared.
No examples provided.
extension_assert ~429
Run a test stage on an extension_dev or extension_start session: one verdict per expectation (see expect): pass, fail, not ready (a precondition; see settledBy) or inconclusive (a platform limit). page-world-clean, no-outside-requests and background-worker-booted read the debug port: meant for the production build (extension_start, allowControl:true). storage-key-present needs the control channel; content-script-injected and console-errors-empty read the dev log stream: extension_dev only.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Session browser; defaults to this project's live session. Required when the project runs sessions for more than one browser: the call is refused with the list rather than picking one. |
| expect | array | yes | One object per expectation, each { assert: <check id>, ...args }. background-worker-booted: no args. surface-rendered: surface (popup, options, sidebar, newtab, history, bookmarks), optional selector… |
| projectPath | string | yes | Extension project root (needs a running session: extension_dev or extension_start) |
| timeout | number | – | Command timeout in ms. When omitted, the default depends on the route: 30000 through the dev session's control channel, 10000 over the Firefox debugger protocol, and 15000 per command over the Chromi… |
No output schema declared.
No examples provided.
extension_browsers ~216
Find, install and remove the browsers Extension.js tooling can launch. Pass action:'detect' (the default) to scan both system-installed and managed browsers, and report each one's binary path, version, engine and debugger support. Pass action:'list' for the managed cache this tool downloads into, with each binary's version and size on disk. Both report the version the binary itself carries (its bundle metadata, else --version), prerelease suffix included (159.0a1), never the cache folder's name. Pass action:'install' to download a managed binary: several hundred MB in one blocking call, so allow a generous client timeout. Pass action:'uninstall' to remove managed binaries; it never touches a system install.
| Name | Type | Req | Description |
|---|---|---|---|
| action | string | – | – |
| all | boolean | – | uninstall: remove every managed binary. |
| browser | string | – | install/uninstall: which managed binary. Required for install. |
| browsers | array | – | detect: limit the scan to these. Omit to check all. |
No output schema declared.
No examples provided.
extension_build ~744
Build a browser extension for production. This tool always passes --browser (chrome unless you set it), so the output lands in dist/<browser>/ (dist/chrome by default) and the answer's outputPath always names the folder that was written (the engine's reported path, else dist/<browser> under the package root, the same folder this tool checked for the manifest), with zipPath beside it when a zip was asked for: read those paths instead of assuming one. To know what is in the build (sizes, entry points, the checks a store reviewer runs) call extension_analyze on the same projectPath and browser instead of listing or reading the output folder by hand; the answer's hint says so. A scaffolded project's own `npm run build` runs `extension build` with no --browser, which the engine defaults to chromium, so that script writes dist/chromium/ instead. Pass zip:true to also package a .zip for store submission. With browser:'safari' the build converts the extension into a macOS app through Xcode, and bundleId sets the identifier it ships under. The build refuses a manifest with build-blocking errors unless you pass skipValidation:true, because such a manifest yields a broken bundle the bundler itself never flags.
| Name | Type | Req | Description |
|---|---|---|---|
| appName | string | – | Safari targets only: name of the generated macOS app, which also names the Xcode scheme and the .app on disk. Defaults to the manifest name. |
| browser | string | – | – |
| bundleId | string | – | Safari targets only: a reverse-DNS bundle identifier you own, such as com.acme.readinglist. Without one the app is packaged under a generated dev.extensionjs.* identifier derived from the app name, w… |
| developmentTeam | string | – | Safari targets only: an Apple Developer team id to sign the app with, the same input extension_dev takes. Left out, the build keeps the team the project's last Safari app was signed with (so a build… |
| forceRegenerate | boolean | – | Safari targets only: regenerate the Xcode project even when the engine considers it up to date. Use it when an earlier packaging run left the project broken. |
| macOsOnly | boolean | – | Safari targets only: generate a macOS-only Xcode project. Pass false for a universal project that also targets iOS and iPadOS, which is what you want if the extension ships on iPhone or iPad. |
| mode | string | – | Bundler mode override (also sets NODE_ENV) |
| polyfill | boolean | – | Apply cross-browser polyfill |
| projectPath | string | yes | Extension project root |
| silent | boolean | – | Suppress build output |
| skipValidation | boolean | – | Build even when extension_manifest_validate reports build-blocking errors. The build normally refuses: the engine itself stops only on missing scripts, icons, DNR rule files, default_locale and manif… |
| zip | boolean | – | Create a .zip file for store distribution |
| zipFilename | string | – | Custom .zip file name (defaults to name and version) |
| zipSource | boolean | – | Include source code zip (required by some stores) |
No output schema declared.
No examples provided.
extension_create ~399
Create a browser extension project from a template in the extension.dev catalog; call extension_templates first to see what is available. The scaffolder may initialize a git repository in the new project (with a first commit), and it also writes store metadata and a .gitignore of its own; the result's defaultsApplied block reads those decisions back. The result's `scripts` lists every package.json script that runs the engine, with the browser it targets and the folder it writes: the scaffolder passes no --browser to `dev`, `start` and `build`, so `npm run build` writes dist/chromium while `build:chrome` writes dist/chrome; read the folder from that list instead of assuming one. A scaffold that answers `created` is verified (its manifest was found and read): it needs no build to prove it, so do not run `npm run build` to check it; the next step is extension_dev with the answered projectPath. Not for a project that exists: extension_dev runs it, extension_project_create puts it on extension.dev. After a successful scaffold this tool sends one telemetry event, draft_seeded (template slug, source and commit, a random install id, never a path or a name), to PostHog; the Telemetry section of this package's readme names the two environment variables that turn it off.
| Name | Type | Req | Description |
|---|---|---|---|
| install | boolean | – | Install dependencies after creation |
| parentDir | string | – | Directory to create the project inside. Defaults to the MCP server process cwd, NOT the caller's cwd, so pass it whenever you care where the project lands. Aliases: parent, into. |
| projectName | string | yes | Name of the extension project (used as directory name). Alias: name. |
| template | string | – | Template slug from the extension.dev catalog (e.g. 'react', 'ai-claude', 'content-vue'). extension_templates discovers them. |
No output schema declared.
No examples provided.
extension_dev ~1,176
Run the extension while you edit it: dev build, hot module replacement, and a browser with the extension loaded. Reach for this first when the ask is "run my extension": by default it waits for the session to be ready and answers with the readiness and the first log lines, so one call covers the ask; pass wait:false to return as soon as the server is spawned. A dev session carries the control channel that extension_storage, extension_reload, extension_open and extension_dom_snapshot need (local, same machine, same user) unless you pass allowControl:false; the eval channel that extension_eval needs is the one gate that stays off until you pass allowEval:true (which implies control, so you never need to pass both). Use extension_start instead to run the production build in a browser. When the answer's ready.status is "ready" the session is up and nothing else needs calling before extension_logs, extension_inspect or the control verbs; extension_wait is only for wait:false, an answer whose ready.status was not ready, or a build-only session.
| Name | Type | Req | Description |
|---|---|---|---|
| allowControl | boolean | – | The agent-bridge control channel that extension_storage/reload/open/dom_snapshot ride (local, same machine). On unless you pass false; eval is the gate that stays off (allowEval). |
| allowEval | boolean | – | Enable extension_eval (runs code in a context; writes a 0600 session token). Implies allowControl, so you never need to pass both. |
| appName | string | – | Safari targets only: name of the generated macOS app, which also names the Xcode scheme and the .app on disk. Defaults to the manifest name. |
| browser | string | – | – |
| bundleId | string | – | Safari targets only: a reverse-DNS bundle identifier you own, such as com.acme.readinglist. Without one the app is packaged under a generated dev.extensionjs.* identifier derived from the app name, w… |
| chromiumBinary | string | – | Custom Chromium-based binary to launch; `browser` still names the target family the engine builds for (chromium-based when none is given). |
| developmentTeam | string | – | Safari targets only: an Apple Developer team id to sign the app with. Without it the app is ad-hoc signed, which Safari loads locally once unsigned extensions are allowed; an ad-hoc rebuild keeps the… |
| enableExtension | boolean | – | Safari targets only: let Extension.js turn the extension on in Safari Settings and allow it on other websites after the app opens, instead of printing the one-time setup steps for the person to click… |
| extensions | array | – | Extra extension paths or store URLs to load alongside the project |
| forceRegenerate | boolean | – | Safari targets only: regenerate the Xcode project even when the engine considers it up to date. |
| geckoBinary | string | – | Custom Gecko/Firefox binary to launch; `browser` still names the target family the engine builds for (gecko-based when none is given). |
| headless | boolean | – | false opens a browser window the person can see, overriding EXTENSION_HEADLESS and any --headless in EXTENSION_BROWSER_FLAGS for this session only; pass it when the person wants to watch or try the e… |
| host | string | – | Bind host, default 127.0.0.1. Use 0.0.0.0 in Docker or devcontainers. |
| macOsOnly | boolean | – | Safari targets only: generate a macOS-only Xcode project. Pass false for a universal project that also targets iOS and iPadOS. |
| noBrowser | boolean | – | Start the dev server without launching a browser |
| polyfill | boolean | – | Apply cross-browser polyfill |
| port | number | – | Dev server port (0 for auto-assign) |
| profile | string | – | Profile path, or "false" to reuse the real user profile. Omit for a throwaway one. |
| projectPath | string | yes | Extension project root |
| publicHost | string | – | Host the browser dials for HMR and reload when it differs from the bind host |
| replace | boolean | – | Stop the live session this projectPath runs for the same browser first, reported as replacedSession. Without it a second call for that browser is refused rather than forking: one session per browser… |
| startingUrl | string | – | URL the browser opens on launch |
| wait | boolean | – | Wait for the session to be ready before answering (the same contract extension_wait reads, up to 45s) and include the first log lines. false returns as soon as the server is spawned; call extension_w… |
No output schema declared.
No examples provided.
extension_docs_search ~141
Find the docs page to cite before answering any question about a WebExtension API, a manifest key, a permission, or a difference between Chrome, Firefox, Edge and Safari. It searches the Extension.js and extension.dev docs (free, no login) and answers with the pages, each with its URL and an excerpt. Memory is the fallback only when it returns nothing.
| Name | Type | Req | Description |
|---|---|---|---|
| api | string | – | Platform base URL (default EXTENSION_DEV_API_URL, else https://www.extension.dev) |
| limit | number | – | How many pages to return, 1 to 8. |
| query | string | yes | What to look up, in a few words. |
No output schema declared.
No examples provided.
extension_doctor ~222
Diagnose a dev session end to end: ready contract, dev-server process, control-port agreement, control channel, eval token, executor, browser liveness. This returns one {check, status, detail, remediation?} per leg, in dependency order. Read a 'skip' as blocked, not as a pass: it names the check that blocked it. A session started with allowControl: false comes back ok:true with status 'read-only', not as an error. On Chromium, value.capabilities reads each extension context's WebGPU adapter, crossOriginIsolated and headless. Run this first when any act tool errors unexpectedly. Call it with no projectPath for a pre-flight environment check (node, the Extension.js CLI, the template cache).
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Browser session to diagnose. Defaults to the active dev session's browser for this project; required when the project runs sessions for more than one browser. |
| projectPath | string | – | Path to the extension project root. Omit for a pre-flight environment check with no project. |
No output schema declared.
No examples provided.
extension_dom_snapshot ~800
Take a shallow structured DOM snapshot of one chosen surface: element counts, extension roots, open shadow roots, optional byte-capped HTML, and optional recent console lines. This is the SURFACE PICKER: the only tool that reads an open extension surface by name (`context`: popup, options, sidebar, devtools) or an override page, the only one that takes a numeric chrome.tabs id, and the only one that enumerates what is open (listTargets for targetIds, listTabs for numeric tab ids). Pass `targetId` (the target.targetId extension_open answers, or one from listTargets) to read that exact page over the debug port. Not for closed shadow roots, selector probes, navigation or a deep read of an open web page: that is extension_inspect. Not for the pixels: that is extension_screenshot. The snapshot rides the agent bridge (localhost only, on unless extension_dev was called with allowControl:false) and needs no CDP; listTargets and `tabUrl` resolution ask the browser directly and need the session's debug port.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Session browser; defaults to this project's live session. Required when the project runs sessions for more than one browser: the call is refused with the list rather than picking one. |
| context | string | – | content/page targets `url`, else the active tab; the rest (popup, options, sidebar, devtools, newtab, history, bookmarks) must already be OPEN, by extension_open. On Firefox, a surface or extension p… |
| include | array | – | What to include; html is byte-capped |
| listTabs | boolean | – | Enumerate open tabs as {tabId,url,title} and return, ignoring the other args. Use when you need a numeric tab id. |
| listTargets | boolean | – | Enumerate live page targets and return, ignoring the other args. The discovery path for `targetId` and `tabUrl`. Chromium: {targetId,url,title,type}. Firefox: RDP tab descriptors {targetId,actor,url,… |
| maxBytes | number | – | – |
| projectPath | string | yes | Extension project root (needs a running session: extension_dev or extension_start) |
| tab | number | – | Numeric chrome.tabs id, only to disambiguate when several tabs match. With neither `tab` nor `url`, content/page target the active tab. |
| tabUrl | string | – | Pick the tab by url through the browser's own target list (needs the session's debug port: CDP page targets on Chromium, RDP tab descriptors on Firefox). One that matches several tabs showing the sam… |
| targetId | string | – | Pick one open page directly by its targetId: the target.targetId extension_open answers, or a targetId from extension_dom_snapshot listTargets: true (a CDP target id on Chromium, the tab's browserId… |
| timeout | number | – | Command timeout in ms. When omitted, the default depends on the route: 30000 through the dev session's control channel, 10000 over the Firefox debugger protocol, and 15000 per command over the Chromi… |
| url | string | – | content/page: pick the tab by url (match pattern, then substring). Preferred over `tab`. |
| withConsole | number|boolean | – | Also include recent console lines. A number is how many; true means 50. |
No output schema declared.
No examples provided.
extension_eval ~1,235
Evaluate an expression in a running extension context: the background, an extension surface (popup, options, sidebar, devtools, newtab, history, bookmarks), a content script or a page, picked by context, url, tab or targetId. Start the session with allowEval:true (extension_dev), which writes a 0600 session token; without that token every route of this tool, the debug port included, answers eval-disabled. A returned promise is awaited; a value that cannot be cloned (a DOM node, a function) comes back as its string form. To wait for a condition that is still settling (a model loading, an element appearing), pass until: {} (or {timeoutMs, intervalMs}) and the eval repeats until its value is truthy, answering that value, or the last one on timeout. Debug-port evaluates run with a user gesture, so gesture-gated APIs (permissions.request, sidePanel.open) can succeed here and still fail when the extension's own code calls them. Naming a tab the browser refuses to script (page-not-scriptable, extension-page-unreachable) needs Extension.js 4.1.34 or later; on an older engine such a refusal still answers as a missing target. On Safari this tool reaches only page contexts, through the automation window's WebDriver session: the background and the extension surfaces are out of reach (extension_logs reads the background). Not for reading a page's structure: that is extension_dom_snapshot, which also lists tabs and targets. Pass `targetId` (the target.targetId extension_open answers, or one from extension_dom_snapshot listTargets:true) to evaluate in that exact page over the debug port; context, url and tab are then ignored.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Session browser; defaults to this project's live session. Required when the project runs sessions for more than one browser: the call is refused with the list rather than picking one. |
| context | string | – | Where to evaluate. Default background, except on a Chromium MV3 session (the default template) where it defaults to page, the active tab; background on Chromium goes over the debug port (the service… |
| expression | string | yes | JavaScript expression to evaluate in the target context |
| projectPath | string | yes | Extension project root (needs a running session: extension_dev or extension_start) |
| tab | number | – | Numeric chrome.tabs id, only to disambiguate when several tabs match. |
| targetId | string | – | Pick one open page directly by its targetId: the target.targetId extension_open answers, or a targetId from extension_dom_snapshot listTargets: true (a CDP target id on Chromium, the tab's browserId… |
| timeout | number | – | Command timeout in ms. When omitted, the default depends on the route: 30000 through the dev session's control channel, 10000 over the Firefox debugger protocol, and 15000 per command over the Chromi… |
| until | object | – | Repeat the eval until it returns a truthy value, for a condition that is still settling (a model loading, an element appearing), instead of answering the first reading. Pass {} for the defaults. Answ… |
| url | string | – | content/page: pick the tab by url (match pattern, then substring). Preferred over `tab`. |
No output schema declared.
No examples provided.
extension_inspect ~599
Inspect a running extension deeply over the browser's debugger protocol: full HTML (open shadow roots of the extension-root hosts inlined; other shadow roots are not crossed by html, dom_snapshot or probe), DOM structure, content-script injection, console messages, and CSS selector queries through `probe`. This is the ONLY tool that pierces closed shadow roots (deepDom), runs selector probes, and navigates a tab to `url` before reading it. It reads a web or override page: the first inspectable target, the first whose url contains `url`, or the exact page `targetId` names (the target.targetId extension_open answers, or one from extension_dom_snapshot listTargets) without navigating. Not for an extension surface by name (popup, options, sidebar, devtools) or a chrome.tabs id, and not for enumerating what is open: that is extension_dom_snapshot. Not for a built extension's files on disk: that is extension_analyze. Chromium rides the Chrome DevTools Protocol and needs the session's debug port, not allowControl; on Firefox the reads ride the agent bridge and need allowEval:true (console rides the RDP watcher replay on engine 4.0.15 and later). This requires an active dev or start session.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Session browser; defaults to this project's live session. Required when the project runs sessions for more than one browser: the call is refused with the list rather than picking one. |
| deepDom | boolean | – | Pierce CLOSED shadow roots (open roots of the extension-root hosts are already inlined in html; other open roots are not read). Chromium: CDP DOM pierce. Firefox: a content-script walk via tabs.execu… |
| include | array | – | What to include |
| maxBytes | number | – | Truncate HTML output at this byte count (0 = unlimited) |
| probe | array | – | CSS selectors to query; returns counts and samples for each |
| projectPath | string | yes | Extension project root (needs a running session: extension_dev or extension_start) |
| targetId | string | – | Pick one open page directly by its targetId: the target.targetId extension_open answers, or a targetId from extension_dom_snapshot listTargets: true (a CDP target id on Chromium, the tab's browserId… |
| url | string | – | Navigate the picked tab here before reading, or, with no navigation wanted, the substring the target's url must contain; a url that matches several tabs showing the same page reads the most recently… |
No output schema declared.
No examples provided.
extension_list_extensions ~226
List the extensions in the running dev browser: id, name, version, and, on Chromium, live contexts. This session's own extension carries ownExtension:true, with name and version from the ready contract even when the browser exposes no identity. On Chromium, entries come from the live CDP targets and from the session profile's installed set, so an extension whose MV3 service worker is dormant is still listed, with running:false and no contexts. Firefox rides the RDP root actor (listAddons, engine 4.0.15 and later), so entries are installed add-ons regardless of contexts, are marked temporarilyInstalled where relevant, and carry no contexts. Other extensions' contexts are never attached to or evaluated in. This requires an active dev or start session.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Session browser; defaults to this project's live session. Required when the project runs sessions for more than one browser: the call is refused with the list rather than picking one. |
| projectPath | string | yes | Extension project root (needs a running session: extension_dev or extension_start) |
No output schema declared.
No examples provided.
extension_logs ~369
Read or stream logs from every context of a running dev session (service worker, content scripts, popup, options, sidebar, devtools, pages) in one ordered timeline. This reads the same agent-bridge plane as the `extension logs` CLI: a one-shot returns the most recent matching lines from logs.ndjson, and follow:true connects to the live control channel, receives the broker's replay of its recent ring (up to 5,000 events, counted in value.replayed) and then the frames that arrive during followMs (value.live). This requires an active extension_dev session.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which dist/extension-js/<browser>/ to read. Defaults to this project's live session, else chrome. Required when the project runs sessions for more than one browser. |
| context | array | – | Restrict to these contexts. Omit for all. |
| follow | boolean | – | Collect from the live control channel for a bounded window instead of reading the file. |
| followMs | number | – | How long to collect live frames when follow=true (clamped 500–15000ms). |
| level | string | – | Minimum severity; a level includes everything more severe. |
| limit | number | – | How many of the most recent events to return. |
| projectPath | string | yes | Extension project root (needs a running session: extension_dev or extension_start) |
| signalsOnly | boolean | – | Only structured dx.signal diagnostics (code/status/remediation), no plain console lines. |
| since | number | – | Only events with seq greater than this; the cursor for polling forward. |
| tab | number | – | Only events from this tab id. |
| url | string | – | Only events whose url/hostname matches (glob or substring), e.g. https://shop.example/*. |
No output schema declared.
No examples provided.
extension_manifest_validate ~139
Validate a manifest.json across browsers. This reports missing fields, invalid permissions, dangling file references, and cross-browser compatibility issues. Read buildBlocking for the errors that make extension_build refuse.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Single browser to validate against; alias for browsers:[browser] to match the other tools. |
| browsers | array | – | Browsers to validate against |
| manifestPath | string | – | Path to manifest.json. Or pass projectPath and the manifest is located for you. |
| projectPath | string | – | Path to the extension project root; manifest.json is resolved from it (root or src/). Accepted in place of manifestPath. |
No output schema declared.
No examples provided.
extension_open ~1,204
Open an extension surface (popup, options, sidebar, the newtab, history or bookmarks override page, or the extension's DevTools panel), navigate a tab to a url, or replay an event (the toolbar action, a chrome.commands shortcut) in a running session, over the dev session's control channel (on unless extension_dev was called with allowControl:false). Opening a url on Chromium answers target.targetId, the CDP target id of the tab it opened: pass it as `targetId` to extension_screenshot, extension_dom_snapshot, extension_inspect or extension_eval to address that exact tab (extension_dom_snapshot listTargets: true lists every targetId, on Firefox too). A url answer also carries tab {active, visibilityState}, viewport {width, height} and headless: an element below the viewport is not observed until it is scrolled into view. A url opens in a background tab unless foreground: true. Not for evaluating in the page once it is open: that is extension_eval. An action or command replay invokes your listener without a user gesture, so the gesture-derived activeTab grant does not apply.
| Name | Type | Req | Description |
|---|---|---|---|
| asTab | boolean | – | popup/options/sidebar: render the surface's document in a real tab instead of a popup window. This is how you inspect a surface HEADLESSLY, and it is applied automatically when a headless session ref… |
| browser | string | – | Session browser; defaults to this project's live session. Required when the project runs sessions for more than one browser: the call is refused with the list rather than picking one. |
| foreground | boolean | – | With `url`, on Chromium: bring the opened tab forward for a page the PERSON must act on (a sign-in, a consent screen, a payment or captcha step), by making it the active tab (CDP Target.activateTarge… |
| name | string | – | For surface 'command': the chrome.commands name to trigger. |
| panel | string | – | For surface 'devtools': the title the extension gave chrome.devtools.panels.create, when it registers more than one panel. Omitted, the extension's first panel is shown. |
| projectPath | string | yes | Extension project root (needs a running session: extension_dev or extension_start) |
| reload | boolean | – | For surface 'devtools': reload the inspected tab once DevTools is open, for extensions that create their panel only when the page reports to them on a load that starts with DevTools open (Preact Devt… |
| surface | string | – | Which surface to open or event to replay. popup, options or sidebar open a UI surface; newtab, history or bookmarks open the matching chrome_url_overrides page in a tab (always a tab, resolved by the… |
| tab | number | – | With `url`: navigate this chrome.tabs id in place instead of opening a new tab (rides the engine's navigate verb over the control channel, which a dev session carries unless started with allowControl… |
| timeout | number | – | Command timeout in ms. When omitted, the default depends on the route: 30000 through the dev session's control channel, 10000 over the Firefox debugger protocol, and 15000 per command over the Chromi… |
| url | string | – | Navigate a real tab here instead of opening a surface, in a NEW tab unless `tab` names one (a blank or new-tab page is reused). An absolute url opens as given; a path with no scheme, such as pages/op… |
| waitMs | number | – | For surface 'devtools': how long to wait for the panel to register after DevTools opens (default 15000, up to 120000). Extensions that create their panel on a page event need longer, or `reload`. |
No output schema declared.
No examples provided.
extension_reload ~259
Reload a running extension's background context, or a tab. Rides the dev session's control channel, on unless extension_dev was called with allowControl:false. A background reload answers once the engine's ready.json shows the new background attached to the dev server again (value.reattached, value.reattachedMs), so the next read or assertion meets the new generation and not the gap between them; if it has not come back within the budget (timeout, at most 5 seconds) the answer is status reloading with the contract's own stamps.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Session browser; defaults to this project's live session. Required when the project runs sessions for more than one browser: the call is refused with the list rather than picking one. |
| context | string | – | – |
| projectPath | string | yes | Extension project root (needs a running session: extension_dev or extension_start) |
| tab | number | – | For content/page: a specific tab id |
| timeout | number | – | Command timeout in ms. When omitted, the default depends on the route: 30000 through the dev session's control channel, 10000 over the Firefox debugger protocol, and 15000 per command over the Chromi… |
No output schema declared.
No examples provided.
extension_screenshot ~1,053
Take a screenshot of one open extension surface (popup, options, sidebar, newtab, history, bookmarks) or web page in a live extension_dev or extension_start session as a .png file. It asks the browser for the page's own pixels over the debug port (CDP on Chromium, the screenshot actor on Firefox, WebDriver on Safari, web pages only); it never captures the screen and never brings a window forward: on Chromium a page behind other windows or in a hidden tab is shown briefly for the capture (value.behindWindow, value.hiddenTab). Open the surface with extension_open first. `targetId` captures that exact page; a url matching several tabs of one page captures the most recently active one and warns. width and height set the Chromium viewport, resize the Safari window and crop from the top left on Firefox; deviceScaleFactor sets the pixel ratio. `store` captures a store listing screenshot at that store's size into store/screenshots/<store>/, where extension_submit sync-listing picks it up; `browserCapture` files a capture of the extension running in this browser under captures/<browser>/, where extension_publish sync-captures picks it up; one call per view. The file goes under dist/extension-js/<browser>/screenshots unless `path` names one; the answer gives path and pixel size.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Session browser; defaults to this project's live session. Required when the project runs sessions for more than one browser: the call is refused with the list rather than picking one. |
| browserCapture | boolean | – | Capture the extension running in this session's browser for the project's build pages on extension.dev: the file goes to captures/<browser>/<context>-NN.png as a flat PNG, numbered per context, and c… |
| context | string | – | page captures a tab picked by `url` (or the only or selected one); the rest capture that extension surface, which must already be open. |
| deviceScaleFactor | number | – | Pixel ratio of the image, 0.5 to 4. Defaults to the browser's own on Chromium and 1 on Firefox; Safari answers at the display's own ratio. Set by store when it is given. |
| height | number | – | Viewport height in CSS pixels (Chromium renders at it; Firefox crops to it). |
| path | string | – | Where to write the image, relative to projectPath and inside it: a file name ending in .png (shots/home.png), or a folder with a trailing slash (shots/) that gets a timestamped .png. A path outside p… |
| projectPath | string | yes | Extension project root (needs a running session: extension_dev or extension_start) |
| store | string | – | Capture for this store's listing at the size it takes, with width, height and deviceScaleFactor set here (chrome, edge and firefox 1280x800 at 1x; safari 1440x900 at 2x, a 2880x1800 image) and the fi… |
| targetId | string | – | Pick one open page directly by its targetId: the target.targetId extension_open answers, or a targetId from extension_dom_snapshot listTargets: true (a CDP target id on Chromium, the tab's browserId… |
| timeout | number | – | Command timeout in ms. When omitted, the default depends on the route: 30000 through the dev session's control channel, 10000 over the Firefox debugger protocol, and 15000 per command over the Chromi… |
| url | string | – | page: the tab whose url contains this (a web address or the extension's own page url). |
| width | number | – | Viewport width in CSS pixels (Chromium renders at it; Firefox crops to it). |
No output schema declared.
No examples provided.
extension_start ~1,120
Run the PRODUCTION build in a browser: build, serve and launch. No hot module replacement, so edits are not picked up; use extension_dev while writing code, and this to check what ships. By default there is no control channel, so extension_eval, extension_storage, extension_reload, extension_open and extension_dom_snapshot cannot attach; pass allowControl:true (or allowEval:true, which implies it) to drive the run with them. Both flags need Extension.js 4.1.34 or later; an older engine is refused as engine-unsupported. build:false launches an existing dist/<browser>; outputPath launches any prebuilt unpacked extension directory. By default it waits for the build and the browser like extension_dev (up to 45 s) and answers build-ready with the session, the extension id and cdpPort, so no extension_wait is needed before extension_open or extension_assert; wait:false answers once the engine process is alive 5 s after spawn, and a wait that runs out answers started naming the extension_wait call. It says what a production session cannot do (no hot reload, no console stream for extension_logs, and the bridge verbs need allowControl); whether the browser loaded the extension is read over its debug port on Extension.js 4.1.34 or later, and only extension_dev proves it on an older engine.
| Name | Type | Req | Description |
|---|---|---|---|
| allowControl | boolean | – | Open the agent-bridge control channel on the production run, so extension_storage, extension_reload, extension_open and extension_dom_snapshot reach it. The browser loads a copy of the build with the… |
| allowEval | boolean | – | Also enable extension_eval on the production run (writes a 0600 session token). Implies allowControl. The build's own content_security_policy still governs eval in its extension pages and background. |
| browser | string | – | – |
| build | boolean | – | Build before serving. false serves the existing dist/<browser> as-is and fails when there is none. |
| chromiumBinary | string | – | Custom Chromium-based binary to launch; `browser` still names the target family the engine builds for (chromium-based when none is given). |
| extensions | array | – | Extra extension paths or store URLs to load alongside the project |
| geckoBinary | string | – | Custom Gecko/Firefox binary to launch; `browser` still names the target family the engine builds for (gecko-based when none is given). |
| headless | boolean | – | false opens a browser window the person can see, overriding EXTENSION_HEADLESS and any --headless in EXTENSION_BROWSER_FLAGS for this session only; pass it when the person wants to watch or try the e… |
| host | string | – | Bind host, default 127.0.0.1. Use 0.0.0.0 in Docker or devcontainers. |
| noBrowser | boolean | – | Build (or, with build:false, check the dist) without launching a browser. A production start serves nothing, so with no browser the engine process ends once the build does; read the result with exten… |
| outputPath | string | – | An existing unpacked extension directory to launch as it is (a manifest.json at its root), for an artifact built by another toolchain or an exact release candidate. Implies build:false; projectPath s… |
| polyfill | boolean | – | Apply cross-browser polyfill (build only) |
| port | number | – | Passed to the engine as --port (0 for auto-assign). A production start serves nothing over it today; it matters only to a toolchain that reads it. |
| profile | string | – | Profile path, or "false" to reuse the real user profile. Omit for a throwaway one. |
| projectPath | string | yes | Extension project root |
| publicHost | string | – | Host the browser dials for HMR and reload when it differs from the bind host |
| replace | boolean | – | Stop the live session this projectPath runs for the same browser first (an extension_dev or extension_start session), reported as replacedSession. Without it the call is refused: one session per brow… |
| startingUrl | string | – | URL the browser opens on launch |
| wait | boolean | – | Wait for the build to land and the browser to launch before answering (the same ready.json contract extension_wait reads, up to 45 s after the 5 s boot check), then answer build-ready with the sessio… |
No output schema declared.
No examples provided.
extension_stop ~484
Stop a session that extension_dev or extension_start is running: terminate the server and the browser it launched, and remove the live-preview carrier if extension_dev placed one. The answer names the server pid and the browser pid the launcher recorded, each with whether it is gone (serverGone, browserGone), so a "close its browser" ask is settled by this answer with no process check by hand. This covers extension_start build:false too, which the registry records as a preview session. Call it when you are done verifying, so sessions do not accumulate.
| Name | Type | Req | Description |
|---|---|---|---|
| all | boolean | – | Stop every known session across projects and browsers, found from this server's registry AND the on-disk markers written by this server or by servers that are no longer running, so it still works aft… |
| browser | string | – | Browser of the session to stop. Only that browser's server and browser processes are ended; a session the same project runs for another browser keeps running. Defaults to the single live session for… |
| confirm | boolean | – | With all: true and no projectPath, end every session the confirm-required answer listed, across projects. Without it, a stop that would cross projects only lists what it would end. |
| includeOtherServers | boolean | – | With all: true, also stop sessions whose markers belong to another MCP server that is still running (the markers sit in a per-user directory every server shares). Off by default, since those sessions… |
| projectPath | string | – | Extension project root |
No output schema declared.
No examples provided.
extension_storage ~366
Read or write chrome.storage in a running extension. Every call runs in the extension's background (the engine honors no context), so it proves nothing about what a content script or page can read. A set is read back and the answer says whether the stored value and its type match. Rides the dev session's control channel, on unless extension_dev was called with allowControl:false. One key per call.
| Name | Type | Req | Description |
|---|---|---|---|
| action | string | yes | get reads a key (or the whole area); set writes a key |
| area | string | – | – |
| browser | string | – | Session browser; defaults to this project's live session. Required when the project runs sessions for more than one browser: the call is refused with the list rather than picking one. |
| key | string | – | Key to get or set |
| projectPath | string | yes | Extension project root (needs a running session: extension_dev or extension_start) |
| timeout | number | – | Command timeout in ms. When omitted, the default depends on the route: 30000 through the dev session's control channel, 10000 over the Firefox debugger protocol, and 15000 per command over the Chromi… |
| value | – | – | Value to set (any JSON value); action=set needs value or valueJson. Some clients send null as the text "null" and an empty string as the two characters ""; both are refused here as ambiguous, so pass… |
| valueJson | string | – | The value to set as JSON text, parsed by this server so its type survives any client: "null" stores null, "\"\"" an empty string, "0", "false", "[1,2]" and "{\"a\":1}" their own types. Pass value or… |
No output schema declared.
No examples provided.
extension_templates ~312
Browse the extension.dev template catalog. Pass action:'list' (the default) to search and filter it and get metadata per template. Pass action:'source' with a `slug` to read one template's files, for learning a pattern before building something similar. Read `framework` as the UI framework only, never the language: TypeScript and JavaScript templates live under slugs ('typescript', 'content-typescript'), shadcn is a React variant ('sidebar-shadcn'), and provider AIs carry the 'ai' tag ('ai-chatgpt', 'ai-claude'). Reach those through query, tags or slug.
| Name | Type | Req | Description |
|---|---|---|---|
| action | string | – | – |
| featured | boolean | – | list: only featured templates. |
| files | array | – | source: paths to read (e.g. ['src/manifest.json']). Omit for the file listing. |
| framework | string | – | list: UI framework filter (empty string = vanilla JS). |
| query | string | – | list: keyword search over slug, description, tags and useCases. Ranks by word matches, so a natural phrase works. |
| slug | string | – | source: which template to read (e.g. 'ai-claude', 'content-react'). Required for source. |
| surface | string | – | list: filter by surface. For a popup/action starter use query:'action', not a surface. |
| tags | array | – | list: filter by tags, e.g. ['ai', 'chat']. |
No output schema declared.
No examples provided.
extension_theme_verify ~228
Verify a Chrome theme manifest before it ships. This settles the four-leg WYSIWYG contract (app-shows == manifest-says == chrome-paints, plus chrome-accepts) as far as is possible headless: it derives every color current Chrome would paint from the manifest through the transcribed Chromium resolver, and classifies each problem as D1 fabrication, D3 parity gap, or D4 acceptance gap (keys Chrome silently discards: dead legacy, incognito, unknown, out-of-range). It verifies only, and never authors or mutates a theme. The app-rendered and real-pixel legs need a browser, so they come back as needsAttended with the step that settles each one (the theme editor at themes.extension.dev, and a dev session in Chrome), never as passed.
| Name | Type | Req | Description |
|---|---|---|---|
| manifest | object | – | The Chrome theme manifest object (with a `theme` block). Pass this or manifestPath. |
| manifestPath | string | – | Path to a theme manifest.json (or a { manifest } seed wrapper). Read in place of the inline manifest. |
No output schema declared.
No examples provided.
extension_wait ~816
Wait for a running dev or start session to be ready, or for a rebuild after a source edit (for: "rebuild"). Call it for a session started with wait:false, after an extension_dev answer whose ready.status was not "ready", and for a build-only (noBrowser) session, which answers as soon as the compile lands; an extension_dev answer with ready.status "ready" already carries this result, so there is nothing left to wait for. It polls the ready.json contract and reports compiled, browserAttached and guestLoaded (the browser's own target list shows your extension, the trustworthy load signal; null when it could not be checked), every answer carries budgetMs and elapsedMs, and on status 'timeout' call again to keep waiting on the same contract. Every ready answer carries the ports the session bound: port (the dev server), controlPort (the control channel) and cdpPort (Chromium) or rdpPort (Gecko), or debugPortNote when the session opened no debug port, so a question about ports is answered here without a shell. The reload stamp a rebuild wait reads (reloadedAt) needs Extension.js 4.1.34 or later; a project on an older engine gets a brief watch for the background to reconnect instead, answering reattached, still-detached or not-stamped. Not for a condition in the page (a model loading, an element appearing): that is extension_eval with until. Never sleep or tail the session log in a shell to wait. A browser that is running while the extension's background went idle answers status "background-asleep" with how to wake it, not "browser-gone"; a Safari WebDriver session that Safari ended answers "webdriver-session-ended" with the restart that opens a new one.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Session browser; defaults to this project's live session. Required when the project runs sessions for more than one browser: the call is refused with the list rather than picking one. |
| for | string | – | What to wait for. "ready" (the default) waits for the session to be ready. "rebuild" waits for the compile after a source edit: until ready.json's compiledAt moves past its value at call time (or pas… |
| projectPath | string | yes | Extension project root |
| since | string | – | for: "rebuild" only. An ISO time: a compile stamped after it counts, so pass the compiledAt an earlier answer returned (or a time before your edit) and a rebuild that landed before this call is not m… |
| timeout | number | – | Deprecated alias of timeoutMs, which wins when both are given. |
| timeoutMs | number | – | Wait budget for this call. Default 45000, clamped to 1000-50000 so one call stays under the client's 60s request timeout. On timeout, call again to keep waiting. |
No output schema declared.
No examples provided.
How do I install the io.github.extensiondev/mcp server?
io.github.extensiondev/mcp runs locally as an npm package, launched with npx -y @extension.dev/mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.
npm · @extension.dev/mcp
claude mcp add extensiondev-mcp -- npx -y @extension.dev/mcp
What is the io.github.extensiondev/mcp server?
io.github.extensiondev/mcp is listed in the public MCP registry as io.github.extensiondev/mcp. Build, run, inspect, and publish browser extensions from any MCP client. 37 tools, 13 browsers. This page covers its npm package (@extension.dev/mcp).
Is the io.github.extensiondev/mcp server safe to use?
io.github.extensiondev/mcp scores 74 out of 100 on VerifyMCP. It declares no install or post-install scripts. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.
What tools does the io.github.extensiondev/mcp server expose?
io.github.extensiondev/mcp exposes 24 tools: extension_create, extension_templates, extension_build, extension_dev, extension_start, and 19 more. Their descriptions and schemas cost roughly 12,809 tokens of context every time the server is loaded.
Is the io.github.extensiondev/mcp server still maintained?
io.github.extensiondev/mcp is still listed as active in the MCP registry. We last reached this channel on 5 November 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.
What licence is the io.github.extensiondev/mcp server under?
io.github.extensiondev/mcp declares the Apache-2.0 licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.