@nekuda/webmcp-sdk 0.7.0-dev.18.1 → 0.7.0-dev.20.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/CHANGELOG.md +7 -0
- package/dist/index.js +4 -4
- package/dist/telemetry.d.ts +6 -3
- package/dist/transport.d.ts +13 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased — the telemetry key rides the query string
|
|
4
|
+
|
|
5
|
+
- A keyed telemetry beacon sends its publishable key as the `key` query parameter
|
|
6
|
+
instead of the `x-api-key` header. The header made every keyed beacon a non-simple
|
|
7
|
+
request with an `OPTIONS` preflight, and every hosted-snippet install is keyed. The
|
|
8
|
+
backend reads both; nothing else changes.
|
|
9
|
+
|
|
3
10
|
## 0.7.0 — 2026-09-21
|
|
4
11
|
|
|
5
12
|
One change, made twice: a page load costs the ingest edge one request instead of
|
package/dist/index.js
CHANGED
|
@@ -180,16 +180,16 @@ function sendToCollect(event, config, scope = globalThis) {
|
|
|
180
180
|
} catch {}
|
|
181
181
|
}
|
|
182
182
|
var TELEMETRY_CONTENT_TYPE = "text/plain";
|
|
183
|
+
var TELEMETRY_KEY_PARAM = "key";
|
|
183
184
|
function sendTelemetry(event, scope = globalThis, endpoint, apiKey) {
|
|
184
185
|
try {
|
|
185
186
|
const json = JSON.stringify(event);
|
|
186
187
|
if (json === undefined)
|
|
187
188
|
return;
|
|
188
|
-
const
|
|
189
|
+
const base = endpoint || DEFAULT_TELEMETRY_ENDPOINT;
|
|
189
190
|
const headers = { "content-type": TELEMETRY_CONTENT_TYPE };
|
|
190
191
|
const authenticated = typeof apiKey === "string" && apiKey.trim().length > 0;
|
|
191
|
-
|
|
192
|
-
headers["x-api-key"] = apiKey;
|
|
192
|
+
const url = authenticated ? `${base}${base.includes("?") ? "&" : "?"}${TELEMETRY_KEY_PARAM}=${encodeURIComponent(apiKey)}` : base;
|
|
193
193
|
tryFetch(scope, url, headers, json, authenticated ? (response) => {
|
|
194
194
|
if (isUnauthorized(response))
|
|
195
195
|
warnKeyRejected(scope);
|
|
@@ -1105,7 +1105,7 @@ function shapeMetrics(inputSchema) {
|
|
|
1105
1105
|
|
|
1106
1106
|
// src/telemetry.ts
|
|
1107
1107
|
var SDK_NAME = "@nekuda/webmcp-sdk";
|
|
1108
|
-
var SDK_VERSION = "0.7.0-dev.
|
|
1108
|
+
var SDK_VERSION = "0.7.0-dev.20.1";
|
|
1109
1109
|
var INSTALL_MODES = ["npm", "cdn_snippet"];
|
|
1110
1110
|
var SDK_INSTALL_MODE = INSTALL_MODES.find((mode) => mode === (typeof __WEBMCP_INSTALL_MODE__ === "string" ? __WEBMCP_INSTALL_MODE__ : "")) ?? "npm";
|
|
1111
1111
|
function parseSampleRate(raw) {
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -15,9 +15,12 @@
|
|
|
15
15
|
* may share a request as a `batch` envelope (see {@link PAGE_BATCH_WINDOW_MS}).
|
|
16
16
|
*
|
|
17
17
|
* No browser storage is touched anywhere in this channel — no `visitorId`, no
|
|
18
|
-
* stored `sessionId` (the ePrivacy Art. 5(3) concern).
|
|
19
|
-
*
|
|
20
|
-
* storage-backed identity, and this one never reads it.
|
|
18
|
+
* stored `sessionId` (the ePrivacy Art. 5(3) concern). Nothing this SDK sends can
|
|
19
|
+
* stitch two visits together; the authenticated `tracking` channel keeps its own
|
|
20
|
+
* storage-backed identity, and this one never reads it. The ingest edge derives a
|
|
21
|
+
* bounded identifier of its own from the request it receives (a daily-salted hash,
|
|
22
|
+
* see *Privacy posture* in `docs/telemetry-schema.md`), which is not something the
|
|
23
|
+
* client participates in or can observe.
|
|
21
24
|
*
|
|
22
25
|
* Every field an event may carry is gated by the baked-in allowlists in
|
|
23
26
|
* `src/telemetry-fields.ts` — one keyed by event path, one by per-tool field.
|
package/dist/transport.d.ts
CHANGED
|
@@ -58,6 +58,8 @@ export interface CollectConfig {
|
|
|
58
58
|
export declare function sendToCollect(event: TrackingEvent, config: CollectConfig, scope?: object): void;
|
|
59
59
|
/** CORS-safelisted, so an anonymous beacon needs no preflight — see sendTelemetry. */
|
|
60
60
|
export declare const TELEMETRY_CONTENT_TYPE = "text/plain";
|
|
61
|
+
/** Query parameter carrying the publishable key; a header would force a preflight (see sendTelemetry). */
|
|
62
|
+
export declare const TELEMETRY_KEY_PARAM = "key";
|
|
61
63
|
/**
|
|
62
64
|
* POST a usage-telemetry event to the telemetry endpoint via `fetch(keepalive)`.
|
|
63
65
|
* The path is the same whether or not `apiKey` is given: the header only *adds*
|
|
@@ -79,8 +81,17 @@ export declare const TELEMETRY_CONTENT_TYPE = "text/plain";
|
|
|
79
81
|
* per event at the edge; at 2026-09 volume that preflight was roughly half of the
|
|
80
82
|
* seven million requests a day the collect API billed. The edge maps `text/plain`
|
|
81
83
|
* to the same template as JSON (ingestion-edge/telemetry.tf), so nothing changes
|
|
82
|
-
* on the wire but the header.
|
|
83
|
-
*
|
|
84
|
+
* on the wire but the header.
|
|
85
|
+
*
|
|
86
|
+
* The publishable key travels as the `key` query parameter, not as `x-api-key`. A
|
|
87
|
+
* custom header is what makes a request non-simple, so a keyed beacon with the header
|
|
88
|
+
* paid the preflight whatever its content type — and every hosted-snippet install IS
|
|
89
|
+
* keyed, because the manifest hands it a default publishable key (ADR-0020). Measured
|
|
90
|
+
* the morning after the content-type change, those keyed preflights were still ~200k
|
|
91
|
+
* requests per two hours on the telemetry route. The key is public by design (it sits
|
|
92
|
+
* in the page's HTML), the authorizer reads the query parameter and the header alike,
|
|
93
|
+
* and the WAF redacts the query string from its logs. `x-api-key` stays accepted for
|
|
94
|
+
* older bundles.
|
|
84
95
|
*
|
|
85
96
|
* A `401` on this path means the key that WAS sent resolved to nothing; the beacon
|
|
86
97
|
* itself was still recorded, anonymously. That is reported through
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nekuda/webmcp-sdk",
|
|
3
|
-
"version": "0.7.0-dev.
|
|
3
|
+
"version": "0.7.0-dev.20.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Phase-1 WebMCP SDK: a thin wrapper over document.modelContext that plugin-generated code targets — defineTool + register/unregister lifecycle. This package pins the plugin↔SDK seam; anonymous tool-call tracking (backend transport via apiKey, OTEL LogRecords via otel) is opt-in through registerTools and default-silent, while anonymous usage telemetry is a separate unauthenticated channel that is on by default (opt out with telemetry: false).",
|
|
6
6
|
"main": "./dist/index.js",
|