@extension.dev/mcp 10.10.4 → 10.10.5
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 +21 -0
- package/claude/commands/extension-publish.md +30 -53
- package/dist/module.js +238 -1
- package/dist/src/lib/store-review.d.ts +23 -0
- package/package.json +1 -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.5",
|
|
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.5",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cezar Augusto",
|
|
7
7
|
"email": "hello@extension.dev",
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 10.10.5
|
|
4
|
+
|
|
5
|
+
- `extension_build` and `extension_analyze` report store review risks
|
|
6
|
+
read off the built package, as `value.reviewRisks` plus one warning
|
|
7
|
+
each with the fix: access to every website, code the package did not
|
|
8
|
+
ship (`eval`, `new Function`, scripts or imports loaded from a URL), a
|
|
9
|
+
Firefox manifest without `data_collection_permissions`, and API
|
|
10
|
+
permissions no shipped script uses. They never block a build. On a
|
|
11
|
+
dev build only the manifest checks run, because the hot-reload runtime
|
|
12
|
+
and the injected `management` permission are not what a store
|
|
13
|
+
receives (measured on seven published extensions' dev builds against a
|
|
14
|
+
production build of an official template).
|
|
15
|
+
|
|
16
|
+
- The bundled `/extension-publish` command now takes an extension through
|
|
17
|
+
store review with this server's own tools instead of sending people to
|
|
18
|
+
the store consoles by hand: it validates, builds, checks for review
|
|
19
|
+
risks, rehearses with `extension_submit` (dry run), and submits only on
|
|
20
|
+
the user's yes through the approval link. It covers Chrome, Firefox,
|
|
21
|
+
Edge and Safari, and keeps whatever manifest version the project
|
|
22
|
+
declares rather than migrating it during a submission.
|
|
23
|
+
|
|
3
24
|
## 10.10.4
|
|
4
25
|
|
|
5
26
|
- `--project <workspace>/<project>` (env `EXTENSION_DEV_PROJECT`) pins a
|
|
@@ -1,62 +1,39 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
3
|
-
argument-hint: "[chrome|firefox|
|
|
2
|
+
description: Get an extension through store review (Chrome Web Store, Firefox AMO, Edge Add-ons, App Store for Safari)
|
|
3
|
+
argument-hint: "[chrome|firefox|edge|safari|all]"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Take the current extension to store review. The user said: $ARGUMENTS
|
|
7
7
|
|
|
8
8
|
## Parse arguments
|
|
9
9
|
|
|
10
|
-
Default to `
|
|
10
|
+
Default to `chrome` and `firefox`. `all` means chrome, firefox, edge and safari. Accept any comma-separated mix.
|
|
11
11
|
|
|
12
12
|
## Steps
|
|
13
13
|
|
|
14
|
-
1. **
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- Zip location: `dist/chrome/<name>.zip`
|
|
41
|
-
- Submit at: https://chrome.google.com/webstore/devconsole
|
|
42
|
-
- Checklist:
|
|
43
|
-
- [ ] manifest_version: 3
|
|
44
|
-
- [ ] Icons: 128x128 PNG
|
|
45
|
-
- [ ] Description under 132 characters (for listing)
|
|
46
|
-
- [ ] Screenshots: 1280x800 or 640x400
|
|
47
|
-
- [ ] Privacy policy URL (if using sensitive permissions)
|
|
48
|
-
|
|
49
|
-
### Firefox Add-ons (AMO)
|
|
50
|
-
- Zip location: `dist/firefox/<name>.zip`
|
|
51
|
-
- Submit at: https://addons.mozilla.org/developers/
|
|
52
|
-
- Checklist:
|
|
53
|
-
- [ ] manifest_version: 2 (recommended for broadest compat) or 3
|
|
54
|
-
- [ ] No Chrome-only APIs without polyfill
|
|
55
|
-
- [ ] Source code zip if using a bundler: `npx extension build --browser=firefox --zip --zip-source`
|
|
56
|
-
- [ ] AMO requires source code review for minified/bundled code
|
|
57
|
-
|
|
58
|
-
5. **Flag issues** that would cause store rejection:
|
|
59
|
-
- `<all_urls>` host permission without justification
|
|
60
|
-
- `activeTab` + `scripting` without clear use case
|
|
61
|
-
- Remote code loading (eval, Function constructor, remote scripts)
|
|
62
|
-
- Excessive permissions for the extension's functionality
|
|
14
|
+
1. **Check the manifest.** Call `extension_manifest_validate` for each target browser. Fix every `buildBlocking` finding before going on; report warnings.
|
|
15
|
+
|
|
16
|
+
2. **Build store bundles.** Call `extension_build` with `zip: true` for each target. If it refuses on a blocking manifest error or returns compiler errors in `value.errors`, fix and rebuild.
|
|
17
|
+
|
|
18
|
+
3. **Look for review risks.** Call `extension_analyze` on each build and flag anything a reviewer rejects:
|
|
19
|
+
- `<all_urls>` or broad host permissions without a reason in the listing
|
|
20
|
+
- remote code (`eval`, `new Function`, scripts loaded from a URL)
|
|
21
|
+
- permissions the code never uses
|
|
22
|
+
- Firefox: a missing `browser_specific_settings.gecko.data_collection_permissions` declaration, which AMO now requires for new add-ons and updates
|
|
23
|
+
- a bundle over 10 MB, source maps in the production build, missing 128px icon
|
|
24
|
+
|
|
25
|
+
4. **Pick the build to submit.** Store review runs from a build on extension.dev, not from local files. Call `extension_release_status` with `include: "releases"` to find the sha. If the user has not shipped this version yet, say so and offer `extension_publish` first.
|
|
26
|
+
|
|
27
|
+
5. **Rehearse.** Call `extension_submit` with the browsers and `buildSha`, leaving `dryRun` at its default (`true`). Show the per-store credential rows. A store whose credentials are not healthy is fixed in the extension.dev console (the response names the page); drop it from `browsers` or stop.
|
|
28
|
+
|
|
29
|
+
6. **Submit only on a clear yes.** Ask the user to confirm the exact stores, build and channel. Then call `extension_submit` with `dryRun: false`.
|
|
30
|
+
- It answers `approval-required` with an `approvalUrl`: give the user that link. A workspace owner approves exactly this submission on extension.dev.
|
|
31
|
+
- When they say it is approved, call `extension_submit` again with the same arguments plus the returned `approvalId`. It runs once.
|
|
32
|
+
|
|
33
|
+
7. **Report.** Call `extension_release_status` for the recorded outcome and review state per store, and give the user each store's listing or dashboard link from the response.
|
|
34
|
+
|
|
35
|
+
## Rules
|
|
36
|
+
|
|
37
|
+
- Never submit without the user's confirmation in step 6, and never retry a real submission with a different build or store list under an old `approvalId`; request a new approval.
|
|
38
|
+
- Keep the manifest version the project already declares. Extension.js templates use `chromium:manifest_version: 3` and `firefox:manifest_version: 2`, and AMO accepts both; do not migrate a manifest as part of a submission.
|
|
39
|
+
- Store credentials are never arguments and never asked for in chat; they live in the extension.dev console.
|
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.5","El":{"OP":"4.1.31"}}');
|
|
240
240
|
const ENVELOPE_SCHEMA = 1;
|
|
241
241
|
const collectWarnings = (warnings)=>{
|
|
242
242
|
if (!warnings) return [];
|
|
@@ -3769,6 +3769,229 @@ function readZipEntryNames(zipPath) {
|
|
|
3769
3769
|
} catch {}
|
|
3770
3770
|
}
|
|
3771
3771
|
}
|
|
3772
|
+
const BROAD_PATTERNS = new Set([
|
|
3773
|
+
"<all_urls>",
|
|
3774
|
+
"*://*/*",
|
|
3775
|
+
"http://*/*",
|
|
3776
|
+
"https://*/*",
|
|
3777
|
+
"*://*/"
|
|
3778
|
+
]);
|
|
3779
|
+
const API_PERMISSIONS = {
|
|
3780
|
+
alarms: [
|
|
3781
|
+
"alarms"
|
|
3782
|
+
],
|
|
3783
|
+
bookmarks: [
|
|
3784
|
+
"bookmarks"
|
|
3785
|
+
],
|
|
3786
|
+
contextMenus: [
|
|
3787
|
+
"contextMenus",
|
|
3788
|
+
"menus"
|
|
3789
|
+
],
|
|
3790
|
+
menus: [
|
|
3791
|
+
"menus",
|
|
3792
|
+
"contextMenus"
|
|
3793
|
+
],
|
|
3794
|
+
cookies: [
|
|
3795
|
+
"cookies"
|
|
3796
|
+
],
|
|
3797
|
+
declarativeNetRequest: [
|
|
3798
|
+
"declarativeNetRequest"
|
|
3799
|
+
],
|
|
3800
|
+
downloads: [
|
|
3801
|
+
"downloads"
|
|
3802
|
+
],
|
|
3803
|
+
history: [
|
|
3804
|
+
"history"
|
|
3805
|
+
],
|
|
3806
|
+
identity: [
|
|
3807
|
+
"identity"
|
|
3808
|
+
],
|
|
3809
|
+
idle: [
|
|
3810
|
+
"idle"
|
|
3811
|
+
],
|
|
3812
|
+
management: [
|
|
3813
|
+
"management"
|
|
3814
|
+
],
|
|
3815
|
+
notifications: [
|
|
3816
|
+
"notifications"
|
|
3817
|
+
],
|
|
3818
|
+
offscreen: [
|
|
3819
|
+
"offscreen"
|
|
3820
|
+
],
|
|
3821
|
+
scripting: [
|
|
3822
|
+
"scripting"
|
|
3823
|
+
],
|
|
3824
|
+
sidePanel: [
|
|
3825
|
+
"sidePanel"
|
|
3826
|
+
],
|
|
3827
|
+
storage: [
|
|
3828
|
+
"storage"
|
|
3829
|
+
],
|
|
3830
|
+
tabGroups: [
|
|
3831
|
+
"tabGroups"
|
|
3832
|
+
],
|
|
3833
|
+
tabs: [
|
|
3834
|
+
"tabs"
|
|
3835
|
+
],
|
|
3836
|
+
topSites: [
|
|
3837
|
+
"topSites"
|
|
3838
|
+
],
|
|
3839
|
+
tts: [
|
|
3840
|
+
"tts"
|
|
3841
|
+
],
|
|
3842
|
+
webNavigation: [
|
|
3843
|
+
"webNavigation"
|
|
3844
|
+
],
|
|
3845
|
+
webRequest: [
|
|
3846
|
+
"webRequest"
|
|
3847
|
+
]
|
|
3848
|
+
};
|
|
3849
|
+
const REMOTE_CODE_PATTERNS = [
|
|
3850
|
+
[
|
|
3851
|
+
"eval()",
|
|
3852
|
+
/\beval\s*\(/
|
|
3853
|
+
],
|
|
3854
|
+
[
|
|
3855
|
+
"new Function()",
|
|
3856
|
+
/\bnew\s+Function\s*\(/
|
|
3857
|
+
],
|
|
3858
|
+
[
|
|
3859
|
+
"importScripts from a URL",
|
|
3860
|
+
/importScripts\(\s*["'`]https?:/
|
|
3861
|
+
],
|
|
3862
|
+
[
|
|
3863
|
+
"import() from a URL",
|
|
3864
|
+
/\bimport\(\s*["'`]https?:/
|
|
3865
|
+
]
|
|
3866
|
+
];
|
|
3867
|
+
const REMOTE_SCRIPT_TAG = /<script[^>]+src\s*=\s*["']https?:/i;
|
|
3868
|
+
const MAX_SCAN_BYTES = 8388608;
|
|
3869
|
+
function strings(value) {
|
|
3870
|
+
return Array.isArray(value) ? value.filter((v)=>"string" == typeof v) : [];
|
|
3871
|
+
}
|
|
3872
|
+
function hostPatterns(manifest) {
|
|
3873
|
+
const found = new Set();
|
|
3874
|
+
for (const pattern of strings(manifest.host_permissions))found.add(pattern);
|
|
3875
|
+
for (const perm of strings(manifest.permissions))if (perm.includes("://") || "<all_urls>" === perm) found.add(perm);
|
|
3876
|
+
const scripts = Array.isArray(manifest.content_scripts) ? manifest.content_scripts : [];
|
|
3877
|
+
for (const entry of scripts)for (const match of strings(entry?.matches))found.add(match);
|
|
3878
|
+
return [
|
|
3879
|
+
...found
|
|
3880
|
+
];
|
|
3881
|
+
}
|
|
3882
|
+
function readText(distPath, rel) {
|
|
3883
|
+
try {
|
|
3884
|
+
return node_fs.readFileSync(node_path.join(distPath, rel), "utf8");
|
|
3885
|
+
} catch {
|
|
3886
|
+
return "";
|
|
3887
|
+
}
|
|
3888
|
+
}
|
|
3889
|
+
function reviewRisks(input) {
|
|
3890
|
+
const risks = [];
|
|
3891
|
+
const development = input.development ?? isDevelopmentBuild(input.files);
|
|
3892
|
+
const broad = hostPatterns(input.manifest).filter((p)=>BROAD_PATTERNS.has(p));
|
|
3893
|
+
if (broad.length) risks.push({
|
|
3894
|
+
code: "BROAD_HOST_ACCESS",
|
|
3895
|
+
message: `The extension asks for access to every website (${broad.join(", ")}). Chrome Web Store and AMO reviewers ask why, and the review takes longer.`,
|
|
3896
|
+
fix: "List only the sites the extension works on, or use activeTab so access is granted per click; if every site is the point, say why in the listing.",
|
|
3897
|
+
permissions: broad
|
|
3898
|
+
});
|
|
3899
|
+
const scripts = input.files.filter((f)=>/\.(m?js|cjs)$/.test(f.path) && !f.path.endsWith(".map"));
|
|
3900
|
+
const pages = input.files.filter((f)=>/\.html?$/.test(f.path));
|
|
3901
|
+
const hits = new Map();
|
|
3902
|
+
const note = (label, file)=>{
|
|
3903
|
+
if (!hits.has(label)) hits.set(label, new Set());
|
|
3904
|
+
hits.get(label).add(file);
|
|
3905
|
+
};
|
|
3906
|
+
let scanned = 0;
|
|
3907
|
+
let source = "";
|
|
3908
|
+
for (const file of scripts){
|
|
3909
|
+
if (scanned >= MAX_SCAN_BYTES) break;
|
|
3910
|
+
const text = readText(input.distPath, file.path);
|
|
3911
|
+
scanned += text.length;
|
|
3912
|
+
source += `\n${text}`;
|
|
3913
|
+
for (const [label, pattern] of REMOTE_CODE_PATTERNS)if (pattern.test(text)) note(label, file.path);
|
|
3914
|
+
}
|
|
3915
|
+
for (const file of pages)if (REMOTE_SCRIPT_TAG.test(readText(input.distPath, file.path))) note("a <script> loaded from a URL", file.path);
|
|
3916
|
+
if (hits.size && !development) {
|
|
3917
|
+
const labels = [
|
|
3918
|
+
...hits.keys()
|
|
3919
|
+
];
|
|
3920
|
+
risks.push({
|
|
3921
|
+
code: "REMOTE_CODE",
|
|
3922
|
+
message: `The package contains ${labels.join(", ")}. Manifest V3 stores reject extensions that run code they did not ship, and reviewers flag these patterns even inside a bundled library.`,
|
|
3923
|
+
fix: "Remove the call, or confirm it only ever runs code that ships in the package and be ready to say so in review.",
|
|
3924
|
+
files: [
|
|
3925
|
+
...new Set([
|
|
3926
|
+
...hits.values()
|
|
3927
|
+
].flatMap((set)=>[
|
|
3928
|
+
...set
|
|
3929
|
+
]))
|
|
3930
|
+
].slice(0, 10)
|
|
3931
|
+
});
|
|
3932
|
+
}
|
|
3933
|
+
if (isGeckoFamily(input.browser)) {
|
|
3934
|
+
const gecko = input.manifest.browser_specific_settings?.gecko;
|
|
3935
|
+
if (!gecko || !("data_collection_permissions" in gecko)) risks.push({
|
|
3936
|
+
code: "FIREFOX_DATA_COLLECTION_MISSING",
|
|
3937
|
+
message: "The Firefox manifest has no browser_specific_settings.gecko.data_collection_permissions. AMO requires it for new add-ons and for updates.",
|
|
3938
|
+
fix: 'Add "browser_specific_settings": { "gecko": { "data_collection_permissions": { "required": ["none"] } } }, or list what the extension does collect.'
|
|
3939
|
+
});
|
|
3940
|
+
}
|
|
3941
|
+
if (source && scanned < MAX_SCAN_BYTES && !development) {
|
|
3942
|
+
const unused = strings(input.manifest.permissions).filter((perm)=>{
|
|
3943
|
+
const apis = API_PERMISSIONS[perm];
|
|
3944
|
+
if (!apis) return false;
|
|
3945
|
+
return !apis.some((api)=>new RegExp(`\\.${api}\\b`).test(source));
|
|
3946
|
+
});
|
|
3947
|
+
if (unused.length) risks.push({
|
|
3948
|
+
code: "UNUSED_PERMISSION",
|
|
3949
|
+
message: `The manifest asks for ${unused.join(", ")}, but no shipped script uses ${1 === unused.length ? "that API" : "those APIs"}. Reviewers reject permissions an extension does not need.`,
|
|
3950
|
+
fix: `Remove ${unused.join(", ")} from permissions, or keep ${1 === unused.length ? "it" : "them"} if the call happens in code this scan cannot see.`,
|
|
3951
|
+
permissions: unused
|
|
3952
|
+
});
|
|
3953
|
+
}
|
|
3954
|
+
return risks;
|
|
3955
|
+
}
|
|
3956
|
+
function isDevelopmentBuild(files) {
|
|
3957
|
+
return files.some((f)=>f.path.endsWith(".map") || /hot-update\./.test(f.path));
|
|
3958
|
+
}
|
|
3959
|
+
function reviewRiskWarnings(risks) {
|
|
3960
|
+
return risks.map((risk)=>`Store review: ${risk.message} ${risk.fix}`);
|
|
3961
|
+
}
|
|
3962
|
+
function listFiles(dir, base = "") {
|
|
3963
|
+
let entries;
|
|
3964
|
+
try {
|
|
3965
|
+
entries = node_fs.readdirSync(dir, {
|
|
3966
|
+
withFileTypes: true
|
|
3967
|
+
});
|
|
3968
|
+
} catch {
|
|
3969
|
+
return [];
|
|
3970
|
+
}
|
|
3971
|
+
const out = [];
|
|
3972
|
+
for (const entry of entries){
|
|
3973
|
+
const rel = base ? `${base}/${entry.name}` : entry.name;
|
|
3974
|
+
if (entry.isDirectory()) out.push(...listFiles(node_path.join(dir, entry.name), rel));
|
|
3975
|
+
else out.push({
|
|
3976
|
+
path: rel
|
|
3977
|
+
});
|
|
3978
|
+
}
|
|
3979
|
+
return out;
|
|
3980
|
+
}
|
|
3981
|
+
function reviewDist(distPath, browser) {
|
|
3982
|
+
let manifest;
|
|
3983
|
+
try {
|
|
3984
|
+
manifest = JSON.parse(node_fs.readFileSync(node_path.join(distPath, "manifest.json"), "utf8"));
|
|
3985
|
+
} catch {
|
|
3986
|
+
return [];
|
|
3987
|
+
}
|
|
3988
|
+
return reviewRisks({
|
|
3989
|
+
distPath,
|
|
3990
|
+
browser,
|
|
3991
|
+
manifest,
|
|
3992
|
+
files: listFiles(distPath)
|
|
3993
|
+
});
|
|
3994
|
+
}
|
|
3772
3995
|
const build_COMMAND = "extension_build";
|
|
3773
3996
|
function readBuildSummary(projectPath, browser, since) {
|
|
3774
3997
|
const file = session_paths_buildSummaryPath(projectPath, browser);
|
|
@@ -4210,6 +4433,7 @@ async function build_handler(args) {
|
|
|
4210
4433
|
const buildWarningsTruncated = buildWarnings.length && "number" == typeof engineSummary?.warnings_count && engineSummary.warnings_count > buildWarnings.length ? engineSummary.warnings_count : void 0;
|
|
4211
4434
|
const distDir = node_path.resolve(args.projectPath, "dist", browser);
|
|
4212
4435
|
const entrypoints = builtEntrypoints(distDir);
|
|
4436
|
+
const risks = reviewDist(distDir, browser);
|
|
4213
4437
|
const contamination = carrierContamination(node_path.resolve(args.projectPath, "dist"));
|
|
4214
4438
|
const uncheckedNote = contamination.unchecked.length ? `Could not read the entry table of ${contamination.unchecked.join(", ")}, so those archives were not checked for the live-preview carrier. Unpack and check them yourself before submitting.` : null;
|
|
4215
4439
|
if (contamination.paths.length) return envelope_envelope({
|
|
@@ -4293,6 +4517,9 @@ async function build_handler(args) {
|
|
|
4293
4517
|
...entrypoints.length ? {
|
|
4294
4518
|
entrypoints
|
|
4295
4519
|
} : {},
|
|
4520
|
+
...risks.length ? {
|
|
4521
|
+
reviewRisks: risks
|
|
4522
|
+
} : {},
|
|
4296
4523
|
...void 0 !== buildWarningsTruncated ? {
|
|
4297
4524
|
buildWarningsTruncated
|
|
4298
4525
|
} : {},
|
|
@@ -4313,6 +4540,7 @@ async function build_handler(args) {
|
|
|
4313
4540
|
...warnings,
|
|
4314
4541
|
...preflight?.warnings ?? [],
|
|
4315
4542
|
...buildWarnings,
|
|
4543
|
+
...reviewRiskWarnings(risks),
|
|
4316
4544
|
...zipNotes,
|
|
4317
4545
|
uncheckedNote,
|
|
4318
4546
|
derivedBundleIdNote,
|
|
@@ -13069,9 +13297,17 @@ async function analyze_handler(args) {
|
|
|
13069
13297
|
}
|
|
13070
13298
|
const note = "development" === buildType ? `This dist contains ${formatBytes(sourcemapSize)} of sourcemaps and looks like a dev build; run extension_build for production sizes. shippableSize excludes sourcemaps.` : void 0;
|
|
13071
13299
|
const archiveNote = archiveSize > 0 ? `This dist contains ${formatBytes(archiveSize)} of .zip archive(s) (store packaging output, written into dist by zip builds). shippableSize excludes them so the packaged copy does not double-count the files it contains.` : void 0;
|
|
13300
|
+
const risks = reviewRisks({
|
|
13301
|
+
distPath,
|
|
13302
|
+
browser,
|
|
13303
|
+
manifest,
|
|
13304
|
+
files,
|
|
13305
|
+
development: "development" === buildType
|
|
13306
|
+
});
|
|
13072
13307
|
const result = {
|
|
13073
13308
|
browser,
|
|
13074
13309
|
distPath,
|
|
13310
|
+
reviewRisks: risks,
|
|
13075
13311
|
entrypoints,
|
|
13076
13312
|
buildType,
|
|
13077
13313
|
totalSize,
|
|
@@ -13120,6 +13356,7 @@ async function analyze_handler(args) {
|
|
|
13120
13356
|
status: "analyzed",
|
|
13121
13357
|
value: result,
|
|
13122
13358
|
warnings: [
|
|
13359
|
+
...reviewRiskWarnings(risks),
|
|
13123
13360
|
...sizeWarnings,
|
|
13124
13361
|
note,
|
|
13125
13362
|
archiveNote
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type ReviewRiskCode = "BROAD_HOST_ACCESS" | "REMOTE_CODE" | "FIREFOX_DATA_COLLECTION_MISSING" | "UNUSED_PERMISSION";
|
|
2
|
+
export interface ReviewRisk {
|
|
3
|
+
code: ReviewRiskCode;
|
|
4
|
+
message: string;
|
|
5
|
+
fix: string;
|
|
6
|
+
files?: string[];
|
|
7
|
+
permissions?: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function reviewRisks(input: {
|
|
10
|
+
distPath: string;
|
|
11
|
+
browser: string;
|
|
12
|
+
manifest: Record<string, unknown>;
|
|
13
|
+
files: Array<{
|
|
14
|
+
path: string;
|
|
15
|
+
type?: string;
|
|
16
|
+
}>;
|
|
17
|
+
development?: boolean;
|
|
18
|
+
}): ReviewRisk[];
|
|
19
|
+
export declare function isDevelopmentBuild(files: Array<{
|
|
20
|
+
path: string;
|
|
21
|
+
}>): boolean;
|
|
22
|
+
export declare function reviewRiskWarnings(risks: ReviewRisk[]): string[];
|
|
23
|
+
export declare function reviewDist(distPath: string, browser: string): ReviewRisk[];
|
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.5",
|
|
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",
|
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.5",
|
|
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.5",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|