hypermail-mcp 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -9,7 +9,6 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
9
9
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
10
10
  import { randomUUID as randomUUID6 } from "crypto";
11
11
  import { createServer as createHttpServer } from "http";
12
- import path4 from "path";
13
12
 
14
13
  // src/store/account-store.ts
15
14
  import { promises as fs2 } from "fs";
@@ -86,23 +85,6 @@ async function resolveKey(dataDir) {
86
85
  await tryKeytarSet(gen);
87
86
  return gen;
88
87
  }
89
- function hashApiKey(apiKey) {
90
- const salt = randomBytes(16).toString("hex");
91
- const hash = scryptSync(apiKey, salt, 32).toString("hex");
92
- return `${salt}:${hash}`;
93
- }
94
- function verifyApiKey(apiKey, stored) {
95
- const [salt, hash] = stored.split(":");
96
- if (!salt || !hash) return false;
97
- try {
98
- const computed = scryptSync(apiKey, salt, 32);
99
- const expected = Buffer.from(hash, "hex");
100
- if (computed.length !== expected.length) return false;
101
- return timingSafeEqual(computed, expected);
102
- } catch {
103
- return false;
104
- }
105
- }
106
88
  async function writeAtomic(filePath, data) {
107
89
  const tmp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
108
90
  await fs.writeFile(tmp, data, { mode: 384 });
@@ -188,128 +170,6 @@ var AccountStore = class _AccountStore {
188
170
  }
189
171
  };
190
172
 
191
- // src/store/agent-store.ts
192
- import path3 from "path";
193
- import { promises as fs3 } from "fs";
194
- var FILE_NAME2 = "agents.json.enc";
195
- var AgentStore = class _AgentStore {
196
- constructor(filePath, key, data) {
197
- this.filePath = filePath;
198
- this.key = key;
199
- this.data = data;
200
- }
201
- filePath;
202
- key;
203
- data;
204
- static async open(opts = {}) {
205
- const dataDir = resolveDataDir(opts.dataDir);
206
- await fs3.mkdir(dataDir, { recursive: true, mode: 448 });
207
- const filePath = path3.join(dataDir, FILE_NAME2);
208
- const key = opts.key ?? await resolveKey(dataDir);
209
- let data;
210
- try {
211
- const buf = await fs3.readFile(filePath);
212
- data = decrypt(buf, key);
213
- } catch (err) {
214
- if (err.code === "ENOENT") {
215
- data = { version: 1, agents: [] };
216
- } else {
217
- throw err;
218
- }
219
- }
220
- return new _AgentStore(filePath, key, data);
221
- }
222
- // ── queries ──
223
- listAgents() {
224
- return this.data.agents.map((a) => ({ ...a }));
225
- }
226
- getAgent(id) {
227
- const rec = this.data.agents.find((a) => a.id === id);
228
- return rec ? { ...rec } : void 0;
229
- }
230
- /**
231
- * Look up an agent by plaintext API key. Hashes the incoming key and
232
- * compares against stored hashes with constant-time comparison.
233
- * Returns undefined if no agent matches.
234
- */
235
- findAgentByApiKey(apiKey) {
236
- for (const agent of this.data.agents) {
237
- if (verifyApiKey(apiKey, agent.apiKeyHash)) {
238
- return { ...agent };
239
- }
240
- }
241
- return void 0;
242
- }
243
- // ── mutations ──
244
- /**
245
- * Add or update an agent. If `plaintextApiKey` is provided, it is hashed
246
- * and stored; if omitted, the existing hash is preserved (useful for
247
- * updates that don't change the key).
248
- */
249
- async upsertAgent(rec) {
250
- const idx = this.data.agents.findIndex((a) => a.id === rec.id);
251
- const existing = idx >= 0 ? this.data.agents[idx] : void 0;
252
- const apiKeyHash = rec.plaintextApiKey ? hashApiKey(rec.plaintextApiKey) : existing?.apiKeyHash;
253
- if (!apiKeyHash) {
254
- throw new Error(
255
- `agent ${rec.id}: must provide plaintextApiKey for new agents`
256
- );
257
- }
258
- const next = {
259
- id: rec.id,
260
- apiKeyHash,
261
- name: rec.name,
262
- accounts: [...rec.accounts ?? []],
263
- provisioning: rec.provisioning ?? false,
264
- createdAt: existing?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
265
- };
266
- if (idx >= 0) {
267
- this.data.agents[idx] = next;
268
- } else {
269
- this.data.agents.push(next);
270
- }
271
- await this.flush();
272
- return { ...next };
273
- }
274
- async removeAgent(id) {
275
- const before = this.data.agents.length;
276
- this.data.agents = this.data.agents.filter((a) => a.id !== id);
277
- if (this.data.agents.length === before) return false;
278
- await this.flush();
279
- return true;
280
- }
281
- /**
282
- * Assign an email account to an agent. Idempotent — no error if already
283
- * assigned. Auto-assignment from `add_account` in HTTP mode calls this.
284
- */
285
- async assignAccount(agentId, email) {
286
- const norm = email.trim().toLowerCase();
287
- const agent = this.data.agents.find((a) => a.id === agentId);
288
- if (!agent) throw new Error(`agent ${agentId} not found`);
289
- if (!agent.accounts.includes(norm)) {
290
- agent.accounts.push(norm);
291
- await this.flush();
292
- }
293
- return { ...agent };
294
- }
295
- /**
296
- * Remove an email account from an agent's assignments. Idempotent.
297
- */
298
- async unassignAccount(agentId, email) {
299
- const norm = email.trim().toLowerCase();
300
- const agent = this.data.agents.find((a) => a.id === agentId);
301
- if (!agent) throw new Error(`agent ${agentId} not found`);
302
- agent.accounts = agent.accounts.filter((a) => a !== norm);
303
- await this.flush();
304
- return { ...agent };
305
- }
306
- // ── persistence ──
307
- async flush() {
308
- const buf = encrypt(this.data, this.key);
309
- await writeAtomic(this.filePath, buf);
310
- }
311
- };
312
-
313
173
  // src/providers/outlook/index.ts
314
174
  import { randomUUID as randomUUID2 } from "crypto";
315
175
  import { writeFileSync } from "fs";
@@ -1565,8 +1425,8 @@ async function markRead(clients, account, id, isRead) {
1565
1425
  async function createFolder(clients, account, input) {
1566
1426
  const client = clients.get(account);
1567
1427
  const imap = await client.getImap();
1568
- const path5 = input.parentFolderId ? `${input.parentFolderId}/${input.displayName}` : input.displayName;
1569
- const result = await imap.mailboxCreate(path5);
1428
+ const path3 = input.parentFolderId ? `${input.parentFolderId}/${input.displayName}` : input.displayName;
1429
+ const result = await imap.mailboxCreate(path3);
1570
1430
  return {
1571
1431
  id: result.path,
1572
1432
  displayName: result.path,
@@ -2754,27 +2614,10 @@ function shouldRegister(name, tools) {
2754
2614
  }
2755
2615
 
2756
2616
  // src/tools/accounts.ts
2757
- import { promises as fs4 } from "fs";
2617
+ import { promises as fs3 } from "fs";
2758
2618
  import { z as z2 } from "zod";
2759
-
2760
- // src/tools/agent-context.ts
2761
- function checkAccountAccess(agentContext, accountEmail) {
2762
- if (!agentContext) return null;
2763
- const norm = accountEmail.trim().toLowerCase();
2764
- if (agentContext.accounts.some((a) => a.toLowerCase() === norm)) {
2765
- return null;
2766
- }
2767
- return `Agent "${agentContext.agentId}" is not authorized for account "${accountEmail}"`;
2768
- }
2769
- function checkProvisioning(agentContext) {
2770
- if (!agentContext) return null;
2771
- if (agentContext.provisioning) return null;
2772
- return `Agent "${agentContext.agentId}" does not have provisioning permission`;
2773
- }
2774
-
2775
- // src/tools/accounts.ts
2776
2619
  function registerAccountTools(server, ctx) {
2777
- const { store, registry, tools, agentContext, agentStore } = ctx;
2620
+ const { store, registry, tools } = ctx;
2778
2621
  const listAccountsOutputSchema = z2.object({
2779
2622
  accounts: z2.array(accountSummaryOutputSchema)
2780
2623
  });
@@ -2828,8 +2671,6 @@ function registerAccountTools(server, ctx) {
2828
2671
  outputSchema: addAccountOutputSchema
2829
2672
  },
2830
2673
  async (args) => {
2831
- const permErr = checkProvisioning(agentContext ?? null);
2832
- if (permErr) return fail(permErr);
2833
2674
  const provider = registry.get(args.provider);
2834
2675
  try {
2835
2676
  const res = await provider.addAccount({
@@ -2860,8 +2701,6 @@ function registerAccountTools(server, ctx) {
2860
2701
  outputSchema: completeAddAccountOutputSchema
2861
2702
  },
2862
2703
  async (args) => {
2863
- const permErr = checkProvisioning(agentContext ?? null);
2864
- if (permErr) return fail(permErr);
2865
2704
  const provider = registry.get(args.provider);
2866
2705
  if (!provider.completeAddAccount) {
2867
2706
  return fail(
@@ -2870,10 +2709,6 @@ function registerAccountTools(server, ctx) {
2870
2709
  }
2871
2710
  try {
2872
2711
  const res = await provider.completeAddAccount(args.handle);
2873
- if (res.status === "ready" && res.account && agentContext && agentStore) {
2874
- agentStore.assignAccount(agentContext.agentId, res.account.email).catch(() => {
2875
- });
2876
- }
2877
2712
  return ok(res, res);
2878
2713
  } catch (err) {
2879
2714
  return fail(errMsg(err));
@@ -2895,8 +2730,6 @@ function registerAccountTools(server, ctx) {
2895
2730
  },
2896
2731
  async (args) => {
2897
2732
  try {
2898
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
2899
- if (accessErr) return fail(accessErr);
2900
2733
  const acct = store.getAccount(args.account);
2901
2734
  if (!acct)
2902
2735
  return fail(`no account registered for "${args.account}"`);
@@ -2941,14 +2774,12 @@ function registerAccountTools(server, ctx) {
2941
2774
  },
2942
2775
  async (args) => {
2943
2776
  try {
2944
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
2945
- if (accessErr) return fail(accessErr);
2946
2777
  const acct = store.getAccount(args.account);
2947
2778
  if (!acct)
2948
2779
  return fail(`no account registered for "${args.account}"`);
2949
2780
  let resolvedSignature = acct.signature;
2950
2781
  if (args.signaturePath) {
2951
- resolvedSignature = await fs4.readFile(args.signaturePath, "utf-8");
2782
+ resolvedSignature = await fs3.readFile(args.signaturePath, "utf-8");
2952
2783
  } else if (args.signature !== void 0) {
2953
2784
  resolvedSignature = args.signature || void 0;
2954
2785
  }
@@ -2981,8 +2812,6 @@ function registerAccountTools(server, ctx) {
2981
2812
  outputSchema: removeAccountOutputSchema
2982
2813
  },
2983
2814
  async (args) => {
2984
- const permErr = checkProvisioning(agentContext ?? null);
2985
- if (permErr) return fail(permErr);
2986
2815
  const removed = await store.removeAccount(args.email);
2987
2816
  const data = { removed, email: args.email };
2988
2817
  return ok(data, data);
@@ -3022,7 +2851,7 @@ function selectBody(msg, format) {
3022
2851
 
3023
2852
  // src/tools/browse.ts
3024
2853
  function registerBrowseTools(server, ctx) {
3025
- const { registry, tools, agentContext } = ctx;
2854
+ const { registry, tools } = ctx;
3026
2855
  const emailListOutputSchema = z3.object({
3027
2856
  account: z3.string(),
3028
2857
  count: z3.number(),
@@ -3051,8 +2880,6 @@ function registerBrowseTools(server, ctx) {
3051
2880
  },
3052
2881
  async (args) => {
3053
2882
  try {
3054
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
3055
- if (accessErr) return fail(accessErr);
3056
2883
  const { provider, account } = registry.resolveByEmail(args.account);
3057
2884
  const { items, hasMore } = await provider.listEmails(account, {
3058
2885
  folder: args.folder,
@@ -3088,8 +2915,6 @@ function registerBrowseTools(server, ctx) {
3088
2915
  },
3089
2916
  async (args) => {
3090
2917
  try {
3091
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
3092
- if (accessErr) return fail(accessErr);
3093
2918
  const { provider, account } = registry.resolveByEmail(args.account);
3094
2919
  const items = await provider.searchEmails(account, args.query, {
3095
2920
  limit: args.limit
@@ -3138,8 +2963,6 @@ function registerBrowseTools(server, ctx) {
3138
2963
  },
3139
2964
  async (args) => {
3140
2965
  try {
3141
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
3142
- if (accessErr) return fail(accessErr);
3143
2966
  const { provider, account } = registry.resolveByEmail(args.account);
3144
2967
  const msg = await provider.readEmail(account, args.id);
3145
2968
  const format = args.format ?? "markdown";
@@ -3186,8 +3009,6 @@ function registerBrowseTools(server, ctx) {
3186
3009
  },
3187
3010
  async (args) => {
3188
3011
  try {
3189
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
3190
- if (accessErr) return fail(accessErr);
3191
3012
  const { provider, account } = registry.resolveByEmail(args.account);
3192
3013
  const res = await provider.readAttachment(
3193
3014
  account,
@@ -3206,7 +3027,7 @@ function registerBrowseTools(server, ctx) {
3206
3027
  // src/tools/folders.ts
3207
3028
  import { z as z4 } from "zod";
3208
3029
  function registerFolderTools(server, ctx) {
3209
- const { registry, tools, agentContext } = ctx;
3030
+ const { registry, tools } = ctx;
3210
3031
  const listFoldersOutputSchema = z4.object({
3211
3032
  account: z4.string(),
3212
3033
  count: z4.number(),
@@ -3227,8 +3048,6 @@ function registerFolderTools(server, ctx) {
3227
3048
  },
3228
3049
  async (args) => {
3229
3050
  try {
3230
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
3231
- if (accessErr) return fail(accessErr);
3232
3051
  const { provider, account } = registry.resolveByEmail(args.account);
3233
3052
  const items = await provider.listFolders(account, {
3234
3053
  parentFolderId: args.parentFolderId
@@ -3265,8 +3084,6 @@ function registerFolderTools(server, ctx) {
3265
3084
  },
3266
3085
  async (args) => {
3267
3086
  try {
3268
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
3269
- if (accessErr) return fail(accessErr);
3270
3087
  const { provider, account } = registry.resolveByEmail(args.account);
3271
3088
  const folder = await provider.createFolder(account, {
3272
3089
  displayName: args.displayName,
@@ -3297,8 +3114,6 @@ function registerFolderTools(server, ctx) {
3297
3114
  },
3298
3115
  async (args) => {
3299
3116
  try {
3300
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
3301
- if (accessErr) return fail(accessErr);
3302
3117
  const { provider, account } = registry.resolveByEmail(args.account);
3303
3118
  await provider.deleteFolder(account, args.folderId);
3304
3119
  const data = { deleted: true, id: args.folderId };
@@ -3327,8 +3142,6 @@ function registerFolderTools(server, ctx) {
3327
3142
  },
3328
3143
  async (args) => {
3329
3144
  try {
3330
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
3331
- if (accessErr) return fail(accessErr);
3332
3145
  const { provider, account } = registry.resolveByEmail(args.account);
3333
3146
  const folder = await provider.renameFolder(
3334
3147
  account,
@@ -3348,7 +3161,7 @@ function registerFolderTools(server, ctx) {
3348
3161
  // src/tools/organize.ts
3349
3162
  import { z as z5 } from "zod";
3350
3163
  function registerOrganizeTools(server, ctx) {
3351
- const { registry, tools, agentContext } = ctx;
3164
+ const { registry, tools } = ctx;
3352
3165
  async function moveToWellKnown(args, destination, resultKey) {
3353
3166
  const { provider, account } = registry.resolveByEmail(args.account);
3354
3167
  await provider.moveEmail(account, args.id, destination);
@@ -3380,8 +3193,6 @@ function registerOrganizeTools(server, ctx) {
3380
3193
  },
3381
3194
  async (args) => {
3382
3195
  try {
3383
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
3384
- if (accessErr) return fail(accessErr);
3385
3196
  return await moveToWellKnown(args, "archive", "archived");
3386
3197
  } catch (err) {
3387
3198
  return fail(errMsg(err));
@@ -3403,8 +3214,6 @@ function registerOrganizeTools(server, ctx) {
3403
3214
  },
3404
3215
  async (args) => {
3405
3216
  try {
3406
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
3407
- if (accessErr) return fail(accessErr);
3408
3217
  return await moveToWellKnown(args, "deleteditems", "trashed");
3409
3218
  } catch (err) {
3410
3219
  return fail(errMsg(err));
@@ -3433,8 +3242,6 @@ function registerOrganizeTools(server, ctx) {
3433
3242
  },
3434
3243
  async (args) => {
3435
3244
  try {
3436
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
3437
- if (accessErr) return fail(accessErr);
3438
3245
  const { provider, account } = registry.resolveByEmail(args.account);
3439
3246
  await provider.moveEmail(account, args.id, args.destination);
3440
3247
  const data = {
@@ -3468,8 +3275,6 @@ function registerOrganizeTools(server, ctx) {
3468
3275
  },
3469
3276
  async (args) => {
3470
3277
  try {
3471
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
3472
- if (accessErr) return fail(accessErr);
3473
3278
  return await markReadState(args, true);
3474
3279
  } catch (err) {
3475
3280
  return fail(errMsg(err));
@@ -3487,8 +3292,6 @@ function registerOrganizeTools(server, ctx) {
3487
3292
  },
3488
3293
  async (args) => {
3489
3294
  try {
3490
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
3491
- if (accessErr) return fail(accessErr);
3492
3295
  return await markReadState(args, false);
3493
3296
  } catch (err) {
3494
3297
  return fail(errMsg(err));
@@ -3501,7 +3304,7 @@ function registerOrganizeTools(server, ctx) {
3501
3304
  // src/tools/compose.ts
3502
3305
  import { z as z6 } from "zod";
3503
3306
  function registerComposeTools(server, ctx) {
3504
- const { store, registry, tools, agentContext } = ctx;
3307
+ const { store, registry, tools } = ctx;
3505
3308
  const sendEmailSchema = z6.object({
3506
3309
  account: z6.string().email(),
3507
3310
  to: z6.array(emailAddrSchema).min(1),
@@ -3527,8 +3330,6 @@ function registerComposeTools(server, ctx) {
3527
3330
  });
3528
3331
  async function handleSendOrDraft(args, action, resultKey, toolName) {
3529
3332
  try {
3530
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
3531
- if (accessErr) return fail(accessErr);
3532
3333
  const { provider, account } = registry.resolveByEmail(args.account);
3533
3334
  if (args.include_signature && !account.signature) {
3534
3335
  return fail(
@@ -3640,8 +3441,6 @@ function registerComposeTools(server, ctx) {
3640
3441
  async (args) => {
3641
3442
  const a = args;
3642
3443
  try {
3643
- const accessErr = checkAccountAccess(agentContext ?? null, a.account);
3644
- if (accessErr) return fail(accessErr);
3645
3444
  const { provider, account } = registry.resolveByEmail(a.account);
3646
3445
  if (a.include_signature && !account.signature) {
3647
3446
  return fail(
@@ -3699,8 +3498,6 @@ function registerComposeTools(server, ctx) {
3699
3498
  },
3700
3499
  async (args) => {
3701
3500
  try {
3702
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
3703
- if (accessErr) return fail(accessErr);
3704
3501
  const { provider, account } = registry.resolveByEmail(args.account);
3705
3502
  const res = await provider.sendDraft(account, args.id);
3706
3503
  const data = { sent: true, id: res.id };
@@ -3736,8 +3533,6 @@ function registerComposeTools(server, ctx) {
3736
3533
  },
3737
3534
  async (args) => {
3738
3535
  try {
3739
- const accessErr = checkAccountAccess(agentContext ?? null, args.account);
3740
- if (accessErr) return fail(accessErr);
3741
3536
  const { provider, account } = registry.resolveByEmail(args.account);
3742
3537
  const res = await provider.addAttachmentToDraft(
3743
3538
  account,
@@ -3760,69 +3555,20 @@ function registerComposeTools(server, ctx) {
3760
3555
  }
3761
3556
  }
3762
3557
 
3763
- // src/tools/notifications.ts
3764
- import { z as z7 } from "zod";
3765
- function registerNotificationTools(server, ctx) {
3766
- const { tools, notificationBuffer, agentContext } = ctx;
3767
- const notifyOutputSchema = z7.object({
3768
- count: z7.number(),
3769
- items: z7.array(
3770
- z7.object({
3771
- type: z7.enum(["new_emails", "auth_failure"]),
3772
- account: z7.string(),
3773
- emails: z7.array(z7.unknown()).optional(),
3774
- error: z7.string().optional(),
3775
- timestamp: z7.string()
3776
- })
3777
- )
3778
- });
3779
- if (shouldRegister("check_notifications", tools)) {
3780
- server.registerTool(
3781
- "check_notifications",
3782
- {
3783
- description: "Check for pending email watch notifications. Returns new-email alerts and auth-failure warnings that the inbox watcher has accumulated since the last call. Drains the notification buffer on read.",
3784
- inputSchema: z7.object({}),
3785
- outputSchema: notifyOutputSchema
3786
- },
3787
- async () => {
3788
- try {
3789
- const pending = notificationBuffer.splice(0);
3790
- const filtered = agentContext ? pending.filter(
3791
- (n) => agentContext.accounts.some(
3792
- (a) => a.toLowerCase() === n.account.toLowerCase()
3793
- )
3794
- ) : pending;
3795
- const data = { count: filtered.length, items: filtered };
3796
- return ok(data, data);
3797
- } catch (err) {
3798
- return fail(errMsg(err));
3799
- }
3800
- }
3801
- );
3802
- }
3803
- }
3804
-
3805
3558
  // src/tools/index.ts
3806
3559
  function registerTools(server, opts) {
3807
- const { store, registry, tools, agentContext, agentStore } = opts;
3808
- registerAccountTools(server, { store, registry, tools, agentContext, agentStore });
3809
- registerBrowseTools(server, { registry, tools, agentContext });
3810
- registerFolderTools(server, { registry, tools, agentContext });
3811
- registerOrganizeTools(server, { registry, tools, agentContext });
3812
- registerComposeTools(server, { store, registry, tools, agentContext });
3813
- if (opts.notificationBuffer) {
3814
- registerNotificationTools(server, {
3815
- tools,
3816
- notificationBuffer: opts.notificationBuffer,
3817
- agentContext
3818
- });
3819
- }
3560
+ const { store, registry, tools } = opts;
3561
+ registerAccountTools(server, { store, registry, tools });
3562
+ registerBrowseTools(server, { registry, tools });
3563
+ registerFolderTools(server, { registry, tools });
3564
+ registerOrganizeTools(server, { registry, tools });
3565
+ registerComposeTools(server, { store, registry, tools });
3820
3566
  }
3821
3567
 
3822
3568
  // package.json
3823
3569
  var package_default = {
3824
3570
  name: "hypermail-mcp",
3825
- version: "0.6.2",
3571
+ version: "0.7.0",
3826
3572
  description: "Unified email MCP server \u2014 operate any inbox (Outlook now, IMAP/Gmail later) by passing an email address.",
3827
3573
  type: "module",
3828
3574
  bin: {
@@ -3871,7 +3617,6 @@ var package_default = {
3871
3617
  googleapis: "^144.0.0",
3872
3618
  imapflow: "^1.3.3",
3873
3619
  "isomorphic-fetch": "^3.0.0",
3874
- "js-yaml": "^4.2.0",
3875
3620
  marked: "^18.0.4",
3876
3621
  nodemailer: "^8.0.8",
3877
3622
  turndown: "^7.2.4",
@@ -3882,7 +3627,6 @@ var package_default = {
3882
3627
  },
3883
3628
  devDependencies: {
3884
3629
  "@types/isomorphic-fetch": "^0.0.39",
3885
- "@types/js-yaml": "^4.0.9",
3886
3630
  "@types/node": "^22.10.2",
3887
3631
  "@types/nodemailer": "^8.0.0",
3888
3632
  tsup: "^8.3.5",
@@ -3894,36 +3638,153 @@ var package_default = {
3894
3638
  // src/version.ts
3895
3639
  var VERSION = package_default.version;
3896
3640
 
3641
+ // src/watcher/webhook.ts
3642
+ async function postWebhook(email, config) {
3643
+ if (!config.webhook) return false;
3644
+ const { url, retry } = config.webhook;
3645
+ const maxAttempts = retry.maxAttempts;
3646
+ const baseDelayMs = retry.baseDelayMs;
3647
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
3648
+ if (attempt > 0) {
3649
+ const delay = baseDelayMs * 2 ** (attempt - 1);
3650
+ await sleep(delay);
3651
+ }
3652
+ try {
3653
+ const res = await fetch(url, {
3654
+ method: "POST",
3655
+ headers: { "content-type": "application/json" },
3656
+ body: JSON.stringify(email)
3657
+ });
3658
+ if (res.ok) return true;
3659
+ console.error(
3660
+ `[hypermail-watch] webhook POST ${email.id} attempt ${attempt + 1}/${maxAttempts}: HTTP ${res.status}`
3661
+ );
3662
+ } catch (err) {
3663
+ const code = err.code ?? "";
3664
+ console.error(
3665
+ `[hypermail-watch] webhook POST ${email.id} attempt ${attempt + 1}/${maxAttempts}: ${code || String(err)}`
3666
+ );
3667
+ }
3668
+ }
3669
+ console.error(
3670
+ `[hypermail-watch] webhook delivery failed after ${maxAttempts} retries for ${email.id}`
3671
+ );
3672
+ return false;
3673
+ }
3674
+ function sleep(ms) {
3675
+ return new Promise((resolve) => setTimeout(resolve, ms));
3676
+ }
3677
+
3678
+ // src/watcher/manager.ts
3679
+ var WatcherManager = class {
3680
+ constructor(store, registry, config) {
3681
+ this.store = store;
3682
+ this.registry = registry;
3683
+ this.config = config;
3684
+ }
3685
+ store;
3686
+ registry;
3687
+ config;
3688
+ intervalId = null;
3689
+ /** Start the poll loop. Fires immediately on the first tick, then every
3690
+ * `pollIntervalSeconds`. Safe to call multiple times — subsequent calls
3691
+ * are no-ops. */
3692
+ start() {
3693
+ if (this.intervalId !== null) return;
3694
+ this.poll();
3695
+ this.intervalId = setInterval(
3696
+ () => this.poll(),
3697
+ this.config.pollIntervalSeconds * 1e3
3698
+ );
3699
+ }
3700
+ /** Stop the poll loop and release the interval. Safe to call when already
3701
+ * stopped. */
3702
+ stop() {
3703
+ if (this.intervalId !== null) {
3704
+ clearInterval(this.intervalId);
3705
+ this.intervalId = null;
3706
+ }
3707
+ }
3708
+ // ── private ──
3709
+ async poll() {
3710
+ const accounts = this.store.listAccounts();
3711
+ for (const acct of accounts) {
3712
+ try {
3713
+ await this.pollAccount(acct.email);
3714
+ } catch (err) {
3715
+ console.error(
3716
+ `[hypermail-watch] poll failed for ${acct.email}:`,
3717
+ err
3718
+ );
3719
+ }
3720
+ }
3721
+ }
3722
+ async pollAccount(email) {
3723
+ const { provider, account } = this.registry.resolveByEmail(email);
3724
+ const result = await provider.listEmails(account, {
3725
+ folder: "inbox",
3726
+ limit: 50
3727
+ });
3728
+ const knownIds = [...account.lastSeenIds ?? []];
3729
+ const newEmails = result.items.filter((e) => !knownIds.includes(e.id));
3730
+ if (newEmails.length === 0) return;
3731
+ for (const summary of newEmails) {
3732
+ try {
3733
+ const full = await provider.readEmail(account, summary.id);
3734
+ await this.emit(full);
3735
+ knownIds.unshift(summary.id);
3736
+ } catch (err) {
3737
+ console.error(
3738
+ `[hypermail-watch] emission failed for ${email}/${summary.id}:`,
3739
+ err
3740
+ );
3741
+ }
3742
+ }
3743
+ const capped = knownIds.slice(0, 200);
3744
+ await this.store.upsertAccount({ ...account, lastSeenIds: capped });
3745
+ }
3746
+ async emit(full) {
3747
+ await postWebhook(full, this.config);
3748
+ }
3749
+ };
3750
+
3897
3751
  // src/config.ts
3898
3752
  import { readFileSync } from "fs";
3899
- import { z as z8 } from "zod";
3900
- var httpConfigSchema = z8.object({
3901
- enabled: z8.boolean().default(false),
3902
- port: z8.number().int().min(1).max(65535).default(3e3),
3903
- host: z8.string().default("127.0.0.1")
3753
+ import { z as z7 } from "zod";
3754
+ var httpConfigSchema = z7.object({
3755
+ enabled: z7.boolean().default(false),
3756
+ port: z7.number().int().min(1).max(65535).default(3e3),
3757
+ host: z7.string().default("127.0.0.1")
3904
3758
  });
3905
- var toolsConfigSchema = z8.object({
3906
- disabled: z8.array(z8.string()).optional(),
3907
- enabled: z8.array(z8.string()).optional()
3759
+ var toolsConfigSchema = z7.object({
3760
+ disabled: z7.array(z7.string()).optional(),
3761
+ enabled: z7.array(z7.string()).optional()
3908
3762
  });
3909
- var outlookProviderSchema = z8.object({
3910
- clientId: z8.string().optional(),
3911
- tenantId: z8.string().optional()
3763
+ var outlookProviderSchema = z7.object({
3764
+ clientId: z7.string().optional(),
3765
+ tenantId: z7.string().optional()
3912
3766
  });
3913
- var gmailProviderSchema = z8.object({
3914
- clientId: z8.string().optional(),
3915
- clientSecret: z8.string().optional()
3767
+ var gmailProviderSchema = z7.object({
3768
+ clientId: z7.string().optional(),
3769
+ clientSecret: z7.string().optional()
3916
3770
  });
3917
- var providersConfigSchema = z8.object({
3771
+ var providersConfigSchema = z7.object({
3918
3772
  outlook: outlookProviderSchema.optional(),
3919
3773
  gmail: gmailProviderSchema.optional()
3920
3774
  });
3921
- var watchConfigSchema = z8.object({
3922
- enabled: z8.boolean().default(true),
3923
- pollIntervalSeconds: z8.number().int().min(10).max(3600).default(60)
3775
+ var watchConfigSchema = z7.object({
3776
+ enabled: z7.boolean().default(false),
3777
+ pollIntervalSeconds: z7.number().int().min(10).max(3600).default(10),
3778
+ webhook: z7.object({
3779
+ url: z7.string(),
3780
+ retry: z7.object({
3781
+ maxAttempts: z7.number().int().min(1).max(10).default(5),
3782
+ baseDelayMs: z7.number().int().min(100).default(1e3)
3783
+ }).optional()
3784
+ }).optional()
3924
3785
  });
3925
- var rawConfigSchema = z8.object({
3926
- dataDir: z8.string().optional(),
3786
+ var rawConfigSchema = z7.object({
3787
+ dataDir: z7.string().optional(),
3927
3788
  http: httpConfigSchema.optional(),
3928
3789
  tools: toolsConfigSchema.optional(),
3929
3790
  providers: providersConfigSchema.optional(),
@@ -4016,13 +3877,20 @@ function loadConfig(configPath, cliOverrides = {}) {
4016
3877
  port: cliOverrides.port ?? parsed.http?.port ?? 3e3,
4017
3878
  host: cliOverrides.host ?? parsed.http?.host ?? "127.0.0.1"
4018
3879
  };
3880
+ let watch;
3881
+ if (parsed.watch || process.env.HYPERMAIL_WATCH_ENABLED === "true") {
3882
+ watch = {
3883
+ enabled: process.env.HYPERMAIL_WATCH_ENABLED === "true" || Boolean(parsed.watch?.enabled),
3884
+ pollIntervalSeconds: parsed.watch?.pollIntervalSeconds ?? 10,
3885
+ webhook: parsed.watch?.webhook
3886
+ };
3887
+ }
4019
3888
  return {
4020
3889
  dataDir: cliOverrides.dataDir ?? parsed.dataDir ?? process.env.HYPERMAIL_MCP_DATA_DIR,
4021
3890
  http,
4022
3891
  tools: parsed.tools ? { disabled: parsed.tools.disabled, enabled: parsed.tools.enabled } : void 0,
4023
3892
  providers: parsed.providers,
4024
- watch: parsed.watch,
4025
- agentsConfigPath: cliOverrides.agentsConfig ?? process.env.HYPERMAIL_AGENTS_CONFIG
3893
+ watch
4026
3894
  };
4027
3895
  }
4028
3896
  function resolveTools(config) {
@@ -4035,327 +3903,37 @@ function resolveTools(config) {
4035
3903
  };
4036
3904
  }
4037
3905
 
4038
- // src/watcher/manager.ts
4039
- var WatcherManager = class {
4040
- opts;
4041
- timers = [];
4042
- running = false;
4043
- /** Per-account inflight guards to prevent overlapping polls. */
4044
- inflight = /* @__PURE__ */ new Map();
4045
- /** Accounts with active polling timers (lowercased email). */
4046
- tracked = /* @__PURE__ */ new Set();
4047
- constructor(opts) {
4048
- this.opts = opts;
4049
- }
4050
- start() {
4051
- if (this.running) return;
4052
- this.running = true;
4053
- this.scanAccounts();
4054
- const rescanTimer = setInterval(() => {
4055
- if (!this.running) return;
4056
- this.scanAccounts();
4057
- }, this.opts.pollIntervalSeconds * 1e3);
4058
- this.timers.push(rescanTimer);
4059
- }
4060
- stop() {
4061
- this.running = false;
4062
- for (const t of this.timers) clearInterval(t);
4063
- this.timers = [];
4064
- this.tracked.clear();
4065
- }
4066
- // ── internals ──
4067
- scanAccounts() {
4068
- let accounts = this.opts.store.listAccounts();
4069
- if (this.opts.accountFilter) {
4070
- const filter = new Set(this.opts.accountFilter.map((e) => e.toLowerCase()));
4071
- accounts = accounts.filter((a) => filter.has(a.email.toLowerCase()));
4072
- }
4073
- for (const account of accounts) {
4074
- this.schedulePoll(account);
4075
- }
4076
- }
4077
- schedulePoll(account) {
4078
- const key = account.email.toLowerCase();
4079
- if (this.tracked.has(key)) return;
4080
- this.tracked.add(key);
4081
- this.pollAccount(account).catch(() => {
4082
- });
4083
- const timer = setInterval(() => {
4084
- if (!this.running) return;
4085
- this.pollAccount(account).catch(() => {
4086
- });
4087
- }, this.opts.pollIntervalSeconds * 1e3);
4088
- this.timers.push(timer);
4089
- }
4090
- async pollAccount(account) {
4091
- const key = account.email.toLowerCase();
4092
- if (this.inflight.get(key)) return;
4093
- this.inflight.set(key, true);
4094
- try {
4095
- const { provider } = this.opts.registry.resolveByEmail(account.email);
4096
- const seenIds = new Set(account.lastSeenIds ?? []);
4097
- const isFirstPoll = !account.lastSeenAt && !account.lastSeenIds?.length;
4098
- const limit = 25;
4099
- const MAX_PAGES = 5;
4100
- let skip = 0;
4101
- let pageCount = 0;
4102
- const newEmails = [];
4103
- let newestTimestamp = account.lastSeenAt ?? "";
4104
- let hitBoundary = false;
4105
- while (pageCount < MAX_PAGES) {
4106
- const { items, hasMore } = await provider.listEmails(account, {
4107
- folder: "inbox",
4108
- limit,
4109
- skip
4110
- });
4111
- pageCount++;
4112
- for (const item of items) {
4113
- if (!item.receivedAt) continue;
4114
- if (seenIds.has(item.id)) {
4115
- hitBoundary = true;
4116
- break;
4117
- }
4118
- newEmails.push(item);
4119
- if (item.receivedAt > newestTimestamp) {
4120
- newestTimestamp = item.receivedAt;
4121
- }
4122
- }
4123
- if (hitBoundary || !hasMore) break;
4124
- skip += limit;
4125
- }
4126
- if (!isFirstPoll && newEmails.length > 0) {
4127
- this.enqueue({
4128
- type: "new_emails",
4129
- account: account.email,
4130
- emails: newEmails,
4131
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
4132
- });
4133
- }
4134
- if (isFirstPoll && newEmails.length === 0 && !newestTimestamp) {
4135
- newestTimestamp = (/* @__PURE__ */ new Date()).toISOString();
4136
- }
4137
- const newIds = newEmails.map((e) => e.id);
4138
- const updatedLastSeenIds = [
4139
- ...newIds,
4140
- ...account.lastSeenIds ?? []
4141
- ].slice(0, 200);
4142
- try {
4143
- await this.opts.store.upsertAccount({
4144
- ...account,
4145
- lastSeenAt: newestTimestamp || void 0,
4146
- lastSeenIds: updatedLastSeenIds
4147
- });
4148
- } catch (storeErr) {
4149
- console.error(
4150
- "[hypermail-mcp] failed to persist poll state for",
4151
- account.email,
4152
- ":",
4153
- storeErr instanceof Error ? storeErr.message : String(storeErr)
4154
- );
4155
- }
4156
- } catch (err) {
4157
- this.enqueue({
4158
- type: "auth_failure",
4159
- account: account.email,
4160
- error: err instanceof Error ? err.message : String(err),
4161
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
4162
- });
4163
- } finally {
4164
- this.inflight.delete(key);
4165
- }
4166
- }
4167
- enqueue(notification) {
4168
- this.opts.buffer.push(notification);
4169
- try {
4170
- this.opts.onNotification(notification);
4171
- } catch {
4172
- }
4173
- }
4174
- };
4175
-
4176
- // src/config/agents-config.ts
4177
- import { readFileSync as readFileSync2, watch } from "fs";
4178
- import { load as loadYaml } from "js-yaml";
4179
- import { z as z9 } from "zod";
4180
- var agentDefSchema = z9.object({
4181
- id: z9.string().min(1).regex(
4182
- /^[a-z0-9_-]+$/,
4183
- "agent id must contain only lowercase letters, digits, hyphens, and underscores"
4184
- ),
4185
- api_key: z9.string().min(1).regex(
4186
- /^hm_sk_[a-f0-9]{64}$/,
4187
- "api_key must match hm_sk_ prefix + 64 hex chars (use `hypermail-mcp generate-key`)"
4188
- ),
4189
- name: z9.string().min(1),
4190
- accounts: z9.array(z9.string().email()).optional().default([]),
4191
- provisioning: z9.boolean().optional().default(false)
4192
- });
4193
- var emailAccountDefSchema = z9.object({
4194
- provider: z9.enum(["outlook", "imap", "gmail"]),
4195
- display_name: z9.string().optional()
4196
- });
4197
- var agentsConfigSchema = z9.object({
4198
- agents: z9.array(agentDefSchema).optional().default([]),
4199
- email_accounts: z9.record(z9.string().email(), emailAccountDefSchema).optional().default({})
4200
- });
4201
- function loadAgentsConfig(configPath) {
4202
- let raw;
4203
- try {
4204
- raw = loadYaml(readFileSync2(configPath, "utf-8"));
4205
- } catch (err) {
4206
- if (err instanceof Error && "code" in err && err.code === "ENOENT") {
4207
- throw new Error(
4208
- `Agents config file not found: ${configPath}. Create an agents.yaml with at least one agent to enable HTTP multi-tenant mode.`
4209
- );
4210
- }
4211
- const detail = err instanceof Error ? err.message : String(err);
4212
- throw new Error(`Failed to parse agents config "${configPath}": ${detail}`);
4213
- }
4214
- const parsed = agentsConfigSchema.parse(raw ?? {});
4215
- const ids = /* @__PURE__ */ new Set();
4216
- for (const a of parsed.agents) {
4217
- if (ids.has(a.id)) {
4218
- throw new Error(`Duplicate agent id "${a.id}" in agents config`);
4219
- }
4220
- ids.add(a.id);
4221
- }
4222
- return {
4223
- agents: parsed.agents.map((a) => ({
4224
- id: a.id,
4225
- api_key: a.api_key,
4226
- name: a.name,
4227
- accounts: a.accounts,
4228
- provisioning: a.provisioning
4229
- })),
4230
- email_accounts: parsed.email_accounts
4231
- };
4232
- }
4233
- async function syncAgentsToStore(config, store) {
4234
- const configAgentIds = new Set(config.agents.map((a) => a.id));
4235
- const storedAgents = store.listAgents();
4236
- for (const def of config.agents) {
4237
- await store.upsertAgent({
4238
- id: def.id,
4239
- plaintextApiKey: def.api_key,
4240
- name: def.name,
4241
- accounts: def.accounts,
4242
- provisioning: def.provisioning
4243
- });
4244
- }
4245
- const removed = [];
4246
- for (const stored of storedAgents) {
4247
- if (!configAgentIds.has(stored.id)) {
4248
- await store.removeAgent(stored.id);
4249
- removed.push(stored.id);
4250
- }
4251
- }
4252
- return removed;
4253
- }
4254
- function watchAgentsConfig(configPath, store, onChange, onError) {
4255
- let timer = null;
4256
- let watcher = null;
4257
- const reload = async () => {
4258
- try {
4259
- const config = loadAgentsConfig(configPath);
4260
- const removed = await syncAgentsToStore(config, store);
4261
- onChange(removed);
4262
- } catch (err) {
4263
- onError(err instanceof Error ? err : new Error(String(err)));
4264
- }
4265
- };
4266
- reload().catch((err) => onError(err));
4267
- try {
4268
- watcher = watch(configPath, (_eventType) => {
4269
- if (timer) clearTimeout(timer);
4270
- timer = setTimeout(() => {
4271
- timer = null;
4272
- reload().catch((err) => onError(err));
4273
- }, 200);
4274
- });
4275
- } catch (err) {
4276
- }
4277
- return {
4278
- close() {
4279
- if (timer) clearTimeout(timer);
4280
- if (watcher) watcher.close();
4281
- }
4282
- };
4283
- }
4284
-
4285
3906
  // src/server.ts
4286
3907
  async function startServer(opts) {
4287
3908
  const { config } = opts;
4288
3909
  const store = await AccountStore.open({ dataDir: config.dataDir });
4289
3910
  const registry = buildRegistry({ store, providers: config.providers });
4290
3911
  const tools = resolveTools(config);
4291
- const watchEnabled = config.http.enabled && config.watch?.enabled !== false;
4292
- const notificationBuffer = watchEnabled ? [] : void 0;
4293
- let agentStoreForFactory;
4294
- const createServer = (agentContext = null) => {
3912
+ let watcher;
3913
+ if (config.watch?.enabled) {
3914
+ watcher = new WatcherManager(store, registry, config.watch);
3915
+ watcher.start();
3916
+ const stop = () => watcher?.stop();
3917
+ process.on("SIGTERM", stop);
3918
+ process.on("SIGINT", stop);
3919
+ }
3920
+ const createServer = () => {
4295
3921
  const s = new McpServer(
4296
3922
  { name: "hypermail-mcp", version: VERSION },
4297
3923
  { capabilities: { tools: {}, logging: {} } }
4298
3924
  );
4299
- registerTools(s, { store, registry, tools, notificationBuffer, agentContext, agentStore: agentStoreForFactory });
3925
+ registerTools(s, { store, registry, tools });
4300
3926
  return s;
4301
3927
  };
4302
3928
  if (config.http.enabled) {
4303
- let liveReloadHandle;
4304
- if (config.agentsConfigPath) {
4305
- agentStoreForFactory = await AgentStore.open({ dataDir: config.dataDir });
4306
- liveReloadHandle = watchAgentsConfig(
4307
- path4.resolve(config.agentsConfigPath),
4308
- agentStoreForFactory,
4309
- (_removedIds) => {
4310
- },
4311
- (err) => {
4312
- console.error("[hypermail-mcp] agents.yaml reload error:", err.message);
4313
- }
4314
- );
4315
- }
4316
- const notifyTargets = /* @__PURE__ */ new Set();
4317
- const accountFilter = agentStoreForFactory ? (() => {
4318
- const all = /* @__PURE__ */ new Set();
4319
- for (const agent of agentStoreForFactory.listAgents()) {
4320
- for (const email of agent.accounts) {
4321
- all.add(email.toLowerCase());
4322
- }
4323
- }
4324
- return all.size > 0 ? [...all] : void 0;
4325
- })() : void 0;
4326
- if (watchEnabled) {
4327
- const watcher = new WatcherManager({
4328
- registry,
4329
- store,
4330
- pollIntervalSeconds: config.watch?.pollIntervalSeconds ?? 60,
4331
- accountFilter,
4332
- onNotification: (notification) => {
4333
- for (const fn of notifyTargets) {
4334
- fn(notification);
4335
- }
4336
- },
4337
- buffer: notificationBuffer
4338
- });
4339
- watcher.start();
4340
- }
4341
- await startHttp(
4342
- createServer,
4343
- config.http.host,
4344
- config.http.port,
4345
- notifyTargets,
4346
- agentStoreForFactory
4347
- );
4348
- if (liveReloadHandle) {
4349
- process.on("SIGINT", () => liveReloadHandle.close());
4350
- process.on("SIGTERM", () => liveReloadHandle.close());
4351
- }
3929
+ await startHttp(createServer, config.http.host, config.http.port);
4352
3930
  } else {
4353
3931
  const server = createServer();
4354
3932
  const transport = new StdioServerTransport();
4355
3933
  await server.connect(transport);
4356
3934
  }
4357
3935
  }
4358
- async function startHttp(createServer, host, port, notifyTargets, agentStore) {
3936
+ async function startHttp(createServer, host, port) {
4359
3937
  const sessions = /* @__PURE__ */ new Map();
4360
3938
  const http = createHttpServer(async (req, res) => {
4361
3939
  try {
@@ -4367,57 +3945,18 @@ async function startHttp(createServer, host, port, notifyTargets, agentStore) {
4367
3945
  const sessionId = req.headers["mcp-session-id"] ?? void 0;
4368
3946
  let session = sessionId ? sessions.get(sessionId) : void 0;
4369
3947
  if (!session) {
4370
- let agentContext = null;
4371
- if (agentStore) {
4372
- const apiKey = req.headers["x-api-key"]?.trim();
4373
- if (!apiKey) {
4374
- res.statusCode = 401;
4375
- res.setHeader("Content-Type", "application/json");
4376
- res.end(JSON.stringify({ error: "Missing x-api-key header" }));
4377
- return;
4378
- }
4379
- const agent = agentStore.findAgentByApiKey(apiKey);
4380
- if (!agent) {
4381
- res.statusCode = 401;
4382
- res.setHeader("Content-Type", "application/json");
4383
- res.end(JSON.stringify({ error: "Invalid API key" }));
4384
- return;
4385
- }
4386
- agentContext = {
4387
- agentId: agent.id,
4388
- accounts: agent.accounts,
4389
- provisioning: agent.provisioning
4390
- };
4391
- }
4392
- const server = createServer(agentContext);
3948
+ const server = createServer();
4393
3949
  const transport = new StreamableHTTPServerTransport({
4394
3950
  sessionIdGenerator: () => randomUUID6(),
4395
3951
  onsessioninitialized: (sid) => {
4396
- sessions.set(sid, { transport, server, agentContext });
4397
- const agentAccounts = agentContext ? new Set(agentContext.accounts.map((a) => a.toLowerCase())) : null;
4398
- const notifyFn = (n) => {
4399
- if (agentAccounts && !agentAccounts.has(n.account.toLowerCase())) {
4400
- return;
4401
- }
4402
- server.server.notification({
4403
- method: "notifications/message",
4404
- params: {
4405
- level: n.type === "new_emails" ? "notice" : "warning",
4406
- logger: "hypermail-watch",
4407
- data: n
4408
- }
4409
- }).catch(() => {
4410
- });
4411
- };
4412
- notifyTargets.add(notifyFn);
3952
+ sessions.set(sid, { transport, server });
4413
3953
  transport.onclose = () => {
4414
3954
  if (transport.sessionId) sessions.delete(transport.sessionId);
4415
- notifyTargets.delete(notifyFn);
4416
3955
  };
4417
3956
  }
4418
3957
  });
4419
3958
  await server.connect(transport);
4420
- session = { transport, server, agentContext };
3959
+ session = { transport, server };
4421
3960
  }
4422
3961
  let body = void 0;
4423
3962
  if (req.method === "POST" || req.method === "DELETE") {
@@ -4465,9 +4004,6 @@ function parseArgs(argv) {
4465
4004
  case "--config":
4466
4005
  out.config = String(argv[++i] ?? "");
4467
4006
  break;
4468
- case "--agents-config":
4469
- out.agentsConfig = String(argv[++i] ?? "");
4470
- break;
4471
4007
  case "-h":
4472
4008
  case "--help":
4473
4009
  out.help = true;
@@ -4527,8 +4063,7 @@ async function main() {
4527
4063
  http: opts.http,
4528
4064
  port: opts.port,
4529
4065
  host: opts.host,
4530
- dataDir: opts.dataDir,
4531
- agentsConfig: opts.agentsConfig
4066
+ dataDir: opts.dataDir
4532
4067
  });
4533
4068
  await startServer({ config });
4534
4069
  }