@chrischall/office-outlook-mcp 0.10.0 → 0.10.1

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.
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "MCP server for Outlook / Microsoft 365 — mail, calendar, contacts and tasks via natural language",
10
- "version": "0.10.0"
10
+ "version": "0.10.1"
11
11
  },
12
12
  "plugins": [
13
13
  {
@@ -15,7 +15,7 @@
15
15
  "displayName": "Outlook (Microsoft 365)",
16
16
  "source": "./",
17
17
  "description": "MCP server for Outlook / Microsoft 365 — triage unread mail, reply, answer meeting invites and schedule meetings, plus folders, calendar, contacts and tasks, with confirmation before anything is sent",
18
- "version": "0.10.0",
18
+ "version": "0.10.1",
19
19
  "author": {
20
20
  "name": "Chris Hall"
21
21
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "office-outlook-mcp",
3
3
  "displayName": "Outlook (Microsoft 365)",
4
- "version": "0.10.0",
4
+ "version": "0.10.1",
5
5
  "description": "Outlook / Microsoft 365 for Claude — triage unread mail, reply, answer meeting invites and schedule meetings, plus folders, calendar, contacts and tasks, with confirmation before anything is sent, using your signed-in browser session",
6
6
  "author": {
7
7
  "name": "Chris Hall",
package/dist/bundle.js CHANGED
@@ -39293,7 +39293,7 @@ var init_version = __esm({
39293
39293
  "src/version.ts"() {
39294
39294
  "use strict";
39295
39295
  PACKAGE_NAME = "office-outlook-mcp";
39296
- VERSION = "0.10.0";
39296
+ VERSION = "0.10.1";
39297
39297
  }
39298
39298
  });
39299
39299
 
@@ -39673,6 +39673,38 @@ var init_capability_availability = __esm({
39673
39673
  }
39674
39674
  });
39675
39675
 
39676
+ // node_modules/@fetchproxy/protocol/dist/binary-fetch.js
39677
+ function parseResponseTooLarge(error62) {
39678
+ const declared = TOO_LARGE_DECLARED_RE.exec(error62);
39679
+ if (declared)
39680
+ return { limitBytes: Number(declared[2]), declaredBytes: Number(declared[1]) };
39681
+ const streamed = TOO_LARGE_STREAMED_RE.exec(error62);
39682
+ if (streamed)
39683
+ return { limitBytes: Number(streamed[1]), declaredBytes: null };
39684
+ return null;
39685
+ }
39686
+ function isBase64Body(s) {
39687
+ return s.length % 4 === 0 && BASE64_RE.test(s);
39688
+ }
39689
+ var FETCH_RESPONSE_TYPE_BASE64, EXTENSION_FEATURE_FETCH_BASE64, KNOWN_EXTENSION_FEATURES, MAX_EXTENSION_FEATURES, MAX_EXTENSION_FEATURE_LENGTH, HOSTED_RELAY_MESSAGE_BYTES, MAX_BINARY_RESPONSE_BYTES, RESPONSE_TOO_LARGE_CODE, TOO_LARGE_DECLARED_RE, TOO_LARGE_STREAMED_RE, BASE64_RE;
39690
+ var init_binary_fetch = __esm({
39691
+ "node_modules/@fetchproxy/protocol/dist/binary-fetch.js"() {
39692
+ FETCH_RESPONSE_TYPE_BASE64 = "base64";
39693
+ EXTENSION_FEATURE_FETCH_BASE64 = "fetch_base64";
39694
+ KNOWN_EXTENSION_FEATURES = /* @__PURE__ */ new Set([
39695
+ EXTENSION_FEATURE_FETCH_BASE64
39696
+ ]);
39697
+ MAX_EXTENSION_FEATURES = 32;
39698
+ MAX_EXTENSION_FEATURE_LENGTH = 64;
39699
+ HOSTED_RELAY_MESSAGE_BYTES = 32 * 1024 * 1024;
39700
+ MAX_BINARY_RESPONSE_BYTES = 16 * 1024 * 1024;
39701
+ RESPONSE_TOO_LARGE_CODE = "response_too_large";
39702
+ TOO_LARGE_DECLARED_RE = /^response body too large: content-length (\d{1,16}) exceeds the (\d{1,16})-byte binary cap/;
39703
+ TOO_LARGE_STREAMED_RE = /^response body too large: more than the (\d{1,16})-byte binary cap was received/;
39704
+ BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
39705
+ }
39706
+ });
39707
+
39676
39708
  // node_modules/@fetchproxy/protocol/dist/json-pointer.js
39677
39709
  function matchesDeclaredKey(requested, declared) {
39678
39710
  for (const decl of declared) {
@@ -39884,7 +39916,7 @@ function assertString(x, label) {
39884
39916
  }
39885
39917
  function assertBase64(x, label) {
39886
39918
  assertString(x, label);
39887
- if (!BASE64_RE.test(x))
39919
+ if (!BASE64_RE2.test(x))
39888
39920
  throw new ProtocolError3(`${label}: invalid base64`);
39889
39921
  }
39890
39922
  function base64ByteLength(s) {
@@ -40288,6 +40320,17 @@ function validateFrame(raw) {
40288
40320
  }
40289
40321
  throw new ProtocolError3(`unknown frame type: ${String(t)}`);
40290
40322
  }
40323
+ function assertTabDomains(value, domains, label) {
40324
+ if (!Array.isArray(value))
40325
+ throw new ProtocolError3(`${label}: expected array`);
40326
+ if (value.length === 0)
40327
+ throw new ProtocolError3(`${label}: must be non-empty`);
40328
+ for (const d of value) {
40329
+ if (typeof d !== "string" || !domains.includes(d)) {
40330
+ throw new ProtocolError3(`${label}: ${JSON.stringify(d)} is not one of the declared domains [${domains.join(", ")}]`);
40331
+ }
40332
+ }
40333
+ }
40291
40334
  function validateHello(raw) {
40292
40335
  if (raw.protocolVersion !== PROTOCOL_VERSION) {
40293
40336
  throw new ProtocolError3(`hello.protocolVersion: must be ${PROTOCOL_VERSION}`);
@@ -40364,6 +40407,9 @@ function validateHello(raw) {
40364
40407
  }
40365
40408
  if (raw.accepts !== void 0)
40366
40409
  assertAcceptsList(raw.accepts);
40410
+ if (raw.tabDomains !== void 0) {
40411
+ assertTabDomains(raw.tabDomains, raw.domains, "hello.tabDomains");
40412
+ }
40367
40413
  if (raw.graphqlOps !== void 0) {
40368
40414
  assertGraphqlOpsArray(raw.graphqlOps, "hello.graphqlOps");
40369
40415
  }
@@ -40390,6 +40436,8 @@ function validateHello(raw) {
40390
40436
  if (raw.unavailableCapabilities !== void 0) {
40391
40437
  assertUnavailableCapabilities(raw.unavailableCapabilities);
40392
40438
  }
40439
+ if (raw.features !== void 0)
40440
+ assertExtensionFeatures(raw.features);
40393
40441
  return raw;
40394
40442
  }
40395
40443
  throw new ProtocolError3(`hello.role: must be 'server' or 'extension', got ${String(role)}`);
@@ -40454,6 +40502,22 @@ function assertAcceptsList(accepts) {
40454
40502
  }
40455
40503
  }
40456
40504
  }
40505
+ function assertExtensionFeatures(list) {
40506
+ const label = "hello.features";
40507
+ if (!Array.isArray(list))
40508
+ throw new ProtocolError3(`${label}: expected array`);
40509
+ if (list.length > MAX_EXTENSION_FEATURES) {
40510
+ throw new ProtocolError3(`${label}: at most ${MAX_EXTENSION_FEATURES} entries, got ${list.length}`);
40511
+ }
40512
+ for (const f of list) {
40513
+ if (typeof f !== "string") {
40514
+ throw new ProtocolError3(`${label}: entry must be string, got ${typeof f}`);
40515
+ }
40516
+ if (f.length < 1 || f.length > MAX_EXTENSION_FEATURE_LENGTH) {
40517
+ throw new ProtocolError3(`${label}: entry must be 1-${MAX_EXTENSION_FEATURE_LENGTH} characters, got ${f.length}`);
40518
+ }
40519
+ }
40520
+ }
40457
40521
  function assertUnavailableCapabilities(list) {
40458
40522
  const label = "hello.unavailableCapabilities";
40459
40523
  if (!Array.isArray(list))
@@ -40657,6 +40721,11 @@ function validateInnerRequest(raw) {
40657
40721
  throw new ProtocolError3("inner.init.credentials: must be 'include' or 'omit'");
40658
40722
  }
40659
40723
  }
40724
+ if (raw.init.responseType !== void 0) {
40725
+ if (raw.init.responseType !== FETCH_RESPONSE_TYPE_BASE64) {
40726
+ throw new ProtocolError3("inner.init.responseType: must be 'base64' when present");
40727
+ }
40728
+ }
40660
40729
  return raw;
40661
40730
  }
40662
40731
  if (raw.op === "read_cookies") {
@@ -40961,6 +41030,16 @@ function validateInnerResponse(raw) {
40961
41030
  assertPositiveInt(raw.status, "inner.status");
40962
41031
  assertString(raw.url, "inner.url");
40963
41032
  assertString(raw.body, "inner.body");
41033
+ if (raw.bodyEncoding !== void 0) {
41034
+ if (raw.bodyEncoding !== "base64") {
41035
+ throw new ProtocolError3("inner.bodyEncoding: must be 'base64' when present");
41036
+ }
41037
+ if (!isBase64Body(raw.body)) {
41038
+ throw new ProtocolError3("inner.body: not standard padded base64 under bodyEncoding base64");
41039
+ }
41040
+ }
41041
+ if (raw.headers !== void 0)
41042
+ assertStringMap(raw.headers, "inner.headers");
40964
41043
  return raw;
40965
41044
  }
40966
41045
  if (op === "read_cookies") {
@@ -41079,11 +41158,12 @@ function validateInnerResponse(raw) {
41079
41158
  }
41080
41159
  throw new ProtocolError3(`inner.ok: must be boolean, got ${typeof raw.ok}`);
41081
41160
  }
41082
- var ProtocolError3, FORBIDDEN_KEYS, BASE64_RE, SCOPE_KEY_RE, SCOPE_KEY_GLOB_RE, HEADER_NAME_RE, HOSTNAME_RE, CAPTURE_PATH_RE, DOM_SELECTOR_RE, DOM_ATTRIBUTE_RE, GRAPHQL_OP_NAME_RE, PAIR_CODE_RE, DISPLAY_FORBIDDEN_RE, ACCOUNT_KEY_FIELDS, ACCOUNT_ATTEST_FIELDS, TYPE_ONLY_FIELDS, BRIDGE_ROLE_FIELDS, BRIDGE_ROLE_SERVING_FIELDS, KNOWN_RESPONSE_OPS;
41161
+ var ProtocolError3, FORBIDDEN_KEYS, BASE64_RE2, SCOPE_KEY_RE, SCOPE_KEY_GLOB_RE, HEADER_NAME_RE, HOSTNAME_RE, CAPTURE_PATH_RE, DOM_SELECTOR_RE, DOM_ATTRIBUTE_RE, GRAPHQL_OP_NAME_RE, PAIR_CODE_RE, DISPLAY_FORBIDDEN_RE, ACCOUNT_KEY_FIELDS, ACCOUNT_ATTEST_FIELDS, TYPE_ONLY_FIELDS, BRIDGE_ROLE_FIELDS, BRIDGE_ROLE_SERVING_FIELDS, KNOWN_RESPONSE_OPS;
41083
41162
  var init_validate = __esm({
41084
41163
  "node_modules/@fetchproxy/protocol/dist/validate.js"() {
41085
41164
  init_frames();
41086
41165
  init_capability_availability();
41166
+ init_binary_fetch();
41087
41167
  init_mcp_id();
41088
41168
  init_account_attest();
41089
41169
  init_json_pointer();
@@ -41096,7 +41176,7 @@ var init_validate = __esm({
41096
41176
  }
41097
41177
  };
41098
41178
  FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
41099
- BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
41179
+ BASE64_RE2 = /^[A-Za-z0-9+/]*={0,2}$/;
41100
41180
  SCOPE_KEY_RE = /^[A-Za-z0-9_.\-]{1,256}$/;
41101
41181
  SCOPE_KEY_GLOB_RE = /^[A-Za-z0-9_.\-]{1,256}\*?$/;
41102
41182
  HEADER_NAME_RE = /^[A-Za-z0-9_\-]{1,128}$/;
@@ -41260,6 +41340,7 @@ var init_dist3 = __esm({
41260
41340
  init_account_attest();
41261
41341
  init_awaiting_approval();
41262
41342
  init_capability_availability();
41343
+ init_binary_fetch();
41263
41344
  init_validate();
41264
41345
  init_room_frames();
41265
41346
  init_crypto();
@@ -41283,7 +41364,30 @@ function sessionInfoFromHello(hello) {
41283
41364
  known.add(c);
41284
41365
  }
41285
41366
  }
41286
- return { platform: hello.platform, unavailableCapabilities: [...known].sort() };
41367
+ const features = /* @__PURE__ */ new Set();
41368
+ const rawFeatures = hello.features;
41369
+ if (Array.isArray(rawFeatures)) {
41370
+ for (const f of rawFeatures) {
41371
+ if (typeof f === "string" && KNOWN_EXTENSION_FEATURES.has(f))
41372
+ features.add(f);
41373
+ }
41374
+ }
41375
+ return {
41376
+ platform: hello.platform,
41377
+ unavailableCapabilities: [...known].sort(),
41378
+ features: [...features].sort()
41379
+ };
41380
+ }
41381
+ function featureForRequest(inner) {
41382
+ if (inner.type !== "request" || inner.op !== "fetch")
41383
+ return null;
41384
+ return inner.init.responseType === "base64" ? EXTENSION_FEATURE_FETCH_BASE64 : null;
41385
+ }
41386
+ function missingFeatureFor(inner, info) {
41387
+ const feature = featureForRequest(inner);
41388
+ if (feature === null || info === null)
41389
+ return null;
41390
+ return info.features.includes(feature) ? null : feature;
41287
41391
  }
41288
41392
  function capabilitiesForRequest(inner) {
41289
41393
  if (inner.type !== "request")
@@ -45113,6 +45217,8 @@ async function buildServerHello(opts) {
45113
45217
  };
45114
45218
  if (opts.accepts && opts.accepts.length > 0)
45115
45219
  hello.accepts = [...opts.accepts];
45220
+ if (opts.tabDomains && opts.tabDomains.length > 0)
45221
+ hello.tabDomains = [...opts.tabDomains];
45116
45222
  if (opts.cookieKeys && opts.cookieKeys.length > 0) {
45117
45223
  hello.cookieKeys = [...opts.cookieKeys];
45118
45224
  }
@@ -45950,7 +46056,8 @@ async function startHost(opts) {
45950
46056
  sessionStoragePointers: opts.ownSessionStoragePointers,
45951
46057
  domSelectors: opts.ownDomSelectors,
45952
46058
  domListSelectors: opts.ownDomListSelectors,
45953
- graphqlOps: opts.ownGraphqlOps
46059
+ graphqlOps: opts.ownGraphqlOps,
46060
+ tabDomains: opts.ownTabDomains
45954
46061
  };
45955
46062
  const generateSessionKeypair = opts.generateSessionKeypair ?? generateX25519;
45956
46063
  let extensionWs = null;
@@ -46536,6 +46643,7 @@ async function startPeer(opts) {
46536
46643
  localStoragePointers: opts.localStoragePointers,
46537
46644
  sessionStoragePointers: opts.sessionStoragePointers,
46538
46645
  graphqlOps: opts.graphqlOps,
46646
+ tabDomains: opts.tabDomains,
46539
46647
  // 2.5.0: let a host ≥2.5.0 tell us when the extension leaves, so
46540
46648
  // `extensionConnected()` / `sessionLinked()` can go back to false.
46541
46649
  // 2.6.0: `hello-rejected` for the same reason, from the extension.
@@ -46914,6 +47022,8 @@ var init_peer = __esm({
46914
47022
  function classifyFetchError(error62, code2) {
46915
47023
  if (code2 === CAPABILITY_UNAVAILABLE_CODE)
46916
47024
  return "capability_unavailable";
47025
+ if (code2 === RESPONSE_TOO_LARGE_CODE)
47026
+ return "body_too_large";
46917
47027
  if (/^(in-page )?fetch threw:/.test(error62)) {
46918
47028
  return "tab_fetch_failed";
46919
47029
  }
@@ -47013,6 +47123,13 @@ function protocolErrorFrom(error62, op, response) {
47013
47123
  platform: unavailable?.platform ?? null
47014
47124
  });
47015
47125
  }
47126
+ const tooLarge = parseResponseTooLarge(error62);
47127
+ if (tooLarge !== null || response?.code === RESPONSE_TOO_LARGE_CODE) {
47128
+ return new FetchproxyResponseTooLargeError(error62, {
47129
+ limitBytes: tooLarge?.limitBytes ?? null,
47130
+ declaredBytes: tooLarge?.declaredBytes ?? null
47131
+ });
47132
+ }
47016
47133
  if (error62 === "timeout")
47017
47134
  return new FetchproxyWaitedError(error62, op);
47018
47135
  if (SCOPE_REJECTION.test(error62))
@@ -47047,6 +47164,13 @@ function assertSubdomainLabel(label) {
47047
47164
  throw new Error(`FetchproxyServer: subdomain must be a DNS label like "www" or "api" (or dot-separated like "auth.api"), got ${JSON.stringify(label)}`);
47048
47165
  }
47049
47166
  }
47167
+ function assertExpectedStatus(response, expected, asHttp = () => response) {
47168
+ if (expected === void 0)
47169
+ return;
47170
+ const matched = Array.isArray(expected) ? expected.includes(response.status) : response.status === expected;
47171
+ if (!matched)
47172
+ throw new FetchproxyHttpError(asHttp());
47173
+ }
47050
47174
  function assertUrlInDomains(field, url2, domains) {
47051
47175
  let parsed;
47052
47176
  try {
@@ -47091,7 +47215,7 @@ function isResendableRequest(inner) {
47091
47215
  return true;
47092
47216
  }
47093
47217
  }
47094
- var FetchproxyProtocolError, FetchproxyHttpError, FetchproxyBridgeDownError, FetchproxyHintedError, FetchproxyScopeError, FetchproxyNoTabError, FetchproxyTabOpeningError, FetchproxyWaitedError, FetchproxyCapabilityUnavailableError, SCOPE_REJECTION, NO_TAB_REJECTION, TAB_OPENING_REJECTION, VERB_DEADLINE_GRACE_MS, FetchproxyTimeoutError, TIMEOUT_NOT_RESENT_NOTE, SUBDOMAIN_LABEL_RE, DEFAULT_JSON_OK_STATUSES, TIMEOUT_RETRY_SAFE_METHODS, FetchproxyServer;
47218
+ var FetchproxyProtocolError, FetchproxyHttpError, FetchproxyBridgeDownError, FetchproxyHintedError, FetchproxyScopeError, FetchproxyNoTabError, FetchproxyTabOpeningError, FetchproxyWaitedError, FetchproxyCapabilityUnavailableError, FetchproxyExtensionUpdateRequiredError, FetchproxyResponseTooLargeError, SCOPE_REJECTION, NO_TAB_REJECTION, TAB_OPENING_REJECTION, VERB_DEADLINE_GRACE_MS, FetchproxyTimeoutError, TIMEOUT_NOT_RESENT_NOTE, SUBDOMAIN_LABEL_RE, DEFAULT_JSON_OK_STATUSES, TIMEOUT_RETRY_SAFE_METHODS, FetchproxyServer;
47095
47219
  var init_ws_server = __esm({
47096
47220
  "node_modules/@fetchproxy/server/dist/ws-server.js"() {
47097
47221
  init_dist3();
@@ -47194,6 +47318,28 @@ var init_ws_server = __esm({
47194
47318
  this.platform = info.platform;
47195
47319
  }
47196
47320
  };
47321
+ FetchproxyExtensionUpdateRequiredError = class extends FetchproxyHintedError {
47322
+ /** The feature the extension lacks (`'fetch_base64'`). */
47323
+ feature;
47324
+ constructor(originalError, info) {
47325
+ super(originalError, `this MCP needs the browser extension to support "${info.feature}" (binary downloads through the bridge), and the ContextMint Bridge build it is paired with does not. Update ContextMint Bridge (Chrome Web Store / App Store, or rebuild a sideloaded copy and reload it), then retry. Nothing is wrong with the MCP or your pairing, and re-pairing will not change it.`);
47326
+ this.name = "FetchproxyExtensionUpdateRequiredError";
47327
+ this.feature = info.feature;
47328
+ }
47329
+ };
47330
+ FetchproxyResponseTooLargeError = class extends FetchproxyHintedError {
47331
+ /** The cap the extension enforced, in bytes, when it said; else `null`. */
47332
+ limitBytes;
47333
+ /** The response's `content-length`, when the refusal came from it; else `null`. */
47334
+ declaredBytes;
47335
+ constructor(originalError, info) {
47336
+ const cap = info.limitBytes ?? MAX_BINARY_RESPONSE_BYTES;
47337
+ super(originalError, `the file is larger than the ${cap}-byte cap one bridge response can carry, so the extension refused it rather than send a frame the bridge cannot deliver. This is not a version or pairing problem: fetch a smaller rendition if the site offers one, or download it directly in the browser.`);
47338
+ this.name = "FetchproxyResponseTooLargeError";
47339
+ this.limitBytes = info.limitBytes;
47340
+ this.declaredBytes = info.declaredBytes;
47341
+ }
47342
+ };
47197
47343
  SCOPE_REJECTION = /not in declared/;
47198
47344
  NO_TAB_REJECTION = /no tab matching (?!.*(content script loaded|one is still opening))/;
47199
47345
  TAB_OPENING_REJECTION = /no tab matching .*one is still opening/;
@@ -47360,6 +47506,9 @@ var init_ws_server = __esm({
47360
47506
  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`);
47361
47507
  }
47362
47508
  }
47509
+ if (opts.tabDomains !== void 0) {
47510
+ assertTabDomains(opts.tabDomains, opts.domains, "FetchproxyServer: opts.tabDomains");
47511
+ }
47363
47512
  let capabilities;
47364
47513
  if (opts.capabilities === void 0) {
47365
47514
  capabilities = ["fetch"];
@@ -47430,6 +47579,7 @@ var init_ws_server = __esm({
47430
47579
  name: d.name,
47431
47580
  operationName: d.operationName
47432
47581
  })),
47582
+ ...opts.tabDomains !== void 0 ? { tabDomains: [...opts.tabDomains] } : {},
47433
47583
  // 0.8.0+: timer + lazy-revive default to ON. Every realty MCP
47434
47584
  // adapter was about to set these to the same numbers anyway; the
47435
47585
  // back-door is `0` (explicit opt-out) if a caller genuinely wants
@@ -47559,6 +47709,7 @@ var init_ws_server = __esm({
47559
47709
  ownDomSelectors: this.opts.domSelectors,
47560
47710
  ownDomListSelectors: this.opts.domListSelectors,
47561
47711
  ownGraphqlOps: this.opts.graphqlOps,
47712
+ ownTabDomains: this.opts.tabDomains,
47562
47713
  onPairCode: this.opts.onPairCode,
47563
47714
  extensionTrust: this.extensionTrust()
47564
47715
  });
@@ -47592,6 +47743,7 @@ var init_ws_server = __esm({
47592
47743
  domSelectors: this.opts.domSelectors,
47593
47744
  domListSelectors: this.opts.domListSelectors,
47594
47745
  graphqlOps: this.opts.graphqlOps,
47746
+ tabDomains: this.opts.tabDomains,
47595
47747
  extensionTrust: this.extensionTrust(),
47596
47748
  requireExtensionIdentity: this.opts.requireExtensionIdentity
47597
47749
  });
@@ -47876,6 +48028,15 @@ var init_ws_server = __esm({
47876
48028
  cause: err
47877
48029
  };
47878
48030
  }
48031
+ if (err instanceof FetchproxyExtensionUpdateRequiredError) {
48032
+ return {
48033
+ ok: false,
48034
+ error: err.originalError,
48035
+ kind: "extension_update_required",
48036
+ retryAttempted: false,
48037
+ cause: err
48038
+ };
48039
+ }
47879
48040
  return {
47880
48041
  ok: false,
47881
48042
  error: err instanceof Error ? err.message : String(err),
@@ -48022,7 +48183,10 @@ var init_ws_server = __esm({
48022
48183
  if (result.kind === "capability_unavailable") {
48023
48184
  return protocolErrorFrom(result.error, void 0, { code: CAPABILITY_UNAVAILABLE_CODE, op });
48024
48185
  }
48025
- return protocolErrorFrom(result.error);
48186
+ if (result.kind === "extension_update_required" && result.cause instanceof FetchproxyExtensionUpdateRequiredError) {
48187
+ return result.cause;
48188
+ }
48189
+ return protocolErrorFrom(result.error, void 0, result.code !== void 0 ? { code: result.code, op } : void 0);
48026
48190
  }
48027
48191
  /**
48028
48192
  * Convenience wrapper around `fetch()`. Builds the URL from a path
@@ -48042,6 +48206,88 @@ var init_ws_server = __esm({
48042
48206
  * the declared domains exactly.
48043
48207
  */
48044
48208
  async request(method, path2, opts = {}) {
48209
+ const init = this.resolveRequestInit("request", method, path2, opts);
48210
+ const result = await this.fetch(init, opts.retryOnTimeout !== void 0 ? { retryOnTimeout: opts.retryOnTimeout } : {});
48211
+ if (!result.ok)
48212
+ this.throwFetchFailure(result, init.url, method, opts.retryOnTimeout);
48213
+ const response = {
48214
+ status: result.status,
48215
+ body: result.body,
48216
+ url: result.url
48217
+ };
48218
+ assertExpectedStatus(response, opts.expectStatus);
48219
+ return response;
48220
+ }
48221
+ /**
48222
+ * #460: like `request()`, but the response body comes back as BYTES,
48223
+ * intact — a PDF, an office file, an image — together with the relayed
48224
+ * `content-type` / `content-length` / `content-disposition` headers and the
48225
+ * final URL after redirects.
48226
+ *
48227
+ * The extension runs it from its background worker rather than a tab, so a
48228
+ * file host that sends no CORS headers is still readable, and it follows
48229
+ * redirects. Both the request URL and the FINAL URL must be inside the
48230
+ * declared `domains` — the extension enforces that, and so does this method
48231
+ * on the answer. To read a signed file host, declare it.
48232
+ *
48233
+ * Bodies are capped at `MAX_BINARY_RESPONSE_BYTES` (16 MiB): over it the
48234
+ * extension refuses with `FetchproxyResponseTooLargeError`. An extension
48235
+ * that predates binary fetch is refused with
48236
+ * `FetchproxyExtensionUpdateRequiredError` — locally when its hello says so,
48237
+ * and on the answer when it replied in text anyway.
48238
+ */
48239
+ async requestBytes(method, path2, opts = {}) {
48240
+ const init = {
48241
+ ...this.resolveRequestInit("requestBytes", method, path2, opts),
48242
+ responseType: "base64"
48243
+ };
48244
+ const result = await this.fetch(init, opts.retryOnTimeout !== void 0 ? { retryOnTimeout: opts.retryOnTimeout } : {});
48245
+ if (!result.ok)
48246
+ this.throwFetchFailure(result, init.url, method, opts.retryOnTimeout);
48247
+ if (result.bodyEncoding !== "base64") {
48248
+ throw new FetchproxyExtensionUpdateRequiredError("the paired ContextMint Bridge extension answered a binary request as text", { feature: EXTENSION_FEATURE_FETCH_BASE64 });
48249
+ }
48250
+ try {
48251
+ assertUrlInDomains("requestBytes final url", result.url, this.opts.domains);
48252
+ } catch (err) {
48253
+ throw new FetchproxyProtocolError(err.message);
48254
+ }
48255
+ if (!isBase64Body(result.body)) {
48256
+ throw new FetchproxyProtocolError("requestBytes: the extension sent a body that is not base64");
48257
+ }
48258
+ const decoded = Buffer.from(result.body, "base64");
48259
+ const response = {
48260
+ status: result.status,
48261
+ url: result.url,
48262
+ headers: { ...result.headers ?? {} },
48263
+ bytes: new Uint8Array(decoded.buffer, decoded.byteOffset, decoded.byteLength)
48264
+ };
48265
+ assertExpectedStatus(response, opts.expectStatus, () => ({
48266
+ status: response.status,
48267
+ url: response.url,
48268
+ // FetchproxyHttpError carries an HttpResponse; a binary body has no
48269
+ // honest text form, so the bytes stay on the BinaryResponse.
48270
+ body: ""
48271
+ }));
48272
+ return response;
48273
+ }
48274
+ /** #460: `requestBytes('GET', path, opts)`. */
48275
+ getBytes(path2, opts = {}) {
48276
+ return this.requestBytes("GET", path2, opts);
48277
+ }
48278
+ /**
48279
+ * Resolve `path` + options into the wire `FetchInit` — URL building, the
48280
+ * declared-domain guard, the relay tab. Shared by `request()` and
48281
+ * `requestBytes()` so the two can never disagree about which URLs are
48282
+ * reachable.
48283
+ *
48284
+ * Path resolution:
48285
+ * - Absolute URL (`https://...`) → used as-is (still guarded against
48286
+ * leaving the declared domain set).
48287
+ * - Relative path → joined with `https://${subdomain}.${baseDomain}`
48288
+ * (or `https://${baseDomain}` if no subdomain is given).
48289
+ */
48290
+ resolveRequestInit(caller, method, path2, opts) {
48045
48291
  if (opts.subdomain !== void 0)
48046
48292
  assertSubdomainLabel(opts.subdomain);
48047
48293
  const baseDomain = this.resolveBaseDomain(opts.domain);
@@ -48051,24 +48297,24 @@ var init_ws_server = __esm({
48051
48297
  try {
48052
48298
  host = new URL(path2).host;
48053
48299
  } catch {
48054
- throw new Error(`FetchproxyServer.request: absolute path is not a valid URL: ${JSON.stringify(path2)}`);
48300
+ throw new Error(`FetchproxyServer.${caller}: absolute path is not a valid URL: ${JSON.stringify(path2)}`);
48055
48301
  }
48056
48302
  } else {
48057
48303
  host = opts.subdomain ? `${opts.subdomain}.${baseDomain}` : baseDomain;
48058
48304
  }
48059
48305
  const url2 = isAbsolute6 ? path2 : `https://${host}${path2}`;
48060
- assertUrlInDomains("request url", url2, this.opts.domains);
48306
+ assertUrlInDomains(`${caller} url`, url2, this.opts.domains);
48061
48307
  let tabUrl = `https://${host}/`;
48062
48308
  if (opts.viaTab !== void 0) {
48063
48309
  try {
48064
48310
  new URL(opts.viaTab);
48065
48311
  } catch {
48066
- throw new Error(`FetchproxyServer.request: viaTab is not a valid URL: ${JSON.stringify(opts.viaTab)}`);
48312
+ throw new Error(`FetchproxyServer.${caller}: viaTab is not a valid URL: ${JSON.stringify(opts.viaTab)}`);
48067
48313
  }
48068
48314
  assertUrlInDomains("viaTab", opts.viaTab, this.opts.domains);
48069
48315
  tabUrl = opts.viaTab;
48070
48316
  }
48071
- const init = {
48317
+ return {
48072
48318
  url: url2,
48073
48319
  method,
48074
48320
  tabUrl,
@@ -48083,29 +48329,16 @@ var init_ws_server = __esm({
48083
48329
  // every version before this option existed.
48084
48330
  ...opts.credentials === "omit" ? { credentials: "omit" } : {}
48085
48331
  };
48086
- const result = await this.fetch(init, opts.retryOnTimeout !== void 0 ? { retryOnTimeout: opts.retryOnTimeout } : {});
48087
- if (!result.ok) {
48088
- if (result.kind === "session_not_ready" && result.cause instanceof Error) {
48089
- throw result.cause;
48090
- }
48091
- if (result.kind === "capability_unavailable" && result.cause instanceof FetchproxyCapabilityUnavailableError) {
48092
- throw result.cause;
48093
- }
48094
- throw this._typedErrorFor(result, init.url, "fetch", result.retryAttempted ?? false, isRetrySafeOnTimeout(method, opts.retryOnTimeout));
48332
+ }
48333
+ /** Turn a failed `fetch()` envelope into the throw `request()` has always made. */
48334
+ throwFetchFailure(result, url2, method, retryOnTimeout) {
48335
+ if (result.kind === "session_not_ready" && result.cause instanceof Error) {
48336
+ throw result.cause;
48095
48337
  }
48096
- const response = {
48097
- status: result.status,
48098
- body: result.body,
48099
- url: result.url
48100
- };
48101
- if (opts.expectStatus !== void 0) {
48102
- const expected = opts.expectStatus;
48103
- const matched = Array.isArray(expected) ? expected.includes(response.status) : response.status === expected;
48104
- if (!matched) {
48105
- throw new FetchproxyHttpError(response);
48106
- }
48338
+ if (result.kind === "capability_unavailable" && result.cause instanceof FetchproxyCapabilityUnavailableError) {
48339
+ throw result.cause;
48107
48340
  }
48108
- return response;
48341
+ throw this._typedErrorFor(result, url2, "fetch", result.retryAttempted ?? false, isRetrySafeOnTimeout(method, retryOnTimeout));
48109
48342
  }
48110
48343
  /** Issue a GET against `path` (resolved via `request()` rules). */
48111
48344
  get(path2, opts = {}) {
@@ -48906,6 +49139,10 @@ var init_ws_server = __esm({
48906
49139
  status: inner.status,
48907
49140
  url: inner.url,
48908
49141
  body: inner.body,
49142
+ // #460: carried only when present, so a text answer's envelope
49143
+ // has exactly the keys it always had.
49144
+ ...inner.bodyEncoding !== void 0 ? { bodyEncoding: inner.bodyEncoding } : {},
49145
+ ...inner.headers !== void 0 ? { headers: { ...inner.headers } } : {},
48909
49146
  retryAttempted: false
48910
49147
  });
48911
49148
  } else {
@@ -48922,7 +49159,8 @@ var init_ws_server = __esm({
48922
49159
  ok: false,
48923
49160
  error: inner.error,
48924
49161
  kind: classifyFetchError(inner.error, inner.code),
48925
- retryAttempted: false
49162
+ retryAttempted: false,
49163
+ ...inner.code !== void 0 ? { code: inner.code } : {}
48926
49164
  });
48927
49165
  }
48928
49166
  return;
@@ -49226,6 +49464,7 @@ var init_ws_server = __esm({
49226
49464
  pairCode,
49227
49465
  extensionConnected: false,
49228
49466
  unavailableCapabilities: [],
49467
+ features: [],
49229
49468
  platform: null
49230
49469
  };
49231
49470
  }
@@ -49237,6 +49476,7 @@ var init_ws_server = __esm({
49237
49476
  pairCode,
49238
49477
  extensionConnected,
49239
49478
  unavailableCapabilities: info ? [...info.unavailableCapabilities] : [],
49479
+ features: info ? [...info.features] : [],
49240
49480
  platform: info?.platform ?? null
49241
49481
  };
49242
49482
  }
@@ -49250,9 +49490,13 @@ var init_ws_server = __esm({
49250
49490
  */
49251
49491
  refuseIfUnavailable(inner, info) {
49252
49492
  const cap = unavailableCapabilityFor(inner, info);
49253
- if (cap === null || info === null)
49254
- return;
49255
- throw new FetchproxyCapabilityUnavailableError(capabilityUnavailableMessage(cap, info.platform), { capability: cap, platform: info.platform });
49493
+ if (cap !== null && info !== null) {
49494
+ throw new FetchproxyCapabilityUnavailableError(capabilityUnavailableMessage(cap, info.platform), { capability: cap, platform: info.platform });
49495
+ }
49496
+ const feature = missingFeatureFor(inner, info);
49497
+ if (feature !== null) {
49498
+ throw new FetchproxyExtensionUpdateRequiredError(`the paired ContextMint Bridge extension does not support "${feature}"`, { feature });
49499
+ }
49256
49500
  }
49257
49501
  /**
49258
49502
  * 0.5.2+: throw `FetchproxyProtocolError` with the actionable pair-code
@@ -49353,6 +49597,7 @@ var init_dist4 = __esm({
49353
49597
  init_deadline();
49354
49598
  init_parse_html();
49355
49599
  init_batch();
49600
+ init_dist3();
49356
49601
  init_extension_trust();
49357
49602
  init_extension_pins();
49358
49603
  init_identity();
package/dist/version.js CHANGED
@@ -10,4 +10,4 @@
10
10
  * line below and nowhere else — not even in prose.
11
11
  */
12
12
  export const PACKAGE_NAME = 'office-outlook-mcp';
13
- export const VERSION = '0.10.0'; // x-release-please-version
13
+ export const VERSION = '0.10.1'; // x-release-please-version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrischall/office-outlook-mcp",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "mcpName": "io.github.chrischall/office-outlook-mcp",
5
5
  "description": "Outlook / Microsoft 365 MCP server for Claude — developed and maintained by AI (Claude Code)",
6
6
  "author": "Claude Code (AI) <https://www.anthropic.com/claude>",
@@ -47,20 +47,20 @@
47
47
  "dependencies": {
48
48
  "@azure/msal-node": "^7.2.1",
49
49
  "@chrischall/mcp-utils": "^3.7.2",
50
- "@fetchproxy/server": "^3.6.2",
51
- "@modelcontextprotocol/server": "^2.3.0",
52
- "dotenv": "^18.0.1",
50
+ "@fetchproxy/server": "^3.8.0",
51
+ "@modelcontextprotocol/server": "^2.4.0",
52
+ "dotenv": "^18.0.10",
53
53
  "ws": "^8.22.2",
54
- "zod": "^4.6.5"
54
+ "zod": "^4.6.7"
55
55
  },
56
56
  "devDependencies": {
57
- "@modelcontextprotocol/client": "^2.3.0",
58
- "@types/node": "^26.0.0",
57
+ "@modelcontextprotocol/client": "^2.4.0",
58
+ "@types/node": "^26.6.6",
59
59
  "@types/ws": "^8.18.2",
60
- "@vitest/coverage-v8": "^5.0.0",
61
- "esbuild": "^0.28.1",
60
+ "@vitest/coverage-v8": "^5.1.0",
61
+ "esbuild": "^0.28.2",
62
62
  "typescript": "^7.0.2",
63
- "vitest": "^5.0.0"
63
+ "vitest": "^5.1.0"
64
64
  },
65
65
  "allowScripts": {
66
66
  "esbuild": false,
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/chrischall/office-outlook-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.10.0",
9
+ "version": "0.10.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@chrischall/office-outlook-mcp",
14
- "version": "0.10.0",
14
+ "version": "0.10.1",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },