@extension.dev/mcp 3.17.0-canary.1779910200.ddcd9eb → 4.0.8

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.
@@ -1 +1,9 @@
1
1
  export declare function startServer(): Promise<void>;
2
+ /**
3
+ * Human-facing subcommands for `extension-mcp <cmd>`. Unlike the MCP tools
4
+ * (which must return promptly and so resume across calls), the bin blocks on
5
+ * the device flow because there is a person watching the terminal. Returns a
6
+ * process exit code. All console output goes to stderr so it never pollutes a
7
+ * machine-readable stdout if a script captures it.
8
+ */
9
+ export declare function runCli(cmd: string, args: string[]): Promise<number>;
@@ -0,0 +1,20 @@
1
+ export declare class CDPConnection {
2
+ private ws;
3
+ private messageId;
4
+ private eventListeners;
5
+ private pendingRequests;
6
+ private consoleMessages;
7
+ connect(wsUrl: string): Promise<void>;
8
+ disconnect(): void;
9
+ private handleMessage;
10
+ private rejectAllPending;
11
+ sendCommand(method: string, params?: Record<string, unknown>, sessionId?: string): Promise<unknown>;
12
+ getConsoleMessages(): Array<{
13
+ level: string;
14
+ text: string;
15
+ source: string;
16
+ timestamp: number;
17
+ }>;
18
+ getConsoleSummary(): Record<string, unknown>;
19
+ protected onEvent(handler: (msg: Record<string, unknown>) => void): () => void;
20
+ }
@@ -0,0 +1,5 @@
1
+ export declare const PAGE_HTML_SCRIPT = "(() => {\n try {\n const doctype = document.doctype;\n const dt = doctype\n ? '<!DOCTYPE ' + doctype.name\n + (doctype.publicId ? ' PUBLIC \"' + doctype.publicId + '\"' : '')\n + (doctype.systemId ? ' \"' + doctype.systemId + '\"' : '')\n + '>'\n : '';\n // Include shadow DOM content from extension roots\n const roots = Array.from(document.querySelectorAll(\n '#extension-root,[data-extension-root]:not([data-extension-root=\"extension-js-devtools\"])'\n ));\n if (roots.length) {\n const clone = document.documentElement.cloneNode(true);\n const clonedRoots = Array.from(clone.querySelectorAll(\n '#extension-root,[data-extension-root]:not([data-extension-root=\"extension-js-devtools\"])'\n ));\n const s = new XMLSerializer();\n for (let i = 0; i < Math.min(roots.length, clonedRoots.length); i++) {\n const sr = roots[i].shadowRoot;\n if (!sr) continue;\n try {\n const shadow = Array.from(sr.childNodes).map(n => {\n try { return s.serializeToString(n); } catch { return ''; }\n }).join('');\n if (shadow) clonedRoots[i].innerHTML = shadow;\n } catch {}\n }\n return dt + '\\n' + clone.outerHTML;\n }\n return dt + '\\n' + document.documentElement.outerHTML;\n } catch (e) { return ''; }\n })()";
2
+ export declare const PAGE_META_SCRIPT = "(() => {\n try {\n return {\n title: document.title,\n url: location.href,\n readyState: document.readyState,\n viewport: {\n width: window.innerWidth,\n height: window.innerHeight,\n devicePixelRatio: window.devicePixelRatio\n },\n frameCount: window.frames.length,\n scriptCount: document.querySelectorAll('script').length,\n styleCount: document.querySelectorAll('style,link[rel=\"stylesheet\"]').length\n };\n } catch { return {}; }\n })()";
3
+ export declare const EXTENSION_ROOT_META_SCRIPT = "(() => {\n try {\n const readGeneration = (node) => {\n const raw = node.getAttribute && node.getAttribute('data-extjs-reinject-generation');\n const parsed = Number(raw);\n return Number.isFinite(parsed) ? parsed : undefined;\n };\n const normalize = (node) => ({\n tag: node.tagName ? String(node.tagName).toLowerCase() : 'unknown',\n id: node.id || undefined,\n key: node.getAttribute ? node.getAttribute('data-extjs-reinject-key') || undefined : undefined,\n generation: readGeneration(node),\n status: node.getAttribute ? node.getAttribute('data-extjs-reinject-status') || undefined : undefined\n });\n const roots = Array.from(\n document.querySelectorAll('#extension-root,[data-extension-root]:not([data-extension-root=\"extension-js-devtools\"])')\n ).slice(0, 10).map(normalize);\n const markers = Array.from(\n document.querySelectorAll('[data-extjs-reinject-marker=\"true\"]')\n ).slice(0, 10).map(normalize);\n if (!roots.length && !markers.length) return null;\n const generations = [...roots, ...markers]\n .map(e => e.generation)\n .filter(g => typeof g === 'number');\n return {\n rootCount: roots.length,\n markerCount: markers.length,\n latestGeneration: generations.length ? Math.max(...generations) : 0,\n roots,\n markers\n };\n } catch { return null; }\n })()";
4
+ export declare function probeSelectorsScript(selectors: string[]): string;
5
+ export declare function domSnapshotScript(maxNodes: number): string;
@@ -1,14 +1,5 @@
1
- export declare class CDPClient {
2
- private ws;
3
- private messageId;
4
- private eventListeners;
5
- private pendingRequests;
6
- private consoleMessages;
7
- connect(wsUrl: string): Promise<void>;
8
- disconnect(): void;
9
- private handleMessage;
10
- private rejectAllPending;
11
- sendCommand(method: string, params?: Record<string, unknown>, sessionId?: string): Promise<unknown>;
1
+ import { CDPConnection } from "./cdp-connection";
2
+ export declare class CDPClient extends CDPConnection {
12
3
  static discoverBrowserWsUrl(port: number, host?: string): Promise<string>;
13
4
  static discoverTargets(port: number, host?: string): Promise<Array<{
14
5
  id: string;
@@ -37,12 +28,4 @@ export declare class CDPClient {
37
28
  }>>;
38
29
  getDomSnapshot(sessionId: string, maxNodes?: number): Promise<Array<Record<string, unknown>>>;
39
30
  getExtensionRootMeta(sessionId: string): Promise<Record<string, unknown> | null>;
40
- getConsoleMessages(): Array<{
41
- level: string;
42
- text: string;
43
- source: string;
44
- timestamp: number;
45
- }>;
46
- getConsoleSummary(): Record<string, unknown>;
47
- private onEvent;
48
31
  }
@@ -0,0 +1,24 @@
1
+ export interface StoredCredentials {
2
+ version: 1;
3
+ /** The HMAC access token the publish path sends as `Bearer`. */
4
+ token: string;
5
+ workspaceSlug: string;
6
+ projectSlug: string;
7
+ /** Token expiry as unix epoch seconds (0 if unknown). */
8
+ expiresAt: number;
9
+ /** Platform base URL the token was minted against. */
10
+ api: string;
11
+ }
12
+ export declare function credentialsPath(): string;
13
+ export declare function readCredentials(): StoredCredentials | null;
14
+ export declare function writeCredentials(creds: StoredCredentials): string;
15
+ export declare function clearCredentials(): {
16
+ cleared: boolean;
17
+ path: string;
18
+ };
19
+ /**
20
+ * Return stored credentials only if the token has not expired. Used by the
21
+ * publish token resolution so an expired local token falls through cleanly to
22
+ * "no token" instead of producing a 401 from the platform.
23
+ */
24
+ export declare function readValidCredentials(nowSeconds?: number): StoredCredentials | null;
@@ -1,4 +1,4 @@
1
- import { type ChildProcess } from "node:child_process";
1
+ import type { ChildProcess } from "node:child_process";
2
2
  export interface CliResult {
3
3
  code: number | null;
4
4
  stdout: string;
@@ -0,0 +1,40 @@
1
+ export interface DeviceCodeStart {
2
+ deviceCode: string;
3
+ userCode: string;
4
+ verificationUri: string;
5
+ /** Minimum seconds to wait between polls. */
6
+ interval: number;
7
+ /** Seconds until the device code expires. */
8
+ expiresIn: number;
9
+ }
10
+ export type PollResult = {
11
+ ok: true;
12
+ githubToken: string;
13
+ } | {
14
+ ok: false;
15
+ reason: "pending" | "expired" | "denied";
16
+ error?: string;
17
+ };
18
+ type FetchImpl = typeof fetch;
19
+ type SleepImpl = (ms: number) => Promise<void>;
20
+ export declare function startDeviceCode(args: {
21
+ clientId: string;
22
+ scope?: string;
23
+ fetchImpl?: FetchImpl;
24
+ }): Promise<DeviceCodeStart>;
25
+ /**
26
+ * Poll for the access token until the budget elapses. A budget shorter than
27
+ * the device-code lifetime lets a caller (e.g. an MCP tool that must return
28
+ * promptly) poll in bounded slices and report "pending" between calls; a long
29
+ * budget (a blocking CLI) waits the whole time. Returns `pending` only on
30
+ * budget exhaustion; `expired`/`denied` are terminal.
31
+ */
32
+ export declare function pollForToken(args: {
33
+ clientId: string;
34
+ deviceCode: string;
35
+ interval: number;
36
+ budgetMs: number;
37
+ fetchImpl?: FetchImpl;
38
+ sleepImpl?: SleepImpl;
39
+ }): Promise<PollResult>;
40
+ export {};
@@ -0,0 +1,25 @@
1
+ import { type StoredCredentials } from "./credentials";
2
+ type FetchImpl = typeof fetch;
3
+ export declare function resolveApiBase(api?: string): string;
4
+ export interface LoginConfig {
5
+ clientId: string;
6
+ scope: string;
7
+ }
8
+ /**
9
+ * Resolve the GitHub OAuth client id the device flow authenticates against.
10
+ * EXTENSION_DEV_GITHUB_CLIENT_ID overrides (useful when pointing at a self-
11
+ * hosted platform); otherwise it comes from the platform's public config.
12
+ */
13
+ export declare function fetchLoginConfig(apiBase: string, fetchImpl?: FetchImpl): Promise<LoginConfig>;
14
+ /**
15
+ * Trade a verified GitHub user token for a project-scoped access token and
16
+ * write it to the local credentials file. Returns the stored credentials
17
+ * (token included for the caller's in-memory use; never logged).
18
+ */
19
+ export declare function exchangeAndPersist(args: {
20
+ apiBase: string;
21
+ githubToken: string;
22
+ project: string;
23
+ fetchImpl?: FetchImpl;
24
+ }): Promise<StoredCredentials>;
25
+ export {};
@@ -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
+ default: string;
14
+ };
15
+ };
16
+ required: string[];
17
+ };
18
+ };
19
+ export declare function handler(args: {
20
+ projectPath: string;
21
+ browser?: string;
22
+ }): Promise<string>;
@@ -0,0 +1,27 @@
1
+ export declare const schema: {
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: "object";
6
+ properties: {
7
+ project: {
8
+ type: string;
9
+ description: string;
10
+ };
11
+ deviceCode: {
12
+ type: string;
13
+ description: string;
14
+ };
15
+ api: {
16
+ type: string;
17
+ description: string;
18
+ };
19
+ };
20
+ required: string[];
21
+ };
22
+ };
23
+ export declare function handler(args: {
24
+ project: string;
25
+ deviceCode?: string;
26
+ api?: string;
27
+ }): Promise<string>;
@@ -0,0 +1,9 @@
1
+ export declare const schema: {
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: "object";
6
+ properties: {};
7
+ };
8
+ };
9
+ export declare function handler(): Promise<string>;
@@ -0,0 +1,6 @@
1
+ export declare const CONTROL_ENVELOPE_VERSION = 1;
2
+ export declare const CONTROL_WS_PATH = "/extjs-control";
3
+ export declare const DEFAULT_LIMIT = 200;
4
+ export declare const DEFAULT_FOLLOW_MS = 4000;
5
+ export declare const MIN_FOLLOW_MS = 500;
6
+ export declare const MAX_FOLLOW_MS = 15000;
@@ -0,0 +1,14 @@
1
+ export interface LogsArgs {
2
+ projectPath: string;
3
+ browser?: string;
4
+ level?: string;
5
+ context?: string[] | string;
6
+ signalsOnly?: boolean;
7
+ since?: number;
8
+ url?: string;
9
+ tab?: number;
10
+ follow?: boolean;
11
+ followMs?: number;
12
+ limit?: number;
13
+ }
14
+ export declare function makeFilter(args: LogsArgs): (event: any) => boolean;
@@ -0,0 +1,65 @@
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
+ default: string;
14
+ description: string;
15
+ };
16
+ level: {
17
+ type: string;
18
+ enum: string[];
19
+ default: string;
20
+ description: string;
21
+ };
22
+ context: {
23
+ type: string;
24
+ items: {
25
+ type: string;
26
+ enum: string[];
27
+ };
28
+ description: string;
29
+ };
30
+ signalsOnly: {
31
+ type: string;
32
+ default: boolean;
33
+ description: string;
34
+ };
35
+ since: {
36
+ type: string;
37
+ description: string;
38
+ };
39
+ url: {
40
+ type: string;
41
+ description: string;
42
+ };
43
+ tab: {
44
+ type: string;
45
+ description: string;
46
+ };
47
+ follow: {
48
+ type: string;
49
+ default: boolean;
50
+ description: string;
51
+ };
52
+ followMs: {
53
+ type: string;
54
+ default: number;
55
+ description: string;
56
+ };
57
+ limit: {
58
+ type: string;
59
+ default: number;
60
+ description: string;
61
+ };
62
+ };
63
+ required: string[];
64
+ };
65
+ };
@@ -1,80 +1,3 @@
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
- default: string;
14
- description: string;
15
- };
16
- level: {
17
- type: string;
18
- enum: string[];
19
- default: string;
20
- description: string;
21
- };
22
- context: {
23
- type: string;
24
- items: {
25
- type: string;
26
- enum: string[];
27
- };
28
- description: string;
29
- };
30
- signalsOnly: {
31
- type: string;
32
- default: boolean;
33
- description: string;
34
- };
35
- since: {
36
- type: string;
37
- description: string;
38
- };
39
- url: {
40
- type: string;
41
- description: string;
42
- };
43
- tab: {
44
- type: string;
45
- description: string;
46
- };
47
- follow: {
48
- type: string;
49
- default: boolean;
50
- description: string;
51
- };
52
- followMs: {
53
- type: string;
54
- default: number;
55
- description: string;
56
- };
57
- limit: {
58
- type: string;
59
- default: number;
60
- description: string;
61
- };
62
- };
63
- required: string[];
64
- };
65
- };
66
- interface LogsArgs {
67
- projectPath: string;
68
- browser?: string;
69
- level?: string;
70
- context?: string[] | string;
71
- signalsOnly?: boolean;
72
- since?: number;
73
- url?: string;
74
- tab?: number;
75
- follow?: boolean;
76
- followMs?: number;
77
- limit?: number;
78
- }
1
+ import { type LogsArgs } from "./logs-filter";
2
+ export { schema } from "./logs-schema";
79
3
  export declare function handler(args: LogsArgs): Promise<string>;
80
- export {};
@@ -14,6 +14,10 @@ export declare const schema: {
14
14
  enum: string[];
15
15
  description: string;
16
16
  };
17
+ name: {
18
+ type: string;
19
+ description: string;
20
+ };
17
21
  browser: {
18
22
  type: string;
19
23
  default: string;
@@ -28,4 +32,5 @@ export declare const schema: {
28
32
  };
29
33
  export declare function handler(args: ActArgs & {
30
34
  surface: string;
35
+ name?: string;
31
36
  }): Promise<string>;
@@ -24,6 +24,23 @@ export declare const schema: {
24
24
  required: string[];
25
25
  };
26
26
  };
27
+ /** Resolve the access token: EXTENSION_DEV_TOKEN env first, then the login creds file. */
28
+ export declare function resolveToken(): string;
29
+ /**
30
+ * Validate the platform base URL BEFORE we attach a bearer token to a request to
31
+ * it. SECURITY: the `api` arg / EXTENSION_DEV_API_URL is operator-supplied, but a
32
+ * hostile value (e.g. via prompt-injection in the client) could redirect the
33
+ * token to an attacker. The access token must never leave over plaintext or go to
34
+ * an arbitrary scheme, so we require https -- allowing http only for localhost
35
+ * dev. Returns the normalized base (no trailing slash) or an error message.
36
+ */
37
+ export declare function safeApiBase(raw: string): {
38
+ ok: true;
39
+ base: string;
40
+ } | {
41
+ ok: false;
42
+ message: string;
43
+ };
27
44
  export declare function handler(args: {
28
45
  projectPath: string;
29
46
  ttlHours?: number;
@@ -0,0 +1,50 @@
1
+ export declare const schema: {
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: "object";
6
+ properties: {
7
+ buildId: {
8
+ type: string;
9
+ description: string;
10
+ };
11
+ channel: {
12
+ type: string;
13
+ description: string;
14
+ };
15
+ sourceChannel: {
16
+ type: string;
17
+ description: string;
18
+ };
19
+ browsers: {
20
+ type: string;
21
+ items: {
22
+ type: string;
23
+ };
24
+ description: string;
25
+ };
26
+ version: {
27
+ type: string;
28
+ description: string;
29
+ };
30
+ releaseNotes: {
31
+ type: string;
32
+ description: string;
33
+ };
34
+ api: {
35
+ type: string;
36
+ description: string;
37
+ };
38
+ };
39
+ required: string[];
40
+ };
41
+ };
42
+ export declare function handler(args: {
43
+ buildId: string;
44
+ channel: string;
45
+ sourceChannel?: string;
46
+ browsers?: string[];
47
+ version?: string;
48
+ releaseNotes?: string;
49
+ api?: string;
50
+ }): Promise<string>;
@@ -0,0 +1,9 @@
1
+ export declare const schema: {
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: "object";
6
+ properties: {};
7
+ };
8
+ };
9
+ export declare function handler(): Promise<string>;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@extension.dev/mcp",
3
3
  "type": "module",
4
- "version": "3.17.0-canary.1779910200.ddcd9eb",
5
- "description": "MCP server for building, debugging, and publishing cross-browser extensions. Gives AI assistants programmatic access to the extension.dev platform and template catalog.",
4
+ "version": "4.0.8",
5
+ "description": "MCP server that lets AI agents build, run, inspect, and publish browser extensions. 26 tools for scaffolding, live DOM inspection, log streaming, and store-ready builds across Chrome, Edge, and Firefox.",
6
6
  "license": "MIT",
7
7
  "author": {
8
8
  "name": "Cezar Augusto",
@@ -11,12 +11,12 @@
11
11
  },
12
12
  "repository": {
13
13
  "type": "git",
14
- "url": "git+https://github.com/extensiondev/extension-dev.git"
14
+ "url": "git+https://github.com/extensiondev/mcp.git"
15
15
  },
16
16
  "bugs": {
17
- "url": "https://github.com/extensiondev/extension-dev/issues"
17
+ "url": "https://github.com/extensiondev/mcp/issues"
18
18
  },
19
- "homepage": "https://github.com/extensiondev/extension-dev#readme",
19
+ "homepage": "https://github.com/extensiondev/mcp#readme",
20
20
  "engines": {
21
21
  "node": ">=20.18"
22
22
  },
@@ -46,21 +46,28 @@
46
46
  "keywords": [
47
47
  "mcp",
48
48
  "model-context-protocol",
49
- "browser-extension",
50
- "extension.dev",
51
- "browser-extension-development",
52
- "extension-dev",
49
+ "ai-agent",
50
+ "agents",
53
51
  "claude",
54
52
  "claude-code",
55
- "ai-tools",
53
+ "claude-desktop",
54
+ "cursor",
55
+ "browser-extension",
56
+ "chrome-extension",
57
+ "firefox-addon",
58
+ "manifest-v3",
56
59
  "webextension",
57
- "cross-browser"
60
+ "cross-browser",
61
+ "developer-tools",
62
+ "extension.dev"
58
63
  ],
59
64
  "dependencies": {
60
65
  "@modelcontextprotocol/sdk": "^1.12.1",
61
- "extension-create": "^3.13.5",
62
- "extension-develop": "^3.13.5",
63
- "extension-install": "^3.13.5",
66
+ "browser-extension-manifest-fields": "^2.2.9",
67
+ "cross-spawn": "^7.0.6",
68
+ "extension-create": "^4.0.8",
69
+ "extension-develop": "^4.0.8",
70
+ "extension-install": "^4.0.8",
64
71
  "ws": "^8.20.0"
65
72
  },
66
73
  "devDependencies": {
@@ -68,7 +75,7 @@
68
75
  "@types/node": "^25.2.0",
69
76
  "@types/ws": "^8.18.1",
70
77
  "typescript": "6.0.2",
71
- "vitest": "3.2.2"
78
+ "vitest": "^4.1.3"
72
79
  },
73
80
  "scripts": {
74
81
  "clean": "rm -rf dist",
@@ -1,2 +0,0 @@
1
- declare const _default: import("@rslib/core").RslibConfig;
2
- export default _default;
@@ -1,3 +0,0 @@
1
- import type { UserConfig } from "vitest/config";
2
- declare const config: UserConfig;
3
- export default config;