@extension.dev/mcp 10.10.2 → 10.10.3
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 +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +38 -0
- package/README.md +24 -0
- package/bin/extension-mcp.js +14 -4
- package/dist/module.d.ts +1 -0
- package/dist/module.js +374 -68
- package/dist/src/index.d.ts +3 -2
- package/dist/src/lib/approval-gate.d.ts +5 -1
- package/dist/src/lib/artifacts-api.d.ts +1 -0
- package/dist/src/lib/envelope.d.ts +1 -1
- package/dist/src/lib/tool-policy.d.ts +33 -0
- package/package.json +4 -4
- package/server.json +2 -2
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "extension-mcp",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "MCP tools for browser extension development: scaffold from 50+ 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": "10.10.
|
|
13
|
+
"version": "10.10.3",
|
|
14
14
|
"category": "development",
|
|
15
15
|
"author": {
|
|
16
16
|
"name": "Cezar Augusto"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "extension-mcp",
|
|
3
3
|
"description": "MCP tools for browser extension development: scaffold from 50+ 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": "10.10.
|
|
4
|
+
"version": "10.10.3",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cezar Augusto",
|
|
7
7
|
"email": "hello@extension.dev",
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 10.10.3
|
|
4
|
+
|
|
5
|
+
- The engine pin moves off the canary to Extension.js `4.1.31` stable:
|
|
6
|
+
`extension_dev`, `extension_start` and `extension_build` spawn that
|
|
7
|
+
release when a project has no engine of its own, and the nightly
|
|
8
|
+
engine-pin check reads it against `latest` again.
|
|
9
|
+
|
|
10
|
+
- A human now approves a real store submission and a promotion to stable
|
|
11
|
+
before either runs, by default. The first call answers
|
|
12
|
+
`approval-required` with an `approvalUrl` on extension.dev
|
|
13
|
+
(`/device/approve/<id>`) and an `approvalId`; once a workspace member
|
|
14
|
+
approves (an owner, for a submission), the same call with that
|
|
15
|
+
`approvalId` runs exactly that action, once. Promotions to other channels
|
|
16
|
+
and share revokes stay ungated unless `EXTENSION_DEV_APPROVAL_GATE=1`;
|
|
17
|
+
`EXTENSION_DEV_APPROVAL_GATE=0` turns the default off. Whatever the
|
|
18
|
+
setting, a platform that answers `APPROVAL_REQUIRED` now gets the same
|
|
19
|
+
approval flow instead of a bare failure.
|
|
20
|
+
|
|
21
|
+
- Two server flags narrow what an agent can reach. `--features=local`
|
|
22
|
+
or `--features=platform` (env `EXTENSION_DEV_FEATURES`) lists one tool
|
|
23
|
+
group; `local` alone is about 40% smaller. `--no-ship` (env
|
|
24
|
+
`EXTENSION_DEV_NO_SHIP`) hides `extension_publish` and
|
|
25
|
+
`extension_release_promote` and refuses a real submit, a share link and
|
|
26
|
+
a share revoke, while dry runs and listing keep working. A refused call
|
|
27
|
+
answers the new code `E_TOOL_DISABLED` with the flag to change. Both
|
|
28
|
+
default to everything on, so no existing setup changes.
|
|
29
|
+
- Every tool now carries MCP annotations (`readOnlyHint`,
|
|
30
|
+
`destructiveHint`, `idempotentHint`, `openWorldHint`) from one policy
|
|
31
|
+
table, so clients can auto-approve reads and ask before the rest. A
|
|
32
|
+
merged tool takes its worst action: `extension_shares` is destructive
|
|
33
|
+
because revoke is.
|
|
34
|
+
- A device-flow step waiting on a human (`extension_auth` login,
|
|
35
|
+
`extension_project_create`, `extension_workspace_create`) now answers
|
|
36
|
+
`ok: true` with `status: "authorization-pending"` and no error, instead
|
|
37
|
+
of `ok: false` with `E_AUTH_PENDING`, so clients stop rendering the
|
|
38
|
+
first login step as a failure. The approval link and code stay in
|
|
39
|
+
`hint` and `value`; `E_AUTH_PENDING` is retired.
|
|
40
|
+
|
|
3
41
|
## 10.10.2
|
|
4
42
|
|
|
5
43
|
- The engine pin moves to the Extension.js canary
|
package/README.md
CHANGED
|
@@ -75,6 +75,30 @@ Or install it as a plugin, the MCP server plus the `/extension`, `/extension-add
|
|
|
75
75
|
}
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
+
### Choosing what the agent can reach
|
|
79
|
+
|
|
80
|
+
Two flags (or environment variables) narrow the server before an agent sees it:
|
|
81
|
+
|
|
82
|
+
- `--features=local` exposes only the tools that work on this machine (create, run, inspect, build), which also cuts the tool list by about 40%. `--features=platform` exposes only the extension.dev account, share, release and store tools. Both are on by default. Env: `EXTENSION_DEV_FEATURES`.
|
|
83
|
+
- `--no-ship` keeps everything that stays on this machine and refuses every call that reaches other people: `extension_publish`, `extension_release_promote`, `extension_submit` with `dryRun: false`, `extension_preview_web` with `share: true`, and a share revoke. Dry runs and share listing still work. Env: `EXTENSION_DEV_NO_SHIP=1`.
|
|
84
|
+
|
|
85
|
+
A refused call answers `E_TOOL_DISABLED` with the flag to change.
|
|
86
|
+
|
|
87
|
+
A real store submission and a promotion to stable also wait for a person by default: the first call answers `approval-required` with a link on extension.dev, a workspace member approves exactly that action, and the same call with the returned `approvalId` runs it once. `EXTENSION_DEV_APPROVAL_GATE=1` extends this to every promotion and share revoke; `EXTENSION_DEV_APPROVAL_GATE=0` turns it off.
|
|
88
|
+
|
|
89
|
+
Every tool also carries MCP annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`), so clients can auto-approve reads and ask before the rest.
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"mcpServers": {
|
|
94
|
+
"extension-dev": {
|
|
95
|
+
"command": "npx",
|
|
96
|
+
"args": ["@extension.dev/mcp", "--no-ship"]
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
78
102
|
### Pair with the skill
|
|
79
103
|
|
|
80
104
|
This server gives agents hands; [`@extension.dev/skill`](https://www.npmjs.com/package/@extension.dev/skill) gives them judgment: the cross-browser rules, silent-failure gotchas, debugging playbooks, and store checklist, packaged in the open [Agent Skills](https://agentskills.io) format. With both installed, agents know to verify against the live browser instead of guessing, and these tools make that a one-call operation.
|
package/bin/extension-mcp.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// Apache License 2.0 (c) 2026 Cezar Augusto and the extension.dev collaborators
|
|
9
9
|
|
|
10
10
|
import {createRequire} from 'node:module'
|
|
11
|
-
import {startServer, runCli} from '../dist/module.js'
|
|
11
|
+
import {startServer, runCli, isServerFlag, resolveServerOptions} from '../dist/module.js'
|
|
12
12
|
|
|
13
13
|
const require = createRequire(import.meta.url)
|
|
14
14
|
const {version} = require('../package.json')
|
|
@@ -16,18 +16,28 @@ const {version} = require('../package.json')
|
|
|
16
16
|
const usage = `@extension.dev/mcp ${version}
|
|
17
17
|
|
|
18
18
|
Usage:
|
|
19
|
-
extension-mcp
|
|
19
|
+
extension-mcp [flags] Start the MCP server on stdio
|
|
20
20
|
extension-mcp login [flags] Device login at extension.dev
|
|
21
21
|
extension-mcp logout Remove the stored credentials
|
|
22
22
|
extension-mcp whoami Show the stored identity
|
|
23
23
|
extension-mcp release [...] Headless release commands
|
|
24
24
|
extension-mcp --version Print the version
|
|
25
|
+
|
|
26
|
+
Server flags:
|
|
27
|
+
--features=local,platform Tool groups to expose (default: both)
|
|
28
|
+
--no-ship Refuse publish, promote, real submits,
|
|
29
|
+
share links and share revokes
|
|
25
30
|
`
|
|
26
31
|
|
|
27
32
|
const [, , cmd, ...rest] = process.argv
|
|
28
33
|
|
|
29
|
-
if (cmd === undefined) {
|
|
30
|
-
|
|
34
|
+
if (cmd === undefined || isServerFlag(cmd)) {
|
|
35
|
+
const resolved = resolveServerOptions(process.argv.slice(2), process.env)
|
|
36
|
+
if (!resolved.ok) {
|
|
37
|
+
process.stderr.write(`${resolved.message}\n\n${usage}`)
|
|
38
|
+
process.exit(1)
|
|
39
|
+
}
|
|
40
|
+
startServer(resolved.options)
|
|
31
41
|
} else if (['login', 'logout', 'whoami', 'release'].includes(cmd)) {
|
|
32
42
|
runCli(cmd, rest)
|
|
33
43
|
.then((code) => process.exit(code))
|
package/dist/module.d.ts
CHANGED
package/dist/module.js
CHANGED
|
@@ -236,7 +236,7 @@ __webpack_require__.d(workspace_create_namespaceObject, {
|
|
|
236
236
|
handler: ()=>workspace_create_handler,
|
|
237
237
|
schema: ()=>workspace_create_schema
|
|
238
238
|
});
|
|
239
|
-
var package_namespaceObject = JSON.parse('{"rE":"10.10.
|
|
239
|
+
var package_namespaceObject = JSON.parse('{"rE":"10.10.3","El":{"OP":"4.1.31"}}');
|
|
240
240
|
const ENVELOPE_SCHEMA = 1;
|
|
241
241
|
const collectWarnings = (warnings)=>{
|
|
242
242
|
if (!warnings) return [];
|
|
@@ -1986,13 +1986,9 @@ function pendingEnvelope(start) {
|
|
|
1986
1986
|
const hasCompleteLink = complete.length > 0 && complete !== start.verificationUri;
|
|
1987
1987
|
const message = hasCompleteLink ? `Open ${complete} and approve creating the project (code ${start.userCode} is pre-filled), then call extension_project_create again with this deviceCode and the same arguments. If the page asks for a code, enter ${start.userCode} at ${start.verificationUri}.` : `Open ${start.verificationUri}, enter code ${start.userCode}, approve creating the project, then call extension_project_create again with this deviceCode and the same arguments.`;
|
|
1988
1988
|
return envelope_envelope({
|
|
1989
|
-
ok:
|
|
1989
|
+
ok: true,
|
|
1990
1990
|
command: project_create_COMMAND,
|
|
1991
1991
|
status: "authorization-pending",
|
|
1992
|
-
error: {
|
|
1993
|
-
code: "E_AUTH_PENDING",
|
|
1994
|
-
message
|
|
1995
|
-
},
|
|
1996
1992
|
value: {
|
|
1997
1993
|
userCode: start.userCode,
|
|
1998
1994
|
verificationUri: start.verificationUri,
|
|
@@ -2131,17 +2127,14 @@ async function project_create_handler(args) {
|
|
|
2131
2127
|
async function finishFromPoll(poll, ctx) {
|
|
2132
2128
|
if (!poll.ok) {
|
|
2133
2129
|
if ("pending" === poll.reason) return envelope_envelope({
|
|
2134
|
-
ok:
|
|
2130
|
+
ok: true,
|
|
2135
2131
|
command: project_create_COMMAND,
|
|
2136
2132
|
status: "authorization-pending",
|
|
2137
|
-
error: {
|
|
2138
|
-
code: "E_AUTH_PENDING",
|
|
2139
|
-
message: `Still waiting for approval at ${ctx.verificationUri}. Approve there, then call extension_project_create again with this same deviceCode.`
|
|
2140
|
-
},
|
|
2141
2133
|
value: {
|
|
2142
2134
|
verificationUri: ctx.verificationUri,
|
|
2143
2135
|
deviceCode: ctx.deviceCode
|
|
2144
|
-
}
|
|
2136
|
+
},
|
|
2137
|
+
hint: `Still waiting for approval at ${ctx.verificationUri}. Approve there, then call extension_project_create again with this same deviceCode.`
|
|
2145
2138
|
});
|
|
2146
2139
|
if ("denied" === poll.reason) return fail("CreateDenied", "Creating the project was denied at extension.dev/device.", "create-denied", "E_AUTH_DENIED");
|
|
2147
2140
|
if ("expired" === poll.reason) return fail("CreateExpired", "The device code expired. Run extension_project_create again to restart.", "create-expired", "E_AUTH_EXPIRED");
|
|
@@ -2332,13 +2325,9 @@ function workspace_create_pendingEnvelope(start) {
|
|
|
2332
2325
|
const hasCompleteLink = complete.length > 0 && complete !== start.verificationUri;
|
|
2333
2326
|
const message = hasCompleteLink ? `Open ${complete} and approve creating the workspace (code ${start.userCode} is pre-filled), then call ${workspace_create_COMMAND} again with this deviceCode and the same arguments. If the page asks for a code, enter ${start.userCode} at ${start.verificationUri}. The GitHub account that approves becomes the workspace owner.` : `Open ${start.verificationUri}, enter code ${start.userCode}, approve creating the workspace, then call ${workspace_create_COMMAND} again with this deviceCode and the same arguments. The GitHub account that approves becomes the workspace owner.`;
|
|
2334
2327
|
return envelope_envelope({
|
|
2335
|
-
ok:
|
|
2328
|
+
ok: true,
|
|
2336
2329
|
command: workspace_create_COMMAND,
|
|
2337
2330
|
status: "authorization-pending",
|
|
2338
|
-
error: {
|
|
2339
|
-
code: "E_AUTH_PENDING",
|
|
2340
|
-
message
|
|
2341
|
-
},
|
|
2342
2331
|
value: {
|
|
2343
2332
|
userCode: start.userCode,
|
|
2344
2333
|
verificationUri: start.verificationUri,
|
|
@@ -2419,17 +2408,14 @@ async function workspace_create_handler(args) {
|
|
|
2419
2408
|
async function workspace_create_finishFromPoll(poll, ctx) {
|
|
2420
2409
|
if (!poll.ok) {
|
|
2421
2410
|
if ("pending" === poll.reason) return envelope_envelope({
|
|
2422
|
-
ok:
|
|
2411
|
+
ok: true,
|
|
2423
2412
|
command: workspace_create_COMMAND,
|
|
2424
2413
|
status: "authorization-pending",
|
|
2425
|
-
error: {
|
|
2426
|
-
code: "E_AUTH_PENDING",
|
|
2427
|
-
message: `Still waiting for approval at ${ctx.verificationUri}. Approve there, then call ${workspace_create_COMMAND} again with this same deviceCode.`
|
|
2428
|
-
},
|
|
2429
2414
|
value: {
|
|
2430
2415
|
verificationUri: ctx.verificationUri,
|
|
2431
2416
|
deviceCode: ctx.deviceCode
|
|
2432
|
-
}
|
|
2417
|
+
},
|
|
2418
|
+
hint: `Still waiting for approval at ${ctx.verificationUri}. Approve there, then call ${workspace_create_COMMAND} again with this same deviceCode.`
|
|
2433
2419
|
});
|
|
2434
2420
|
if ("denied" === poll.reason) return workspace_create_fail("CreateDenied", "Creating the workspace was denied at extension.dev/device.", "create-denied", "E_AUTH_DENIED");
|
|
2435
2421
|
if ("expired" === poll.reason) return workspace_create_fail("CreateExpired", `The device code expired. Run ${workspace_create_COMMAND} again to restart.`, "create-expired", "E_AUTH_EXPIRED");
|
|
@@ -8204,6 +8190,9 @@ async function revokeArtifact(options) {
|
|
|
8204
8190
|
error: {
|
|
8205
8191
|
name: "SharesRevokeError",
|
|
8206
8192
|
status: res.status,
|
|
8193
|
+
..."string" == typeof data?.code ? {
|
|
8194
|
+
code: data.code
|
|
8195
|
+
} : {},
|
|
8207
8196
|
message: `Revoking ${options.artifactId} failed (${res.status}): ${data?.message || "unknown error"}`
|
|
8208
8197
|
}
|
|
8209
8198
|
};
|
|
@@ -9176,9 +9165,29 @@ const APPROVAL_GATE_ENV = "EXTENSION_DEV_APPROVAL_GATE";
|
|
|
9176
9165
|
const APPROVAL_REQUIRED_STATUS = "approval-required";
|
|
9177
9166
|
const APPROVAL_PENDING_STATUS = "approval-pending";
|
|
9178
9167
|
const APPROVAL_REJECTED_STATUS = "approval-rejected";
|
|
9179
|
-
function approvalGateEnabled() {
|
|
9168
|
+
function approvalGateEnabled(defaultOn = false) {
|
|
9180
9169
|
const raw = String(process.env[APPROVAL_GATE_ENV] || "").trim().toLowerCase();
|
|
9181
|
-
|
|
9170
|
+
if ("" === raw) return defaultOn;
|
|
9171
|
+
return "0" !== raw && "false" !== raw && "off" !== raw;
|
|
9172
|
+
}
|
|
9173
|
+
function platformRequiresApproval(res, data) {
|
|
9174
|
+
return 403 === res.status && !!data && "object" == typeof data && "APPROVAL_REQUIRED" === data.code;
|
|
9175
|
+
}
|
|
9176
|
+
async function requestApprovalAfterRefusal(input) {
|
|
9177
|
+
const gate = await evaluateApproval({
|
|
9178
|
+
...input,
|
|
9179
|
+
approvalId: void 0,
|
|
9180
|
+
enabled: true
|
|
9181
|
+
});
|
|
9182
|
+
return gate.blocked ? gate.envelope : envelope_envelope({
|
|
9183
|
+
ok: false,
|
|
9184
|
+
command: input.command,
|
|
9185
|
+
status: APPROVAL_REQUIRED_STATUS,
|
|
9186
|
+
error: {
|
|
9187
|
+
code: "E_APPROVAL_REQUIRED",
|
|
9188
|
+
message: "The platform requires an approval for this action."
|
|
9189
|
+
}
|
|
9190
|
+
});
|
|
9182
9191
|
}
|
|
9183
9192
|
function canonicalScope(scope) {
|
|
9184
9193
|
return Object.keys(scope).sort().map((key)=>{
|
|
@@ -9666,18 +9675,19 @@ async function revokeShare(args) {
|
|
|
9666
9675
|
const token = resolveToken({
|
|
9667
9676
|
project: args.project
|
|
9668
9677
|
});
|
|
9678
|
+
const gateInput = {
|
|
9679
|
+
command: "extension_shares",
|
|
9680
|
+
action: "extension_shares.revoke",
|
|
9681
|
+
scope: {
|
|
9682
|
+
artifactId: ref
|
|
9683
|
+
},
|
|
9684
|
+
description: `Permanently revoke share ${ref}, killing the link for everyone with no way to restore it.`,
|
|
9685
|
+
approvalId: args.approvalId,
|
|
9686
|
+
token: token || "",
|
|
9687
|
+
api: args.api
|
|
9688
|
+
};
|
|
9669
9689
|
if (token) {
|
|
9670
|
-
const gate = await evaluateApproval(
|
|
9671
|
-
command: "extension_shares",
|
|
9672
|
-
action: "extension_shares.revoke",
|
|
9673
|
-
scope: {
|
|
9674
|
-
artifactId: ref
|
|
9675
|
-
},
|
|
9676
|
-
description: `Permanently revoke share ${ref}, killing the link for everyone with no way to restore it.`,
|
|
9677
|
-
approvalId: args.approvalId,
|
|
9678
|
-
token,
|
|
9679
|
-
api: args.api
|
|
9680
|
-
});
|
|
9690
|
+
const gate = await evaluateApproval(gateInput);
|
|
9681
9691
|
if (gate.blocked) return gate.envelope;
|
|
9682
9692
|
}
|
|
9683
9693
|
const result = await revokeArtifact({
|
|
@@ -9703,6 +9713,7 @@ async function revokeShare(args) {
|
|
|
9703
9713
|
artifactId: ref
|
|
9704
9714
|
}
|
|
9705
9715
|
});
|
|
9716
|
+
if (token && !args.approvalId && 403 === result.error.status && "APPROVAL_REQUIRED" === result.error.code) return requestApprovalAfterRefusal(gateInput);
|
|
9706
9717
|
const isAuth = "SharesAuthError" === result.error.name;
|
|
9707
9718
|
return envelope_envelope({
|
|
9708
9719
|
ok: false,
|
|
@@ -15393,7 +15404,7 @@ async function release_promote_handler(args) {
|
|
|
15393
15404
|
const url = `${apiCheck.base}/api/cli/release/promote`;
|
|
15394
15405
|
const sourceChannel = args.sourceChannel ? String(args.sourceChannel).trim() : "";
|
|
15395
15406
|
const gateBrowsers = (Array.isArray(args.browsers) ? args.browsers : []).map((b)=>String(b).trim()).filter(Boolean);
|
|
15396
|
-
const
|
|
15407
|
+
const gateInput = {
|
|
15397
15408
|
command: "extension_release_promote",
|
|
15398
15409
|
action: "extension_release_promote",
|
|
15399
15410
|
scope: {
|
|
@@ -15412,6 +15423,10 @@ async function release_promote_handler(args) {
|
|
|
15412
15423
|
approvalId: args.approvalId,
|
|
15413
15424
|
token,
|
|
15414
15425
|
api: args.api
|
|
15426
|
+
};
|
|
15427
|
+
const gate = await evaluateApproval({
|
|
15428
|
+
...gateInput,
|
|
15429
|
+
enabled: approvalGateEnabled("stable" === channel)
|
|
15415
15430
|
});
|
|
15416
15431
|
if (gate.blocked) return gate.envelope;
|
|
15417
15432
|
const body = {
|
|
@@ -15456,6 +15471,7 @@ async function release_promote_handler(args) {
|
|
|
15456
15471
|
buildId
|
|
15457
15472
|
}
|
|
15458
15473
|
});
|
|
15474
|
+
if (!args.approvalId && platformRequiresApproval(res, data)) return requestApprovalAfterRefusal(gateInput);
|
|
15459
15475
|
const code = "string" == typeof data?.code ? data.code : void 0;
|
|
15460
15476
|
const enrich = {};
|
|
15461
15477
|
let hint = "";
|
|
@@ -16090,21 +16106,25 @@ async function submit_handler(args) {
|
|
|
16090
16106
|
};
|
|
16091
16107
|
if (args.channel) body.channel = String(args.channel).trim();
|
|
16092
16108
|
if (args.version) body.version = String(args.version).trim();
|
|
16109
|
+
const gateInput = {
|
|
16110
|
+
command: "extension_submit",
|
|
16111
|
+
action: "extension_submit",
|
|
16112
|
+
scope: {
|
|
16113
|
+
browsers: [
|
|
16114
|
+
...browsers
|
|
16115
|
+
].sort(),
|
|
16116
|
+
buildSha: buildSha.toLowerCase(),
|
|
16117
|
+
channel
|
|
16118
|
+
},
|
|
16119
|
+
description: `Submit build ${buildSha} to ${browsers.join(", ")} for store review.`,
|
|
16120
|
+
approvalId: args.approvalId,
|
|
16121
|
+
token,
|
|
16122
|
+
api: args.api
|
|
16123
|
+
};
|
|
16093
16124
|
if (!dryRun) {
|
|
16094
16125
|
const gate = await evaluateApproval({
|
|
16095
|
-
|
|
16096
|
-
|
|
16097
|
-
scope: {
|
|
16098
|
-
browsers: [
|
|
16099
|
-
...browsers
|
|
16100
|
-
].sort(),
|
|
16101
|
-
buildSha: buildSha.toLowerCase(),
|
|
16102
|
-
channel
|
|
16103
|
-
},
|
|
16104
|
-
description: `Submit build ${buildSha} to ${browsers.join(", ")} for store review.`,
|
|
16105
|
-
approvalId: args.approvalId,
|
|
16106
|
-
token,
|
|
16107
|
-
api: args.api
|
|
16126
|
+
...gateInput,
|
|
16127
|
+
enabled: approvalGateEnabled(true)
|
|
16108
16128
|
});
|
|
16109
16129
|
if (gate.blocked) return gate.envelope;
|
|
16110
16130
|
if (gate.approvalId) body.approvalId = gate.approvalId;
|
|
@@ -16144,6 +16164,7 @@ async function submit_handler(args) {
|
|
|
16144
16164
|
dryRun
|
|
16145
16165
|
}
|
|
16146
16166
|
});
|
|
16167
|
+
if (!dryRun && !args.approvalId && platformRequiresApproval(res, data)) return requestApprovalAfterRefusal(gateInput);
|
|
16147
16168
|
return submit_fail("SubmitError", `${dryRun ? "preflight" : "submit"} failed (${res.status}): ${data?.message || text || "unknown error"}`, "submit-failed", "E_PLATFORM");
|
|
16148
16169
|
}
|
|
16149
16170
|
const warnings = Array.isArray(data?.warnings) ? [
|
|
@@ -16793,13 +16814,9 @@ function login_pending(start) {
|
|
|
16793
16814
|
const hasCompleteLink = complete.length > 0 && complete !== start.verificationUri;
|
|
16794
16815
|
const message = hasCompleteLink ? `Open ${complete} and approve (code ${start.userCode} is pre-filled), then call extension_auth (action: login) again with this deviceCode and the same project. If the page asks for a code, enter ${start.userCode} at ${start.verificationUri}.` : `Open ${start.verificationUri} and enter code ${start.userCode}, then call extension_auth (action: login) again with this deviceCode and the same project.`;
|
|
16795
16816
|
return envelope_envelope({
|
|
16796
|
-
ok:
|
|
16817
|
+
ok: true,
|
|
16797
16818
|
command: "extension_auth",
|
|
16798
16819
|
status: "authorization-pending",
|
|
16799
|
-
error: {
|
|
16800
|
-
code: "E_AUTH_PENDING",
|
|
16801
|
-
message
|
|
16802
|
-
},
|
|
16803
16820
|
value: {
|
|
16804
16821
|
userCode: start.userCode,
|
|
16805
16822
|
verificationUri: start.verificationUri,
|
|
@@ -16818,13 +16835,9 @@ function login_pending(start) {
|
|
|
16818
16835
|
function resumePending(deviceCode, verificationUri) {
|
|
16819
16836
|
const message = `Still waiting for authorization. The one-click link and code from the previous response are still valid: open that link (or enter the code at ${verificationUri}), then call extension_auth (action: login) again with this same deviceCode and the same project.`;
|
|
16820
16837
|
return envelope_envelope({
|
|
16821
|
-
ok:
|
|
16838
|
+
ok: true,
|
|
16822
16839
|
command: "extension_auth",
|
|
16823
16840
|
status: "authorization-pending",
|
|
16824
|
-
error: {
|
|
16825
|
-
code: "E_AUTH_PENDING",
|
|
16826
|
-
message
|
|
16827
|
-
},
|
|
16828
16841
|
value: {
|
|
16829
16842
|
verificationUri,
|
|
16830
16843
|
deviceCode,
|
|
@@ -17843,6 +17856,288 @@ function inputValidationError(toolName, issues, inputSchema) {
|
|
|
17843
17856
|
}
|
|
17844
17857
|
});
|
|
17845
17858
|
}
|
|
17859
|
+
const FEATURE_GROUPS = [
|
|
17860
|
+
"local",
|
|
17861
|
+
"platform"
|
|
17862
|
+
];
|
|
17863
|
+
const FEATURES_ENV = "EXTENSION_DEV_FEATURES";
|
|
17864
|
+
const NO_SHIP_ENV = "EXTENSION_DEV_NO_SHIP";
|
|
17865
|
+
const reads = (openWorld = false)=>({
|
|
17866
|
+
readOnlyHint: true,
|
|
17867
|
+
destructiveHint: false,
|
|
17868
|
+
idempotentHint: true,
|
|
17869
|
+
openWorldHint: openWorld
|
|
17870
|
+
});
|
|
17871
|
+
const acts = (over = {})=>({
|
|
17872
|
+
readOnlyHint: false,
|
|
17873
|
+
destructiveHint: false,
|
|
17874
|
+
idempotentHint: false,
|
|
17875
|
+
openWorldHint: false,
|
|
17876
|
+
...over
|
|
17877
|
+
});
|
|
17878
|
+
const TOOL_POLICY = {
|
|
17879
|
+
extension_create: {
|
|
17880
|
+
group: "local",
|
|
17881
|
+
annotations: acts({
|
|
17882
|
+
openWorldHint: true
|
|
17883
|
+
})
|
|
17884
|
+
},
|
|
17885
|
+
extension_templates: {
|
|
17886
|
+
group: "local",
|
|
17887
|
+
annotations: reads(true)
|
|
17888
|
+
},
|
|
17889
|
+
extension_add_feature: {
|
|
17890
|
+
group: "local",
|
|
17891
|
+
annotations: reads()
|
|
17892
|
+
},
|
|
17893
|
+
extension_build: {
|
|
17894
|
+
group: "local",
|
|
17895
|
+
annotations: acts({
|
|
17896
|
+
idempotentHint: true
|
|
17897
|
+
})
|
|
17898
|
+
},
|
|
17899
|
+
extension_dev: {
|
|
17900
|
+
group: "local",
|
|
17901
|
+
annotations: acts()
|
|
17902
|
+
},
|
|
17903
|
+
extension_start: {
|
|
17904
|
+
group: "local",
|
|
17905
|
+
annotations: acts()
|
|
17906
|
+
},
|
|
17907
|
+
extension_wait: {
|
|
17908
|
+
group: "local",
|
|
17909
|
+
annotations: reads()
|
|
17910
|
+
},
|
|
17911
|
+
extension_stop: {
|
|
17912
|
+
group: "local",
|
|
17913
|
+
annotations: acts({
|
|
17914
|
+
idempotentHint: true
|
|
17915
|
+
})
|
|
17916
|
+
},
|
|
17917
|
+
extension_manifest_validate: {
|
|
17918
|
+
group: "local",
|
|
17919
|
+
annotations: reads()
|
|
17920
|
+
},
|
|
17921
|
+
extension_theme_verify: {
|
|
17922
|
+
group: "local",
|
|
17923
|
+
annotations: reads()
|
|
17924
|
+
},
|
|
17925
|
+
extension_analyze: {
|
|
17926
|
+
group: "local",
|
|
17927
|
+
annotations: reads()
|
|
17928
|
+
},
|
|
17929
|
+
extension_assert: {
|
|
17930
|
+
group: "local",
|
|
17931
|
+
annotations: acts({
|
|
17932
|
+
idempotentHint: true
|
|
17933
|
+
})
|
|
17934
|
+
},
|
|
17935
|
+
extension_inspect: {
|
|
17936
|
+
group: "local",
|
|
17937
|
+
annotations: acts({
|
|
17938
|
+
idempotentHint: true
|
|
17939
|
+
})
|
|
17940
|
+
},
|
|
17941
|
+
extension_dom_snapshot: {
|
|
17942
|
+
group: "local",
|
|
17943
|
+
annotations: reads()
|
|
17944
|
+
},
|
|
17945
|
+
extension_list_extensions: {
|
|
17946
|
+
group: "local",
|
|
17947
|
+
annotations: reads()
|
|
17948
|
+
},
|
|
17949
|
+
extension_logs: {
|
|
17950
|
+
group: "local",
|
|
17951
|
+
annotations: reads()
|
|
17952
|
+
},
|
|
17953
|
+
extension_doctor: {
|
|
17954
|
+
group: "local",
|
|
17955
|
+
annotations: reads()
|
|
17956
|
+
},
|
|
17957
|
+
extension_eval: {
|
|
17958
|
+
group: "local",
|
|
17959
|
+
annotations: acts({
|
|
17960
|
+
destructiveHint: true,
|
|
17961
|
+
openWorldHint: true
|
|
17962
|
+
})
|
|
17963
|
+
},
|
|
17964
|
+
extension_storage: {
|
|
17965
|
+
group: "local",
|
|
17966
|
+
annotations: acts({
|
|
17967
|
+
destructiveHint: true
|
|
17968
|
+
})
|
|
17969
|
+
},
|
|
17970
|
+
extension_reload: {
|
|
17971
|
+
group: "local",
|
|
17972
|
+
annotations: acts({
|
|
17973
|
+
idempotentHint: true
|
|
17974
|
+
})
|
|
17975
|
+
},
|
|
17976
|
+
extension_open: {
|
|
17977
|
+
group: "local",
|
|
17978
|
+
annotations: acts({
|
|
17979
|
+
openWorldHint: true
|
|
17980
|
+
})
|
|
17981
|
+
},
|
|
17982
|
+
extension_browsers: {
|
|
17983
|
+
group: "local",
|
|
17984
|
+
annotations: acts({
|
|
17985
|
+
destructiveHint: true,
|
|
17986
|
+
openWorldHint: true
|
|
17987
|
+
})
|
|
17988
|
+
},
|
|
17989
|
+
extension_auth: {
|
|
17990
|
+
group: "platform",
|
|
17991
|
+
annotations: acts({
|
|
17992
|
+
openWorldHint: true
|
|
17993
|
+
})
|
|
17994
|
+
},
|
|
17995
|
+
extension_workspace_create: {
|
|
17996
|
+
group: "platform",
|
|
17997
|
+
annotations: acts({
|
|
17998
|
+
openWorldHint: true
|
|
17999
|
+
})
|
|
18000
|
+
},
|
|
18001
|
+
extension_project_create: {
|
|
18002
|
+
group: "platform",
|
|
18003
|
+
annotations: acts({
|
|
18004
|
+
openWorldHint: true
|
|
18005
|
+
})
|
|
18006
|
+
},
|
|
18007
|
+
extension_preview_web: {
|
|
18008
|
+
group: "platform",
|
|
18009
|
+
annotations: acts({
|
|
18010
|
+
openWorldHint: true
|
|
18011
|
+
}),
|
|
18012
|
+
ships: (args)=>true === args.share
|
|
18013
|
+
},
|
|
18014
|
+
extension_shares: {
|
|
18015
|
+
group: "platform",
|
|
18016
|
+
annotations: acts({
|
|
18017
|
+
destructiveHint: true,
|
|
18018
|
+
openWorldHint: true
|
|
18019
|
+
}),
|
|
18020
|
+
ships: (args)=>"revoke" === args.action
|
|
18021
|
+
},
|
|
18022
|
+
extension_publish: {
|
|
18023
|
+
group: "platform",
|
|
18024
|
+
annotations: acts({
|
|
18025
|
+
openWorldHint: true
|
|
18026
|
+
}),
|
|
18027
|
+
ships: "always"
|
|
18028
|
+
},
|
|
18029
|
+
extension_release_status: {
|
|
18030
|
+
group: "platform",
|
|
18031
|
+
annotations: reads(true)
|
|
18032
|
+
},
|
|
18033
|
+
extension_release_promote: {
|
|
18034
|
+
group: "platform",
|
|
18035
|
+
annotations: acts({
|
|
18036
|
+
destructiveHint: true,
|
|
18037
|
+
openWorldHint: true
|
|
18038
|
+
}),
|
|
18039
|
+
ships: "always"
|
|
18040
|
+
},
|
|
18041
|
+
extension_submit: {
|
|
18042
|
+
group: "platform",
|
|
18043
|
+
annotations: acts({
|
|
18044
|
+
destructiveHint: true,
|
|
18045
|
+
openWorldHint: true
|
|
18046
|
+
}),
|
|
18047
|
+
ships: (args)=>false === args.dryRun
|
|
18048
|
+
}
|
|
18049
|
+
};
|
|
18050
|
+
const DEFAULT_SERVER_OPTIONS = {
|
|
18051
|
+
features: [
|
|
18052
|
+
...FEATURE_GROUPS
|
|
18053
|
+
],
|
|
18054
|
+
noShip: false
|
|
18055
|
+
};
|
|
18056
|
+
const truthy = (raw)=>{
|
|
18057
|
+
const value = String(raw ?? "").trim().toLowerCase();
|
|
18058
|
+
return "" !== value && "0" !== value && "false" !== value && "off" !== value;
|
|
18059
|
+
};
|
|
18060
|
+
function isServerFlag(arg) {
|
|
18061
|
+
return "--no-ship" === arg || "--features" === arg || arg.startsWith("--features=");
|
|
18062
|
+
}
|
|
18063
|
+
function resolveServerOptions(argv, env) {
|
|
18064
|
+
let rawFeatures = env[FEATURES_ENV];
|
|
18065
|
+
let noShip = truthy(env[NO_SHIP_ENV]);
|
|
18066
|
+
for(let i = 0; i < argv.length; i++){
|
|
18067
|
+
const arg = argv[i];
|
|
18068
|
+
if ("--no-ship" === arg) noShip = true;
|
|
18069
|
+
else if ("--features" === arg) rawFeatures = argv[++i] ?? "";
|
|
18070
|
+
else {
|
|
18071
|
+
if (!arg.startsWith("--features=")) return {
|
|
18072
|
+
ok: false,
|
|
18073
|
+
message: `Unknown flag "${arg}".`
|
|
18074
|
+
};
|
|
18075
|
+
rawFeatures = arg.slice(11);
|
|
18076
|
+
}
|
|
18077
|
+
}
|
|
18078
|
+
if (void 0 === rawFeatures || "" === rawFeatures.trim()) return {
|
|
18079
|
+
ok: true,
|
|
18080
|
+
options: {
|
|
18081
|
+
features: [
|
|
18082
|
+
...FEATURE_GROUPS
|
|
18083
|
+
],
|
|
18084
|
+
noShip
|
|
18085
|
+
}
|
|
18086
|
+
};
|
|
18087
|
+
const names = rawFeatures.split(",").map((name)=>name.trim().toLowerCase()).filter(Boolean);
|
|
18088
|
+
const unknown = names.filter((name)=>!FEATURE_GROUPS.includes(name));
|
|
18089
|
+
if (unknown.length) return {
|
|
18090
|
+
ok: false,
|
|
18091
|
+
message: `Unknown feature group ${unknown.map((n)=>`"${n}"`).join(", ")}. Use ${FEATURE_GROUPS.join(" or ")}, comma-separated.`
|
|
18092
|
+
};
|
|
18093
|
+
const features = FEATURE_GROUPS.filter((group)=>names.includes(group));
|
|
18094
|
+
return {
|
|
18095
|
+
ok: true,
|
|
18096
|
+
options: {
|
|
18097
|
+
features,
|
|
18098
|
+
noShip
|
|
18099
|
+
}
|
|
18100
|
+
};
|
|
18101
|
+
}
|
|
18102
|
+
function isToolListed(name, options) {
|
|
18103
|
+
const policy = TOOL_POLICY[name];
|
|
18104
|
+
if (!policy) return false;
|
|
18105
|
+
if (!options.features.includes(policy.group)) return false;
|
|
18106
|
+
return !(options.noShip && "always" === policy.ships);
|
|
18107
|
+
}
|
|
18108
|
+
function disabledToolEnvelope(name, args, options) {
|
|
18109
|
+
const policy = TOOL_POLICY[name];
|
|
18110
|
+
if (!policy) return null;
|
|
18111
|
+
if (!options.features.includes(policy.group)) return envelope_envelope({
|
|
18112
|
+
ok: false,
|
|
18113
|
+
command: name,
|
|
18114
|
+
status: "tool-disabled",
|
|
18115
|
+
error: {
|
|
18116
|
+
code: "E_TOOL_DISABLED",
|
|
18117
|
+
message: `${name} is in the "${policy.group}" feature group, which this server was started without.`
|
|
18118
|
+
},
|
|
18119
|
+
value: {
|
|
18120
|
+
group: policy.group,
|
|
18121
|
+
features: options.features
|
|
18122
|
+
},
|
|
18123
|
+
hint: `Add ${policy.group} to --features (or ${FEATURES_ENV}) in this server's MCP config, or ask the user to.`
|
|
18124
|
+
});
|
|
18125
|
+
const ships = "always" === policy.ships || "function" == typeof policy.ships && policy.ships(args);
|
|
18126
|
+
if (options.noShip && ships) return envelope_envelope({
|
|
18127
|
+
ok: false,
|
|
18128
|
+
command: name,
|
|
18129
|
+
status: "tool-disabled",
|
|
18130
|
+
error: {
|
|
18131
|
+
code: "E_TOOL_DISABLED",
|
|
18132
|
+
message: `${name} with these arguments reaches people outside this machine, and this server was started in no-ship mode.`
|
|
18133
|
+
},
|
|
18134
|
+
value: {
|
|
18135
|
+
noShip: true
|
|
18136
|
+
},
|
|
18137
|
+
hint: `Dry runs, previews without share, and listing still work. To ship, the user removes --no-ship (or ${NO_SHIP_ENV}) from this server's MCP config.`
|
|
18138
|
+
});
|
|
18139
|
+
return null;
|
|
18140
|
+
}
|
|
17846
18141
|
const tools = [
|
|
17847
18142
|
create_namespaceObject,
|
|
17848
18143
|
templates_namespaceObject,
|
|
@@ -17898,8 +18193,8 @@ function toolResultFrame(result) {
|
|
|
17898
18193
|
}
|
|
17899
18194
|
const toolMap = new Map();
|
|
17900
18195
|
for (const tool of tools)toolMap.set(tool.schema.name, tool);
|
|
17901
|
-
const SERVER_INSTRUCTIONS = "extension-dev runs, inspects, drives, builds and publishes browser extensions (Chrome, Edge, Firefox, Safari and the other Chromium and Gecko browsers) through Extension.js and extension.dev.\nWhen the ask is to run, start, wait for, watch, inspect, drive, test, debug or build a browser extension, search this server first and use its tools: extension_dev starts the dev session (allowEval: true also turns on control), extension_wait blocks until it is ready, extension_logs streams its console, extension_open opens a surface or a url, extension_dom_snapshot and extension_inspect read a live page, extension_eval runs code in a context, extension_build makes a store-ready bundle, extension_stop ends the session.\nThese replace hand-rolled ps, curl, remote-debugging-port lookups and CDP or Playwright scripts: the server already holds the session's debug port, the extension id and the session token.\nEvery tool answers one JSON envelope {ok, status, value, error, hint, warnings}; read hint and warnings before choosing the next call, and treat ok: false as the answer, not a transport error.";
|
|
17902
|
-
function createServer() {
|
|
18196
|
+
const SERVER_INSTRUCTIONS = "extension-dev runs, inspects, drives, builds and publishes browser extensions (Chrome, Edge, Firefox, Safari and the other Chromium and Gecko browsers) through Extension.js and extension.dev.\nWhen the ask is to run, start, wait for, watch, inspect, drive, test, debug or build a browser extension, search this server first and use its tools: extension_dev starts the dev session (allowEval: true also turns on control), extension_wait blocks until it is ready, extension_logs streams its console, extension_open opens a surface or a url, extension_dom_snapshot and extension_inspect read a live page, extension_eval runs code in a context, extension_build makes a store-ready bundle, extension_stop ends the session.\nThese replace hand-rolled ps, curl, remote-debugging-port lookups and CDP or Playwright scripts: the server already holds the session's debug port, the extension id and the session token.\nEvery tool answers one JSON envelope {ok, status, value, error, hint, warnings}; read hint and warnings before choosing the next call, and treat ok: false as the answer, not a transport error. status authorization-pending is ok: true and means a human approves at the link in hint before you call again; it is not done yet.";
|
|
18197
|
+
function createServer(options = DEFAULT_SERVER_OPTIONS) {
|
|
17903
18198
|
const server = new Server({
|
|
17904
18199
|
name: "extension-dev",
|
|
17905
18200
|
version: package_namespaceObject.rE
|
|
@@ -17910,10 +18205,11 @@ function createServer() {
|
|
|
17910
18205
|
instructions: SERVER_INSTRUCTIONS
|
|
17911
18206
|
});
|
|
17912
18207
|
server.setRequestHandler(ListToolsRequestSchema, async ()=>({
|
|
17913
|
-
tools: tools.map((t)=>({
|
|
18208
|
+
tools: tools.filter((t)=>isToolListed(t.schema.name, options)).map((t)=>({
|
|
17914
18209
|
name: t.schema.name,
|
|
17915
18210
|
description: t.schema.description,
|
|
17916
|
-
inputSchema: t.schema.inputSchema
|
|
18211
|
+
inputSchema: t.schema.inputSchema,
|
|
18212
|
+
annotations: TOOL_POLICY[t.schema.name].annotations
|
|
17917
18213
|
}))
|
|
17918
18214
|
}));
|
|
17919
18215
|
server.setRequestHandler(CallToolRequestSchema, async (request)=>{
|
|
@@ -17932,7 +18228,7 @@ function createServer() {
|
|
|
17932
18228
|
message: `Unknown tool: ${name}`
|
|
17933
18229
|
},
|
|
17934
18230
|
value: {
|
|
17935
|
-
availableTools: tools.map((t)=>t.schema.name)
|
|
18231
|
+
availableTools: tools.map((t)=>t.schema.name).filter((toolName)=>isToolListed(toolName, options))
|
|
17936
18232
|
}
|
|
17937
18233
|
})
|
|
17938
18234
|
}
|
|
@@ -17940,6 +18236,16 @@ function createServer() {
|
|
|
17940
18236
|
isError: true
|
|
17941
18237
|
};
|
|
17942
18238
|
const normalizedArgs = normalizeArgAliases(tool.schema.inputSchema, args ?? {});
|
|
18239
|
+
const disabled = disabledToolEnvelope(name, normalizedArgs, options);
|
|
18240
|
+
if (disabled) return {
|
|
18241
|
+
content: [
|
|
18242
|
+
{
|
|
18243
|
+
type: "text",
|
|
18244
|
+
text: disabled
|
|
18245
|
+
}
|
|
18246
|
+
],
|
|
18247
|
+
isError: true
|
|
18248
|
+
};
|
|
17943
18249
|
const issues = validateToolInput(tool.schema.inputSchema, normalizedArgs);
|
|
17944
18250
|
if (issues.length) return {
|
|
17945
18251
|
content: [
|
|
@@ -17976,9 +18282,9 @@ function createServer() {
|
|
|
17976
18282
|
});
|
|
17977
18283
|
return server;
|
|
17978
18284
|
}
|
|
17979
|
-
async function startServer() {
|
|
18285
|
+
async function startServer(options = DEFAULT_SERVER_OPTIONS) {
|
|
17980
18286
|
installCarrierExitCleanup();
|
|
17981
|
-
const server = createServer();
|
|
18287
|
+
const server = createServer(options);
|
|
17982
18288
|
const transport = new StdioServerTransport();
|
|
17983
18289
|
await server.connect(transport);
|
|
17984
18290
|
}
|
|
@@ -18074,4 +18380,4 @@ async function runCli(cmd, args) {
|
|
|
18074
18380
|
return 1;
|
|
18075
18381
|
}
|
|
18076
18382
|
var version_0 = package_namespaceObject.rE;
|
|
18077
|
-
export { runCli, startServer, version_0 as version };
|
|
18383
|
+
export { isServerFlag, resolveServerOptions, runCli, startServer, version_0 as version };
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
2
|
import { version } from "../package.json";
|
|
3
3
|
export { version };
|
|
4
|
+
import { type ServerOptions } from "./lib/tool-policy";
|
|
4
5
|
export interface ToolModule {
|
|
5
6
|
schema: {
|
|
6
7
|
name: string;
|
|
@@ -18,6 +19,6 @@ export declare function toolResultFrame(result: string): {
|
|
|
18
19
|
isError?: boolean;
|
|
19
20
|
};
|
|
20
21
|
export declare const SERVER_INSTRUCTIONS: string;
|
|
21
|
-
export declare function createServer(): Server;
|
|
22
|
-
export declare function startServer(): Promise<void>;
|
|
22
|
+
export declare function createServer(options?: ServerOptions): Server;
|
|
23
|
+
export declare function startServer(options?: ServerOptions): Promise<void>;
|
|
23
24
|
export declare function runCli(cmd: string, args: string[]): Promise<number>;
|
|
@@ -2,7 +2,11 @@ export declare const APPROVAL_GATE_ENV = "EXTENSION_DEV_APPROVAL_GATE";
|
|
|
2
2
|
export declare const APPROVAL_REQUIRED_STATUS = "approval-required";
|
|
3
3
|
export declare const APPROVAL_PENDING_STATUS = "approval-pending";
|
|
4
4
|
export declare const APPROVAL_REJECTED_STATUS = "approval-rejected";
|
|
5
|
-
export declare function approvalGateEnabled(): boolean;
|
|
5
|
+
export declare function approvalGateEnabled(defaultOn?: boolean): boolean;
|
|
6
|
+
export declare function platformRequiresApproval(res: {
|
|
7
|
+
status: number;
|
|
8
|
+
}, data: unknown): boolean;
|
|
9
|
+
export declare function requestApprovalAfterRefusal(input: ApprovalGateInput): Promise<string>;
|
|
6
10
|
export type ApprovalScope = Record<string, string | string[]>;
|
|
7
11
|
export declare function actionFingerprint(action: string, scope: ApprovalScope): string;
|
|
8
12
|
export interface ApprovalGateInput {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const ENVELOPE_SCHEMA: 1;
|
|
2
2
|
export declare const DECISION_D6 = "The MCP tool name owns the envelope `command` key; forking the key to `tool` would fork the schema the checksum test pins.";
|
|
3
|
-
export type ErrorCode = "E_AMBIGUOUS_TARGET" | "E_APPROVAL_PENDING" | "E_APPROVAL_REJECTED" | "E_APPROVAL_REQUIRED" | "E_AUTH_DENIED" | "E_AUTH_EXPIRED" | "E_AUTH_FAILED" | "
|
|
3
|
+
export type ErrorCode = "E_AMBIGUOUS_TARGET" | "E_APPROVAL_PENDING" | "E_APPROVAL_REJECTED" | "E_APPROVAL_REQUIRED" | "E_AUTH_DENIED" | "E_AUTH_EXPIRED" | "E_AUTH_FAILED" | "E_AUTH_REQUIRED" | "E_BAD_HOST_URL" | "E_BAD_MANIFEST" | "E_BAD_REQUEST" | "E_BRIDGE" | "E_BROWSER_EXITED" | "E_BROWSER_INSTALL" | "E_BROWSER_UNINSTALL" | "E_BUILD_FAILED" | "E_CARRIER_IN_DIST" | "E_CDP" | "E_CLI" | "E_COMPILE" | "E_CONFIG" | "E_CONTRACT_ERROR" | "E_CONTROL_CHANNEL" | "E_CONTROL_ENVELOPE" | "E_ENGINE_TOO_OLD" | "E_ENTRYPOINT_MISSING" | "E_FIRST_COMPILE" | "E_INPUT_VALIDATION" | "E_INTERNAL" | "E_INVALID_BUNDLE_ID" | "E_LOGS_MISSING" | "E_MANIFEST_BLOCKING" | "E_MANIFEST_NOT_FOUND" | "E_NAVIGATE_ERROR" | "E_NAVIGATE_FAILED" | "E_NETWORK" | "E_NO_CONTROL_CHANNEL" | "E_NO_DIST" | "E_NO_EXTENSION_ID" | "E_NO_MATCHING_TARGET" | "E_NO_REFERENCE_TEMPLATE" | "E_NO_SESSION" | "E_NO_SURFACE_DOCUMENT" | "E_NO_TARGET" | "E_NOT_ATTACHED" | "E_PARSE" | "E_PLATFORM" | "E_PREVIEW_HOST_UNREACHABLE" | "E_PROFILE_LOCKED" | "E_RDP" | "E_SAFARI_ONLY_OPTION" | "E_EVAL" | "E_SCAFFOLD_FAILED" | "E_SCAFFOLD_INCOMPLETE" | "E_SESSION_EXISTS" | "E_SESSION_EXITED" | "E_STALE_CONTRACT" | "E_SURFACE_DID_NOT_OPEN" | "E_TEMPLATE_FETCH" | "E_TEMPLATE_NOT_FOUND" | "E_TOOL_DISABLED" | "E_UNKNOWN_COMMAND" | "E_UNKNOWN_TOOL" | "E_UNSUPPORTED_BROWSER" | "E_WAIT_TIMEOUT";
|
|
4
4
|
export declare const ERROR_CODES: ErrorCode[];
|
|
5
5
|
export interface EnvelopeError {
|
|
6
6
|
code: ErrorCode | string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type FeatureGroup = "local" | "platform";
|
|
2
|
+
export declare const FEATURE_GROUPS: FeatureGroup[];
|
|
3
|
+
export declare const FEATURES_ENV = "EXTENSION_DEV_FEATURES";
|
|
4
|
+
export declare const NO_SHIP_ENV = "EXTENSION_DEV_NO_SHIP";
|
|
5
|
+
export interface ToolAnnotations {
|
|
6
|
+
readOnlyHint: boolean;
|
|
7
|
+
destructiveHint: boolean;
|
|
8
|
+
idempotentHint: boolean;
|
|
9
|
+
openWorldHint: boolean;
|
|
10
|
+
}
|
|
11
|
+
type Args = Record<string, unknown>;
|
|
12
|
+
export interface ToolPolicy {
|
|
13
|
+
group: FeatureGroup;
|
|
14
|
+
annotations: ToolAnnotations;
|
|
15
|
+
ships?: "always" | ((args: Args) => boolean);
|
|
16
|
+
}
|
|
17
|
+
export declare const TOOL_POLICY: Record<string, ToolPolicy>;
|
|
18
|
+
export interface ServerOptions {
|
|
19
|
+
features: FeatureGroup[];
|
|
20
|
+
noShip: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare const DEFAULT_SERVER_OPTIONS: ServerOptions;
|
|
23
|
+
export declare function isServerFlag(arg: string): boolean;
|
|
24
|
+
export declare function resolveServerOptions(argv: string[], env: Record<string, string | undefined>): {
|
|
25
|
+
ok: true;
|
|
26
|
+
options: ServerOptions;
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
message: string;
|
|
30
|
+
};
|
|
31
|
+
export declare function isToolListed(name: string, options: ServerOptions): boolean;
|
|
32
|
+
export declare function disabledToolEnvelope(name: string, args: Args, options: ServerOptions): string | null;
|
|
33
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@extension.dev/mcp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "10.10.
|
|
4
|
+
"version": "10.10.3",
|
|
5
5
|
"description": "MCP server that lets AI agents (Claude Code, Claude Desktop, Cursor, Copilot, Codex) build, run, inspect, and publish browser extensions. 31 tools for scaffolding, live DOM inspection, log streaming, and store-ready builds across Chrome, Edge, Firefox, Safari, and every Chromium- or Gecko-based browser (Brave, Opera, Vivaldi, Yandex, Waterfox, LibreWolf). Powered by extension.dev and Extension.js.",
|
|
6
6
|
"mcpName": "io.github.extensiondev/mcp",
|
|
7
7
|
"license": "Apache-2.0",
|
|
@@ -91,9 +91,9 @@
|
|
|
91
91
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
92
92
|
"browser-extension-manifest-fields": "^2.2.9",
|
|
93
93
|
"cross-spawn": "^7.0.6",
|
|
94
|
-
"extension-create": "4.1.31
|
|
95
|
-
"extension-develop": "4.1.31
|
|
96
|
-
"extension-install": "4.1.31
|
|
94
|
+
"extension-create": "4.1.31",
|
|
95
|
+
"extension-develop": "4.1.31",
|
|
96
|
+
"extension-install": "4.1.31",
|
|
97
97
|
"ws": "^8.20.0"
|
|
98
98
|
},
|
|
99
99
|
"devDependencies": {
|
package/server.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
9
|
"websiteUrl": "https://extension.dev",
|
|
10
|
-
"version": "10.10.
|
|
10
|
+
"version": "10.10.3",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "@extension.dev/mcp",
|
|
16
|
-
"version": "10.10.
|
|
16
|
+
"version": "10.10.3",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|