hypermail-mcp 0.7.19 → 0.7.20

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/README.md CHANGED
@@ -3,6 +3,14 @@
3
3
  A **Model Context Protocol** server that lets an agent operate any of the user's
4
4
  inboxes through a single, unified tool surface.
5
5
 
6
+ > **v0.7.20** — Hardened long-running IMAP and new-email polling paths:
7
+ > IMAP connections now use explicit connection/greeting/socket/operation
8
+ > timeouts, reset stuck connections, and report post-TLS authentication
9
+ > failures with account-specific remediation guidance. `get_new_emails` now
10
+ > applies per-account timeouts during candidate collection and hydration so one
11
+ > hanging account does not block all-account polling; partial timeout failures
12
+ > are returned alongside successful accounts' emails.
13
+ >
6
14
  > **v0.7.19** — Hardened Outlook ID handling when Microsoft Graph `$search`
7
15
  > returns IDs that `/me/messages/{id}` rejects as malformed: `search_emails`
8
16
  > now returns a translated readable immutable ID when available, and
@@ -344,7 +352,9 @@ tool on their own schedule, for example every 30–60 seconds.
344
352
  - `limit: 0` can initialize/check state without fetching message bodies.
345
353
 
346
354
  All-account calls return partial failures as `errors: [{ account, message }]`
347
- and still return successful accounts' emails.
355
+ and still return successful accounts' emails. Pollers should continue processing
356
+ returned emails when `errors` is non-empty, and use those entries to notify or
357
+ log which accounts are failing.
348
358
 
349
359
  See [`examples/hermes/`](examples/hermes/) for a Hermes scheduler integration
350
360
  that polls this tool and hands new-email payloads to a Hermes agent.
@@ -373,6 +383,13 @@ that polls this tool and hands new-email payloads to a Hermes agent.
373
383
  it returns `{ "status": "ready", "account": {...} }`.
374
384
  5. From then on, any tool can be called with `account: "<that-email>"`.
375
385
 
386
+ ### IMAP
387
+
388
+ IMAP accounts are added synchronously with host/user/password settings. If the
389
+ server accepts TCP/TLS but closes during login, Hypermail reports an IMAP
390
+ authentication failure. Check the mailbox password or app-password, confirm IMAP
391
+ access is enabled by the provider, then re-add or update the account.
392
+
376
393
  ### Gmail
377
394
 
378
395
  Gmail uses Google OAuth 2.0, matching the official Gmail MCP model. Google's
package/dist/cli.js CHANGED
@@ -8625,19 +8625,19 @@ var getRefs = (options) => {
8625
8625
  };
8626
8626
 
8627
8627
  // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@4.4.3/node_modules/zod-to-json-schema/dist/esm/errorMessages.js
8628
- function addErrorMessage(res, key, errorMessage, refs) {
8628
+ function addErrorMessage(res, key, errorMessage2, refs) {
8629
8629
  if (!refs?.errorMessages)
8630
8630
  return;
8631
- if (errorMessage) {
8631
+ if (errorMessage2) {
8632
8632
  res.errorMessage = {
8633
8633
  ...res.errorMessage,
8634
- [key]: errorMessage
8634
+ [key]: errorMessage2
8635
8635
  };
8636
8636
  }
8637
8637
  }
8638
- function setResponseValueAndErrors(res, key, value, errorMessage, refs) {
8638
+ function setResponseValueAndErrors(res, key, value, errorMessage2, refs) {
8639
8639
  res[key] = value;
8640
- addErrorMessage(res, key, errorMessage, refs);
8640
+ addErrorMessage(res, key, errorMessage2, refs);
8641
8641
  }
8642
8642
 
8643
8643
  // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@4.4.3/node_modules/zod-to-json-schema/dist/esm/getRelativePath.js
@@ -9955,8 +9955,8 @@ var Protocol = class {
9955
9955
  if (queuedMessage.type === "response") {
9956
9956
  resolver(message);
9957
9957
  } else {
9958
- const errorMessage = message;
9959
- const error = new McpError(errorMessage.error.code, errorMessage.error.message, errorMessage.error.data);
9958
+ const errorMessage2 = message;
9959
+ const error = new McpError(errorMessage2.error.code, errorMessage2.error.message, errorMessage2.error.data);
9960
9960
  resolver(error);
9961
9961
  }
9962
9962
  } else {
@@ -11256,23 +11256,23 @@ var Server = class extends Protocol {
11256
11256
  const wrappedHandler = async (request, extra) => {
11257
11257
  const validatedRequest = safeParse2(CallToolRequestSchema, request);
11258
11258
  if (!validatedRequest.success) {
11259
- const errorMessage = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
11260
- throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage}`);
11259
+ const errorMessage2 = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
11260
+ throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage2}`);
11261
11261
  }
11262
11262
  const { params } = validatedRequest.data;
11263
11263
  const result = await Promise.resolve(handler(request, extra));
11264
11264
  if (params.task) {
11265
11265
  const taskValidationResult = safeParse2(CreateTaskResultSchema, result);
11266
11266
  if (!taskValidationResult.success) {
11267
- const errorMessage = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
11268
- throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage}`);
11267
+ const errorMessage2 = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
11268
+ throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage2}`);
11269
11269
  }
11270
11270
  return taskValidationResult.data;
11271
11271
  }
11272
11272
  const validationResult = safeParse2(CallToolResultSchema, result);
11273
11273
  if (!validationResult.success) {
11274
- const errorMessage = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
11275
- throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage}`);
11274
+ const errorMessage2 = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
11275
+ throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage2}`);
11276
11276
  }
11277
11277
  return validationResult.data;
11278
11278
  };
@@ -11767,12 +11767,12 @@ var McpServer = class {
11767
11767
  * @param errorMessage - The error message.
11768
11768
  * @returns The tool error result.
11769
11769
  */
11770
- createToolError(errorMessage) {
11770
+ createToolError(errorMessage2) {
11771
11771
  return {
11772
11772
  content: [
11773
11773
  {
11774
11774
  type: "text",
11775
- text: errorMessage
11775
+ text: errorMessage2
11776
11776
  }
11777
11777
  ],
11778
11778
  isError: true
@@ -11790,8 +11790,8 @@ var McpServer = class {
11790
11790
  const parseResult = await safeParseAsync2(schemaToParse, args);
11791
11791
  if (!parseResult.success) {
11792
11792
  const error = "error" in parseResult ? parseResult.error : "Unknown error";
11793
- const errorMessage = getParseErrorMessage(error);
11794
- throw new McpError(ErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${errorMessage}`);
11793
+ const errorMessage2 = getParseErrorMessage(error);
11794
+ throw new McpError(ErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${errorMessage2}`);
11795
11795
  }
11796
11796
  return parseResult.data;
11797
11797
  }
@@ -11815,8 +11815,8 @@ var McpServer = class {
11815
11815
  const parseResult = await safeParseAsync2(outputObj, result.structuredContent);
11816
11816
  if (!parseResult.success) {
11817
11817
  const error = "error" in parseResult ? parseResult.error : "Unknown error";
11818
- const errorMessage = getParseErrorMessage(error);
11819
- throw new McpError(ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage}`);
11818
+ const errorMessage2 = getParseErrorMessage(error);
11819
+ throw new McpError(ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage2}`);
11820
11820
  }
11821
11821
  }
11822
11822
  /**
@@ -12028,8 +12028,8 @@ var McpServer = class {
12028
12028
  const parseResult = await safeParseAsync2(argsObj, request.params.arguments);
12029
12029
  if (!parseResult.success) {
12030
12030
  const error = "error" in parseResult ? parseResult.error : "Unknown error";
12031
- const errorMessage = getParseErrorMessage(error);
12032
- throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for prompt ${request.params.name}: ${errorMessage}`);
12031
+ const errorMessage2 = getParseErrorMessage(error);
12032
+ throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for prompt ${request.params.name}: ${errorMessage2}`);
12033
12033
  }
12034
12034
  const args = parseResult.data;
12035
12035
  const cb = prompt.callback;
@@ -15276,6 +15276,10 @@ var OutlookProvider = class {
15276
15276
  // src/providers/imap/client.ts
15277
15277
  import { ImapFlow } from "imapflow";
15278
15278
  import nodemailer from "nodemailer";
15279
+ var IMAP_CONNECTION_TIMEOUT_MS = 15e3;
15280
+ var IMAP_GREETING_TIMEOUT_MS = 15e3;
15281
+ var IMAP_SOCKET_TIMEOUT_MS = 45e3;
15282
+ var IMAP_OPERATION_TIMEOUT_MS = 45e3;
15279
15283
  function isImapTokens(obj) {
15280
15284
  if (typeof obj !== "object" || obj === null) return false;
15281
15285
  const o = obj;
@@ -15290,10 +15294,12 @@ function extractTokens(account) {
15290
15294
  return account.tokens;
15291
15295
  }
15292
15296
  var ImapClient = class {
15293
- constructor(tokens) {
15297
+ constructor(tokens, opts = {}) {
15294
15298
  this.tokens = tokens;
15299
+ this.opts = opts;
15295
15300
  }
15296
15301
  tokens;
15302
+ opts;
15297
15303
  imap = null;
15298
15304
  transporter = null;
15299
15305
  connecting = null;
@@ -15309,16 +15315,28 @@ var ImapClient = class {
15309
15315
  user: this.tokens.user,
15310
15316
  pass: this.tokens.password
15311
15317
  },
15312
- logger: false
15318
+ logger: false,
15319
+ connectionTimeout: IMAP_CONNECTION_TIMEOUT_MS,
15320
+ greetingTimeout: IMAP_GREETING_TIMEOUT_MS,
15321
+ socketTimeout: IMAP_SOCKET_TIMEOUT_MS
15313
15322
  });
15314
15323
  if (!this.connecting) {
15315
- this.connecting = this.imap.connect().catch((err) => {
15316
- this.imap = null;
15317
- this.connecting = null;
15318
- throw err;
15324
+ this.log("connectStart");
15325
+ this.connecting = this.withOperationTimeout("connect", this.imap.connect()).then(() => {
15326
+ this.log("connectEnd");
15327
+ }).catch((err) => {
15328
+ const normalized = this.normalizeConnectError(err);
15329
+ this.log("connectError", {
15330
+ message: normalized.message,
15331
+ code: imapErrorCode(err) ?? null,
15332
+ authenticationFailed: hasAuthenticationFailedFlag(err)
15333
+ });
15334
+ this.resetImap();
15335
+ throw normalized;
15319
15336
  });
15320
15337
  }
15321
15338
  await this.connecting;
15339
+ if (!this.imap) throw new Error("IMAP connection unavailable after connect");
15322
15340
  return this.imap;
15323
15341
  }
15324
15342
  /** Get (or create) a nodemailer SMTP transporter. */
@@ -15345,7 +15363,17 @@ var ImapClient = class {
15345
15363
  () => void 0,
15346
15364
  () => void 0
15347
15365
  );
15348
- return run;
15366
+ try {
15367
+ return await this.withOperationTimeout("operation", run, () => {
15368
+ this.imapQueue = Promise.resolve();
15369
+ this.resetImap();
15370
+ });
15371
+ } catch (err) {
15372
+ if (err instanceof ImapTimeoutError) {
15373
+ this.log("operationTimeout", { message: err.message });
15374
+ }
15375
+ throw err;
15376
+ }
15349
15377
  }
15350
15378
  /**
15351
15379
  * Acquire a mailbox lock and run `fn` with the mailbox selected.
@@ -15376,15 +15404,101 @@ var ImapClient = class {
15376
15404
  this.transporter = null;
15377
15405
  }
15378
15406
  }
15407
+ resetImap() {
15408
+ const imap = this.imap;
15409
+ this.imap = null;
15410
+ this.connecting = null;
15411
+ if (imap) {
15412
+ imap.logout().catch(() => {
15413
+ });
15414
+ }
15415
+ }
15416
+ async withOperationTimeout(operation, promise, onTimeout) {
15417
+ let timer;
15418
+ const timeout = new Promise((_, reject) => {
15419
+ timer = setTimeout(() => {
15420
+ onTimeout?.();
15421
+ reject(new ImapTimeoutError(this.timeoutMessage(operation)));
15422
+ }, IMAP_OPERATION_TIMEOUT_MS);
15423
+ });
15424
+ try {
15425
+ return await Promise.race([promise, timeout]);
15426
+ } finally {
15427
+ if (timer) clearTimeout(timer);
15428
+ }
15429
+ }
15430
+ timeoutMessage(operation) {
15431
+ return `IMAP ${operation} timed out after ${IMAP_OPERATION_TIMEOUT_MS}ms for account ${this.accountLabel()} (${this.tokens.host}:${this.tokens.port})`;
15432
+ }
15433
+ accountLabel() {
15434
+ return this.opts.account ?? this.tokens.user;
15435
+ }
15436
+ normalizeConnectError(err) {
15437
+ if (!isImapAuthenticationError(err)) {
15438
+ return err instanceof Error ? err : new Error(String(err));
15439
+ }
15440
+ const code = imapErrorCode(err);
15441
+ const codeSuffix = code ? ` Provider error code: ${code}.` : "";
15442
+ return new Error(
15443
+ `IMAP authentication failed for account ${this.accountLabel()} (${this.tokens.host}:${this.tokens.port}). Verify the password/app-password and IMAP access policy, then re-add or update the account.` + codeSuffix,
15444
+ { cause: err }
15445
+ );
15446
+ }
15447
+ log(event, fields = {}) {
15448
+ (this.opts.logger ?? noopLogger).debug("imap", event, {
15449
+ account: this.accountLabel(),
15450
+ host: this.tokens.host,
15451
+ port: this.tokens.port,
15452
+ ...fields
15453
+ });
15454
+ }
15379
15455
  };
15456
+ var ImapTimeoutError = class extends Error {
15457
+ constructor(message) {
15458
+ super(message);
15459
+ this.name = "ImapTimeoutError";
15460
+ }
15461
+ };
15462
+ function errorMessage(err) {
15463
+ return err instanceof Error ? err.message : String(err);
15464
+ }
15465
+ function isImapAuthenticationError(err) {
15466
+ if (hasAuthenticationFailedFlag(err)) return true;
15467
+ const code = imapErrorCode(err);
15468
+ if (code === "ClosedAfterConnectTLS") return true;
15469
+ const message = errorMessage(err).toLowerCase();
15470
+ return message.includes("authentication failed") || message.includes("invalid credentials") || message.includes("login failed");
15471
+ }
15472
+ function hasAuthenticationFailedFlag(err) {
15473
+ return readErrorField(err, "authenticationFailed") === true;
15474
+ }
15475
+ function imapErrorCode(err) {
15476
+ const direct = readErrorField(err, "code");
15477
+ if (typeof direct === "string" && direct !== "NoConnection") return direct;
15478
+ const nested = readErrorField(readErrorField(err, "error"), "code");
15479
+ if (typeof nested === "string") return nested;
15480
+ if (typeof direct === "string") return direct;
15481
+ return void 0;
15482
+ }
15483
+ function readErrorField(err, field) {
15484
+ if (typeof err !== "object" || err === null) return void 0;
15485
+ return err[field];
15486
+ }
15380
15487
  var ImapClientFactory = class {
15488
+ constructor(logger = noopLogger) {
15489
+ this.logger = logger;
15490
+ }
15491
+ logger;
15381
15492
  cache = /* @__PURE__ */ new Map();
15382
15493
  get(account) {
15383
15494
  const key = account.email.toLowerCase();
15384
15495
  const existing = this.cache.get(key);
15385
15496
  if (existing) return existing;
15386
15497
  const tokens = extractTokens(account);
15387
- const client = new ImapClient(tokens);
15498
+ const client = new ImapClient(tokens, {
15499
+ account: account.email,
15500
+ logger: this.logger
15501
+ });
15388
15502
  this.cache.set(key, client);
15389
15503
  return client;
15390
15504
  }
@@ -16155,12 +16269,13 @@ async function deleteFolder2(clients, account, folderId) {
16155
16269
 
16156
16270
  // src/providers/imap/index.ts
16157
16271
  var ImapProvider = class {
16158
- constructor(store) {
16272
+ constructor(store, logger = noopLogger) {
16159
16273
  this.store = store;
16274
+ this.clients = new ImapClientFactory(logger);
16160
16275
  }
16161
16276
  store;
16162
16277
  id = "imap";
16163
- clients = new ImapClientFactory();
16278
+ clients;
16164
16279
  // ---------- account lifecycle ----------
16165
16280
  async addAccount(input) {
16166
16281
  if (!this.store) throw new Error("IMAP provider requires an AccountStore");
@@ -17345,7 +17460,7 @@ function buildRegistry(opts) {
17345
17460
  clientId: outlookCfg?.clientId,
17346
17461
  tenantId: outlookCfg?.tenantId
17347
17462
  }));
17348
- providers.set("imap", new ImapProvider(opts.store));
17463
+ providers.set("imap", new ImapProvider(opts.store, opts.logger));
17349
17464
  const gmailCfg = opts.providers?.gmail;
17350
17465
  providers.set("gmail", new GmailProvider({
17351
17466
  store: opts.store,
@@ -17820,10 +17935,46 @@ function selectBody(msg, format) {
17820
17935
 
17821
17936
  // src/tools/new-emails.ts
17822
17937
  import { z as z4 } from "zod";
17938
+
17939
+ // src/tools/new-emails-timeout.ts
17940
+ var ACCOUNT_POLL_TIMEOUT_MS = 45e3;
17941
+ var MISSING_RECEIVED_AT = "1970-01-01T00:00:00.000Z";
17942
+ function effectiveReceivedAt(receivedAt) {
17943
+ return normalizeTimestamp2(receivedAt) ?? MISSING_RECEIVED_AT;
17944
+ }
17945
+ function normalizeTimestamp2(value) {
17946
+ if (!value) return null;
17947
+ const ms = Date.parse(value);
17948
+ if (!Number.isFinite(ms)) return null;
17949
+ return new Date(ms).toISOString();
17950
+ }
17951
+ function compareTimestamp2(a, b) {
17952
+ if (a < b) return -1;
17953
+ if (a > b) return 1;
17954
+ return 0;
17955
+ }
17956
+ async function withAccountPollTimeout(account, operation, promise) {
17957
+ let timer;
17958
+ const timeout = new Promise((_, reject) => {
17959
+ timer = setTimeout(() => {
17960
+ reject(
17961
+ new Error(
17962
+ `${operation} timed out after ${ACCOUNT_POLL_TIMEOUT_MS}ms for account ${account}`
17963
+ )
17964
+ );
17965
+ }, ACCOUNT_POLL_TIMEOUT_MS);
17966
+ });
17967
+ try {
17968
+ return await Promise.race([promise, timeout]);
17969
+ } finally {
17970
+ if (timer) clearTimeout(timer);
17971
+ }
17972
+ }
17973
+
17974
+ // src/tools/new-emails.ts
17823
17975
  var DEFAULT_LIMIT = 10;
17824
17976
  var BODY_LIMIT = 2e4;
17825
17977
  var PAGE_SIZE = 100;
17826
- var MISSING_RECEIVED_AT = "1970-01-01T00:00:00.000Z";
17827
17978
  function registerNewEmailTool(server, ctx) {
17828
17979
  const { store, registry, tools, logger = noopLogger } = ctx;
17829
17980
  if (!shouldRegister("get_new_emails", tools)) return;
@@ -17874,7 +18025,11 @@ function registerNewEmailTool(server, ctx) {
17874
18025
  if (args.account) {
17875
18026
  try {
17876
18027
  const { provider, account } = registry.resolveByEmail(args.account);
17877
- const result = await collectCandidatesForAccount(store, provider, account, logger);
18028
+ const result = await withAccountPollTimeout(
18029
+ account.email,
18030
+ "collect new-email candidates",
18031
+ collectCandidatesForAccount(store, provider, account, logger)
18032
+ );
17878
18033
  const selected2 = oldestCandidatesFirst(result.candidates).slice(0, limit);
17879
18034
  logger.debug("get-new-emails", "selected", {
17880
18035
  account: result.account.email,
@@ -17884,7 +18039,11 @@ function registerNewEmailTool(server, ctx) {
17884
18039
  selectedReceivedAt: selected2.map((candidate) => candidate.timestamp),
17885
18040
  limit
17886
18041
  });
17887
- const emails2 = limit === 0 ? [] : await hydrateAndAdvance(store, provider, result.account, selected2, logger);
18042
+ const emails2 = limit === 0 ? [] : await withAccountPollTimeout(
18043
+ result.account.email,
18044
+ "hydrate new emails",
18045
+ hydrateAndAdvance(store, provider, result.account, selected2, logger)
18046
+ );
17888
18047
  const data2 = { count: emails2.length, emails: emails2, errors: [] };
17889
18048
  logger.debug("get-new-emails", "end", {
17890
18049
  account: result.account.email,
@@ -17917,7 +18076,11 @@ function registerNewEmailTool(server, ctx) {
17917
18076
  accounts.map(async (stored) => {
17918
18077
  try {
17919
18078
  const { provider, account } = registry.resolveByEmail(stored.email);
17920
- const result = await collectCandidatesForAccount(store, provider, account, logger);
18079
+ const result = await withAccountPollTimeout(
18080
+ account.email,
18081
+ "collect new-email candidates",
18082
+ collectCandidatesForAccount(store, provider, account, logger)
18083
+ );
17921
18084
  return {
17922
18085
  status: "ok",
17923
18086
  account: result.account,
@@ -17969,7 +18132,11 @@ function registerNewEmailTool(server, ctx) {
17969
18132
  try {
17970
18133
  return {
17971
18134
  status: "ok",
17972
- emails: await hydrateAndAdvance(store, provider, account, accountCandidates, logger)
18135
+ emails: await withAccountPollTimeout(
18136
+ email,
18137
+ "hydrate new emails",
18138
+ hydrateAndAdvance(store, provider, account, accountCandidates, logger)
18139
+ )
17973
18140
  };
17974
18141
  } catch (err) {
17975
18142
  const message = errMsg(err);
@@ -18138,15 +18305,6 @@ function normalizeCheckpoint2(checkpoint) {
18138
18305
  deliveredIdsAtReceivedAt: checkpoint?.deliveredIdsAtReceivedAt ?? []
18139
18306
  };
18140
18307
  }
18141
- function effectiveReceivedAt(receivedAt) {
18142
- return normalizeTimestamp2(receivedAt) ?? MISSING_RECEIVED_AT;
18143
- }
18144
- function normalizeTimestamp2(value) {
18145
- if (!value) return null;
18146
- const ms = Date.parse(value);
18147
- if (!Number.isFinite(ms)) return null;
18148
- return new Date(ms).toISOString();
18149
- }
18150
18308
  function oldestCandidatesFirst(items) {
18151
18309
  return [...items].sort((a, b) => {
18152
18310
  const byTimestamp = compareTimestamp2(a.timestamp, b.timestamp);
@@ -18162,11 +18320,6 @@ function compareNewEmailOutputOldestFirst(a, b) {
18162
18320
  if (byTimestamp !== 0) return byTimestamp;
18163
18321
  return a.id.localeCompare(b.id);
18164
18322
  }
18165
- function compareTimestamp2(a, b) {
18166
- if (a < b) return -1;
18167
- if (a > b) return 1;
18168
- return 0;
18169
- }
18170
18323
 
18171
18324
  // src/tools/browse.ts
18172
18325
  function registerBrowseTools(server, ctx) {
@@ -19138,7 +19291,7 @@ function registerTools(server, opts) {
19138
19291
  // package.json
19139
19292
  var package_default = {
19140
19293
  name: "hypermail-mcp",
19141
- version: "0.7.19",
19294
+ version: "0.7.20",
19142
19295
  description: "Unified email MCP server \u2014 operate any inbox (Outlook now, IMAP/Gmail later) by passing an email address.",
19143
19296
  type: "module",
19144
19297
  bin: {
@@ -19419,7 +19572,7 @@ async function startServer(opts) {
19419
19572
  toolsDisabled: config.tools?.disabled ?? null
19420
19573
  });
19421
19574
  const store = await AccountStore.open({ dataDir, logger });
19422
- const registry = buildRegistry({ store, providers: config.providers });
19575
+ const registry = buildRegistry({ store, providers: config.providers, logger });
19423
19576
  const tools = resolveTools(config);
19424
19577
  const createServer = () => {
19425
19578
  const s = new McpServer(