@extension.dev/mcp 4.2.1 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +22 -0
- package/.claude-plugin/plugin.json +30 -0
- package/CHANGELOG.md +73 -0
- package/README.md +14 -2
- package/bin/extension-mcp.js +0 -1
- package/dist/module.js +215 -57
- package/dist/src/__tests__/fixtures/ready-contract.d.ts +7 -0
- package/dist/src/index.d.ts +0 -7
- package/dist/src/lib/act.d.ts +1 -11
- package/dist/src/lib/browser-family.d.ts +4 -0
- package/dist/src/lib/exec.d.ts +0 -11
- package/dist/src/lib/session-browser.d.ts +6 -0
- package/dist/src/lib/validate-input.d.ts +0 -5
- package/dist/src/tools/doctor.d.ts +22 -0
- package/dist/src/tools/dom-inspect.d.ts +1 -1
- package/dist/src/tools/eval.d.ts +1 -1
- package/dist/src/tools/list-extensions.d.ts +1 -1
- package/dist/src/tools/logs-schema.d.ts +0 -1
- package/dist/src/tools/open.d.ts +1 -1
- package/dist/src/tools/reload.d.ts +1 -1
- package/dist/src/tools/source-inspect.d.ts +1 -1
- package/dist/src/tools/storage.d.ts +1 -1
- package/dist/src/tools/wait.d.ts +0 -1
- package/package.json +21 -15
- package/server.json +23 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "extensiondev-mcp",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "Cezar Augusto",
|
|
5
|
+
"email": "boss@cezaraugusto.net",
|
|
6
|
+
"url": "https://extension.dev"
|
|
7
|
+
},
|
|
8
|
+
"plugins": [
|
|
9
|
+
{
|
|
10
|
+
"name": "extension-mcp",
|
|
11
|
+
"source": "./",
|
|
12
|
+
"description": "MCP tools for browser extension development: scaffold from 60+ templates, run the dev server with HMR, inspect the live DOM and logs, and publish store-ready builds for Chrome, Edge, and Firefox.",
|
|
13
|
+
"version": "4.3.0",
|
|
14
|
+
"category": "development",
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Cezar Augusto"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/extensiondev/mcp",
|
|
19
|
+
"strict": false
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "extension-mcp",
|
|
3
|
+
"description": "MCP tools for browser extension development: scaffold from 60+ templates, run the dev server with HMR, inspect the live DOM and logs, and publish store-ready builds for Chrome, Edge, and Firefox. Ships /extension, /extension-add, /extension-debug, and /extension-publish commands.",
|
|
4
|
+
"version": "4.3.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Cezar Augusto",
|
|
7
|
+
"email": "boss@cezaraugusto.net",
|
|
8
|
+
"url": "https://cezaraugusto.com"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/extensiondev/mcp",
|
|
11
|
+
"repository": "https://github.com/extensiondev/mcp",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"mcp",
|
|
15
|
+
"browser-extension",
|
|
16
|
+
"chrome-extension",
|
|
17
|
+
"firefox-addon",
|
|
18
|
+
"edge-extension",
|
|
19
|
+
"manifest-v3",
|
|
20
|
+
"webextension",
|
|
21
|
+
"cross-browser"
|
|
22
|
+
],
|
|
23
|
+
"commands": "./claude/commands",
|
|
24
|
+
"mcpServers": {
|
|
25
|
+
"extension-dev": {
|
|
26
|
+
"command": "npx",
|
|
27
|
+
"args": ["-y", "@extension.dev/mcp"]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,78 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.3.0
|
|
4
|
+
|
|
5
|
+
Diagnosis + version-skew release: a 29th tool that turns "an act tool
|
|
6
|
+
errored, now what" into one call, and a CI that tests the engine
|
|
7
|
+
versions users actually run.
|
|
8
|
+
|
|
9
|
+
- New tool `extension_doctor`. Wraps `extension doctor --output json`:
|
|
10
|
+
walks the dev session's control-channel legs (ready contract,
|
|
11
|
+
dev-server process, control-port agreement, control channel, eval
|
|
12
|
+
token, executor, browser liveness) and returns one
|
|
13
|
+
`{check, status, detail, remediation?}` entry per leg in dependency
|
|
14
|
+
order. Detail and remediation prose are rewritten to MCP-speak like
|
|
15
|
+
every other act-verb error. Engines that predate the `doctor` verb get
|
|
16
|
+
a clean CliError with a hint instead of a crash.
|
|
17
|
+
- Browser-family classification now has ONE copy
|
|
18
|
+
(`src/lib/browser-family.ts`). Fixes real drift: `browsers:
|
|
19
|
+
["chromium"]` ran ZERO family checks in `extension_manifest_validate`
|
|
20
|
+
(an MV2 manifest validated "fine"), and `chromium` was still missing
|
|
21
|
+
from the `extension_build` / `extension_dev` / `extension_preview` /
|
|
22
|
+
`extension_start` schema enums.
|
|
23
|
+
- CI version-skew matrix: every push builds and tests against the
|
|
24
|
+
engine canary, the latest stable, and the vendored floor (deduped),
|
|
25
|
+
plus a nightly run that exercises the real `npx extension@<pin>` path
|
|
26
|
+
end-to-end (`RUN_CLI_SMOKE=1`). A red canary cell now surfaces engine
|
|
27
|
+
regressions the day they publish instead of on the next unrelated PR.
|
|
28
|
+
- Legacy ready-contract compatibility suite: fixtures pin the contract
|
|
29
|
+
shapes older engines wrote (no `cdpPort`, no `pid`), so a 4.0.6-era
|
|
30
|
+
session stays visible to browser defaulting and `resolveCdpPort`
|
|
31
|
+
refuses to adopt an unrelated developer Chrome instead of probing a
|
|
32
|
+
bogus port.
|
|
33
|
+
|
|
34
|
+
## 4.2.2
|
|
35
|
+
|
|
36
|
+
Agent-ergonomics release from the 4.2.1 fresh-eyes walk: the two changes
|
|
37
|
+
that removed nearly all friction a real MCP client hit.
|
|
38
|
+
|
|
39
|
+
- Session-aware browser default. Tools that target a running session
|
|
40
|
+
(`extension_logs`, `extension_reload`, `extension_eval`,
|
|
41
|
+
`extension_storage`, `extension_open`, `extension_dom_inspect`,
|
|
42
|
+
`extension_list_extensions`, `extension_source_inspect`,
|
|
43
|
+
`extension_wait`) no longer hard-default `browser` to a constant that
|
|
44
|
+
could disagree with the session `extension_dev` actually started.
|
|
45
|
+
Omitting `browser` now resolves to the active session's browser:
|
|
46
|
+
in-memory registry first, then the freshest live `ready.json` contract
|
|
47
|
+
on disk (dead pids ignored), then the old constant. Starting a session
|
|
48
|
+
with `browser: "chrome"` and calling `extension_logs` with no args now
|
|
49
|
+
just works instead of erroring about a missing chromium channel.
|
|
50
|
+
- Error hints speak the MCP tool surface, not the CLI. Act-verb error
|
|
51
|
+
prose is rewritten before returning: `` `extension dev
|
|
52
|
+
--browser=chromium --allow-control` `` becomes `extension_dev with
|
|
53
|
+
{ browser: "chromium", allowControl: true }`, and stray
|
|
54
|
+
`--allow-control` / `--allow-eval` / `--browser=<x>` mentions become
|
|
55
|
+
their tool-argument names. Result data is never touched — only
|
|
56
|
+
error/hint prose. Tool descriptions now name `allowControl` /
|
|
57
|
+
`allowEval` directly, so agents no longer discover the gates by
|
|
58
|
+
fuzzing the schema.
|
|
59
|
+
- The no-channel error now names the session that IS running ("Active
|
|
60
|
+
session browser(s) for this project: chrome — pass that as `browser`"),
|
|
61
|
+
so an agent retargets instead of spawning a second, conflicting
|
|
62
|
+
session. Same for the `extension_logs` follow miss.
|
|
63
|
+
- `extension_list_extensions` / `extension_source_inspect` accept
|
|
64
|
+
`browser: "chromium"` (the default dev target) instead of rejecting it
|
|
65
|
+
as non-Chromium.
|
|
66
|
+
- Tests: session-browser resolution + hint-translation suite (137 total).
|
|
67
|
+
|
|
68
|
+
## 4.2.1
|
|
69
|
+
|
|
70
|
+
`extension_build` failures no longer kill the MCP server process
|
|
71
|
+
(fatal-error path returned a rejected promise the server didn't catch).
|
|
72
|
+
CDP-dependent tools resolve the debug port from the session's ready
|
|
73
|
+
contract instead of assuming 9222 (plus a test-only engine pin
|
|
74
|
+
override, `EXTENSION_MCP_CLI_VERSION`).
|
|
75
|
+
|
|
3
76
|
## 4.2.0
|
|
4
77
|
|
|
5
78
|
Session lifecycle + determinism release. Tool count 27 -> 28.
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
# @extension.dev/mcp [![Version][npm-version-image]][npm-version-url] [![Downloads][npm-downloads-image]][npm-downloads-url] [![Discord][discord-image]][discord-url]
|
|
9
9
|
|
|
10
|
-
> Give your AI agent hands for browser extension development.
|
|
10
|
+
> Give your AI agent hands for browser extension development. 29 MCP tools that scaffold, run, inspect, debug, and publish cross-browser extensions.
|
|
11
11
|
|
|
12
12
|
<img alt="Logo" align="right" src="https://avatars.githubusercontent.com/u/106714027" width="20%" />
|
|
13
13
|
|
|
@@ -41,6 +41,17 @@ Built on [Extension.js](https://extension.js.org), the open-source cross-browser
|
|
|
41
41
|
claude mcp add extension-dev npx @extension.dev/mcp
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
Or install it as a plugin — the MCP server plus the `/extension`, `/extension-add`, `/extension-debug`, and `/extension-publish` commands in one step:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
/plugin marketplace add extensiondev/mcp
|
|
48
|
+
/plugin install extension-mcp@extensiondev-mcp
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Cursor
|
|
52
|
+
|
|
53
|
+
[](https://cursor.com/en/install-mcp?name=extension-dev&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyJAZXh0ZW5zaW9uLmRldi9tY3AiXX0%3D)
|
|
54
|
+
|
|
44
55
|
### Claude Desktop / `.mcp.json`
|
|
45
56
|
|
|
46
57
|
```json
|
|
@@ -96,7 +107,8 @@ cp node_modules/@extension.dev/mcp/claude/commands/*.md ~/my-extension/.claude/c
|
|
|
96
107
|
| see | `extension_dom_inspect` | CDP-free DOM snapshot |
|
|
97
108
|
| see | `extension_list_extensions` | List loaded extensions (Chromium) |
|
|
98
109
|
| see | `extension_logs` | Stream logs from every context |
|
|
99
|
-
|
|
|
110
|
+
| see | `extension_doctor` | Diagnose the dev session leg by leg (ready contract, ports, token, executor, browser) |
|
|
111
|
+
| act | `extension_eval` | Evaluate in a context (needs `allowEval: true` on `extension_dev`) |
|
|
100
112
|
| act | `extension_storage` | Read/write `chrome.storage` |
|
|
101
113
|
| act | `extension_reload` | Reload extension or tab |
|
|
102
114
|
| act | `extension_open` | Open a surface / trigger `action`, `command` |
|
package/bin/extension-mcp.js
CHANGED
package/dist/module.js
CHANGED
|
@@ -45,6 +45,12 @@ __webpack_require__.d(dev_namespaceObject, {
|
|
|
45
45
|
handler: ()=>dev_handler,
|
|
46
46
|
schema: ()=>dev_schema
|
|
47
47
|
});
|
|
48
|
+
var doctor_namespaceObject = {};
|
|
49
|
+
__webpack_require__.r(doctor_namespaceObject);
|
|
50
|
+
__webpack_require__.d(doctor_namespaceObject, {
|
|
51
|
+
handler: ()=>doctor_handler,
|
|
52
|
+
schema: ()=>doctor_schema
|
|
53
|
+
});
|
|
48
54
|
var dom_inspect_namespaceObject = {};
|
|
49
55
|
__webpack_require__.r(dom_inspect_namespaceObject);
|
|
50
56
|
__webpack_require__.d(dom_inspect_namespaceObject, {
|
|
@@ -183,7 +189,7 @@ __webpack_require__.d(whoami_namespaceObject, {
|
|
|
183
189
|
handler: ()=>whoami_handler,
|
|
184
190
|
schema: ()=>whoami_schema
|
|
185
191
|
});
|
|
186
|
-
var package_namespaceObject = JSON.parse('{"rE":"4.
|
|
192
|
+
var package_namespaceObject = JSON.parse('{"rE":"4.3.0","El":{"OP":"^4.0.8"}}');
|
|
187
193
|
const create_schema = {
|
|
188
194
|
name: "extension_create",
|
|
189
195
|
description: "Create a new browser extension project from a template in the extension.dev template catalog. Use extension_list_templates to see available options.",
|
|
@@ -371,6 +377,7 @@ const build_schema = {
|
|
|
371
377
|
type: "string",
|
|
372
378
|
enum: [
|
|
373
379
|
"chrome",
|
|
380
|
+
"chromium",
|
|
374
381
|
"edge",
|
|
375
382
|
"firefox",
|
|
376
383
|
"chromium-based",
|
|
@@ -528,6 +535,7 @@ const dev_schema = {
|
|
|
528
535
|
type: "string",
|
|
529
536
|
enum: [
|
|
530
537
|
"chrome",
|
|
538
|
+
"chromium",
|
|
531
539
|
"edge",
|
|
532
540
|
"firefox",
|
|
533
541
|
"chromium-based",
|
|
@@ -617,6 +625,7 @@ const start_schema = {
|
|
|
617
625
|
type: "string",
|
|
618
626
|
enum: [
|
|
619
627
|
"chrome",
|
|
628
|
+
"chromium",
|
|
620
629
|
"edge",
|
|
621
630
|
"firefox",
|
|
622
631
|
"chromium-based",
|
|
@@ -687,6 +696,7 @@ const preview_schema = {
|
|
|
687
696
|
type: "string",
|
|
688
697
|
enum: [
|
|
689
698
|
"chrome",
|
|
699
|
+
"chromium",
|
|
690
700
|
"edge",
|
|
691
701
|
"firefox",
|
|
692
702
|
"chromium-based",
|
|
@@ -902,6 +912,23 @@ async function get_template_source_handler(args) {
|
|
|
902
912
|
} : {}
|
|
903
913
|
});
|
|
904
914
|
}
|
|
915
|
+
const CHROMIUM_FAMILY = new Set([
|
|
916
|
+
"chrome",
|
|
917
|
+
"chromium",
|
|
918
|
+
"edge",
|
|
919
|
+
"chromium-based"
|
|
920
|
+
]);
|
|
921
|
+
const GECKO_FAMILY = new Set([
|
|
922
|
+
"firefox",
|
|
923
|
+
"gecko-based",
|
|
924
|
+
"firefox-based"
|
|
925
|
+
]);
|
|
926
|
+
function isChromiumFamily(browser) {
|
|
927
|
+
return CHROMIUM_FAMILY.has(browser);
|
|
928
|
+
}
|
|
929
|
+
function isGeckoFamily(browser) {
|
|
930
|
+
return GECKO_FAMILY.has(browser);
|
|
931
|
+
}
|
|
905
932
|
const manifest_validate_schema = {
|
|
906
933
|
name: "extension_manifest_validate",
|
|
907
934
|
description: "Validate a manifest.json file for correctness across browsers. Reports missing fields, invalid permissions, and cross-browser compatibility issues.",
|
|
@@ -961,15 +988,8 @@ async function manifest_validate_handler(args) {
|
|
|
961
988
|
const chromiumManifest = filterKeysForThisBrowser(manifest, "chrome");
|
|
962
989
|
if (!chromiumManifest.manifest_version) result.errors.push('Missing manifest_version. Use "chromium:manifest_version": 3 and "firefox:manifest_version": 2 for cross-browser support.');
|
|
963
990
|
for (const browser of browsers){
|
|
964
|
-
const isChromium =
|
|
965
|
-
|
|
966
|
-
"edge",
|
|
967
|
-
"chromium-based"
|
|
968
|
-
].includes(browser);
|
|
969
|
-
const isFirefox = [
|
|
970
|
-
"firefox",
|
|
971
|
-
"gecko-based"
|
|
972
|
-
].includes(browser);
|
|
991
|
+
const isChromium = isChromiumFamily(browser);
|
|
992
|
+
const isFirefox = isGeckoFamily(browser);
|
|
973
993
|
const effective = filterKeysForThisBrowser(manifest, browser);
|
|
974
994
|
const issues = [];
|
|
975
995
|
if (isChromium) {
|
|
@@ -1620,6 +1640,66 @@ function isCdpEndpoint(port) {
|
|
|
1620
1640
|
});
|
|
1621
1641
|
});
|
|
1622
1642
|
}
|
|
1643
|
+
function contractSightings(projectPath) {
|
|
1644
|
+
const root = node_path.resolve(projectPath, "dist", "extension-js");
|
|
1645
|
+
let dirs;
|
|
1646
|
+
try {
|
|
1647
|
+
dirs = node_fs.readdirSync(root);
|
|
1648
|
+
} catch {
|
|
1649
|
+
return [];
|
|
1650
|
+
}
|
|
1651
|
+
const sightings = [];
|
|
1652
|
+
for (const dir of dirs){
|
|
1653
|
+
const readyPath = node_path.join(root, dir, "ready.json");
|
|
1654
|
+
try {
|
|
1655
|
+
const stat = node_fs.statSync(readyPath);
|
|
1656
|
+
const contract = JSON.parse(node_fs.readFileSync(readyPath, "utf8"));
|
|
1657
|
+
if (contract?.status !== "ready") continue;
|
|
1658
|
+
sightings.push({
|
|
1659
|
+
browser: dir,
|
|
1660
|
+
mtimeMs: stat.mtimeMs,
|
|
1661
|
+
pid: "number" == typeof contract.pid ? contract.pid : void 0
|
|
1662
|
+
});
|
|
1663
|
+
} catch {}
|
|
1664
|
+
}
|
|
1665
|
+
return sightings;
|
|
1666
|
+
}
|
|
1667
|
+
function pidAlive(pid) {
|
|
1668
|
+
try {
|
|
1669
|
+
process.kill(pid, 0);
|
|
1670
|
+
return true;
|
|
1671
|
+
} catch {
|
|
1672
|
+
return false;
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
function knownSessionBrowsers(projectPath) {
|
|
1676
|
+
const resolved = node_path.resolve(projectPath);
|
|
1677
|
+
const browsers = [];
|
|
1678
|
+
for (const session of listSessions())if (node_path.resolve(session.projectPath) === resolved) browsers.push(session.browser);
|
|
1679
|
+
for (const sighting of contractSightings(projectPath))if (void 0 === sighting.pid || pidAlive(sighting.pid)) browsers.push(sighting.browser);
|
|
1680
|
+
return Array.from(new Set(browsers));
|
|
1681
|
+
}
|
|
1682
|
+
function resolveSessionBrowser(projectPath, explicit, fallback = "chromium") {
|
|
1683
|
+
if (explicit) return {
|
|
1684
|
+
browser: explicit,
|
|
1685
|
+
source: "explicit"
|
|
1686
|
+
};
|
|
1687
|
+
const resolved = node_path.resolve(projectPath);
|
|
1688
|
+
const mine = listSessions().filter((s)=>node_path.resolve(s.projectPath) === resolved);
|
|
1689
|
+
if (mine.length > 0) return {
|
|
1690
|
+
browser: mine[mine.length - 1].browser,
|
|
1691
|
+
source: "session"
|
|
1692
|
+
};
|
|
1693
|
+
const sightings = contractSightings(projectPath).filter((s)=>void 0 === s.pid || pidAlive(s.pid)).sort((a, b)=>b.mtimeMs - a.mtimeMs);
|
|
1694
|
+
if (sightings.length > 0) return {
|
|
1695
|
+
browser: sightings[0].browser,
|
|
1696
|
+
source: "contract"
|
|
1697
|
+
};
|
|
1698
|
+
return {
|
|
1699
|
+
browser: fallback,
|
|
1700
|
+
source: "fallback"
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1623
1703
|
const source_inspect_schema = {
|
|
1624
1704
|
name: "extension_source_inspect",
|
|
1625
1705
|
description: "Inspect a running extension's live state via Chrome DevTools Protocol: full HTML (with shadow DOM), DOM structure, content script injection, console messages, and CSS selector queries. Requires an active dev or start session.",
|
|
@@ -1663,7 +1743,7 @@ const source_inspect_schema = {
|
|
|
1663
1743
|
},
|
|
1664
1744
|
browser: {
|
|
1665
1745
|
type: "string",
|
|
1666
|
-
|
|
1746
|
+
description: "Browser session to target. Defaults to the active dev session's browser for this project."
|
|
1667
1747
|
},
|
|
1668
1748
|
maxBytes: {
|
|
1669
1749
|
type: "number",
|
|
@@ -1682,21 +1762,16 @@ const source_inspect_schema = {
|
|
|
1682
1762
|
}
|
|
1683
1763
|
};
|
|
1684
1764
|
async function source_inspect_handler(args) {
|
|
1685
|
-
const browser = args.browser
|
|
1765
|
+
const { browser } = resolveSessionBrowser(args.projectPath, args.browser, "chrome");
|
|
1686
1766
|
const include = new Set(args.include ?? [
|
|
1687
1767
|
"summary",
|
|
1688
1768
|
"meta",
|
|
1689
1769
|
"console"
|
|
1690
1770
|
]);
|
|
1691
1771
|
const maxBytes = args.maxBytes ?? 262144;
|
|
1692
|
-
|
|
1693
|
-
"chrome",
|
|
1694
|
-
"edge",
|
|
1695
|
-
"chromium-based"
|
|
1696
|
-
].includes(browser);
|
|
1697
|
-
if (!isChromium) return JSON.stringify({
|
|
1772
|
+
if (!isChromiumFamily(browser)) return JSON.stringify({
|
|
1698
1773
|
error: `Source inspection for ${browser} uses RDP (Remote Debug Protocol). Currently only Chromium CDP is supported.`,
|
|
1699
|
-
hint:
|
|
1774
|
+
hint: 'Pass browser: "chrome" (against a Chromium-family dev session).'
|
|
1700
1775
|
});
|
|
1701
1776
|
const resolved = await resolveCdpPort(args.projectPath, browser);
|
|
1702
1777
|
if (!resolved) return JSON.stringify({
|
|
@@ -1797,7 +1872,7 @@ const list_extensions_schema = {
|
|
|
1797
1872
|
},
|
|
1798
1873
|
browser: {
|
|
1799
1874
|
type: "string",
|
|
1800
|
-
|
|
1875
|
+
description: "Browser session to target. Defaults to the active dev session's browser for this project."
|
|
1801
1876
|
}
|
|
1802
1877
|
},
|
|
1803
1878
|
required: [
|
|
@@ -1806,15 +1881,10 @@ const list_extensions_schema = {
|
|
|
1806
1881
|
}
|
|
1807
1882
|
};
|
|
1808
1883
|
async function list_extensions_handler(args) {
|
|
1809
|
-
const browser = args.browser
|
|
1810
|
-
|
|
1811
|
-
"chrome",
|
|
1812
|
-
"edge",
|
|
1813
|
-
"chromium-based"
|
|
1814
|
-
].includes(browser);
|
|
1815
|
-
if (!isChromium) return JSON.stringify({
|
|
1884
|
+
const { browser } = resolveSessionBrowser(args.projectPath, args.browser, "chrome");
|
|
1885
|
+
if (!isChromiumFamily(browser)) return JSON.stringify({
|
|
1816
1886
|
error: `Listing extensions for ${browser} uses RDP (Remote Debug Protocol). Currently only Chromium CDP is supported.`,
|
|
1817
|
-
hint:
|
|
1887
|
+
hint: 'Pass browser: "chrome" (against a Chromium-family dev session).'
|
|
1818
1888
|
});
|
|
1819
1889
|
const resolved = await resolveCdpPort(args.projectPath, browser);
|
|
1820
1890
|
if (!resolved) return JSON.stringify({
|
|
@@ -1950,8 +2020,7 @@ const logs_schema_schema = {
|
|
|
1950
2020
|
},
|
|
1951
2021
|
browser: {
|
|
1952
2022
|
type: "string",
|
|
1953
|
-
|
|
1954
|
-
description: "Which dist/extension-js/<browser>/ to read. Defaults to chromium (the default dev target)."
|
|
2023
|
+
description: "Which dist/extension-js/<browser>/ to read. Defaults to the active dev session's browser for this project (falls back to chromium)."
|
|
1955
2024
|
},
|
|
1956
2025
|
level: {
|
|
1957
2026
|
type: "string",
|
|
@@ -2092,10 +2161,14 @@ async function readFromFile(args, browser, limit) {
|
|
|
2092
2161
|
}
|
|
2093
2162
|
async function readFromStream(args, browser, limit) {
|
|
2094
2163
|
const ready = readReadyContract(args.projectPath, browser);
|
|
2095
|
-
if (!ready)
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2164
|
+
if (!ready) {
|
|
2165
|
+
const running = knownSessionBrowsers(args.projectPath).filter((b)=>b !== browser);
|
|
2166
|
+
const retarget = running.length ? `An active session exists for browser(s): ${running.join(", ")} — pass that as \`browser\`. Otherwise run` : "Run";
|
|
2167
|
+
return JSON.stringify({
|
|
2168
|
+
error: `No active control channel found for ${browser}.`,
|
|
2169
|
+
hint: `${retarget} extension_dev (browser: ${browser}) and wait for it to be ready, then retry. For past logs without a live channel, call without follow.`
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2099
2172
|
const followMs = Math.min(Math.max(args.followMs ?? 4000, 500), 15000);
|
|
2100
2173
|
const matches = makeFilter(args);
|
|
2101
2174
|
const events = [];
|
|
@@ -2158,11 +2231,27 @@ async function readFromStream(args, browser, limit) {
|
|
|
2158
2231
|
});
|
|
2159
2232
|
}
|
|
2160
2233
|
async function logs_handler(args) {
|
|
2161
|
-
const browser = args.browser
|
|
2234
|
+
const { browser } = resolveSessionBrowser(args.projectPath, args.browser);
|
|
2162
2235
|
const limit = args.limit && args.limit > 0 ? args.limit : 200;
|
|
2163
2236
|
if (args.follow) return readFromStream(args, browser, limit);
|
|
2164
2237
|
return readFromFile(args, browser, limit);
|
|
2165
2238
|
}
|
|
2239
|
+
function toMcpSpeak(text) {
|
|
2240
|
+
return text.replace(/`?extension dev(?: [^\s`]*)? --browser[= ]([\w-]+) --allow-control`?/g, 'extension_dev with { browser: "$1", allowControl: true }').replace(/--allow-control/g, "allowControl: true (extension_dev)").replace(/--allow-eval/g, "allowEval: true (extension_dev)").replace(/--browser[= ]([\w-]+)/g, 'browser: "$1"').replace(/`extension dev`/g, "extension_dev").replace(/\bextension dev\b/g, "extension_dev");
|
|
2241
|
+
}
|
|
2242
|
+
function withSessionContext(message, projectPath) {
|
|
2243
|
+
if (!/no active control channel/i.test(message)) return message;
|
|
2244
|
+
const running = knownSessionBrowsers(projectPath);
|
|
2245
|
+
if (0 === running.length) return message;
|
|
2246
|
+
return `${message} Active session browser(s) for this project: ${running.join(", ")} — pass that as \`browser\`, or restart it via extension_dev with allowControl: true if the control channel is off.`;
|
|
2247
|
+
}
|
|
2248
|
+
function translateFrame(frame, projectPath) {
|
|
2249
|
+
if (!frame || false !== frame.ok) return frame;
|
|
2250
|
+
if (frame.error && "string" == typeof frame.error.message) frame.error.message = withSessionContext(toMcpSpeak(frame.error.message), projectPath);
|
|
2251
|
+
if ("string" == typeof frame.error?.hint) frame.error.hint = toMcpSpeak(frame.error.hint);
|
|
2252
|
+
if ("string" == typeof frame.hint) frame.hint = toMcpSpeak(frame.hint);
|
|
2253
|
+
return frame;
|
|
2254
|
+
}
|
|
2166
2255
|
async function runActVerb(args, projectPath, timeoutMs) {
|
|
2167
2256
|
const { code, stdout, stderr } = await runExtensionCli([
|
|
2168
2257
|
...args,
|
|
@@ -2174,14 +2263,16 @@ async function runActVerb(args, projectPath, timeoutMs) {
|
|
|
2174
2263
|
});
|
|
2175
2264
|
const out = stdout.trim();
|
|
2176
2265
|
if (out) try {
|
|
2177
|
-
JSON.parse(out);
|
|
2266
|
+
const frame = JSON.parse(out);
|
|
2267
|
+
if (frame && false === frame.ok) return JSON.stringify(translateFrame(frame, projectPath));
|
|
2178
2268
|
return out;
|
|
2179
2269
|
} catch {}
|
|
2270
|
+
const message = stderr.trim() || `extension exited with code ${code}`;
|
|
2180
2271
|
return JSON.stringify({
|
|
2181
2272
|
ok: false,
|
|
2182
2273
|
error: {
|
|
2183
2274
|
name: "CliError",
|
|
2184
|
-
message:
|
|
2275
|
+
message: withSessionContext(toMcpSpeak(message), projectPath)
|
|
2185
2276
|
}
|
|
2186
2277
|
});
|
|
2187
2278
|
}
|
|
@@ -2196,7 +2287,7 @@ function commonFlags(args) {
|
|
|
2196
2287
|
}
|
|
2197
2288
|
const eval_schema = {
|
|
2198
2289
|
name: "extension_eval",
|
|
2199
|
-
description: "Evaluate an expression in a running extension context (service worker, content script, popup, options, sidebar). Requires the dev session to be started with
|
|
2290
|
+
description: "Evaluate an expression in a running extension context (service worker, content script, popup, options, sidebar). Requires the dev session to be started with allowEval: true (extension_dev; writes a 0600 session token the CLI reads). Wraps `extension eval`.",
|
|
2200
2291
|
inputSchema: {
|
|
2201
2292
|
type: "object",
|
|
2202
2293
|
properties: {
|
|
@@ -2232,7 +2323,7 @@ const eval_schema = {
|
|
|
2232
2323
|
},
|
|
2233
2324
|
browser: {
|
|
2234
2325
|
type: "string",
|
|
2235
|
-
|
|
2326
|
+
description: "Browser session to target. Defaults to the active dev session's browser for this project."
|
|
2236
2327
|
},
|
|
2237
2328
|
timeout: {
|
|
2238
2329
|
type: "number",
|
|
@@ -2246,16 +2337,20 @@ const eval_schema = {
|
|
|
2246
2337
|
}
|
|
2247
2338
|
};
|
|
2248
2339
|
async function eval_handler(args) {
|
|
2340
|
+
const { browser } = resolveSessionBrowser(args.projectPath, args.browser);
|
|
2249
2341
|
return runActVerb([
|
|
2250
2342
|
"eval",
|
|
2251
2343
|
args.expression,
|
|
2252
2344
|
args.projectPath,
|
|
2253
|
-
...commonFlags(
|
|
2345
|
+
...commonFlags({
|
|
2346
|
+
...args,
|
|
2347
|
+
browser
|
|
2348
|
+
})
|
|
2254
2349
|
], args.projectPath, args.timeout);
|
|
2255
2350
|
}
|
|
2256
2351
|
const storage_schema = {
|
|
2257
2352
|
name: "extension_storage",
|
|
2258
|
-
description: "Read or write chrome.storage in a running extension. Requires the dev session to be started with
|
|
2353
|
+
description: "Read or write chrome.storage in a running extension. Requires the dev session to be started with allowControl: true (extension_dev). Wraps `extension storage get|set`.",
|
|
2259
2354
|
inputSchema: {
|
|
2260
2355
|
type: "object",
|
|
2261
2356
|
properties: {
|
|
@@ -2301,7 +2396,7 @@ const storage_schema = {
|
|
|
2301
2396
|
},
|
|
2302
2397
|
browser: {
|
|
2303
2398
|
type: "string",
|
|
2304
|
-
|
|
2399
|
+
description: "Browser session to target. Defaults to the active dev session's browser for this project."
|
|
2305
2400
|
},
|
|
2306
2401
|
timeout: {
|
|
2307
2402
|
type: "number",
|
|
@@ -2315,6 +2410,7 @@ const storage_schema = {
|
|
|
2315
2410
|
}
|
|
2316
2411
|
};
|
|
2317
2412
|
async function storage_handler(args) {
|
|
2413
|
+
const { browser } = resolveSessionBrowser(args.projectPath, args.browser);
|
|
2318
2414
|
const cli = [
|
|
2319
2415
|
"storage",
|
|
2320
2416
|
args.action,
|
|
@@ -2333,13 +2429,13 @@ async function storage_handler(args) {
|
|
|
2333
2429
|
cli.push("--value", JSON.stringify(args.value));
|
|
2334
2430
|
}
|
|
2335
2431
|
if (args.context) cli.push("--context", args.context);
|
|
2336
|
-
|
|
2432
|
+
cli.push("--browser", browser);
|
|
2337
2433
|
if (null != args.timeout) cli.push("--timeout", String(args.timeout));
|
|
2338
2434
|
return runActVerb(cli, args.projectPath, args.timeout);
|
|
2339
2435
|
}
|
|
2340
2436
|
const reload_schema = {
|
|
2341
2437
|
name: "extension_reload",
|
|
2342
|
-
description: "Reload a running extension (background) or a tab. Requires the dev session to be started with
|
|
2438
|
+
description: "Reload a running extension (background) or a tab. Requires the dev session to be started with allowControl: true (extension_dev). Wraps `extension reload`.",
|
|
2343
2439
|
inputSchema: {
|
|
2344
2440
|
type: "object",
|
|
2345
2441
|
properties: {
|
|
@@ -2362,7 +2458,7 @@ const reload_schema = {
|
|
|
2362
2458
|
},
|
|
2363
2459
|
browser: {
|
|
2364
2460
|
type: "string",
|
|
2365
|
-
|
|
2461
|
+
description: "Browser session to target. Defaults to the active dev session's browser for this project."
|
|
2366
2462
|
},
|
|
2367
2463
|
timeout: {
|
|
2368
2464
|
type: "number",
|
|
@@ -2375,15 +2471,19 @@ const reload_schema = {
|
|
|
2375
2471
|
}
|
|
2376
2472
|
};
|
|
2377
2473
|
async function reload_handler(args) {
|
|
2474
|
+
const { browser } = resolveSessionBrowser(args.projectPath, args.browser);
|
|
2378
2475
|
return runActVerb([
|
|
2379
2476
|
"reload",
|
|
2380
2477
|
args.projectPath,
|
|
2381
|
-
...commonFlags(
|
|
2478
|
+
...commonFlags({
|
|
2479
|
+
...args,
|
|
2480
|
+
browser
|
|
2481
|
+
})
|
|
2382
2482
|
], args.projectPath, args.timeout);
|
|
2383
2483
|
}
|
|
2384
2484
|
const open_schema = {
|
|
2385
2485
|
name: "extension_open",
|
|
2386
|
-
description: "Open an extension surface or replay an event in a running session. 'popup'/'options'/'sidebar' open UI surfaces. 'action' triggers the toolbar action: opens the action's popup, or (no popup) replays chrome.action.onClicked. 'command' replays a chrome.commands.onCommand keyboard shortcut (pass `name`). NOTE: action/command replay invokes your listener WITHOUT a user gesture, so the gesture-derived activeTab grant does not apply (the result includes gesture:false and a warning when activeTab is declared). Requires
|
|
2486
|
+
description: "Open an extension surface or replay an event in a running session. 'popup'/'options'/'sidebar' open UI surfaces. 'action' triggers the toolbar action: opens the action's popup, or (no popup) replays chrome.action.onClicked. 'command' replays a chrome.commands.onCommand keyboard shortcut (pass `name`). NOTE: action/command replay invokes your listener WITHOUT a user gesture, so the gesture-derived activeTab grant does not apply (the result includes gesture:false and a warning when activeTab is declared). Requires the dev session to be started with allowControl: true (extension_dev). Wraps `extension open`.",
|
|
2387
2487
|
inputSchema: {
|
|
2388
2488
|
type: "object",
|
|
2389
2489
|
properties: {
|
|
@@ -2408,7 +2508,7 @@ const open_schema = {
|
|
|
2408
2508
|
},
|
|
2409
2509
|
browser: {
|
|
2410
2510
|
type: "string",
|
|
2411
|
-
|
|
2511
|
+
description: "Browser session to target. Defaults to the active dev session's browser for this project."
|
|
2412
2512
|
},
|
|
2413
2513
|
timeout: {
|
|
2414
2514
|
type: "number",
|
|
@@ -2422,19 +2522,20 @@ const open_schema = {
|
|
|
2422
2522
|
}
|
|
2423
2523
|
};
|
|
2424
2524
|
async function open_handler(args) {
|
|
2525
|
+
const { browser } = resolveSessionBrowser(args.projectPath, args.browser);
|
|
2425
2526
|
const cli = [
|
|
2426
2527
|
"open",
|
|
2427
2528
|
args.surface,
|
|
2428
2529
|
args.projectPath
|
|
2429
2530
|
];
|
|
2430
2531
|
if ("command" === args.surface && args.name) cli.push("--name", args.name);
|
|
2431
|
-
|
|
2532
|
+
cli.push("--browser", browser);
|
|
2432
2533
|
if (null != args.timeout) cli.push("--timeout", String(args.timeout));
|
|
2433
2534
|
return runActVerb(cli, args.projectPath, args.timeout);
|
|
2434
2535
|
}
|
|
2435
2536
|
const dom_inspect_schema = {
|
|
2436
2537
|
name: "extension_dom_inspect",
|
|
2437
|
-
description: "Inspect a page/content-script DOM via the agent bridge (CDP-free, localhost). Returns a structured snapshot (counts, extension roots, open shadow roots, optional capped HTML). Requires
|
|
2538
|
+
description: "Inspect a page/content-script DOM via the agent bridge (CDP-free, localhost). Returns a structured snapshot (counts, extension roots, open shadow roots, optional capped HTML). Requires the dev session to be started with allowControl: true (extension_dev). For closed shadow roots or deep CDP inspection use extension_source_inspect. Wraps `extension inspect`.",
|
|
2438
2539
|
inputSchema: {
|
|
2439
2540
|
type: "object",
|
|
2440
2541
|
properties: {
|
|
@@ -2483,7 +2584,7 @@ const dom_inspect_schema = {
|
|
|
2483
2584
|
},
|
|
2484
2585
|
browser: {
|
|
2485
2586
|
type: "string",
|
|
2486
|
-
|
|
2587
|
+
description: "Browser session to target. Defaults to the active dev session's browser for this project."
|
|
2487
2588
|
},
|
|
2488
2589
|
timeout: {
|
|
2489
2590
|
type: "number",
|
|
@@ -2519,7 +2620,7 @@ async function dom_inspect_handler(args) {
|
|
|
2519
2620
|
if (args.include?.length) cli.push("--include", args.include.join(","));
|
|
2520
2621
|
if (null != args.maxBytes) cli.push("--max-bytes", String(args.maxBytes));
|
|
2521
2622
|
if (null != args.withConsole) cli.push("--with-console", String(args.withConsole));
|
|
2522
|
-
|
|
2623
|
+
cli.push("--browser", resolveSessionBrowser(args.projectPath, args.browser).browser);
|
|
2523
2624
|
if (null != args.timeout) cli.push("--timeout", String(args.timeout));
|
|
2524
2625
|
return runActVerb(cli, args.projectPath, args.timeout);
|
|
2525
2626
|
}
|
|
@@ -2676,8 +2777,7 @@ const wait_schema = {
|
|
|
2676
2777
|
},
|
|
2677
2778
|
browser: {
|
|
2678
2779
|
type: "string",
|
|
2679
|
-
|
|
2680
|
-
description: "Browser to check readiness for"
|
|
2780
|
+
description: "Browser to check readiness for. Defaults to the active dev session's browser for this project."
|
|
2681
2781
|
},
|
|
2682
2782
|
timeout: {
|
|
2683
2783
|
type: "number",
|
|
@@ -2691,7 +2791,7 @@ const wait_schema = {
|
|
|
2691
2791
|
}
|
|
2692
2792
|
};
|
|
2693
2793
|
async function wait_handler(args) {
|
|
2694
|
-
const browser = args.browser
|
|
2794
|
+
const { browser } = resolveSessionBrowser(args.projectPath, args.browser, "chrome");
|
|
2695
2795
|
const timeout = args.timeout ?? 60000;
|
|
2696
2796
|
const readyPath = node_path.resolve(args.projectPath, "dist", "extension-js", browser, "ready.json");
|
|
2697
2797
|
const start = Date.now();
|
|
@@ -3156,7 +3256,7 @@ async function install_browser_handler(args) {
|
|
|
3156
3256
|
status: "installed",
|
|
3157
3257
|
browser: args.browser,
|
|
3158
3258
|
duration: Date.now() - start,
|
|
3159
|
-
hint: `Browser "${args.browser}" is now available. Use extension_dev or extension_start with
|
|
3259
|
+
hint: `Browser "${args.browser}" is now available. Use extension_dev or extension_start with browser: "${args.browser}".`
|
|
3160
3260
|
});
|
|
3161
3261
|
} catch (err) {
|
|
3162
3262
|
return JSON.stringify({
|
|
@@ -3452,6 +3552,63 @@ async function detect_browsers_handler(args) {
|
|
|
3452
3552
|
hint: missing.length ? `Missing browser(s): ${missing.map((d)=>d.browser).join(", ")}. Use extension_install_browser to install them.` : "All requested browsers are available."
|
|
3453
3553
|
});
|
|
3454
3554
|
}
|
|
3555
|
+
const doctor_schema = {
|
|
3556
|
+
name: "extension_doctor",
|
|
3557
|
+
description: "Diagnose a dev session end-to-end: ready contract, dev-server process, control-port agreement, control channel, eval token, executor, and browser liveness. Returns one {check, status, detail, remediation?} entry per leg in dependency order — a 'skip' names the check that blocked it and is NOT a pass. Run this first when any act tool (storage/reload/eval/open) errors unexpectedly. Wraps `extension doctor`.",
|
|
3558
|
+
inputSchema: {
|
|
3559
|
+
type: "object",
|
|
3560
|
+
properties: {
|
|
3561
|
+
projectPath: {
|
|
3562
|
+
type: "string",
|
|
3563
|
+
description: "Path to the extension project root"
|
|
3564
|
+
},
|
|
3565
|
+
browser: {
|
|
3566
|
+
type: "string",
|
|
3567
|
+
description: "Browser session to diagnose. Defaults to the active dev session's browser for this project."
|
|
3568
|
+
}
|
|
3569
|
+
},
|
|
3570
|
+
required: [
|
|
3571
|
+
"projectPath"
|
|
3572
|
+
]
|
|
3573
|
+
}
|
|
3574
|
+
};
|
|
3575
|
+
async function doctor_handler(args) {
|
|
3576
|
+
const { browser } = resolveSessionBrowser(args.projectPath, args.browser);
|
|
3577
|
+
const { code, stdout, stderr } = await runExtensionCli([
|
|
3578
|
+
"doctor",
|
|
3579
|
+
args.projectPath,
|
|
3580
|
+
"--browser",
|
|
3581
|
+
browser,
|
|
3582
|
+
"--output",
|
|
3583
|
+
"json"
|
|
3584
|
+
], {
|
|
3585
|
+
cwd: args.projectPath
|
|
3586
|
+
});
|
|
3587
|
+
const out = stdout.trim();
|
|
3588
|
+
try {
|
|
3589
|
+
const checks = JSON.parse(out);
|
|
3590
|
+
if (!Array.isArray(checks)) throw new Error("not a check array");
|
|
3591
|
+
for (const check of checks){
|
|
3592
|
+
if ("string" == typeof check.detail) check.detail = toMcpSpeak(check.detail);
|
|
3593
|
+
if ("string" == typeof check.remediation) check.remediation = toMcpSpeak(check.remediation);
|
|
3594
|
+
}
|
|
3595
|
+
return JSON.stringify({
|
|
3596
|
+
browser,
|
|
3597
|
+
healthy: 0 === code,
|
|
3598
|
+
checks
|
|
3599
|
+
});
|
|
3600
|
+
} catch {
|
|
3601
|
+
const message = stderr.trim() || `extension exited with code ${code}`;
|
|
3602
|
+
return JSON.stringify({
|
|
3603
|
+
ok: false,
|
|
3604
|
+
error: {
|
|
3605
|
+
name: "CliError",
|
|
3606
|
+
message: toMcpSpeak(message),
|
|
3607
|
+
hint: "extension doctor requires a recent extension CLI — the project's local install may predate it."
|
|
3608
|
+
}
|
|
3609
|
+
});
|
|
3610
|
+
}
|
|
3611
|
+
}
|
|
3455
3612
|
function typeOf(value) {
|
|
3456
3613
|
if (Array.isArray(value)) return "array";
|
|
3457
3614
|
if (null === value) return "null";
|
|
@@ -3544,7 +3701,8 @@ const tools = [
|
|
|
3544
3701
|
logout_namespaceObject,
|
|
3545
3702
|
install_browser_namespaceObject,
|
|
3546
3703
|
list_browsers_namespaceObject,
|
|
3547
|
-
detect_browsers_namespaceObject
|
|
3704
|
+
detect_browsers_namespaceObject,
|
|
3705
|
+
doctor_namespaceObject
|
|
3548
3706
|
];
|
|
3549
3707
|
const toolMap = new Map();
|
|
3550
3708
|
for (const tool of tools)toolMap.set(tool.schema.name, tool);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function writeModernContract(projectPath: string, browser: string, overrides?: Record<string, unknown>): string;
|
|
2
|
+
export declare function writeLegacyContract(projectPath: string, browser: string, overrides?: Record<string, unknown>): string;
|
|
3
|
+
export declare function writeErrorContract(projectPath: string, browser: string): string;
|
|
4
|
+
export declare function writeLegacyEngineState(projectPath: string, browser: string): {
|
|
5
|
+
legacyPortFile: string;
|
|
6
|
+
legacyTokenFile: string;
|
|
7
|
+
};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -8,11 +8,4 @@ export interface ToolModule {
|
|
|
8
8
|
}
|
|
9
9
|
export declare const tools: ToolModule[];
|
|
10
10
|
export declare function startServer(): Promise<void>;
|
|
11
|
-
/**
|
|
12
|
-
* Human-facing subcommands for `extension-mcp <cmd>`. Unlike the MCP tools
|
|
13
|
-
* (which must return promptly and so resume across calls), the bin blocks on
|
|
14
|
-
* the device flow because there is a person watching the terminal. Returns a
|
|
15
|
-
* process exit code. All console output goes to stderr so it never pollutes a
|
|
16
|
-
* machine-readable stdout if a script captures it.
|
|
17
|
-
*/
|
|
18
11
|
export declare function runCli(cmd: string, args: string[]): Promise<number>;
|
package/dist/src/lib/act.d.ts
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
* Run an `extension <verb> … --output json` act command and return its result
|
|
3
|
-
* JSON as a string (the MCP tool payload). The CLI prints the control-channel
|
|
4
|
-
* result frame to stdout (for ok and !ok alike); connection/no-session failures
|
|
5
|
-
* go to stderr with a non-zero exit. Either way we return a JSON object.
|
|
6
|
-
*
|
|
7
|
-
* Per lockstep invariant #1 the act tools wrap the CLI verb rather than talking
|
|
8
|
-
* to the control WS directly — so MCP behavior can never drift from the CLI.
|
|
9
|
-
*/
|
|
1
|
+
export declare function toMcpSpeak(text: string): string;
|
|
10
2
|
export declare function runActVerb(args: string[], projectPath: string, timeoutMs?: number): Promise<string>;
|
|
11
|
-
/** Shared input fields for act tools. */
|
|
12
3
|
export interface ActArgs {
|
|
13
4
|
projectPath: string;
|
|
14
5
|
browser?: string;
|
|
@@ -17,5 +8,4 @@ export interface ActArgs {
|
|
|
17
8
|
tab?: number;
|
|
18
9
|
timeout?: number;
|
|
19
10
|
}
|
|
20
|
-
/** Build the trailing CLI flags common to act verbs. */
|
|
21
11
|
export declare function commonFlags(args: ActArgs): string[];
|
package/dist/src/lib/exec.d.ts
CHANGED
|
@@ -4,17 +4,6 @@ export interface CliResult {
|
|
|
4
4
|
stdout: string;
|
|
5
5
|
stderr: string;
|
|
6
6
|
}
|
|
7
|
-
/**
|
|
8
|
-
* Resolve how to invoke the `extension` CLI for a given project.
|
|
9
|
-
*
|
|
10
|
-
* Preference order:
|
|
11
|
-
* 1. The project's own `node_modules/.bin/extension` — the version the project
|
|
12
|
-
* pinned is the single source of behavior for that project (lockstep
|
|
13
|
-
* invariant), and spawning it needs no network.
|
|
14
|
-
* 2. `npx extension@<pinned>` — pinned to the extension-develop version this
|
|
15
|
-
* package vendors, never a floating `latest`, so MCP behavior stays
|
|
16
|
-
* reproducible even without a project-local install.
|
|
17
|
-
*/
|
|
18
7
|
export declare function resolveExtensionInvocation(projectDir?: string): {
|
|
19
8
|
command: string;
|
|
20
9
|
prefixArgs: string[];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface ResolvedBrowser {
|
|
2
|
+
browser: string;
|
|
3
|
+
source: "explicit" | "session" | "contract" | "fallback";
|
|
4
|
+
}
|
|
5
|
+
export declare function knownSessionBrowsers(projectPath: string): string[];
|
|
6
|
+
export declare function resolveSessionBrowser(projectPath: string, explicit: string | undefined, fallback?: string): ResolvedBrowser;
|
|
@@ -2,10 +2,5 @@ export interface InputIssue {
|
|
|
2
2
|
path: string;
|
|
3
3
|
message: string;
|
|
4
4
|
}
|
|
5
|
-
/**
|
|
6
|
-
* Validate tools/call arguments against a tool inputSchema. Returns an empty
|
|
7
|
-
* array when the args are acceptable.
|
|
8
|
-
*/
|
|
9
5
|
export declare function validateToolInput(inputSchema: Record<string, unknown>, args: Record<string, unknown>): InputIssue[];
|
|
10
|
-
/** Frozen-style error envelope for input validation failures. */
|
|
11
6
|
export declare function inputValidationError(toolName: string, issues: InputIssue[]): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const schema: {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object";
|
|
6
|
+
properties: {
|
|
7
|
+
projectPath: {
|
|
8
|
+
type: string;
|
|
9
|
+
description: string;
|
|
10
|
+
};
|
|
11
|
+
browser: {
|
|
12
|
+
type: string;
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
required: string[];
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export declare function handler(args: {
|
|
20
|
+
projectPath: string;
|
|
21
|
+
browser?: string;
|
|
22
|
+
}): Promise<string>;
|
package/dist/src/tools/eval.d.ts
CHANGED
package/dist/src/tools/open.d.ts
CHANGED
package/dist/src/tools/wait.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@extension.dev/mcp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "4.
|
|
5
|
-
"description": "MCP server that lets AI agents build, run, inspect, and publish browser extensions.
|
|
4
|
+
"version": "4.3.0",
|
|
5
|
+
"description": "MCP server that lets AI agents (Claude Code, Claude Desktop, Cursor, Copilot, Codex) build, run, inspect, and publish browser extensions. 29 tools for scaffolding, live DOM inspection, log streaming, and store-ready builds across Chrome, Edge, and Firefox. Powered by extension.dev and Extension.js.",
|
|
6
|
+
"mcpName": "io.github.extensiondev/mcp",
|
|
6
7
|
"license": "MIT",
|
|
7
8
|
"author": {
|
|
8
9
|
"name": "Cezar Augusto",
|
|
@@ -16,11 +17,10 @@
|
|
|
16
17
|
"bugs": {
|
|
17
18
|
"url": "https://github.com/extensiondev/mcp/issues"
|
|
18
19
|
},
|
|
19
|
-
"homepage": "https://
|
|
20
|
+
"homepage": "https://extension.dev",
|
|
20
21
|
"engines": {
|
|
21
22
|
"node": ">=20.18"
|
|
22
23
|
},
|
|
23
|
-
"packageManager": "pnpm@10.19.0",
|
|
24
24
|
"exports": {
|
|
25
25
|
".": {
|
|
26
26
|
"types": "./dist/module.d.ts",
|
|
@@ -36,25 +36,19 @@
|
|
|
36
36
|
"dist",
|
|
37
37
|
"bin",
|
|
38
38
|
"claude",
|
|
39
|
+
".claude-plugin",
|
|
40
|
+
"server.json",
|
|
39
41
|
"README.md",
|
|
40
42
|
"CHANGELOG.md",
|
|
41
43
|
"LICENSE"
|
|
42
44
|
],
|
|
43
|
-
"scripts": {
|
|
44
|
-
"clean": "rm -rf dist",
|
|
45
|
-
"watch": "rslib build --watch",
|
|
46
|
-
"compile": "rslib build",
|
|
47
|
-
"build": "pnpm run compile",
|
|
48
|
-
"start": "node bin/extension-mcp.js",
|
|
49
|
-
"test": "vitest run",
|
|
50
|
-
"prepublishOnly": "pnpm run test && pnpm run compile"
|
|
51
|
-
},
|
|
52
45
|
"publishConfig": {
|
|
53
46
|
"access": "public",
|
|
54
47
|
"registry": "https://registry.npmjs.org"
|
|
55
48
|
},
|
|
56
49
|
"keywords": [
|
|
57
50
|
"mcp",
|
|
51
|
+
"mcp-server",
|
|
58
52
|
"model-context-protocol",
|
|
59
53
|
"ai-agent",
|
|
60
54
|
"agents",
|
|
@@ -62,14 +56,18 @@
|
|
|
62
56
|
"claude-code",
|
|
63
57
|
"claude-desktop",
|
|
64
58
|
"cursor",
|
|
59
|
+
"github-copilot",
|
|
60
|
+
"codex",
|
|
65
61
|
"browser-extension",
|
|
66
62
|
"chrome-extension",
|
|
67
63
|
"firefox-addon",
|
|
64
|
+
"edge-extension",
|
|
68
65
|
"manifest-v3",
|
|
69
66
|
"webextension",
|
|
70
67
|
"cross-browser",
|
|
71
68
|
"developer-tools",
|
|
72
|
-
"extension.dev"
|
|
69
|
+
"extension.dev",
|
|
70
|
+
"extension.js"
|
|
73
71
|
],
|
|
74
72
|
"dependencies": {
|
|
75
73
|
"@extension.dev/core": "^0.2.0",
|
|
@@ -87,5 +85,13 @@
|
|
|
87
85
|
"@types/ws": "^8.18.1",
|
|
88
86
|
"typescript": "6.0.2",
|
|
89
87
|
"vitest": "^4.1.3"
|
|
88
|
+
},
|
|
89
|
+
"scripts": {
|
|
90
|
+
"clean": "rm -rf dist",
|
|
91
|
+
"watch": "rslib build --watch",
|
|
92
|
+
"compile": "rslib build",
|
|
93
|
+
"build": "pnpm run compile",
|
|
94
|
+
"start": "node bin/extension-mcp.js",
|
|
95
|
+
"test": "vitest run"
|
|
90
96
|
}
|
|
91
|
-
}
|
|
97
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
|
3
|
+
"name": "io.github.extensiondev/mcp",
|
|
4
|
+
"description": "Build, run, inspect, and publish browser extensions from any MCP client. 29 tools for scaffolding, live DOM inspection, log streaming, and store-ready builds across Chrome, Edge, and Firefox.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"url": "https://github.com/extensiondev/mcp",
|
|
7
|
+
"source": "github"
|
|
8
|
+
},
|
|
9
|
+
"websiteUrl": "https://extension.dev",
|
|
10
|
+
"version": "4.3.0",
|
|
11
|
+
"packages": [
|
|
12
|
+
{
|
|
13
|
+
"registryType": "npm",
|
|
14
|
+
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
|
+
"identifier": "@extension.dev/mcp",
|
|
16
|
+
"version": "4.3.0",
|
|
17
|
+
"runtimeHint": "npx",
|
|
18
|
+
"transport": {
|
|
19
|
+
"type": "stdio"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|