@nekuda/webmcp-sdk 0.4.0-dev.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +244 -0
- package/dist/define.d.ts +79 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.js +1607 -0
- package/dist/register.d.ts +80 -0
- package/dist/spec.d.ts +56 -0
- package/dist/telemetry-context.d.ts +164 -0
- package/dist/telemetry-events.d.ts +224 -0
- package/dist/telemetry-fields.d.ts +119 -0
- package/dist/telemetry.d.ts +471 -0
- package/dist/tool-metrics.d.ts +52 -0
- package/dist/tracking.d.ts +208 -0
- package/dist/transport.d.ts +94 -0
- package/package.json +45 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased — usage telemetry schema 2 (breaking wire format)
|
|
4
|
+
|
|
5
|
+
The usage-telemetry channel emits **three events instead of two**, carrying
|
|
6
|
+
derived signals instead of raw page and payload strings. `schema` bumps `1` → `2`.
|
|
7
|
+
Nothing was deployed against `1`, so there is no compatibility shim: an ingest
|
|
8
|
+
reading v1 events reads nothing from this build. `registerTools` behavior, the
|
|
9
|
+
`tracking` channel, and every public API besides the exported event types are
|
|
10
|
+
unchanged. See `docs/telemetry-schema.md`.
|
|
11
|
+
|
|
12
|
+
### Breaking
|
|
13
|
+
|
|
14
|
+
- **`sdk_init` fires once per page load, not once per `registerTools` call.** It is
|
|
15
|
+
scheduled at import time and flushed on the next task, so it now fires even when
|
|
16
|
+
nobody registers anything — which is the only way a broken integration is
|
|
17
|
+
visible. A page with three `registerTools` calls no longer emits three "init"
|
|
18
|
+
events.
|
|
19
|
+
- **Per-call information moved to the new `tool_registration` event**, one per
|
|
20
|
+
`registerTools` call: `registrationIndex`, `trigger`, `settleMs`, the `config.*`
|
|
21
|
+
booleans (moved off `sdk_init`, which fires before any batch may have run), and
|
|
22
|
+
`tools[]` — the per-tool registration outcomes v1 computed and discarded, so a
|
|
23
|
+
site where 3 of 8 tools fail on a duplicate name is now distinguishable from one
|
|
24
|
+
where all 8 registered. Each entry carries a `schemaHash`, parameter-shape
|
|
25
|
+
metrics, and `descriptionLength` — never a parameter name, description, or schema.
|
|
26
|
+
The event emits when the batch settles, at a 2 s timeout, or on `pagehide` —
|
|
27
|
+
whichever comes first — so a surface whose `registerTool` never resolves still
|
|
28
|
+
reports; tools unsettled at that point carry `outcome: "pending"`, which is not one
|
|
29
|
+
of the registration states `registerTools` itself returns. Each telemetry-enabled
|
|
30
|
+
batch therefore holds one timer and one `{ once: true }` `pagehide` listener until
|
|
31
|
+
it emits, both released on emit.
|
|
32
|
+
- **`tool_call` carries derived signals only.** Added: `callIndex`,
|
|
33
|
+
`toolCallIndex`, `precededBy`, `routeTemplate`, `agentRuntime`,
|
|
34
|
+
`timeSinceInitMs`, `tool.{stableKey,schemaHash,intent}`,
|
|
35
|
+
`response.{bytes,contentBlocks,isError}`, and `errorSignature` (the message with
|
|
36
|
+
quoted literals, opaque tokens, emails, URLs, relative paths, and digit runs
|
|
37
|
+
templated to `*`, so failures cluster and interpolated values do not ship).
|
|
38
|
+
Removed:
|
|
39
|
+
raw `input`, raw `response`, raw `error`, and the whole environment/page block.
|
|
40
|
+
Consequence: it carries no input signal at all in the interim — the
|
|
41
|
+
`schemaValid`/`missingRequired` block needs a JSON-Schema subset validator and is
|
|
42
|
+
deferred.
|
|
43
|
+
- **Every high-cardinality and PII-shaped field is gone**, replaced by a derived
|
|
44
|
+
one: `fullUserAgent` → `client.agentRuntime` + UA-CH brand, `url`/`path` →
|
|
45
|
+
`routeTemplate` (`/products/12345` → `/products/:id`, word slugs kept and anything
|
|
46
|
+
else → `:token`, query and fragment dropped), `referrer` → `referrerClass`,
|
|
47
|
+
`screen` → `formFactor`,
|
|
48
|
+
`language` → its primary subtag, and `timezone`/`platform`/`title` dropped
|
|
49
|
+
outright. They are not allowlist entries set to `false` — they no longer exist,
|
|
50
|
+
so no allowlist change brings them back.
|
|
51
|
+
- **No browser storage anywhere in this channel.** `visitorId` is gone and
|
|
52
|
+
`sessionId` is minted in memory per page load (the ePrivacy Art. 5(3) concern).
|
|
53
|
+
Cross-visit stitching is permanently impossible here; the authenticated
|
|
54
|
+
`tracking` channel keeps its own storage-backed identity and this one never reads
|
|
55
|
+
it. `tracking: { disabled: true }` consequently has nothing to narrow here.
|
|
56
|
+
- **Arrival order is not guaranteed.** `tool_registration` can reach the wire
|
|
57
|
+
before `sdk_init`. Consumers join the three on `sessionId` and must not assume
|
|
58
|
+
ordering.
|
|
59
|
+
- **`TelemetryEvent` is now a discriminated union** of `SdkInitEvent |
|
|
60
|
+
ToolRegistrationEvent | ToolCallEvent`, exported alongside `RegisteredToolEntry`,
|
|
61
|
+
`TelemetryEnvelope`, and the derived value unions (`AgentRuntime`,
|
|
62
|
+
`ReferrerClass`, `SurfaceInfo`, `ToolShapeMetrics`, …). The v1 open event shape
|
|
63
|
+
(`[key: string]: unknown`) is gone, so a field cannot reach the wire with nothing
|
|
64
|
+
declaring it.
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
|
|
68
|
+
- **`globalThis.__WEBMCP_TELEMETRY__ = false`** silences the channel page-wide
|
|
69
|
+
without touching a `registerTools` call site — the only lever a site has when the
|
|
70
|
+
calls come from generated code it does not edit. Strictly `false`, like GPC is
|
|
71
|
+
strictly `true`. It, GPC, and an absent `document` are read at emit time and so
|
|
72
|
+
silence everything; `telemetry: false` cancels `sdk_init` page-wide but reaches the
|
|
73
|
+
other two events per batch — see `docs/telemetry-schema.md` for the exact matrix.
|
|
74
|
+
- **Optional `source` and `intent` on `defineTool`** (`"scanner_generated"` /
|
|
75
|
+
`"merchant_authored"`; `"answer"` / `"act"` / `"transact"`), validated as enums
|
|
76
|
+
and reported per tool. Both stay absent when omitted — absence is the honest
|
|
77
|
+
signal, not a defaulted `merchant_authored`. Both enums are exported from the
|
|
78
|
+
package entry as `ToolSource` / `ToolIntent`.
|
|
79
|
+
- **`x-api-key` on telemetry beacons when the page configures one.** Same
|
|
80
|
+
`/v1/telemetry` path either way; the header only adds tenant attribution, and an
|
|
81
|
+
unkeyed page keeps sending anonymously. Note it makes the request non-simple
|
|
82
|
+
under CORS, so an authenticated beacon costs an `OPTIONS` preflight.
|
|
83
|
+
- **`surface.provenance`** separates a `native` WebMCP surface from a `polyfill` or
|
|
84
|
+
`extension` one (a shim opts in via a `__webmcpProvenance` marker), and
|
|
85
|
+
`sdk.installMode` distinguishes an npm build from a CDN snippet — injected at build
|
|
86
|
+
time via `bun build --define '__WEBMCP_INSTALL_MODE__="cdn_snippet"'`, defaulting to
|
|
87
|
+
`"npm"` for any absent or unrecognized value.
|
|
88
|
+
- **`tools[]` degrades instead of dropping the event.** A batch over the 64 KB
|
|
89
|
+
bound first loses every entry's shape detail, keeping identity and outcome, and
|
|
90
|
+
only then collapses to `{ __truncated, originalBytes, toolCount }` — so "this
|
|
91
|
+
site registered 500 tools" survives even when the entries do not. Both degraded
|
|
92
|
+
shapes are named on the wire contract (`StrippedToolEntry`, `TruncatedTools`), so
|
|
93
|
+
`ToolRegistrationEvent.tools` is a union: narrow with `Array.isArray` before
|
|
94
|
+
reading entries.
|
|
95
|
+
- **Each batch's beacons carry that batch's own `apiKey`.** On a page where two
|
|
96
|
+
tenants each call `registerTools` with their own key, a batch's
|
|
97
|
+
`tool_registration` and `tool_call` authenticate as the tenant that registered
|
|
98
|
+
them rather than as whichever key the page configured last. A keyless batch still
|
|
99
|
+
authenticates as the tenant the page named, and `sdk_init` — which carries page
|
|
100
|
+
context and no tool identity — keeps using the page's key. `precededBy` is scoped
|
|
101
|
+
the same way, being the one field that puts another batch's `stableKey` on this
|
|
102
|
+
batch's event: the call chain breaks where the tenant changes rather than naming
|
|
103
|
+
tenant A's tool in an event signed with tenant B's key.
|
|
104
|
+
|
|
105
|
+
### Fixed
|
|
106
|
+
|
|
107
|
+
- **The baked ingest host is `ingest.<zone>`, which is the host the backend
|
|
108
|
+
actually serves.** Every published build pointed at `collect.agentlane.com` /
|
|
109
|
+
`collect.agentlane.dev`, and neither name has a DNS record: a dedicated
|
|
110
|
+
`collect.<zone>` host was planned, then dropped in favour of the DNS zone's
|
|
111
|
+
record budget, leaving the backend serving both paths on `ingest.<zone>` while
|
|
112
|
+
the SDK kept shipping the name that was cut. Every beacon on **both** channels —
|
|
113
|
+
`/v1/collect` as well as `/v1/telemetry` — failed at DNS resolution, and
|
|
114
|
+
silently, because the transport swallows every error by design.
|
|
115
|
+
|
|
116
|
+
Verified against dev: `collect.agentlane.dev` does not resolve, `ingest.agentlane.dev`
|
|
117
|
+
does, and the dev publish artifact built from this change delivers all three
|
|
118
|
+
telemetry events (HTTP 200) and lands them in the raw zone unquarantined. The
|
|
119
|
+
in-source fallback, both publish workflows and their baked-host guardrails move
|
|
120
|
+
together. Paths are unchanged and an explicit `endpoint` override still wins.
|
|
121
|
+
|
|
122
|
+
## 0.4.0 — @nekuda/webmcp-sdk, publishable dual-flavor builds
|
|
123
|
+
|
|
124
|
+
The package is renamed **`@nekuda/webmcp` → `@nekuda/webmcp-sdk`** and becomes
|
|
125
|
+
publishable: prod releases go to public npmjs (tag + human approval), dev
|
|
126
|
+
prereleases to npmjs under the `dev` dist-tag on every merge to `main`.
|
|
127
|
+
Telemetry `sdk.name` and the OTEL logger name follow the rename.
|
|
128
|
+
|
|
129
|
+
### Changed
|
|
130
|
+
|
|
131
|
+
- **Real ingest domains, baked per flavor.** The placeholder collect/telemetry
|
|
132
|
+
defaults are replaced by `https://collect.agentlane.com` (prod, also the
|
|
133
|
+
in-source fallback) and `https://collect.agentlane.dev` (dev builds), injected
|
|
134
|
+
at build time via `bun build --define __WEBMCP_INGEST_BASE__`. Paths are
|
|
135
|
+
unchanged (`/v1/collect`, `/v1/telemetry`); explicit `endpoint` overrides
|
|
136
|
+
still win.
|
|
137
|
+
- **Dev builds label their exact version.** `sdk.version` in telemetry events is
|
|
138
|
+
injectable via `__WEBMCP_SDK_VERSION__`, so a `0.4.0-dev.N` build no longer
|
|
139
|
+
reports itself as the base release.
|
|
140
|
+
- **Published artifact is `dist/`** — browser ESM bundle plus type declarations
|
|
141
|
+
(`main`/`types`/`exports` now point there; the tarball ships `dist` and this
|
|
142
|
+
changelog only). Relative imports in `src/` carry explicit `.js` extensions so the
|
|
143
|
+
emitted `.d.ts` resolves under `moduleResolution: node16`/`nodenext`; without them
|
|
144
|
+
a Node-ESM consumer with the usual `skipLibCheck: true` silently gets no types at
|
|
145
|
+
all for the whole package.
|
|
146
|
+
- **`@opentelemetry/api-logs` is no longer inlined into the bundle.** The build
|
|
147
|
+
marks it external, restoring the optional-peer design: the guarded lazy import
|
|
148
|
+
resolves the host's copy when installed and no-ops otherwise.
|
|
149
|
+
|
|
150
|
+
## 0.3.0 — default-on anonymous usage telemetry
|
|
151
|
+
|
|
152
|
+
Adds a second telemetry channel, independent of the opt-in `tracking` channel.
|
|
153
|
+
It is **on by default and needs no `apiKey`**, so it reports from every site the
|
|
154
|
+
SDK runs on. `tracking` behavior is unchanged.
|
|
155
|
+
|
|
156
|
+
### Added
|
|
157
|
+
|
|
158
|
+
- **Default-on usage telemetry.** One `sdk_init` event per `registerTools` call
|
|
159
|
+
(tools registered, WebMCP availability, browser, `tracking`/`otel`/custom-endpoint
|
|
160
|
+
booleans) and one `tool_call` event per settled invocation (outcome, duration,
|
|
161
|
+
error class). Fire-and-forget, no correlation ID, no batching.
|
|
162
|
+
- **Unauthenticated `/v1/telemetry` endpoint**, separate from `/v1/collect`: the
|
|
163
|
+
request carries no `x-api-key` and no key in the URL or body. As with the collect
|
|
164
|
+
default, the built-in domain is a **placeholder** — no prod telemetry endpoint is
|
|
165
|
+
stood up yet.
|
|
166
|
+
- **`telemetry: false` on `registerTools`** opts a batch out entirely: no events,
|
|
167
|
+
no identity resolution, no storage access, no network.
|
|
168
|
+
- **Global Privacy Control honored.** `navigator.globalPrivacyControl === true`
|
|
169
|
+
silences the channel with no opt-out flag needed.
|
|
170
|
+
- **Browser-only.** With no `document` in scope the channel emits nothing, so a
|
|
171
|
+
module-scope `registerTools` stays silent during SSR/prerender rather than
|
|
172
|
+
beaconing from the consumer's server. Unsupported browsers still report — they
|
|
173
|
+
are missing only `document.modelContext`, which is the adoption signal itself.
|
|
174
|
+
- **`tracking: { disabled: true }` keeps this channel out of browser storage.**
|
|
175
|
+
The two channels share one storage namespace, so a closed consent gate means no
|
|
176
|
+
identity is created, read, or emitted here either — the channel still reports
|
|
177
|
+
the storage-free adoption fields (`telemetry: false` and GPC are its opt-outs).
|
|
178
|
+
A consent-gated tenant also reports `config.trackingEnabled: false`, since that
|
|
179
|
+
channel emits nothing.
|
|
180
|
+
- **`TelemetryEvent` exported** from the entry module.
|
|
181
|
+
|
|
182
|
+
### Breaking
|
|
183
|
+
|
|
184
|
+
- **`defineTool` rejects a `stableKey` that isn't dot-namespaced `domain.action`**
|
|
185
|
+
(`TypeError`): two or more `[a-z0-9_]+` segments joined by `.`. Closes the gap
|
|
186
|
+
where a `stableKey` copying the wire `name` (e.g. `search_blog_posts`) passed
|
|
187
|
+
validation cleanly — exactly the rename-fragility the field exists to prevent.
|
|
188
|
+
|
|
189
|
+
### What v1 collects
|
|
190
|
+
|
|
191
|
+
v1 deliberately collects the **superset** of the spec's included and excluded
|
|
192
|
+
field sets: the envelope plus full user agent, language, timezone, screen,
|
|
193
|
+
platform, page fields (origin/url/path/referrer/title), anonymous
|
|
194
|
+
visitor/session identity, and — on `tool_call` — the tool identity, input,
|
|
195
|
+
response, and error message. Payloads are bounded to 64 KB; no stack traces.
|
|
196
|
+
Every field is gated by the typed allowlist in `src/telemetry-fields.ts`, all
|
|
197
|
+
`true` today, so dropping a field is a one-line change plus a release. Disabling
|
|
198
|
+
`visitorId`/`sessionId` there makes the channel storage-free.
|
|
199
|
+
|
|
200
|
+
## 0.2.0 — the tracking event schema and its transports (breaking)
|
|
201
|
+
|
|
202
|
+
Settles the tool-call event schema and both of its outputs, and adapts the
|
|
203
|
+
backend transport to the deployed ingest contract. Consumers of
|
|
204
|
+
`registerTools({ tracking })` must migrate.
|
|
205
|
+
|
|
206
|
+
### Added
|
|
207
|
+
|
|
208
|
+
- **`otel: true` on `tracking`** emits each event as an OTEL LogRecord through the
|
|
209
|
+
global `LoggerProvider` (optional peer dependency `@opentelemetry/api-logs`
|
|
210
|
+
`>=0.50.0`; the host app owns exporters). Independent of and composable with the
|
|
211
|
+
backend transport — a call can emit to neither output, either, or both.
|
|
212
|
+
- **`endpoint` on `tracking`** overrides the collect URL.
|
|
213
|
+
- **Optional `ToolDefinition.version`**, emitted as `toolVersion` for drift
|
|
214
|
+
analytics; absent from events when omitted.
|
|
215
|
+
- **`callId`** correlates each invocation's request/response pair, and
|
|
216
|
+
`duration_ms` reports client-measured elapsed time on the response event.
|
|
217
|
+
- **Serialized events bounded to 64 KB** (schema §1.5): `response`, then `input`,
|
|
218
|
+
then `error` are replaced by a `{ __truncated: true, originalBytes }` marker only
|
|
219
|
+
as far as needed to fit, then page strings are sliced.
|
|
220
|
+
|
|
221
|
+
### Breaking
|
|
222
|
+
|
|
223
|
+
- **The SDK is silent by default.** The 0.1.0 `console.log` tracking stub is gone:
|
|
224
|
+
with neither `apiKey` nor `otel` set, no event is built at all — no identity, no
|
|
225
|
+
storage access, no output. Anything relying on the console output must set `otel`.
|
|
226
|
+
- **`customerId` removed from `TrackingOptions` and the event body.** It was
|
|
227
|
+
required in 0.1.0; the backend now resolves the `api_key` to an `org_id` and
|
|
228
|
+
injects it server-side, so `apiKey` is the sole tenant credential.
|
|
229
|
+
- **Transport is `x-api-key` header + `fetch`, not `?api_key=` + `sendBeacon`.**
|
|
230
|
+
The deployed authorizer's identity source is the `x-api-key` header, which
|
|
231
|
+
`sendBeacon` cannot set, so the beacon path is dropped. Sends now use
|
|
232
|
+
`fetch(endpoint, { keepalive: true, headers: { "x-api-key": … } })` — the key
|
|
233
|
+
travels only in the header, never in the URL or body.
|
|
234
|
+
- **`defineTool` rejects a `stableKey` or `version` over 1024 characters**
|
|
235
|
+
(`TypeError`). They are identity keys copied verbatim onto every event and never
|
|
236
|
+
truncated in flight, so the cap is enforced at authoring time instead.
|
|
237
|
+
|
|
238
|
+
### Migration
|
|
239
|
+
|
|
240
|
+
- Replace `tracking: { customerId }` with `tracking: { apiKey }`.
|
|
241
|
+
- No prod collect domain is stood up yet: consumers targeting a live backend
|
|
242
|
+
must pass an explicit `endpoint` (e.g. the deployed dev API Gateway URL).
|
|
243
|
+
- Client-side visitor/session storage is now namespaced off a non-reversible
|
|
244
|
+
hash of `apiKey` instead of `customerId`; existing anonymous identities re-mint.
|
package/dist/define.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { ToolAnnotations } from "./spec.js";
|
|
2
|
+
/** How the tool came to exist. Reported on telemetry as `tools[].source`. */
|
|
3
|
+
export type ToolSource = "scanner_generated" | "merchant_authored";
|
|
4
|
+
/** What the tool is for. Reported on telemetry as `tools[].intent`. */
|
|
5
|
+
export type ToolIntent = "answer" | "act" | "transact";
|
|
6
|
+
/** The declared `source`/`intent` values, shared with the telemetry builders. */
|
|
7
|
+
export declare const TOOL_SOURCES: readonly ToolSource[];
|
|
8
|
+
export declare const TOOL_INTENTS: readonly ToolIntent[];
|
|
9
|
+
/**
|
|
10
|
+
* A developer-authored (or plugin-generated) tool declaration. Declaring is
|
|
11
|
+
* side-effect-free — nothing touches the browser until `registerTools`.
|
|
12
|
+
*/
|
|
13
|
+
export interface ToolDefinition<TInput extends Record<string, unknown> = Record<string, unknown>> {
|
|
14
|
+
/**
|
|
15
|
+
* Developer-authored durable identity. The WebMCP `name` is the wire identity and
|
|
16
|
+
* may be renamed freely; `stableKey` is what the platform keys on when the site is
|
|
17
|
+
* later connected (analytics continuity, claiming), so it must survive renames and
|
|
18
|
+
* codegen re-runs. It is never sent to the browser API. Enforced shape: dot-namespaced
|
|
19
|
+
* `domain.action` (`[a-z0-9_]+` segments, at least two, joined by `.`) — see
|
|
20
|
+
* `STABLE_KEY_PATTERN`.
|
|
21
|
+
*/
|
|
22
|
+
stableKey: string;
|
|
23
|
+
/**
|
|
24
|
+
* WebMCP tool name: 1–128 chars of [A-Za-z0-9_\-.]. Optional — defaults to
|
|
25
|
+
* `stableKey` when omitted, so a tool needs an explicit name only when its wire
|
|
26
|
+
* identity should differ from its durable key.
|
|
27
|
+
*/
|
|
28
|
+
name?: string;
|
|
29
|
+
/** Optional human-readable display name. */
|
|
30
|
+
title?: string;
|
|
31
|
+
/** What the tool does, for the agent. Required and non-empty per spec. */
|
|
32
|
+
description: string;
|
|
33
|
+
/** JSON Schema for `execute`'s input, as a plain object. */
|
|
34
|
+
inputSchema?: Record<string, unknown>;
|
|
35
|
+
annotations?: ToolAnnotations;
|
|
36
|
+
/**
|
|
37
|
+
* Optional per-tool version, surfaced on emitted tracking events as `toolVersion`
|
|
38
|
+
* for drift analytics. Free-form string (e.g. semver or a codegen hash); when
|
|
39
|
+
* omitted, `toolVersion` is absent from emitted events (the backend defaults it
|
|
40
|
+
* to `unknown`).
|
|
41
|
+
*/
|
|
42
|
+
version?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Optional provenance: was this tool emitted by the scanner or written by hand?
|
|
45
|
+
* Left absent when omitted — the SDK never guesses `merchant_authored`, since
|
|
46
|
+
* absence is the honest signal and the backend can bucket it separately.
|
|
47
|
+
*/
|
|
48
|
+
source?: ToolSource;
|
|
49
|
+
/**
|
|
50
|
+
* Optional purpose classification, reported per tool on telemetry. Per-tool
|
|
51
|
+
* rather than per-batch because one tool answers while another transacts.
|
|
52
|
+
*/
|
|
53
|
+
intent?: ToolIntent;
|
|
54
|
+
/**
|
|
55
|
+
* The page-owned behavior. May return anything JSON-serializable, a plain string,
|
|
56
|
+
* or a ready-made `{ content: [...] }` result — the SDK normalizes for the agent.
|
|
57
|
+
* Thrown errors propagate to the agent unchanged.
|
|
58
|
+
*/
|
|
59
|
+
execute(input: TInput): unknown;
|
|
60
|
+
}
|
|
61
|
+
declare const defined: unique symbol;
|
|
62
|
+
/**
|
|
63
|
+
* A validated, frozen tool — the only thing `registerTools` accepts. `name` is
|
|
64
|
+
* always resolved (falling back to `stableKey`), so it is required here.
|
|
65
|
+
*/
|
|
66
|
+
export type WebMCPTool<TInput extends Record<string, unknown> = Record<string, unknown>> = Readonly<ToolDefinition<TInput> & {
|
|
67
|
+
name: string;
|
|
68
|
+
}> & {
|
|
69
|
+
readonly [defined]: true;
|
|
70
|
+
};
|
|
71
|
+
/** `WebMCPTool` erased of its input type, for heterogeneous collections. */
|
|
72
|
+
export type AnyWebMCPTool = WebMCPTool<any>;
|
|
73
|
+
/**
|
|
74
|
+
* Validate and freeze a tool definition. Validation is eager and throws `TypeError`
|
|
75
|
+
* so an invalid tool fails at module-evaluation time (an authoring/codegen bug),
|
|
76
|
+
* not at registration time on some visitor's browser.
|
|
77
|
+
*/
|
|
78
|
+
export declare function defineTool<TInput extends Record<string, unknown> = Record<string, unknown>>(definition: ToolDefinition<TInput>): WebMCPTool<TInput>;
|
|
79
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nekuda/webmcp-sdk — the phase-1 WebMCP SDK: a thin wrapper over the plain-JS WebMCP
|
|
3
|
+
* spec (https://webmachinelearning.github.io/webmcp/) for registering and
|
|
4
|
+
* unregistering tools. Plugin-generated code targets THIS surface, never the raw
|
|
5
|
+
* `document.modelContext` API — the wrapper pins a spec version and absorbs the
|
|
6
|
+
* draft's monthly breaking churn (R23).
|
|
7
|
+
*
|
|
8
|
+
* The seam contract (what plugin codegen may rely on):
|
|
9
|
+
*
|
|
10
|
+
* 1. `defineTool(definition)` is side-effect-free: it validates eagerly, freezes,
|
|
11
|
+
* and returns the tool. Nothing touches the browser until `registerTools`.
|
|
12
|
+
* 2. `stableKey` lives on the definition, next to `name`. It is the
|
|
13
|
+
* developer-authored durable identity — it survives renames and codegen re-runs
|
|
14
|
+
* and is never sent to the browser.
|
|
15
|
+
* 3. Registration lifecycle: `registerTools(tools, { signal?, tracking?, telemetry? })`
|
|
16
|
+
* registers on call
|
|
17
|
+
* and returns `{ ready, unregister, signal }`. Unregistration happens ONLY via
|
|
18
|
+
* `unregister()` or the external signal aborting (page teardown / SPA unmount).
|
|
19
|
+
* Browsers without a WebMCP surface are a graceful no-op (`state: "unsupported"`).
|
|
20
|
+
* 4. "Connect later without rewrite": generated modules only `defineTool` and
|
|
21
|
+
* EXPORT tools; one entry module calls `registerTools`. Connecting a site to the
|
|
22
|
+
* platform later (API key, telemetry) changes wrapper internals and config only —
|
|
23
|
+
* generated modules and their imports do not change. See `examples/add-to-cart.ts`.
|
|
24
|
+
* 5. Anonymous tool-call tracking is opt-in via `registerTools`'s `tracking`
|
|
25
|
+
* option (see `TrackingOptions`) and default-silent: with neither `apiKey`
|
|
26
|
+
* nor `otel` set, no event is built at all — no identity resolution, no
|
|
27
|
+
* storage writes, no network, and zero behavior change. When enabled it
|
|
28
|
+
* resolves an anonymous visitor/session identity and emits a
|
|
29
|
+
* request+response event per tool call. The two outputs are independent and
|
|
30
|
+
* composable: `apiKey` POSTs each event to the collect endpoint via `fetch`
|
|
31
|
+
* (`keepalive: true`), authenticated with the `x-api-key` **header** (never a
|
|
32
|
+
* query param or body field); the backend resolves the key to `org_id` and
|
|
33
|
+
* injects it server-side, so the client never sends or knows it. `otel: true`
|
|
34
|
+
* emits each event as a
|
|
35
|
+
* LogRecord through the global OTEL `LoggerProvider` (optional peer dep —
|
|
36
|
+
* the host app owns exporters). `disabled: true` is the hard consent gate for
|
|
37
|
+
* both outputs — but for THIS channel only; the default-on telemetry channel of
|
|
38
|
+
* point 6 has its own opt-out (`telemetry: false`) and `disabled` does not
|
|
39
|
+
* narrow it. See `src/tracking.ts`, `src/transport.ts`, and
|
|
40
|
+
* `examples/add-to-cart.ts`.
|
|
41
|
+
* 6. Anonymous usage telemetry is a SECOND, independent channel: default-ON, so it
|
|
42
|
+
* reports from every site the SDK runs on, not only from `apiKey` tenants. Three
|
|
43
|
+
* events (`TelemetryEvent`), all `schema: 2` and joined on an in-memory
|
|
44
|
+
* `sessionId`: `sdk_init` once per page load, scheduled at *import* time so a
|
|
45
|
+
* site that loads the SDK and never registers is still visible;
|
|
46
|
+
* `tool_registration` once per `registerTools` call, carrying each tool's
|
|
47
|
+
* outcome; and `tool_call` once per settled invocation, as derived signals only —
|
|
48
|
+
* no raw `input`, `response`, or error message, unlike point 5. All three go to a
|
|
49
|
+
* separate `/v1/telemetry` endpoint, fire-and-forget, no batching; arrival order
|
|
50
|
+
* is NOT guaranteed, so consumers join on `sessionId` and never on ordering. A
|
|
51
|
+
* page that configures point 5's `apiKey` has it sent here as `x-api-key` too,
|
|
52
|
+
* which only adds tenant attribution — an unkeyed page sends anonymously to the
|
|
53
|
+
* same path. It composes with point 5: a call can emit to neither channel, either
|
|
54
|
+
* one, or both. Three page-level levers silence it entirely (no event, no
|
|
55
|
+
* network), because all three are read at emit time:
|
|
56
|
+
* `globalThis.__WEBMCP_TELEMETRY__ = false` (strictly `false`), Global Privacy
|
|
57
|
+
* Control (`navigator.globalPrivacyControl === true`), and no `document` in scope —
|
|
58
|
+
* so the module-scope `registerTools` of point 4 stays silent during
|
|
59
|
+
* SSR/prerender instead of beaconing from the consumer's server. A fourth,
|
|
60
|
+
* `telemetry: false` on `registerTools`, silences that batch's `tool_registration`
|
|
61
|
+
* and `tool_call` and cancels `sdk_init` page-wide; see `RegisterToolsOptions` for
|
|
62
|
+
* what that does and does not cover. No browser storage is touched
|
|
63
|
+
* anywhere in this channel: no `visitorId`, no stored `sessionId`, which is why
|
|
64
|
+
* point 5's `disabled` consent gate has nothing to narrow here. An unsupported
|
|
65
|
+
* browser still reports — it is missing only `document.modelContext`, and that
|
|
66
|
+
* fact is itself the adoption signal. Every field is gated by the two baked-in
|
|
67
|
+
* allowlists in `src/telemetry-fields.ts` (one by event path, one by per-tool
|
|
68
|
+
* key; all `true` today), so dropping a field is a one-line change there. See
|
|
69
|
+
* `src/telemetry.ts` and `docs/telemetry-schema.md`.
|
|
70
|
+
* 7. Transport wiring (endpoints, the OTEL logs API, apiKey handling) is
|
|
71
|
+
* wrapper-internal: it is reached only through `TrackingOptions` and the
|
|
72
|
+
* `telemetry` flag, never as a separate export, so connecting a site to the
|
|
73
|
+
* platform stays a config-only change to the entry module (point 4).
|
|
74
|
+
*/
|
|
75
|
+
export { type AnyWebMCPTool, type ToolDefinition, type ToolIntent, type ToolSource, type WebMCPTool, defineTool, } from "./define.js";
|
|
76
|
+
export { type RegisterToolsOptions, type ToolRegistration, type ToolRegistrationResult, type ToolRegistrationState, registerTools, } from "./register.js";
|
|
77
|
+
export { type ModelContextLike, type RegisterToolOptions, type SpecTool, type ToolAnnotations, resolveModelContext, } from "./spec.js";
|
|
78
|
+
export type { AgentRuntime, FormFactor, FrameContext, PageVisibility, ReferrerClass, SurfaceGlobal, SurfaceInfo, SurfaceProvenance, } from "./telemetry-context.js";
|
|
79
|
+
export type { ClientContext, InstallMode, PageContext, RegisteredToolEntry, RegistrationTrigger, SdkInfo, SdkInitEvent, StrippedToolEntry, TelemetryEnvelope, TelemetryEvent, TelemetryEventName, ToolCallEvent, ToolCallOutcome, ToolCallResponseMetrics, ToolCallToolInfo, ToolRegistrationEvent, ToolRegistrationOutcome, TrackingConfigInfo, TruncatedTools, } from "./telemetry-events.js";
|
|
80
|
+
/** The per-tool shape metrics `tool_registration` entries inherit as optional. */
|
|
81
|
+
export type { ToolShapeMetrics } from "./tool-metrics.js";
|
|
82
|
+
export type { TrackingEvent, TrackingOptions } from "./tracking.js";
|