@chrischall/office-outlook-mcp 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bundle.js CHANGED
@@ -248,6 +248,7 @@ __export(util_exports, {
248
248
  required: () => required,
249
249
  safeExtend: () => safeExtend,
250
250
  shallowClone: () => shallowClone,
251
+ sizeCaps: () => sizeCaps,
251
252
  slugify: () => slugify,
252
253
  stringifyPrimitive: () => stringifyPrimitive,
253
254
  toZod: () => toZod,
@@ -671,6 +672,18 @@ function prefixIssues(path, issues) {
671
672
  function unwrapMessage(message) {
672
673
  return typeof message === "string" ? message : message?.message;
673
674
  }
675
+ function sizeCaps(defChecks) {
676
+ const caps = [];
677
+ for (const ch of defChecks ?? []) {
678
+ const d = ch._zod.def;
679
+ if (!/_(length|size)$|_equals$/.test(d.check))
680
+ break;
681
+ const max = d.maximum ?? d.length ?? d.size;
682
+ if (max >= 0)
683
+ caps.push([max, ch]);
684
+ }
685
+ return caps;
686
+ }
674
687
  function attachSchema(issues, start, inst) {
675
688
  var _a3;
676
689
  for (let i = start; i < issues.length; i++) {
@@ -2234,7 +2247,7 @@ var init_versions = __esm({
2234
2247
  version = {
2235
2248
  major: 4,
2236
2249
  minor: 6,
2237
- patch: 5
2250
+ patch: 7
2238
2251
  };
2239
2252
  }
2240
2253
  });
@@ -2396,6 +2409,28 @@ function isValidJWT(token, algorithm = null) {
2396
2409
  return false;
2397
2410
  }
2398
2411
  }
2412
+ function capSize(inst, size) {
2413
+ const caps = sizeCaps(inst._zod.def.checks);
2414
+ if (!caps.length)
2415
+ return;
2416
+ const cap = Math.min(...caps.map((c) => c[0]));
2417
+ const parse3 = inst._zod.parse;
2418
+ inst._zod.parse = (payload, ctx) => {
2419
+ const n = size(payload.value);
2420
+ if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
2421
+ for (const [max, ch] of sizeCaps(inst._zod.def.checks)) {
2422
+ if (n > max && ch._zod.def.when(payload)) {
2423
+ const start = payload.issues.length;
2424
+ ch._zod.check(payload);
2425
+ payload.issues[start].continue = false;
2426
+ attachSchema(payload.issues, start, inst);
2427
+ return payload;
2428
+ }
2429
+ }
2430
+ }
2431
+ return parse3(payload, ctx);
2432
+ };
2433
+ }
2399
2434
  function handleArrayResult(result, final, index) {
2400
2435
  if (result.issues.length) {
2401
2436
  final.issues.push(...prefixIssues(index, result.issues));
@@ -2896,7 +2931,7 @@ function handleRefineResult(result, payload, input2, inst) {
2896
2931
  payload.issues.push(issue(_iss));
2897
2932
  }
2898
2933
  }
2899
- var $ZodType, toStandardResult, $ZodString, $ZodStringFormat, $ZodGUID, $ZodUUID, $ZodEmail, URL_BAD_FORMAT, URL_UNPARSEABLE, asciiTabOrNewline, $ZodURL, $ZodEmoji, $ZodNanoID, $ZodCUID, $ZodCUID2, $ZodULID, $ZodXID, $ZodKSUID, $ZodISODateTime, $ZodISODate, $ZodISOTime, $ZodISODuration, $ZodIPv4, ipv6Alphabet, $ZodIPv6, $ZodMAC, $ZodCIDRv4, $ZodCIDRv6, base64Charset, $ZodBase64, base64urlCharset, $ZodBase64URL, $ZodE164, CC_SANITIZE, $ZodCreditCard, $ZodIBAN, $ZodJWT, $ZodCustomStringFormat, $ZodNumber, $ZodNumberFormat, $ZodBoolean, $ZodBigInt, $ZodBigIntFormat, $ZodSymbol, $ZodUndefined, $ZodNull, $ZodAny, $ZodUnknown, $ZodNever, $ZodVoid, $ZodDate, $ZodArray, NO_SYMBOL_KEYS, $ZodObject, $ZodObjectJIT, $ZodUnion, $ZodXor, $ZodDiscriminatedUnion, $ZodIntersection, $ZodTuple, $ZodRecord, $ZodMap, $ZodSet, $ZodEnum, $ZodLiteral, $ZodFile, $ZodTransform, $ZodOptional, $ZodExactOptional, $ZodNullable, $ZodDefault, $ZodPrefault, $ZodNonOptional, $ZodSuccess, $ZodCatch, $ZodNaN, $ZodPipe, $ZodCodec, $ZodPreprocess, $ZodReadonly, $ZodTemplateLiteral, $ZodFunction, $ZodPromise, $ZodLazy, $ZodCustom;
2934
+ var $ZodType, toStandardResult, $ZodString, $ZodStringFormat, $ZodGUID, $ZodUUID, $ZodEmail, URL_BAD_FORMAT, URL_UNPARSEABLE, asciiTabOrNewline, $ZodURL, $ZodEmoji, $ZodNanoID, $ZodCUID, $ZodCUID2, $ZodULID, $ZodXID, $ZodKSUID, $ZodISODateTime, $ZodISODate, $ZodISOTime, $ZodISODuration, $ZodIPv4, ipv6Alphabet, $ZodIPv6, $ZodMAC, $ZodCIDRv4, $ZodCIDRv6, base64Charset, $ZodBase64, base64urlCharset, $ZodBase64URL, $ZodE164, CC_SANITIZE, $ZodCreditCard, $ZodIBAN, $ZodJWT, $ZodCustomStringFormat, $ZodNumber, $ZodNumberFormat, $ZodBoolean, $ZodBigInt, $ZodBigIntFormat, $ZodSymbol, $ZodUndefined, $ZodNull, $ZodAny, $ZodUnknown, $ZodNever, $ZodVoid, $ZodDate, arraySize, mapSize, setSize, $ZodArray, NO_SYMBOL_KEYS, $ZodObject, $ZodObjectJIT, $ZodUnion, $ZodXor, $ZodDiscriminatedUnion, $ZodIntersection, $ZodTuple, $ZodRecord, $ZodMap, $ZodSet, $ZodEnum, $ZodLiteral, $ZodFile, $ZodTransform, $ZodOptional, $ZodExactOptional, $ZodNullable, $ZodDefault, $ZodPrefault, $ZodNonOptional, $ZodSuccess, $ZodCatch, $ZodNaN, $ZodPipe, $ZodCodec, $ZodPreprocess, $ZodReadonly, $ZodTemplateLiteral, $ZodFunction, $ZodPromise, $ZodLazy, $ZodCustom;
2900
2935
  var init_schemas = __esm({
2901
2936
  "node_modules/zod/v4/core/schemas.js"() {
2902
2937
  init_checks();
@@ -3503,6 +3538,9 @@ var init_schemas = __esm({
3503
3538
  return payload;
3504
3539
  };
3505
3540
  });
3541
+ arraySize = (input2) => Array.isArray(input2) ? input2.length : -1;
3542
+ mapSize = (input2) => input2 instanceof Map ? input2.size : -1;
3543
+ setSize = (input2) => input2 instanceof Set ? input2.size : -1;
3506
3544
  $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
3507
3545
  $ZodType.init(inst, def);
3508
3546
  const memo3 = globalConfig.memoizer;
@@ -3540,6 +3578,7 @@ var init_schemas = __esm({
3540
3578
  }
3541
3579
  return payload;
3542
3580
  };
3581
+ capSize(inst, arraySize);
3543
3582
  });
3544
3583
  NO_SYMBOL_KEYS = [];
3545
3584
  $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
@@ -4178,6 +4217,7 @@ var init_schemas = __esm({
4178
4217
  return Promise.all(proms).then(() => payload);
4179
4218
  return payload;
4180
4219
  };
4220
+ capSize(inst, mapSize);
4181
4221
  });
4182
4222
  $ZodSet = /* @__PURE__ */ $constructor("$ZodSet", (inst, def) => {
4183
4223
  $ZodType.init(inst, def);
@@ -4214,6 +4254,7 @@ var init_schemas = __esm({
4214
4254
  return Promise.all(proms).then(() => payload);
4215
4255
  return payload;
4216
4256
  };
4257
+ capSize(inst, setSize);
4217
4258
  });
4218
4259
  $ZodEnum = /* @__PURE__ */ $constructor("$ZodEnum", (inst, def) => {
4219
4260
  $ZodType.init(inst, def);
@@ -14003,9 +14044,15 @@ function generateNullableCheck(doc, ctx, schema, accessor, buildsValue = true) {
14003
14044
  doc.write(`}`);
14004
14045
  return outputVar;
14005
14046
  }
14047
+ function generateSizeGate(doc, schema, size) {
14048
+ for (const [max, check2] of sizeCaps(schema._zod.def.checks)) {
14049
+ doc.write(`if (${size} > ${numericOperand(max, check2._zod.def.check)}) return INVALID;`);
14050
+ }
14051
+ }
14006
14052
  function generateArrayCheck(doc, ctx, schema, accessor, buildsValue = true) {
14007
14053
  const def = schema._zod.def;
14008
14054
  doc.write(`if (!Array.isArray(${accessor})) return INVALID;`);
14055
+ generateSizeGate(doc, schema, `${accessor}.length`);
14009
14056
  const outputVar = buildsValue ? newVar(ctx) : null;
14010
14057
  const iVar = newVar(ctx);
14011
14058
  const elemVar = newVar(ctx);
@@ -14405,6 +14452,7 @@ function literalPropertyKey(ctx, key) {
14405
14452
  function generateMapCheck(doc, ctx, schema, accessor) {
14406
14453
  const def = schema._zod.def;
14407
14454
  doc.write(`if (!(${accessor} instanceof Map)) return INVALID;`);
14455
+ generateSizeGate(doc, schema, `${accessor}.size`);
14408
14456
  const outputVar = newVar(ctx);
14409
14457
  const kVar = newVar(ctx);
14410
14458
  const valVar = newVar(ctx);
@@ -14421,6 +14469,7 @@ function generateMapCheck(doc, ctx, schema, accessor) {
14421
14469
  function generateSetCheck(doc, ctx, schema, accessor) {
14422
14470
  const def = schema._zod.def;
14423
14471
  doc.write(`if (!(${accessor} instanceof Set)) return INVALID;`);
14472
+ generateSizeGate(doc, schema, `${accessor}.size`);
14424
14473
  const outputVar = newVar(ctx);
14425
14474
  const valVar = newVar(ctx);
14426
14475
  doc.write(`const ${outputVar} = new Set();`);
@@ -35372,10 +35421,33 @@ function redactJsonCookie(quote) {
35372
35421
  function redactSecrets(text) {
35373
35422
  return text.replace(BEARER_RE, "$1[REDACTED]").replace(BASIC_AUTH_RE, "$1[REDACTED]").replace(SET_COOKIE_RE, "$1$2=[REDACTED]").replace(COOKIE_HEADER_RE, (_m, prefix, pairs) => `${prefix}${pairs.replace(/=[^;,\s]*/g, "=[REDACTED]")}`).replace(API_KEY_RE, "[REDACTED]").replace(GOOGLE_OAUTH_TOKEN_RE, "[REDACTED]").replace(HEADER_SECRET_RE, "$1[REDACTED]").replace(QUERY_SECRET_RE, "$1[REDACTED]").replace(OAUTH_CODE_RE, "$1[REDACTED]").replace(FORM_PASSWORD_RE, "$1[REDACTED]").replace(AWS_SIGV4_RE, "$1[REDACTED]").replace(JSON_COOKIE_DQ_RE, redactJsonCookie('"')).replace(JSON_COOKIE_SQ_RE, redactJsonCookie("'")).replace(JSON_SECRET_DQ_RE, "$1[REDACTED]$2").replace(JSON_SECRET_SQ_RE, "$1[REDACTED]$2").replace(JSON_SECRET_NUM_RE, "$1[REDACTED]").replace(JWT_RE, "[REDACTED]");
35374
35423
  }
35375
- function truncateErrorMessage(text, max = DEFAULT_ERROR_MESSAGE_MAX) {
35424
+ function redactValues(text, values, opts = {}) {
35425
+ const placeholder = opts.placeholder ?? "[REDACTED]";
35426
+ const minLength = Math.max(1, opts.minLength ?? 1);
35427
+ const needles = /* @__PURE__ */ new Set();
35428
+ for (const value of values) {
35429
+ if (typeof value !== "string" || value.length < minLength)
35430
+ continue;
35431
+ needles.add(value);
35432
+ if (opts.jsonEscaped !== false) {
35433
+ const escaped = JSON.stringify(value).slice(1, -1);
35434
+ if (escaped !== value && escaped.length >= minLength)
35435
+ needles.add(escaped);
35436
+ }
35437
+ }
35438
+ let out = String(text ?? "");
35439
+ for (const needle of [...needles].sort((a, b) => b.length - a.length)) {
35440
+ if (out.includes(needle))
35441
+ out = out.split(needle).join(placeholder);
35442
+ }
35443
+ return out;
35444
+ }
35445
+ function truncateErrorMessage(text, max = DEFAULT_ERROR_MESSAGE_MAX, opts = {}) {
35376
35446
  const str = text === null || text === void 0 ? "" : String(text);
35377
35447
  const cap = Math.max(ERROR_REDACTION_INPUT_MAX, max);
35378
- const redacted = redactSecrets(str.length > cap ? str.slice(0, cap) : str);
35448
+ const capped = str.length > cap ? str.slice(0, cap) : str;
35449
+ const scrubbed = opts.values !== void 0 ? redactValues(capped, opts.values, opts.placeholder !== void 0 ? { placeholder: opts.placeholder } : {}) : capped;
35450
+ const redacted = redactSecrets(scrubbed);
35379
35451
  if (redacted.length <= max)
35380
35452
  return redacted;
35381
35453
  return `${redacted.slice(0, max)}\u2026 [truncated]`;
@@ -35482,7 +35554,7 @@ var init_errors3 = __esm({
35482
35554
  // webhook signing secret (Stripe-style)
35483
35555
  ].map((p) => `\\b${p}`).join("|"), "g");
35484
35556
  GOOGLE_OAUTH_TOKEN_RE = /(?<![A-Za-z0-9+/])(?:ya29\.|1\/\/)[A-Za-z0-9._-]{8,}/g;
35485
- QUERY_SECRET_RE = new RegExp(`([?&](?:(?:access|refresh|id|auth|session|csrf|xsrf)[_-]?token|client[_-]?secret|api[_-]?secret|api[_-]?key|password|passwd|signature|token|key|sig|(?:code[_-]?)?verifier|(?:php|j)?sess(?:ion)?[_-]?id|sid|x[-_]${HEADER_NAME_RUN}?(?:api[-_]?key|token|secret|auth)${HEADER_NAME_RUN})=)[^&#\\s"'<>\`]+`, "gi");
35557
+ QUERY_SECRET_RE = new RegExp(`([?&#;](?:(?:access|refresh|id|auth|session|csrf|xsrf|api)[_-]?token|client[_-]?secret|api[_-]?secret|api[_-]?key|password|passwd|signature|secret|token|auth|key|sig|(?:code[_-]?)?verifier|(?:php|j)?sess(?:ion)?[_-]?id|sid|x[-_]${HEADER_NAME_RUN}?(?:api[-_]?key|token|secret|auth)${HEADER_NAME_RUN})=)[^&#\\s"'<>\`]+`, "gi");
35486
35558
  OAUTH_CODE_RE = /([?&]code=)(?=[^&#\s"'<>`]{16,})[^&#\s"'<>`]+/gi;
35487
35559
  FORM_PASSWORD_RE = /((?:^|[\s,;:"'(\[{])(?:password|passwd)=)[^&#\s"'<>`]+/gim;
35488
35560
  HEADER_SECRET_RE = new RegExp(`(\\b(?:x[-_]${HEADER_NAME_RUN}?(?:api[-_]?key|token|secret|auth)${HEADER_NAME_RUN}|api[-_]?key)\\s*:\\s*)[^\\s,;"'<>\`]+`, "gi");
@@ -35493,6 +35565,8 @@ var init_errors3 = __esm({
35493
35565
  "api[_-]?secret",
35494
35566
  "(?:x[_-])?api[_-]?key",
35495
35567
  "private[_-]?key",
35568
+ "aws[_-]?secret[_-]?access[_-]?key",
35569
+ // fleet-audit#1297
35496
35570
  "code[_-]?verifier",
35497
35571
  // PKCE; a bare `verifier` key is too generic for JSON
35498
35572
  `x[_-]${HEADER_NAME_RUN}?(?:api[_-]?key|token|secret|auth)${HEADER_NAME_RUN}`,
@@ -35522,6 +35596,16 @@ var init_zod = __esm({
35522
35596
  }
35523
35597
  });
35524
35598
 
35599
+ // node_modules/@chrischall/mcp-utils/dist/response/media.js
35600
+ var MEDIA_NOUN, MEDIA_QUALIFIER, MEDIA_KEY;
35601
+ var init_media = __esm({
35602
+ "node_modules/@chrischall/mcp-utils/dist/response/media.js"() {
35603
+ MEDIA_NOUN = "(?:avatar|picture|photo|thumbnail|thumb|image|icon|banner|profile_pic(?:ture)?|logo)";
35604
+ MEDIA_QUALIFIER = "(?:primary|secondary|main|default|cover|hero|profile|master|rendered|small|medium|large|full|original|tall)";
35605
+ MEDIA_KEY = new RegExp(`^(?:(?:${MEDIA_QUALIFIER}|${MEDIA_NOUN})[_-]?)?${MEDIA_NOUN}s?(?:[_-]?(?:link|uri|url|src)s?)?$`, "i");
35606
+ }
35607
+ });
35608
+
35525
35609
  // node_modules/@chrischall/mcp-utils/dist/response/view.js
35526
35610
  function viewParam(honoured, opts = {}) {
35527
35611
  if (honoured.length < 2) {
@@ -35560,6 +35644,7 @@ var VIEWS, DEFAULT_VIEW, BLURB;
35560
35644
  var init_view = __esm({
35561
35645
  "node_modules/@chrischall/mcp-utils/dist/response/view.js"() {
35562
35646
  init_zod();
35647
+ init_media();
35563
35648
  VIEWS = ["compact", "full", "raw"];
35564
35649
  DEFAULT_VIEW = "compact";
35565
35650
  BLURB = {
@@ -35570,16 +35655,6 @@ var init_view = __esm({
35570
35655
  }
35571
35656
  });
35572
35657
 
35573
- // node_modules/@chrischall/mcp-utils/dist/response/media.js
35574
- var MEDIA_NOUN, MEDIA_QUALIFIER, MEDIA_KEY;
35575
- var init_media = __esm({
35576
- "node_modules/@chrischall/mcp-utils/dist/response/media.js"() {
35577
- MEDIA_NOUN = "(?:avatar|picture|photo|thumbnail|thumb|image|icon|banner|profile_pic(?:ture)?|logo)";
35578
- MEDIA_QUALIFIER = "(?:primary|secondary|main|default|cover|hero|profile|master|rendered|small|medium|large|full|original|tall)";
35579
- MEDIA_KEY = new RegExp(`^(?:(?:${MEDIA_QUALIFIER}|${MEDIA_NOUN})[_-]?)?${MEDIA_NOUN}s?(?:[_-]?(?:link|uri|url|src)s?)?$`, "i");
35580
- }
35581
- });
35582
-
35583
35658
  // node_modules/@chrischall/mcp-utils/dist/response/untrusted.js
35584
35659
  function isPlainObject3(value) {
35585
35660
  if (value === null || typeof value !== "object" || Array.isArray(value))
@@ -36651,6 +36726,9 @@ var init_headers = __esm({
36651
36726
  });
36652
36727
 
36653
36728
  // node_modules/@chrischall/mcp-utils/dist/internal/ascii.js
36729
+ function asciiLower(s) {
36730
+ return s.replace(/[A-Z]+/g, (m) => m.toLowerCase());
36731
+ }
36654
36732
  var init_ascii = __esm({
36655
36733
  "node_modules/@chrischall/mcp-utils/dist/internal/ascii.js"() {
36656
36734
  }
@@ -36662,16 +36740,42 @@ var init_code_point = __esm({
36662
36740
  }
36663
36741
  });
36664
36742
 
36743
+ // node_modules/@chrischall/mcp-utils/dist/internal/tags.js
36744
+ var init_tags = __esm({
36745
+ "node_modules/@chrischall/mcp-utils/dist/internal/tags.js"() {
36746
+ }
36747
+ });
36748
+
36665
36749
  // node_modules/@chrischall/mcp-utils/dist/scrape/index.js
36666
36750
  function isCloudflareChallenge(html) {
36667
- return /_cf_chl_opt/.test(html) || /<title[^>]*>\s*Just a moment/i.test(html);
36751
+ return html.includes("_cf_chl_opt") || hasJustAMomentTitle(html);
36752
+ }
36753
+ function hasJustAMomentTitle(html) {
36754
+ const lower2 = asciiLower(html);
36755
+ let i = 0;
36756
+ for (; ; ) {
36757
+ const open2 = lower2.indexOf("<title", i);
36758
+ if (open2 < 0)
36759
+ return false;
36760
+ const gt = lower2.indexOf(">", open2 + 6);
36761
+ if (gt < 0)
36762
+ return false;
36763
+ let j = gt + 1;
36764
+ while (j < lower2.length && /\s/.test(lower2[j]))
36765
+ j++;
36766
+ if (lower2.startsWith(JUST_A_MOMENT, j))
36767
+ return true;
36768
+ i = gt + 1;
36769
+ }
36668
36770
  }
36669
- var NEXT_DATA_MAX_CHARS;
36771
+ var NEXT_DATA_MAX_CHARS, JUST_A_MOMENT;
36670
36772
  var init_scrape = __esm({
36671
36773
  "node_modules/@chrischall/mcp-utils/dist/scrape/index.js"() {
36672
36774
  init_ascii();
36673
36775
  init_code_point();
36776
+ init_tags();
36674
36777
  NEXT_DATA_MAX_CHARS = 16 * 1024 * 1024;
36778
+ JUST_A_MOMENT = "just a moment";
36675
36779
  }
36676
36780
  });
36677
36781
 
@@ -36707,7 +36811,7 @@ var init_net_atoms = __esm({
36707
36811
  // node_modules/@chrischall/mcp-utils/dist/http/body-limit.js
36708
36812
  async function readBytesCapped(res, maxBytes, service, bounded) {
36709
36813
  if (maxBytes !== void 0) {
36710
- const declared = Number(res.headers.get("content-length") ?? NaN);
36814
+ const declared = Number(responseHeader(res, "content-length") ?? NaN);
36711
36815
  if (Number.isFinite(declared) && declared > maxBytes) {
36712
36816
  cancelStream(res.body);
36713
36817
  throw new ResponseTooLargeError(service, maxBytes);
@@ -36752,6 +36856,7 @@ var ResponseTooLargeError;
36752
36856
  var init_body_limit = __esm({
36753
36857
  "node_modules/@chrischall/mcp-utils/dist/http/body-limit.js"() {
36754
36858
  init_errors3();
36859
+ init_headers();
36755
36860
  ResponseTooLargeError = class extends McpToolError {
36756
36861
  maxBytes;
36757
36862
  constructor(service, maxBytes) {
@@ -36811,6 +36916,126 @@ var init_url_safety = __esm({
36811
36916
  }
36812
36917
  });
36813
36918
 
36919
+ // node_modules/@chrischall/mcp-utils/dist/http/transport-failure.js
36920
+ function codeOf(e) {
36921
+ if (e === null || typeof e !== "object")
36922
+ return void 0;
36923
+ const code2 = e.code;
36924
+ return typeof code2 === "string" && code2 !== "" ? code2 : void 0;
36925
+ }
36926
+ function causes(err) {
36927
+ const out = [];
36928
+ let at = err;
36929
+ for (let depth = 0; depth < MAX_CAUSE_DEPTH && at !== void 0 && at !== null; depth++) {
36930
+ out.push(at);
36931
+ if (at instanceof AggregateError)
36932
+ out.push(...at.errors);
36933
+ at = at.cause;
36934
+ }
36935
+ return out;
36936
+ }
36937
+ function transportCauseCode(err) {
36938
+ for (const e of causes(err)) {
36939
+ const code2 = codeOf(e);
36940
+ if (code2 !== void 0)
36941
+ return code2;
36942
+ }
36943
+ return void 0;
36944
+ }
36945
+ function causeMessage(err) {
36946
+ const chain = causes(err).slice(1);
36947
+ for (let i = chain.length - 1; i >= 0; i--) {
36948
+ const e = chain[i];
36949
+ if (e instanceof Error && e.message !== "")
36950
+ return e.message;
36951
+ }
36952
+ return void 0;
36953
+ }
36954
+ function isFetchFailedError(err) {
36955
+ return err instanceof TypeError && err.message === "fetch failed";
36956
+ }
36957
+ function proxyRefusalStatus(err) {
36958
+ for (const e of causes(err)) {
36959
+ if (codeOf(e) === "ERR_PROXY_TUNNEL") {
36960
+ const m = /\b(\d{3})\b/.exec(e instanceof Error ? e.message.slice(0, 500) : "");
36961
+ return m ? Number(m[1]) : "unknown";
36962
+ }
36963
+ if (e instanceof Error) {
36964
+ const m = /^Proxy response \((\d{3})\) !== 200 when HTTP Tunneling/.exec(e.message);
36965
+ if (m)
36966
+ return Number(m[1]);
36967
+ }
36968
+ }
36969
+ return void 0;
36970
+ }
36971
+ function classify(err, code2) {
36972
+ const refused = proxyRefusalStatus(err);
36973
+ if (refused !== void 0) {
36974
+ return { cls: refused === 403 || refused === 407 || refused === "unknown" ? "egress" : "proxy", proxyStatus: refused };
36975
+ }
36976
+ if (code2 !== void 0 && DNS_CODES.has(code2))
36977
+ return { cls: "dns" };
36978
+ if (code2 !== void 0 && CERT_CODES.has(code2))
36979
+ return { cls: "tls" };
36980
+ if (code2 !== void 0 && CONNECT_CODES.has(code2))
36981
+ return { cls: "connect" };
36982
+ return { cls: "other" };
36983
+ }
36984
+ function describeTransportFailure(err, url2, service, opts = {}) {
36985
+ if (!isFetchFailedError(err))
36986
+ return void 0;
36987
+ let host;
36988
+ try {
36989
+ host = new URL(url2).hostname;
36990
+ } catch {
36991
+ host = "(unknown host)";
36992
+ }
36993
+ const code2 = transportCauseCode(err);
36994
+ const detail = [code2, causeMessage(err)].filter((s) => s !== void 0);
36995
+ const why = detail.length > 0 ? ` (${[...new Set(detail)].join(": ")})` : "";
36996
+ const { cls, proxyStatus } = classify(err, code2);
36997
+ const ctx = { host, service, code: code2, ...proxyStatus !== void 0 ? { proxyStatus } : {} };
36998
+ const hint = (opts.hints?.[cls] ?? DEFAULT_HINTS[cls])(host, ctx);
36999
+ return new McpToolError(`Could not reach ${service} at ${host}${why}.`, { kind: "transport", hint, cause: err });
37000
+ }
37001
+ var MAX_CAUSE_DEPTH, DNS_CODES, CONNECT_CODES, CERT_CODES, DEFAULT_HINTS;
37002
+ var init_transport_failure = __esm({
37003
+ "node_modules/@chrischall/mcp-utils/dist/http/transport-failure.js"() {
37004
+ init_errors3();
37005
+ MAX_CAUSE_DEPTH = 5;
37006
+ DNS_CODES = /* @__PURE__ */ new Set(["ENOTFOUND", "EAI_AGAIN", "EAI_FAIL", "EAI_NONAME"]);
37007
+ CONNECT_CODES = /* @__PURE__ */ new Set([
37008
+ "ECONNREFUSED",
37009
+ "ECONNRESET",
37010
+ "ETIMEDOUT",
37011
+ "ENETUNREACH",
37012
+ "EHOSTUNREACH",
37013
+ "EPIPE",
37014
+ "UND_ERR_CONNECT_TIMEOUT",
37015
+ "UND_ERR_SOCKET",
37016
+ "UND_ERR_HEADERS_TIMEOUT"
37017
+ ]);
37018
+ CERT_CODES = /* @__PURE__ */ new Set([
37019
+ "UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
37020
+ "UNABLE_TO_GET_ISSUER_CERT",
37021
+ "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
37022
+ "SELF_SIGNED_CERT_IN_CHAIN",
37023
+ "DEPTH_ZERO_SELF_SIGNED_CERT",
37024
+ "CERT_HAS_EXPIRED",
37025
+ "CERT_NOT_YET_VALID",
37026
+ "ERR_TLS_CERT_ALTNAME_INVALID"
37027
+ ]);
37028
+ DEFAULT_HINTS = {
37029
+ egress: (host) => `The outbound proxy this server runs behind refused the connection to ${host}: its egress policy does not allow that host. On a hosted (mcp-host) registration, the host must be on the registration's egress allow-list \u2014 the owner accepts the manifest's egress proposal (or adds the host) on the registration, then retries. Elsewhere, ask whoever runs the proxy to allow it.`,
37030
+ proxy: (host, ctx) => `The outbound proxy this server runs behind could not open a connection to ${host} (it answered ${String(ctx.proxyStatus)}). Retry; if it persists, check the proxy can reach the host.`,
37031
+ dns: (host) => `This machine could not resolve ${host}. Check DNS and VPN: a split-tunnel VPN that resolves some of a service's hosts but not others fails exactly like this. \`nslookup ${host}\` on the machine running this server tells you which.`,
37032
+ tls: (host) => `Node did not trust the TLS certificate ${host} presented. On a corporate network this is usually TLS inspection: export your proxy's root CA as PEM and start this server with NODE_EXTRA_CA_CERTS=/path/to/ca.pem (Node does not read the system keychain). If the certificate is expired or not yet valid, also check the machine's clock.`,
37033
+ connect: (host, ctx) => `The connection to ${host} was refused, reset or timed out before ${ctx.service} answered \u2014 usually a firewall, or a network that only lets traffic out through a proxy. Node's fetch ignores proxy settings unless told: start this server with HTTPS_PROXY=<your proxy URL> and NODE_USE_ENV_PROXY=1 (Node 22.21+ / 24.5+), or allow the host through the firewall.`,
37034
+ other: (host) => `No response from ${host}. Check this machine can reach it (VPN, proxy, firewall); \`curl -sI https://${host}/\` from the same machine is a quick test.`
37035
+ };
37036
+ }
37037
+ });
37038
+
36814
37039
  // node_modules/@chrischall/mcp-utils/dist/internal/retry-after.js
36815
37040
  function retryAfterToMs(header, now = Date.now()) {
36816
37041
  if (header == null)
@@ -36831,6 +37056,27 @@ var init_retry_after = __esm({
36831
37056
  });
36832
37057
 
36833
37058
  // node_modules/@chrischall/mcp-utils/dist/http/index.js
37059
+ function withoutCredentials(headers, tokenHeader) {
37060
+ const drop = new Set(CROSS_ORIGIN_DROPPED_HEADERS);
37061
+ if (tokenHeader !== void 0)
37062
+ drop.add(tokenHeader.toLowerCase());
37063
+ const out = {};
37064
+ for (const [k, v] of Object.entries(headers))
37065
+ if (!drop.has(k.toLowerCase()))
37066
+ out[k] = v;
37067
+ return out;
37068
+ }
37069
+ function joinQuery(path, query) {
37070
+ if (!query)
37071
+ return path;
37072
+ const hashAt = path.indexOf("#");
37073
+ const head = hashAt < 0 ? path : path.slice(0, hashAt);
37074
+ const fragment = hashAt < 0 ? "" : path.slice(hashAt);
37075
+ if (!head.includes("?"))
37076
+ return `${head}${query}${fragment}`;
37077
+ const sep = head.endsWith("?") || head.endsWith("&") ? "" : "&";
37078
+ return `${head}${sep}${query.slice(1)}${fragment}`;
37079
+ }
36834
37080
  function cancelBody(res) {
36835
37081
  try {
36836
37082
  const body = res.body;
@@ -36839,6 +37085,27 @@ function cancelBody(res) {
36839
37085
  } catch {
36840
37086
  }
36841
37087
  }
37088
+ function writeOutcomeStatus(err) {
37089
+ const status = err.cause?.status;
37090
+ return typeof status === "number" ? status : void 0;
37091
+ }
37092
+ function unknownOutcomeReason(err, opts = {}) {
37093
+ if (err.timedOut)
37094
+ return "it did not answer in time";
37095
+ const status = writeOutcomeStatus(err);
37096
+ if (status !== void 0)
37097
+ return `it answered HTTP ${status} after ${opts.sent ?? "the request"} was sent`;
37098
+ return "the connection dropped";
37099
+ }
37100
+ async function writeOrUnknown(run, onUnknown) {
37101
+ try {
37102
+ return { ok: true, value: await run() };
37103
+ } catch (err) {
37104
+ if (!(err instanceof WriteOutcomeUnknownError))
37105
+ throw err;
37106
+ return { ok: false, result: await onUnknown(err, unknownOutcomeReason(err)) };
37107
+ }
37108
+ }
36842
37109
  function headerOf(headers, name) {
36843
37110
  if (headers === void 0)
36844
37111
  return void 0;
@@ -36959,7 +37226,9 @@ function createApiClient(opts) {
36959
37226
  const retryStatuses = retry.statuses ?? [429];
36960
37227
  const timeoutMs = opts.timeout === false ? 0 : opts.timeout ?? DEFAULT_REQUEST_TIMEOUT_MS;
36961
37228
  const redirectMode = opts.redirect;
36962
- const maxRedirects = opts.maxRedirects ?? DEFAULT_MAX_REDIRECTS;
37229
+ const stripCrossOrigin = redirectMode === void 0 && opts.tokenHeader !== void 0;
37230
+ const manualRedirects = redirectMode === "same-origin" || stripCrossOrigin;
37231
+ const maxRedirects = opts.maxRedirects ?? (stripCrossOrigin ? FETCH_MAX_REDIRECTS : DEFAULT_MAX_REDIRECTS);
36963
37232
  const reportWriteOutcome = opts.writeOutcomeUnknown !== false;
36964
37233
  const defaultMaxBytes = checkMaxBytes("maxResponseBytes", opts.maxResponseBytes);
36965
37234
  if (!Number.isSafeInteger(maxRedirects) || maxRedirects < 0) {
@@ -37061,7 +37330,7 @@ function createApiClient(opts) {
37061
37330
  `Refusing request to ${service}: path ${JSON.stringify(path.split(/[?#]/, 1)[0])} contains a ${hazard}, which URL normalisation would rewrite. Build paths with apiPath\`...\` so each value is one encoded segment.`
37062
37331
  );
37063
37332
  }
37064
- const url2 = `${base}${path}${query}`;
37333
+ const url2 = `${base}${joinQuery(path, query)}`;
37065
37334
  if (baseOrigin === void 0)
37066
37335
  return url2;
37067
37336
  let parsed;
@@ -37075,14 +37344,21 @@ function createApiClient(opts) {
37075
37344
  }
37076
37345
  return url2;
37077
37346
  }
37078
- function sameOriginHop(location, from) {
37079
- let next;
37347
+ function resolveLocation(location, from) {
37080
37348
  try {
37081
- next = new URL(location, from);
37349
+ return new URL(location, from).href;
37082
37350
  } catch {
37083
37351
  throw new RedirectRefusedError(`Refusing redirect from ${service}: the Location header is not a valid URL.`);
37084
37352
  }
37085
- if (baseOrigin === void 0 || next.origin !== baseOrigin || next.username !== baseParts?.username || next.password !== baseParts?.password) {
37353
+ }
37354
+ function onBaseOrigin(href) {
37355
+ const next = new URL(href);
37356
+ return baseOrigin !== void 0 && next.origin === baseOrigin && next.username === baseParts?.username && next.password === baseParts?.password;
37357
+ }
37358
+ function sameOriginHop(location, from) {
37359
+ const href = resolveLocation(location, from);
37360
+ const next = new URL(href);
37361
+ if (!onBaseOrigin(href)) {
37086
37362
  throw new RedirectRefusedError(`Refusing redirect from ${service} to ${next.origin}: it leaves the base origin ${baseOrigin ?? "(unparseable)"}, and following it would re-send the credential there.`);
37087
37363
  }
37088
37364
  return next.href;
@@ -37092,20 +37368,21 @@ function createApiClient(opts) {
37092
37368
  const query = opt.query ? buildQueryString(opt.query) : "";
37093
37369
  const url2 = resolveUrl(path, query);
37094
37370
  const bodyInit = reqBody !== void 0 ? { body: reqBody } : {};
37095
- const fetchHop = (hopUrl, hopMethod, withBody, token, signal) => {
37371
+ const fetchHop = (hopUrl, hopMethod, withBody, token, signal, credentialed = true) => {
37096
37372
  if (!signal?.aborted)
37097
37373
  dispatch.sent = true;
37374
+ const headers = {
37375
+ Accept: "application/json",
37376
+ ...withBody && contentType !== void 0 ? { "Content-Type": contentType } : {},
37377
+ ...typeof opts.baseHeaders === "function" ? opts.baseHeaders() : opts.baseHeaders,
37378
+ ...authHeader(token || void 0),
37379
+ ...opt.headers
37380
+ };
37098
37381
  return doFetch(hopUrl, {
37099
37382
  method: hopMethod,
37100
- headers: {
37101
- Accept: "application/json",
37102
- ...withBody && contentType !== void 0 ? { "Content-Type": contentType } : {},
37103
- ...opts.baseHeaders,
37104
- ...authHeader(token || void 0),
37105
- ...opt.headers
37106
- },
37383
+ headers: credentialed ? headers : withoutCredentials(headers, opts.tokenHeader),
37107
37384
  ...signal ? { signal } : {},
37108
- ...redirectMode !== void 0 ? { redirect: redirectMode === "same-origin" ? "manual" : redirectMode } : {},
37385
+ ...manualRedirects ? { redirect: "manual" } : redirectMode !== void 0 ? { redirect: redirectMode } : {},
37109
37386
  ...withBody ? bodyInit : {}
37110
37387
  }).catch((err) => {
37111
37388
  markTransportFailure(err);
@@ -37113,13 +37390,14 @@ function createApiClient(opts) {
37113
37390
  });
37114
37391
  };
37115
37392
  const fetchWith = async (token, signal) => {
37116
- if (redirectMode !== "same-origin")
37393
+ if (!manualRedirects)
37117
37394
  return fetchHop(url2, method, true, token, signal);
37118
37395
  let hopUrl = url2;
37119
37396
  let hopMethod = method;
37120
37397
  let withBody = true;
37398
+ let credentialed = true;
37121
37399
  for (let hops = 0; ; hops += 1) {
37122
- const res = await fetchHop(hopUrl, hopMethod, withBody, token, signal);
37400
+ const res = await fetchHop(hopUrl, hopMethod, withBody, token, signal, credentialed);
37123
37401
  const location = REDIRECT_STATUSES.has(res.status) ? responseHeader(res, "location") : void 0;
37124
37402
  if (location === void 0 || location === null)
37125
37403
  return res;
@@ -37127,7 +37405,13 @@ function createApiClient(opts) {
37127
37405
  if (hops >= maxRedirects) {
37128
37406
  throw new RedirectRefusedError(`Refusing request to ${service}: too many redirects (more than ${maxRedirects}).`);
37129
37407
  }
37130
- hopUrl = sameOriginHop(location, hopUrl);
37408
+ if (stripCrossOrigin) {
37409
+ hopUrl = resolveLocation(location, hopUrl);
37410
+ if (credentialed && !onBaseOrigin(hopUrl))
37411
+ credentialed = false;
37412
+ } else {
37413
+ hopUrl = sameOriginHop(location, hopUrl);
37414
+ }
37131
37415
  if (res.status === 303 || (res.status === 301 || res.status === 302) && hopMethod === "POST") {
37132
37416
  if (hopMethod !== "HEAD")
37133
37417
  hopMethod = "GET";
@@ -37204,24 +37488,62 @@ function createApiClient(opts) {
37204
37488
  throw err;
37205
37489
  }
37206
37490
  }
37207
- const fetchJson = (method, path, opt = {}) => guardWrite(method, opt, (dispatch) => fetchJsonOnce(method, path, opt, dispatch));
37208
- const fetchHtml = (method, path, opt = {}) => guardWrite(method, opt, (dispatch) => fetchHtmlOnce(method, path, opt, dispatch));
37209
- const fetchRaw = (method, path, opt = {}) => guardWrite(method, opt, (dispatch) => fetchRawOnce(method, path, opt, dispatch));
37491
+ function withIdempotency(opt) {
37492
+ const name = opts.idempotencyHeader;
37493
+ if (name === void 0 || name === "")
37494
+ return opt;
37495
+ const lower2 = name.toLowerCase();
37496
+ if (opt.headers && Object.keys(opt.headers).some((k) => k.toLowerCase() === lower2))
37497
+ return opt;
37498
+ const key = opt.idempotencyKey ?? globalThis.crypto.randomUUID();
37499
+ return { ...opt, headers: { ...opt.headers, [name]: key } };
37500
+ }
37501
+ async function errorFor(res, text, method, path, retried) {
37502
+ const edge = detectEdgeBlock({ body: text, headers: res.headers, status: res.status });
37503
+ if (edge)
37504
+ return new EdgeBlockedError(res.status, edge.vendor, { service, method, path });
37505
+ if (opts.onErrorResponse) {
37506
+ const decision = await opts.onErrorResponse({ status: res.status, method, path, headers: res.headers, body: text, retried });
37507
+ if (decision instanceof Error)
37508
+ return decision;
37509
+ if (decision === "retry" && !retried)
37510
+ return "retry";
37511
+ }
37512
+ return httpError(res, text, method, path, service);
37513
+ }
37514
+ const fetchJson = (method, path, opt = {}) => {
37515
+ const o = withIdempotency(opt);
37516
+ return guardWrite(method, o, (dispatch) => fetchJsonOnce(method, path, o, dispatch));
37517
+ };
37518
+ const fetchHtml = (method, path, opt = {}) => {
37519
+ const o = withIdempotency(opt);
37520
+ return guardWrite(method, o, (dispatch) => fetchHtmlOnce(method, path, o, dispatch));
37521
+ };
37522
+ const fetchRaw = (method, path, opt = {}) => {
37523
+ const o = withIdempotency(opt);
37524
+ return guardWrite(method, o, (dispatch) => fetchRawOnce(method, path, o, dispatch));
37525
+ };
37210
37526
  async function fetchJsonOnce(method, path, opt, dispatch) {
37211
37527
  const maxBytes = capOf(opt);
37212
- const attempt = await send(method, path, opt, dispatch);
37213
- const res = attempt.res;
37214
- if (res.status === 401)
37215
- throw await unauthorizedOrEdge(attempt, method, path, maxBytes);
37216
- if (res.status === 429)
37217
- throw await rateLimitedError(attempt, method, path, maxBytes);
37218
- if (res.status === 204) {
37219
- discard(attempt);
37220
- return parseJsonBody("", { expect: opt.expect, service, method, path, status: 204 });
37221
- }
37222
- if (!res.ok) {
37223
- throw httpError(res, await readErrorText(attempt, maxBytes), method, path, service);
37528
+ let attempt;
37529
+ for (let retried = false; ; retried = true) {
37530
+ attempt = await send(method, path, opt, dispatch);
37531
+ const res2 = attempt.res;
37532
+ if (res2.status === 401)
37533
+ throw await unauthorizedOrEdge(attempt, method, path, maxBytes);
37534
+ if (res2.status === 429)
37535
+ throw await rateLimitedError(attempt, method, path, maxBytes);
37536
+ if (res2.status === 204) {
37537
+ discard(attempt);
37538
+ return parseJsonBody("", { expect: opt.expect, service, method, path, status: 204 });
37539
+ }
37540
+ if (res2.ok)
37541
+ break;
37542
+ const failure2 = await errorFor(res2, await readErrorText(attempt, maxBytes), method, path, retried);
37543
+ if (failure2 !== "retry")
37544
+ throw failure2;
37224
37545
  }
37546
+ const res = attempt.res;
37225
37547
  const text = await readText(attempt, maxBytes);
37226
37548
  return parseJsonBody(text, {
37227
37549
  expect: opt.expect,
@@ -37235,34 +37557,43 @@ function createApiClient(opts) {
37235
37557
  async function fetchHtmlOnce(method, path, opt, dispatch) {
37236
37558
  const maxBytes = capOf(opt);
37237
37559
  const headers = { Accept: "text/html,*/*", ...opt.headers };
37238
- const attempt = await send(method, path, { ...opt, headers }, dispatch);
37239
- const res = attempt.res;
37240
- if (res.status === 401)
37241
- throw await unauthorizedOrEdge(attempt, method, path, maxBytes);
37242
- if (res.status === 429)
37243
- throw await rateLimitedError(attempt, method, path, maxBytes);
37244
- if (!res.ok) {
37245
- throw httpError(res, await readErrorText(attempt, maxBytes), method, path, service);
37560
+ for (let retried = false; ; retried = true) {
37561
+ const attempt = await send(method, path, { ...opt, headers }, dispatch);
37562
+ const res = attempt.res;
37563
+ if (res.status === 401)
37564
+ throw await unauthorizedOrEdge(attempt, method, path, maxBytes);
37565
+ if (res.status === 429)
37566
+ throw await rateLimitedError(attempt, method, path, maxBytes);
37567
+ if (res.ok)
37568
+ return readText(attempt, maxBytes);
37569
+ const failure2 = await errorFor(res, await readErrorText(attempt, maxBytes), method, path, retried);
37570
+ if (failure2 !== "retry")
37571
+ throw failure2;
37246
37572
  }
37247
- return readText(attempt, maxBytes);
37248
37573
  }
37249
37574
  async function fetchRawOnce(method, path, opt, dispatch) {
37250
37575
  const maxBytes = capOf(opt);
37251
37576
  const headers = { Accept: "*/*", ...opt.headers };
37252
- const attempt = await send(method, path, { ...opt, headers }, dispatch);
37253
- const res = attempt.res;
37254
- if (res.status === 401)
37255
- throw await unauthorizedOrEdge(attempt, method, path, maxBytes);
37256
- if (res.status === 429)
37257
- throw await rateLimitedError(attempt, method, path, maxBytes);
37258
- if (!res.ok) {
37577
+ let attempt;
37578
+ for (let retried = false; ; retried = true) {
37579
+ attempt = await send(method, path, { ...opt, headers }, dispatch);
37580
+ const res2 = attempt.res;
37581
+ if (res2.status === 401)
37582
+ throw await unauthorizedOrEdge(attempt, method, path, maxBytes);
37583
+ if (res2.status === 429)
37584
+ throw await rateLimitedError(attempt, method, path, maxBytes);
37585
+ if (res2.ok)
37586
+ break;
37259
37587
  const text = await readText(attempt, maxBytes).catch((err) => {
37260
37588
  if (err instanceof RequestTimeoutError)
37261
37589
  throw err;
37262
37590
  return "";
37263
37591
  });
37264
- throw httpError(res, text, method, path, service);
37592
+ const failure2 = await errorFor(res2, text, method, path, retried);
37593
+ if (failure2 !== "retry")
37594
+ throw failure2;
37265
37595
  }
37596
+ const res = attempt.res;
37266
37597
  const bytes = await readBytes(attempt, maxBytes);
37267
37598
  return {
37268
37599
  status: res.status,
@@ -37331,7 +37662,7 @@ function decodeJwtExp(token) {
37331
37662
  }
37332
37663
  return exp;
37333
37664
  }
37334
- var DEFAULT_RETRY, defaultSleep, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_MAX_REDIRECTS, REDIRECT_STATUSES, UnauthorizedError, RateLimitedError, RequestTimeoutError, DEFAULT_WRITE_OUTCOME_HINT, WriteOutcomeUnknownError, SAFE_METHODS, ApiError, EdgeBlockedError, EDGE_BLOCK_SCAN_MAX, EDGE_BLOCK_MARKERS;
37665
+ var DEFAULT_RETRY, defaultSleep, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_MAX_REDIRECTS, FETCH_MAX_REDIRECTS, CROSS_ORIGIN_DROPPED_HEADERS, REDIRECT_STATUSES, UnauthorizedError, RateLimitedError, RequestTimeoutError, DEFAULT_WRITE_OUTCOME_HINT, WriteOutcomeUnknownError, SAFE_METHODS, ApiError, EdgeBlockedError, EDGE_BLOCK_SCAN_MAX, EDGE_BLOCK_MARKERS;
37335
37666
  var init_http = __esm({
37336
37667
  "node_modules/@chrischall/mcp-utils/dist/http/index.js"() {
37337
37668
  init_headers();
@@ -37343,6 +37674,7 @@ var init_http = __esm({
37343
37674
  init_net_atoms();
37344
37675
  init_fetch_bounded();
37345
37676
  init_url_safety();
37677
+ init_transport_failure();
37346
37678
  init_body_limit();
37347
37679
  init_net_atoms();
37348
37680
  init_url_safety();
@@ -37351,6 +37683,8 @@ var init_http = __esm({
37351
37683
  defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
37352
37684
  DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
37353
37685
  DEFAULT_MAX_REDIRECTS = 5;
37686
+ FETCH_MAX_REDIRECTS = 20;
37687
+ CROSS_ORIGIN_DROPPED_HEADERS = ["authorization", "cookie", "proxy-authorization"];
37354
37688
  REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
37355
37689
  UnauthorizedError = class extends Error {
37356
37690
  status = 401;
@@ -37588,7 +37922,7 @@ var init_version = __esm({
37588
37922
  "src/version.ts"() {
37589
37923
  "use strict";
37590
37924
  PACKAGE_NAME = "office-outlook-mcp";
37591
- VERSION = "0.6.0";
37925
+ VERSION = "0.6.2";
37592
37926
  }
37593
37927
  });
37594
37928
 
@@ -38143,6 +38477,21 @@ var init_public_suffix = __esm({
38143
38477
  }
38144
38478
  });
38145
38479
 
38480
+ // node_modules/@fetchproxy/protocol/dist/ip-literal.js
38481
+ function isIpLiteralHost(host) {
38482
+ if (host.includes(":") || host.startsWith("["))
38483
+ return true;
38484
+ const labels = host.split(".");
38485
+ if (labels.length > 1 && labels[labels.length - 1] === "")
38486
+ labels.pop();
38487
+ const last = labels[labels.length - 1] ?? "";
38488
+ return /^[0-9]+$/.test(last) || /^0x[0-9a-f]*$/i.test(last);
38489
+ }
38490
+ var init_ip_literal = __esm({
38491
+ "node_modules/@fetchproxy/protocol/dist/ip-literal.js"() {
38492
+ }
38493
+ });
38494
+
38146
38495
  // node_modules/@fetchproxy/protocol/dist/validate.js
38147
38496
  function assertObject(x, label) {
38148
38497
  if (typeof x !== "object" || x === null || Array.isArray(x)) {
@@ -38592,6 +38941,9 @@ function validateHello(raw) {
38592
38941
  if (!HOSTNAME_RE.test(d)) {
38593
38942
  throw new ProtocolError3(`hello.domains: invalid hostname ${JSON.stringify(d)}`);
38594
38943
  }
38944
+ if (isIpLiteralHost(d)) {
38945
+ throw new ProtocolError3(`hello.domains: ${JSON.stringify(d)} is an IP address \u2014 declare the site's DNS hostname; IP-literal origins (LAN, loopback, link-local) are not reachable through the bridge`);
38946
+ }
38595
38947
  if (isPublicSuffix(d)) {
38596
38948
  throw new ProtocolError3(`hello.domains: ${JSON.stringify(d)} is a public suffix \u2014 declare a registrable domain (e.g. "example.${d}"), not the suffix every site under it shares`);
38597
38949
  }
@@ -39365,6 +39717,7 @@ var init_validate = __esm({
39365
39717
  init_account_attest();
39366
39718
  init_json_pointer();
39367
39719
  init_public_suffix();
39720
+ init_ip_literal();
39368
39721
  ProtocolError3 = class extends Error {
39369
39722
  constructor(message) {
39370
39723
  super(message);
@@ -39544,6 +39897,7 @@ var init_dist3 = __esm({
39544
39897
  init_seal();
39545
39898
  init_encoding();
39546
39899
  init_public_suffix();
39900
+ init_ip_literal();
39547
39901
  init_json_pointer();
39548
39902
  }
39549
39903
  });
@@ -44514,7 +44868,7 @@ async function startHost(opts) {
44514
44868
  if (ownRetryableRefusal)
44515
44869
  resetSessionPromise();
44516
44870
  resolveOwnSession(ownSession);
44517
- } else {
44871
+ } else if (identified === "extension" && extensionWs === ws) {
44518
44872
  const slot = peers.get(frame.mcpId);
44519
44873
  if (slot)
44520
44874
  slot.ws.send(JSON.stringify(frame));
@@ -45602,6 +45956,9 @@ var init_ws_server = __esm({
45602
45956
  throw new Error("FetchproxyServer: opts.domains must be a non-empty array of hostnames");
45603
45957
  }
45604
45958
  for (const d of opts.domains) {
45959
+ if (typeof d === "string" && isIpLiteralHost(d)) {
45960
+ throw new Error(`FetchproxyServer: opts.domains entry ${JSON.stringify(d)} is an IP address \u2014 declare the site's DNS hostname; IP-literal origins (LAN, loopback, link-local) are not reachable through the bridge`);
45961
+ }
45605
45962
  if (typeof d === "string" && isPublicSuffix(d)) {
45606
45963
  throw new Error(`FetchproxyServer: opts.domains entry ${JSON.stringify(d)} is a public suffix \u2014 declare a registrable domain (e.g. "example.${d}"), not the suffix every site under it shares`);
45607
45964
  }
@@ -47547,8 +47904,10 @@ var init_bulk = __esm({
47547
47904
  });
47548
47905
 
47549
47906
  // node_modules/@fetchproxy/server/dist/bot-wall.js
47907
+ var CLOUDFLARE_TITLE_SCAN_MAX;
47550
47908
  var init_bot_wall = __esm({
47551
47909
  "node_modules/@fetchproxy/server/dist/bot-wall.js"() {
47910
+ CLOUDFLARE_TITLE_SCAN_MAX = 64 * 1024;
47552
47911
  }
47553
47912
  });
47554
47913
 
@@ -47909,6 +48268,71 @@ function createBootstrapOpts(args) {
47909
48268
  ...nonEmpty(b.domSelectors) ? { domSelectors: b.domSelectors } : {}
47910
48269
  };
47911
48270
  }
48271
+ function stripBearer(raw) {
48272
+ const m = /^\s*Bearer\s+(.+)$/i.exec(raw);
48273
+ return (m?.[1] ?? raw).trim();
48274
+ }
48275
+ async function raceCaptures(attempts, windowMs) {
48276
+ for (const a of attempts)
48277
+ a.catch(() => {
48278
+ });
48279
+ let timer;
48280
+ const expiry = new Promise((_resolve, reject) => {
48281
+ timer = setTimeout(() => reject(new Error(`timed out after ${Math.round(windowMs / 1e3)}s waiting for the page to make a request we could read`)), windowMs);
48282
+ });
48283
+ try {
48284
+ return await Promise.race([
48285
+ Promise.any(attempts).catch((e) => {
48286
+ throw new Error(e instanceof AggregateError ? e.errors.map((x) => messageOf(x)).join("; ") : messageOf(e));
48287
+ }),
48288
+ expiry
48289
+ ]);
48290
+ } finally {
48291
+ clearTimeout(timer);
48292
+ }
48293
+ }
48294
+ async function captureBearerHeader(opts) {
48295
+ if (opts.captures.length === 0)
48296
+ throw new Error("captureBearerHeader: at least one capture declaration is required.");
48297
+ const minLength = opts.minLength ?? 100;
48298
+ const normalise = opts.normalise ?? stripBearer;
48299
+ const domains = opts.domains ?? [...new Set(opts.captures.map((c) => c.host))];
48300
+ const build2 = opts.createTransport ?? ((o) => createFetchproxyTransport(o));
48301
+ const transport = build2({
48302
+ ...opts.transportOptions,
48303
+ ...createBootstrapOpts({ domains: [...domains], bootstrap: { captureHeaders: opts.captures.map((c) => ({ ...c })) } }),
48304
+ ...opts.port !== void 0 ? { port: opts.port } : {},
48305
+ // Both: the explicit deadline for every bridge version, and the declared
48306
+ // window so the transport's own derivation agrees with it.
48307
+ fetchTimeoutMs: deadlineForCaptureWindow(void 0, opts.windowMs) ?? SERVER_DEFAULT_FETCH_TIMEOUT_MS,
48308
+ captureWindowMs: opts.windowMs,
48309
+ serverName: opts.serverName,
48310
+ version: opts.version
48311
+ });
48312
+ const one = async (decl) => {
48313
+ const raw = await transport.server.captureRequestHeader({ ...decl, timeoutMs: opts.windowMs });
48314
+ if (typeof raw !== "string")
48315
+ throw new Error(`${decl.host}: capture returned no header`);
48316
+ const token = normalise(raw);
48317
+ if (token.length < minLength) {
48318
+ throw new Error(`${decl.host}: ${decl.headerName} header was present but too short to be a token (${token.length} chars)`);
48319
+ }
48320
+ return token;
48321
+ };
48322
+ try {
48323
+ await transport.start();
48324
+ try {
48325
+ return opts.captures.length === 1 ? await one(opts.captures[0]) : await raceCaptures(opts.captures.map(one), opts.windowMs);
48326
+ } catch (e) {
48327
+ throw new Error(`could not capture ${opts.what} from the browser (${messageOf(e)}). ${opts.guidance}`);
48328
+ }
48329
+ } finally {
48330
+ try {
48331
+ await transport.close();
48332
+ } catch {
48333
+ }
48334
+ }
48335
+ }
47912
48336
  var PLACEHOLDER_RE2, SERVER_DEFAULT_FETCH_TIMEOUT_MS, CAPTURE_DEADLINE_MARGIN_MS;
47913
48337
  var init_fetchproxy = __esm({
47914
48338
  "node_modules/@chrischall/mcp-utils/dist/fetchproxy/index.js"() {
@@ -47937,97 +48361,26 @@ __export(auth_fetchproxy_exports, {
47937
48361
  function getWsPort() {
47938
48362
  return readPortEnv("OUTLOOK_WS_PORT", DEFAULT_WS_PORT);
47939
48363
  }
47940
- function withFetch(opts) {
47941
- const derived2 = opts.capabilities ?? [];
47942
- return {
47943
- ...opts,
47944
- capabilities: derived2.includes("fetch") ? derived2 : [...derived2, "fetch"]
47945
- };
47946
- }
47947
- async function raceCaptures(attempts, windowMs) {
47948
- for (const a of attempts) a.catch(() => {
47949
- });
47950
- let timer;
47951
- const expiry = new Promise((_, reject) => {
47952
- timer = setTimeout(
47953
- () => reject(
47954
- new Error(
47955
- `timed out after ${Math.round(windowMs / 1e3)}s waiting for the page to make a request we could read`
47956
- )
47957
- ),
47958
- windowMs
47959
- );
47960
- });
47961
- try {
47962
- return await Promise.race([
47963
- Promise.any(attempts).catch((e) => {
47964
- throw new Error(
47965
- e instanceof AggregateError ? e.errors.map((x) => x.message).join("; ") : e.message
47966
- );
47967
- }),
47968
- expiry
47969
- ]);
47970
- } finally {
47971
- clearTimeout(timer);
47972
- }
47973
- }
47974
- function stripBearer(raw) {
47975
- const m = /^\s*Bearer\s+(.+)$/i.exec(raw);
47976
- return (m ? m[1] : raw).trim();
47977
- }
47978
48364
  async function captureTokenViaFetchproxy() {
47979
- const transport = createFetchproxyTransport({
47980
- ...withFetch(
47981
- createBootstrapOpts({
47982
- domains: [...TRUST_DOMAINS],
47983
- bootstrap: {
47984
- captureHeaders: [{ ...CAPTURE_DECL_CLOUD }, { ...CAPTURE_DECL_OFFICE }]
47985
- }
47986
- })
47987
- ),
48365
+ return captureBearerHeader({
48366
+ captures: [CAPTURE_DECL_CLOUD, CAPTURE_DECL_OFFICE],
48367
+ domains: TRUST_DOMAINS,
48368
+ windowMs: CAPTURE_TIMEOUT_MS,
47988
48369
  port: getWsPort(),
47989
- fetchTimeoutMs: BRIDGE_DEADLINE_MS,
47990
48370
  serverName: PACKAGE_NAME,
47991
- version: VERSION
48371
+ version: VERSION,
48372
+ what: "an Outlook token",
48373
+ guidance: "Open a signed-in Outlook tab (outlook.cloud.microsoft or outlook.office.com), leave it in the foreground for a moment so the page makes a request, and retry."
47992
48374
  });
47993
- try {
47994
- await transport.start();
47995
- const attempts = [CAPTURE_DECL_CLOUD, CAPTURE_DECL_OFFICE].map(
47996
- (decl) => transport.server.captureRequestHeader({ ...decl, timeoutMs: CAPTURE_TIMEOUT_MS }).then((raw) => {
47997
- if (typeof raw !== "string") {
47998
- throw new Error(`${decl.host}: capture returned no header`);
47999
- }
48000
- const token = stripBearer(raw);
48001
- if (token.length < MIN_TOKEN_LENGTH) {
48002
- throw new Error(
48003
- `${decl.host}: authorization header was present but too short to be a token (${token.length} chars)`
48004
- );
48005
- }
48006
- return token;
48007
- })
48008
- );
48009
- try {
48010
- return await raceCaptures(attempts, CAPTURE_TIMEOUT_MS);
48011
- } catch (e) {
48012
- const detail = e.message;
48013
- throw new Error(
48014
- `could not capture an Outlook token from the browser (${detail}). Open a signed-in Outlook tab (outlook.cloud.microsoft or outlook.office.com), leave it in the foreground for a moment so the page makes a request, and retry.`
48015
- );
48016
- }
48017
- } finally {
48018
- try {
48019
- await transport.close();
48020
- } catch {
48021
- }
48022
- }
48023
48375
  }
48024
- var DEFAULT_WS_PORT, CAPTURE_DECL_CLOUD, CAPTURE_DECL_OFFICE, CAPTURE_HOSTS, TRUST_DOMAINS, CAPTURE_TIMEOUT_MS, BRIDGE_DEADLINE_MS, MIN_TOKEN_LENGTH;
48376
+ var DEFAULT_WS_PORT, CAPTURE_DECL_CLOUD, CAPTURE_DECL_OFFICE, CAPTURE_HOSTS, TRUST_DOMAINS, CAPTURE_TIMEOUT_MS;
48025
48377
  var init_auth_fetchproxy = __esm({
48026
48378
  "src/auth-fetchproxy.ts"() {
48027
48379
  "use strict";
48028
48380
  init_fetchproxy();
48029
48381
  init_dist2();
48030
48382
  init_version();
48383
+ init_fetchproxy();
48031
48384
  DEFAULT_WS_PORT = 37149;
48032
48385
  CAPTURE_DECL_CLOUD = {
48033
48386
  host: "outlook.cloud.microsoft",
@@ -48042,8 +48395,6 @@ var init_auth_fetchproxy = __esm({
48042
48395
  CAPTURE_HOSTS = [CAPTURE_DECL_CLOUD.host, CAPTURE_DECL_OFFICE.host];
48043
48396
  TRUST_DOMAINS = [...CAPTURE_HOSTS];
48044
48397
  CAPTURE_TIMEOUT_MS = readTtlMsEnv("OUTLOOK_CAPTURE_TIMEOUT", 3e4);
48045
- BRIDGE_DEADLINE_MS = Math.max(CAPTURE_TIMEOUT_MS + 15e3, 3e4);
48046
- MIN_TOKEN_LENGTH = 100;
48047
48398
  }
48048
48399
  });
48049
48400
 
@@ -48625,6 +48976,46 @@ var TokenManager = class {
48625
48976
  return res;
48626
48977
  }
48627
48978
  };
48979
+ var RECAPTURE_SENTINEL = "fetchproxy:recapture";
48980
+ var UNKNOWN_CAPTURED_TOKEN_TTL_MS = 10 * 60 * 1e3;
48981
+ function capturedBearerTokens(accessToken, opts = {}) {
48982
+ const now = opts.now ?? Date.now;
48983
+ let expiresAt;
48984
+ try {
48985
+ expiresAt = decodeJwtExp(accessToken) * 1e3;
48986
+ } catch {
48987
+ expiresAt = now() + (opts.unknownTtlMs ?? UNKNOWN_CAPTURED_TOKEN_TTL_MS);
48988
+ }
48989
+ return { accessToken, refreshToken: RECAPTURE_SENTINEL, expiresAt };
48990
+ }
48991
+ function createCapturedTokenManager(opts) {
48992
+ const { direct } = opts;
48993
+ const wrap = (token) => capturedBearerTokens(token, {
48994
+ ...opts.unknownTtlMs !== void 0 ? { unknownTtlMs: opts.unknownTtlMs } : {},
48995
+ ...opts.now !== void 0 ? { now: opts.now } : {}
48996
+ });
48997
+ const persistence = direct === void 0 ? opts.persistence ?? void 0 : void 0;
48998
+ return new TokenManager({
48999
+ initial: async () => wrap(direct ?? await opts.capture()),
49000
+ refresh: async () => {
49001
+ if (direct !== void 0) {
49002
+ throw opts.onDirectRejected ? opts.onDirectRejected() : new McpToolError("The directly supplied access token was rejected (401).", {
49003
+ status: 401,
49004
+ kind: "credential_rejected",
49005
+ hint: "A directly supplied token cannot be refreshed automatically. Replace it with a current one, or unset it to capture from a signed-in browser tab instead."
49006
+ });
49007
+ }
49008
+ return wrap(await opts.capture());
49009
+ },
49010
+ ...persistence !== void 0 ? { persistence } : {},
49011
+ // Never fatal: the token is in hand and re-capturable.
49012
+ onPersistError: (err) => {
49013
+ opts.onPersistError?.(err);
49014
+ },
49015
+ ...opts.skewMs !== void 0 ? { skewMs: opts.skewMs } : {},
49016
+ ...opts.now !== void 0 ? { now: opts.now } : {}
49017
+ });
49018
+ }
48628
49019
 
48629
49020
  // src/token-cache.ts
48630
49021
  init_dist2();
@@ -48636,11 +49027,6 @@ function tokenCachePath(env = process.env) {
48636
49027
  fileName: "token.json"
48637
49028
  });
48638
49029
  }
48639
- function isToken(raw) {
48640
- if (raw === null || typeof raw !== "object") return false;
48641
- const t = raw;
48642
- return typeof t.accessToken === "string" && t.accessToken !== "" && typeof t.expiresAt === "number" && (t.refreshToken === void 0 || typeof t.refreshToken === "string");
48643
- }
48644
49030
  function bindingFor(env) {
48645
49031
  if (readEnvVar("OUTLOOK_ACCESS_TOKEN", { env }) !== void 0) return null;
48646
49032
  if (parseBoolEnv("OUTLOOK_DISABLE_FETCHPROXY", { env })) return null;
@@ -48653,7 +49039,7 @@ function createTokenCache(env = process.env) {
48653
49039
  return createFileStatePersistence({
48654
49040
  filePath: tokenCachePath(env),
48655
49041
  boundTo,
48656
- validate: (raw) => isToken(raw) ? raw : null
49042
+ validate: (raw) => isBearerTokens(raw) ? raw : null
48657
49043
  });
48658
49044
  }
48659
49045
  function reportCacheWriteFailure(err) {
@@ -48669,16 +49055,9 @@ async function captureTokenLazily() {
48669
49055
  return mod.captureTokenViaFetchproxy();
48670
49056
  }
48671
49057
  var DEFAULT_API_BASE = "https://outlook.office.com/api/v2.0";
48672
- var UNKNOWN_TOKEN_TTL_MS = 10 * 60 * 1e3;
48673
- var RECAPTURE_SENTINEL = "fetchproxy:recapture";
48674
- function toBearerTokens(accessToken) {
48675
- let expiresAt;
48676
- try {
48677
- expiresAt = decodeJwtExp(accessToken) * 1e3;
48678
- } catch {
48679
- expiresAt = Date.now() + UNKNOWN_TOKEN_TTL_MS;
48680
- }
48681
- return { accessToken, refreshToken: RECAPTURE_SENTINEL, expiresAt };
49058
+ function urlOf(input2) {
49059
+ if (typeof input2 === "string") return input2;
49060
+ return input2 instanceof URL ? input2.href : input2.url;
48682
49061
  }
48683
49062
  function isCredentialFailure(e) {
48684
49063
  const kind = errorKindOf(e);
@@ -48703,7 +49082,14 @@ var OutlookClient = class {
48703
49082
  constructor(opts = {}) {
48704
49083
  this.#env = opts.env ?? process.env;
48705
49084
  this.#capture = opts.captureToken ?? captureTokenLazily;
48706
- this.#fetchImpl = opts.fetchImpl ?? ((input2, init) => fetch(input2, init));
49085
+ const fetchImpl = opts.fetchImpl ?? ((input2, init) => fetch(input2, init));
49086
+ this.#fetchImpl = async (input2, init) => {
49087
+ try {
49088
+ return await fetchImpl(input2, init);
49089
+ } catch (e) {
49090
+ throw describeTransportFailure(e, urlOf(input2), "Outlook") ?? e;
49091
+ }
49092
+ };
48707
49093
  this.#baseUrl = readEnvVar("OUTLOOK_API_BASE", { env: this.#env }) ?? DEFAULT_API_BASE;
48708
49094
  const direct = readEnvVar("OUTLOOK_ACCESS_TOKEN", { env: this.#env });
48709
49095
  const bridgeOff = parseBoolEnv("OUTLOOK_DISABLE_FETCHPROXY", { env: this.#env });
@@ -48726,51 +49112,17 @@ var OutlookClient = class {
48726
49112
  if (this.#configError) throw this.#configError;
48727
49113
  }
48728
49114
  #tokenManager() {
48729
- if (this.#tokens) return this.#tokens;
48730
49115
  const direct = readEnvVar("OUTLOOK_ACCESS_TOKEN", { env: this.#env });
48731
- const persistence = direct === void 0 ? createTokenCache(this.#env) : null;
48732
- const mint = async () => toBearerTokens(direct ?? await this.#capture());
48733
- this.#tokens = new TokenManager({
48734
- initial: mint,
48735
- // There is no refresh grant — re-capture instead. The sentinel argument
48736
- // is ignored by design.
48737
- //
48738
- // But a token supplied as OUTLOOK_ACCESS_TOKEN must NOT be "refreshed" by
48739
- // reaching for the browser: that silently swaps the credential the
48740
- // operator chose for whichever account a tab happens to be signed into,
48741
- // and on a machine with no bridge it just hangs for the capture window on
48742
- // every 401. Say what is wrong instead.
48743
- refresh: async () => {
48744
- if (direct !== void 0) {
48745
- throw new McpToolError("OUTLOOK_ACCESS_TOKEN was rejected by Outlook.", {
48746
- hint: "A directly-supplied token cannot be refreshed automatically. Replace OUTLOOK_ACCESS_TOKEN with a current one, or unset it to capture from a signed-in Outlook browser tab instead.",
48747
- status: 401,
48748
- kind: "credential_rejected"
48749
- });
48750
- }
48751
- return toBearerTokens(await this.#capture());
48752
- },
48753
- // A failed cache write must not fail the call that triggered it: the
48754
- // token is in hand and re-capturable, so a read-only data dir should cost
48755
- // the next cold start a capture, not this request.
48756
- ...persistence ? {
48757
- persistence: {
48758
- load: () => persistence.load(),
48759
- save: (t) => {
48760
- try {
48761
- persistence.save(t);
48762
- } catch (e) {
48763
- reportCacheWriteFailure(e);
48764
- }
48765
- },
48766
- clear: () => {
48767
- try {
48768
- persistence.clear();
48769
- } catch {
48770
- }
48771
- }
48772
- }
48773
- } : {}
49116
+ this.#tokens ??= createCapturedTokenManager({
49117
+ capture: () => this.#capture(),
49118
+ direct,
49119
+ onDirectRejected: () => new McpToolError("OUTLOOK_ACCESS_TOKEN was rejected by Outlook.", {
49120
+ hint: "A directly-supplied token cannot be refreshed automatically. Replace OUTLOOK_ACCESS_TOKEN with a current one, or unset it to capture from a signed-in Outlook browser tab instead.",
49121
+ status: 401,
49122
+ kind: "credential_rejected"
49123
+ }),
49124
+ persistence: direct === void 0 ? createTokenCache(this.#env) : null,
49125
+ onPersistError: reportCacheWriteFailure
48774
49126
  });
48775
49127
  return this.#tokens;
48776
49128
  }
@@ -49928,6 +50280,9 @@ function messageBody(subject, body, html) {
49928
50280
  }
49929
50281
  var TEAMS = { IsOnlineMeeting: true, OnlineMeetingProvider: "TeamsForBusiness" };
49930
50282
  var teamsMeetingParam = external_exports.boolean().optional().describe("Attach a Microsoft Teams meeting (default true). false leaves it off.");
50283
+ var addTeamsMeetingParam = external_exports.boolean().optional().describe(
50284
+ "true adds a Microsoft Teams meeting to an event that lacks one (default false: the way people join is left as it is)."
50285
+ );
49931
50286
  var EVENT_STATE_SELECT = "Id,Subject,IsOrganizer,Start,End,Location,Attendees,IsOnlineMeeting,OnlineMeetingProvider,OnlineMeeting";
49932
50287
  function attendee(Address, Type) {
49933
50288
  return { Type, EmailAddress: { Address } };
@@ -49949,19 +50304,14 @@ function dateTimeTimeZone2(value, zone) {
49949
50304
  return { DateTime: value, TimeZone: zone };
49950
50305
  }
49951
50306
  var TIMEOUT_SENTENCE = 'If Outlook does not answer in time (or the connection drops) the result has status "unknown": the write may well have gone through, so check before retrying \u2014 never resend blindly.';
49952
- async function writeOrUnknown(run, checkWhere) {
49953
- try {
49954
- return { ok: true, value: await run() };
49955
- } catch (e) {
49956
- if (!(e instanceof WriteOutcomeUnknownError)) throw e;
49957
- return {
49958
- ok: false,
49959
- result: minifiedResult({
49960
- status: "unknown",
49961
- warning: `Outlook did not confirm this write (${e.timedOut ? "it did not answer in time" : "the connection dropped"}), so it may already have happened. Check ${checkWhere} before retrying; do not resend blindly.`
49962
- })
49963
- };
49964
- }
50307
+ function writeOrUnknown2(run, checkWhere) {
50308
+ return writeOrUnknown(
50309
+ run,
50310
+ (e) => minifiedResult({
50311
+ status: "unknown",
50312
+ warning: `Outlook did not confirm this write (${unknownOutcomeReason(e)}), so it may already have happened. Check ${checkWhere} before retrying; do not resend blindly.`
50313
+ })
50314
+ );
49965
50315
  }
49966
50316
  var INVITE_EVENT_SELECT = "Id,Subject,IsOrganizer,IsCancelled,Start,End,Organizer,ResponseStatus";
49967
50317
  var DELETE_EVENT_SELECT = "Id,Subject,IsOrganizer,IsCancelled,ResponseStatus,Type,SeriesMasterId,Start,End,Organizer,Attendees";
@@ -50069,7 +50419,7 @@ function registerWriteTools(server, client2) {
50069
50419
  confirmToken
50070
50420
  });
50071
50421
  if (gate) return gate;
50072
- const sent = await writeOrUnknown(() => client2.write("POST", "/me/sendmail", payload), "Sent Items");
50422
+ const sent = await writeOrUnknown2(() => client2.write("POST", "/me/sendmail", payload), "Sent Items");
50073
50423
  if (!sent.ok) return sent.result;
50074
50424
  return minifiedResult({ sent: true, subject, recipients: recipients || null });
50075
50425
  }
@@ -50077,39 +50427,30 @@ function registerWriteTools(server, client2) {
50077
50427
  server.registerTool(
50078
50428
  "outlook_create_draft",
50079
50429
  {
50080
- description: "Create a draft message in the Drafts folder without sending it. Returns the created draft, which can be reviewed and sent from Outlook. " + CONFIRM_FLOW_SENTENCE,
50081
- annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
50430
+ description: "Create a draft message in the Drafts folder without sending it. Returns the created draft, which can be reviewed and sent from Outlook. It does not ask to confirm: a draft reaches nobody until it is sent from Outlook. Calling it twice leaves two drafts. " + TIMEOUT_SENTENCE,
50431
+ // Self-only and additive, like outlook_reply with draftOnly: nothing is
50432
+ // overwritten or sent. Not idempotent: a repeat creates a second draft.
50433
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
50082
50434
  inputSchema: external_exports.object({
50083
50435
  to: recipientList,
50084
50436
  cc: recipientList,
50085
50437
  subject: external_exports.string().describe("Subject line"),
50086
50438
  body: external_exports.string().describe("Message body"),
50087
- html: external_exports.boolean().optional().describe("Compose the body as HTML (default: plain text)"),
50088
- confirmToken: confirmTokenParam
50439
+ html: external_exports.boolean().optional().describe("Compose the body as HTML (default: plain text)")
50089
50440
  })
50090
50441
  },
50091
- async ({ to, cc, subject, body, html, confirmToken }, ctx) => {
50442
+ async ({ to, cc, subject, body, html }) => {
50092
50443
  const payload = {
50093
50444
  ...messageBody(subject, body, html),
50094
50445
  ToRecipients: toRecipients(to),
50095
50446
  ...cc?.length ? { CcRecipients: toRecipients(cc) } : {}
50096
50447
  };
50097
- const gate = await confirmWrite(ctx, {
50098
- tool: "outlook_create_draft",
50099
- action: "mail.create_draft",
50100
- message: "Review and confirm this draft:",
50101
- summary: `Create draft "${subject}"`,
50102
- // One signed-in mailbox per server process.
50103
- account: void 0,
50104
- request: { method: "POST", path: "/me/messages", body: payload },
50105
- confirmToken
50106
- });
50107
- if (gate) return gate;
50108
- const created = await client2.write(
50109
- "POST",
50110
- "/me/messages",
50111
- payload
50448
+ const create = await writeOrUnknown2(
50449
+ () => client2.write("POST", "/me/messages", payload),
50450
+ "Drafts"
50112
50451
  );
50452
+ if (!create.ok) return create.result;
50453
+ const created = create.value;
50113
50454
  return minifiedResult({ created: true, Id: created?.Id, WebLink: created?.WebLink });
50114
50455
  }
50115
50456
  );
@@ -50300,7 +50641,7 @@ function registerWriteTools(server, client2) {
50300
50641
  confirmToken
50301
50642
  });
50302
50643
  if (gate) return gate;
50303
- const write = await writeOrUnknown(
50644
+ const write = await writeOrUnknown2(
50304
50645
  () => client2.write("POST", "/me/events", payload),
50305
50646
  "the calendar (outlook_list_events)"
50306
50647
  );
@@ -50326,7 +50667,7 @@ function registerWriteTools(server, client2) {
50326
50667
  server.registerTool(
50327
50668
  "outlook_update_event",
50328
50669
  {
50329
- description: "Update a meeting you organize: retitle, move, relocate, edit the description, or add/remove attendees. Moving only `start` keeps the meeting's length. A Microsoft Teams meeting is added if the event lacks one (`teamsMeeting: false` to skip). Attendees are sent an updated invitation. Only the organizer can update a meeting \u2014 an attendee's edit would change their own copy alone, so it is refused. The result is verified by re-reading the event. " + CONFIRM_FLOW_SENTENCE + " " + CONFIRM_INJECTION_RULE,
50670
+ description: "Update a meeting you organize: retitle, move, relocate, edit the description, or add/remove attendees. Moving only `start` keeps the meeting's length. It does not add a Microsoft Teams meeting unless you pass `teamsMeeting: true` (on an event that lacks one). Attendees are sent an updated invitation. Only the organizer can update a meeting \u2014 an attendee's edit would change their own copy alone, so it is refused. The result is verified by re-reading the event. " + CONFIRM_FLOW_SENTENCE + " " + CONFIRM_INJECTION_RULE,
50330
50671
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
50331
50672
  inputSchema: external_exports.object({
50332
50673
  id: external_exports.string().min(1).describe("Event Id from outlook_list_events or outlook_get_event"),
@@ -50339,7 +50680,7 @@ function registerWriteTools(server, client2) {
50339
50680
  addAttendees: recipientList.describe("Required attendees to add"),
50340
50681
  addOptionalAttendees: recipientList.describe("Optional attendees to add"),
50341
50682
  removeAttendees: recipientList.describe("Attendees to remove"),
50342
- teamsMeeting: teamsMeetingParam,
50683
+ teamsMeeting: addTeamsMeetingParam,
50343
50684
  confirmToken: confirmTokenParam
50344
50685
  })
50345
50686
  },
@@ -50392,18 +50733,21 @@ function registerWriteTools(server, client2) {
50392
50733
  }
50393
50734
  payload.Attendees = next;
50394
50735
  }
50395
- const addTeams = args.teamsMeeting !== false && !(current && isTeams(current));
50736
+ const addTeams = args.teamsMeeting === true && !(current && isTeams(current));
50396
50737
  if (addTeams) Object.assign(payload, TEAMS);
50397
50738
  if (Object.keys(payload).length === 0) {
50398
50739
  throw new McpToolError("Nothing to update.", {
50399
- hint: "Pass at least one of subject, start, end, location, body, or attendee changes."
50740
+ hint: "Pass at least one of subject, start, end, location, body, attendee changes, or teamsMeeting: true."
50400
50741
  });
50401
50742
  }
50402
50743
  const gate = await confirmWrite(ctx, {
50403
50744
  tool: "outlook_update_event",
50404
50745
  action: "calendar.update_event",
50405
- message: "Review and confirm this change (attendees are sent an updated invitation):",
50406
- summary: `Update "${current?.Subject ?? args.id}": ${Object.keys(payload).join(", ")}`,
50746
+ message: addTeams ? "Review and confirm this change (attendees are sent an updated invitation). This will also make it a Microsoft Teams meeting: attendees get a Teams join link." : "Review and confirm this change (attendees are sent an updated invitation):",
50747
+ summary: `Update "${current?.Subject ?? args.id}": ${[
50748
+ Object.keys(payload).filter((k) => !(k in TEAMS)).join(", "),
50749
+ addTeams ? "this will also make it a Microsoft Teams meeting" : ""
50750
+ ].filter((part) => part !== "").join("; ")}`,
50407
50751
  // One signed-in mailbox per server process.
50408
50752
  account: void 0,
50409
50753
  target: args.id,
@@ -50518,7 +50862,7 @@ function registerWriteTools(server, client2) {
50518
50862
  });
50519
50863
  if (gate) return gate;
50520
50864
  }
50521
- const write = await writeOrUnknown(
50865
+ const write = await writeOrUnknown2(
50522
50866
  () => client2.write("POST", path, payload),
50523
50867
  "the event's response (outlook_get_event)"
50524
50868
  );
@@ -50601,7 +50945,7 @@ function registerWriteTools(server, client2) {
50601
50945
  });
50602
50946
  if (gate) return gate;
50603
50947
  }
50604
- const write = await writeOrUnknown(
50948
+ const write = await writeOrUnknown2(
50605
50949
  () => client2.write("DELETE", path),
50606
50950
  "the calendar (outlook_get_event)"
50607
50951
  );
@@ -50654,7 +50998,7 @@ function registerWriteTools(server, client2) {
50654
50998
  const base = `/me/messages/${encodeURIComponent(messageId)}`;
50655
50999
  const forwardTo = mode === "forward" ? { ToRecipients: toRecipients(to) } : {};
50656
51000
  if (draftOnly === true) {
50657
- const create = await writeOrUnknown(
51001
+ const create = await writeOrUnknown2(
50658
51002
  () => client2.write(
50659
51003
  "POST",
50660
51004
  `${base}/${draft}`,
@@ -50721,7 +51065,7 @@ function registerWriteTools(server, client2) {
50721
51065
  confirmToken
50722
51066
  });
50723
51067
  if (gate) return gate;
50724
- const sent = await writeOrUnknown(() => client2.write("POST", path, payload), "Sent Items");
51068
+ const sent = await writeOrUnknown2(() => client2.write("POST", path, payload), "Sent Items");
50725
51069
  if (!sent.ok) return sent.result;
50726
51070
  return mailboxUntrusted({
50727
51071
  sent: true,