PageSourceSearch

https://www.webhostingpk.com/.webmcp/bridge.js

js webhostingpk.com collected 2026-10-08 03:27:58 UTC 72,952 bytes, 2,218 lines download raw bytes

1// src/tool-pack.ts
2function isDynamicPack(pack) {
3  return pack.kind === "dynamic";
4}
5function ok(data) {
6  return {
7    content: [{ type: "text", text: JSON.stringify(data) }],
8    structuredContent: data
9  };
10}
11function customResult(result) {
12  return result;
13}
14function errorResult(message) {
15  return {
16    content: [{ type: "text", text: message }],
17    isError: true
18  };
19}
20
21// src/bridge/registry.ts
22function parsePackList(raw) {
23  if (raw === void 0) return null;
24  return raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
25}
26var DEFAULT_MCP_URL = "/mcp";
27function normalizeMcpUrl(raw) {
28  const v = (raw ?? "").trim();
29  if (v.toLowerCase() === "none") return void 0;
30  if (v === "") {
31    console.warn(
32      `[webmcp-interceptor] No data-mcp-url configured; defaulting to /mcp. This probes a same-origin endpoint with the user's session credentials. Set data-mcp-url="none" to opt out, or specify the endpoint explicitly.`
33    );
34    return DEFAULT_MCP_URL;
35  }
36  return v;
37}
38function selectActivePacks(all, requested, defaults) {
39  const names = requested ?? defaults;
40  const byName = new Map(all.map((p) => [p.name, p]));
41  const active = [];
42  for (const name of names) {
43    const pack = byName.get(name);
44    if (pack) active.push(pack);
45    else
46      console.warn(
47        `[webmcp-interceptor] Requested pack "${name}" is not bundled; skipping.`
48      );
49  }
50  return active;
51}
52function resolveStaticPacks(packs) {
53  const resolved = [];
54  for (const pack of packs) {
55    if (!isDynamicPack(pack)) {
56      resolved.push({
57        name: pack.name,
58        tools: pack.tools,
59        handlers: pack.handlers,
60        dynamic: false
61      });
62    }
63  }
64  return resolved;
65}
66async function resolveDynamicPacks(packs, ctx) {
67  const dynamics = packs.filter(isDynamicPack);
68  return Promise.all(
69    dynamics.map(async (pack) => {
70      try {
71        const { tools, handlers } = await pack.resolve(ctx);
72        return { name: pack.name, tools, handlers, dynamic: true };
73      } catch (err) {
74        console.warn(
75          `[webmcp-interceptor] Dynamic pack "${pack.name}" failed to resolve; skipping.`,
76          err
77        );
78        return { name: pack.name, tools: [], handlers: {}, dynamic: true };
79      }
80    })
81  );
82}
83var MAX_TOTAL_TOOLS = 200;
84function buildRegistry(packs) {
85  const tools = [];
86  const handlers = {};
87  const seen = /* @__PURE__ */ new Map();
88  for (const pack of packs) {
89    const toolNames = new Set(pack.tools.map((t) => t.name));
90    for (const tool of pack.tools) {
91      if (!pack.handlers[tool.name]) {
92        throw new Error(
93          `Tool-pack "${pack.name}" declares tool "${tool.name}" but has no matching handler.`
94        );
95      }
96    }
97    for (const name of Object.keys(pack.handlers)) {
98      if (!toolNames.has(name)) {
99        throw new Error(
100          `Tool-pack "${pack.name}" has a handler for "${name}" with no matching tool descriptor.`
101        );
102      }
103    }
104    for (const tool of pack.tools) {
105      const existing = seen.get(tool.name);
106      if (existing) {
107        if (pack.dynamic) {
108          console.warn(
109            `[webmcp-interceptor] Tool "${tool.name}" from dynamic pack "${pack.name}" collides with "${existing}"; skipping the dynamic one.`
110          );
111          continue;
112        }
113        throw new Error(
114          `Tool name collision: "${tool.name}" registered by both "${existing}" and "${pack.name}".`
115        );
116      }
117      if (tools.length >= MAX_TOTAL_TOOLS) {
118        console.warn(
119          `[webmcp-interceptor] Reached ${MAX_TOTAL_TOOLS}-tool cap; skipping remaining tools from "${pack.name}".`
120        );
121        return { tools, handlers };
122      }
123      const handler = pack.handlers[tool.name];
124      if (!handler) {
125        throw new Error(
126          `Tool-pack "${pack.name}" declares tool "${tool.name}" but has no matching handler.`
127        );
128      }
129      seen.set(tool.name, pack.name);
130      tools.push(tool);
131      handlers[tool.name] = handler;
132    }
133  }
134  return { tools, handlers };
135}
136
137// src/bridge/packs/c2pa/cbor.ts
138var BREAK = 255;
139var textDecoder = new TextDecoder("utf-8", { fatal: false });
140var MAX_ITEMS = 1e6;
141var MAX_INDEFINITE_BYTES = 16 * 1024 * 1024;
142function decodeCbor(bytes) {
143  return new CborDecoder(bytes).decode();
144}
145function isCborTag(v) {
146  return typeof v === "object" && v !== null && !(v instanceof Uint8Array) && !Array.isArray(v) && !(v instanceof Map) && "tag" in v;
147}
148function isCborSimple(v) {
149  return typeof v === "object" && v !== null && !(v instanceof Uint8Array) && !Array.isArray(v) && !(v instanceof Map) && "simple" in v;
150}
151var CborDecoder = class {
152  constructor(bytes) {
153    this.bytes = bytes;
154    this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
155  }
156  bytes;
157  view;
158  off = 0;
159  itemCount = 0;
160  decode() {
161    return this.readValue(0);
162  }
163  /** Read one item. `depth` guards against pathological nesting. */
164  readValue(depth) {
165    if (depth > 256) throw new Error("CBOR nesting too deep");
166    if (++this.itemCount > MAX_ITEMS) throw new Error("CBOR item count exceeds budget");
167    const ib = this.u8();
168    const major = ib >> 5;
169    const ai = ib & 31;
170    switch (major) {
171      case 0:
172        return this.readUint(ai);
173      case 1: {
174        const n = this.readUint(ai);
175        return typeof n === "bigint" ? -1n - n : -1 - n;
176      }
177      case 2:
178        return this.readByteString(ai, depth, 2);
179      case 3:
180        return textDecoder.decode(this.readByteString(ai, depth, 3));
181      case 4:
182        return this.readArray(ai, depth);
183      case 5:
184        return this.readMap(ai, depth);
185      case 6: {
186        const tag = this.asLength(this.readUint(ai));
187        return { tag, value: this.readValue(depth + 1) };
188      }
189      default:
190        return this.readSimple(ai);
191    }
192  }
193  /** Read the argument that follows the initial byte (major 0/1/6). */
194  readUint(ai) {
195    if (ai < 24) return ai;
196    if (ai === 24) return this.u8();
197    if (ai === 25) {
198      const v = this.view.getUint16(this.off);
199      this.off += 2;
200      return v;
201    }
202    if (ai === 26) {
203      const v = this.view.getUint32(this.off);
204      this.off += 4;
205      return v;
206    }
207    if (ai === 27) {
208      const v = this.view.getBigUint64(this.off);
209      this.off += 8;
210      return v <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(v) : v;
211    }
212    throw new Error(`Invalid CBOR additional-info value: ${ai}`);
213  }
214  readByteString(ai, depth, majorType) {
215    if (ai === 31) {
216      const chunks = [];
217      let total = 0;
218      for (; ; ) {
219        const ib = this.peek();
220        if (ib === BREAK) {
221          this.off += 1;
222          break;
223        }
224        const major = ib >> 5;
225        const chunkAi = ib & 31;
226        if (major !== majorType || chunkAi === 31) {
227          throw new Error("Indefinite-length string chunk must be a definite-length string of the same type");
228        }
229        this.off += 1;
230        if (++this.itemCount > MAX_ITEMS) throw new Error("CBOR item count exceeds budget");
231        const chunk = this.readByteString(chunkAi, depth + 1, majorType);
232        chunks.push(chunk);
233        total += chunk.length;
234        if (total > MAX_INDEFINITE_BYTES) {
235          throw new Error("Indefinite-length string exceeds size limit");
236        }
237      }
238      const out2 = new Uint8Array(total);
239      let p = 0;
240      for (const c of chunks) {
241        out2.set(c, p);
242        p += c.length;
243      }
244      return out2;
245    }
246    const len = this.asLength(this.readUint(ai));
247    const end = this.off + len;
248    if (end > this.bytes.length) throw new Error("CBOR byte string truncated");
249    const out = this.bytes.subarray(this.off, end);
250    this.off = end;
251    return out;
252  }
253  readArray(ai, depth) {
254    const out = [];
255    if (ai === 31) {
256      while (this.peek() !== BREAK) out.push(this.readValue(depth + 1));
257      this.off += 1;
258      return out;
259    }
260    const len = this.asLength(this.readUint(ai));
261    for (let i = 0; i < len; i++) out.push(this.readValue(depth + 1));
262    return out;
263  }
264  readMap(ai, depth) {
265    const out = /* @__PURE__ */ new Map();
266    if (ai === 31) {
267      while (this.peek() !== BREAK) {
268        const k = this.readValue(depth + 1);
269        out.set(k, this.readValue(depth + 1));
270      }
271      this.off += 1;
272      return out;
273    }
274    const len = this.asLength(this.readUint(ai));
275    for (let i = 0; i < len; i++) {
276      const k = this.readValue(depth + 1);
277      out.set(k, this.readValue(depth + 1));
278    }
279    return out;
280  }
281  readSimple(ai) {
282    switch (ai) {
283      case 20:
284        return false;
285      case 21:
286        return true;
287      case 22:
288        return null;
289      case 23:
290        return void 0;
291      case 24:
292        return { simple: this.u8() };
293      // one-byte simple value (32-255)
294      case 25:
295        return this.readHalfFloat();
296      case 26: {
297        const v = this.view.getFloat32(this.off);
298        this.off += 4;
299        return v;
300      }
301      case 27: {
302        const v = this.view.getFloat64(this.off);
303        this.off += 8;
304        return v;
305      }
306      default:
307        return { simple: ai };
308    }
309  }
310  /** IEEE 754 half-precision (binary16) → JS number. */
311  readHalfFloat() {
312    const half = this.view.getUint16(this.off);
313    this.off += 2;
314    const exp = (half & 31744) >> 10;
315    const frac = half & 1023;
316    const sign = half & 32768 ? -1 : 1;
317    if (exp === 0) return sign * 2 ** -14 * (frac / 1024);
318    if (exp === 31) return frac ? NaN : sign * Infinity;
319    return sign * 2 ** (exp - 15) * (1 + frac / 1024);
320  }
321  u8() {
322    if (this.off >= this.bytes.length) throw new Error("CBOR truncated");
323    const v = this.view.getUint8(this.off);
324    this.off += 1;
325    return v;
326  }
327  peek() {
328    if (this.off >= this.bytes.length) throw new Error("CBOR truncated");
329    return this.view.getUint8(this.off);
330  }
331  /** Coerce a length/tag argument to a sane JS number or throw. */
332  asLength(v) {
333    if (typeof v === "bigint" || v > 2147483647) {
334      throw new Error("CBOR length/tag exceeds supported range");
335    }
336    return v;
337  }
338};
339
340// src/bridge/packs/c2pa/x509.ts
341var textDecoder2 = new TextDecoder("utf-8", { fatal: false });
342var OID_COMMON_NAME = [85, 4, 3];
343function parseCertificate(der) {
344  try {
345    return parseCertificateUnsafe(der);
346  } catch {
347    return {};
348  }
349}
350function parseCertificateUnsafe(der) {
351  const view = new DataView(der.buffer, der.byteOffset, der.byteLength);
352  const cert = readTlv(view, 0, der.length);
353  if (!cert) return {};
354  const tbs = readTlv(view, cert.contentStart, cert.contentEnd);
355  if (!tbs) return {};
356  const fields = readChildren(view, der, tbs.contentStart, tbs.contentEnd);
357  let i = 0;
358  if (fields[i] && fields[i]?.tag === 160) i += 1;
359  const issuer = fields[i + 2];
360  const validity = fields[i + 3];
361  const subject = fields[i + 4];
362  const info = {};
363  if (issuer) info.issuerCN = extractCommonName(view, der, issuer);
364  if (subject) info.subjectCN = extractCommonName(view, der, subject);
365  if (validity) {
366    const times = readChildren(view, der, validity.contentStart, validity.contentEnd);
367    if (times[0]) info.notBefore = parseTime(der, times[0]);
368    if (times[1]) info.notAfter = parseTime(der, times[1]);
369  }
370  return info;
371}
372function readTlv(view, off, limit) {
373  if (off + 2 > limit) return null;
374  const tag = view.getUint8(off);
375  let p = off + 1;
376  let len = view.getUint8(p);
377  p += 1;
378  if (len & 128) {
379    const n = len & 127;
380    if (n === 0 || n > 4) return null;
381    len = 0;
382    for (let i = 0; i < n; i++) {
383      len = len << 8 | view.getUint8(p);
384      p += 1;
385    }
386  }
387  const contentStart = p;
388  const contentEnd = p + len;
389  if (contentEnd > limit) return null;
390  return { tag, contentStart, contentEnd, end: contentEnd };
391}
392function readChildren(view, bytes, start, end) {
393  const out = [];
394  let off = start;
395  while (off < end) {
396    const node = readTlv(view, off, end);
397    if (!node) break;
398    out.push(node);
399    off = node.end;
400  }
401  return out;
402}
403function extractCommonName(view, bytes, name) {
404  for (const rdn of readChildren(view, bytes, name.contentStart, name.contentEnd)) {
405    for (const atv of readChildren(view, bytes, rdn.contentStart, rdn.contentEnd)) {
406      const parts = readChildren(view, bytes, atv.contentStart, atv.contentEnd);
407      const oid = parts[0];
408      const value = parts[1];
409      if (oid && value && oid.tag === 6 && oidEquals(bytes, oid, OID_COMMON_NAME)) {
410        return textDecoder2.decode(bytes.subarray(value.contentStart, value.contentEnd));
411      }
412    }
413  }
414  return void 0;
415}
416function oidEquals(bytes, node, expected) {
417  const len = node.contentEnd - node.contentStart;
418  if (len !== expected.length) return false;
419  for (let i = 0; i < len; i++) {
420    if (bytes[node.contentStart + i] !== expected[i]) return false;
421  }
422  return true;
423}
424function parseTime(bytes, node) {
425  const raw = textDecoder2.decode(bytes.subarray(node.contentStart, node.contentEnd));
426  const isUtc = node.tag === 23;
427  const m = isUtc ? /^(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})?Z?$/.exec(raw) : /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})?Z?$/.exec(raw);
428  if (!m) return raw;
429  let year = Number(m[1]);
430  if (isUtc) year += year < 50 ? 2e3 : 1900;
431  const iso = `${pad(year, 4)}-${m[2]}-${m[3]}T${m[4]}:${m[5]}:${m[6] ?? "00"}Z`;
432  return iso;
433}
434function pad(n, width) {
435  return String(n).padStart(width, "0");
436}
437
438// src/bridge/packs/c2pa/manifest.ts
439function buildManifestStore(store) {
440  const manifests = store.children.map(decodeManifest);
441  const active = manifests[manifests.length - 1];
442  const result = {
443    manifestCount: manifests.length,
444    manifests
445  };
446  if (store.label !== void 0) result.label = store.label;
447  if (active?.label !== void 0) result.activeManifestLabel = active.label;
448  return result;
449}
450function decodeManifest(node) {
451  let claimNode;
452  let signatureNode;
453  let assertionStore;
454  for (const child of node.children) {
455    const label = child.label ?? "";
456    if (label.startsWith("c2pa.claim") || child.contentType === "c2cl") {
457      claimNode = child;
458    } else if (label === "c2pa.signature" || child.contentType === "c2cs") {
459      signatureNode = child;
460    } else if (label === "c2pa.assertions" || child.contentType === "c2as") {
461      assertionStore = child;
462    }
463  }
464  const assertions = assertionStore ? assertionStore.children.map(decodeAssertion) : [];
465  const manifest = { assertions };
466  if (node.label !== void 0) manifest.label = node.label;
467  if (claimNode?.cbor !== void 0) {
468    const claim = cborToJson(claimNode.cbor);
469    if (claim !== null && typeof claim === "object" && !Array.isArray(claim)) {
470      manifest.claim = claim;
471    }
472  }
473  if (signatureNode?.cbor !== void 0) {
474    manifest.signature = decodeCoseSignature(signatureNode.cbor);
475  }
476  return manifest;
477}
478function decodeAssertion(node) {
479  const out = {};
480  if (node.label !== void 0) out.label = node.label;
481  if (node.cbor !== void 0) {
482    out.data = cborToJson(node.cbor);
483  } else if (node.json !== void 0) {
484    out.data = node.json;
485  } else if (node.binary !== void 0) {
486    out.binary = node.binary;
487  }
488  return out;
489}
490var COSE_ALG = {
491  [-7]: "ES256",
492  [-35]: "ES384",
493  [-36]: "ES512",
494  [-8]: "EdDSA",
495  [-37]: "PS256",
496  [-38]: "PS384",
497  [-39]: "PS512",
498  [-257]: "RS256",
499  [-258]: "RS384",
500  [-259]: "RS512"
501};
502function decodeCoseSignature(cose) {
503  const arr = isCborTag(cose) ? cose.value : cose;
504  if (!Array.isArray(arr) || arr.length < 4) return {};
505  const protectedHeader = decodeProtectedHeader(arr[0]);
506  const unprotected = arr[1] instanceof Map ? arr[1] : /* @__PURE__ */ new Map();
507  const sig = {};
508  const algId = asInt(protectedHeader.get(1) ?? unprotected.get(1));
509  if (algId !== void 0) sig.alg = COSE_ALG[algId] ?? `COSE(${algId})`;
510  const certs = readX5Chain(protectedHeader.get(33) ?? unprotected.get(33));
511  if (certs.length > 0) {
512    sig.certChainLength = certs.length;
513    const first = certs[0];
514    if (first) {
515      const info = parseCertificate(first);
516      if (info.subjectCN !== void 0) sig.signedBy = info.subjectCN;
517      if (info.issuerCN !== void 0) sig.certifiedBy = info.issuerCN;
518      if (info.notBefore !== void 0) sig.certValidFrom = info.notBefore;
519      if (info.notAfter !== void 0) sig.certValidTo = info.notAfter;
520    }
521  }
522  return sig;
523}
524function decodeProtectedHeader(value) {
525  if (value instanceof Uint8Array && value.length > 0) {
526    const decoded = decodeCbor(value);
527    if (decoded instanceof Map) return decoded;
528  }
529  return /* @__PURE__ */ new Map();
530}
531function readX5Chain(value) {
532  if (value instanceof Uint8Array) return [value];
533  if (Array.isArray(value)) {
534    return value.filter((c) => c instanceof Uint8Array);
535  }
536  return [];
537}
538function asInt(value) {
539  if (typeof value === "number" && Number.isInteger(value)) return value;
540  if (typeof value === "bigint") return Number(value);
541  return void 0;
542}
543var MAX_INLINE_BYTES = 64;
544function cborToJson(value) {
545  if (value === null || value === void 0) return value ?? null;
546  if (typeof value === "bigint") return value.toString();
547  if (value instanceof Uint8Array) return bytesToJson(value);
548  if (Array.isArray(value)) return value.map(cborToJson);
549  if (value instanceof Map) {
550    const obj = /* @__PURE__ */ Object.create(null);
551    for (const [k, v] of value) obj[keyToString(k)] = cborToJson(v);
552    return obj;
553  }
554  if (isCborTag(value)) {
555    return { $tag: value.tag, value: cborToJson(value.value) };
556  }
557  if (isCborSimple(value)) {
558    return { $simple: value.simple };
559  }
560  return value;
561}
562function bytesToJson(b) {
563  if (b.length <= MAX_INLINE_BYTES) return { $bytes: b.length, hex: toHex(b) };
564  return { $bytes: b.length, hex: `${toHex(b.subarray(0, 32))}\u2026` };
565}
566function keyToString(k) {
567  if (typeof k === "string") return k;
568  if (typeof k === "number" || typeof k === "bigint" || typeof k === "boolean") {
569    return String(k);
570  }
571  return JSON.stringify(cborToJson(k));
572}
573function toHex(b) {
574  let s = "";
575  for (let i = 0; i < b.length; i++) {
576    const byte = b[i];
577    if (byte === void 0) break;
578    s += byte.toString(16).padStart(2, "0");
579  }
580  return s;
581}
582
583// src/bridge/packs/c2pa/jumbf.ts
584var textDecoder3 = new TextDecoder("utf-8", { fatal: false });
585var MAX_JUMBF_BOXES = 1e4;
586var MAX_APP11_PACKETS = 1024;
587var MAX_FRAGMENTS_PER_INSTANCE = 256;
588function detectImageFormat(bytes) {
589  if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) {
590    return "jpeg";
591  }
592  if (bytes.length >= 8 && bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71 && bytes[4] === 13 && bytes[5] === 10 && bytes[6] === 26 && bytes[7] === 10) {
593    return "png";
594  }
595  return "unknown";
596}
597function extractC2paJumbf(bytes) {
598  switch (detectImageFormat(bytes)) {
599    case "jpeg":
600      return extractFromJpeg(bytes);
601    case "png":
602      return extractFromPng(bytes);
603    default:
604      return null;
605  }
606}
607function parseManifestStore(jumbf) {
608  const view = new DataView(jumbf.buffer, jumbf.byteOffset, jumbf.byteLength);
609  const top = readBox(view, jumbf, 0, jumbf.length);
610  if (!top || top.type !== "jumb") return null;
611  return parseSuperbox(view, jumbf, top.payloadStart, top.payloadEnd, 0, {
612    count: 0
613  });
614}
615function extractFromJpeg(bytes) {
616  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
617  if (view.getUint16(0) !== 65496) return null;
618  const packets = /* @__PURE__ */ new Map();
619  let app11Count = 0;
620  let off = 2;
621  while (off + 2 <= bytes.length) {
622    if (view.getUint8(off) !== 255) break;
623    let marker = view.getUint8(off + 1);
624    while (marker === 255 && off + 2 < bytes.length) {
625      off += 1;
626      marker = view.getUint8(off + 1);
627    }
628    off += 2;
629    if (marker === 217 || marker === 218) break;
630    if (marker === 1 || marker >= 208 && marker <= 215) continue;
631    if (off + 2 > bytes.length) break;
632    const segLen = view.getUint16(off);
633    const segStart = off + 2;
634    const segEnd = off + segLen;
635    if (segLen < 2 || segEnd > bytes.length) break;
636    if (marker === 235 && segEnd - segStart >= 8) {
637      if (view.getUint16(segStart) === 19024) {
638        if (++app11Count > MAX_APP11_PACKETS) break;
639        const en = view.getUint16(segStart + 2);
640        const z = view.getUint32(segStart + 4);
641        const data = bytes.subarray(segStart + 8, segEnd);
642        const arr = packets.get(en) ?? [];
643        if (arr.length < MAX_FRAGMENTS_PER_INSTANCE) {
644          arr.push({ z, data });
645          packets.set(en, arr);
646        }
647      }
648    }
649    off = segEnd;
650  }
651  for (const arr of packets.values()) {
652    arr.sort((a, b) => a.z - b.z);
653    const first = arr[0];
654    if (!first) continue;
655    let headerLen = 8;
656    if (first.data.length >= 4) {
657      const fv = new DataView(
658        first.data.buffer,
659        first.data.byteOffset,
660        first.data.byteLength
661      );
662      if (fv.getUint32(0) === 1) headerLen = 16;
663    }
664    const pieces = [];
665    let total = 0;
666    for (let i = 0; i < arr.length; i++) {
667      const pk = arr[i];
668      if (!pk) continue;
669      const piece = i === 0 ? pk.data : pk.data.subarray(headerLen);
670      pieces.push(piece);
671      total += piece.length;
672    }
673    const buf = new Uint8Array(total);
674    let p = 0;
675    for (const piece of pieces) {
676      buf.set(piece, p);
677      p += piece.length;
678    }
679    if (isC2paStore(buf)) return buf;
680  }
681  return null;
682}
683function extractFromPng(bytes) {
684  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
685  let off = 8;
686  while (off + 8 <= bytes.length) {
687    const len = view.getUint32(off);
688    const type = ascii(bytes, off + 4, 4);
689    const dataStart = off + 8;
690    const dataEnd = dataStart + len;
691    if (dataEnd + 4 > bytes.length) break;
692    if (type === "caBX") {
693      const buf = bytes.subarray(dataStart, dataEnd);
694      return isC2paStore(buf) ? buf : null;
695    }
696    if (type === "IEND") break;
697    off = dataEnd + 4;
698  }
699  return null;
700}
701function readBox(view, bytes, offset, limit) {
702  if (offset + 8 > limit) return null;
703  let len = view.getUint32(offset);
704  const type = ascii(bytes, offset + 4, 4);
705  let headerLen = 8;
706  if (len === 1) {
707    if (offset + 16 > limit) return null;
708    const hi = view.getUint32(offset + 8);
709    const lo = view.getUint32(offset + 12);
710    len = hi * 4294967296 + lo;
711    headerLen = 16;
712  } else if (len === 0) {
713    len = limit - offset;
714  }
715  const boxEnd = offset + len;
716  if (len < headerLen || boxEnd > limit) return null;
717  return { type, payloadStart: offset + headerLen, payloadEnd: boxEnd, boxEnd };
718}
719function parseSuperbox(view, bytes, start, end, depth, counter) {
720  const node = { contentType: "", children: [] };
721  if (depth > 64) return node;
722  let off = start;
723  const first = readBox(view, bytes, off, end);
724  if (first && first.type === "jumd") {
725    if (++counter.count > MAX_JUMBF_BOXES) return node;
726    parseDescription(view, bytes, first.payloadStart, first.payloadEnd, node);
727    off = first.boxEnd;
728  }
729  while (off < end) {
730    if (++counter.count > MAX_JUMBF_BOXES) return node;
731    const box = readBox(view, bytes, off, end);
732    if (!box) break;
733    switch (box.type) {
734      case "jumb":
735        node.children.push(
736          parseSuperbox(view, bytes, box.payloadStart, box.payloadEnd, depth + 1, counter)
737        );
738        break;
739      case "cbor":
740        try {
741          node.cbor = decodeCbor(bytes.subarray(box.payloadStart, box.payloadEnd));
742        } catch {
743        }
744        break;
745      case "json":
746        try {
747          node.json = JSON.parse(
748            textDecoder3.decode(bytes.subarray(box.payloadStart, box.payloadEnd))
749          );
750        } catch {
751        }
752        break;
753      case "bidb":
754      // embedded-file data box
755      case "uuid":
756        node.binary = { size: box.payloadEnd - box.payloadStart };
757        break;
758      default:
759        break;
760    }
761    off = box.boxEnd;
762  }
763  return node;
764}
765function parseDescription(view, bytes, start, end, node) {
766  if (start + 17 > end) return;
767  node.contentType = ascii(bytes, start, 4);
768  const toggles = view.getUint8(start + 16);
769  let off = start + 17;
770  if (toggles & 2) {
771    let z = off;
772    while (z < end && view.getUint8(z) !== 0) z += 1;
773    node.label = textDecoder3.decode(bytes.subarray(off, z));
774  }
775}
776function isC2paStore(buf) {
777  if (buf.length < 16) return false;
778  const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
779  const top = readBox(view, buf, 0, buf.length);
780  if (!top || top.type !== "jumb") return false;
781  const desc = readBox(view, buf, top.payloadStart, top.payloadEnd);
782  if (!desc || desc.type !== "jumd") return false;
783  return ascii(buf, desc.payloadStart, 4) === "c2pa";
784}
785function ascii(bytes, off, len) {
786  let s = "";
787  for (let i = 0; i < len; i++) {
788    const c = bytes[off + i];
789    if (c === void 0) break;
790    s += String.fromCharCode(c);
791  }
792  return s;
793}
794
795// src/bridge/packs/c2pa/c2pa-executor.ts
796var MAX_SCAN_IMAGES = 100;
797var MAX_IMAGE_BYTES = 64 * 1024 * 1024;
798var FETCH_TIMEOUT_MS = 15e3;
799var SCAN_CONCURRENCY = 6;
800var SCAN_BUDGET_MS = 3e4;
801var NO_VERIFICATION_NOTE = "Manifest decoded only. Cryptographic signature, certificate-chain, and trust-list verification were NOT performed; treat provenance as unverified claims.";
802async function scanImages(limit, signal) {
803  const allSources = collectImageSources();
804  const capped = Math.min(limit, MAX_SCAN_IMAGES);
805  const sources = allSources.slice(0, capped);
806  const deadline = anySignal(signal, AbortSignal.timeout(SCAN_BUDGET_MS));
807  const results = await mapWithConcurrency(
808    sources,
809    SCAN_CONCURRENCY,
810    (src) => scanOne(src, deadline)
811  );
812  const withC2pa = results.filter((r) => r.hasC2pa).length;
813  return {
814    imageCount: allSources.length,
815    scanned: results.length,
816    withC2pa,
817    results
818  };
819}
820async function scanOne(src, signal) {
821  try {
822    const bytes = await fetchImageBytes(src, signal);
823    const analysis = analyzeBytes(bytes);
824    if (!analysis.store) {
825      return { src, hasC2pa: false, format: analysis.format };
826    }
827    const active = activeManifest(analysis.store);
828    const summary = {
829      src,
830      hasC2pa: true,
831      format: analysis.format,
832      manifestCount: analysis.store.manifestCount,
833      signatureVerified: false
834    };
835    const claimGenerator = readClaimGenerator(active?.claim);
836    const title = readTitle(active?.claim);
837    if (claimGenerator !== void 0) summary.claimGenerator = claimGenerator;
838    if (title !== void 0) summary.title = title;
839    if (active?.signature?.signedBy !== void 0) {
840      summary.signedBy = active.signature.signedBy;
841    }
842    return summary;
843  } catch (err) {
844    return { src, hasC2pa: false, error: errorMessage(err) };
845  }
846}
847async function inspectImage(opts, signal) {
848  const isUrlMode = Boolean(opts.url);
849  const src = resolveSource(opts);
850  const bytes = await fetchImageBytes(src, signal, isUrlMode);
851  const analysis = analyzeBytes(bytes);
852  if (!analysis.store) {
853    return {
854      src,
855      hasC2pa: false,
856      format: analysis.format,
857      message: analysis.format === "unknown" ? "Unsupported image format (only JPEG and PNG are parsed today)." : "No C2PA manifest found in this image."
858    };
859  }
860  return {
861    src,
862    hasC2pa: true,
863    format: analysis.format,
864    manifestStore: analysis.store,
865    validation: { signatureVerified: false, note: NO_VERIFICATION_NOTE }
866  };
867}
868function analyzeBytes(bytes) {
869  const format = detectImageFormat(bytes);
870  const jumbf = extractC2paJumbf(bytes);
871  if (!jumbf) return { format };
872  const tree = parseManifestStore(jumbf);
873  if (!tree) return { format };
874  return { format, store: buildManifestStore(tree) };
875}
876function collectImageSources() {
877  const seen = /* @__PURE__ */ new Set();
878  const out = [];
879  for (const img of Array.from(document.querySelectorAll("img"))) {
880    const el = img;
881    const src = el.currentSrc || el.src;
882    if (src && !seen.has(src)) {
883      seen.add(src);
884      out.push(src);
885    }
886  }
887  return out;
888}
889function resolveSource(opts) {
890  if (opts.url) {
891    const resolved = new URL(opts.url, document.location.href);
892    if (resolved.origin !== document.location.origin) {
893      throw new Error("URL must be same-origin.");
894    }
895    return resolved.toString();
896  }
897  if (opts.selector) {
898    const el = document.querySelector(opts.selector);
899    if (!el) throw new Error(`No element matches selector: ${opts.selector}`);
900    if (!(el instanceof HTMLImageElement)) {
901      throw new Error(`Element is not an <img>: ${opts.selector}`);
902    }
903    const src = el.currentSrc || el.src;
904    if (!src) throw new Error(`<img> has no resolved src: ${opts.selector}`);
905    return src;
906  }
907  throw new Error("Provide either a `selector` or a `url`.");
908}
909async function fetchImageBytes(src, signal, noRedirect = false) {
910  const combined = anySignal(signal, AbortSignal.timeout(FETCH_TIMEOUT_MS));
911  let res;
912  try {
913    res = await fetch(src, {
914      signal: combined,
915      // Only block redirects when the agent supplied the URL.
916      // Scan/selector mode fetches already-resolved currentSrc URLs.
917      ...noRedirect ? { redirect: "error" } : {}
918    });
919  } catch (err) {
920    if (err instanceof DOMException && err.name === "TimeoutError") {
921      throw new Error("Image fetch timed out");
922    }
923    if (err instanceof DOMException && err.name === "AbortError") {
924      throw new Error("Image fetch aborted");
925    }
926    throw err;
927  }
928  if (!res.ok) throw new Error("Image fetch failed.");
929  const length = Number(res.headers.get("content-length") ?? "0");
930  if (length > MAX_IMAGE_BYTES) {
931    throw new Error(`Image too large to inspect (${length} bytes)`);
932  }
933  if (!res.body) throw new Error("Response has no body stream");
934  const reader = res.body.getReader();
935  const chunks = [];
936  let total = 0;
937  for (; ; ) {
938    const { done, value } = await reader.read();
939    if (done) break;
940    total += value.byteLength;
941    if (total > MAX_IMAGE_BYTES) {
942      await reader.cancel();
943      throw new Error(`Image too large to inspect (${total} bytes)`);
944    }
945    chunks.push(value);
946  }
947  const assembled = new Uint8Array(total);
948  let offset = 0;
949  for (const chunk of chunks) {
950    assembled.set(chunk, offset);
951    offset += chunk.byteLength;
952  }
953  return assembled;
954}
955function activeManifest(store) {
956  return store.manifests[store.manifests.length - 1];
957}
958function readClaimGenerator(claim) {
959  if (!claim) return void 0;
960  const info = claim["claim_generator_info"];
961  if (Array.isArray(info) && info.length > 0) {
962    const first = info[0];
963    if (first && typeof first === "object") {
964      const name = first["name"];
965      const version = first["version"];
966      if (typeof name === "string") {
967        return typeof version === "string" ? `${name} ${version}` : name;
968      }
969    }
970  }
971  const generator = claim["claim_generator"];
972  return typeof generator === "string" ? generator : void 0;
973}
974function readTitle(claim) {
975  if (!claim) return void 0;
976  const title = claim["dc:title"] ?? claim["title"];
977  return typeof title === "string" ? title : void 0;
978}
979function errorMessage(err) {
980  return err instanceof Error ? err.message : String(err);
981}
982function anySignal(optional, required) {
983  return optional ? AbortSignal.any([optional, required]) : required;
984}
985async function mapWithConcurrency(items, concurrency, fn) {
986  const results = new Array(items.length);
987  let next = 0;
988  const workers = [];
989  const limit = Math.max(1, Math.min(concurrency, items.length));
990  for (let w = 0; w < limit; w++) {
991    workers.push(
992      (async () => {
993        for (; ; ) {
994          const i = next++;
995          if (i >= items.length) return;
996          const item = items[i];
997          if (item === void 0) continue;
998          results[i] = await fn(item);
999        }
1000      })()
1001    );
1002  }
1003  await Promise.all(workers);
1004  return results;
1005}
1006
1007// src/bridge/packs/c2pa/index.ts
1008var TOOLS = [
1009  {
1010    name: "scan_images_c2pa",
1011    description: "Scan every <img> on the current page for C2PA Content Credentials (content provenance metadata) and return a per-image summary: whether a manifest is present, the claim generator (the tool that produced or edited the image), the title, and the signer name. Use this to find which images on a page carry provenance data. Note: provenance is decoded but NOT cryptographically verified.",
1012    inputSchema: {
1013      type: "object",
1014      properties: {
1015        limit: {
1016          type: "integer",
1017          minimum: 1,
1018          maximum: 100,
1019          default: 25,
1020          description: "Maximum number of images to fetch and scan (default 25, max 100)."
1021        }
1022      },
1023      required: []
1024    }
1025  },
1026  {
1027    name: "inspect_image_c2pa",
1028    description: "Fetch a single image (by CSS selector or URL) and return its fully decoded C2PA manifest store: every manifest's claim, assertions (e.g. c2pa.actions edit history, schema.org authorship), and the COSE signature details (algorithm and signing-certificate identity). Provide exactly one of `selector` or `url`. Note: the signature is decoded but NOT cryptographically verified.",
1029    inputSchema: {
1030      type: "object",
1031      properties: {
1032        selector: {
1033          type: "string",
1034          maxLength: 8192,
1035          description: "CSS selector for an <img> element on the page."
1036        },
1037        url: {
1038          type: "string",
1039          maxLength: 8192,
1040          description: "Same-origin image URL (absolute same-origin, or relative to the current page) to fetch and inspect. Cross-origin URLs are rejected."
1041        }
1042      },
1043      required: []
1044    }
1045  }
1046];
1047function optionalInt(args, key, min, max, fallback) {
1048  if (typeof args !== "object" || args === null) return fallback;
1049  const v = args[key];
1050  if (v === void 0) return fallback;
1051  if (typeof v !== "number" || !Number.isInteger(v)) return null;
1052  if (v < min || v > max) return null;
1053  return v;
1054}
1055var MAX_STRING_ARG = 8192;
1056function readStringArg(args, key) {
1057  if (typeof args !== "object" || args === null) return { kind: "absent" };
1058  const v = args[key];
1059  if (v === void 0 || v === null) return { kind: "absent" };
1060  if (typeof v !== "string") {
1061    return { kind: "invalid", reason: `\`${key}\` must be a string.` };
1062  }
1063  if (v.length === 0) return { kind: "absent" };
1064  if (v.length > MAX_STRING_ARG) {
1065    return {
1066      kind: "invalid",
1067      reason: `\`${key}\` exceeds the ${MAX_STRING_ARG}-character limit.`
1068    };
1069  }
1070  return { kind: "ok", value: v };
1071}
1072var handleScanImages = async (rawArgs, ctx) => {
1073  const limit = optionalInt(rawArgs, "limit", 1, 100, 25);
1074  if (limit === null) return errorResult("Invalid tool arguments");
1075  return ok({ ...await scanImages(limit, ctx.signal) });
1076};
1077var handleInspectImage = async (rawArgs, ctx) => {
1078  const selector = readStringArg(rawArgs, "selector");
1079  const url = readStringArg(rawArgs, "url");
1080  if (selector.kind === "invalid") return errorResult(selector.reason);
1081  if (url.kind === "invalid") return errorResult(url.reason);
1082  const hasSelector = selector.kind === "ok";
1083  const hasUrl = url.kind === "ok";
1084  if (!hasSelector && !hasUrl) {
1085    return errorResult("Provide either `selector` or `url`.");
1086  }
1087  if (hasSelector && hasUrl) {
1088    return errorResult("Provide only one of `selector` or `url`, not both.");
1089  }
1090  const result = await inspectImage(
1091    {
1092      ...selector.kind === "ok" ? { selector: selector.value } : {},
1093      ...url.kind === "ok" ? { url: url.value } : {}
1094    },
1095    ctx.signal
1096  );
1097  return ok({ ...result });
1098};
1099function wrapErrors(h) {
1100  return async (args, ctx) => {
1101    try {
1102      return await h(args, ctx);
1103    } catch (err) {
1104      return errorResult(err instanceof Error ? err.message : String(err));
1105    }
1106  };
1107}
1108var c2paPack = {
1109  kind: "static",
1110  name: "c2pa",
1111  tools: TOOLS,
1112  handlers: {
1113    scan_images_c2pa: wrapErrors(handleScanImages),
1114    inspect_image_c2pa: wrapErrors(handleInspectImage)
1115  }
1116};
1117
1118// src/bridge/packs/mcp-server-client/mcp-http-utils.ts
1119var MCP_TIMEOUT_MS = 2e4;
1120var MCP_MAX_BYTES = 8 * 1024 * 1024;
1121var MCP_MAX_RESPONSE_CHUNKS = 65536;
1122var MCP_MAX_LIST_PAGES = 20;
1123var MCP_MAX_TOOLS = 200;
1124var MCP_MAX_STRUCTURED_CONTENT_BYTES = 64 * 1024;
1125var MCP_MAX_CONTENT_TEXT_BYTES = 64 * 1024;
1126var MCP_MAX_CONTENT_TEXT_TOTAL_BYTES = 256 * 1024;
1127var RPC_ID = 1;
1128var MCP_CLIENT_INFO = {
1129  name: "cloudflare-webmcp-bridge",
1130  version: "0.0.1"
1131};
1132var encoder = new TextEncoder();
1133var McpHttpError = class extends Error {
1134  constructor(status, rpcError) {
1135    super(rpcError?.message ?? `MCP endpoint returned HTTP ${status}`);
1136    this.status = status;
1137    this.rpcError = rpcError;
1138  }
1139  status;
1140  rpcError;
1141  _tag = "McpHttpError";
1142};
1143function isObject(v) {
1144  return typeof v === "object" && v !== null && !Array.isArray(v);
1145}
1146function sliceUtf8Bytes(s, maxBytes) {
1147  const bytes = encoder.encode(s);
1148  if (bytes.length <= maxBytes) return s;
1149  let end = maxBytes;
1150  while (end > 0) {
1151    const b = bytes[end];
1152    if (b === void 0 || (b & 192) !== 128) break;
1153    end--;
1154  }
1155  return new TextDecoder("utf-8", { fatal: false }).decode(
1156    bytes.slice(0, end)
1157  );
1158}
1159async function readBoundedText(res) {
1160  const body = res.body;
1161  if (!body) return res.text();
1162  const reader = body.getReader();
1163  const chunks = [];
1164  let total = 0;
1165  let done = false;
1166  try {
1167    for (let count = 0; count < MCP_MAX_RESPONSE_CHUNKS; count++) {
1168      const { done: streamDone, value } = await reader.read();
1169      if (streamDone) {
1170        done = true;
1171        break;
1172      }
1173      if (value) {
1174        total += value.byteLength;
1175        if (total > MCP_MAX_BYTES) {
1176          throw new Error(
1177            `MCP response exceeded ${MCP_MAX_BYTES} bytes; aborting.`
1178          );
1179        }
1180        chunks.push(value);
1181      }
1182    }
1183    if (!done) throw new Error("MCP response exceeded chunk limit; aborting.");
1184  } finally {
1185    await reader.cancel().catch(() => {
1186    });
1187  }
1188  const merged = new Uint8Array(total);
1189  let offset = 0;
1190  for (const chunk of chunks) {
1191    merged.set(chunk, offset);
1192    offset += chunk.byteLength;
1193  }
1194  return new TextDecoder().decode(merged);
1195}
1196function callSignal(signal) {
1197  const timeout = AbortSignal.timeout(MCP_TIMEOUT_MS);
1198  return signal ? AbortSignal.any([signal, timeout]) : timeout;
1199}
1200function combineSignals(signal, lifecycleSignal) {
1201  return signal ? AbortSignal.any([signal, lifecycleSignal]) : lifecycleSignal;
1202}
1203async function waitFor(pending, signal) {
1204  if (!signal) return pending;
1205  if (signal.aborted) throw signal.reason;
1206  return new Promise((resolve2, reject) => {
1207    const onAbort = () => reject(signal.reason);
1208    signal.addEventListener("abort", onAbort, { once: true });
1209    pending.then(
1210      (value) => {
1211        signal.removeEventListener("abort", onAbort);
1212        resolve2(value);
1213      },
1214      (error) => {
1215        signal.removeEventListener("abort", onAbort);
1216        reject(error);
1217      }
1218    );
1219  });
1220}
1221function assertSameOrigin(endpoint, origin) {
1222  if (!origin) return endpoint;
1223  try {
1224    const url = new URL(endpoint, origin);
1225    if (url.origin !== origin) {
1226      throw new Error(
1227        `MCP endpoint "${endpoint}" is cross-origin; only same-origin endpoints are allowed.`
1228      );
1229    }
1230    return url.toString();
1231  } catch (error) {
1232    if (error instanceof Error && error.message.includes("cross-origin")) {
1233      throw error;
1234    }
1235    throw new Error(`MCP endpoint "${endpoint}" is not a valid URL.`);
1236  }
1237}
1238function requestHeaders(protocolVersion, sessionId) {
1239  const headers = {
1240    "content-type": "application/json",
1241    accept: "application/json, text/event-stream",
1242    // Distinguish agent-mediated calls from user-initiated ones so the
1243    // site can attribute actions and apply different policy.
1244    "x-webmcp-agent": "1"
1245  };
1246  if (protocolVersion) headers["MCP-Protocol-Version"] = protocolVersion;
1247  if (sessionId) headers["MCP-Session-Id"] = sessionId;
1248  return headers;
1249}
1250async function mcpRpcResponse(endpoint, origin, method, params, options = {}) {
1251  const rpcId = options.rpcId ?? RPC_ID;
1252  const res = await fetch(assertSameOrigin(endpoint, origin), {
1253    method: "POST",
1254    headers: options.headers ?? requestHeaders(),
1255    credentials: "same-origin",
1256    redirect: "error",
1257    body: JSON.stringify({
1258      jsonrpc: "2.0",
1259      id: rpcId,
1260      method,
1261      params: options.requestParams ?? params
1262    }),
1263    signal: callSignal(options.signal)
1264  });
1265  if (!res.ok) {
1266    let rpcError;
1267    const raw = await readBoundedText(res);
1268    if (raw) {
1269      try {
1270        const parsed = JSON.parse(raw);
1271        if (isObject(parsed) && isResponseEnvelope(parsed, rpcId)) {
1272          const error = parsed["error"];
1273          if (isJsonRpcError(error)) rpcError = error;
1274        }
1275      } catch {
1276      }
1277    }
1278    throw new McpHttpError(res.status, rpcError);
1279  }
1280  const contentType = res.headers.get("content-type") ?? "";
1281  const rpc = contentType.includes("text/event-stream") ? await readEventStreamResponse(res, rpcId) : parseJson(await readBoundedText(res));
1282  if (!isResponseEnvelope(rpc, rpcId)) {
1283    throw new Error("MCP endpoint returned a mismatched JSON-RPC response");
1284  }
1285  return { result: extractResult(rpc), headers: res.headers };
1286}
1287async function mcpRpcNotification(endpoint, origin, method, signal, protocolVersion, sessionId) {
1288  const res = await fetch(assertSameOrigin(endpoint, origin), {
1289    method: "POST",
1290    headers: requestHeaders(protocolVersion, sessionId),
1291    credentials: "same-origin",
1292    redirect: "error",
1293    body: JSON.stringify({ jsonrpc: "2.0", method }),
1294    signal: callSignal(signal)
1295  });
1296  if (!res.ok) throw new McpHttpError(res.status);
1297  await res.body?.cancel().catch(() => {
1298  });
1299}
1300async function listTools(rpc, signal) {
1301  const out = [];
1302  let cursor;
1303  for (let page = 0; page < MCP_MAX_LIST_PAGES; page++) {
1304    const result = await rpc("tools/list", cursor ? { cursor } : {}, signal);
1305    if (!Array.isArray(result.tools)) {
1306      throw new Error("MCP tools/list response has no tools array");
1307    }
1308    for (const tool of result.tools) {
1309      const definition = toToolDef(tool);
1310      if (definition) {
1311        out.push(definition);
1312        if (out.length >= MCP_MAX_TOOLS) {
1313          console.warn(
1314            `[webmcp-interceptor] MCP endpoint returned >= ${MCP_MAX_TOOLS} tools; stopping discovery early.`
1315          );
1316          return out;
1317        }
1318      }
1319    }
1320    cursor = typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? result.nextCursor : void 0;
1321    if (!cursor) break;
1322  }
1323  return out;
1324}
1325function toToolDef(value) {
1326  if (!isObject(value)) return null;
1327  if (typeof value.name !== "string" || value.name.trim().length === 0) return null;
1328  const definition = { name: value.name };
1329  if (typeof value.description === "string" && value.description.trim().length > 0) {
1330    definition.description = value.description;
1331  }
1332  if (isObject(value.inputSchema) && value.inputSchema["type"] === "object") {
1333    definition.inputSchema = value.inputSchema;
1334  }
1335  return definition;
1336}
1337function toCallResult(result) {
1338  if (!Array.isArray(result.content)) {
1339    throw new Error("MCP tools/call result has no content array");
1340  }
1341  let textTotal = 0;
1342  const cappedContent = result.content.filter(isContentBlock).map((block) => {
1343    if (isObject(block) && block["type"] === "text") {
1344      const text = block["text"];
1345      if (typeof text === "string") {
1346        const remaining = MCP_MAX_CONTENT_TEXT_TOTAL_BYTES - textTotal;
1347        if (remaining <= 0) return { type: "text", text: "" };
1348        let capped = text;
1349        if (encoder.encode(capped).length > MCP_MAX_CONTENT_TEXT_BYTES) {
1350          capped = sliceUtf8Bytes(capped, MCP_MAX_CONTENT_TEXT_BYTES);
1351        }
1352        if (encoder.encode(capped).length > remaining) {
1353          capped = sliceUtf8Bytes(capped, remaining);
1354        }
1355        textTotal += encoder.encode(capped).length;
1356        return { type: "text", text: capped };
1357      }
1358    }
1359    return block;
1360  });
1361  const out = {
1362    content: cappedContent,
1363    isError: result.isError === true
1364  };
1365  if (isObject(result.structuredContent)) {
1366    try {
1367      const size = encoder.encode(JSON.stringify(result.structuredContent)).length;
1368      if (size <= MCP_MAX_STRUCTURED_CONTENT_BYTES) {
1369        out.structuredContent = result.structuredContent;
1370      } else {
1371        console.warn(
1372          `[webmcp-interceptor] mcp-server-client: structuredContent exceeds ${MCP_MAX_STRUCTURED_CONTENT_BYTES} bytes; dropping.`
1373        );
1374      }
1375    } catch {
1376    }
1377  }
1378  return out;
1379}
1380function isContentBlock(value) {
1381  if (!isObject(value) || typeof value["type"] !== "string") return false;
1382  if (value["type"] === "text") return typeof value["text"] === "string";
1383  return true;
1384}
1385function parseJson(raw) {
1386  try {
1387    return JSON.parse(raw);
1388  } catch {
1389    throw new Error("MCP endpoint returned a non-JSON response");
1390  }
1391}
1392async function readEventStreamResponse(res, rpcId = RPC_ID) {
1393  const body = res.body;
1394  if (!body) return parseEventStream(await res.text());
1395  const reader = body.getReader();
1396  const decoder = new TextDecoder();
1397  let tail = "";
1398  let total = 0;
1399  try {
1400    for (; ; ) {
1401      const { done, value } = await reader.read();
1402      if (done) break;
1403      if (!value) continue;
1404      total += value.byteLength;
1405      if (total > MCP_MAX_BYTES) {
1406        throw new Error(`MCP response exceeded ${MCP_MAX_BYTES} bytes; aborting.`);
1407      }
1408      const chunk = decoder.decode(value, { stream: true });
1409      let position = 0;
1410      let newline;
1411      while ((newline = chunk.indexOf("\n", position)) >= 0) {
1412        const rawLine = tail + chunk.slice(position, newline);
1413        tail = "";
1414        position = newline + 1;
1415        const envelope2 = tryParseDataLine(rawLine, rpcId);
1416        if (envelope2 !== void 0) return envelope2;
1417      }
1418      tail += chunk.slice(position);
1419    }
1420    const envelope = tryParseDataLine(tail, rpcId);
1421    if (envelope !== void 0) return envelope;
1422  } finally {
1423    await reader.cancel().catch(() => {
1424    });
1425  }
1426  throw new Error("MCP event stream contained no JSON-RPC response");
1427}
1428function parseEventStream(raw) {
1429  for (const rawLine of raw.split(/\r?\n/)) {
1430    const envelope = tryParseDataLine(rawLine, RPC_ID);
1431    if (envelope !== void 0) return envelope;
1432  }
1433  throw new Error("MCP event stream contained no JSON-RPC response");
1434}
1435function tryParseDataLine(rawLine, rpcId) {
1436  const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
1437  if (!line.startsWith("data:")) return void 0;
1438  const payload = line.slice("data:".length).trim();
1439  if (!payload) return void 0;
1440  try {
1441    const parsed = JSON.parse(payload);
1442    if (isResponseEnvelope(parsed, rpcId)) return parsed;
1443  } catch {
1444  }
1445  return void 0;
1446}
1447function isResponseEnvelope(value, rpcId) {
1448  if (!isObject(value) || value["jsonrpc"] !== "2.0") return false;
1449  const id = value["id"];
1450  return id === rpcId || id === null && isJsonRpcError(value["error"]);
1451}
1452function isJsonRpcError(value) {
1453  return isObject(value) && typeof value["code"] === "number" && Number.isInteger(value["code"]) && typeof value["message"] === "string";
1454}
1455function extractResult(rpc) {
1456  if (!isObject(rpc)) {
1457    throw new Error("Malformed JSON-RPC response from MCP endpoint");
1458  }
1459  if ("error" in rpc) {
1460    const error = rpc.error;
1461    if (!isJsonRpcError(error)) {
1462      throw new Error("MCP endpoint returned a malformed JSON-RPC error");
1463    }
1464    throw new Error(`MCP endpoint error: ${error["message"]}`);
1465  }
1466  const result = rpc.result;
1467  if (!isObject(result)) throw new Error("MCP response missing result");
1468  return result;
1469}
1470async function terminateMcpSession(endpoint, origin, protocolVersion, sessionId) {
1471  try {
1472    await fetch(assertSameOrigin(endpoint, origin), {
1473      method: "DELETE",
1474      headers: requestHeaders(protocolVersion, sessionId),
1475      credentials: "same-origin",
1476      redirect: "error",
1477      keepalive: true,
1478      signal: AbortSignal.timeout(MCP_TIMEOUT_MS)
1479    });
1480  } catch {
1481  }
1482}
1483
1484// src/bridge/packs/mcp-server-client/initialized-client.ts
1485var MCP_INITIALIZE_MAX_ATTEMPTS = 2;
1486var MCP_MAX_SESSION_ID_LENGTH = 512;
1487var MCP_SUPPORTED_PROTOCOL_VERSIONS = [
1488  "2025-11-25",
1489  "2025-06-18",
1490  "2025-03-26"
1491];
1492var InitializedClient = class {
1493  constructor(options) {
1494    this.options = options;
1495  }
1496  options;
1497  protocolVersion;
1498  sessionId;
1499  initializing;
1500  get negotiatedProtocolVersion() {
1501    return this.protocolVersion;
1502  }
1503  async connect(signal) {
1504    await this.ensureInitialized(signal);
1505  }
1506  async listTools(signal) {
1507    return listTools(
1508      (method, params, requestSignal) => this.sessionRpc(method, params, requestSignal),
1509      signal
1510    );
1511  }
1512  async callTool(name, args, signal) {
1513    return toCallResult(
1514      await this.sessionRpc("tools/call", { name, arguments: args }, signal)
1515    );
1516  }
1517  async close() {
1518    const sessionId = this.sessionId;
1519    const protocolVersion = this.protocolVersion;
1520    this.clearSession();
1521    if (!sessionId || !protocolVersion) return;
1522    await terminateMcpSession(
1523      this.options.endpoint,
1524      this.options.origin,
1525      protocolVersion,
1526      sessionId
1527    );
1528  }
1529  async ensureInitialized(signal) {
1530    this.options.assertOpen();
1531    if (this.protocolVersion) return;
1532    this.initializing ??= this.initialize().finally(() => {
1533      this.initializing = void 0;
1534    });
1535    await waitFor(this.initializing, signal);
1536    this.options.assertOpen();
1537  }
1538  async initialize() {
1539    for (let attempt = 0; attempt < MCP_INITIALIZE_MAX_ATTEMPTS; attempt++) {
1540      this.options.assertOpen();
1541      const response = await mcpRpcResponse(
1542        this.options.endpoint,
1543        this.options.origin,
1544        "initialize",
1545        {
1546          protocolVersion: MCP_SUPPORTED_PROTOCOL_VERSIONS[0],
1547          capabilities: {},
1548          clientInfo: MCP_CLIENT_INFO
1549        },
1550        {
1551          signal: this.options.lifecycleSignal,
1552          rpcId: this.options.nextRpcId()
1553        }
1554      );
1555      const sessionId = readSessionId(response.headers);
1556      const protocolVersion = response.result["protocolVersion"];
1557      try {
1558        this.options.assertOpen();
1559      } catch (error) {
1560        if (sessionId) {
1561          void terminateMcpSession(
1562            this.options.endpoint,
1563            this.options.origin,
1564            typeof protocolVersion === "string" ? protocolVersion : MCP_SUPPORTED_PROTOCOL_VERSIONS[0],
1565            sessionId
1566          );
1567        }
1568        throw error;
1569      }
1570      try {
1571        if (typeof protocolVersion !== "string" || !MCP_SUPPORTED_PROTOCOL_VERSIONS.includes(protocolVersion)) {
1572          throw new Error(
1573            `MCP server negotiated unsupported protocol version: ${String(protocolVersion)}`
1574          );
1575        }
1576        const capabilities = response.result["capabilities"];
1577        if (!isObject(capabilities) || !isObject(capabilities["tools"])) {
1578          throw new Error("MCP server does not advertise the tools capability");
1579        }
1580      } catch (error) {
1581        if (sessionId) {
1582          void terminateMcpSession(
1583            this.options.endpoint,
1584            this.options.origin,
1585            typeof protocolVersion === "string" ? protocolVersion : MCP_SUPPORTED_PROTOCOL_VERSIONS[0],
1586            sessionId
1587          );
1588        }
1589        throw error;
1590      }
1591      try {
1592        await mcpRpcNotification(
1593          this.options.endpoint,
1594          this.options.origin,
1595          "notifications/initialized",
1596          this.options.lifecycleSignal,
1597          protocolVersion,
1598          sessionId
1599        );
1600        this.options.assertOpen();
1601        this.protocolVersion = protocolVersion;
1602        this.sessionId = sessionId;
1603        return;
1604      } catch (error) {
1605        const sessionMissing = error instanceof McpHttpError && error.status === 404 && sessionId !== void 0;
1606        if (sessionMissing && attempt + 1 < MCP_INITIALIZE_MAX_ATTEMPTS) continue;
1607        if (sessionId && !sessionMissing) {
1608          void terminateMcpSession(
1609            this.options.endpoint,
1610            this.options.origin,
1611            protocolVersion,
1612            sessionId
1613          );
1614        }
1615        throw error;
1616      }
1617    }
1618  }
1619  async sessionRpc(method, params, signal) {
1620    await this.ensureInitialized(signal);
1621    this.options.assertOpen();
1622    const sessionId = this.sessionId;
1623    try {
1624      return await this.rpcWithCurrentSession(method, params, signal);
1625    } catch (error) {
1626      if (!(error instanceof McpHttpError) || error.status !== 404 || !sessionId) {
1627        throw error;
1628      }
1629      this.options.assertOpen();
1630      if (this.sessionId === sessionId) this.clearSession();
1631      await this.ensureInitialized(signal);
1632      return this.rpcWithCurrentSession(method, params, signal);
1633    }
1634  }
1635  async rpcWithCurrentSession(method, params, signal) {
1636    const protocolVersion = this.protocolVersion;
1637    if (!protocolVersion) throw new Error("MCP client is not initialized");
1638    this.options.assertOpen();
1639    return (await mcpRpcResponse(
1640      this.options.endpoint,
1641      this.options.origin,
1642      method,
1643      params,
1644      {
1645        signal: combineSignals(signal, this.options.lifecycleSignal),
1646        headers: requestHeaders(protocolVersion, this.sessionId),
1647        rpcId: this.options.nextRpcId()
1648      }
1649    )).result;
1650  }
1651  clearSession() {
1652    this.protocolVersion = void 0;
1653    this.sessionId = void 0;
1654  }
1655};
1656function readSessionId(headers) {
1657  const sessionId = headers.get("MCP-Session-Id");
1658  if (sessionId === null) return void 0;
1659  if (sessionId.length === 0 || sessionId.length > MCP_MAX_SESSION_ID_LENGTH || !/^[\x21-\x7e]+$/.test(sessionId)) {
1660    throw new Error("MCP endpoint returned an invalid session ID");
1661  }
1662  return sessionId;
1663}
1664
1665// src/bridge/packs/mcp-server-client/per-request-client.ts
1666var MCP_PER_REQUEST_PROTOCOL_VERSION = "2026-07-28";
1667var MCP_UNSUPPORTED_PROTOCOL_VERSION_ERROR_CODE = -32022;
1668var MCP_MAX_HEADER_BINDINGS = 64;
1669var MCP_MAX_HEADER_VALUE_BYTES = 8 * 1024;
1670var MCP_MAX_SCHEMA_PROPERTIES = 256;
1671var headerEncoder = new TextEncoder();
1672var InitializedFallbackRequired = class extends Error {
1673};
1674var PerRequestProtocolSelectedError = class extends Error {
1675  constructor(originalError) {
1676    super("Per-request MCP protocol was selected before a later request failed");
1677    this.originalError = originalError;
1678  }
1679  originalError;
1680};
1681var PerRequestClient = class {
1682  constructor(options) {
1683    this.options = options;
1684  }
1685  options;
1686  tools = /* @__PURE__ */ new Map();
1687  async discoverTools() {
1688    let responseReceived = false;
1689    try {
1690      const tools = await listTools(
1691        async (method, params, signal) => {
1692          return this.rpc(method, params, signal, () => {
1693            responseReceived = true;
1694          });
1695        },
1696        this.options.lifecycleSignal
1697      );
1698      return this.rememberTools(tools);
1699    } catch (error) {
1700      if (responseReceived) throw new PerRequestProtocolSelectedError(error);
1701      if (!responseReceived && shouldFallbackToInitializedMode(error)) {
1702        throw new InitializedFallbackRequired();
1703      }
1704      throw error;
1705    }
1706  }
1707  async listTools(signal) {
1708    return this.rememberTools(
1709      await listTools(
1710        (method, params, requestSignal) => this.rpc(method, params, requestSignal),
1711        signal
1712      )
1713    );
1714  }
1715  async callTool(name, args, signal) {
1716    return toCallResult(
1717      await this.rpc("tools/call", { name, arguments: args }, signal)
1718    );
1719  }
1720  async rpc(method, params, signal, onResponse) {
1721    this.options.assertOpen();
1722    const toolName = typeof params["name"] === "string" ? params["name"] : void 0;
1723    const response = await mcpRpcResponse(
1724      this.options.endpoint,
1725      this.options.origin,
1726      method,
1727      params,
1728      {
1729        signal: combineSignals(signal, this.options.lifecycleSignal),
1730        rpcId: this.options.nextRpcId(),
1731        headers: buildPerRequestHeaders(
1732          requestHeaders(MCP_PER_REQUEST_PROTOCOL_VERSION),
1733          method,
1734          params,
1735          toolName ? this.tools.get(toolName) : void 0
1736        ),
1737        requestParams: {
1738          ...params,
1739          _meta: {
1740            "io.modelcontextprotocol/protocolVersion": MCP_PER_REQUEST_PROTOCOL_VERSION,
1741            "io.modelcontextprotocol/clientInfo": MCP_CLIENT_INFO,
1742            "io.modelcontextprotocol/clientCapabilities": {}
1743          }
1744        }
1745      }
1746    );
1747    onResponse?.();
1748    const resultType = response.result["resultType"];
1749    if (resultType !== void 0 && resultType !== "complete") {
1750      throw new Error(
1751        `MCP endpoint returned unsupported resultType: ${String(resultType)}`
1752      );
1753    }
1754    return response.result;
1755  }
1756  rememberTools(tools) {
1757    this.tools.clear();
1758    return tools.filter((tool) => {
1759      const headerError = validatePerRequestHeaderSchema(tool.inputSchema);
1760      if (headerError) {
1761        console.warn(
1762          `[webmcp-interceptor] mcp-server-client: skipping ${tool.name}: ${headerError}.`
1763        );
1764        return false;
1765      }
1766      this.tools.set(tool.name, tool);
1767      return true;
1768    });
1769  }
1770};
1771function shouldFallbackToInitializedMode(error) {
1772  if (!(error instanceof McpHttpError) || error.status !== 400) return false;
1773  if (error.rpcError?.code !== MCP_UNSUPPORTED_PROTOCOL_VERSION_ERROR_CODE) {
1774    return true;
1775  }
1776  if (!isObject(error.rpcError.data)) return true;
1777  const supported = error.rpcError.data["supported"];
1778  if (!Array.isArray(supported) || supported.length === 0 || !supported.every((version) => typeof version === "string")) {
1779    return true;
1780  }
1781  if (supported.includes(MCP_PER_REQUEST_PROTOCOL_VERSION)) return false;
1782  const perRequestVersions = supported.filter(
1783    (version) => version >= MCP_PER_REQUEST_PROTOCOL_VERSION
1784  );
1785  if (perRequestVersions.length > 0) {
1786    console.warn(
1787      `[webmcp-interceptor] mcp-server-client: server advertised unsupported MCP protocol version(s) ${perRequestVersions.join(", ")}; this bridge supports ${MCP_PER_REQUEST_PROTOCOL_VERSION}.`
1788    );
1789    return false;
1790  }
1791  return true;
1792}
1793function encodeHeaderValue(value) {
1794  const bytes = headerEncoder.encode(value);
1795  let encoded;
1796  if (value.length > 0 && value === value.trim() && /^[\x20-\x7e]+$/.test(value) && !(value.startsWith("=?base64?") && value.endsWith("?="))) {
1797    encoded = value;
1798  } else {
1799    let binary = "";
1800    for (const byte of bytes) binary += String.fromCharCode(byte);
1801    encoded = `=?base64?${btoa(binary)}?=`;
1802  }
1803  if (encoded.length > MCP_MAX_HEADER_VALUE_BYTES) {
1804    throw new Error("MCP header value exceeds size limit");
1805  }
1806  return encoded;
1807}
1808function readHeaderBindings(schema) {
1809  if (!schema || !isObject(schema["properties"])) return { bindings: [] };
1810  const bindings = [];
1811  const headerNames = /* @__PURE__ */ new Set();
1812  const properties = Object.entries(schema["properties"]);
1813  if (properties.length > MCP_MAX_SCHEMA_PROPERTIES) {
1814    return { bindings: [], error: "too many schema properties" };
1815  }
1816  for (const [argumentName, property] of properties) {
1817    if (!isObject(property) || property["x-mcp-header"] === void 0) continue;
1818    const headerName = property["x-mcp-header"];
1819    const type = property["type"];
1820    if (typeof headerName !== "string" || !/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(headerName) || type !== "string" && type !== "integer" && type !== "boolean") {
1821      return { bindings: [], error: "invalid x-mcp-header annotation" };
1822    }
1823    const normalizedName = headerName.toLowerCase();
1824    if (headerNames.has(normalizedName)) {
1825      return { bindings: [], error: "duplicate x-mcp-header annotation" };
1826    }
1827    if (bindings.length >= MCP_MAX_HEADER_BINDINGS) {
1828      return { bindings: [], error: "too many x-mcp-header annotations" };
1829    }
1830    headerNames.add(normalizedName);
1831    bindings.push({ headerName, argumentName, type });
1832  }
1833  return { bindings };
1834}
1835function validatePerRequestHeaderSchema(schema) {
1836  return readHeaderBindings(schema).error;
1837}
1838function buildPerRequestHeaders(baseHeaders, method, params, tool) {
1839  const headers = {
1840    ...baseHeaders,
1841    "Mcp-Method": method
1842  };
1843  if (typeof params["name"] === "string") {
1844    headers["Mcp-Name"] = encodeHeaderValue(params["name"]);
1845  }
1846  if (!tool || !isObject(params["arguments"])) return headers;
1847  const { bindings, error } = readHeaderBindings(tool.inputSchema);
1848  if (error) throw new Error(`${tool.name}: ${error}`);
1849  for (const binding of bindings) {
1850    if (!Object.hasOwn(params["arguments"], binding.argumentName)) continue;
1851    const value = params["arguments"][binding.argumentName];
1852    if (value === void 0 || value === null) continue;
1853    const valid = binding.type === "string" && typeof value === "string" || binding.type === "boolean" && typeof value === "boolean" || binding.type === "integer" && typeof value === "number" && Number.isSafeInteger(value);
1854    if (!valid) throw new Error(`${tool.name}: invalid ${binding.argumentName}`);
1855    headers[`Mcp-Param-${binding.headerName}`] = encodeHeaderValue(String(value));
1856  }
1857  return headers;
1858}
1859
1860// src/bridge/packs/mcp-server-client/mcp-client.ts
1861var McpHttpClient = class {
1862  selectedClient;
1863  negotiating;
1864  pagehideHandler;
1865  lifecycleController = new AbortController();
1866  perRequestClient;
1867  initializedClient;
1868  closed = false;
1869  rpcId = 0;
1870  constructor(endpoint, origin) {
1871    const options = {
1872      endpoint,
1873      origin,
1874      lifecycleSignal: this.lifecycleController.signal,
1875      nextRpcId: () => this.nextRpcId(),
1876      assertOpen: () => this.assertOpen()
1877    };
1878    this.perRequestClient = new PerRequestClient(options);
1879    this.initializedClient = new InitializedClient(options);
1880  }
1881  async listTools(signal) {
1882    const initialTools = await this.ensureClient(signal);
1883    if (initialTools) return initialTools;
1884    return this.requireSelectedClient().listTools(signal);
1885  }
1886  async callTool(name, args, signal) {
1887    await this.ensureClient(signal);
1888    return this.requireSelectedClient().callTool(name, args, signal);
1889  }
1890  attachPageLifecycle() {
1891    if (typeof window === "undefined" || this.pagehideHandler || this.closed) return;
1892    this.pagehideHandler = (event) => {
1893      if (event.persisted) return;
1894      if (this.pagehideHandler) {
1895        window.removeEventListener("pagehide", this.pagehideHandler);
1896        this.pagehideHandler = void 0;
1897      }
1898      void this.close();
1899    };
1900    window.addEventListener("pagehide", this.pagehideHandler);
1901  }
1902  async close() {
1903    if (this.closed) return;
1904    this.closed = true;
1905    this.lifecycleController.abort();
1906    if (typeof window !== "undefined" && this.pagehideHandler) {
1907      window.removeEventListener("pagehide", this.pagehideHandler);
1908      this.pagehideHandler = void 0;
1909    }
1910    await this.initializedClient.close();
1911  }
1912  async ensureClient(signal) {
1913    this.assertOpen();
1914    if (this.selectedClient) return void 0;
1915    this.negotiating ??= this.negotiate().finally(() => {
1916      this.negotiating = void 0;
1917    });
1918    const tools = await waitFor(this.negotiating, signal);
1919    this.assertOpen();
1920    return tools;
1921  }
1922  async negotiate() {
1923    try {
1924      const tools = await this.perRequestClient.discoverTools();
1925      this.assertOpen();
1926      this.selectedClient = this.perRequestClient;
1927      return tools;
1928    } catch (error) {
1929      if (error instanceof PerRequestProtocolSelectedError) {
1930        this.selectedClient = this.perRequestClient;
1931        throw error.originalError;
1932      }
1933      if (!(error instanceof InitializedFallbackRequired)) throw error;
1934      await this.initializedClient.connect();
1935      this.assertOpen();
1936      this.selectedClient = this.initializedClient;
1937      console.warn(
1938        `[webmcp-interceptor] mcp-server-client: falling back from MCP ${MCP_PER_REQUEST_PROTOCOL_VERSION} to initialized MCP ${this.initializedClient.negotiatedProtocolVersion}.`
1939      );
1940      return void 0;
1941    }
1942  }
1943  requireSelectedClient() {
1944    if (!this.selectedClient) {
1945      throw new Error("MCP client has not selected a protocol mode");
1946    }
1947    return this.selectedClient;
1948  }
1949  assertOpen() {
1950    if (this.closed) throw new Error("MCP client is closed");
1951  }
1952  nextRpcId() {
1953    this.rpcId += 1;
1954    return this.rpcId;
1955  }
1956};
1957
1958// src/bridge/packs/mcp-server-client/index.ts
1959function isObject2(v) {
1960  return typeof v === "object" && v !== null;
1961}
1962var MAX_DESCRIPTION_CHARS = 4096;
1963var MAX_SCHEMA_BYTES = 32 * 1024;
1964var encoder2 = new TextEncoder();
1965var SITE_TOOL_NOTE = "[Site-provided MCP tool \u2014 executes with your session credentials.] ";
1966function clampDescription(desc) {
1967  return desc.length > MAX_DESCRIPTION_CHARS ? desc.slice(0, MAX_DESCRIPTION_CHARS) : desc;
1968}
1969function safeInputSchema(schema) {
1970  if (!isObject2(schema) || schema["type"] !== "object") {
1971    return { type: "object", properties: {} };
1972  }
1973  try {
1974    const size = encoder2.encode(JSON.stringify(schema)).length;
1975    if (size > MAX_SCHEMA_BYTES) {
1976      console.warn(
1977        `[webmcp-interceptor] mcp-server-client: tool inputSchema exceeds ${MAX_SCHEMA_BYTES} bytes; using empty schema.`
1978      );
1979      return { type: "object", properties: {} };
1980    }
1981  } catch {
1982    return { type: "object", properties: {} };
1983  }
1984  return schema;
1985}
1986function makeHandler(client, toolName) {
1987  return async (rawArgs, ctx) => {
1988    const args = isObject2(rawArgs) ? rawArgs : {};
1989    try {
1990      const result = await client.callTool(toolName, args, ctx.signal);
1991      const out = {
1992        content: result.content
1993      };
1994      if (isObject2(result.structuredContent)) {
1995        out.structuredContent = result.structuredContent;
1996      }
1997      if (result.isError) out.isError = true;
1998      return customResult(out);
1999    } catch (err) {
2000      return errorResult(err instanceof Error ? err.message : String(err));
2001    }
2002  };
2003}
2004async function resolve(ctx) {
2005  const endpoint = ctx.mcpUrl;
2006  if (!endpoint) {
2007    return { tools: [], handlers: {} };
2008  }
2009  let defs;
2010  const client = new McpHttpClient(endpoint, ctx.origin);
2011  client.attachPageLifecycle();
2012  try {
2013    defs = await client.listTools(ctx.signal);
2014  } catch (err) {
2015    console.warn(
2016      `[webmcp-interceptor] mcp-server-client: tools/list failed for "${endpoint}"; registering no site tools.`,
2017      err
2018    );
2019    void client.close();
2020    return { tools: [], handlers: {} };
2021  }
2022  if (defs.length === 0) {
2023    void client.close();
2024    return { tools: [], handlers: {} };
2025  }
2026  const tools = [];
2027  const handlers = {};
2028  for (const def of defs) {
2029    tools.push({
2030      name: def.name,
2031      // Prepend a provenance note so the agent can distinguish site-
2032      // provided tools from built-ins, and cap the description length so
2033      // a hostile endpoint can't flood the agent via an oversized
2034      // description.
2035      description: SITE_TOOL_NOTE + clampDescription(def.description || def.name),
2036      inputSchema: safeInputSchema(def.inputSchema)
2037    });
2038    handlers[def.name] = makeHandler(client, def.name);
2039  }
2040  console.debug(
2041    `[webmcp-interceptor] mcp-server-client: registered ${tools.length} site tool(s) from ${endpoint}.`
2042  );
2043  return { tools, handlers };
2044}
2045var mcpServerClientPack = {
2046  kind: "dynamic",
2047  name: "mcp-server-client",
2048  resolve
2049};
2050
2051// src/bridge/bridge.ts
2052var ALL_PACKS = [c2paPack, mcpServerClientPack];
2053var DEFAULT_ACTIVE_PACKS = [];
2054function resolveOwnScript() {
2055  const candidates = Array.from(
2056    document.querySelectorAll("script[src]")
2057  );
2058  try {
2059    const selfUrl = import.meta.url;
2060    const exact = candidates.find((s) => s.src === selfUrl);
2061    if (exact) return exact;
2062  } catch {
2063  }
2064  return candidates.find((s) => s.src.includes("/.webmcp/bridge.js")) ?? null;
2065}
2066var script = resolveOwnScript();
2067initBridge().catch((err) => {
2068  console.error("[webmcp-interceptor] Bridge initialisation failed:", err);
2069});
2070async function initBridge() {
2071  const mc = resolveModelContext();
2072  if (!mc) {
2073    console.warn(
2074      "[webmcp-interceptor] document.modelContext is not available.\nTo enable WebMCP, use Chrome M146+ and enable:\nchrome://flags/#enable-experimental-web-platform-features"
2075    );
2076    return;
2077  }
2078  const workerBaseUrl = resolveWorkerBaseUrl();
2079  const mcpUrl = normalizeMcpUrl(script?.dataset["mcpUrl"]);
2080  const ctx = {
2081    origin: window.location.origin,
2082    workerRpc: workerBaseUrl ? makeWorkerRpc(workerBaseUrl) : workerRpcUnavailable,
2083    ...mcpUrl ? { mcpUrl } : {}
2084  };
2085  const activePacks = selectActivePacks(
2086    ALL_PACKS,
2087    parsePackList(script?.dataset["packs"]),
2088    DEFAULT_ACTIVE_PACKS
2089  );
2090  const staticResolved = resolveStaticPacks(activePacks);
2091  const staticRegistry = buildRegistry(staticResolved);
2092  registerTools(mc, ctx, staticRegistry);
2093  const registeredNames = new Set(staticRegistry.tools.map((t) => t.name));
2094  console.debug(
2095    `[webmcp-interceptor] Registered ${staticRegistry.tools.length} static tool(s); resolving dynamic packs\u2026`
2096  );
2097  const dynamicResolved = await resolveDynamicPacks(activePacks, ctx);
2098  if (dynamicResolved.length > 0) {
2099    const combined = buildRegistry([...staticResolved, ...dynamicResolved]);
2100    const additions = {
2101      tools: combined.tools.filter((t) => !registeredNames.has(t.name)),
2102      handlers: combined.handlers
2103    };
2104    registerTools(mc, ctx, additions);
2105    console.debug(
2106      `[webmcp-interceptor] Registered ${additions.tools.length} dynamic tool(s) from ${dynamicResolved.length} dynamic pack(s).`
2107    );
2108  }
2109}
2110function registerTools(mc, ctx, registry) {
2111  for (const tool of registry.tools) {
2112    const handler = registry.handlers[tool.name];
2113    if (!handler) continue;
2114    mc.registerTool({
2115      name: tool.name,
2116      // MCP's `Tool.description` is optional but WebMCP's
2117      // `registerTool()` requires a non-empty string. Fall back to
2118      // the tool name when the description is missing OR empty (`??`
2119      // alone would let an empty string through) so registration never
2120      // fails — the agent at least sees the name.
2121      description: tool.description || tool.name,
2122      inputSchema: tool.inputSchema,
2123      // WebMCP may pass an options bag with an AbortSignal as the
2124      // second argument; forward it so handlers can cancel in-flight
2125      // work. When absent, the per-call context simply has no signal.
2126      execute: (args, options) => invokeHandler(tool.name, handler, args, {
2127        ...ctx,
2128        ...options?.signal ? { signal: options.signal } : {}
2129      })
2130    });
2131  }
2132}
2133async function invokeHandler(toolName, handler, args, ctx) {
2134  let result;
2135  try {
2136    result = await handler(args, ctx);
2137  } catch (err) {
2138    const message = err instanceof Error ? err.message : String(err);
2139    console.warn(`[webmcp-interceptor] Tool "${toolName}" threw:`, message);
2140    throw new Error(message);
2141  }
2142  if (result.isError) {
2143    const text = extractText(result) ?? `${toolName} failed`;
2144    const error = new Error(text, { cause: result });
2145    error.webmcpResult = result;
2146    throw error;
2147  }
2148  if (result.structuredContent !== void 0) return result.structuredContent;
2149  return extractText(result) ?? null;
2150}
2151function extractText(r) {
2152  if (!r.content || r.content.length === 0) return null;
2153  const texts = [];
2154  for (const block of r.content) {
2155    if (block.type === "text") texts.push(block.text);
2156  }
2157  return texts.length === 0 ? null : texts.join("\n");
2158}
2159function resolveWorkerBaseUrl() {
2160  let src = script?.src;
2161  if (!src) {
2162    try {
2163      src = import.meta.url;
2164    } catch {
2165      src = void 0;
2166    }
2167  }
2168  if (!src) return null;
2169  try {
2170    const url = new URL(src);
2171    const webmcpIndex = url.pathname.indexOf("/.webmcp/");
2172    if (webmcpIndex >= 0) {
2173      url.pathname = url.pathname.slice(0, webmcpIndex);
2174    }
2175    url.search = "";
2176    url.hash = "";
2177    return url.toString().replace(/\/$/, "");
2178  } catch {
2179    return null;
2180  }
2181}
2182function makeWorkerRpc(baseUrl) {
2183  return async (path, body, validate) => {
2184    const url = `${baseUrl}/.webmcp/rpc/${path.replace(/^\//, "")}`;
2185    const res = await fetch(url, {
2186      method: "POST",
2187      headers: { "content-type": "application/json" },
2188      // Important: NOT `credentials: 'include'`. The worker is on a
2189      // different origin and we don't want to forward the page's
2190      // cookies to it. Each pack is responsible for sending whatever
2191      // worker-specific auth it needs (today, none).
2192      body: JSON.stringify(body)
2193    });
2194    const text = await res.text();
2195    let parsed;
2196    try {
2197      parsed = text === "" ? null : JSON.parse(text);
2198    } catch {
2199      throw new Error(`Worker returned non-JSON response (HTTP ${res.status})`);
2200    }
2201    if (!res.ok) {
2202      const message = parsed && typeof parsed === "object" && "error" in parsed && typeof parsed.error === "string" ? parsed.error : `Worker RPC failed (HTTP ${res.status})`;
2203      throw new Error(message);
2204    }
2205    return validate(parsed);
2206  };
2207}
2208function workerRpcUnavailable() {
2209  throw new Error(
2210    "Worker base URL could not be determined. Worker-backed tools are unavailable on this page."
2211  );
2212}
2213function resolveModelContext() {
2214  const fromDocument = document.modelContext;
2215  if (fromDocument) return fromDocument;
2216  const fromNavigator = navigator.modelContext;
2217  return fromNavigator ?? null;
2218}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.