@modelcontextprotocol/inspector 2.13.0 → 2.14.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.
Files changed (32) hide show
  1. package/README.md +1 -1
  2. package/clients/cli/build/index.js +1013 -80
  3. package/clients/mcpdo/build/mcp-bin.js +50 -22
  4. package/clients/mcpdo/build/mcpdod.js +1 -1
  5. package/clients/mcpdo/build/{server-BHuKGkMj.js → server-Dv339Ejx.js} +886 -48
  6. package/clients/tui/build/index.js +781 -46
  7. package/clients/web/build/index.js +223 -124
  8. package/clients/web/dist/assets/fredoka-normal-hebrew-CjvktpUN.woff2 +0 -0
  9. package/clients/web/dist/assets/fredoka-normal-latin-DOQG_Jwn.woff2 +0 -0
  10. package/clients/web/dist/assets/fredoka-normal-latin-ext-tYNUQHO6.woff2 +0 -0
  11. package/clients/web/dist/assets/index-BCud2uMC.js +1594 -0
  12. package/clients/web/dist/assets/{index-dqp8Sd8W.css → index-Dval2a7I.css} +1 -1
  13. package/clients/web/dist/assets/{prism-light-BIhJtJkv.js → prism-light-CoVRM9R2.js} +1 -1
  14. package/clients/web/dist/assets/roboto-mono-italic-cyrillic-C1E63de8.woff2 +0 -0
  15. package/clients/web/dist/assets/roboto-mono-italic-cyrillic-ext-DR9ovEqT.woff2 +0 -0
  16. package/clients/web/dist/assets/roboto-mono-italic-greek-Cs2u2nVh.woff2 +0 -0
  17. package/clients/web/dist/assets/roboto-mono-italic-latin-D_P-kX1t.woff2 +0 -0
  18. package/clients/web/dist/assets/roboto-mono-italic-latin-ext-CErTcBzw.woff2 +0 -0
  19. package/clients/web/dist/assets/roboto-mono-italic-vietnamese-D4UvzJMM.woff2 +0 -0
  20. package/clients/web/dist/assets/roboto-mono-normal-cyrillic-DfzWIUPd.woff2 +0 -0
  21. package/clients/web/dist/assets/roboto-mono-normal-cyrillic-ext-D8QBEkUe.woff2 +0 -0
  22. package/clients/web/dist/assets/roboto-mono-normal-greek-DlC4X1lQ.woff2 +0 -0
  23. package/clients/web/dist/assets/roboto-mono-normal-latin-BAKpfWyj.woff2 +0 -0
  24. package/clients/web/dist/assets/roboto-mono-normal-latin-ext-CSSVNFqV.woff2 +0 -0
  25. package/clients/web/dist/assets/roboto-mono-normal-vietnamese-DFkHeOHB.woff2 +0 -0
  26. package/clients/web/dist/fonts/Fredoka-OFL.txt +93 -0
  27. package/clients/web/dist/fonts/RobotoMono-OFL.txt +93 -0
  28. package/clients/web/dist/index.html +2 -5
  29. package/clients/web/static/sandbox_proxy.html +177 -6
  30. package/package.json +5 -4
  31. package/skills/mcpdo/SKILL.md +14 -0
  32. package/clients/web/dist/assets/index-BqEbhCDX.js +0 -1587
@@ -28,7 +28,10 @@ import { DispatchError, JsonRpcResponseError, TaskFailedError, createApplication
28
28
  import { bindTaskReceiver } from "@modelcontextprotocol/ext-tasks/receiver";
29
29
  import { runtimeCodecFromStandardSchema, taskId, toJsonValue } from "@modelcontextprotocol/ext-tasks/core";
30
30
  import { TASKS_EXTENSION_ID_V2 as TASKS_EXTENSION_KEY } from "@modelcontextprotocol/ext-tasks/core/v2";
31
- import { AjvJsonSchemaValidator } from "@modelcontextprotocol/client/validators/ajv";
31
+ import Ajv from "ajv";
32
+ import { Ajv2019 } from "ajv/dist/2019.js";
33
+ import { Ajv2020 } from "ajv/dist/2020.js";
34
+ import { addFormats } from "@modelcontextprotocol/client/validators/ajv";
32
35
  import { createServer } from "node:http";
33
36
  import open from "open";
34
37
  import { createInterface as createInterface$1 } from "node:readline/promises";
@@ -4511,7 +4514,8 @@ const EXIT_CODES = {
4511
4514
  SKILL_INCOMPLETE: 8,
4512
4515
  SKILL_UNVERIFIABLE: 9,
4513
4516
  LOAD_THRESHOLD_EXCEEDED: 10,
4514
- RATE_LIMITED: 11
4517
+ RATE_LIMITED: 11,
4518
+ INTERRUPTED: 130
4515
4519
  };
4516
4520
  function rateLimitFieldsOf(error) {
4517
4521
  const data = error?.data;
@@ -6171,6 +6175,14 @@ function expectedSecretFields(stored) {
6171
6175
  if (isStdioStored(stored) && stored.env) for (const k of Object.keys(stored.env)) fields.push(envSecretField(k));
6172
6176
  return fields;
6173
6177
  }
6178
+ function adHocServerName(config) {
6179
+ if (config.type === "sse" || config.type === "streamable-http") try {
6180
+ return new URL(config.url).host || "server";
6181
+ } catch {
6182
+ return "server";
6183
+ }
6184
+ return config.command?.split(/[\\/]/).pop() || "server";
6185
+ }
6174
6186
  //#endregion
6175
6187
  //#region ../../core/mcp/node/config.ts
6176
6188
  function parseKeyValuePair(value, previous = {}) {
@@ -6436,8 +6448,9 @@ async function loadServerEntries(serverOptions) {
6436
6448
  }
6437
6449
  const configs = resolveServerConfigs(serverOptions, "multi");
6438
6450
  if (configs.length === 0) throw new Error("At least one server is required. Use --catalog/--config <path> or an ad-hoc target (command/URL).");
6439
- return { default: {
6440
- config: configs[0],
6451
+ const config = configs[0];
6452
+ return { [adHocServerName(config)]: {
6453
+ config,
6441
6454
  settings: mergeSettings(void 0, serverOptions)
6442
6455
  } };
6443
6456
  }
@@ -6986,6 +6999,13 @@ function readInspectorVersion(callerUrl) {
6986
6999
  }
6987
7000
  throw new Error(`Could not locate the ${ROOT_PACKAGE_NAME} root package.json above ${startDir}`);
6988
7001
  }
7002
+ function readInspectorVersionSafe(callerUrl) {
7003
+ try {
7004
+ return readInspectorVersion(callerUrl);
7005
+ } catch {
7006
+ return;
7007
+ }
7008
+ }
6989
7009
  //#endregion
6990
7010
  //#region src/daemon/paths.ts
6991
7011
  const DAEMON_DIR_ENV = "MCP_INSPECTOR_DAEMON_DIR";
@@ -8439,6 +8459,7 @@ function resolveLoadOptions(flags) {
8439
8459
  };
8440
8460
  }
8441
8461
  const OTHER_ERRORS = "(other)";
8462
+ const NO_TOOL_ERROR_TEXT = "(no text content)";
8442
8463
  const yieldToEventLoop = () => new Promise((resolve) => setImmediate(resolve));
8443
8464
  const realClock = {
8444
8465
  now: () => performance.now(),
@@ -8508,10 +8529,45 @@ var ErrorTally = class {
8508
8529
  return list;
8509
8530
  }
8510
8531
  };
8532
+ function isLoadStop(value) {
8533
+ if (typeof value !== "object" || value === null) return false;
8534
+ const { reason, detail } = value;
8535
+ return (reason === "interrupted" || reason === "connection_lost" || reason === "consecutive_errors") && typeof detail === "string";
8536
+ }
8537
+ function toolErrorText(content) {
8538
+ const blocks = Array.isArray(content) ? content : [];
8539
+ for (const block of blocks) {
8540
+ if (typeof block !== "object" || block === null) continue;
8541
+ const { type, text } = block;
8542
+ if (type !== "text" || typeof text !== "string") continue;
8543
+ const trimmed = text.trim();
8544
+ if (trimmed === "") continue;
8545
+ return trimmed.length > 200 ? `${trimmed.slice(0, 200)}…` : trimmed;
8546
+ }
8547
+ return NO_TOOL_ERROR_TEXT;
8548
+ }
8549
+ const LOAD_FLAG_CONFLICT = "load_flag_conflict";
8550
+ function loadSummaryWarnings(summary) {
8551
+ const warnings = [];
8552
+ const stop = summary.stoppedEarly;
8553
+ if (stop !== void 0 && stop.reason !== "interrupted") warnings.push(`Warning: the load run stopped early (${stop.reason}): ${stop.detail}.`);
8554
+ const shortfall = loadRateShortfallWarning(summary);
8555
+ if (shortfall !== void 0) warnings.push(shortfall);
8556
+ return warnings;
8557
+ }
8558
+ function loadRateShortfallWarning(summary) {
8559
+ const { rate, achievedRate, plannedStarts, droppedSlots } = summary;
8560
+ if (rate === void 0 || plannedStarts === void 0 || droppedSlots === void 0 || plannedStarts === 0) return;
8561
+ const started = plannedStarts - droppedSlots;
8562
+ if (started >= plannedStarts * .9) return void 0;
8563
+ return `Warning: --load-rate ${rate}/s was not reached: ${started} of ${plannedStarts} planned calls started (${achievedRate}/s); ${droppedSlots} slots were dropped because every worker was busy. Raise --load-concurrency or lower --load-rate.`;
8564
+ }
8511
8565
  async function runLoad(call, options, clock = realClock, signal) {
8512
8566
  const { concurrency, durationMs, requests, rate } = options;
8513
- const latencies = [];
8567
+ const okLatencies = [];
8568
+ const errorLatencies = [];
8514
8569
  const tally = new ErrorTally();
8570
+ const toolTally = new ErrorTally();
8515
8571
  let ok = 0;
8516
8572
  let toolErrors = 0;
8517
8573
  let errors = 0;
@@ -8520,6 +8576,37 @@ async function runLoad(call, options, clock = realClock, signal) {
8520
8576
  const deadline = durationMs === void 0 ? Infinity : start + durationMs;
8521
8577
  const interval = rate === void 0 ? 0 : 1e3 / rate;
8522
8578
  let nextStart = start;
8579
+ const halt = new AbortController();
8580
+ let stoppedEarly;
8581
+ let stoppedAt = 0;
8582
+ const stopRun = (stop) => {
8583
+ if (stoppedEarly) {
8584
+ if (stop.reason === "interrupted") stoppedEarly = stop;
8585
+ return;
8586
+ }
8587
+ stoppedEarly = stop;
8588
+ stoppedAt = clock.now();
8589
+ halt.abort();
8590
+ };
8591
+ const onAbort = () => {
8592
+ const reason = signal?.reason;
8593
+ stopRun(isLoadStop(reason) ? reason : {
8594
+ reason: "interrupted",
8595
+ detail: "the run was interrupted"
8596
+ });
8597
+ };
8598
+ if (signal?.aborted) onAbort();
8599
+ else signal?.addEventListener("abort", onAbort, { once: true });
8600
+ let errorStreak = 0;
8601
+ let streakSince = 0;
8602
+ const noteThrown = (now) => {
8603
+ if (errorStreak++ === 0) streakSince = now;
8604
+ const window = now - streakSince;
8605
+ if ((requests === void 0 || started < requests) && now < deadline && nextStart < deadline && (errorStreak >= 50 || errorStreak >= 5 && window >= 1e3)) stopRun({
8606
+ reason: "consecutive_errors",
8607
+ detail: `${errorStreak} calls in a row threw over ${round(window)}ms; the server looks unavailable`
8608
+ });
8609
+ };
8523
8610
  let turnstile = Promise.resolve();
8524
8611
  const claimSlot = async () => {
8525
8612
  const ahead = turnstile;
@@ -8529,7 +8616,7 @@ async function runLoad(call, options, clock = realClock, signal) {
8529
8616
  try {
8530
8617
  if (started > 0 && started % 64 === 0) await yieldToEventLoop();
8531
8618
  for (;;) {
8532
- if (signal?.aborted) return false;
8619
+ if (halt.signal.aborted) return false;
8533
8620
  if (requests !== void 0 && started >= requests) return false;
8534
8621
  const now = clock.now();
8535
8622
  if (now >= deadline) return false;
@@ -8540,7 +8627,7 @@ async function runLoad(call, options, clock = realClock, signal) {
8540
8627
  nextStart = now - nextStart < interval ? nextStart + interval : now + interval;
8541
8628
  return true;
8542
8629
  }
8543
- await sleepUnlessAborted(clock, scheduled - now, signal);
8630
+ await sleepUnlessAborted(clock, scheduled - now, halt.signal);
8544
8631
  }
8545
8632
  } finally {
8546
8633
  release();
@@ -8548,26 +8635,57 @@ async function runLoad(call, options, clock = realClock, signal) {
8548
8635
  };
8549
8636
  const worker = async (index) => {
8550
8637
  while (await claimSlot()) {
8551
- if (signal?.aborted) return;
8638
+ if (halt.signal.aborted) {
8639
+ started--;
8640
+ return;
8641
+ }
8552
8642
  const t0 = clock.now();
8553
8643
  try {
8554
- if (await call(index) === "ok") ok++;
8555
- else toolErrors++;
8644
+ const outcome = await call(index);
8645
+ const latency = clock.now() - t0;
8646
+ errorStreak = 0;
8647
+ if (outcome === "ok") {
8648
+ ok++;
8649
+ okLatencies.push(latency);
8650
+ } else {
8651
+ toolErrors++;
8652
+ errorLatencies.push(latency);
8653
+ toolTally.add(typeof outcome === "object" ? outcome.message : NO_TOOL_ERROR_TEXT);
8654
+ }
8556
8655
  } catch (err) {
8656
+ const now = clock.now();
8557
8657
  errors++;
8658
+ errorLatencies.push(now - t0);
8558
8659
  tally.add(err instanceof Error ? err.message : String(err));
8660
+ noteThrown(now);
8559
8661
  }
8560
- latencies.push(clock.now() - t0);
8561
8662
  }
8562
8663
  };
8563
- await Promise.all(Array.from({ length: concurrency }, (_, i) => worker(i)));
8564
- const elapsedMs = clock.now() - start;
8664
+ try {
8665
+ await Promise.all(Array.from({ length: concurrency }, (_, i) => worker(i)));
8666
+ } finally {
8667
+ signal?.removeEventListener("abort", onAbort);
8668
+ }
8669
+ const end = clock.now();
8670
+ const requestCapped = requests !== void 0 && started >= requests;
8671
+ const elapsedMs = durationMs !== void 0 && stoppedEarly === void 0 && !requestCapped ? Math.max(end - start, durationMs) : end - start;
8565
8672
  const completed = ok + toolErrors + errors;
8673
+ const startWindowEnd = () => {
8674
+ let windowEnd = deadline;
8675
+ if (requestCapped) windowEnd = Math.min(windowEnd, nextStart);
8676
+ if (stoppedEarly !== void 0) windowEnd = Math.min(windowEnd, stoppedAt);
8677
+ /* v8 ignore next -- defensive: a run with no duration, no request cap
8678
+ and no stop never ends, so one of the three always applies. */
8679
+ return Number.isFinite(windowEnd) ? windowEnd : end;
8680
+ };
8566
8681
  return {
8567
8682
  concurrency,
8568
8683
  ...durationMs !== void 0 && { durationMs },
8569
8684
  ...requests !== void 0 && { requestLimit: requests },
8570
- ...rate !== void 0 && { rate },
8685
+ ...rate !== void 0 && rateFigures(rate, {
8686
+ started,
8687
+ windowMs: startWindowEnd() - start
8688
+ }),
8571
8689
  requests: completed,
8572
8690
  ok,
8573
8691
  toolErrors,
@@ -8575,8 +8693,23 @@ async function runLoad(call, options, clock = realClock, signal) {
8575
8693
  errorRate: completed === 0 ? 0 : round((toolErrors + errors) / completed, 4),
8576
8694
  elapsedMs: round(elapsedMs),
8577
8695
  throughput: elapsedMs > 0 ? round(completed * 1e3 / elapsedMs) : 0,
8578
- latencyMs: summarizeLatency(latencies),
8579
- errorMessages: tally.entries()
8696
+ okThroughput: elapsedMs > 0 ? round(ok * 1e3 / elapsedMs) : 0,
8697
+ latencyMs: summarizeLatency(okLatencies),
8698
+ errorLatencyMs: summarizeLatency(errorLatencies),
8699
+ errorMessages: tally.entries(),
8700
+ toolErrorMessages: toolTally.entries(),
8701
+ ...stoppedEarly !== void 0 && { stoppedEarly }
8702
+ };
8703
+ }
8704
+ function rateFigures(rate, run) {
8705
+ const { started, windowMs } = run;
8706
+ const slots = Math.ceil(windowMs * rate / 1e3 - 1e-9);
8707
+ const plannedStarts = Math.max(started, slots);
8708
+ return {
8709
+ rate,
8710
+ plannedStarts,
8711
+ droppedSlots: plannedStarts - started,
8712
+ achievedRate: windowMs > 0 ? round(started * 1e3 / windowMs) : 0
8580
8713
  };
8581
8714
  }
8582
8715
  async function runLoadAcrossConnections(primary, openConnection, callOn, options, clock = realClock, signal) {
@@ -8638,7 +8771,11 @@ async function runToolLoad(inspectorClient, args, options, extra = {}) {
8638
8771
  /* v8 ignore next 3 -- defensive: `ToolCallInvocation.result` is nullable,
8639
8772
  but both call paths throw on failure rather than return a null result. */
8640
8773
  if (invocation.result === null) throw new Error(invocation.error ?? "Tool call failed");
8641
- return invocation.result.isError === true ? "tool_error" : "ok";
8774
+ if (invocation.result.isError !== true) return "ok";
8775
+ return {
8776
+ outcome: "tool_error",
8777
+ message: toolErrorText(invocation.result.content)
8778
+ };
8642
8779
  };
8643
8780
  const summary = await runLoadAcrossConnections(inspectorClient, extra.openConnection, call, options, extra.clock, extra.signal);
8644
8781
  return {
@@ -9855,6 +9992,627 @@ function validateToolOutput(provider, tool, result) {
9855
9992
  }
9856
9993
  }
9857
9994
  //#endregion
9995
+ //#region ../../core/json/linearRegExp.ts
9996
+ var CharMatcher = class {
9997
+ re;
9998
+ ascii = /* @__PURE__ */ new Uint8Array(128);
9999
+ constructor(source, unicode) {
10000
+ this.re = new RegExp(`^(?:${source})$`, unicode ? "u" : "");
10001
+ }
10002
+ matches(cp, unicode) {
10003
+ if (cp < 128) {
10004
+ const memo = this.ascii[cp];
10005
+ if (memo !== 0) return memo === 1;
10006
+ const result = this.re.test(String.fromCharCode(cp));
10007
+ this.ascii[cp] = result ? 1 : 2;
10008
+ return result;
10009
+ }
10010
+ return this.re.test(unicode ? String.fromCodePoint(cp) : String.fromCharCode(cp));
10011
+ }
10012
+ };
10013
+ var Unsupported = class extends Error {};
10014
+ const MAX_GROUP_DEPTH = 64;
10015
+ const MAX_PROGRAM_SIZE = 1e4;
10016
+ const MAX_COMPILE_STEPS = 10 * MAX_PROGRAM_SIZE;
10017
+ const MAX_PATTERN_LENGTH = 8192;
10018
+ const COMPILE_BUDGET_EXHAUSTED = "the patterns together exceed the compilation budget";
10019
+ function isCompileBudgetExhausted(budget) {
10020
+ return budget.steps < 0 || budget.size < 0;
10021
+ }
10022
+ const QUANTIFIER_BRACES = /\{(\d+)(?:(,)(\d*))?\}/y;
10023
+ const HEX4 = /^[0-9a-fA-F]{4}$/;
10024
+ const HEX2 = /^[0-9a-fA-F]{2}$/;
10025
+ var Parser = class {
10026
+ pos = 0;
10027
+ src;
10028
+ unicode;
10029
+ constructor(src, unicode) {
10030
+ this.src = src;
10031
+ this.unicode = unicode;
10032
+ }
10033
+ parse() {
10034
+ const node = this.parseAlternation(0);
10035
+ /* v8 ignore next -- the native RegExp constructor rejects an unbalanced `)` first */
10036
+ if (this.pos !== this.src.length) throw new Unsupported("unbalanced group");
10037
+ return node;
10038
+ }
10039
+ parseAlternation(depth) {
10040
+ const options = [this.parseSequence(depth)];
10041
+ while (this.src[this.pos] === "|") {
10042
+ this.pos++;
10043
+ options.push(this.parseSequence(depth));
10044
+ }
10045
+ return options.length === 1 ? options[0] : {
10046
+ kind: "alt",
10047
+ options
10048
+ };
10049
+ }
10050
+ parseSequence(depth) {
10051
+ const items = [];
10052
+ while (this.pos < this.src.length && this.src[this.pos] !== "|" && this.src[this.pos] !== ")") items.push(this.parseTerm(depth));
10053
+ return items.length === 1 ? items[0] : {
10054
+ kind: "seq",
10055
+ items
10056
+ };
10057
+ }
10058
+ parseTerm(depth) {
10059
+ const atom = this.parseAtom(depth);
10060
+ const quantifier = this.readQuantifier();
10061
+ if (!quantifier) return atom;
10062
+ if (this.src[this.pos] === "?") this.pos++;
10063
+ return {
10064
+ kind: "repeat",
10065
+ body: atom,
10066
+ ...quantifier
10067
+ };
10068
+ }
10069
+ readQuantifier() {
10070
+ const c = this.src[this.pos];
10071
+ if (c === "*") {
10072
+ this.pos++;
10073
+ return {
10074
+ min: 0,
10075
+ max: Infinity
10076
+ };
10077
+ }
10078
+ if (c === "+") {
10079
+ this.pos++;
10080
+ return {
10081
+ min: 1,
10082
+ max: Infinity
10083
+ };
10084
+ }
10085
+ if (c === "?") {
10086
+ this.pos++;
10087
+ return {
10088
+ min: 0,
10089
+ max: 1
10090
+ };
10091
+ }
10092
+ if (c === "{") {
10093
+ QUANTIFIER_BRACES.lastIndex = this.pos;
10094
+ const m = QUANTIFIER_BRACES.exec(this.src);
10095
+ if (!m) return void 0;
10096
+ this.pos += m[0].length;
10097
+ const min = Number(m[1]);
10098
+ return {
10099
+ min,
10100
+ max: m[2] === void 0 ? min : m[3] ? Number(m[3]) : Infinity
10101
+ };
10102
+ }
10103
+ }
10104
+ parseAtom(depth) {
10105
+ switch (this.src[this.pos]) {
10106
+ case "(": return this.parseGroup(depth);
10107
+ case "[": return this.parseClass();
10108
+ case "\\": return this.parseEscape();
10109
+ case ".":
10110
+ this.pos++;
10111
+ return {
10112
+ kind: "char",
10113
+ source: "."
10114
+ };
10115
+ case "^":
10116
+ this.pos++;
10117
+ return {
10118
+ kind: "assert",
10119
+ assertion: "start"
10120
+ };
10121
+ case "$":
10122
+ this.pos++;
10123
+ return {
10124
+ kind: "assert",
10125
+ assertion: "end"
10126
+ };
10127
+ case "*":
10128
+ case "+":
10129
+ case "?":
10130
+ /* v8 ignore next -- "nothing to repeat" is a native syntax error */
10131
+ throw new Unsupported("nothing to repeat");
10132
+ case "{":
10133
+ QUANTIFIER_BRACES.lastIndex = this.pos;
10134
+ if (QUANTIFIER_BRACES.test(this.src))
10135
+ /* v8 ignore next -- rejected by the native RegExp constructor first */
10136
+ throw new Unsupported("nothing to repeat");
10137
+ this.pos++;
10138
+ return {
10139
+ kind: "char",
10140
+ source: "\\{"
10141
+ };
10142
+ default: {
10143
+ const width = this.codePointWidth(this.pos);
10144
+ const source = this.src.slice(this.pos, this.pos + width);
10145
+ this.pos += width;
10146
+ return {
10147
+ kind: "char",
10148
+ source
10149
+ };
10150
+ }
10151
+ }
10152
+ }
10153
+ codePointWidth(at) {
10154
+ if (!this.unicode) return 1;
10155
+ return this.src.codePointAt(at) > 65535 ? 2 : 1;
10156
+ }
10157
+ parseGroup(depth) {
10158
+ if (depth >= MAX_GROUP_DEPTH) throw new Unsupported("groups nested too deeply");
10159
+ this.pos++;
10160
+ if (this.src.startsWith("?:", this.pos)) this.pos += 2;
10161
+ else if (this.src.startsWith("?<", this.pos) && this.src[this.pos + 2] !== "=" && this.src[this.pos + 2] !== "!") {
10162
+ const close = this.src.indexOf(">", this.pos);
10163
+ /* v8 ignore next -- an unterminated group name is a native syntax error */
10164
+ if (close < 0) throw new Unsupported("unterminated group name");
10165
+ this.pos = close + 1;
10166
+ } else if (this.src[this.pos] === "?") throw new Unsupported("lookaround or group modifier");
10167
+ const body = this.parseAlternation(depth + 1);
10168
+ /* v8 ignore next -- an unterminated group is a native syntax error */
10169
+ if (this.src[this.pos] !== ")") throw new Unsupported("unterminated group");
10170
+ this.pos++;
10171
+ return body;
10172
+ }
10173
+ parseClass() {
10174
+ const start = this.pos;
10175
+ this.pos++;
10176
+ while (this.pos < this.src.length && this.src[this.pos] !== "]") this.pos += this.src[this.pos] === "\\" ? 2 : 1;
10177
+ /* v8 ignore next -- an unterminated class is a native syntax error */
10178
+ if (this.pos >= this.src.length) throw new Unsupported("unterminated class");
10179
+ this.pos++;
10180
+ return {
10181
+ kind: "char",
10182
+ source: this.src.slice(start, this.pos)
10183
+ };
10184
+ }
10185
+ parseEscape() {
10186
+ const next = this.src[this.pos + 1];
10187
+ /* v8 ignore next -- a trailing backslash is a native syntax error */
10188
+ if (next === void 0) throw new Unsupported("trailing backslash");
10189
+ switch (next) {
10190
+ case "b":
10191
+ this.pos += 2;
10192
+ return {
10193
+ kind: "assert",
10194
+ assertion: "wordBoundary"
10195
+ };
10196
+ case "B":
10197
+ this.pos += 2;
10198
+ return {
10199
+ kind: "assert",
10200
+ assertion: "notWordBoundary"
10201
+ };
10202
+ case "1":
10203
+ case "2":
10204
+ case "3":
10205
+ case "4":
10206
+ case "5":
10207
+ case "6":
10208
+ case "7":
10209
+ case "8":
10210
+ case "9":
10211
+ case "k": throw new Unsupported("backreference");
10212
+ case "0":
10213
+ if (/[0-9]/.test(this.src[this.pos + 2] ?? "")) throw new Unsupported("octal escape");
10214
+ return this.takeChar(2);
10215
+ case "c":
10216
+ if (!/[a-zA-Z]/.test(this.src[this.pos + 2] ?? "")) throw new Unsupported("incomplete control escape");
10217
+ return this.takeChar(3);
10218
+ case "p":
10219
+ case "P":
10220
+ if (this.unicode && this.src[this.pos + 2] === "{") return this.takeThrough("}");
10221
+ return this.takeChar(2);
10222
+ case "x": return this.takeChar(HEX2.test(this.src.slice(this.pos + 2, this.pos + 4)) ? 4 : 2);
10223
+ case "u": return this.parseUnicodeEscape();
10224
+ default: return this.takeChar(1 + this.codePointWidth(this.pos + 1));
10225
+ }
10226
+ }
10227
+ parseUnicodeEscape() {
10228
+ if (this.unicode && this.src[this.pos + 2] === "{") return this.takeThrough("}");
10229
+ const hex = this.src.slice(this.pos + 2, this.pos + 6);
10230
+ if (!HEX4.test(hex)) return this.takeChar(2);
10231
+ const unit = parseInt(hex, 16);
10232
+ if (this.unicode && unit >= 55296 && unit <= 56319 && this.src.startsWith("\\u", this.pos + 6)) {
10233
+ const low = this.src.slice(this.pos + 8, this.pos + 12);
10234
+ if (HEX4.test(low)) {
10235
+ const lowUnit = parseInt(low, 16);
10236
+ if (lowUnit >= 56320 && lowUnit <= 57343) return this.takeChar(12);
10237
+ }
10238
+ }
10239
+ return this.takeChar(6);
10240
+ }
10241
+ takeChar(length) {
10242
+ const source = this.src.slice(this.pos, this.pos + length);
10243
+ this.pos += length;
10244
+ return {
10245
+ kind: "char",
10246
+ source
10247
+ };
10248
+ }
10249
+ takeThrough(terminator) {
10250
+ const end = this.src.indexOf(terminator, this.pos);
10251
+ /* v8 ignore next -- an unterminated `\p{` / `\u{` is a native syntax error */
10252
+ if (end < 0) throw new Unsupported("unterminated escape");
10253
+ return this.takeChar(end + 1 - this.pos);
10254
+ }
10255
+ };
10256
+ var Compiler = class {
10257
+ prog = [];
10258
+ matchers = /* @__PURE__ */ new Map();
10259
+ unicode;
10260
+ budget;
10261
+ steps = 0;
10262
+ constructor(unicode, budget) {
10263
+ this.unicode = unicode;
10264
+ this.budget = budget;
10265
+ }
10266
+ emit(inst) {
10267
+ if (this.prog.length >= 1e4) throw new Unsupported("pattern too complex");
10268
+ if (this.budget && --this.budget.size < 0) throw new Unsupported(COMPILE_BUDGET_EXHAUSTED);
10269
+ this.prog.push(inst);
10270
+ return this.prog.length - 1;
10271
+ }
10272
+ matcher(source) {
10273
+ let matcher = this.matchers.get(source);
10274
+ if (!matcher) {
10275
+ try {
10276
+ matcher = new CharMatcher(source, this.unicode);
10277
+ } catch {
10278
+ /* v8 ignore start -- defensive: each atom is a slice the native constructor already accepted in context */
10279
+ throw new Unsupported("unrecognized character syntax");
10280
+ }
10281
+ this.matchers.set(source, matcher);
10282
+ }
10283
+ return matcher;
10284
+ }
10285
+ compile(node) {
10286
+ if (++this.steps > 1e5) throw new Unsupported("pattern too complex");
10287
+ if (this.budget && --this.budget.steps < 0) throw new Unsupported(COMPILE_BUDGET_EXHAUSTED);
10288
+ switch (node.kind) {
10289
+ case "char":
10290
+ this.emit({
10291
+ op: "char",
10292
+ matcher: this.matcher(node.source)
10293
+ });
10294
+ return;
10295
+ case "assert":
10296
+ this.emit({
10297
+ op: "assert",
10298
+ assertion: node.assertion
10299
+ });
10300
+ return;
10301
+ case "seq":
10302
+ for (const item of node.items) this.compile(item);
10303
+ return;
10304
+ case "alt":
10305
+ this.compileAlternation(node.options);
10306
+ return;
10307
+ case "repeat":
10308
+ this.compileRepeat(node.body, node.min, node.max);
10309
+ return;
10310
+ }
10311
+ }
10312
+ compileAlternation(options) {
10313
+ const jumps = [];
10314
+ for (let i = 0; i < options.length - 1; i++) {
10315
+ const split = this.emit({
10316
+ op: "split",
10317
+ a: 0,
10318
+ b: 0
10319
+ });
10320
+ this.patchSplit(split, split + 1, -1);
10321
+ this.compile(options[i]);
10322
+ jumps.push(this.emit({
10323
+ op: "jmp",
10324
+ to: 0
10325
+ }));
10326
+ this.patchSplit(split, -1, this.prog.length);
10327
+ }
10328
+ this.compile(options[options.length - 1]);
10329
+ for (const jump of jumps) this.patchJump(jump, this.prog.length);
10330
+ }
10331
+ compileRepeat(body, min, max) {
10332
+ for (let i = 0; i < min; i++) {
10333
+ const before = this.prog.length;
10334
+ this.compile(body);
10335
+ if (this.prog.length === before) return;
10336
+ }
10337
+ if (max === Infinity) {
10338
+ const split = this.emit({
10339
+ op: "split",
10340
+ a: 0,
10341
+ b: 0
10342
+ });
10343
+ this.compile(body);
10344
+ this.emit({
10345
+ op: "jmp",
10346
+ to: split
10347
+ });
10348
+ this.patchSplit(split, split + 1, this.prog.length);
10349
+ return;
10350
+ }
10351
+ const splits = [];
10352
+ for (let i = min; i < max; i++) {
10353
+ const split = this.emit({
10354
+ op: "split",
10355
+ a: 0,
10356
+ b: 0
10357
+ });
10358
+ this.patchSplit(split, split + 1, -1);
10359
+ splits.push(split);
10360
+ this.compile(body);
10361
+ }
10362
+ for (const split of splits) this.patchSplit(split, -1, this.prog.length);
10363
+ }
10364
+ patchSplit(at, a, b) {
10365
+ const inst = this.prog[at];
10366
+ if (a >= 0) inst.a = a;
10367
+ if (b >= 0) inst.b = b;
10368
+ }
10369
+ patchJump(at, to) {
10370
+ this.prog[at].to = to;
10371
+ }
10372
+ };
10373
+ function isWordUnit(unit) {
10374
+ return unit >= 48 && unit <= 57 || unit >= 65 && unit <= 90 || unit >= 97 && unit <= 122 || unit === 95;
10375
+ }
10376
+ var LinearRegExp = class {
10377
+ anchoredStart;
10378
+ prog;
10379
+ unicode;
10380
+ constructor(prog, unicode) {
10381
+ this.prog = prog;
10382
+ this.unicode = unicode;
10383
+ const first = prog[0];
10384
+ this.anchoredStart = first !== void 0 && first.op === "assert" && first.assertion === "start";
10385
+ }
10386
+ get size() {
10387
+ return this.prog.length;
10388
+ }
10389
+ cost(input) {
10390
+ return (input.length + 1) * this.prog.length;
10391
+ }
10392
+ search(input, budget) {
10393
+ if (this.cost(input) > budget) return void 0;
10394
+ const prog = this.prog;
10395
+ const size = prog.length;
10396
+ const mark = new Int32Array(size).fill(-1);
10397
+ const stack = [];
10398
+ let clist = new Int32Array(size);
10399
+ let nlist = new Int32Array(size);
10400
+ const add = (list, count, start, pos, generation) => {
10401
+ stack.push(start);
10402
+ while (stack.length > 0) {
10403
+ const pc = stack.pop();
10404
+ if (mark[pc] === generation) continue;
10405
+ mark[pc] = generation;
10406
+ const inst = prog[pc];
10407
+ switch (inst.op) {
10408
+ case "match":
10409
+ stack.length = 0;
10410
+ return -1;
10411
+ case "jmp":
10412
+ stack.push(inst.to);
10413
+ break;
10414
+ case "split":
10415
+ stack.push(inst.b, inst.a);
10416
+ break;
10417
+ case "assert":
10418
+ if (this.holds(inst.assertion, input, pos)) stack.push(pc + 1);
10419
+ break;
10420
+ case "char": list[count++] = pc;
10421
+ }
10422
+ }
10423
+ return count;
10424
+ };
10425
+ let pos = 0;
10426
+ let generation = 0;
10427
+ let clen = add(clist, 0, 0, pos, generation);
10428
+ if (clen < 0) return true;
10429
+ while (pos < input.length) {
10430
+ if (this.anchoredStart && clen === 0) return false;
10431
+ const cp = this.unicode ? input.codePointAt(pos) : input.charCodeAt(pos);
10432
+ const next = pos + (this.unicode && cp > 65535 ? 2 : 1);
10433
+ generation++;
10434
+ let nlen = 0;
10435
+ for (let i = 0; i < clen; i++) {
10436
+ const pc = clist[i];
10437
+ if (prog[pc].matcher.matches(cp, this.unicode)) {
10438
+ nlen = add(nlist, nlen, pc + 1, next, generation);
10439
+ if (nlen < 0) return true;
10440
+ }
10441
+ }
10442
+ nlen = add(nlist, nlen, 0, next, generation);
10443
+ if (nlen < 0) return true;
10444
+ [clist, nlist] = [nlist, clist];
10445
+ clen = nlen;
10446
+ pos = next;
10447
+ }
10448
+ return false;
10449
+ }
10450
+ holds(assertion, input, pos) {
10451
+ switch (assertion) {
10452
+ case "start": return pos === 0;
10453
+ case "end": return pos === input.length;
10454
+ case "wordBoundary":
10455
+ case "notWordBoundary": return (pos > 0 && isWordUnit(input.charCodeAt(pos - 1))) !== (pos < input.length && isWordUnit(input.charCodeAt(pos))) === (assertion === "wordBoundary");
10456
+ }
10457
+ }
10458
+ };
10459
+ function compileLinearRegExp(pattern, unicode, budget) {
10460
+ if (pattern.length > 8192) return {
10461
+ ok: false,
10462
+ reason: `pattern longer than ${MAX_PATTERN_LENGTH} characters`
10463
+ };
10464
+ if (budget) {
10465
+ budget.steps -= pattern.length;
10466
+ if (isCompileBudgetExhausted(budget)) return {
10467
+ ok: false,
10468
+ reason: COMPILE_BUDGET_EXHAUSTED
10469
+ };
10470
+ }
10471
+ new RegExp(pattern, unicode ? "u" : "");
10472
+ try {
10473
+ const ast = new Parser(pattern, unicode).parse();
10474
+ const compiler = new Compiler(unicode, budget);
10475
+ compiler.compile(ast);
10476
+ compiler.prog.push({ op: "match" });
10477
+ return {
10478
+ ok: true,
10479
+ regExp: new LinearRegExp(compiler.prog, unicode)
10480
+ };
10481
+ } catch (error) {
10482
+ /* v8 ignore next -- every throw inside the parser/compiler is an Unsupported */
10483
+ if (!(error instanceof Unsupported)) throw error;
10484
+ return {
10485
+ ok: false,
10486
+ reason: error.message
10487
+ };
10488
+ }
10489
+ }
10490
+ //#endregion
10491
+ //#region ../../core/mcp/linearPatternValidator.ts
10492
+ const PATTERN_EVALUATION_BUDGET = 2e7;
10493
+ const SCHEMA_COMPILE_BUDGET = {
10494
+ steps: 10 * MAX_COMPILE_STEPS,
10495
+ size: 10 * MAX_PROGRAM_SIZE
10496
+ };
10497
+ const MAX_QUOTED_PATTERN = 120;
10498
+ const DRAFT_2020_12 = /* @__PURE__ */ new Set(["https://json-schema.org/draft/2020-12/schema", "http://json-schema.org/draft/2020-12/schema"]);
10499
+ const DRAFT_2019_09 = /* @__PURE__ */ new Set(["https://json-schema.org/draft/2019-09/schema", "http://json-schema.org/draft/2019-09/schema"]);
10500
+ const DRAFT_07_OR_06 = /* @__PURE__ */ new Set([
10501
+ "https://json-schema.org/draft-07/schema",
10502
+ "http://json-schema.org/draft-07/schema",
10503
+ "https://json-schema.org/draft-06/schema",
10504
+ "http://json-schema.org/draft-06/schema"
10505
+ ]);
10506
+ function declaredDialect(schema) {
10507
+ const declared = schema.$schema;
10508
+ if (typeof declared !== "string") return "2020-12";
10509
+ const uri = declared.replace(/#$/, "");
10510
+ if (DRAFT_2020_12.has(uri)) return "2020-12";
10511
+ if (DRAFT_2019_09.has(uri)) return "2019-09";
10512
+ if (DRAFT_07_OR_06.has(uri)) return "draft-7";
10513
+ throw new Error(`JSON Schema declares an unsupported dialect ("$schema": "${declared.slice(0, 200)}"). Supported: JSON Schema 2020-12, 2019-09, draft-07, and draft-06.`);
10514
+ }
10515
+ function quote(pattern) {
10516
+ return pattern.length > MAX_QUOTED_PATTERN ? `${pattern.slice(0, MAX_QUOTED_PATTERN)}…` : pattern;
10517
+ }
10518
+ var PatternLedger = class {
10519
+ remaining = PATTERN_EVALUATION_BUDGET;
10520
+ unevaluated = /* @__PURE__ */ new Set();
10521
+ reset() {
10522
+ this.remaining = PATTERN_EVALUATION_BUDGET;
10523
+ this.unevaluated.clear();
10524
+ }
10525
+ };
10526
+ var LinearPattern = class {
10527
+ source;
10528
+ flags;
10529
+ compiled;
10530
+ ledger;
10531
+ constructor(source, flags, compiled, ledger) {
10532
+ this.source = source;
10533
+ this.flags = flags;
10534
+ this.compiled = compiled;
10535
+ this.ledger = ledger;
10536
+ }
10537
+ test(input) {
10538
+ if (!this.compiled.ok) return this.skip(this.compiled.reason);
10539
+ const { regExp } = this.compiled;
10540
+ const cost = regExp.cost(input);
10541
+ if (cost > this.ledger.remaining) return this.skip(`checking a ${input.length}-character value would exceed the evaluation budget`);
10542
+ this.ledger.remaining -= cost;
10543
+ return regExp.search(input, cost) === true;
10544
+ }
10545
+ skip(reason) {
10546
+ this.ledger.unevaluated.add(`pattern "${quote(this.source)}" was not evaluated (${reason})`);
10547
+ return true;
10548
+ }
10549
+ toString() {
10550
+ return `/${this.source}/${this.flags}`;
10551
+ }
10552
+ };
10553
+ function createRegExpEngine(ledger, budget) {
10554
+ const engine = (pattern, flags) => new LinearPattern(pattern, flags, compileLinearRegExp(pattern, flags.includes("u"), budget()), ledger);
10555
+ return Object.assign(engine, { code: "linearRegExp" });
10556
+ }
10557
+ var LinearPatternJsonSchemaValidator = class {
10558
+ ledger = new PatternLedger();
10559
+ engines = /* @__PURE__ */ new Map();
10560
+ compileBudget = { ...SCHEMA_COMPILE_BUDGET };
10561
+ overBudget = /* @__PURE__ */ new WeakSet();
10562
+ engineFor(dialect) {
10563
+ let engine = this.engines.get(dialect);
10564
+ if (!engine) {
10565
+ const options = {
10566
+ strict: false,
10567
+ validateFormats: true,
10568
+ validateSchema: false,
10569
+ allErrors: true,
10570
+ code: { regExp: createRegExpEngine(this.ledger, () => this.compileBudget) }
10571
+ };
10572
+ engine = dialect === "2020-12" ? new Ajv2020(options) : dialect === "2019-09" ? new Ajv2019(options) : new Ajv(options);
10573
+ addFormats(engine);
10574
+ this.engines.set(dialect, engine);
10575
+ }
10576
+ return engine;
10577
+ }
10578
+ getValidator(schema) {
10579
+ const engine = this.engineFor(declaredDialect(schema));
10580
+ const id = schema.$id;
10581
+ const validate = this.tracked(() => (typeof id === "string" ? engine.getSchema(id) : void 0) ?? engine.compile(schema));
10582
+ const overBudget = this.overBudget.has(validate);
10583
+ return (input) => {
10584
+ this.ledger.reset();
10585
+ const valid = validate(input);
10586
+ const unevaluated = [...this.ledger.unevaluated];
10587
+ if (overBudget) unevaluated.unshift("the schema's patterns exceed the compilation budget");
10588
+ if (valid && unevaluated.length === 0) return {
10589
+ valid: true,
10590
+ data: input,
10591
+ errorMessage: void 0
10592
+ };
10593
+ const messages = valid ? [] : [engine.errorsText(validate.errors)];
10594
+ if (unevaluated.length > 0) messages.push(`${unevaluated.join("; ")}, so the value could not be verified against the schema`);
10595
+ return {
10596
+ valid: false,
10597
+ data: void 0,
10598
+ errorMessage: messages.join("; ")
10599
+ };
10600
+ };
10601
+ }
10602
+ tracked(run) {
10603
+ const outer = this.compileBudget;
10604
+ const budget = { ...SCHEMA_COMPILE_BUDGET };
10605
+ this.compileBudget = budget;
10606
+ try {
10607
+ const validate = run();
10608
+ if (isCompileBudgetExhausted(budget)) this.overBudget.add(validate);
10609
+ return validate;
10610
+ } finally {
10611
+ this.compileBudget = outer;
10612
+ }
10613
+ }
10614
+ };
10615
+ //#endregion
9858
10616
  //#region ../../core/mcp/uriTemplate.ts
9859
10617
  const OPERATORS = [
9860
10618
  "+",
@@ -11490,6 +12248,16 @@ var RemoteClientTransport = class {
11490
12248
  }
11491
12249
  return await res.json();
11492
12250
  }
12251
+ releaseOnPageHide() {
12252
+ if (!this._sessionId) return false;
12253
+ this.fetchFn(`${this.baseUrl}/api/mcp/disconnect`, {
12254
+ method: "POST",
12255
+ headers: this.headers,
12256
+ body: JSON.stringify({ sessionId: this._sessionId }),
12257
+ keepalive: true
12258
+ }).catch(() => {});
12259
+ return true;
12260
+ }
11493
12261
  async close() {
11494
12262
  if (this.closed) return;
11495
12263
  this.closed = true;
@@ -12427,7 +13195,7 @@ var InspectorClient = class InspectorClient extends InspectorClientEventTarget {
12427
13195
  params: request.params,
12428
13196
  signal: controller.signal
12429
13197
  });
12430
- this.outputValidator ??= new AjvJsonSchemaValidator();
13198
+ this.outputValidator ??= new LinearPatternJsonSchemaValidator();
12431
13199
  const invalid = validateAppElicitResult(this.outputValidator, request.params, result);
12432
13200
  if (invalid) {
12433
13201
  this.logger.warn({
@@ -13026,7 +13794,7 @@ var InspectorClient = class InspectorClient extends InspectorClientEventTarget {
13026
13794
  });
13027
13795
  }
13028
13796
  validateToolOutput(tool, result) {
13029
- this.outputValidator ??= new AjvJsonSchemaValidator();
13797
+ this.outputValidator ??= new LinearPatternJsonSchemaValidator();
13030
13798
  return validateToolOutput(this.outputValidator, tool, result);
13031
13799
  }
13032
13800
  cancelPendingTaskInput(taskId) {
@@ -13873,6 +14641,10 @@ var InspectorClient = class InspectorClient extends InspectorClientEventTarget {
13873
14641
  getRemoteBackendSessionId() {
13874
14642
  if (this.baseTransport instanceof RemoteClientTransport) return this.baseTransport.getRemoteBackendSessionId();
13875
14643
  }
14644
+ releaseRemoteSessionOnPageHide() {
14645
+ if (this.baseTransport instanceof RemoteClientTransport) return this.baseTransport.releaseOnPageHide();
14646
+ return false;
14647
+ }
13876
14648
  async resumeAfterOAuth(authorizationCode, options) {
13877
14649
  await this.completeOAuthFlow(authorizationCode, options?.iss, options?.state);
13878
14650
  const remoteSessionId = options?.remoteSessionId;
@@ -14338,21 +15110,38 @@ function isLoopbackHost(host) {
14338
15110
  //#region ../../core/auth/node/oauth-callback-server.ts
14339
15111
  const DEFAULT_HOSTNAME = "127.0.0.1";
14340
15112
  const DEFAULT_CALLBACK_PATH = "/oauth/callback";
14341
- const SUCCESS_HTML = `<!DOCTYPE html>
14342
- <html>
14343
- <head><meta charset="utf-8"><title>OAuth complete</title></head>
14344
- <body><p>OAuth complete. You can close this window.</p></body>
14345
- </html>`;
14346
- function errorHtml(message) {
15113
+ const SUCCESS_HTML = page("OAuth complete", "OAuth complete. You can close this window.");
15114
+ const NOT_CALLBACK_HTML = page("Not the OAuth callback", "This address is not the OAuth callback the Inspector is waiting for. Sign-in has not finished; return to the Inspector to continue.");
15115
+ const STATE_MISMATCH_HTML = page("OAuth callback ignored", "This callback does not belong to the sign-in in progress and was ignored. Sign-in has not finished; the Inspector is still waiting for the authorization server.");
15116
+ const ALREADY_HANDLED_HTML = page("OAuth callback already received", "This sign-in has already received its callback. You can close this window.");
15117
+ const STOPPED_HTML = page("OAuth callback not expected", "The Inspector is no longer waiting for this sign-in. Start sign-in again from the Inspector.");
15118
+ function errorHtml(serverText) {
15119
+ return page("OAuth error", `OAuth failed. The authorization server said: ${serverText}`);
15120
+ }
15121
+ function exchangeErrorHtml(message) {
15122
+ return page("OAuth error", `OAuth failed: ${message}`);
15123
+ }
15124
+ function page(title, text) {
14347
15125
  return `<!DOCTYPE html>
14348
15126
  <html>
14349
- <head><meta charset="utf-8"><title>OAuth error</title></head>
14350
- <body><p>OAuth failed: ${escapeHtml(message)}</p></body>
15127
+ <head><meta charset="utf-8"><title>${escapeHtml(title)}</title></head>
15128
+ <body><p>${escapeHtml(text)}</p></body>
14351
15129
  </html>`;
14352
15130
  }
14353
15131
  function escapeHtml(s) {
14354
15132
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
14355
15133
  }
15134
+ const ANY_STATE = Symbol("any-state");
15135
+ const STOPPED = Symbol("stopped");
15136
+ function createStopSignal() {
15137
+ let fire;
15138
+ return {
15139
+ stopped: new Promise((resolve) => {
15140
+ fire = () => resolve(STOPPED);
15141
+ }),
15142
+ fire
15143
+ };
15144
+ }
14356
15145
  var OAuthCallbackServer = class {
14357
15146
  server = null;
14358
15147
  port = 0;
@@ -14360,6 +15149,8 @@ var OAuthCallbackServer = class {
14360
15149
  handled = false;
14361
15150
  onCallback;
14362
15151
  onError;
15152
+ expectedState = Promise.resolve(ANY_STATE);
15153
+ stopSignal = createStopSignal();
14363
15154
  async start(options = {}) {
14364
15155
  const { port = 0, hostname = DEFAULT_HOSTNAME, path = DEFAULT_CALLBACK_PATH, onCallback, onError } = options;
14365
15156
  if (!path.startsWith("/")) return Promise.reject(/* @__PURE__ */ new Error("Callback path must start with '/' (absolute path)"));
@@ -14367,6 +15158,8 @@ var OAuthCallbackServer = class {
14367
15158
  this.onError = onError;
14368
15159
  this.handled = false;
14369
15160
  this.callbackPath = path;
15161
+ this.stopSignal = createStopSignal();
15162
+ this.expectedState = "expectedState" in options ? Promise.race([Promise.resolve(options.expectedState).catch(() => STOPPED), this.stopSignal.stopped]) : Promise.resolve(ANY_STATE);
14370
15163
  return new Promise((resolve, reject) => {
14371
15164
  this.server = createServer((req, res) => this.handleRequest(req, res));
14372
15165
  this.server.on("error", reject);
@@ -14390,6 +15183,7 @@ var OAuthCallbackServer = class {
14390
15183
  }
14391
15184
  async stop() {
14392
15185
  if (!this.server) return;
15186
+ this.stopSignal.fire();
14393
15187
  await new Promise((resolve) => {
14394
15188
  this.server.close(() => resolve());
14395
15189
  });
@@ -14398,11 +15192,14 @@ var OAuthCallbackServer = class {
14398
15192
  handleRequest(req, res) {
14399
15193
  const needJson = req.headers["accept"]?.includes("application/json");
14400
15194
  const send = (status, body, contentType = "text/html; charset=utf-8") => {
14401
- res.writeHead(status, { "Content-Type": contentType });
15195
+ res.writeHead(status, {
15196
+ "Content-Type": contentType,
15197
+ Connection: "close"
15198
+ });
14402
15199
  res.end(body);
14403
15200
  };
14404
15201
  if (req.method !== "GET") {
14405
- send(405, needJson ? "{\"error\":\"Method Not Allowed\"}" : SUCCESS_HTML);
15202
+ send(405, needJson ? "{\"error\":\"Method Not Allowed\"}" : NOT_CALLBACK_HTML);
14406
15203
  return;
14407
15204
  }
14408
15205
  let pathname;
@@ -14419,15 +15216,26 @@ var OAuthCallbackServer = class {
14419
15216
  search = u.search;
14420
15217
  state = u.searchParams.get("state") ?? void 0;
14421
15218
  } catch {
14422
- send(400, needJson ? "{\"error\":\"Bad Request\"}" : SUCCESS_HTML);
15219
+ send(400, needJson ? "{\"error\":\"Bad Request\"}" : NOT_CALLBACK_HTML);
14423
15220
  return;
14424
15221
  }
14425
15222
  if (pathname !== this.callbackPath) {
14426
- send(404, needJson ? "{\"error\":\"Not Found\"}" : SUCCESS_HTML);
15223
+ send(404, needJson ? "{\"error\":\"Not Found\"}" : NOT_CALLBACK_HTML);
15224
+ return;
15225
+ }
15226
+ this.expectedState.then((expected) => this.handleCallback(expected, state, search, needJson, send));
15227
+ }
15228
+ handleCallback(expected, state, search, needJson, send) {
15229
+ if (expected === STOPPED) {
15230
+ send(503, needJson ? "{\"error\":\"Not waiting for a callback\"}" : STOPPED_HTML);
15231
+ return;
15232
+ }
15233
+ if (expected !== ANY_STATE && state !== expected) {
15234
+ send(400, needJson ? "{\"error\":\"State mismatch\"}" : STATE_MISMATCH_HTML);
14427
15235
  return;
14428
15236
  }
14429
15237
  if (this.handled) {
14430
- send(409, needJson ? "{\"error\":\"Callback already handled\"}" : SUCCESS_HTML);
15238
+ send(409, needJson ? "{\"error\":\"Callback already handled\"}" : ALREADY_HANDLED_HTML);
14431
15239
  return;
14432
15240
  }
14433
15241
  const params = parseOAuthCallbackParams(search);
@@ -14445,9 +15253,10 @@ var OAuthCallbackServer = class {
14445
15253
  const msg = err instanceof Error ? err.message : String(err);
14446
15254
  this.onError?.({
14447
15255
  error: "callback_error",
14448
- error_description: msg
15256
+ error_description: msg,
15257
+ source: "callback"
14449
15258
  });
14450
- send(500, errorHtml(msg));
15259
+ send(500, exchangeErrorHtml(msg));
14451
15260
  this.stop();
14452
15261
  });
14453
15262
  else {
@@ -14460,7 +15269,8 @@ var OAuthCallbackServer = class {
14460
15269
  const msg = generateOAuthErrorDescription(params);
14461
15270
  this.onError?.({
14462
15271
  error: params.error,
14463
- error_description: params.error_description ?? void 0
15272
+ error_description: params.error_description ?? void 0,
15273
+ source: "redirect"
14464
15274
  });
14465
15275
  send(400, errorHtml(msg));
14466
15276
  }
@@ -14524,6 +15334,14 @@ function toRunnerOAuthError(err) {
14524
15334
  }
14525
15335
  return err instanceof Error ? err : new Error(String(err));
14526
15336
  }
15337
+ function stateOf(authorizationUrl) {
15338
+ return authorizationUrl.searchParams.get("state") ?? void 0;
15339
+ }
15340
+ function callbackErrorMessage(params) {
15341
+ if (params.source === "callback") return params.error_description ?? "OAuth callback failed";
15342
+ if (!params.error && !params.error_description) return "OAuth error";
15343
+ return `OAuth authorization failed. The authorization server said: ${params.error_description ? `${params.error_description} (${params.error})` : params.error}`;
15344
+ }
14527
15345
  async function runRunnerInteractiveOAuth(options) {
14528
15346
  const server = (options.createCallbackServer ?? createOAuthCallbackServer)();
14529
15347
  let flowResolve;
@@ -14553,11 +15371,16 @@ async function runRunnerInteractiveOAuth(options) {
14553
15371
  process.on("SIGTERM", onSignal);
14554
15372
  }
14555
15373
  let timeoutId;
15374
+ let resolveExpectedState;
15375
+ const expectedState = new Promise((resolve) => {
15376
+ resolveExpectedState = resolve;
15377
+ });
14556
15378
  try {
14557
15379
  const { redirectUrl } = await racingSignals(server.start({
14558
15380
  hostname: options.callbackListen.hostname,
14559
15381
  port: options.callbackListen.port,
14560
15382
  path: options.callbackListen.pathname,
15383
+ expectedState,
14561
15384
  onCallback: async (params) => {
14562
15385
  try {
14563
15386
  await options.client.completeOAuthFlow(params.code, params.iss, params.state);
@@ -14567,10 +15390,7 @@ async function runRunnerInteractiveOAuth(options) {
14567
15390
  }
14568
15391
  },
14569
15392
  onError: (params) => {
14570
- flowReject(new Error(
14571
- /* v8 ignore next -- params.error is a required non-null string, so the "OAuth error" fallback is unreachable */
14572
- params.error_description ?? params.error ?? "OAuth error"
14573
- ));
15393
+ flowReject(new Error(callbackErrorMessage(params)));
14574
15394
  }
14575
15395
  }));
14576
15396
  options.onCallbackServer?.(server);
@@ -14586,10 +15406,16 @@ async function runRunnerInteractiveOAuth(options) {
14586
15406
  })]);
14587
15407
  waitForCallback.catch(() => {});
14588
15408
  if (options.authorizationUrl) {
15409
+ resolveExpectedState(stateOf(options.authorizationUrl));
14589
15410
  await racingSignals(options.client.beginInteractiveAuthorization(options.authorizationUrl));
14590
15411
  await waitForCallback;
14591
- } else if (await racingSignals(options.client.authenticate()) !== void 0) await waitForCallback;
14592
- else return { kind: "already_authorized" };
15412
+ } else {
15413
+ const authUrl = await racingSignals(options.client.authenticate());
15414
+ if (authUrl !== void 0) {
15415
+ resolveExpectedState(stateOf(authUrl));
15416
+ await waitForCallback;
15417
+ } else return { kind: "already_authorized" };
15418
+ }
14593
15419
  if (options.authChallenge) {
14594
15420
  if (!await racingSignals(options.client.checkAuthChallengeSatisfied(options.authChallenge, { scopeOnly: true }))) return {
14595
15421
  kind: "insufficient_scope",
@@ -15655,6 +16481,7 @@ function connectCancelledError() {
15655
16481
  }
15656
16482
  //#endregion
15657
16483
  //#region src/daemon/server.ts
16484
+ const DAEMON_VERSION = readInspectorVersionSafe(import.meta.url);
15658
16485
  const MCPDO_RUN_OPTIONS = { skillBudgetHint: MCPDO_SKILL_BUDGET_HINT };
15659
16486
  const autoCancelElicitationChannel = { request(frame) {
15660
16487
  return Promise.resolve({
@@ -15828,7 +16655,8 @@ var DaemonServer = class DaemonServer {
15828
16655
  result: {
15829
16656
  pong: true,
15830
16657
  pid: process.pid,
15831
- stopping: this.stopping
16658
+ stopping: this.stopping,
16659
+ version: DAEMON_VERSION
15832
16660
  }
15833
16661
  } };
15834
16662
  case "connect": {
@@ -15976,13 +16804,23 @@ var DaemonServer = class DaemonServer {
15976
16804
  if (isTerminalStatus(client.getStatus())) throw new CliExitCodeError(EXIT_CODES.UNREACHABLE, "The connection dropped before this command could run; re-run the command to reconnect.", { code: "connection_stale" });
15977
16805
  const unwire = wireElicitationBridge(client, autoCancelElicitationChannel, requestId);
15978
16806
  const stop = new AbortController();
15979
- const onCallerGone = () => stop.abort();
16807
+ const abortRequests = new AbortController();
16808
+ const onCallerGone = () => {
16809
+ stop.abort();
16810
+ abortRequests.abort();
16811
+ };
15980
16812
  signal?.addEventListener("abort", onCallerGone, { once: true });
15981
16813
  const onStatus = (event) => {
15982
- if (isTerminalStatus(event.detail)) stop.abort();
16814
+ if (!isTerminalStatus(event.detail)) return;
16815
+ const lost = {
16816
+ reason: "connection_lost",
16817
+ detail: `the connection went ${event.detail}`
16818
+ };
16819
+ stop.abort(lost);
16820
+ abortRequests.abort(new DOMException(`load run stopped: ${lost.detail}`, "AbortError"));
15983
16821
  };
15984
16822
  client.addEventListener("statusChange", onStatus);
15985
- const clearAmbientSignal = client.setAmbientRequestSignal(stop.signal);
16823
+ const clearAmbientSignal = client.setAmbientRequestSignal(abortRequests.signal);
15986
16824
  const extras = [];
15987
16825
  try {
15988
16826
  return {
@@ -15990,8 +16828,8 @@ var DaemonServer = class DaemonServer {
15990
16828
  summary: await runToolLoad(client, methodArgs, load, {
15991
16829
  signal: stop.signal,
15992
16830
  ...openLoadClient && { openConnection: async () => {
15993
- const extra = await openLoadClient(client, stop.signal);
15994
- const clearExtraSignal = extra.setAmbientRequestSignal(stop.signal);
16831
+ const extra = await openLoadClient(client, abortRequests.signal);
16832
+ const clearExtraSignal = extra.setAmbientRequestSignal(abortRequests.signal);
15995
16833
  const unwireExtra = wireElicitationBridge(extra, autoCancelElicitationChannel, requestId);
15996
16834
  extras.push({
15997
16835
  client: extra,
@@ -16277,4 +17115,4 @@ function raceCallOrElicitation(outcome, channel) {
16277
17115
  }))]);
16278
17116
  }
16279
17117
  //#endregion
16280
- export { CliExitCodeError as $, sanitizeDeep as A, ensureDaemonDir as B, clearAllOAuthClientState as C, positiveNumberParser as D, positiveIntegerParser as E, readDaemonTokenFile as F, loadServerEntries as G, getDaemonLogPath as H, DAEMON_DIR_ENV as I, parseKeyValuePair as J, selectServerEntry as K, DAEMON_TOKEN_ENV as L, encodeRequest as M, generateDaemonToken as N, resolveLoadOptions as O, getDaemonTokenFromEnv as P, DEFAULT_TASK_TTL_MS as Q, assertSocketPathWithinLimit as R, NodeOAuthStorage as S, resetNodeOAuthStorageCache as T, getDaemonSocketPath as U, getDaemonDir as V, readInspectorVersion as W, withDefaultCatalogPath as X, resolveServerSource as Y, DEFAULT_MAX_FETCH_REQUESTS as Z, isCliAutoOpenForced as _, obtainPendingUrlForKey as a, setSecretStorageWarningsQuiet as at, DEFAULT_CONNECT_TIMEOUT_MS as b, runAuthHelper as c, CallbackNavigation as ct, emaLogin as d, idpOAuthStorageKey as dt, EXIT_CODES as et, emaLogout as f, normalizeIdpIssuer as ft, runEmaIdpInteractiveFlow as g, requireIdp as h, awaitableLog as ht, obtainPendingAuthUrl as i, getSecretStorageInfo as it, sanitizeText as j, isSafeLinkTarget as k, writePendingAuthMarker as l, clearEmaIdpSession as lt, loadEmaIdpConfig as m, awaitableError as mt, AUTH_HELPER_COMMAND as n, readOAuthStore as nt, pendingAuthMarkerPath as o, warnAboutSecretStorage as ot, getEmaStatus as p, parseIdpOAuthStorageKey as pt, parseHeaderPair as q, PENDING_AUTH_TTL_MS as r, disallowMemorySecretStoreFallback as rt, removeOwnPendingAuthMarker as s, InMemorySecretStore as st, DaemonServer as t, handleError as tt, authorizeInFrontend as u, getEmaIdpLoginState as ut, PLAIN as v, getStateFilePath as w, withConnectTimeout as x, styleFromOpts as y, createPrivateDaemonDir as z };
17118
+ export { withDefaultCatalogPath as $, resolveLoadOptions as A, assertSocketPathWithinLimit as B, clearAllOAuthClientState as C, loadSummaryWarnings as D, LOAD_FLAG_CONFLICT as E, generateDaemonToken as F, getDaemonSocketPath as G, ensureDaemonDir as H, getDaemonTokenFromEnv as I, loadServerEntries as J, readInspectorVersion as K, readDaemonTokenFile as L, sanitizeDeep as M, sanitizeText as N, positiveIntegerParser as O, encodeRequest as P, resolveServerSource as Q, DAEMON_DIR_ENV as R, NodeOAuthStorage as S, resetNodeOAuthStorageCache as T, getDaemonDir as U, createPrivateDaemonDir as V, getDaemonLogPath as W, parseHeaderPair as X, selectServerEntry as Y, parseKeyValuePair as Z, isCliAutoOpenForced as _, awaitableError as _t, obtainPendingUrlForKey as a, readOAuthStore as at, DEFAULT_CONNECT_TIMEOUT_MS as b, runAuthHelper as c, setSecretStorageWarningsQuiet as ct, emaLogin as d, CallbackNavigation as dt, DEFAULT_MAX_FETCH_REQUESTS as et, emaLogout as f, clearEmaIdpSession as ft, runEmaIdpInteractiveFlow as g, parseIdpOAuthStorageKey as gt, requireIdp as h, normalizeIdpIssuer as ht, obtainPendingAuthUrl as i, handleError as it, isSafeLinkTarget as j, positiveNumberParser as k, writePendingAuthMarker as l, warnAboutSecretStorage as lt, loadEmaIdpConfig as m, idpOAuthStorageKey as mt, AUTH_HELPER_COMMAND as n, CliExitCodeError as nt, pendingAuthMarkerPath as o, disallowMemorySecretStoreFallback as ot, getEmaStatus as p, getEmaIdpLoginState as pt, readInspectorVersionSafe as q, PENDING_AUTH_TTL_MS as r, EXIT_CODES as rt, removeOwnPendingAuthMarker as s, getSecretStorageInfo as st, DaemonServer as t, DEFAULT_TASK_TTL_MS as tt, authorizeInFrontend as u, InMemorySecretStore as ut, PLAIN as v, awaitableLog as vt, getStateFilePath as w, withConnectTimeout as x, styleFromOpts as y, DAEMON_TOKEN_ENV as z };