@extension.dev/mcp 10.10.3 → 10.10.4
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 +17 -0
- package/README.md +70 -3
- package/bin/extension-mcp.js +1 -0
- package/dist/clients.js +157 -0
- package/dist/module.js +84 -11
- package/dist/src/clients/index.d.ts +31 -0
- package/dist/src/clients/readme.d.ts +4 -0
- package/dist/src/lib/credentials.d.ts +4 -0
- package/dist/src/lib/tool-policy.d.ts +6 -0
- package/package.json +6 -1
- 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.4",
|
|
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.4",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cezar Augusto",
|
|
7
7
|
"email": "hello@extension.dev",
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 10.10.4
|
|
4
|
+
|
|
5
|
+
- `--project <workspace>/<project>` (env `EXTENSION_DEV_PROJECT`) pins a
|
|
6
|
+
server to one project. Every unnamed credential read takes that
|
|
7
|
+
project's login instead of the most recent one, an agent signed in to
|
|
8
|
+
several projects can no longer act on the wrong one, and a call that
|
|
9
|
+
names a different project is refused with `status: "project-pinned"`.
|
|
10
|
+
A pinned project with no stored login falls back to
|
|
11
|
+
`EXTENSION_DEV_TOKEN`, so CI keeps working.
|
|
12
|
+
- New `@extension.dev/mcp/clients` export: `CLIENTS` and
|
|
13
|
+
`buildRecipe({ client, reach, strictApproval, project })` build the
|
|
14
|
+
exact setup for Claude Code, Cursor, VS Code, Codex and any
|
|
15
|
+
`mcpServers` client. The README's Setup section is generated from it
|
|
16
|
+
(`pnpm readme:clients`, checked by a spec), and the extension.dev
|
|
17
|
+
console's Connect dialog renders the same recipes, so the instructions
|
|
18
|
+
cannot drift. It never emits a setting that turns approvals off.
|
|
19
|
+
|
|
3
20
|
## 10.10.3
|
|
4
21
|
|
|
5
22
|
- The engine pin moves off the canary to Extension.js `4.1.31` stable:
|
package/README.md
CHANGED
|
@@ -45,10 +45,12 @@ Built on [Extension.js](https://extension.js.org), the open-source cross-browser
|
|
|
45
45
|
|
|
46
46
|
## Setup
|
|
47
47
|
|
|
48
|
+
<!-- setup:start (generated from src/clients, run pnpm readme:clients) -->
|
|
49
|
+
|
|
48
50
|
### Claude Code
|
|
49
51
|
|
|
50
52
|
```bash
|
|
51
|
-
claude mcp add extension-dev npx @extension.dev/mcp
|
|
53
|
+
claude mcp add extension-dev -- npx @extension.dev/mcp
|
|
52
54
|
```
|
|
53
55
|
|
|
54
56
|
Or install it as a plugin, the MCP server plus the `/extension`, `/extension-add`, `/extension-debug`, and `/extension-publish` commands in one step:
|
|
@@ -62,19 +64,84 @@ Or install it as a plugin, the MCP server plus the `/extension`, `/extension-add
|
|
|
62
64
|
|
|
63
65
|
[](https://cursor.com/en/install-mcp?name=extension-dev&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyJAZXh0ZW5zaW9uLmRldi9tY3AiXX0%3D)
|
|
64
66
|
|
|
65
|
-
|
|
67
|
+
`.cursor/mcp.json`:
|
|
66
68
|
|
|
67
69
|
```json
|
|
68
70
|
{
|
|
69
71
|
"mcpServers": {
|
|
70
72
|
"extension-dev": {
|
|
71
73
|
"command": "npx",
|
|
72
|
-
"args": [
|
|
74
|
+
"args": [
|
|
75
|
+
"@extension.dev/mcp"
|
|
76
|
+
]
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
80
|
```
|
|
77
81
|
|
|
82
|
+
### VS Code (Also GitHub Copilot)
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
code --add-mcp '{"name":"extension-dev","command":"npx","args":["@extension.dev/mcp"]}'
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`.vscode/mcp.json`:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"servers": {
|
|
93
|
+
"extension-dev": {
|
|
94
|
+
"type": "stdio",
|
|
95
|
+
"command": "npx",
|
|
96
|
+
"args": [
|
|
97
|
+
"@extension.dev/mcp"
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Codex
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
codex mcp add extension-dev -- npx @extension.dev/mcp
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`~/.codex/config.toml`:
|
|
111
|
+
|
|
112
|
+
```toml
|
|
113
|
+
[mcp_servers.extension-dev]
|
|
114
|
+
command = "npx"
|
|
115
|
+
args = ["@extension.dev/mcp"]
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Other clients (Claude Desktop and .mcp.json)
|
|
119
|
+
|
|
120
|
+
`.mcp.json`:
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"mcpServers": {
|
|
125
|
+
"extension-dev": {
|
|
126
|
+
"command": "npx",
|
|
127
|
+
"args": [
|
|
128
|
+
"@extension.dev/mcp"
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Signing in and pinning a project
|
|
136
|
+
|
|
137
|
+
The platform tools need a login. Sign in once per project, then add `--project <workspace>/<project>` to the server's arguments (or set `EXTENSION_DEV_PROJECT`) so that server only ever acts on that project, however many logins this machine holds. The console's Connect dialog fills both in for your project.
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npx @extension.dev/mcp login --project <workspace>/<project>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
<!-- setup:end -->
|
|
144
|
+
|
|
78
145
|
### Choosing what the agent can reach
|
|
79
146
|
|
|
80
147
|
Two flags (or environment variables) narrow the server before an agent sees it:
|
package/bin/extension-mcp.js
CHANGED
|
@@ -27,6 +27,7 @@ Server flags:
|
|
|
27
27
|
--features=local,platform Tool groups to expose (default: both)
|
|
28
28
|
--no-ship Refuse publish, promote, real submits,
|
|
29
29
|
share links and share revokes
|
|
30
|
+
--project <workspace>/<proj> Use only this project's login
|
|
30
31
|
`
|
|
31
32
|
|
|
32
33
|
const [, , cmd, ...rest] = process.argv
|
package/dist/clients.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
const CLIENTS = [
|
|
2
|
+
{
|
|
3
|
+
id: "claude-code",
|
|
4
|
+
label: "Claude Code"
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
id: "cursor",
|
|
8
|
+
label: "Cursor"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
id: "vscode",
|
|
12
|
+
label: "VS Code",
|
|
13
|
+
subtitle: "Also GitHub Copilot"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: "codex",
|
|
17
|
+
label: "Codex"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "json",
|
|
21
|
+
label: "Other clients",
|
|
22
|
+
subtitle: "Claude Desktop and .mcp.json"
|
|
23
|
+
}
|
|
24
|
+
];
|
|
25
|
+
const SERVER_NAME = "extension-dev";
|
|
26
|
+
const PACKAGE = "@extension.dev/mcp";
|
|
27
|
+
function serverArgs(input) {
|
|
28
|
+
const args = [
|
|
29
|
+
PACKAGE
|
|
30
|
+
];
|
|
31
|
+
if ("local" !== input.reach && input.project) args.push("--project", input.project);
|
|
32
|
+
if ("no-ship" === input.reach) args.push("--no-ship");
|
|
33
|
+
if ("local" === input.reach) args.push("--features=local");
|
|
34
|
+
return args;
|
|
35
|
+
}
|
|
36
|
+
function serverEnv(input) {
|
|
37
|
+
return "everything" === input.reach && input.strictApproval ? {
|
|
38
|
+
EXTENSION_DEV_APPROVAL_GATE: "1"
|
|
39
|
+
} : {};
|
|
40
|
+
}
|
|
41
|
+
const shellQuote = (value)=>/^[A-Za-z0-9_@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
|
|
42
|
+
const base64 = (text)=>"function" == typeof btoa ? btoa(text) : globalThis.Buffer.from(text).toString("base64");
|
|
43
|
+
const tomlString = (value)=>JSON.stringify(value);
|
|
44
|
+
function loginCommand(project) {
|
|
45
|
+
return `npx ${PACKAGE} login --project ${shellQuote(project)}`;
|
|
46
|
+
}
|
|
47
|
+
function buildRecipe(input) {
|
|
48
|
+
const args = serverArgs(input);
|
|
49
|
+
const env = serverEnv(input);
|
|
50
|
+
const envEntries = Object.entries(env);
|
|
51
|
+
const stdio = {
|
|
52
|
+
command: "npx",
|
|
53
|
+
args,
|
|
54
|
+
...envEntries.length ? {
|
|
55
|
+
env
|
|
56
|
+
} : {}
|
|
57
|
+
};
|
|
58
|
+
const login = loginCommand(input.project);
|
|
59
|
+
const argLine = args.map(shellQuote).join(" ");
|
|
60
|
+
switch(input.client){
|
|
61
|
+
case "claude-code":
|
|
62
|
+
return {
|
|
63
|
+
client: "claude-code",
|
|
64
|
+
command: [
|
|
65
|
+
"claude mcp add",
|
|
66
|
+
SERVER_NAME,
|
|
67
|
+
...envEntries.map(([k, v])=>`-e ${k}=${shellQuote(v)}`),
|
|
68
|
+
"-- npx",
|
|
69
|
+
argLine
|
|
70
|
+
].join(" "),
|
|
71
|
+
login
|
|
72
|
+
};
|
|
73
|
+
case "cursor":
|
|
74
|
+
return {
|
|
75
|
+
client: "cursor",
|
|
76
|
+
deeplink: `https://cursor.com/en/install-mcp?name=${SERVER_NAME}&config=${encodeURIComponent(base64(JSON.stringify(stdio)))}`,
|
|
77
|
+
config: {
|
|
78
|
+
language: "json",
|
|
79
|
+
path: ".cursor/mcp.json",
|
|
80
|
+
text: JSON.stringify({
|
|
81
|
+
mcpServers: {
|
|
82
|
+
[SERVER_NAME]: stdio
|
|
83
|
+
}
|
|
84
|
+
}, null, 2)
|
|
85
|
+
},
|
|
86
|
+
login
|
|
87
|
+
};
|
|
88
|
+
case "vscode":
|
|
89
|
+
{
|
|
90
|
+
const named = {
|
|
91
|
+
name: SERVER_NAME,
|
|
92
|
+
...stdio
|
|
93
|
+
};
|
|
94
|
+
return {
|
|
95
|
+
client: "vscode",
|
|
96
|
+
command: `code --add-mcp ${shellQuote(JSON.stringify(named))}`,
|
|
97
|
+
deeplink: `vscode:mcp/install?${encodeURIComponent(JSON.stringify(named))}`,
|
|
98
|
+
config: {
|
|
99
|
+
language: "json",
|
|
100
|
+
path: ".vscode/mcp.json",
|
|
101
|
+
text: JSON.stringify({
|
|
102
|
+
servers: {
|
|
103
|
+
[SERVER_NAME]: {
|
|
104
|
+
type: "stdio",
|
|
105
|
+
...stdio
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}, null, 2)
|
|
109
|
+
},
|
|
110
|
+
login
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
case "codex":
|
|
114
|
+
{
|
|
115
|
+
const lines = [
|
|
116
|
+
`[mcp_servers.${SERVER_NAME}]`,
|
|
117
|
+
'command = "npx"',
|
|
118
|
+
`args = [${args.map(tomlString).join(", ")}]`
|
|
119
|
+
];
|
|
120
|
+
if (envEntries.length) {
|
|
121
|
+
lines.push("", `[mcp_servers.${SERVER_NAME}.env]`);
|
|
122
|
+
for (const [k, v] of envEntries)lines.push(`${k} = ${tomlString(v)}`);
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
client: "codex",
|
|
126
|
+
command: [
|
|
127
|
+
"codex mcp add",
|
|
128
|
+
SERVER_NAME,
|
|
129
|
+
...envEntries.map(([k, v])=>`--env ${k}=${shellQuote(v)}`),
|
|
130
|
+
"-- npx",
|
|
131
|
+
argLine
|
|
132
|
+
].join(" "),
|
|
133
|
+
config: {
|
|
134
|
+
language: "toml",
|
|
135
|
+
path: "~/.codex/config.toml",
|
|
136
|
+
text: lines.join("\n")
|
|
137
|
+
},
|
|
138
|
+
login
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
case "json":
|
|
142
|
+
return {
|
|
143
|
+
client: "json",
|
|
144
|
+
config: {
|
|
145
|
+
language: "json",
|
|
146
|
+
path: ".mcp.json",
|
|
147
|
+
text: JSON.stringify({
|
|
148
|
+
mcpServers: {
|
|
149
|
+
[SERVER_NAME]: stdio
|
|
150
|
+
}
|
|
151
|
+
}, null, 2)
|
|
152
|
+
},
|
|
153
|
+
login
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
export { CLIENTS, PACKAGE, SERVER_NAME, buildRecipe, loginCommand, serverArgs, serverEnv };
|
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.4","El":{"OP":"4.1.31"}}');
|
|
240
240
|
const ENVELOPE_SCHEMA = 1;
|
|
241
241
|
const collectWarnings = (warnings)=>{
|
|
242
242
|
if (!warnings) return [];
|
|
@@ -509,8 +509,20 @@ function readCredentialStore() {
|
|
|
509
509
|
entries
|
|
510
510
|
};
|
|
511
511
|
}
|
|
512
|
+
const PROJECT_PIN_ENV = "EXTENSION_DEV_PROJECT";
|
|
513
|
+
function pinnedProject() {
|
|
514
|
+
return String(process.env[PROJECT_PIN_ENV] || "").trim().toLowerCase();
|
|
515
|
+
}
|
|
516
|
+
function isProjectRef(value) {
|
|
517
|
+
return /^[a-z0-9][a-z0-9-_.]*\/[a-z0-9][a-z0-9-_.]*$/i.test(value);
|
|
518
|
+
}
|
|
519
|
+
function sameProject(named, pinned) {
|
|
520
|
+
const a = named.trim().toLowerCase();
|
|
521
|
+
if (!a || !pinned) return true;
|
|
522
|
+
return a.includes("/") ? a === pinned : a === pinned.split("/")[1];
|
|
523
|
+
}
|
|
512
524
|
function selectEntry(store, selector) {
|
|
513
|
-
const wanted = String(selector?.project ?? "").trim().toLowerCase();
|
|
525
|
+
const wanted = (String(selector?.project ?? "").trim() || pinnedProject()).toLowerCase();
|
|
514
526
|
if (!wanted) return store.active && store.entries[store.active] || Object.values(store.entries)[0] || null;
|
|
515
527
|
if (wanted.includes("/")) return store.entries[wanted] ?? null;
|
|
516
528
|
const bySlug = Object.values(store.entries).filter((entry)=>entry.projectSlug.toLowerCase() === wanted);
|
|
@@ -7921,12 +7933,15 @@ async function openSidebarThroughGesture(projectPath, browser, refusal) {
|
|
|
7921
7933
|
return actFrameJson(refusal);
|
|
7922
7934
|
}
|
|
7923
7935
|
function resolveToken(options = {}) {
|
|
7924
|
-
const
|
|
7936
|
+
const named = String(options.project ?? "").trim();
|
|
7937
|
+
const project = named || pinnedProject();
|
|
7925
7938
|
if (project) {
|
|
7926
7939
|
const creds = readValidCredentials(void 0, {
|
|
7927
7940
|
project
|
|
7928
7941
|
});
|
|
7929
|
-
|
|
7942
|
+
if (creds?.token) return String(creds.token).trim();
|
|
7943
|
+
if (named) return "";
|
|
7944
|
+
return String(process.env.EXTENSION_DEV_TOKEN || "").trim();
|
|
7930
7945
|
}
|
|
7931
7946
|
const fromEnv = String(process.env.EXTENSION_DEV_TOKEN || "").trim();
|
|
7932
7947
|
if (fromEnv) return fromEnv;
|
|
@@ -18058,30 +18073,41 @@ const truthy = (raw)=>{
|
|
|
18058
18073
|
return "" !== value && "0" !== value && "false" !== value && "off" !== value;
|
|
18059
18074
|
};
|
|
18060
18075
|
function isServerFlag(arg) {
|
|
18061
|
-
return "--no-ship" === arg || "--features" === arg || arg.startsWith("--features=");
|
|
18076
|
+
return "--no-ship" === arg || "--features" === arg || arg.startsWith("--features=") || "--project" === arg || arg.startsWith("--project=");
|
|
18062
18077
|
}
|
|
18063
18078
|
function resolveServerOptions(argv, env) {
|
|
18064
18079
|
let rawFeatures = env[FEATURES_ENV];
|
|
18065
18080
|
let noShip = truthy(env[NO_SHIP_ENV]);
|
|
18081
|
+
let project = String(env[PROJECT_PIN_ENV] || "").trim().toLowerCase();
|
|
18066
18082
|
for(let i = 0; i < argv.length; i++){
|
|
18067
18083
|
const arg = argv[i];
|
|
18068
18084
|
if ("--no-ship" === arg) noShip = true;
|
|
18069
18085
|
else if ("--features" === arg) rawFeatures = argv[++i] ?? "";
|
|
18086
|
+
else if (arg.startsWith("--features=")) rawFeatures = arg.slice(11);
|
|
18087
|
+
else if ("--project" === arg) project = String(argv[++i] ?? "").trim().toLowerCase();
|
|
18070
18088
|
else {
|
|
18071
|
-
if (!arg.startsWith("--
|
|
18089
|
+
if (!arg.startsWith("--project=")) return {
|
|
18072
18090
|
ok: false,
|
|
18073
18091
|
message: `Unknown flag "${arg}".`
|
|
18074
18092
|
};
|
|
18075
|
-
|
|
18093
|
+
project = arg.slice(10).trim().toLowerCase();
|
|
18076
18094
|
}
|
|
18077
18095
|
}
|
|
18096
|
+
if (project && !isProjectRef(project)) return {
|
|
18097
|
+
ok: false,
|
|
18098
|
+
message: `--project takes "<workspace>/<project>", got "${project}".`
|
|
18099
|
+
};
|
|
18100
|
+
const pin = project ? {
|
|
18101
|
+
project
|
|
18102
|
+
} : {};
|
|
18078
18103
|
if (void 0 === rawFeatures || "" === rawFeatures.trim()) return {
|
|
18079
18104
|
ok: true,
|
|
18080
18105
|
options: {
|
|
18081
18106
|
features: [
|
|
18082
18107
|
...FEATURE_GROUPS
|
|
18083
18108
|
],
|
|
18084
|
-
noShip
|
|
18109
|
+
noShip,
|
|
18110
|
+
...pin
|
|
18085
18111
|
}
|
|
18086
18112
|
};
|
|
18087
18113
|
const names = rawFeatures.split(",").map((name)=>name.trim().toLowerCase()).filter(Boolean);
|
|
@@ -18095,7 +18121,8 @@ function resolveServerOptions(argv, env) {
|
|
|
18095
18121
|
ok: true,
|
|
18096
18122
|
options: {
|
|
18097
18123
|
features,
|
|
18098
|
-
noShip
|
|
18124
|
+
noShip,
|
|
18125
|
+
...pin
|
|
18099
18126
|
}
|
|
18100
18127
|
};
|
|
18101
18128
|
}
|
|
@@ -18138,6 +18165,40 @@ function disabledToolEnvelope(name, args, options) {
|
|
|
18138
18165
|
});
|
|
18139
18166
|
return null;
|
|
18140
18167
|
}
|
|
18168
|
+
function pinProjectArgs(name, args, inputSchema, options) {
|
|
18169
|
+
const pinned = options.project;
|
|
18170
|
+
if (!pinned) return {
|
|
18171
|
+
args
|
|
18172
|
+
};
|
|
18173
|
+
const named = "string" == typeof args.project ? args.project : "";
|
|
18174
|
+
if (named && !sameProject(named, pinned)) return {
|
|
18175
|
+
refused: envelope_envelope({
|
|
18176
|
+
ok: false,
|
|
18177
|
+
command: name,
|
|
18178
|
+
status: "project-pinned",
|
|
18179
|
+
error: {
|
|
18180
|
+
code: "E_TOOL_DISABLED",
|
|
18181
|
+
message: `This server is pinned to ${pinned} and was asked to act on ${named}.`
|
|
18182
|
+
},
|
|
18183
|
+
value: {
|
|
18184
|
+
pinned,
|
|
18185
|
+
named
|
|
18186
|
+
},
|
|
18187
|
+
hint: `Work on ${named} through an MCP server configured with --project ${named}, or change this server's --project (or ${PROJECT_PIN_ENV}).`
|
|
18188
|
+
})
|
|
18189
|
+
};
|
|
18190
|
+
const properties = inputSchema.properties ?? {};
|
|
18191
|
+
const statusOnly = "extension_auth" === name && "status" === args.action;
|
|
18192
|
+
if (named || statusOnly || !("project" in properties)) return {
|
|
18193
|
+
args
|
|
18194
|
+
};
|
|
18195
|
+
return {
|
|
18196
|
+
args: {
|
|
18197
|
+
...args,
|
|
18198
|
+
project: pinned
|
|
18199
|
+
}
|
|
18200
|
+
};
|
|
18201
|
+
}
|
|
18141
18202
|
const tools = [
|
|
18142
18203
|
create_namespaceObject,
|
|
18143
18204
|
templates_namespaceObject,
|
|
@@ -18235,8 +18296,8 @@ function createServer(options = DEFAULT_SERVER_OPTIONS) {
|
|
|
18235
18296
|
],
|
|
18236
18297
|
isError: true
|
|
18237
18298
|
};
|
|
18238
|
-
const
|
|
18239
|
-
const disabled = disabledToolEnvelope(name,
|
|
18299
|
+
const aliasedArgs = normalizeArgAliases(tool.schema.inputSchema, args ?? {});
|
|
18300
|
+
const disabled = disabledToolEnvelope(name, aliasedArgs, options);
|
|
18240
18301
|
if (disabled) return {
|
|
18241
18302
|
content: [
|
|
18242
18303
|
{
|
|
@@ -18246,6 +18307,17 @@ function createServer(options = DEFAULT_SERVER_OPTIONS) {
|
|
|
18246
18307
|
],
|
|
18247
18308
|
isError: true
|
|
18248
18309
|
};
|
|
18310
|
+
const pinned = pinProjectArgs(name, aliasedArgs, tool.schema.inputSchema, options);
|
|
18311
|
+
if ("refused" in pinned) return {
|
|
18312
|
+
content: [
|
|
18313
|
+
{
|
|
18314
|
+
type: "text",
|
|
18315
|
+
text: pinned.refused
|
|
18316
|
+
}
|
|
18317
|
+
],
|
|
18318
|
+
isError: true
|
|
18319
|
+
};
|
|
18320
|
+
const normalizedArgs = pinned.args;
|
|
18249
18321
|
const issues = validateToolInput(tool.schema.inputSchema, normalizedArgs);
|
|
18250
18322
|
if (issues.length) return {
|
|
18251
18323
|
content: [
|
|
@@ -18284,6 +18356,7 @@ function createServer(options = DEFAULT_SERVER_OPTIONS) {
|
|
|
18284
18356
|
}
|
|
18285
18357
|
async function startServer(options = DEFAULT_SERVER_OPTIONS) {
|
|
18286
18358
|
installCarrierExitCleanup();
|
|
18359
|
+
if (options.project) process.env.EXTENSION_DEV_PROJECT = options.project;
|
|
18287
18360
|
const server = createServer(options);
|
|
18288
18361
|
const transport = new StdioServerTransport();
|
|
18289
18362
|
await server.connect(transport);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type ClientId = "claude-code" | "cursor" | "vscode" | "codex" | "json";
|
|
2
|
+
export type Reach = "everything" | "no-ship" | "local";
|
|
3
|
+
export interface ClientInfo {
|
|
4
|
+
id: ClientId;
|
|
5
|
+
label: string;
|
|
6
|
+
subtitle?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const CLIENTS: readonly ClientInfo[];
|
|
9
|
+
export interface Recipe {
|
|
10
|
+
client: ClientId;
|
|
11
|
+
command?: string;
|
|
12
|
+
config?: {
|
|
13
|
+
language: "json" | "toml";
|
|
14
|
+
path: string;
|
|
15
|
+
text: string;
|
|
16
|
+
};
|
|
17
|
+
deeplink?: string;
|
|
18
|
+
login: string;
|
|
19
|
+
}
|
|
20
|
+
export interface RecipeInput {
|
|
21
|
+
client: ClientId;
|
|
22
|
+
reach: Reach;
|
|
23
|
+
strictApproval: boolean;
|
|
24
|
+
project: string;
|
|
25
|
+
}
|
|
26
|
+
export declare const SERVER_NAME = "extension-dev";
|
|
27
|
+
export declare const PACKAGE = "@extension.dev/mcp";
|
|
28
|
+
export declare function serverArgs(input: Omit<RecipeInput, "client">): string[];
|
|
29
|
+
export declare function serverEnv(input: Omit<RecipeInput, "client">): Record<string, string>;
|
|
30
|
+
export declare function loginCommand(project: string): string;
|
|
31
|
+
export declare function buildRecipe(input: RecipeInput): Recipe;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const README_SETUP_START = "<!-- setup:start (generated from src/clients, run pnpm readme:clients) -->";
|
|
2
|
+
export declare const README_SETUP_END = "<!-- setup:end -->";
|
|
3
|
+
export declare function renderReadmeSetup(): string;
|
|
4
|
+
export declare function spliceReadmeSetup(readme: string): string;
|
|
@@ -18,6 +18,10 @@ export interface CredentialSelector {
|
|
|
18
18
|
}
|
|
19
19
|
export declare function credentialKey(workspaceSlug: string, projectSlug: string): string;
|
|
20
20
|
export declare function readCredentialStore(): CredentialStore | null;
|
|
21
|
+
export declare const PROJECT_PIN_ENV = "EXTENSION_DEV_PROJECT";
|
|
22
|
+
export declare function pinnedProject(): string;
|
|
23
|
+
export declare function isProjectRef(value: string): boolean;
|
|
24
|
+
export declare function sameProject(named: string, pinned: string): boolean;
|
|
21
25
|
export declare function readCredentials(selector?: CredentialSelector): StoredCredentials | null;
|
|
22
26
|
export declare function listCredentials(): Array<StoredCredentials & {
|
|
23
27
|
key: string;
|
|
@@ -18,6 +18,7 @@ export declare const TOOL_POLICY: Record<string, ToolPolicy>;
|
|
|
18
18
|
export interface ServerOptions {
|
|
19
19
|
features: FeatureGroup[];
|
|
20
20
|
noShip: boolean;
|
|
21
|
+
project?: string;
|
|
21
22
|
}
|
|
22
23
|
export declare const DEFAULT_SERVER_OPTIONS: ServerOptions;
|
|
23
24
|
export declare function isServerFlag(arg: string): boolean;
|
|
@@ -30,4 +31,9 @@ export declare function resolveServerOptions(argv: string[], env: Record<string,
|
|
|
30
31
|
};
|
|
31
32
|
export declare function isToolListed(name: string, options: ServerOptions): boolean;
|
|
32
33
|
export declare function disabledToolEnvelope(name: string, args: Args, options: ServerOptions): string | null;
|
|
34
|
+
export declare function pinProjectArgs(name: string, args: Args, inputSchema: Record<string, unknown>, options: ServerOptions): {
|
|
35
|
+
args: Args;
|
|
36
|
+
} | {
|
|
37
|
+
refused: string;
|
|
38
|
+
};
|
|
33
39
|
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.4",
|
|
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",
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
".": {
|
|
27
27
|
"types": "./dist/module.d.ts",
|
|
28
28
|
"import": "./dist/module.js"
|
|
29
|
+
},
|
|
30
|
+
"./clients": {
|
|
31
|
+
"types": "./dist/src/clients/index.d.ts",
|
|
32
|
+
"import": "./dist/clients.js"
|
|
29
33
|
}
|
|
30
34
|
},
|
|
31
35
|
"main": "./dist/module.js",
|
|
@@ -46,6 +50,7 @@
|
|
|
46
50
|
],
|
|
47
51
|
"scripts": {
|
|
48
52
|
"clean": "rm -rf dist",
|
|
53
|
+
"readme:clients": "UPDATE_README=1 vitest run src/__tests__/readme-clients.test.ts",
|
|
49
54
|
"watch": "rslib build --watch",
|
|
50
55
|
"compile": "rslib build",
|
|
51
56
|
"build": "pnpm run compile",
|
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.4",
|
|
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.4",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|