@extension.dev/mcp 4.2.0 → 4.2.1
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/bin/extension-mcp.js +0 -0
- package/dist/module.js +74 -57
- package/dist/src/lib/cdp-port.d.ts +7 -0
- package/package.json +12 -10
package/bin/extension-mcp.js
CHANGED
|
File without changes
|
package/dist/module.js
CHANGED
|
@@ -9,8 +9,8 @@ import node_os from "node:os";
|
|
|
9
9
|
import { extensionBuild } from "extension-develop";
|
|
10
10
|
import cross_spawn from "cross-spawn";
|
|
11
11
|
import { filterKeysForThisBrowser } from "browser-extension-manifest-fields";
|
|
12
|
-
import node_net from "node:net";
|
|
13
12
|
import ws from "ws";
|
|
13
|
+
import node_http from "node:http";
|
|
14
14
|
import { clearCredentials, exchangeAndPersist, fetchLoginConfig, pollForToken, publish, readCredentials, resolveApiBase, resolveToken, safeApiBase, startDeviceCode } from "@extension.dev/core";
|
|
15
15
|
import { extensionInstall, getManagedBrowsersCacheRoot } from "extension-install";
|
|
16
16
|
import { execFile } from "node:child_process";
|
|
@@ -183,7 +183,7 @@ __webpack_require__.d(whoami_namespaceObject, {
|
|
|
183
183
|
handler: ()=>whoami_handler,
|
|
184
184
|
schema: ()=>whoami_schema
|
|
185
185
|
});
|
|
186
|
-
var package_namespaceObject = JSON.parse('{"rE":"4.2.
|
|
186
|
+
var package_namespaceObject = JSON.parse('{"rE":"4.2.1","El":{"OP":"^4.0.8"}}');
|
|
187
187
|
const create_schema = {
|
|
188
188
|
name: "extension_create",
|
|
189
189
|
description: "Create a new browser extension project from a template in the extension.dev template catalog. Use extension_list_templates to see available options.",
|
|
@@ -401,7 +401,8 @@ async function build_handler(args) {
|
|
|
401
401
|
const summary = await extensionBuild(args.projectPath, {
|
|
402
402
|
browser: args.browser ?? "chrome",
|
|
403
403
|
zip: args.zip ?? false,
|
|
404
|
-
zipSource: args.zipSource ?? false
|
|
404
|
+
zipSource: args.zipSource ?? false,
|
|
405
|
+
exitOnError: false
|
|
405
406
|
});
|
|
406
407
|
return JSON.stringify({
|
|
407
408
|
success: true,
|
|
@@ -417,6 +418,10 @@ async function build_handler(args) {
|
|
|
417
418
|
}
|
|
418
419
|
}
|
|
419
420
|
const PINNED_CLI_VERSION = String(package_namespaceObject.El.OP ?? "latest").replace(/^[\^~]/, "");
|
|
421
|
+
function pinnedCliVersion() {
|
|
422
|
+
const override = String(process.env.EXTENSION_MCP_CLI_VERSION || "").trim();
|
|
423
|
+
return override || PINNED_CLI_VERSION;
|
|
424
|
+
}
|
|
420
425
|
function resolveExtensionInvocation(projectDir) {
|
|
421
426
|
if (projectDir) {
|
|
422
427
|
const bin = node_path.join(projectDir, "node_modules", ".bin", "win32" === process.platform ? "extension.cmd" : "extension");
|
|
@@ -431,7 +436,7 @@ function resolveExtensionInvocation(projectDir) {
|
|
|
431
436
|
return {
|
|
432
437
|
command: "npx",
|
|
433
438
|
prefixArgs: [
|
|
434
|
-
`extension@${
|
|
439
|
+
`extension@${pinnedCliVersion()}`
|
|
435
440
|
]
|
|
436
441
|
};
|
|
437
442
|
}
|
|
@@ -1571,6 +1576,50 @@ class CDPClient extends CDPConnection {
|
|
|
1571
1576
|
return result ?? null;
|
|
1572
1577
|
}
|
|
1573
1578
|
}
|
|
1579
|
+
async function resolveCdpPort(projectPath, browser, options) {
|
|
1580
|
+
const waitMs = options?.waitMs ?? 20000;
|
|
1581
|
+
const readyPath = node_path.resolve(projectPath, "dist", "extension-js", browser, "ready.json");
|
|
1582
|
+
const deadline = Date.now() + waitMs;
|
|
1583
|
+
let contractSeen = false;
|
|
1584
|
+
for(;;){
|
|
1585
|
+
try {
|
|
1586
|
+
const contract = JSON.parse(node_fs.readFileSync(readyPath, "utf8"));
|
|
1587
|
+
contractSeen = true;
|
|
1588
|
+
if ("number" == typeof contract.cdpPort) return {
|
|
1589
|
+
port: contract.cdpPort,
|
|
1590
|
+
source: "contract"
|
|
1591
|
+
};
|
|
1592
|
+
} catch {
|
|
1593
|
+
if (!contractSeen) break;
|
|
1594
|
+
}
|
|
1595
|
+
if (Date.now() >= deadline) break;
|
|
1596
|
+
await new Promise((resolve)=>setTimeout(resolve, 500));
|
|
1597
|
+
}
|
|
1598
|
+
if (!contractSeen && await isCdpEndpoint(9222)) return {
|
|
1599
|
+
port: 9222,
|
|
1600
|
+
source: "default-probe"
|
|
1601
|
+
};
|
|
1602
|
+
return null;
|
|
1603
|
+
}
|
|
1604
|
+
const CDP_PORT_MISSING_HINT = "The session's ready contract has no CDP port (the browser may still be binding its debug port, or was launched without one). Confirm the session with extension_wait, give it a moment, and retry.";
|
|
1605
|
+
function isCdpEndpoint(port) {
|
|
1606
|
+
return new Promise((resolve)=>{
|
|
1607
|
+
const req = node_http.get({
|
|
1608
|
+
host: "127.0.0.1",
|
|
1609
|
+
port,
|
|
1610
|
+
path: "/json/version",
|
|
1611
|
+
timeout: 1000
|
|
1612
|
+
}, (res)=>{
|
|
1613
|
+
res.resume();
|
|
1614
|
+
resolve(200 === res.statusCode);
|
|
1615
|
+
});
|
|
1616
|
+
req.on("error", ()=>resolve(false));
|
|
1617
|
+
req.on("timeout", ()=>{
|
|
1618
|
+
req.destroy();
|
|
1619
|
+
resolve(false);
|
|
1620
|
+
});
|
|
1621
|
+
});
|
|
1622
|
+
}
|
|
1574
1623
|
const source_inspect_schema = {
|
|
1575
1624
|
name: "extension_source_inspect",
|
|
1576
1625
|
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.",
|
|
@@ -1632,28 +1681,6 @@ const source_inspect_schema = {
|
|
|
1632
1681
|
]
|
|
1633
1682
|
}
|
|
1634
1683
|
};
|
|
1635
|
-
async function findCdpPort(projectPath, browser) {
|
|
1636
|
-
const readyPath = node_path.resolve(projectPath, "dist", "extension-js", browser, "ready.json");
|
|
1637
|
-
try {
|
|
1638
|
-
const contract = JSON.parse(node_fs.readFileSync(readyPath, "utf8"));
|
|
1639
|
-
if ("number" == typeof contract.cdpPort) return contract.cdpPort;
|
|
1640
|
-
} catch {}
|
|
1641
|
-
const defaultPort = 9222;
|
|
1642
|
-
return new Promise((resolve)=>{
|
|
1643
|
-
const socket = new node_net.Socket();
|
|
1644
|
-
socket.setTimeout(1000);
|
|
1645
|
-
socket.on("connect", ()=>{
|
|
1646
|
-
socket.destroy();
|
|
1647
|
-
resolve(defaultPort);
|
|
1648
|
-
});
|
|
1649
|
-
socket.on("error", ()=>resolve(null));
|
|
1650
|
-
socket.on("timeout", ()=>{
|
|
1651
|
-
socket.destroy();
|
|
1652
|
-
resolve(null);
|
|
1653
|
-
});
|
|
1654
|
-
socket.connect(defaultPort, "127.0.0.1");
|
|
1655
|
-
});
|
|
1656
|
-
}
|
|
1657
1684
|
async function source_inspect_handler(args) {
|
|
1658
1685
|
const browser = args.browser ?? "chrome";
|
|
1659
1686
|
const include = new Set(args.include ?? [
|
|
@@ -1671,11 +1698,12 @@ async function source_inspect_handler(args) {
|
|
|
1671
1698
|
error: `Source inspection for ${browser} uses RDP (Remote Debug Protocol). Currently only Chromium CDP is supported.`,
|
|
1672
1699
|
hint: "Use --browser=chrome for source inspection, or use the CLI: npx extension dev --source"
|
|
1673
1700
|
});
|
|
1674
|
-
const
|
|
1675
|
-
if (!
|
|
1701
|
+
const resolved = await resolveCdpPort(args.projectPath, browser);
|
|
1702
|
+
if (!resolved) return JSON.stringify({
|
|
1676
1703
|
error: "No active dev session found. Cannot connect to Chrome DevTools Protocol.",
|
|
1677
|
-
hint:
|
|
1704
|
+
hint: `Start a dev session first with extension_dev, then use extension_wait to confirm it is ready. ${CDP_PORT_MISSING_HINT}`
|
|
1678
1705
|
});
|
|
1706
|
+
const cdpPort = resolved.port;
|
|
1679
1707
|
const cdp = new CDPClient();
|
|
1680
1708
|
try {
|
|
1681
1709
|
const allTargets = await CDPClient.discoverTargets(cdpPort);
|
|
@@ -1788,16 +1816,27 @@ async function list_extensions_handler(args) {
|
|
|
1788
1816
|
error: `Listing extensions for ${browser} uses RDP (Remote Debug Protocol). Currently only Chromium CDP is supported.`,
|
|
1789
1817
|
hint: "Use --browser=chrome."
|
|
1790
1818
|
});
|
|
1791
|
-
const
|
|
1792
|
-
if (!
|
|
1819
|
+
const resolved = await resolveCdpPort(args.projectPath, browser);
|
|
1820
|
+
if (!resolved) return JSON.stringify({
|
|
1793
1821
|
error: "No active dev session found. Cannot connect to Chrome DevTools Protocol.",
|
|
1794
|
-
hint:
|
|
1822
|
+
hint: `Start a dev session first with extension_dev, then use extension_wait to confirm it is ready. ${CDP_PORT_MISSING_HINT}`
|
|
1795
1823
|
});
|
|
1824
|
+
const cdpPort = resolved.port;
|
|
1796
1825
|
const cdp = new CDPClient();
|
|
1797
1826
|
try {
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1827
|
+
let targets = null;
|
|
1828
|
+
let lastError = null;
|
|
1829
|
+
for(let attempt = 0; attempt < 3; attempt++)try {
|
|
1830
|
+
const browserWsUrl = await CDPClient.discoverBrowserWsUrl(cdpPort);
|
|
1831
|
+
await cdp.connect(browserWsUrl);
|
|
1832
|
+
targets = await cdp.getTargets();
|
|
1833
|
+
break;
|
|
1834
|
+
} catch (error) {
|
|
1835
|
+
lastError = error;
|
|
1836
|
+
cdp.disconnect();
|
|
1837
|
+
if (attempt < 2) await new Promise((resolve)=>setTimeout(resolve, 800));
|
|
1838
|
+
}
|
|
1839
|
+
if (!targets) throw lastError;
|
|
1801
1840
|
const byId = new Map();
|
|
1802
1841
|
for (const t of targets){
|
|
1803
1842
|
const url = String(t.url ?? "");
|
|
@@ -1849,28 +1888,6 @@ async function list_extensions_handler(args) {
|
|
|
1849
1888
|
cdp.disconnect();
|
|
1850
1889
|
}
|
|
1851
1890
|
}
|
|
1852
|
-
async function list_extensions_findCdpPort(projectPath, browser) {
|
|
1853
|
-
const readyPath = node_path.resolve(projectPath, "dist", "extension-js", browser, "ready.json");
|
|
1854
|
-
try {
|
|
1855
|
-
const contract = JSON.parse(node_fs.readFileSync(readyPath, "utf8"));
|
|
1856
|
-
if ("number" == typeof contract.cdpPort) return contract.cdpPort;
|
|
1857
|
-
} catch {}
|
|
1858
|
-
const defaultPort = 9222;
|
|
1859
|
-
return new Promise((resolve)=>{
|
|
1860
|
-
const socket = new node_net.Socket();
|
|
1861
|
-
socket.setTimeout(1000);
|
|
1862
|
-
socket.on("connect", ()=>{
|
|
1863
|
-
socket.destroy();
|
|
1864
|
-
resolve(defaultPort);
|
|
1865
|
-
});
|
|
1866
|
-
socket.on("error", ()=>resolve(null));
|
|
1867
|
-
socket.on("timeout", ()=>{
|
|
1868
|
-
socket.destroy();
|
|
1869
|
-
resolve(null);
|
|
1870
|
-
});
|
|
1871
|
-
socket.connect(defaultPort, "127.0.0.1");
|
|
1872
|
-
});
|
|
1873
|
-
}
|
|
1874
1891
|
const CONTROL_WS_PATH = "/extjs-control";
|
|
1875
1892
|
const LEVEL_ORDER = [
|
|
1876
1893
|
"error",
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function resolveCdpPort(projectPath: string, browser: string, options?: {
|
|
2
|
+
waitMs?: number;
|
|
3
|
+
}): Promise<{
|
|
4
|
+
port: number;
|
|
5
|
+
source: "contract" | "default-probe";
|
|
6
|
+
} | null>;
|
|
7
|
+
export declare const CDP_PORT_MISSING_HINT = "The session's ready contract has no CDP port (the browser may still be binding its debug port, or was launched without one). Confirm the session with extension_wait, give it a moment, and retry.";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@extension.dev/mcp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "4.2.
|
|
4
|
+
"version": "4.2.1",
|
|
5
5
|
"description": "MCP server that lets AI agents build, run, inspect, and publish browser extensions. 28 tools for scaffolding, live DOM inspection, log streaming, and store-ready builds across Chrome, Edge, and Firefox.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": {
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"engines": {
|
|
21
21
|
"node": ">=20.18"
|
|
22
22
|
},
|
|
23
|
+
"packageManager": "pnpm@10.19.0",
|
|
23
24
|
"exports": {
|
|
24
25
|
".": {
|
|
25
26
|
"types": "./dist/module.d.ts",
|
|
@@ -39,6 +40,15 @@
|
|
|
39
40
|
"CHANGELOG.md",
|
|
40
41
|
"LICENSE"
|
|
41
42
|
],
|
|
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
|
+
},
|
|
42
52
|
"publishConfig": {
|
|
43
53
|
"access": "public",
|
|
44
54
|
"registry": "https://registry.npmjs.org"
|
|
@@ -77,13 +87,5 @@
|
|
|
77
87
|
"@types/ws": "^8.18.1",
|
|
78
88
|
"typescript": "6.0.2",
|
|
79
89
|
"vitest": "^4.1.3"
|
|
80
|
-
},
|
|
81
|
-
"scripts": {
|
|
82
|
-
"clean": "rm -rf dist",
|
|
83
|
-
"watch": "rslib build --watch",
|
|
84
|
-
"compile": "rslib build",
|
|
85
|
-
"build": "pnpm run compile",
|
|
86
|
-
"start": "node bin/extension-mcp.js",
|
|
87
|
-
"test": "vitest run"
|
|
88
90
|
}
|
|
89
|
-
}
|
|
91
|
+
}
|