@mcp-b/webmcp-polyfill 6.0.2 → 6.1.0
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/LICENSE.webmcp-polyfill +202 -21
- package/README.md +12 -108
- package/dist/index.d.ts +7 -4
- package/dist/index.iife.js +4 -4
- package/dist/index.js +14 -852
- package/dist/index.js.map +1 -1
- package/package.json +4 -7
package/dist/index.js
CHANGED
|
@@ -1,458 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
const annotationNames = [
|
|
3
|
-
"consequentialHint",
|
|
4
|
-
"debugging",
|
|
5
|
-
"readOnlyHint",
|
|
6
|
-
"untrustedContentHint"
|
|
7
|
-
];
|
|
8
|
-
const protocol = "webmcp-polyfill";
|
|
9
|
-
const deadline = 500;
|
|
10
|
-
const NativeDOMException$1 = globalThis.DOMException;
|
|
11
|
-
var FrameBridge = class {
|
|
12
|
-
#document;
|
|
13
|
-
#window;
|
|
14
|
-
#handlers;
|
|
15
|
-
#knownPeers = /* @__PURE__ */ new Set();
|
|
16
|
-
#unanswered = /* @__PURE__ */ new Set();
|
|
17
|
-
#started = Promise.withResolvers();
|
|
18
|
-
#handshakes = /* @__PURE__ */ new Map();
|
|
19
|
-
#sessions = /* @__PURE__ */ new Set();
|
|
20
|
-
#monitor;
|
|
21
|
-
#hidden = false;
|
|
22
|
-
constructor(view, handlers) {
|
|
23
|
-
this.#window = view;
|
|
24
|
-
this.#document = view.document;
|
|
25
|
-
this.#handlers = handlers;
|
|
26
|
-
this.#window.addEventListener("message", (event) => this.#receive(event), true);
|
|
27
|
-
this.#window.addEventListener("pagehide", () => {
|
|
28
|
-
this.#hidden = true;
|
|
29
|
-
for (const session of this.#sessions) session.end();
|
|
30
|
-
});
|
|
31
|
-
this.#window.addEventListener("pageshow", (event) => {
|
|
32
|
-
if (event.persisted) {
|
|
33
|
-
this.#hidden = false;
|
|
34
|
-
this.#announce();
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
for (const frame of this.#announce()) this.#unanswered.add(frame);
|
|
38
|
-
if (this.#unanswered.size === 0) this.#started.resolve();
|
|
39
|
-
else setTimeout(() => {
|
|
40
|
-
this.#unanswered.clear();
|
|
41
|
-
this.#started.resolve();
|
|
42
|
-
}, deadline);
|
|
43
|
-
}
|
|
44
|
-
async allowed() {
|
|
45
|
-
if (this.#hidden) return false;
|
|
46
|
-
return this.#documentAllowed(this.#document);
|
|
47
|
-
}
|
|
48
|
-
async getTools(fromOrigins) {
|
|
49
|
-
await this.#started.promise;
|
|
50
|
-
const ownOrigin = this.#window.origin;
|
|
51
|
-
const origins = [ownOrigin, ...fromOrigins];
|
|
52
|
-
return (await Promise.all(this.#participants().map(async (peer) => {
|
|
53
|
-
if (peer === this.#window) return this.#handlers.getTools(ownOrigin).map((metadata) => toRegisteredTool(metadata, this.#window, ownOrigin));
|
|
54
|
-
try {
|
|
55
|
-
const reply = await this.#request(peer, origins, { kind: "getTools" });
|
|
56
|
-
if (!Array.isArray(reply.value)) throw new TypeError("Expected a tool list from the frame");
|
|
57
|
-
return reply.value.map((value) => toRegisteredTool(readToolMetadata(value), peer, reply.origin));
|
|
58
|
-
} catch (error) {
|
|
59
|
-
if (!(error instanceof NativeDOMException$1 && error.name === "UnknownError")) console.warn("WebMCP: could not read tools from a frame.", error);
|
|
60
|
-
return [];
|
|
61
|
-
}
|
|
62
|
-
}))).flat();
|
|
63
|
-
}
|
|
64
|
-
async execute(target, expectedOrigin, name, serializedInput, signal) {
|
|
65
|
-
const reply = await this.#request(target, [expectedOrigin], {
|
|
66
|
-
kind: "execute",
|
|
67
|
-
name,
|
|
68
|
-
input: serializedInput
|
|
69
|
-
}, signal);
|
|
70
|
-
if (typeof reply.value !== "string") throw executionError();
|
|
71
|
-
return reply.value;
|
|
72
|
-
}
|
|
73
|
-
async notify(exposedTo) {
|
|
74
|
-
await this.#started.promise;
|
|
75
|
-
const origins = [this.#window.origin, ...exposedTo];
|
|
76
|
-
for (const peer of this.#participants()) {
|
|
77
|
-
if (peer === this.#window) {
|
|
78
|
-
await this.#handlers.changed();
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
await this.#request(peer, origins, { kind: "changed" });
|
|
83
|
-
} catch {}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
#request(target, origins, request, signal) {
|
|
87
|
-
return new Promise((resolve, reject) => {
|
|
88
|
-
signal?.throwIfAborted();
|
|
89
|
-
if (this.#hidden || !this.#related(target)) {
|
|
90
|
-
reject(executionError());
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
const id = crypto.randomUUID();
|
|
94
|
-
const { port1, port2 } = new MessageChannel();
|
|
95
|
-
let peerOrigin;
|
|
96
|
-
const close = () => {
|
|
97
|
-
clearTimeout(timer);
|
|
98
|
-
stopWatching();
|
|
99
|
-
signal?.removeEventListener("abort", abort);
|
|
100
|
-
this.#handshakes.delete(id);
|
|
101
|
-
port1.close();
|
|
102
|
-
};
|
|
103
|
-
const fail = (error = executionError()) => {
|
|
104
|
-
port1.postMessage({ kind: "cancel" });
|
|
105
|
-
close();
|
|
106
|
-
reject(error);
|
|
107
|
-
};
|
|
108
|
-
const abort = () => fail(signal?.reason);
|
|
109
|
-
const expire = () => {
|
|
110
|
-
if (peerOrigin === void 0) this.#forget(target);
|
|
111
|
-
fail();
|
|
112
|
-
};
|
|
113
|
-
const connected = (event) => {
|
|
114
|
-
if (event.source !== target) return;
|
|
115
|
-
this.#handshakes.delete(id);
|
|
116
|
-
if (origins && !origins.includes(event.origin)) {
|
|
117
|
-
fail();
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
peerOrigin = event.origin;
|
|
121
|
-
clearTimeout(timer);
|
|
122
|
-
if (request.kind !== "execute" && request.kind !== "changed") timer = setTimeout(expire, deadline);
|
|
123
|
-
port1.postMessage(request);
|
|
124
|
-
};
|
|
125
|
-
let timer = setTimeout(expire, deadline);
|
|
126
|
-
const stopWatching = this.#watch(target, fail);
|
|
127
|
-
port1.onmessage = (event) => {
|
|
128
|
-
const reply = record(event.data);
|
|
129
|
-
if (reply?.kind === "error") {
|
|
130
|
-
fail();
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
if (reply?.kind !== "result" || peerOrigin === void 0) return;
|
|
134
|
-
close();
|
|
135
|
-
resolve({
|
|
136
|
-
value: reply.value,
|
|
137
|
-
origin: peerOrigin
|
|
138
|
-
});
|
|
139
|
-
};
|
|
140
|
-
port1.onmessageerror = () => fail();
|
|
141
|
-
signal?.addEventListener("abort", abort, { once: true });
|
|
142
|
-
this.#handshakes.set(id, connected);
|
|
143
|
-
try {
|
|
144
|
-
target.postMessage(`${protocol}:connect:${id}`, "*", [port2]);
|
|
145
|
-
} catch {
|
|
146
|
-
port2.close();
|
|
147
|
-
fail();
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
#receive(event) {
|
|
152
|
-
const message = readWindowMessage(event.data);
|
|
153
|
-
if (!message) return;
|
|
154
|
-
event.stopImmediatePropagation();
|
|
155
|
-
const source = event.source;
|
|
156
|
-
if (!event.isTrusted || !source || !isWindow(source) || !this.#related(source) || event.origin === "null" || this.#hidden) return;
|
|
157
|
-
if (message.kind === "announce") {
|
|
158
|
-
const isNew = !this.#knownPeers.has(source);
|
|
159
|
-
this.#learn(source);
|
|
160
|
-
source.postMessage(`${protocol}:present`, event.origin);
|
|
161
|
-
if (!isNew || this.#window !== this.#window.top) return;
|
|
162
|
-
for (const peer of this.#knownPeers) {
|
|
163
|
-
if (peer === source) continue;
|
|
164
|
-
peer.postMessage(`${protocol}:refresh`, "*");
|
|
165
|
-
}
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
if (message.kind === "present") {
|
|
169
|
-
this.#learn(source);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
if (message.kind === "refresh") {
|
|
173
|
-
if (source === this.#window.top) this.#announce();
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
if (message.kind === "ready") {
|
|
177
|
-
this.#handshakes.get(message.id)?.(event);
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
const [port] = event.ports;
|
|
181
|
-
if (message.kind !== "connect" || !message.id || !port || event.ports.length !== 1) return;
|
|
182
|
-
this.#learn(source);
|
|
183
|
-
this.#accept(source, event.origin, message.id, port);
|
|
184
|
-
}
|
|
185
|
-
#accept(source, origin, id, port) {
|
|
186
|
-
const controller = new AbortController();
|
|
187
|
-
let requested = false;
|
|
188
|
-
const close = () => {
|
|
189
|
-
clearTimeout(timer);
|
|
190
|
-
stopWatching();
|
|
191
|
-
port.close();
|
|
192
|
-
};
|
|
193
|
-
const cancel = () => {
|
|
194
|
-
controller.abort();
|
|
195
|
-
close();
|
|
196
|
-
};
|
|
197
|
-
const fail = () => {
|
|
198
|
-
port.postMessage({ kind: "error" });
|
|
199
|
-
cancel();
|
|
200
|
-
};
|
|
201
|
-
const timer = setTimeout(fail, deadline);
|
|
202
|
-
const stopWatching = this.#watch(source, fail);
|
|
203
|
-
port.onmessageerror = fail;
|
|
204
|
-
port.onmessage = async (event) => {
|
|
205
|
-
const request = record(event.data);
|
|
206
|
-
if (request?.kind === "cancel") {
|
|
207
|
-
cancel();
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
if (requested) return;
|
|
211
|
-
requested = true;
|
|
212
|
-
clearTimeout(timer);
|
|
213
|
-
try {
|
|
214
|
-
const value = await this.#respond(source, origin, request, controller.signal);
|
|
215
|
-
port.postMessage({
|
|
216
|
-
kind: "result",
|
|
217
|
-
value
|
|
218
|
-
});
|
|
219
|
-
close();
|
|
220
|
-
} catch {
|
|
221
|
-
fail();
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
source.postMessage(`${protocol}:ready:${id}`, origin);
|
|
225
|
-
}
|
|
226
|
-
async #respond(source, origin, request, signal) {
|
|
227
|
-
if (!request || !this.#related(source)) throw executionError();
|
|
228
|
-
if (request.kind === "permission") {
|
|
229
|
-
const { childIndex, childOrigin } = request;
|
|
230
|
-
if (typeof childIndex !== "number" || !Number.isInteger(childIndex) || childIndex < 0 || typeof childOrigin !== "string") throw executionError();
|
|
231
|
-
return this.#childPermission(childIndex, childOrigin);
|
|
232
|
-
}
|
|
233
|
-
if (!await this.allowed() || !await this.#sourceAllowed(source, origin)) throw executionError();
|
|
234
|
-
if (!this.#related(source)) throw executionError();
|
|
235
|
-
signal.throwIfAborted();
|
|
236
|
-
if (request.kind === "getTools") return this.#handlers.getTools(origin);
|
|
237
|
-
if (request.kind === "execute" && typeof request.name === "string" && typeof request.input === "string") return this.#handlers.execute(origin, request.name, request.input, signal);
|
|
238
|
-
if (request.kind === "changed") {
|
|
239
|
-
await this.#handlers.changed();
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
throw executionError();
|
|
243
|
-
}
|
|
244
|
-
async #childPermission(childIndex, childOrigin) {
|
|
245
|
-
const child = this.#window[childIndex];
|
|
246
|
-
return child !== void 0 && await this.allowed() && childAllowed(this.#document, child, childOrigin);
|
|
247
|
-
}
|
|
248
|
-
async #documentAllowed(owner) {
|
|
249
|
-
const view = activeWindow(owner);
|
|
250
|
-
if (!view) return false;
|
|
251
|
-
const policy = readToolsPolicy(owner);
|
|
252
|
-
if (policy !== void 0) return policy;
|
|
253
|
-
if (view === view.parent) return true;
|
|
254
|
-
const parentOrigin = view.location.ancestorOrigins?.[0];
|
|
255
|
-
const parentDocument = readDocument(view.parent, parentOrigin === void 0 || parentOrigin === view.origin);
|
|
256
|
-
if (!parentDocument) return this.#parentPermission(view, view.origin);
|
|
257
|
-
return childAllowed(parentDocument, view, view.origin) && await this.#documentAllowed(parentDocument);
|
|
258
|
-
}
|
|
259
|
-
async #sourceAllowed(source, origin) {
|
|
260
|
-
const sourceDocument = readDocument(source, origin === this.#window.origin);
|
|
261
|
-
return sourceDocument ? this.#documentAllowed(sourceDocument) : this.#parentPermission(source, origin);
|
|
262
|
-
}
|
|
263
|
-
async #parentPermission(child, origin) {
|
|
264
|
-
const parent = child.parent;
|
|
265
|
-
if (parent === child) return true;
|
|
266
|
-
let childIndex = -1;
|
|
267
|
-
for (let index = 0; index < parent.length; index++) if (parent[index] === child) {
|
|
268
|
-
childIndex = index;
|
|
269
|
-
break;
|
|
270
|
-
}
|
|
271
|
-
if (childIndex < 0) return false;
|
|
272
|
-
if (parent === this.#window) return await this.allowed() && childAllowed(this.#document, child, origin);
|
|
273
|
-
await this.#started.promise;
|
|
274
|
-
if (!this.#knownPeers.has(parent)) return false;
|
|
275
|
-
try {
|
|
276
|
-
const reply = await this.#request(parent, void 0, {
|
|
277
|
-
kind: "permission",
|
|
278
|
-
childIndex,
|
|
279
|
-
childOrigin: origin
|
|
280
|
-
});
|
|
281
|
-
return reply.value === true && await this.#sourceAllowed(parent, reply.origin);
|
|
282
|
-
} catch {
|
|
283
|
-
return false;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
#related(peer) {
|
|
287
|
-
try {
|
|
288
|
-
return !peer.closed && peer !== this.#window && peer.top === this.#window.top;
|
|
289
|
-
} catch {
|
|
290
|
-
return false;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
#tree() {
|
|
294
|
-
if (this.#window.closed) return [];
|
|
295
|
-
for (const peer of this.#knownPeers) if (!this.#related(peer)) this.#knownPeers.delete(peer);
|
|
296
|
-
const candidates = [this.#window, ...this.#knownPeers];
|
|
297
|
-
for (let ancestor = this.#window.parent; ancestor !== ancestor.parent; ancestor = ancestor.parent) candidates.push(ancestor);
|
|
298
|
-
const tree = /* @__PURE__ */ new Set();
|
|
299
|
-
const pending = [this.#window.top];
|
|
300
|
-
while (pending.length) {
|
|
301
|
-
const frame = pending.pop();
|
|
302
|
-
if (tree.has(frame)) continue;
|
|
303
|
-
tree.add(frame);
|
|
304
|
-
const children = [];
|
|
305
|
-
for (let index = 0; index < frame.length; index++) {
|
|
306
|
-
const child = frame[index];
|
|
307
|
-
if (child) children.push(child);
|
|
308
|
-
}
|
|
309
|
-
for (const candidate of candidates) if (candidate !== frame && candidate.parent === frame && !children.includes(candidate)) children.push(candidate);
|
|
310
|
-
pending.push(...children.reverse());
|
|
311
|
-
}
|
|
312
|
-
return Array.from(tree).filter((frame) => frame === this.#window || this.#related(frame));
|
|
313
|
-
}
|
|
314
|
-
#participants() {
|
|
315
|
-
return this.#tree().filter((frame) => frame === this.#window || this.#knownPeers.has(frame));
|
|
316
|
-
}
|
|
317
|
-
#announce() {
|
|
318
|
-
const others = this.#tree().filter((frame) => frame !== this.#window);
|
|
319
|
-
for (const frame of others) frame.postMessage(`${protocol}:announce`, "*");
|
|
320
|
-
return others;
|
|
321
|
-
}
|
|
322
|
-
#learn(peer) {
|
|
323
|
-
this.#knownPeers.add(peer);
|
|
324
|
-
this.#unanswered.delete(peer);
|
|
325
|
-
if (this.#unanswered.size === 0) this.#started.resolve();
|
|
326
|
-
}
|
|
327
|
-
#forget(peer) {
|
|
328
|
-
this.#knownPeers.delete(peer);
|
|
329
|
-
if (this.#related(peer)) peer.postMessage(`${protocol}:announce`, "*");
|
|
330
|
-
}
|
|
331
|
-
#watch(peer, end) {
|
|
332
|
-
const session = {
|
|
333
|
-
peer,
|
|
334
|
-
end
|
|
335
|
-
};
|
|
336
|
-
this.#sessions.add(session);
|
|
337
|
-
if (this.#monitor === void 0) this.#monitor = this.#window.setInterval(() => {
|
|
338
|
-
for (const open of this.#sessions) if (!this.#related(open.peer)) open.end();
|
|
339
|
-
}, 100);
|
|
340
|
-
return () => {
|
|
341
|
-
this.#sessions.delete(session);
|
|
342
|
-
if (this.#sessions.size === 0) {
|
|
343
|
-
this.#window.clearInterval(this.#monitor);
|
|
344
|
-
this.#monitor = void 0;
|
|
345
|
-
}
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
};
|
|
349
|
-
function childAllowed(owner, child, origin) {
|
|
350
|
-
const ownerOrigin = owner.defaultView?.origin;
|
|
351
|
-
const frame = findFrame(owner, child, origin === ownerOrigin);
|
|
352
|
-
if (!frame) return false;
|
|
353
|
-
const directive = (frame.getAttribute("allow") ?? "").split(";").map((part) => part.trim().split(/\s+/u)).find(([name]) => name === "tools");
|
|
354
|
-
if (!directive) return origin === ownerOrigin;
|
|
355
|
-
const sources = directive.length === 1 ? ["'src'"] : directive.slice(1);
|
|
356
|
-
if (sources.includes("'none'")) return false;
|
|
357
|
-
const src = frame.hasAttribute("srcdoc") ? null : frame.getAttribute("src");
|
|
358
|
-
const srcURL = src ? URL.parse(src, owner.baseURI) : null;
|
|
359
|
-
const sourceOrigin = !src || srcURL?.protocol === "about:" ? ownerOrigin : srcURL?.origin;
|
|
360
|
-
return sources.some((source) => source === "*" || source === "'self'" && origin === ownerOrigin || source === "'src'" && origin === sourceOrigin || source === origin);
|
|
361
|
-
}
|
|
362
|
-
function readDocument(view, sameOrigin) {
|
|
363
|
-
if (!sameOrigin) return;
|
|
364
|
-
try {
|
|
365
|
-
return view.document;
|
|
366
|
-
} catch {
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
function findFrame(owner, child, sameOrigin) {
|
|
371
|
-
if (sameOrigin) try {
|
|
372
|
-
const frame = child.frameElement;
|
|
373
|
-
if (frame?.ownerDocument === owner) return frame;
|
|
374
|
-
} catch {}
|
|
375
|
-
return searchFrames(owner, child);
|
|
376
|
-
}
|
|
377
|
-
function searchFrames(root, child) {
|
|
378
|
-
const frames = root.querySelectorAll("iframe, frame");
|
|
379
|
-
for (const frame of frames) if (frame.contentWindow === child) return frame;
|
|
380
|
-
for (const element of root.querySelectorAll("*")) {
|
|
381
|
-
if (!element.shadowRoot) continue;
|
|
382
|
-
const frame = searchFrames(element.shadowRoot, child);
|
|
383
|
-
if (frame) return frame;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
function activeWindow(owner) {
|
|
387
|
-
const view = owner.defaultView;
|
|
388
|
-
return view && !view.closed && view.document === owner ? view : void 0;
|
|
389
|
-
}
|
|
390
|
-
function readToolsPolicy(owner) {
|
|
391
|
-
const source = owner;
|
|
392
|
-
const policy = record(source.permissionsPolicy ?? source.featurePolicy);
|
|
393
|
-
if (typeof policy?.features !== "function" || typeof policy.allowsFeature !== "function") return;
|
|
394
|
-
const features = policy.features();
|
|
395
|
-
return Array.isArray(features) && features.includes("tools") ? Boolean(policy.allowsFeature("tools")) : void 0;
|
|
396
|
-
}
|
|
397
|
-
function toRegisteredTool(metadata, owner, origin) {
|
|
398
|
-
const inputSchema = metadata.serializedSchema === void 0 ? void 0 : JSON.parse(metadata.serializedSchema);
|
|
399
|
-
return {
|
|
400
|
-
...metadata.annotations !== void 0 && { annotations: { ...metadata.annotations } },
|
|
401
|
-
description: metadata.description,
|
|
402
|
-
...inputSchema !== void 0 && { inputSchema },
|
|
403
|
-
name: metadata.name,
|
|
404
|
-
origin,
|
|
405
|
-
title: metadata.title,
|
|
406
|
-
window: owner
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
function readToolMetadata(value) {
|
|
410
|
-
const tool = record(value);
|
|
411
|
-
if (!tool || typeof tool.name !== "string" || typeof tool.title !== "string" || typeof tool.description !== "string" || tool.serializedSchema !== void 0 && typeof tool.serializedSchema !== "string") throw new TypeError("Invalid tool metadata from the frame");
|
|
412
|
-
const metadata = {
|
|
413
|
-
name: tool.name,
|
|
414
|
-
title: tool.title,
|
|
415
|
-
description: tool.description,
|
|
416
|
-
annotations: void 0,
|
|
417
|
-
serializedSchema: tool.serializedSchema
|
|
418
|
-
};
|
|
419
|
-
if (tool.annotations === void 0) return metadata;
|
|
420
|
-
const hints = record(tool.annotations);
|
|
421
|
-
if (!hints) throw new TypeError("Invalid tool annotations from the frame");
|
|
422
|
-
const annotations = {};
|
|
423
|
-
for (const name of annotationNames) {
|
|
424
|
-
const hint = hints[name];
|
|
425
|
-
if (hint === void 0) continue;
|
|
426
|
-
if (typeof hint !== "boolean") throw new TypeError(`Invalid ${name} annotation from the frame`);
|
|
427
|
-
annotations[name] = hint;
|
|
428
|
-
}
|
|
429
|
-
metadata.annotations = annotations;
|
|
430
|
-
return metadata;
|
|
431
|
-
}
|
|
432
|
-
function readWindowMessage(value) {
|
|
433
|
-
if (typeof value !== "string") return;
|
|
434
|
-
const [namespace, kind = "", id = "", extra] = value.split(":");
|
|
435
|
-
return namespace === protocol && extra === void 0 ? {
|
|
436
|
-
kind,
|
|
437
|
-
id
|
|
438
|
-
} : void 0;
|
|
439
|
-
}
|
|
440
|
-
function record(value) {
|
|
441
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return;
|
|
442
|
-
return value;
|
|
443
|
-
}
|
|
444
|
-
function isWindow(value) {
|
|
445
|
-
try {
|
|
446
|
-
return "postMessage" in value && "window" in value;
|
|
447
|
-
} catch {
|
|
448
|
-
return false;
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
function executionError() {
|
|
452
|
-
return new NativeDOMException$1("Tool execution failed", "UnknownError");
|
|
453
|
-
}
|
|
1
|
+
import { installWebMCP as installWebMCP$1 } from "@w3c/webmcp-polyfill";
|
|
454
2
|
|
|
455
|
-
//#endregion
|
|
456
3
|
//#region src/declarative-forms.ts
|
|
457
4
|
const agentInvokedEvents = /* @__PURE__ */ new WeakSet();
|
|
458
5
|
const agentResponses = /* @__PURE__ */ new WeakMap();
|
|
@@ -832,6 +379,9 @@ function fillForm(form, input) {
|
|
|
832
379
|
function findSubmitter(form) {
|
|
833
380
|
return [...getFormControls(form)].find((element) => !element.matches(":disabled") && (element instanceof HTMLButtonElement && element.type === "submit" || element instanceof HTMLInputElement && ["image", "submit"].includes(element.type)));
|
|
834
381
|
}
|
|
382
|
+
function executionError() {
|
|
383
|
+
return new DOMException("Tool execution failed", "UnknownError");
|
|
384
|
+
}
|
|
835
385
|
function lifecycleEvent(type, toolName) {
|
|
836
386
|
const event = new Event(type);
|
|
837
387
|
Object.defineProperty(event, "toolName", {
|
|
@@ -909,7 +459,7 @@ function waitForSubmission(context, registration, definition, submitter, signal)
|
|
|
909
459
|
}
|
|
910
460
|
});
|
|
911
461
|
}
|
|
912
|
-
/** Adds declarative tools until
|
|
462
|
+
/** Adds declarative tools until upstream implements them. */
|
|
913
463
|
function installWebMCPDeclarativeExtensions(context) {
|
|
914
464
|
const prototype = SubmitEvent.prototype;
|
|
915
465
|
if ("agentInvoked" in prototype && "respondWith" in prototype) return;
|
|
@@ -979,12 +529,12 @@ function installWebMCPDeclarativeExtensions(context) {
|
|
|
979
529
|
candidates.add(form);
|
|
980
530
|
const definition = toolDefinition(form);
|
|
981
531
|
const fingerprint = JSON.stringify(definition);
|
|
982
|
-
|
|
983
|
-
if (blockedFingerprint === fingerprint) continue;
|
|
532
|
+
if (blockedDefinitions.get(form) === fingerprint) continue;
|
|
984
533
|
blockedDefinitions.delete(form);
|
|
985
534
|
const existingForm = selectedByName.get(definition.name);
|
|
986
535
|
if (existingForm) {
|
|
987
|
-
|
|
536
|
+
const holdsName = (candidate) => registrations.get(candidate)?.name === definition.name;
|
|
537
|
+
if (!holdsName(form) || holdsName(existingForm)) {
|
|
988
538
|
blockedDefinitions.set(form, fingerprint);
|
|
989
539
|
continue;
|
|
990
540
|
}
|
|
@@ -1010,7 +560,8 @@ function installWebMCPDeclarativeExtensions(context) {
|
|
|
1010
560
|
const registration = {
|
|
1011
561
|
controller,
|
|
1012
562
|
fingerprint,
|
|
1013
|
-
form
|
|
563
|
+
form,
|
|
564
|
+
name: definition.name
|
|
1014
565
|
};
|
|
1015
566
|
registrations.set(form, registration);
|
|
1016
567
|
context.registerTool({
|
|
@@ -1059,405 +610,16 @@ function installWebMCPDeclarativeExtensions(context) {
|
|
|
1059
610
|
sync();
|
|
1060
611
|
}
|
|
1061
612
|
|
|
1062
|
-
//#endregion
|
|
1063
|
-
//#region src/upstream/index.ts
|
|
1064
|
-
/*!
|
|
1065
|
-
* Copyright (c) 2026 WebMCP polyfill contributors
|
|
1066
|
-
* SPDX-License-Identifier: MIT
|
|
1067
|
-
*/
|
|
1068
|
-
const NativeDOMException = globalThis.DOMException;
|
|
1069
|
-
const getWindow = Object.getOwnPropertyDescriptor(globalThis, "window")?.get;
|
|
1070
|
-
const contexts = /* @__PURE__ */ new WeakMap();
|
|
1071
|
-
/**
|
|
1072
|
-
* Install WebMCP in the current window.
|
|
1073
|
-
*
|
|
1074
|
-
* Does nothing outside a secure browser context or when `document.modelContext`
|
|
1075
|
-
* already exists, including partial native implementations. Call before registering
|
|
1076
|
-
* tools in each frame; repeated calls preserve existing contexts and registrations.
|
|
1077
|
-
*
|
|
1078
|
-
* Installing joins cross-frame discovery: the window listens for the polyfill's messages and
|
|
1079
|
-
* announces itself to the other frames of its tree.
|
|
1080
|
-
*
|
|
1081
|
-
* @throws {TypeError} If the window or Document prototype prevents installation.
|
|
1082
|
-
* @example
|
|
1083
|
-
* import { installWebMCP } from "webmcp-polyfill";
|
|
1084
|
-
* installWebMCP();
|
|
1085
|
-
*
|
|
1086
|
-
* @see https://webmachinelearning.github.io/webmcp/#document-extension
|
|
1087
|
-
* @see https://github.com/webmachinelearning/webmcp-polyfill/blob/main/TESTING.md
|
|
1088
|
-
*/
|
|
1089
|
-
function installWebMCP$1() {
|
|
1090
|
-
if (typeof document === "undefined" || !globalThis.isSecureContext) return;
|
|
1091
|
-
if ("modelContext" in document) return;
|
|
1092
|
-
const documentPrototype = Document.prototype;
|
|
1093
|
-
const getDefaultView = Object.getOwnPropertyDescriptor(documentPrototype, "defaultView").get;
|
|
1094
|
-
const constructorDescriptor = Object.getOwnPropertyDescriptor(globalThis, "ModelContext");
|
|
1095
|
-
if (!Object.isExtensible(documentPrototype) || constructorDescriptor && !constructorDescriptor.configurable || !constructorDescriptor && !Object.isExtensible(globalThis)) throw new TypeError("Cannot install WebMCP on this realm");
|
|
1096
|
-
Object.defineProperty(globalThis, "ModelContext", {
|
|
1097
|
-
value: modelContextConstructor,
|
|
1098
|
-
configurable: true,
|
|
1099
|
-
writable: true
|
|
1100
|
-
});
|
|
1101
|
-
const { getModelContext } = { getModelContext() {
|
|
1102
|
-
getDefaultView.call(this);
|
|
1103
|
-
let context = contexts.get(this);
|
|
1104
|
-
if (!context) {
|
|
1105
|
-
context = new ModelContextPolyfill(this);
|
|
1106
|
-
contexts.set(this, context);
|
|
1107
|
-
}
|
|
1108
|
-
return context;
|
|
1109
|
-
} };
|
|
1110
|
-
Object.defineProperty(getModelContext, "name", { value: "get modelContext" });
|
|
1111
|
-
Object.defineProperty(documentPrototype, "modelContext", {
|
|
1112
|
-
configurable: true,
|
|
1113
|
-
enumerable: true,
|
|
1114
|
-
get: getModelContext
|
|
1115
|
-
});
|
|
1116
|
-
document.modelContext;
|
|
1117
|
-
}
|
|
1118
|
-
var ModelContextPolyfill = class extends EventTarget {
|
|
1119
|
-
#document;
|
|
1120
|
-
#tools = /* @__PURE__ */ new Map();
|
|
1121
|
-
#frames;
|
|
1122
|
-
#toolchangeHandler = null;
|
|
1123
|
-
#toolchangeListener = (event) => {
|
|
1124
|
-
const handler = this.#toolchangeHandler;
|
|
1125
|
-
if (typeof handler !== "function") return;
|
|
1126
|
-
if (Reflect.apply(handler, this, [event]) === false) Event.prototype.preventDefault.call(event);
|
|
1127
|
-
};
|
|
1128
|
-
constructor(owner) {
|
|
1129
|
-
super();
|
|
1130
|
-
this.#document = owner;
|
|
1131
|
-
const view = activeWindow(owner);
|
|
1132
|
-
if (!view) return;
|
|
1133
|
-
this.#frames = new FrameBridge(view, {
|
|
1134
|
-
getTools: (callerOrigin) => this.#exposedTools(callerOrigin),
|
|
1135
|
-
execute: (callerOrigin, name, serializedInput, signal) => {
|
|
1136
|
-
const ownerOrigin = requireActiveWindow(owner).origin;
|
|
1137
|
-
return this.#executeLocal(name, serializedInput, ownerOrigin, callerOrigin, signal);
|
|
1138
|
-
},
|
|
1139
|
-
changed: () => this.#queueToolChange()
|
|
1140
|
-
});
|
|
1141
|
-
}
|
|
1142
|
-
get ontoolchange() {
|
|
1143
|
-
return this.#toolchangeHandler;
|
|
1144
|
-
}
|
|
1145
|
-
set ontoolchange(handler) {
|
|
1146
|
-
const nextHandler = isObject(handler) ? handler : null;
|
|
1147
|
-
if (!this.#toolchangeHandler && nextHandler) this.addEventListener("toolchange", this.#toolchangeListener);
|
|
1148
|
-
if (this.#toolchangeHandler && !nextHandler) this.removeEventListener("toolchange", this.#toolchangeListener);
|
|
1149
|
-
this.#toolchangeHandler = nextHandler;
|
|
1150
|
-
}
|
|
1151
|
-
async registerTool(tool, options = {}) {
|
|
1152
|
-
const ownerDocument = this.#document;
|
|
1153
|
-
const { name, title, description, annotations, inputSchema, execute } = readToolDefinition(tool);
|
|
1154
|
-
const settings = readDictionary(options);
|
|
1155
|
-
const exposedTo = readOriginSequence(settings.exposedTo);
|
|
1156
|
-
const registrationSignal = readAbortSignal(settings.signal);
|
|
1157
|
-
requireActiveWindow(ownerDocument);
|
|
1158
|
-
const requireUnusedName = () => {
|
|
1159
|
-
if (this.#tools.has(name)) throw new NativeDOMException(`A tool named ${name} is already registered`, "InvalidStateError");
|
|
1160
|
-
};
|
|
1161
|
-
requireUnusedName();
|
|
1162
|
-
if (!/^[A-Za-z0-9_.-]{1,128}$/u.test(name)) throw new NativeDOMException(`Tool names are 1 to 128 characters of ASCII alphanumerics, "_", "-" or ".": ${name}`, "InvalidStateError");
|
|
1163
|
-
if (!description) throw new NativeDOMException("A tool description cannot be empty", "InvalidStateError");
|
|
1164
|
-
const serializedSchema = inputSchema === void 0 ? void 0 : serializeJSON(inputSchema);
|
|
1165
|
-
registrationSignal?.throwIfAborted();
|
|
1166
|
-
const exposedOrigins = parseOrigins(exposedTo);
|
|
1167
|
-
const storedTool = {
|
|
1168
|
-
metadata: {
|
|
1169
|
-
name,
|
|
1170
|
-
title,
|
|
1171
|
-
description,
|
|
1172
|
-
annotations,
|
|
1173
|
-
serializedSchema
|
|
1174
|
-
},
|
|
1175
|
-
execute,
|
|
1176
|
-
exposedTo: exposedOrigins
|
|
1177
|
-
};
|
|
1178
|
-
const frames = await this.#requireFrames();
|
|
1179
|
-
registrationSignal?.throwIfAborted();
|
|
1180
|
-
requireUnusedName();
|
|
1181
|
-
return new Promise((resolve, reject) => {
|
|
1182
|
-
registrationSignal?.addEventListener("abort", () => {
|
|
1183
|
-
this.#tools.delete(name);
|
|
1184
|
-
frames.notify(exposedOrigins);
|
|
1185
|
-
reject(registrationSignal.reason);
|
|
1186
|
-
}, { once: true });
|
|
1187
|
-
this.#tools.set(name, storedTool);
|
|
1188
|
-
frames.notify(exposedOrigins).then(resolve, reject);
|
|
1189
|
-
});
|
|
1190
|
-
}
|
|
1191
|
-
async getTools(options = {}) {
|
|
1192
|
-
const ownerDocument = this.#document;
|
|
1193
|
-
const fromOrigins = readOriginSequence(readDictionary(options).fromOrigins);
|
|
1194
|
-
requireActiveWindow(ownerDocument);
|
|
1195
|
-
const frames = await this.#requireFrames();
|
|
1196
|
-
const requestedOrigins = parseOrigins(fromOrigins);
|
|
1197
|
-
const tools = await frames.getTools(requestedOrigins);
|
|
1198
|
-
tools.sort((left, right) => {
|
|
1199
|
-
if (left.name === right.name) return 0;
|
|
1200
|
-
return left.name < right.name ? -1 : 1;
|
|
1201
|
-
});
|
|
1202
|
-
return new Promise((resolve) => {
|
|
1203
|
-
queueTask(() => resolve(tools));
|
|
1204
|
-
});
|
|
1205
|
-
}
|
|
1206
|
-
async executeTool(tool, inputObject = {}, options = {}) {
|
|
1207
|
-
const ownerDocument = this.#document;
|
|
1208
|
-
const target = readExecutionTarget(tool);
|
|
1209
|
-
const callerSignal = readAbortSignal(readDictionary(options).signal);
|
|
1210
|
-
requireActiveWindow(ownerDocument);
|
|
1211
|
-
const expectedOrigin = URL.parse(target.origin)?.origin;
|
|
1212
|
-
if (!expectedOrigin || expectedOrigin === "null") throw new NativeDOMException("Invalid or opaque origin", "NotSupportedError");
|
|
1213
|
-
if (!isObject(inputObject)) throw new TypeError("inputObject must be an object");
|
|
1214
|
-
const serializedInput = serializeJSON(inputObject);
|
|
1215
|
-
callerSignal?.throwIfAborted();
|
|
1216
|
-
const frames = await this.#requireFrames();
|
|
1217
|
-
callerSignal?.throwIfAborted();
|
|
1218
|
-
const callerWindow = requireActiveWindow(ownerDocument);
|
|
1219
|
-
if (target.window !== callerWindow) return frames.execute(target.window, expectedOrigin, target.name, serializedInput, callerSignal);
|
|
1220
|
-
return this.#executeLocal(target.name, serializedInput, expectedOrigin, callerWindow.origin, callerSignal);
|
|
1221
|
-
}
|
|
1222
|
-
#executeLocal(name, serializedInput, expectedOrigin, callerOrigin, callerSignal) {
|
|
1223
|
-
return new Promise((resolve, reject) => {
|
|
1224
|
-
callerSignal?.throwIfAborted();
|
|
1225
|
-
const callbackController = new AbortController();
|
|
1226
|
-
let callbackFinished = false;
|
|
1227
|
-
const onCallerAbort = () => {
|
|
1228
|
-
reject(callerSignal.reason);
|
|
1229
|
-
queueTask(() => {
|
|
1230
|
-
if (!callbackFinished) callbackController.abort();
|
|
1231
|
-
});
|
|
1232
|
-
};
|
|
1233
|
-
const rejectExecution = () => {
|
|
1234
|
-
callbackFinished = true;
|
|
1235
|
-
queueTask(() => {
|
|
1236
|
-
callerSignal?.removeEventListener("abort", onCallerAbort);
|
|
1237
|
-
reject(executionError());
|
|
1238
|
-
});
|
|
1239
|
-
};
|
|
1240
|
-
const completeExecution = (value) => {
|
|
1241
|
-
callbackFinished = true;
|
|
1242
|
-
if (callerSignal?.aborted) return;
|
|
1243
|
-
try {
|
|
1244
|
-
const serializedResult = serializeJSON(value);
|
|
1245
|
-
queueTask(() => {
|
|
1246
|
-
callerSignal?.removeEventListener("abort", onCallerAbort);
|
|
1247
|
-
resolve(serializedResult);
|
|
1248
|
-
});
|
|
1249
|
-
} catch {
|
|
1250
|
-
rejectExecution();
|
|
1251
|
-
}
|
|
1252
|
-
};
|
|
1253
|
-
const dispatchTool = () => {
|
|
1254
|
-
if (callerSignal?.aborted) return;
|
|
1255
|
-
try {
|
|
1256
|
-
const ownerWindow = requireActiveWindow(this.#document);
|
|
1257
|
-
const storedTool = this.#tools.get(name);
|
|
1258
|
-
if (!storedTool || expectedOrigin !== ownerWindow.origin || !isExposedTo(storedTool, ownerWindow.origin, callerOrigin)) {
|
|
1259
|
-
rejectExecution();
|
|
1260
|
-
return;
|
|
1261
|
-
}
|
|
1262
|
-
const input = JSON.parse(serializedInput);
|
|
1263
|
-
if (!isObject(input)) {
|
|
1264
|
-
rejectExecution();
|
|
1265
|
-
return;
|
|
1266
|
-
}
|
|
1267
|
-
const execute = storedTool.execute;
|
|
1268
|
-
const callbackResult = execute(input, { signal: callbackController.signal });
|
|
1269
|
-
Promise.resolve(callbackResult).then(completeExecution, rejectExecution);
|
|
1270
|
-
} catch {
|
|
1271
|
-
rejectExecution();
|
|
1272
|
-
}
|
|
1273
|
-
};
|
|
1274
|
-
callerSignal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
1275
|
-
queueTask(dispatchTool);
|
|
1276
|
-
});
|
|
1277
|
-
}
|
|
1278
|
-
async #requireFrames() {
|
|
1279
|
-
const frames = this.#frames;
|
|
1280
|
-
if (!frames || !await frames.allowed()) throw new NativeDOMException("WebMCP is disabled by Permissions Policy", "NotAllowedError");
|
|
1281
|
-
requireActiveWindow(this.#document);
|
|
1282
|
-
return frames;
|
|
1283
|
-
}
|
|
1284
|
-
#exposedTools(callerOrigin) {
|
|
1285
|
-
const ownerOrigin = requireActiveWindow(this.#document).origin;
|
|
1286
|
-
const exposed = [];
|
|
1287
|
-
for (const storedTool of this.#tools.values()) {
|
|
1288
|
-
if (!isExposedTo(storedTool, ownerOrigin, callerOrigin)) continue;
|
|
1289
|
-
exposed.push(storedTool.metadata);
|
|
1290
|
-
}
|
|
1291
|
-
return exposed;
|
|
1292
|
-
}
|
|
1293
|
-
#queueToolChange() {
|
|
1294
|
-
return new Promise((resolve) => {
|
|
1295
|
-
queueTask(() => {
|
|
1296
|
-
this.dispatchEvent(new Event("toolchange"));
|
|
1297
|
-
resolve();
|
|
1298
|
-
});
|
|
1299
|
-
});
|
|
1300
|
-
}
|
|
1301
|
-
};
|
|
1302
|
-
function isExposedTo(tool, ownerOrigin, callerOrigin) {
|
|
1303
|
-
return ownerOrigin === callerOrigin || tool.exposedTo.includes(callerOrigin);
|
|
1304
|
-
}
|
|
1305
|
-
const modelContextConstructor = function ModelContext() {
|
|
1306
|
-
throw new TypeError("Illegal constructor");
|
|
1307
|
-
};
|
|
1308
|
-
Object.defineProperty(modelContextConstructor, "name", { value: "ModelContext" });
|
|
1309
|
-
Object.defineProperty(modelContextConstructor, "prototype", {
|
|
1310
|
-
value: ModelContextPolyfill.prototype,
|
|
1311
|
-
writable: false
|
|
1312
|
-
});
|
|
1313
|
-
Object.setPrototypeOf(modelContextConstructor, EventTarget);
|
|
1314
|
-
Object.defineProperties(ModelContextPolyfill.prototype, {
|
|
1315
|
-
constructor: {
|
|
1316
|
-
value: modelContextConstructor,
|
|
1317
|
-
configurable: true,
|
|
1318
|
-
writable: true
|
|
1319
|
-
},
|
|
1320
|
-
[Symbol.toStringTag]: {
|
|
1321
|
-
value: "ModelContext",
|
|
1322
|
-
configurable: true
|
|
1323
|
-
},
|
|
1324
|
-
registerTool: { enumerable: true },
|
|
1325
|
-
getTools: { enumerable: true },
|
|
1326
|
-
executeTool: { enumerable: true },
|
|
1327
|
-
ontoolchange: { enumerable: true }
|
|
1328
|
-
});
|
|
1329
|
-
function readToolDefinition(value) {
|
|
1330
|
-
const descriptor = readDictionary(value);
|
|
1331
|
-
const annotations = readAnnotations(descriptor.annotations);
|
|
1332
|
-
const description = toDOMString(requireMember(descriptor.description, "description"));
|
|
1333
|
-
const callback = requireMember(descriptor.execute, "execute");
|
|
1334
|
-
if (typeof callback !== "function") throw new TypeError("execute must be a function");
|
|
1335
|
-
const execute = callback;
|
|
1336
|
-
const inputSchema = readInputSchema(descriptor.inputSchema);
|
|
1337
|
-
const name = toDOMString(requireMember(descriptor.name, "name"));
|
|
1338
|
-
const rawTitle = descriptor.title;
|
|
1339
|
-
return {
|
|
1340
|
-
name,
|
|
1341
|
-
title: rawTitle === void 0 ? "" : toUSVString(rawTitle),
|
|
1342
|
-
description,
|
|
1343
|
-
annotations,
|
|
1344
|
-
inputSchema,
|
|
1345
|
-
execute
|
|
1346
|
-
};
|
|
1347
|
-
}
|
|
1348
|
-
function readExecutionTarget(value) {
|
|
1349
|
-
const descriptor = readDictionary(value);
|
|
1350
|
-
readAnnotations(descriptor.annotations);
|
|
1351
|
-
toDOMString(requireMember(descriptor.description, "description"));
|
|
1352
|
-
readInputSchema(descriptor.inputSchema);
|
|
1353
|
-
const name = toDOMString(requireMember(descriptor.name, "name"));
|
|
1354
|
-
const origin = toUSVString(requireMember(descriptor.origin, "origin"));
|
|
1355
|
-
const title = descriptor.title;
|
|
1356
|
-
if (title !== void 0) toDOMString(title);
|
|
1357
|
-
const targetWindow = requireMember(descriptor.window, "window");
|
|
1358
|
-
if (!isObject(targetWindow)) throw new TypeError("window must be a Window");
|
|
1359
|
-
getWindow.call(targetWindow);
|
|
1360
|
-
return {
|
|
1361
|
-
name,
|
|
1362
|
-
origin,
|
|
1363
|
-
window: targetWindow
|
|
1364
|
-
};
|
|
1365
|
-
}
|
|
1366
|
-
function readInputSchema(value) {
|
|
1367
|
-
if (value !== void 0 && !isObject(value)) throw new TypeError("inputSchema must be an object");
|
|
1368
|
-
return value;
|
|
1369
|
-
}
|
|
1370
|
-
function isObject(value) {
|
|
1371
|
-
return typeof value === "object" && value !== null || typeof value === "function";
|
|
1372
|
-
}
|
|
1373
|
-
function readDictionary(value) {
|
|
1374
|
-
if (value == null) return {};
|
|
1375
|
-
if (!isObject(value)) throw new TypeError("Expected a dictionary");
|
|
1376
|
-
return value;
|
|
1377
|
-
}
|
|
1378
|
-
function readAnnotations(value) {
|
|
1379
|
-
if (value === void 0) return;
|
|
1380
|
-
const dictionary = readDictionary(value);
|
|
1381
|
-
const annotations = {};
|
|
1382
|
-
for (const name of annotationNames) annotations[name] = Boolean(dictionary[name]);
|
|
1383
|
-
return annotations;
|
|
1384
|
-
}
|
|
1385
|
-
function toDOMString(value) {
|
|
1386
|
-
if (typeof value === "symbol") throw new TypeError("Cannot convert a Symbol to a string");
|
|
1387
|
-
return String(value);
|
|
1388
|
-
}
|
|
1389
|
-
function toUSVString(value) {
|
|
1390
|
-
return toDOMString(value).toWellFormed();
|
|
1391
|
-
}
|
|
1392
|
-
function requireMember(value, name) {
|
|
1393
|
-
if (value === void 0) throw new TypeError(`${name} is required`);
|
|
1394
|
-
return value;
|
|
1395
|
-
}
|
|
1396
|
-
function serializeJSON(value) {
|
|
1397
|
-
const result = JSON.stringify(value);
|
|
1398
|
-
if (result === void 0) throw new TypeError("Value is not JSON-serializable");
|
|
1399
|
-
return result;
|
|
1400
|
-
}
|
|
1401
|
-
function readAbortSignal(value) {
|
|
1402
|
-
if (value === void 0) return;
|
|
1403
|
-
return AbortSignal.any([value]);
|
|
1404
|
-
}
|
|
1405
|
-
function readOriginSequence(value) {
|
|
1406
|
-
if (value === void 0) return [];
|
|
1407
|
-
if (!isObject(value)) throw new TypeError("Origins must be a sequence");
|
|
1408
|
-
const getIterator = readDictionary(value)[Symbol.iterator];
|
|
1409
|
-
if (typeof getIterator !== "function") throw new TypeError("Origins must be a sequence");
|
|
1410
|
-
const iterable = { [Symbol.iterator]() {
|
|
1411
|
-
const iterator = Reflect.apply(getIterator, value, []);
|
|
1412
|
-
if (!isObject(iterator)) throw new TypeError("Iterator must be an object");
|
|
1413
|
-
const next = readDictionary(iterator).next;
|
|
1414
|
-
if (typeof next !== "function") throw new TypeError("Iterator next must be a function");
|
|
1415
|
-
return { next() {
|
|
1416
|
-
return Reflect.apply(next, iterator, []);
|
|
1417
|
-
} };
|
|
1418
|
-
} };
|
|
1419
|
-
return Array.from(iterable, toUSVString);
|
|
1420
|
-
}
|
|
1421
|
-
function parseOrigins(origins) {
|
|
1422
|
-
return origins.map((value) => {
|
|
1423
|
-
const origin = URL.parse(value)?.origin;
|
|
1424
|
-
if (origin === void 0) throw new NativeDOMException("Invalid origin", "SecurityError");
|
|
1425
|
-
const url = URL.parse(origin);
|
|
1426
|
-
if (!url || !isPotentiallyTrustworthy(url)) throw new NativeDOMException("Origin is not potentially trustworthy", "SecurityError");
|
|
1427
|
-
return origin;
|
|
1428
|
-
});
|
|
1429
|
-
}
|
|
1430
|
-
function isPotentiallyTrustworthy({ protocol, hostname }) {
|
|
1431
|
-
if (["https:", "wss:"].includes(protocol)) return true;
|
|
1432
|
-
const isLoopback = hostname === "[::1]" || /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(hostname);
|
|
1433
|
-
const isLocalhost = hostname === "localhost" || hostname === "localhost." || hostname.endsWith(".localhost") || hostname.endsWith(".localhost.");
|
|
1434
|
-
return ["http:", "ws:"].includes(protocol) && (isLoopback || isLocalhost);
|
|
1435
|
-
}
|
|
1436
|
-
function requireActiveWindow(owner) {
|
|
1437
|
-
const view = activeWindow(owner);
|
|
1438
|
-
if (!view) throw new NativeDOMException("The document is not fully active", "InvalidStateError");
|
|
1439
|
-
if (readToolsPolicy(owner) === false) throw new NativeDOMException("WebMCP is disabled by Permissions Policy", "NotAllowedError");
|
|
1440
|
-
return view;
|
|
1441
|
-
}
|
|
1442
|
-
let taskPort;
|
|
1443
|
-
const queuedTasks = [];
|
|
1444
|
-
function queueTask(callback) {
|
|
1445
|
-
if (!taskPort) {
|
|
1446
|
-
const channel = new MessageChannel();
|
|
1447
|
-
channel.port1.onmessage = () => queuedTasks.shift()?.();
|
|
1448
|
-
taskPort = channel.port2;
|
|
1449
|
-
}
|
|
1450
|
-
queuedTasks.push(callback);
|
|
1451
|
-
taskPort.postMessage(void 0);
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
613
|
//#endregion
|
|
1455
614
|
//#region src/index.ts
|
|
1456
615
|
/**
|
|
1457
616
|
* Installs upstream WebMCP and the temporary declarative tools layer.
|
|
1458
|
-
* Engines missing an API the
|
|
617
|
+
* Engines missing an API the upstream core calls are left untouched: upstream defines
|
|
1459
618
|
* the document getter before it constructs the context, so a failed construction would
|
|
1460
619
|
* leave `document.modelContext` throwing.
|
|
620
|
+
*
|
|
621
|
+
* @deprecated Use `installWebMCP` from `@w3c/webmcp-polyfill`. This wrapper only adds
|
|
622
|
+
* declarative tools until upstream ships them.
|
|
1461
623
|
*/
|
|
1462
624
|
function installWebMCP() {
|
|
1463
625
|
if (typeof document === "undefined" || !globalThis.isSecureContext) return;
|