@extenshi/mcp 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/dist/bff.d.ts +24 -0
- package/dist/bff.d.ts.map +1 -1
- package/dist/bff.js +3 -0
- package/dist/bff.js.map +1 -1
- package/dist/extension-templates.d.ts +111 -0
- package/dist/extension-templates.d.ts.map +1 -0
- package/dist/extension-templates.js +182 -0
- package/dist/extension-templates.js.map +1 -0
- package/dist/icon-workflow.js +1 -1
- package/dist/icon-workflow.js.map +1 -1
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/startup.d.ts +63 -0
- package/dist/startup.d.ts.map +1 -0
- package/dist/startup.js +57 -0
- package/dist/startup.js.map +1 -0
- package/dist/telemetry.d.ts +9 -8
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +23 -14
- package/dist/telemetry.js.map +1 -1
- package/dist/templates.d.ts +24 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +56 -0
- package/dist/templates.js.map +1 -0
- package/dist/tools.d.ts +1 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +158 -11
- package/dist/tools.js.map +1 -1
- package/dist/untrusted.d.ts +71 -0
- package/dist/untrusted.d.ts.map +1 -0
- package/dist/untrusted.js +130 -0
- package/dist/untrusted.js.map +1 -0
- package/dist/welcome-workflow.d.ts.map +1 -1
- package/dist/welcome-workflow.js +2 -1
- package/dist/welcome-workflow.js.map +1 -1
- package/package.json +3 -3
- package/scripts/postinstall.cjs +69 -15
package/README.md
CHANGED
|
@@ -15,8 +15,9 @@ enforces it too.
|
|
|
15
15
|
|
|
16
16
|
1. Sign up at **https://auth.extenshi.io/signup**
|
|
17
17
|
2. Create a key at **https://dojo.extenshi.io/api-keys**
|
|
18
|
-
3. Provide it via the `EXTENSHI_API_KEY` environment variable (or run
|
|
19
|
-
which the MCP server reads from
|
|
18
|
+
3. Provide it via the `EXTENSHI_API_KEY` environment variable (or run
|
|
19
|
+
`npx @extenshi/cli@latest login`, which the MCP server reads from
|
|
20
|
+
`~/.extenshi/config.json`).
|
|
20
21
|
|
|
21
22
|
## Configure your MCP client
|
|
22
23
|
|
|
@@ -40,10 +41,12 @@ enforces it too.
|
|
|
40
41
|
| `get_extension` | Full catalog detail for one extension — by numeric catalog ID **or** by its store id (see below) | 1 read |
|
|
41
42
|
| `get_reviews` | Paginated Firefox/Edge user reviews (rating, short excerpt, date, language) + a store-level aggregate (rating, count, reviews link) — Chrome Web Store review rows excluded (aggregate is the only public content for Chrome); reviewer identity omitted; sort by recent or rating | 1 read |
|
|
42
43
|
| `get_security` | Risk score + finding counts + top grouped findings (reads existing scans) | 3 reads |
|
|
44
|
+
| `get_risk_by_store_ids` | Safety scores for **up to 40** extensions in one call, by store id — for auditing a list of installed extensions instead of calling `get_security` per extension | 1 read per call |
|
|
43
45
|
| `market_overview` | Catalog-wide market intelligence with no args (totals, store split, category tree, and the extended breakdown — MV2/MV3, sensitive permissions, risk tiers, trader status, recency, reviews); pass a `query` to scope facets to a search | 1 read |
|
|
44
46
|
| `get_credit_balance` | Remaining credits across every pool (read / scan / icon / inventory), so an agent can size a batch before running it instead of hitting a mid-batch limit | Free |
|
|
45
47
|
| `search_docs` | Search the Extenshi docs + `@extenshi/cli` reference so the assistant can quote exact commands | Free (no key) |
|
|
46
|
-
| `generate_icon_workflow` | Icon design requirements + the local agent-draws-SVG → `extenshi icon preview` → export workflow | Free (no key) |
|
|
48
|
+
| `generate_icon_workflow` | Icon design requirements + the local agent-draws-SVG → `npx @extenshi/cli@latest icon preview` → export workflow | Free (no key) |
|
|
49
|
+
| `generate_welcome_page_workflow` | Design brief for the post-install welcome page: the one action it must drive, which illustrations to produce, where to place click markers, and the block JSON to return | Free (no key) |
|
|
47
50
|
| `scan_extension` | Pre-publish security scan of a local artifact (.zip/.crx/.xpi), with live progress | 1 scan |
|
|
48
51
|
| `publish_extension` | Publish to Chrome/Firefox/Edge with your own store credentials (fully local) | Free |
|
|
49
52
|
|
package/dist/bff.d.ts
CHANGED
|
@@ -70,6 +70,30 @@ export interface Bff {
|
|
|
70
70
|
store: 'CHROME' | 'FIREFOX' | 'EDGE';
|
|
71
71
|
}>;
|
|
72
72
|
}): Promise<unknown>;
|
|
73
|
+
/**
|
|
74
|
+
* The caller's OWN extension projects. FREE — credits pay for catalog data
|
|
75
|
+
* about other people's extensions, never for reading your own work.
|
|
76
|
+
*/
|
|
77
|
+
listMyProjects(): Promise<unknown>;
|
|
78
|
+
/**
|
|
79
|
+
* One own project as state an agent can act on: chosen extension types, the
|
|
80
|
+
* manifest.json that project produces per browser, live hosted URLs, and an
|
|
81
|
+
* index of saved tool state. FREE, same reasoning. `includeToolStates` inlines
|
|
82
|
+
* named payloads; without it the response lists what exists and how big.
|
|
83
|
+
*/
|
|
84
|
+
getMyProjectState(input: {
|
|
85
|
+
projectId: string;
|
|
86
|
+
includeToolStates?: string[];
|
|
87
|
+
}): Promise<unknown>;
|
|
88
|
+
/**
|
|
89
|
+
* The starter extension one own project produces, as files — the same set the
|
|
90
|
+
* site commits and packs into its download. FREE, same reasoning. One browser
|
|
91
|
+
* per call: the manifests differ between them and the rest does not.
|
|
92
|
+
*/
|
|
93
|
+
getMyProjectScaffold(input: {
|
|
94
|
+
projectId: string;
|
|
95
|
+
browser?: string;
|
|
96
|
+
}): Promise<unknown>;
|
|
73
97
|
}
|
|
74
98
|
/** Build a BFF client from a static `ek_…` key (stdio path). */
|
|
75
99
|
export declare function makeBff(bffUrl: string, apiKey: string): Bff;
|
package/dist/bff.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bff.d.ts","sourceRoot":"","sources":["../src/bff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,4DAA4D;AAC5D,MAAM,WAAW,UAAU;IAC1B,2GAA2G;IAC3G,SAAS,EAAE,MAAM,CAAA;IACjB,iGAAiG;IACjG,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;CAChB;AAED,kFAAkF;AAClF,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,UAAU,CAAA;IAChB,IAAI,EAAE,UAAU,CAAA;IAChB,IAAI,EAAE,UAAU,CAAA;IAChB,SAAS,EAAE,UAAU,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;CAClB;AAED,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAA;CACpC;AAED,MAAM,WAAW,GAAG;IACnB,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAClE,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC9C,2EAA2E;IAC3E,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5D,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACtD,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACrD,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACjE,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5B,sGAAsG;IACtG,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3C,2DAA2D;IAC3D,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IACnC,yFAAyF;IACzF,mBAAmB,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAChD;;;;OAIG;IACH,mBAAmB,CAAC,KAAK,EAAE;QAC1B,OAAO,EAAE,MAAM,CAAA;QACf,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAA;KACrC,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAA;IAChC;;;;;OAKG;IACH,uBAAuB,CAAC,KAAK,EAAE;QAC9B,UAAU,EAAE,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAA;SAAE,CAAC,CAAA;KAC5E,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"bff.d.ts","sourceRoot":"","sources":["../src/bff.ts"],"names":[],"mappings":"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"}
|
package/dist/bff.js
CHANGED
|
@@ -47,6 +47,9 @@ export function makeBffWithAuth(bffUrl, authHeader) {
|
|
|
47
47
|
getApiCallerBalance: () => client.cli.getApiCallerBalance.query(),
|
|
48
48
|
resolveExtensionRef: (input) => client.catalog.resolveExtensionRef.query(input),
|
|
49
49
|
getSecuritySummaryBatch: (input) => client.security.getSecuritySummaryBatch.query(input),
|
|
50
|
+
listMyProjects: () => client.devProject.agentListProjects.query(),
|
|
51
|
+
getMyProjectState: (input) => client.devProject.agentGetProjectState.query(input),
|
|
52
|
+
getMyProjectScaffold: (input) => client.devProject.agentGetProjectScaffold.query(input),
|
|
50
53
|
};
|
|
51
54
|
}
|
|
52
55
|
//# sourceMappingURL=bff.js.map
|
package/dist/bff.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bff.js","sourceRoot":"","sources":["../src/bff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"bff.js","sourceRoot":"","sources":["../src/bff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAkF9D,gEAAgE;AAChE,MAAM,UAAU,OAAO,CAAC,MAAc,EAAE,MAAc;IACrD,OAAO,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,MAAM,EAAE,CAAC,CAAA;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,UAA0C;IACzF,MAAM,MAAM,GAAkB,gBAAgB,CAAC;QAC9C,KAAK,EAAE;YACN,aAAa,CAAC;gBACb,GAAG,EAAE,GAAG,MAAM,WAAW;gBACzB,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,UAAU,EAAE,EAAE,CAAC;aAC5D,CAAC;SACF;KACD,CAAC,CAAA;IAEF,uEAAuE;IACvE,mFAAmF;IACnF,OAAO;QACN,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC;QACzE,gBAAgB,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;QACvE,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,sBAAsB,CAAC,KAAK,CAAC,KAAK,CAAC;QACzE,eAAe,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;QACxF,cAAc,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;QACtF,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC;QACvE,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE;QAC/C,uBAAuB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,uBAAuB,CAAC,KAAK,EAAE;QAC7E,eAAe,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,KAAK,EAAE;QAC7D,mBAAmB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,KAAK,EAAE;QACjE,mBAAmB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC;QAC/E,uBAAuB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAC,KAAK,CAAC,KAAK,CAAC;QACxF,cAAc,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAAK,EAAE;QACjE,iBAAiB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,oBAAoB,CAAC,KAAK,CAAC,KAAK,CAAC;QACjF,oBAAoB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,uBAAuB,CAAC,KAAK,CAAC,KAAK,CAAC;KACvF,CAAA;AACF,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extension templates — the "what kind of extension is this?" taxonomy.
|
|
3
|
+
*
|
|
4
|
+
* A template is not a folder of files to copy. It is a small set of claims
|
|
5
|
+
* about the extension's shape: which manifest components it cannot work
|
|
6
|
+
* without, which permissions those components force, and which files the
|
|
7
|
+
* scaffold has to write so the manifest does not point at something that isn't
|
|
8
|
+
* there. Everything downstream — the picker in the manifest generator, the
|
|
9
|
+
* locked checkboxes, the scaffolded file set, and (next phase) the MCP tool
|
|
10
|
+
* that hands this catalog to a developer's own agent — reads THIS module.
|
|
11
|
+
*
|
|
12
|
+
* It lives in shared-types rather than in dojo because the readers span
|
|
13
|
+
* packages: the website, catalog-bff (answering a developer's own agent), and
|
|
14
|
+
* the published `@extenshi/mcp` package, whose tool tells an agent what kinds of
|
|
15
|
+
* extension exist and what each costs in permissions. All three must give the
|
|
16
|
+
* same answer.
|
|
17
|
+
*
|
|
18
|
+
* SELF-CONTAINED ON PURPOSE — no imports, so it can be copied verbatim into
|
|
19
|
+
* `@extenshi/mcp`, which cannot depend on unpublished workspace packages (same
|
|
20
|
+
* constraint and same solution as welcome-agent-brief.ts).
|
|
21
|
+
*
|
|
22
|
+
* SINGLE SOURCE, TWO COPIES, byte-identical:
|
|
23
|
+
* shared-types/extension-templates.ts ← canonical
|
|
24
|
+
* tools/extenshi-mcp/src/extension-templates.ts ← copy, pinned by a test
|
|
25
|
+
* Edit the canonical file and copy it over; the sync test in the MCP package
|
|
26
|
+
* fails on any divergence inside the monorepo and skips in the npm tarball.
|
|
27
|
+
*
|
|
28
|
+
* Templates COMBINE. A side panel with a content script is one extension with
|
|
29
|
+
* two capabilities, not a fifth template — the picker is multi-select and every
|
|
30
|
+
* helper here takes a list and unions the result. That is why requirements are
|
|
31
|
+
* expressed as components + permissions rather than as whole manifests.
|
|
32
|
+
*/
|
|
33
|
+
export declare const EXTENSION_TEMPLATE_IDS: readonly ["popup", "sidepanel", "content", "overlay"];
|
|
34
|
+
export type ExtensionTemplateId = (typeof EXTENSION_TEMPLATE_IDS)[number];
|
|
35
|
+
/**
|
|
36
|
+
* The template applied when a project has never picked one.
|
|
37
|
+
*
|
|
38
|
+
* `popup` is deliberately the same shape the scaffold produced before templates
|
|
39
|
+
* existed, so an old project regenerates byte-for-byte what it did yesterday.
|
|
40
|
+
*/
|
|
41
|
+
export declare const DEFAULT_EXTENSION_TEMPLATE: ExtensionTemplateId;
|
|
42
|
+
/**
|
|
43
|
+
* Manifest components a template can require.
|
|
44
|
+
*
|
|
45
|
+
* These are the things the UI can lock: a side-panel extension whose
|
|
46
|
+
* `side_panel` key is turned off is not a side-panel extension, it is a broken
|
|
47
|
+
* one. Each maps to a group of fields in the manifest draft.
|
|
48
|
+
*/
|
|
49
|
+
export declare const EXTENSION_TEMPLATE_COMPONENTS: readonly ["action", "sidePanel", "contentScript"];
|
|
50
|
+
export type ExtensionTemplateComponent = (typeof EXTENSION_TEMPLATE_COMPONENTS)[number];
|
|
51
|
+
/** Where scaffolded source goes, so the manifest and the file writer agree. */
|
|
52
|
+
export declare const EXTENSION_TEMPLATE_SOURCE_DIR = "src";
|
|
53
|
+
/**
|
|
54
|
+
* The background service worker every template gets, template or not.
|
|
55
|
+
*
|
|
56
|
+
* It is not part of any one template because it carries the integration the
|
|
57
|
+
* whole product is built around: `setUninstallURL` must be registered at
|
|
58
|
+
* startup, and a template that skipped the background script would silently
|
|
59
|
+
* ship an extension that can never report why someone uninstalled it.
|
|
60
|
+
*/
|
|
61
|
+
export declare const EXTENSION_BACKGROUND_PATH = "src/background.js";
|
|
62
|
+
export interface ExtensionTemplate {
|
|
63
|
+
id: ExtensionTemplateId;
|
|
64
|
+
label: string;
|
|
65
|
+
/** One line on the picker card: what this kind of extension is for. */
|
|
66
|
+
summary: string;
|
|
67
|
+
/** Components locked on while this template is selected. */
|
|
68
|
+
requires: readonly ExtensionTemplateComponent[];
|
|
69
|
+
/**
|
|
70
|
+
* Permissions the manifest cannot drop while this template is selected.
|
|
71
|
+
*
|
|
72
|
+
* Chromium vocabulary. Firefox reaches the same capability through a
|
|
73
|
+
* different manifest key and needs no permission for it (`sidebar_action`
|
|
74
|
+
* instead of `sidePanel` + `side_panel`), so a per-browser manifest builder
|
|
75
|
+
* is expected to translate rather than to emit these verbatim.
|
|
76
|
+
*/
|
|
77
|
+
requiredPermissions: readonly string[];
|
|
78
|
+
/** Pre-ticked because the template usually needs them — always removable. */
|
|
79
|
+
recommendedPermissions: readonly string[];
|
|
80
|
+
/** Repo-relative file the required component points at. */
|
|
81
|
+
entry: string;
|
|
82
|
+
/** Every file the scaffold writes for this template, `entry` included. */
|
|
83
|
+
emits: readonly string[];
|
|
84
|
+
/** Seed match patterns, for the templates whose component is a content script. */
|
|
85
|
+
defaultMatches?: readonly string[];
|
|
86
|
+
}
|
|
87
|
+
export declare const EXTENSION_TEMPLATE_CATALOG: readonly ExtensionTemplate[];
|
|
88
|
+
export declare function isExtensionTemplateId(value: unknown): value is ExtensionTemplateId;
|
|
89
|
+
export declare function getExtensionTemplate(id: ExtensionTemplateId): ExtensionTemplate;
|
|
90
|
+
/**
|
|
91
|
+
* Clean a persisted or client-supplied list into templates we know.
|
|
92
|
+
*
|
|
93
|
+
* Saved tool state is free-form JSON written by whatever build was live at the
|
|
94
|
+
* time, so an unknown id is expected rather than exceptional: it is dropped,
|
|
95
|
+
* not thrown on. Order follows the catalog so two projects that picked the same
|
|
96
|
+
* set produce the same manifest, and duplicates collapse.
|
|
97
|
+
*/
|
|
98
|
+
export declare function normalizeExtensionTemplates(value: unknown): ExtensionTemplateId[];
|
|
99
|
+
/** The selection to build with: what was picked, or the default when nothing was. */
|
|
100
|
+
export declare function effectiveExtensionTemplates(value: unknown): ExtensionTemplateId[];
|
|
101
|
+
/** Permissions the selection forces into the manifest (locked in the UI). */
|
|
102
|
+
export declare function extensionTemplateRequiredPermissions(ids: readonly ExtensionTemplateId[]): string[];
|
|
103
|
+
/** Permissions ticked when a template is picked, minus the ones already forced. */
|
|
104
|
+
export declare function extensionTemplateRecommendedPermissions(ids: readonly ExtensionTemplateId[]): string[];
|
|
105
|
+
/** Components the selection locks on. */
|
|
106
|
+
export declare function extensionTemplateComponents(ids: readonly ExtensionTemplateId[]): ExtensionTemplateComponent[];
|
|
107
|
+
/** Templates whose component is a content script, in catalog order. */
|
|
108
|
+
export declare function extensionTemplatesWithContentScripts(ids: readonly ExtensionTemplateId[]): ExtensionTemplate[];
|
|
109
|
+
/** Every file the scaffold writes for a selection, background script included. */
|
|
110
|
+
export declare function extensionTemplateFiles(ids: readonly ExtensionTemplateId[]): string[];
|
|
111
|
+
//# sourceMappingURL=extension-templates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension-templates.d.ts","sourceRoot":"","sources":["../src/extension-templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,eAAO,MAAM,sBAAsB,uDAAwD,CAAA;AAC3F,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,EAAE,mBAA6B,CAAA;AAEtE;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,mDAAoD,CAAA;AAC9F,MAAM,MAAM,0BAA0B,GAAG,CAAC,OAAO,6BAA6B,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvF,+EAA+E;AAC/E,eAAO,MAAM,6BAA6B,QAAQ,CAAA;AAElD;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,sBAAmD,CAAA;AAEzF,MAAM,WAAW,iBAAiB;IACjC,EAAE,EAAE,mBAAmB,CAAA;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAA;IACf,4DAA4D;IAC5D,QAAQ,EAAE,SAAS,0BAA0B,EAAE,CAAA;IAC/C;;;;;;;OAOG;IACH,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC,6EAA6E;IAC7E,sBAAsB,EAAE,SAAS,MAAM,EAAE,CAAA;IACzC,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAA;IACb,0EAA0E;IAC1E,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,kFAAkF;IAClF,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAClC;AAED,eAAO,MAAM,0BAA0B,EAAE,SAAS,iBAAiB,EAoDlE,CAAA;AAMD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAElF;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,mBAAmB,GAAG,iBAAiB,CAM/E;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,EAAE,CAIjF;AAED,qFAAqF;AACrF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,EAAE,CAGjF;AAeD,6EAA6E;AAC7E,wBAAgB,oCAAoC,CAAC,GAAG,EAAE,SAAS,mBAAmB,EAAE,GAAG,MAAM,EAAE,CAElG;AAED,mFAAmF;AACnF,wBAAgB,uCAAuC,CAAC,GAAG,EAAE,SAAS,mBAAmB,EAAE,GAAG,MAAM,EAAE,CAGrG;AAED,yCAAyC;AACzC,wBAAgB,2BAA2B,CAC1C,GAAG,EAAE,SAAS,mBAAmB,EAAE,GACjC,0BAA0B,EAAE,CAQ9B;AAED,uEAAuE;AACvE,wBAAgB,oCAAoC,CACnD,GAAG,EAAE,SAAS,mBAAmB,EAAE,GACjC,iBAAiB,EAAE,CAErB;AAED,kFAAkF;AAClF,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,SAAS,mBAAmB,EAAE,GAAG,MAAM,EAAE,CAEpF"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extension templates — the "what kind of extension is this?" taxonomy.
|
|
3
|
+
*
|
|
4
|
+
* A template is not a folder of files to copy. It is a small set of claims
|
|
5
|
+
* about the extension's shape: which manifest components it cannot work
|
|
6
|
+
* without, which permissions those components force, and which files the
|
|
7
|
+
* scaffold has to write so the manifest does not point at something that isn't
|
|
8
|
+
* there. Everything downstream — the picker in the manifest generator, the
|
|
9
|
+
* locked checkboxes, the scaffolded file set, and (next phase) the MCP tool
|
|
10
|
+
* that hands this catalog to a developer's own agent — reads THIS module.
|
|
11
|
+
*
|
|
12
|
+
* It lives in shared-types rather than in dojo because the readers span
|
|
13
|
+
* packages: the website, catalog-bff (answering a developer's own agent), and
|
|
14
|
+
* the published `@extenshi/mcp` package, whose tool tells an agent what kinds of
|
|
15
|
+
* extension exist and what each costs in permissions. All three must give the
|
|
16
|
+
* same answer.
|
|
17
|
+
*
|
|
18
|
+
* SELF-CONTAINED ON PURPOSE — no imports, so it can be copied verbatim into
|
|
19
|
+
* `@extenshi/mcp`, which cannot depend on unpublished workspace packages (same
|
|
20
|
+
* constraint and same solution as welcome-agent-brief.ts).
|
|
21
|
+
*
|
|
22
|
+
* SINGLE SOURCE, TWO COPIES, byte-identical:
|
|
23
|
+
* shared-types/extension-templates.ts ← canonical
|
|
24
|
+
* tools/extenshi-mcp/src/extension-templates.ts ← copy, pinned by a test
|
|
25
|
+
* Edit the canonical file and copy it over; the sync test in the MCP package
|
|
26
|
+
* fails on any divergence inside the monorepo and skips in the npm tarball.
|
|
27
|
+
*
|
|
28
|
+
* Templates COMBINE. A side panel with a content script is one extension with
|
|
29
|
+
* two capabilities, not a fifth template — the picker is multi-select and every
|
|
30
|
+
* helper here takes a list and unions the result. That is why requirements are
|
|
31
|
+
* expressed as components + permissions rather than as whole manifests.
|
|
32
|
+
*/
|
|
33
|
+
export const EXTENSION_TEMPLATE_IDS = ['popup', 'sidepanel', 'content', 'overlay'];
|
|
34
|
+
/**
|
|
35
|
+
* The template applied when a project has never picked one.
|
|
36
|
+
*
|
|
37
|
+
* `popup` is deliberately the same shape the scaffold produced before templates
|
|
38
|
+
* existed, so an old project regenerates byte-for-byte what it did yesterday.
|
|
39
|
+
*/
|
|
40
|
+
export const DEFAULT_EXTENSION_TEMPLATE = 'popup';
|
|
41
|
+
/**
|
|
42
|
+
* Manifest components a template can require.
|
|
43
|
+
*
|
|
44
|
+
* These are the things the UI can lock: a side-panel extension whose
|
|
45
|
+
* `side_panel` key is turned off is not a side-panel extension, it is a broken
|
|
46
|
+
* one. Each maps to a group of fields in the manifest draft.
|
|
47
|
+
*/
|
|
48
|
+
export const EXTENSION_TEMPLATE_COMPONENTS = ['action', 'sidePanel', 'contentScript'];
|
|
49
|
+
/** Where scaffolded source goes, so the manifest and the file writer agree. */
|
|
50
|
+
export const EXTENSION_TEMPLATE_SOURCE_DIR = 'src';
|
|
51
|
+
/**
|
|
52
|
+
* The background service worker every template gets, template or not.
|
|
53
|
+
*
|
|
54
|
+
* It is not part of any one template because it carries the integration the
|
|
55
|
+
* whole product is built around: `setUninstallURL` must be registered at
|
|
56
|
+
* startup, and a template that skipped the background script would silently
|
|
57
|
+
* ship an extension that can never report why someone uninstalled it.
|
|
58
|
+
*/
|
|
59
|
+
export const EXTENSION_BACKGROUND_PATH = `${EXTENSION_TEMPLATE_SOURCE_DIR}/background.js`;
|
|
60
|
+
export const EXTENSION_TEMPLATE_CATALOG = [
|
|
61
|
+
{
|
|
62
|
+
id: 'popup',
|
|
63
|
+
label: 'Popup',
|
|
64
|
+
summary: 'Opens a small window from the toolbar icon. The default shape for a self-contained tool.',
|
|
65
|
+
requires: ['action'],
|
|
66
|
+
// A popup needs nothing. Saying so is the point: our own store-compliance
|
|
67
|
+
// advice is "ask for the minimum", and a starter that pads the manifest to
|
|
68
|
+
// look substantial teaches the opposite on day one.
|
|
69
|
+
requiredPermissions: [],
|
|
70
|
+
recommendedPermissions: ['storage'],
|
|
71
|
+
entry: `${EXTENSION_TEMPLATE_SOURCE_DIR}/popup.html`,
|
|
72
|
+
emits: [`${EXTENSION_TEMPLATE_SOURCE_DIR}/popup.html`],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: 'sidepanel',
|
|
76
|
+
label: 'Side panel',
|
|
77
|
+
summary: 'Opens a panel docked beside the page, which stays open while the user browses.',
|
|
78
|
+
requires: ['sidePanel', 'action'],
|
|
79
|
+
requiredPermissions: ['sidePanel'],
|
|
80
|
+
recommendedPermissions: [],
|
|
81
|
+
entry: `${EXTENSION_TEMPLATE_SOURCE_DIR}/sidepanel.html`,
|
|
82
|
+
emits: [
|
|
83
|
+
`${EXTENSION_TEMPLATE_SOURCE_DIR}/sidepanel.html`,
|
|
84
|
+
`${EXTENSION_TEMPLATE_SOURCE_DIR}/sidepanel.js`,
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: 'content',
|
|
89
|
+
label: 'Page enhancer',
|
|
90
|
+
summary: 'Runs inside specific sites you list and changes what is already on the page.',
|
|
91
|
+
requires: ['contentScript'],
|
|
92
|
+
requiredPermissions: [],
|
|
93
|
+
recommendedPermissions: [],
|
|
94
|
+
entry: `${EXTENSION_TEMPLATE_SOURCE_DIR}/content.js`,
|
|
95
|
+
emits: [`${EXTENSION_TEMPLATE_SOURCE_DIR}/content.js`],
|
|
96
|
+
defaultMatches: ['https://example.com/*'],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: 'overlay',
|
|
100
|
+
label: 'In-page assistant',
|
|
101
|
+
summary: 'Adds your own UI on top of any site — a form filler, a password helper, a clipper.',
|
|
102
|
+
requires: ['contentScript'],
|
|
103
|
+
requiredPermissions: [],
|
|
104
|
+
// An assistant that cannot remember anything between pages is a demo, so
|
|
105
|
+
// storage is ticked — but it stays removable, because an overlay that only
|
|
106
|
+
// reformats what is on screen genuinely does not need it.
|
|
107
|
+
recommendedPermissions: ['storage'],
|
|
108
|
+
entry: `${EXTENSION_TEMPLATE_SOURCE_DIR}/overlay.js`,
|
|
109
|
+
emits: [`${EXTENSION_TEMPLATE_SOURCE_DIR}/overlay.js`],
|
|
110
|
+
defaultMatches: ['<all_urls>'],
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
const BY_ID = new Map(EXTENSION_TEMPLATE_CATALOG.map((t) => [t.id, t]));
|
|
114
|
+
export function isExtensionTemplateId(value) {
|
|
115
|
+
return typeof value === 'string' && BY_ID.has(value);
|
|
116
|
+
}
|
|
117
|
+
export function getExtensionTemplate(id) {
|
|
118
|
+
const template = BY_ID.get(id);
|
|
119
|
+
// Unreachable through the type, but this module is also read from persisted
|
|
120
|
+
// JSON — throwing here beats returning undefined into a manifest builder.
|
|
121
|
+
if (!template)
|
|
122
|
+
throw new Error(`Unknown extension template: ${id}`);
|
|
123
|
+
return template;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Clean a persisted or client-supplied list into templates we know.
|
|
127
|
+
*
|
|
128
|
+
* Saved tool state is free-form JSON written by whatever build was live at the
|
|
129
|
+
* time, so an unknown id is expected rather than exceptional: it is dropped,
|
|
130
|
+
* not thrown on. Order follows the catalog so two projects that picked the same
|
|
131
|
+
* set produce the same manifest, and duplicates collapse.
|
|
132
|
+
*/
|
|
133
|
+
export function normalizeExtensionTemplates(value) {
|
|
134
|
+
if (!Array.isArray(value))
|
|
135
|
+
return [];
|
|
136
|
+
const picked = new Set(value.filter(isExtensionTemplateId));
|
|
137
|
+
return EXTENSION_TEMPLATE_CATALOG.filter((t) => picked.has(t.id)).map((t) => t.id);
|
|
138
|
+
}
|
|
139
|
+
/** The selection to build with: what was picked, or the default when nothing was. */
|
|
140
|
+
export function effectiveExtensionTemplates(value) {
|
|
141
|
+
const templates = normalizeExtensionTemplates(value);
|
|
142
|
+
return templates.length ? templates : [DEFAULT_EXTENSION_TEMPLATE];
|
|
143
|
+
}
|
|
144
|
+
function unionOf(ids, pick) {
|
|
145
|
+
const out = [];
|
|
146
|
+
for (const id of ids) {
|
|
147
|
+
for (const value of pick(getExtensionTemplate(id))) {
|
|
148
|
+
if (!out.includes(value))
|
|
149
|
+
out.push(value);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return out;
|
|
153
|
+
}
|
|
154
|
+
/** Permissions the selection forces into the manifest (locked in the UI). */
|
|
155
|
+
export function extensionTemplateRequiredPermissions(ids) {
|
|
156
|
+
return unionOf(ids, (t) => t.requiredPermissions);
|
|
157
|
+
}
|
|
158
|
+
/** Permissions ticked when a template is picked, minus the ones already forced. */
|
|
159
|
+
export function extensionTemplateRecommendedPermissions(ids) {
|
|
160
|
+
const required = extensionTemplateRequiredPermissions(ids);
|
|
161
|
+
return unionOf(ids, (t) => t.recommendedPermissions).filter((p) => !required.includes(p));
|
|
162
|
+
}
|
|
163
|
+
/** Components the selection locks on. */
|
|
164
|
+
export function extensionTemplateComponents(ids) {
|
|
165
|
+
const out = [];
|
|
166
|
+
for (const id of ids) {
|
|
167
|
+
for (const component of getExtensionTemplate(id).requires) {
|
|
168
|
+
if (!out.includes(component))
|
|
169
|
+
out.push(component);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return out;
|
|
173
|
+
}
|
|
174
|
+
/** Templates whose component is a content script, in catalog order. */
|
|
175
|
+
export function extensionTemplatesWithContentScripts(ids) {
|
|
176
|
+
return ids.map(getExtensionTemplate).filter((t) => t.requires.includes('contentScript'));
|
|
177
|
+
}
|
|
178
|
+
/** Every file the scaffold writes for a selection, background script included. */
|
|
179
|
+
export function extensionTemplateFiles(ids) {
|
|
180
|
+
return [EXTENSION_BACKGROUND_PATH, ...unionOf(ids, (t) => t.emits)];
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=extension-templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension-templates.js","sourceRoot":"","sources":["../src/extension-templates.ts"],"names":[],"mappings":"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"}
|
package/dist/icon-workflow.js
CHANGED
|
@@ -39,7 +39,7 @@ Extenshi CLI renders an offline verification page. No API key, no credits, no up
|
|
|
39
39
|
2. **Render the verification page** (free, offline):
|
|
40
40
|
|
|
41
41
|
\`\`\`bash
|
|
42
|
-
npx @extenshi/cli icon preview ${iconFile} --name "${name}"
|
|
42
|
+
npx @extenshi/cli@latest icon preview ${iconFile} --name "${name}"
|
|
43
43
|
\`\`\`
|
|
44
44
|
|
|
45
45
|
This writes a self-contained HTML file and opens it: Chrome / Firefox / Edge toolbar
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icon-workflow.js","sourceRoot":"","sources":["../src/icon-workflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,MAAM,UAAU,kBAAkB,CAAC,IAAsB;IACxD,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,cAAc,CAAA;IACzD,MAAM,QAAQ,GAAG,UAAU,CAAA;IAC3B,OAAO;;;;;;;;;;;;;;;;;;;;;+CAqBuC,QAAQ;;;;;
|
|
1
|
+
{"version":3,"file":"icon-workflow.js","sourceRoot":"","sources":["../src/icon-workflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,MAAM,UAAU,kBAAkB,CAAC,IAAsB;IACxD,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,cAAc,CAAA;IACzD,MAAM,QAAQ,GAAG,UAAU,CAAA;IAC3B,OAAO;;;;;;;;;;;;;;;;;;;;;+CAqBuC,QAAQ;;;;;2CAKZ,QAAQ,YAAY,IAAI;;;;;;;2DAOR,QAAQ;;;;;;;;;;;;;;;;;;;;;CAqBlE,CAAA;AACD,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ import { createRequire } from 'node:module';
|
|
|
23
23
|
import { FastMCP, UserError } from 'fastmcp';
|
|
24
24
|
import { makeBff } from './bff.js';
|
|
25
25
|
import { loadConfig } from './config.js';
|
|
26
|
+
import { reportServerStarted } from './startup.js';
|
|
26
27
|
import { flushTelemetry, initTelemetry } from './telemetry.js';
|
|
27
28
|
import { MISSING_KEY_MESSAGE, registerTools, SERVER_INSTRUCTIONS, SERVER_NAME, } from './tools.js';
|
|
28
29
|
import { checkForUpdate } from './update-check.js';
|
|
@@ -69,7 +70,21 @@ for (const signal of ['SIGTERM', 'SIGINT']) {
|
|
|
69
70
|
process.once('beforeExit', () => {
|
|
70
71
|
void flushTelemetry();
|
|
71
72
|
});
|
|
72
|
-
|
|
73
|
+
// Awaited, unlike the fire-and-forget calls around it. FastMCP's stdio `start()`
|
|
74
|
+
// is genuinely async — it awaits `session.connect(transport)` and only then
|
|
75
|
+
// registers the close handlers — so calling it and reporting on the next line
|
|
76
|
+
// would time the event to "we called start", not "the session connected". It
|
|
77
|
+
// resolves once connected rather than on shutdown, so awaiting it does not
|
|
78
|
+
// strand the lines below. A rejection stays as loud as it was before: nothing
|
|
79
|
+
// catches it, the process dies, and no event is emitted for a server that never
|
|
80
|
+
// served. Top-level await is available here (ESM, target ES2022).
|
|
81
|
+
await server.start({ transportType: 'stdio' });
|
|
82
|
+
// Reaching this line means a client actually EXECUTED the server and the stdio
|
|
83
|
+
// session connected: config parsed, tools registered, transport live. That is the
|
|
84
|
+
// population `mcp_installed` cannot describe — postinstall counts npm extractions,
|
|
85
|
+
// most of which are ecosystem scanners that never run the binary. See
|
|
86
|
+
// ./startup.ts for the measurement.
|
|
87
|
+
reportServerStarted(cfg.apiKey);
|
|
73
88
|
// Fire-and-forget: nudge to stderr if a newer version is on npm. Never blocks
|
|
74
89
|
// startup, never touches stdout (the MCP channel), fully fail-soft.
|
|
75
90
|
void checkForUpdate(pkg.version);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAC5C,OAAO,EAAY,OAAO,EAAE,MAAM,UAAU,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC9D,OAAO,EAEN,mBAAmB,EACnB,aAAa,EACb,mBAAmB,EACnB,WAAW,GAEX,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAElD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAwB,CAAA;AAE7D,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;AAEvD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAA;AAExB,0EAA0E;AAC1E,SAAS,UAAU;IAClB,IAAI,CAAC,GAAG,CAAC,MAAM;QAAE,MAAM,IAAI,SAAS,CAAC,mBAAmB,CAAC,CAAA;IACzD,OAAO,GAAG,CAAC,MAAM,CAAA;AAClB,CAAC;AAED,mFAAmF;AACnF,SAAS,GAAG;IACX,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;AACzC,CAAC;AAED,MAAM,MAAM,GAAG,IAAI,OAAO,CAAC;IAC1B,IAAI,EAAE,WAAW;IACjB,2FAA2F;IAC3F,OAAO,EAAE,GAAG,CAAC,OAA0C;IACvD,YAAY,EAAE,mBAAmB;CACjC,CAAC,CAAA;AAEF,4EAA4E;AAC5E,+EAA+E;AAC/E,MAAM,SAAS,GAAa;IAC3B,GAAG,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE;IACvE,YAAY,EAAE,IAAI,GAAG,CAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IACtE,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE;IACnB,aAAa,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE;IACjC,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,IAAI,SAAS;CACxC,CAAA;AAED,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;AAEhC,wEAAwE;AACxE,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,6DAA6D;AAC7D,KAAK,MAAM,MAAM,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAU,EAAE,CAAC;IACrD,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE;QACzB,KAAK,cAAc,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;IAClD,CAAC,CAAC,CAAA;AACH,CAAC;AACD,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,EAAE;IAC/B,KAAK,cAAc,EAAE,CAAA;AACtB,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "A real MCP client launched this server" — the signal `mcp_installed` is not.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: `mcp_installed` fires from scripts/postinstall.cjs the moment
|
|
5
|
+
* npm extracts the tarball, so it counts every machine that INSTALLS the
|
|
6
|
+
* package, whether or not anything ever runs the binary. Measured over the
|
|
7
|
+
* package's lifetime on 2026-08-08, that population is overwhelmingly automated:
|
|
8
|
+
* 107 of 109 install-reporting machines installed exactly one version, exactly
|
|
9
|
+
* once, and were never seen again; 88 of 122 install events were linux/x64
|
|
10
|
+
* across nine different Node majors, arriving within days of each publish. None
|
|
11
|
+
* of them ever emitted a tool call. That is the signature of npm-ecosystem
|
|
12
|
+
* scanners and mirrors, not of developers — so an "activation rate" computed as
|
|
13
|
+
* tool-callers ÷ installers measures robots, and reads as a product failure.
|
|
14
|
+
*
|
|
15
|
+
* A scanner sandbox runs `npm install` and stops there. An MCP client, by
|
|
16
|
+
* construction, EXECUTES the binary — so this event, emitted once per server
|
|
17
|
+
* process, is the honest denominator for "installed → actually used". Pair it
|
|
18
|
+
* with `mcp_tool_called` for a funnel whose numerator and denominator describe
|
|
19
|
+
* the same population.
|
|
20
|
+
*
|
|
21
|
+
* `has_api_key` is a boolean and never the key itself. It answers, at the
|
|
22
|
+
* population level, the question this funnel cannot otherwise settle: of the
|
|
23
|
+
* servers that really start, how many are configured well enough to be useful.
|
|
24
|
+
* Every read/scan tool refuses without a key (see MISSING_KEY_MESSAGE), so a
|
|
25
|
+
* high started-but-keyless share is an onboarding problem, not a demand problem.
|
|
26
|
+
*
|
|
27
|
+
* Privacy is unchanged from ./telemetry.ts: one boolean on top of the anonymous
|
|
28
|
+
* base properties. No path, no key, no user input.
|
|
29
|
+
*/
|
|
30
|
+
/** The event name, exported so tests and analyses can't drift from the emitter. */
|
|
31
|
+
export declare const SERVER_STARTED_EVENT = "mcp_server_started";
|
|
32
|
+
/** Sink shape, matching captureEvent — injectable so tests need no PostHog. */
|
|
33
|
+
type Capture = (event: string, properties: Record<string, unknown>) => void;
|
|
34
|
+
/**
|
|
35
|
+
* Everything this event carries beyond the anonymous base properties. Declared
|
|
36
|
+
* as a closed type rather than a `Record`, so "the payload is one boolean" is
|
|
37
|
+
* enforced by the compiler instead of asserted in prose: a later edit that tries
|
|
38
|
+
* to smuggle the key itself in here does not type-check.
|
|
39
|
+
*
|
|
40
|
+
* A type alias, not an `interface`, deliberately: an interface gets no implicit
|
|
41
|
+
* index signature, so it is not assignable to the `Record<string, unknown>` the
|
|
42
|
+
* sink takes and this would not compile.
|
|
43
|
+
*/
|
|
44
|
+
export type ServerStartedProperties = {
|
|
45
|
+
has_api_key: boolean;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The properties carried by SERVER_STARTED_EVENT. Split out from the emitter so
|
|
49
|
+
* the mapping "configured key → has_api_key" is directly testable, and so it is
|
|
50
|
+
* obvious at review time that the key's VALUE never reaches the payload.
|
|
51
|
+
*/
|
|
52
|
+
export declare function serverStartedProperties(apiKey: string | null | undefined): ServerStartedProperties;
|
|
53
|
+
/**
|
|
54
|
+
* Report that this process got far enough to serve the MCP protocol.
|
|
55
|
+
*
|
|
56
|
+
* Fail-soft in the same sense as everything else in this package: telemetry must
|
|
57
|
+
* never be able to stop a server from serving, so any throw from the sink is
|
|
58
|
+
* swallowed. Buffered like every other event — the periodic flush and the
|
|
59
|
+
* SIGTERM/beforeExit handlers in index.ts drain it.
|
|
60
|
+
*/
|
|
61
|
+
export declare function reportServerStarted(apiKey: string | null | undefined, capture?: Capture): void;
|
|
62
|
+
export {};
|
|
63
|
+
//# sourceMappingURL=startup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup.d.ts","sourceRoot":"","sources":["../src/startup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,mFAAmF;AACnF,eAAO,MAAM,oBAAoB,uBAAuB,CAAA;AAExD,+EAA+E;AAC/E,KAAK,OAAO,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;AAE3E;;;;;;;;;GASG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACrC,WAAW,EAAE,OAAO,CAAA;CACpB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,uBAAuB,CAElG;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAClC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACjC,OAAO,GAAE,OAAsB,GAC7B,IAAI,CAMN"}
|
package/dist/startup.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "A real MCP client launched this server" — the signal `mcp_installed` is not.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: `mcp_installed` fires from scripts/postinstall.cjs the moment
|
|
5
|
+
* npm extracts the tarball, so it counts every machine that INSTALLS the
|
|
6
|
+
* package, whether or not anything ever runs the binary. Measured over the
|
|
7
|
+
* package's lifetime on 2026-08-08, that population is overwhelmingly automated:
|
|
8
|
+
* 107 of 109 install-reporting machines installed exactly one version, exactly
|
|
9
|
+
* once, and were never seen again; 88 of 122 install events were linux/x64
|
|
10
|
+
* across nine different Node majors, arriving within days of each publish. None
|
|
11
|
+
* of them ever emitted a tool call. That is the signature of npm-ecosystem
|
|
12
|
+
* scanners and mirrors, not of developers — so an "activation rate" computed as
|
|
13
|
+
* tool-callers ÷ installers measures robots, and reads as a product failure.
|
|
14
|
+
*
|
|
15
|
+
* A scanner sandbox runs `npm install` and stops there. An MCP client, by
|
|
16
|
+
* construction, EXECUTES the binary — so this event, emitted once per server
|
|
17
|
+
* process, is the honest denominator for "installed → actually used". Pair it
|
|
18
|
+
* with `mcp_tool_called` for a funnel whose numerator and denominator describe
|
|
19
|
+
* the same population.
|
|
20
|
+
*
|
|
21
|
+
* `has_api_key` is a boolean and never the key itself. It answers, at the
|
|
22
|
+
* population level, the question this funnel cannot otherwise settle: of the
|
|
23
|
+
* servers that really start, how many are configured well enough to be useful.
|
|
24
|
+
* Every read/scan tool refuses without a key (see MISSING_KEY_MESSAGE), so a
|
|
25
|
+
* high started-but-keyless share is an onboarding problem, not a demand problem.
|
|
26
|
+
*
|
|
27
|
+
* Privacy is unchanged from ./telemetry.ts: one boolean on top of the anonymous
|
|
28
|
+
* base properties. No path, no key, no user input.
|
|
29
|
+
*/
|
|
30
|
+
import { captureEvent } from './telemetry.js';
|
|
31
|
+
/** The event name, exported so tests and analyses can't drift from the emitter. */
|
|
32
|
+
export const SERVER_STARTED_EVENT = 'mcp_server_started';
|
|
33
|
+
/**
|
|
34
|
+
* The properties carried by SERVER_STARTED_EVENT. Split out from the emitter so
|
|
35
|
+
* the mapping "configured key → has_api_key" is directly testable, and so it is
|
|
36
|
+
* obvious at review time that the key's VALUE never reaches the payload.
|
|
37
|
+
*/
|
|
38
|
+
export function serverStartedProperties(apiKey) {
|
|
39
|
+
return { has_api_key: Boolean(apiKey) };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Report that this process got far enough to serve the MCP protocol.
|
|
43
|
+
*
|
|
44
|
+
* Fail-soft in the same sense as everything else in this package: telemetry must
|
|
45
|
+
* never be able to stop a server from serving, so any throw from the sink is
|
|
46
|
+
* swallowed. Buffered like every other event — the periodic flush and the
|
|
47
|
+
* SIGTERM/beforeExit handlers in index.ts drain it.
|
|
48
|
+
*/
|
|
49
|
+
export function reportServerStarted(apiKey, capture = captureEvent) {
|
|
50
|
+
try {
|
|
51
|
+
capture(SERVER_STARTED_EVENT, serverStartedProperties(apiKey));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Never throw into startup.
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=startup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup.js","sourceRoot":"","sources":["../src/startup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,mFAAmF;AACnF,MAAM,CAAC,MAAM,oBAAoB,GAAG,oBAAoB,CAAA;AAmBxD;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAiC;IACxE,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAA;AACxC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAClC,MAAiC,EACjC,UAAmB,YAAY;IAE/B,IAAI,CAAC;QACJ,OAAO,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,MAAM,CAAC,CAAC,CAAA;IAC/D,CAAC;IAAC,MAAM,CAAC;QACR,4BAA4B;IAC7B,CAAC;AACF,CAAC"}
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -41,15 +41,16 @@ export declare function telemetryEnabled(): boolean;
|
|
|
41
41
|
export declare function redactText(s: string): string;
|
|
42
42
|
/**
|
|
43
43
|
* Coarse error bucket. Driven by a numeric status when the error carries one
|
|
44
|
-
* (ScanError `status`, tRPC `data.httpStatus`), else by message patterns (
|
|
45
|
-
*
|
|
46
|
-
* semantic buckets.
|
|
44
|
+
* (ScanError `status`, tRPC `data.httpStatus`), else by message patterns (both
|
|
45
|
+
* surfaces also throw plain Errors). Order matters — status first, then
|
|
46
|
+
* transport, then semantic buckets.
|
|
47
47
|
*
|
|
48
|
-
* Follows the `cause` chain
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* hangs off `cause`, so an
|
|
52
|
-
* without
|
|
48
|
+
* Follows the `cause` chain, because both surfaces re-throw a failure wrapped in
|
|
49
|
+
* an error of their own so it renders for the caller — the MCP tool layer wraps
|
|
50
|
+
* in fastmcp's `UserError` (readError() in its tools.ts) — and the wrapper's own
|
|
51
|
+
* message can carry no status. The origin hangs off `cause`, so an
|
|
52
|
+
* unclassifiable wrapper defers to what it wraps; without this, a BFF 500 behind
|
|
53
|
+
* a wrapper reads as `unexpected`.
|
|
53
54
|
*/
|
|
54
55
|
export declare function classifyError(err: unknown): string;
|
|
55
56
|
/**
|
package/dist/telemetry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AA+BH,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAG9E;AAmCD,wBAAgB,gBAAgB,IAAI,OAAO,CAO1C;AA6CD,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAO5C;
|
|
1
|
+
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AA+BH,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAG9E;AAmCD,wBAAgB,gBAAgB,IAAI,OAAO,CAO1C;AA6CD,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAO5C;AA2DD;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAgBlD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,MAAM,EAAE,CAM9E;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI,CAQ1F;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI,CAQtF;AASD;;;;GAIG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CASpD"}
|