@kononenko-e/email-mcp 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/LICENSE +155 -0
  2. package/README.md +820 -0
  3. package/dist/cli/account-commands.d.ts +10 -0
  4. package/dist/cli/account-commands.d.ts.map +1 -0
  5. package/dist/cli/account-commands.js +703 -0
  6. package/dist/cli/account-commands.js.map +1 -0
  7. package/dist/cli/config-commands.d.ts +9 -0
  8. package/dist/cli/config-commands.d.ts.map +1 -0
  9. package/dist/cli/config-commands.js +156 -0
  10. package/dist/cli/config-commands.js.map +1 -0
  11. package/dist/cli/guard.d.ts +11 -0
  12. package/dist/cli/guard.d.ts.map +1 -0
  13. package/dist/cli/guard.js +18 -0
  14. package/dist/cli/guard.js.map +1 -0
  15. package/dist/cli/install-commands.d.ts +12 -0
  16. package/dist/cli/install-commands.d.ts.map +1 -0
  17. package/dist/cli/install-commands.js +320 -0
  18. package/dist/cli/install-commands.js.map +1 -0
  19. package/dist/cli/notify.d.ts +8 -0
  20. package/dist/cli/notify.d.ts.map +1 -0
  21. package/dist/cli/notify.js +143 -0
  22. package/dist/cli/notify.js.map +1 -0
  23. package/dist/cli/providers.d.ts +13 -0
  24. package/dist/cli/providers.d.ts.map +1 -0
  25. package/dist/cli/providers.js +180 -0
  26. package/dist/cli/providers.js.map +1 -0
  27. package/dist/cli/scheduler.d.ts +8 -0
  28. package/dist/cli/scheduler.d.ts.map +1 -0
  29. package/dist/cli/scheduler.js +268 -0
  30. package/dist/cli/scheduler.js.map +1 -0
  31. package/dist/cli/setup.d.ts +12 -0
  32. package/dist/cli/setup.d.ts.map +1 -0
  33. package/dist/cli/setup.js +15 -0
  34. package/dist/cli/setup.js.map +1 -0
  35. package/dist/cli/test.d.ts +7 -0
  36. package/dist/cli/test.d.ts.map +1 -0
  37. package/dist/cli/test.js +67 -0
  38. package/dist/cli/test.js.map +1 -0
  39. package/dist/config/loader.d.ts +36 -0
  40. package/dist/config/loader.d.ts.map +1 -0
  41. package/dist/config/loader.js +372 -0
  42. package/dist/config/loader.js.map +1 -0
  43. package/dist/config/schema.d.ts +351 -0
  44. package/dist/config/schema.d.ts.map +1 -0
  45. package/dist/config/schema.js +165 -0
  46. package/dist/config/schema.js.map +1 -0
  47. package/dist/config/xdg.d.ts +27 -0
  48. package/dist/config/xdg.d.ts.map +1 -0
  49. package/dist/config/xdg.js +30 -0
  50. package/dist/config/xdg.js.map +1 -0
  51. package/dist/connections/manager.d.ts +43 -0
  52. package/dist/connections/manager.d.ts.map +1 -0
  53. package/dist/connections/manager.js +272 -0
  54. package/dist/connections/manager.js.map +1 -0
  55. package/dist/connections/types.d.ts +13 -0
  56. package/dist/connections/types.d.ts.map +1 -0
  57. package/dist/connections/types.js +2 -0
  58. package/dist/connections/types.js.map +1 -0
  59. package/dist/logging.d.ts +46 -0
  60. package/dist/logging.d.ts.map +1 -0
  61. package/dist/logging.js +63 -0
  62. package/dist/logging.js.map +1 -0
  63. package/dist/main.d.ts +14 -0
  64. package/dist/main.d.ts.map +1 -0
  65. package/dist/main.js +381 -0
  66. package/dist/main.js.map +1 -0
  67. package/dist/prompts/actions.prompt.d.ts +9 -0
  68. package/dist/prompts/actions.prompt.d.ts.map +1 -0
  69. package/dist/prompts/actions.prompt.js +64 -0
  70. package/dist/prompts/actions.prompt.js.map +1 -0
  71. package/dist/prompts/calendar.prompt.d.ts +9 -0
  72. package/dist/prompts/calendar.prompt.d.ts.map +1 -0
  73. package/dist/prompts/calendar.prompt.js +55 -0
  74. package/dist/prompts/calendar.prompt.js.map +1 -0
  75. package/dist/prompts/cleanup.prompt.d.ts +9 -0
  76. package/dist/prompts/cleanup.prompt.d.ts.map +1 -0
  77. package/dist/prompts/cleanup.prompt.js +78 -0
  78. package/dist/prompts/cleanup.prompt.js.map +1 -0
  79. package/dist/prompts/compose.prompt.d.ts +8 -0
  80. package/dist/prompts/compose.prompt.d.ts.map +1 -0
  81. package/dist/prompts/compose.prompt.js +116 -0
  82. package/dist/prompts/compose.prompt.js.map +1 -0
  83. package/dist/prompts/register.d.ts +8 -0
  84. package/dist/prompts/register.d.ts.map +1 -0
  85. package/dist/prompts/register.js +20 -0
  86. package/dist/prompts/register.js.map +1 -0
  87. package/dist/prompts/thread.prompt.d.ts +9 -0
  88. package/dist/prompts/thread.prompt.d.ts.map +1 -0
  89. package/dist/prompts/thread.prompt.js +58 -0
  90. package/dist/prompts/thread.prompt.js.map +1 -0
  91. package/dist/prompts/triage.prompt.d.ts +9 -0
  92. package/dist/prompts/triage.prompt.d.ts.map +1 -0
  93. package/dist/prompts/triage.prompt.js +64 -0
  94. package/dist/prompts/triage.prompt.js.map +1 -0
  95. package/dist/resources/accounts.resource.d.ts +9 -0
  96. package/dist/resources/accounts.resource.d.ts.map +1 -0
  97. package/dist/resources/accounts.resource.js +26 -0
  98. package/dist/resources/accounts.resource.js.map +1 -0
  99. package/dist/resources/mailboxes.resource.d.ts +10 -0
  100. package/dist/resources/mailboxes.resource.d.ts.map +1 -0
  101. package/dist/resources/mailboxes.resource.js +33 -0
  102. package/dist/resources/mailboxes.resource.js.map +1 -0
  103. package/dist/resources/register.d.ts +12 -0
  104. package/dist/resources/register.d.ts.map +1 -0
  105. package/dist/resources/register.js +20 -0
  106. package/dist/resources/register.js.map +1 -0
  107. package/dist/resources/scheduled.resource.d.ts +9 -0
  108. package/dist/resources/scheduled.resource.d.ts.map +1 -0
  109. package/dist/resources/scheduled.resource.js +31 -0
  110. package/dist/resources/scheduled.resource.js.map +1 -0
  111. package/dist/resources/stats.resource.d.ts +10 -0
  112. package/dist/resources/stats.resource.d.ts.map +1 -0
  113. package/dist/resources/stats.resource.js +45 -0
  114. package/dist/resources/stats.resource.js.map +1 -0
  115. package/dist/resources/templates.resource.d.ts +9 -0
  116. package/dist/resources/templates.resource.d.ts.map +1 -0
  117. package/dist/resources/templates.resource.js +34 -0
  118. package/dist/resources/templates.resource.js.map +1 -0
  119. package/dist/resources/unread.resource.d.ts +11 -0
  120. package/dist/resources/unread.resource.d.ts.map +1 -0
  121. package/dist/resources/unread.resource.js +46 -0
  122. package/dist/resources/unread.resource.js.map +1 -0
  123. package/dist/safety/audit.d.ts +17 -0
  124. package/dist/safety/audit.d.ts.map +1 -0
  125. package/dist/safety/audit.js +50 -0
  126. package/dist/safety/audit.js.map +1 -0
  127. package/dist/safety/rate-limiter.d.ts +22 -0
  128. package/dist/safety/rate-limiter.d.ts.map +1 -0
  129. package/dist/safety/rate-limiter.js +52 -0
  130. package/dist/safety/rate-limiter.js.map +1 -0
  131. package/dist/safety/validation.d.ts +44 -0
  132. package/dist/safety/validation.d.ts.map +1 -0
  133. package/dist/safety/validation.js +113 -0
  134. package/dist/safety/validation.js.map +1 -0
  135. package/dist/server.d.ts +10 -0
  136. package/dist/server.d.ts.map +1 -0
  137. package/dist/server.js +25 -0
  138. package/dist/server.js.map +1 -0
  139. package/dist/services/calendar.service.d.ts +22 -0
  140. package/dist/services/calendar.service.d.ts.map +1 -0
  141. package/dist/services/calendar.service.js +115 -0
  142. package/dist/services/calendar.service.js.map +1 -0
  143. package/dist/services/event-bus.d.ts +28 -0
  144. package/dist/services/event-bus.d.ts.map +1 -0
  145. package/dist/services/event-bus.js +16 -0
  146. package/dist/services/event-bus.js.map +1 -0
  147. package/dist/services/hooks.service.d.ts +78 -0
  148. package/dist/services/hooks.service.d.ts.map +1 -0
  149. package/dist/services/hooks.service.js +497 -0
  150. package/dist/services/hooks.service.js.map +1 -0
  151. package/dist/services/imap.service.d.ts +133 -0
  152. package/dist/services/imap.service.d.ts.map +1 -0
  153. package/dist/services/imap.service.js +1393 -0
  154. package/dist/services/imap.service.js.map +1 -0
  155. package/dist/services/label-strategy.d.ts +20 -0
  156. package/dist/services/label-strategy.d.ts.map +1 -0
  157. package/dist/services/label-strategy.js +237 -0
  158. package/dist/services/label-strategy.js.map +1 -0
  159. package/dist/services/local-calendar.service.d.ts +126 -0
  160. package/dist/services/local-calendar.service.d.ts.map +1 -0
  161. package/dist/services/local-calendar.service.js +428 -0
  162. package/dist/services/local-calendar.service.js.map +1 -0
  163. package/dist/services/notifier.service.d.ts +66 -0
  164. package/dist/services/notifier.service.d.ts.map +1 -0
  165. package/dist/services/notifier.service.js +316 -0
  166. package/dist/services/notifier.service.js.map +1 -0
  167. package/dist/services/oauth.service.d.ts +47 -0
  168. package/dist/services/oauth.service.d.ts.map +1 -0
  169. package/dist/services/oauth.service.js +140 -0
  170. package/dist/services/oauth.service.js.map +1 -0
  171. package/dist/services/presets.d.ts +36 -0
  172. package/dist/services/presets.d.ts.map +1 -0
  173. package/dist/services/presets.js +173 -0
  174. package/dist/services/presets.js.map +1 -0
  175. package/dist/services/reminders.service.d.ts +63 -0
  176. package/dist/services/reminders.service.d.ts.map +1 -0
  177. package/dist/services/reminders.service.js +281 -0
  178. package/dist/services/reminders.service.js.map +1 -0
  179. package/dist/services/scheduler.service.d.ts +42 -0
  180. package/dist/services/scheduler.service.d.ts.map +1 -0
  181. package/dist/services/scheduler.service.js +260 -0
  182. package/dist/services/scheduler.service.js.map +1 -0
  183. package/dist/services/smtp.service.d.ts +40 -0
  184. package/dist/services/smtp.service.d.ts.map +1 -0
  185. package/dist/services/smtp.service.js +151 -0
  186. package/dist/services/smtp.service.js.map +1 -0
  187. package/dist/services/template.service.d.ts +33 -0
  188. package/dist/services/template.service.d.ts.map +1 -0
  189. package/dist/services/template.service.js +123 -0
  190. package/dist/services/template.service.js.map +1 -0
  191. package/dist/services/watcher.service.d.ts +37 -0
  192. package/dist/services/watcher.service.d.ts.map +1 -0
  193. package/dist/services/watcher.service.js +244 -0
  194. package/dist/services/watcher.service.js.map +1 -0
  195. package/dist/tools/account-config.tool.d.ts +14 -0
  196. package/dist/tools/account-config.tool.d.ts.map +1 -0
  197. package/dist/tools/account-config.tool.js +245 -0
  198. package/dist/tools/account-config.tool.js.map +1 -0
  199. package/dist/tools/accounts.tool.d.ts +7 -0
  200. package/dist/tools/accounts.tool.d.ts.map +1 -0
  201. package/dist/tools/accounts.tool.js +29 -0
  202. package/dist/tools/accounts.tool.js.map +1 -0
  203. package/dist/tools/analytics.tool.d.ts +9 -0
  204. package/dist/tools/analytics.tool.d.ts.map +1 -0
  205. package/dist/tools/analytics.tool.js +27 -0
  206. package/dist/tools/analytics.tool.js.map +1 -0
  207. package/dist/tools/attachments.tool.d.ts +7 -0
  208. package/dist/tools/attachments.tool.d.ts.map +1 -0
  209. package/dist/tools/attachments.tool.js +45 -0
  210. package/dist/tools/attachments.tool.js.map +1 -0
  211. package/dist/tools/bulk.tool.d.ts +7 -0
  212. package/dist/tools/bulk.tool.d.ts.map +1 -0
  213. package/dist/tools/bulk.tool.js +75 -0
  214. package/dist/tools/bulk.tool.js.map +1 -0
  215. package/dist/tools/calendar.tool.d.ts +19 -0
  216. package/dist/tools/calendar.tool.d.ts.map +1 -0
  217. package/dist/tools/calendar.tool.js +538 -0
  218. package/dist/tools/calendar.tool.js.map +1 -0
  219. package/dist/tools/contacts.tool.d.ts +7 -0
  220. package/dist/tools/contacts.tool.d.ts.map +1 -0
  221. package/dist/tools/contacts.tool.js +44 -0
  222. package/dist/tools/contacts.tool.js.map +1 -0
  223. package/dist/tools/drafts.tool.d.ts +8 -0
  224. package/dist/tools/drafts.tool.d.ts.map +1 -0
  225. package/dist/tools/drafts.tool.js +92 -0
  226. package/dist/tools/drafts.tool.js.map +1 -0
  227. package/dist/tools/emails.tool.d.ts +7 -0
  228. package/dist/tools/emails.tool.d.ts.map +1 -0
  229. package/dist/tools/emails.tool.js +400 -0
  230. package/dist/tools/emails.tool.js.map +1 -0
  231. package/dist/tools/folders.tool.d.ts +7 -0
  232. package/dist/tools/folders.tool.d.ts.map +1 -0
  233. package/dist/tools/folders.tool.js +111 -0
  234. package/dist/tools/folders.tool.js.map +1 -0
  235. package/dist/tools/health.tool.d.ts +10 -0
  236. package/dist/tools/health.tool.d.ts.map +1 -0
  237. package/dist/tools/health.tool.js +78 -0
  238. package/dist/tools/health.tool.js.map +1 -0
  239. package/dist/tools/label.tool.d.ts +11 -0
  240. package/dist/tools/label.tool.d.ts.map +1 -0
  241. package/dist/tools/label.tool.js +165 -0
  242. package/dist/tools/label.tool.js.map +1 -0
  243. package/dist/tools/locate.tool.d.ts +11 -0
  244. package/dist/tools/locate.tool.d.ts.map +1 -0
  245. package/dist/tools/locate.tool.js +59 -0
  246. package/dist/tools/locate.tool.js.map +1 -0
  247. package/dist/tools/mailboxes.tool.d.ts +7 -0
  248. package/dist/tools/mailboxes.tool.d.ts.map +1 -0
  249. package/dist/tools/mailboxes.tool.js +38 -0
  250. package/dist/tools/mailboxes.tool.js.map +1 -0
  251. package/dist/tools/manage.tool.d.ts +7 -0
  252. package/dist/tools/manage.tool.d.ts.map +1 -0
  253. package/dist/tools/manage.tool.js +125 -0
  254. package/dist/tools/manage.tool.js.map +1 -0
  255. package/dist/tools/register.d.ts +21 -0
  256. package/dist/tools/register.d.ts.map +1 -0
  257. package/dist/tools/register.js +55 -0
  258. package/dist/tools/register.js.map +1 -0
  259. package/dist/tools/scheduler.tool.d.ts +9 -0
  260. package/dist/tools/scheduler.tool.d.ts.map +1 -0
  261. package/dist/tools/scheduler.tool.js +104 -0
  262. package/dist/tools/scheduler.tool.js.map +1 -0
  263. package/dist/tools/send.tool.d.ts +7 -0
  264. package/dist/tools/send.tool.d.ts.map +1 -0
  265. package/dist/tools/send.tool.js +123 -0
  266. package/dist/tools/send.tool.js.map +1 -0
  267. package/dist/tools/templates.tool.d.ts +12 -0
  268. package/dist/tools/templates.tool.d.ts.map +1 -0
  269. package/dist/tools/templates.tool.js +140 -0
  270. package/dist/tools/templates.tool.js.map +1 -0
  271. package/dist/tools/thread.tool.d.ts +10 -0
  272. package/dist/tools/thread.tool.d.ts.map +1 -0
  273. package/dist/tools/thread.tool.js +146 -0
  274. package/dist/tools/thread.tool.js.map +1 -0
  275. package/dist/tools/watcher.tool.d.ts +9 -0
  276. package/dist/tools/watcher.tool.d.ts.map +1 -0
  277. package/dist/tools/watcher.tool.js +282 -0
  278. package/dist/tools/watcher.tool.js.map +1 -0
  279. package/dist/types/index.d.ts +271 -0
  280. package/dist/types/index.d.ts.map +1 -0
  281. package/dist/types/index.js +5 -0
  282. package/dist/types/index.js.map +1 -0
  283. package/dist/utils/calendar-notes.d.ts +31 -0
  284. package/dist/utils/calendar-notes.d.ts.map +1 -0
  285. package/dist/utils/calendar-notes.js +99 -0
  286. package/dist/utils/calendar-notes.js.map +1 -0
  287. package/dist/utils/calendar-state.d.ts +27 -0
  288. package/dist/utils/calendar-state.d.ts.map +1 -0
  289. package/dist/utils/calendar-state.js +85 -0
  290. package/dist/utils/calendar-state.js.map +1 -0
  291. package/dist/utils/conference-details.d.ts +12 -0
  292. package/dist/utils/conference-details.d.ts.map +1 -0
  293. package/dist/utils/conference-details.js +71 -0
  294. package/dist/utils/conference-details.js.map +1 -0
  295. package/dist/utils/meeting-url.d.ts +10 -0
  296. package/dist/utils/meeting-url.d.ts.map +1 -0
  297. package/dist/utils/meeting-url.js +30 -0
  298. package/dist/utils/meeting-url.js.map +1 -0
  299. package/package.json +103 -0
@@ -0,0 +1,34 @@
1
+ /**
2
+ * MCP Resource: email://templates/{name}
3
+ *
4
+ * Dynamic resource exposing user-defined email templates.
5
+ */
6
+ import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
7
+ export default function registerTemplatesResource(server, templateService) {
8
+ server.resource('templates', new ResourceTemplate('email://templates/{name}', {
9
+ list: async () => {
10
+ const templates = await templateService.listTemplates();
11
+ return {
12
+ resources: templates.map((t) => ({
13
+ uri: `email://templates/${t.name}`,
14
+ name: `Template: ${t.name}`,
15
+ description: t.description ?? `Email template "${t.name}"`,
16
+ mimeType: 'application/json',
17
+ })),
18
+ };
19
+ },
20
+ }), { description: 'User-defined email template with variable placeholders' }, async (uri, { name }) => {
21
+ const templateName = name;
22
+ const template = await templateService.getTemplate(templateName);
23
+ return {
24
+ contents: [
25
+ {
26
+ uri: uri.href,
27
+ mimeType: 'application/json',
28
+ text: JSON.stringify(template, null, 2),
29
+ },
30
+ ],
31
+ };
32
+ });
33
+ }
34
+ //# sourceMappingURL=templates.resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.resource.js","sourceRoot":"","sources":["../../src/resources/templates.resource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAI3E,MAAM,CAAC,OAAO,UAAU,yBAAyB,CAC/C,MAAiB,EACjB,eAAgC;IAEhC,MAAM,CAAC,QAAQ,CACb,WAAW,EACX,IAAI,gBAAgB,CAAC,0BAA0B,EAAE;QAC/C,IAAI,EAAE,KAAK,IAAI,EAAE;YACf,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,aAAa,EAAE,CAAC;YACxD,OAAO;gBACL,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBAC/B,GAAG,EAAE,qBAAqB,CAAC,CAAC,IAAI,EAAE;oBAClC,IAAI,EAAE,aAAa,CAAC,CAAC,IAAI,EAAE;oBAC3B,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,mBAAmB,CAAC,CAAC,IAAI,GAAG;oBAC1D,QAAQ,EAAE,kBAAkB;iBAC7B,CAAC,CAAC;aACJ,CAAC;QACJ,CAAC;KACF,CAAC,EACF,EAAE,WAAW,EAAE,wDAAwD,EAAE,EACzE,KAAK,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACtB,MAAM,YAAY,GAAG,IAAc,CAAC;QACpC,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;QAEjE,OAAO;YACL,QAAQ,EAAE;gBACR;oBACE,GAAG,EAAE,GAAG,CAAC,IAAI;oBACb,QAAQ,EAAE,kBAAkB;oBAC5B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;iBACxC;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * MCP Resource: email://{account}/unread
3
+ *
4
+ * Dynamic resource providing an unread email summary per folder.
5
+ * Only includes folders with unread messages.
6
+ */
7
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
8
+ import type ConnectionManager from '../connections/manager.js';
9
+ import type ImapService from '../services/imap.service.js';
10
+ export default function registerUnreadResource(server: McpServer, connections: ConnectionManager, imapService: ImapService): void;
11
+ //# sourceMappingURL=unread.resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unread.resource.d.ts","sourceRoot":"","sources":["../../src/resources/unread.resource.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,KAAK,iBAAiB,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,WAAW,MAAM,6BAA6B,CAAC;AAE3D,MAAM,CAAC,OAAO,UAAU,sBAAsB,CAC5C,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,iBAAiB,EAC9B,WAAW,EAAE,WAAW,GACvB,IAAI,CA+CN"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * MCP Resource: email://{account}/unread
3
+ *
4
+ * Dynamic resource providing an unread email summary per folder.
5
+ * Only includes folders with unread messages.
6
+ */
7
+ import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
8
+ export default function registerUnreadResource(server, connections, imapService) {
9
+ const names = connections.getAccountNames();
10
+ const accounts = names.map((name) => connections.getAccount(name));
11
+ server.resource('unread', new ResourceTemplate('email://{account}/unread', {
12
+ list: async () => ({
13
+ resources: accounts.map((a) => ({
14
+ uri: `email://${a.name}/unread`,
15
+ name: `${a.name} — Unread summary`,
16
+ description: `Unread email counts by folder for ${a.email}`,
17
+ mimeType: 'application/json',
18
+ })),
19
+ }),
20
+ }), { description: 'Unread email count summary by folder for an account' }, async (uri, { account }) => {
21
+ const accountName = account;
22
+ const mailboxes = await imapService.listMailboxes(accountName);
23
+ const unreadFolders = mailboxes
24
+ .filter((mb) => mb.unseenMessages > 0)
25
+ .map((mb) => ({
26
+ path: mb.path,
27
+ unread: mb.unseenMessages,
28
+ }));
29
+ const totalUnread = unreadFolders.reduce((sum, f) => sum + f.unread, 0);
30
+ const summary = {
31
+ account: accountName,
32
+ totalUnread,
33
+ folders: unreadFolders,
34
+ };
35
+ return {
36
+ contents: [
37
+ {
38
+ uri: uri.href,
39
+ mimeType: 'application/json',
40
+ text: JSON.stringify(summary, null, 2),
41
+ },
42
+ ],
43
+ };
44
+ });
45
+ }
46
+ //# sourceMappingURL=unread.resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unread.resource.js","sourceRoot":"","sources":["../../src/resources/unread.resource.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAK3E,MAAM,CAAC,OAAO,UAAU,sBAAsB,CAC5C,MAAiB,EACjB,WAA8B,EAC9B,WAAwB;IAExB,MAAM,KAAK,GAAG,WAAW,CAAC,eAAe,EAAE,CAAC;IAC5C,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IAEnE,MAAM,CAAC,QAAQ,CACb,QAAQ,EACR,IAAI,gBAAgB,CAAC,0BAA0B,EAAE;QAC/C,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;YACjB,SAAS,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC9B,GAAG,EAAE,WAAW,CAAC,CAAC,IAAI,SAAS;gBAC/B,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,mBAAmB;gBAClC,WAAW,EAAE,qCAAqC,CAAC,CAAC,KAAK,EAAE;gBAC3D,QAAQ,EAAE,kBAAkB;aAC7B,CAAC,CAAC;SACJ,CAAC;KACH,CAAC,EACF,EAAE,WAAW,EAAE,qDAAqD,EAAE,EACtE,KAAK,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACzB,MAAM,WAAW,GAAG,OAAiB,CAAC;QACtC,MAAM,SAAS,GAAG,MAAM,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;QAE/D,MAAM,aAAa,GAAG,SAAS;aAC5B,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,cAAc,GAAG,CAAC,CAAC;aACrC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YACZ,IAAI,EAAE,EAAE,CAAC,IAAI;YACb,MAAM,EAAE,EAAE,CAAC,cAAc;SAC1B,CAAC,CAAC,CAAC;QAEN,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAExE,MAAM,OAAO,GAAG;YACd,OAAO,EAAE,WAAW;YACpB,WAAW;YACX,OAAO,EAAE,aAAa;SACvB,CAAC;QAEF,OAAO;YACL,QAAQ,EAAE;gBACR;oBACE,GAAG,EAAE,GAAG,CAAC,IAAI;oBACb,QAAQ,EAAE,kBAAkB;oBAC5B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;iBACvC;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Audit logger — append-only JSON Lines log for all write operations.
3
+ *
4
+ * Logs are stored at the XDG data directory:
5
+ * ~/.local/share/email-mcp/audit.log
6
+ */
7
+ /**
8
+ * Append a single audit entry to the log file.
9
+ * Creates the directory and file if they don't exist.
10
+ */
11
+ declare function log(tool: string, account: string, params: Record<string, unknown>, result: 'ok' | 'error', error?: string): Promise<void>;
12
+ declare const audit: {
13
+ readonly log: typeof log;
14
+ readonly AUDIT_LOG_PATH: string;
15
+ };
16
+ export default audit;
17
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/safety/audit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgCH;;;GAGG;AACH,iBAAe,GAAG,CAChB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,MAAM,EAAE,IAAI,GAAG,OAAO,EACtB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,QAAA,MAAM,KAAK;;;CAAmC,CAAC;AAE/C,eAAe,KAAK,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Audit logger — append-only JSON Lines log for all write operations.
3
+ *
4
+ * Logs are stored at the XDG data directory:
5
+ * ~/.local/share/email-mcp/audit.log
6
+ */
7
+ import fs from 'node:fs/promises';
8
+ import path from 'node:path';
9
+ import { xdg } from '../config/xdg.js';
10
+ const AUDIT_LOG_PATH = path.join(xdg.data, 'audit.log');
11
+ /** Fields redacted from audit params to protect sensitive data. */
12
+ const REDACTED_FIELDS = new Set(['password', 'body', 'bodyText', 'bodyHtml', 'content_base64']);
13
+ function redactParams(params) {
14
+ const cleaned = {};
15
+ Object.entries(params).forEach(([key, value]) => {
16
+ if (REDACTED_FIELDS.has(key)) {
17
+ cleaned[key] = '[REDACTED]';
18
+ }
19
+ else if (value && typeof value === 'object' && !Array.isArray(value)) {
20
+ cleaned[key] = redactParams(value);
21
+ }
22
+ else {
23
+ cleaned[key] = value;
24
+ }
25
+ });
26
+ return cleaned;
27
+ }
28
+ async function ensureLogDir() {
29
+ await fs.mkdir(path.dirname(AUDIT_LOG_PATH), { recursive: true });
30
+ }
31
+ /**
32
+ * Append a single audit entry to the log file.
33
+ * Creates the directory and file if they don't exist.
34
+ */
35
+ async function log(tool, account, params, result, error) {
36
+ await ensureLogDir();
37
+ const entry = {
38
+ ts: new Date().toISOString(),
39
+ tool,
40
+ account,
41
+ params: redactParams(params),
42
+ result,
43
+ ...(error ? { error } : {}),
44
+ };
45
+ const line = `${JSON.stringify(entry)}\n`;
46
+ await fs.appendFile(AUDIT_LOG_PATH, line, 'utf-8');
47
+ }
48
+ const audit = { log, AUDIT_LOG_PATH };
49
+ export default audit;
50
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/safety/audit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAIvC,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AAExD,mEAAmE;AACnE,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAEhG,SAAS,YAAY,CAAC,MAA+B;IACnD,MAAM,OAAO,GAA4B,EAAE,CAAC;IAC5C,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QAC9C,IAAI,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC;QAC9B,CAAC;aAAM,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACvE,OAAO,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,KAAgC,CAAC,CAAC;QAChE,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,YAAY;IACzB,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACpE,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,GAAG,CAChB,IAAY,EACZ,OAAe,EACf,MAA+B,EAC/B,MAAsB,EACtB,KAAc;IAEd,MAAM,YAAY,EAAE,CAAC;IAErB,MAAM,KAAK,GAAe;QACxB,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC5B,IAAI;QACJ,OAAO;QACP,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC;QAC5B,MAAM;QACN,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5B,CAAC;IAEF,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;IAC1C,MAAM,EAAE,CAAC,UAAU,CAAC,cAAc,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,KAAK,GAAG,EAAE,GAAG,EAAE,cAAc,EAAW,CAAC;AAE/C,eAAe,KAAK,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Token-bucket rate limiter.
3
+ *
4
+ * Limits the number of send operations per account within a time window.
5
+ * Defaults to 10 sends per 60-second window.
6
+ */
7
+ export default class RateLimiter {
8
+ private buckets;
9
+ private readonly maxTokens;
10
+ private readonly windowMs;
11
+ constructor(maxPerMinute?: number);
12
+ /**
13
+ * Try to consume a token for the given account.
14
+ * @returns `true` if allowed, `false` if rate-limited.
15
+ */
16
+ tryConsume(accountName: string): boolean;
17
+ /**
18
+ * Get remaining tokens for an account.
19
+ */
20
+ remaining(accountName: string): number;
21
+ }
22
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/safety/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,OAAO,OAAO,WAAW;IAC9B,OAAO,CAAC,OAAO,CAA6D;IAE5E,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,YAAY,SAAK;IAK7B;;;OAGG;IACH,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAyBxC;;OAEG;IACH,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;CASvC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Token-bucket rate limiter.
3
+ *
4
+ * Limits the number of send operations per account within a time window.
5
+ * Defaults to 10 sends per 60-second window.
6
+ */
7
+ export default class RateLimiter {
8
+ buckets = new Map();
9
+ maxTokens;
10
+ windowMs;
11
+ constructor(maxPerMinute = 10) {
12
+ this.maxTokens = maxPerMinute;
13
+ this.windowMs = 60_000;
14
+ }
15
+ /**
16
+ * Try to consume a token for the given account.
17
+ * @returns `true` if allowed, `false` if rate-limited.
18
+ */
19
+ tryConsume(accountName) {
20
+ const now = Date.now();
21
+ let bucket = this.buckets.get(accountName);
22
+ if (!bucket) {
23
+ bucket = { tokens: this.maxTokens, lastRefill: now };
24
+ this.buckets.set(accountName, bucket);
25
+ }
26
+ // Refill tokens based on elapsed time
27
+ const elapsed = now - bucket.lastRefill;
28
+ const refill = Math.floor((elapsed / this.windowMs) * this.maxTokens);
29
+ if (refill > 0) {
30
+ bucket.tokens = Math.min(this.maxTokens, bucket.tokens + refill);
31
+ bucket.lastRefill = now;
32
+ }
33
+ if (bucket.tokens > 0) {
34
+ bucket.tokens -= 1;
35
+ return true;
36
+ }
37
+ return false;
38
+ }
39
+ /**
40
+ * Get remaining tokens for an account.
41
+ */
42
+ remaining(accountName) {
43
+ const bucket = this.buckets.get(accountName);
44
+ if (!bucket)
45
+ return this.maxTokens;
46
+ // Calculate refill
47
+ const elapsed = Date.now() - bucket.lastRefill;
48
+ const refill = Math.floor((elapsed / this.windowMs) * this.maxTokens);
49
+ return Math.min(this.maxTokens, bucket.tokens + refill);
50
+ }
51
+ }
52
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/safety/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,OAAO,OAAO,WAAW;IACtB,OAAO,GAAG,IAAI,GAAG,EAAkD,CAAC;IAE3D,SAAS,CAAS;IAElB,QAAQ,CAAS;IAElC,YAAY,YAAY,GAAG,EAAE;QAC3B,IAAI,CAAC,SAAS,GAAG,YAAY,CAAC;QAC9B,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC;IACzB,CAAC;IAED;;;OAGG;IACH,UAAU,CAAC,WAAmB;QAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAE3C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC;YACrD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACxC,CAAC;QAED,sCAAsC;QACtC,MAAM,OAAO,GAAG,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;QACtE,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YACf,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;YACjE,MAAM,CAAC,UAAU,GAAG,GAAG,CAAC;QAC1B,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;YACnB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,WAAmB;QAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC;QAEnC,mBAAmB;QACnB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAC1D,CAAC;CACF"}
@@ -0,0 +1,44 @@
1
+ /** Input validation and sanitization utilities. */
2
+ /**
3
+ * Validate and sanitize an IMAP mailbox name.
4
+ * Rejects names containing IMAP wildcard characters (`*`, `%`) or empty strings.
5
+ * @param name - The mailbox name to validate.
6
+ * @returns The trimmed mailbox name.
7
+ */
8
+ export declare function sanitizeMailboxName(name: string): string;
9
+ /**
10
+ * Strip control characters from an IMAP search query.
11
+ * Removes ASCII 0-31 except tab (0x09) and newline (0x0A).
12
+ * @param query - The raw search query.
13
+ * @returns The sanitized query string.
14
+ */
15
+ export declare function sanitizeSearchQuery(query: string): string;
16
+ /**
17
+ * Validate a webhook URL.
18
+ * Ensures the URL uses http(s) and does not point to a private or loopback address.
19
+ * @param url - The webhook URL to validate.
20
+ */
21
+ export declare function validateWebhookUrl(url: string): void;
22
+ /**
23
+ * Sanitize a value for use in a template.
24
+ * When `html` is true, HTML-special characters are escaped.
25
+ * @param value - The template variable value.
26
+ * @param html - Whether to apply HTML escaping.
27
+ * @returns The sanitized value.
28
+ */
29
+ export declare function sanitizeTemplateVariable(value: string, html: boolean): string;
30
+ /**
31
+ * Validate an email label name.
32
+ * Rejects labels with control characters or that exceed 200 characters.
33
+ * @param name - The label name to validate.
34
+ * @returns The trimmed label name.
35
+ */
36
+ export declare function validateLabelName(name: string): string;
37
+ /**
38
+ * Validate that an input string does not exceed a maximum length.
39
+ * @param input - The input string to check.
40
+ * @param maxLength - The maximum allowed length.
41
+ * @param fieldName - The field name for the error message.
42
+ */
43
+ export declare function validateInputLength(input: string, maxLength: number, fieldName: string): void;
44
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/safety/validation.ts"],"names":[],"mappings":"AAAA,mDAAmD;AAEnD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASxD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOzD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CA2BpD;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAU7E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAetD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAI7F"}
@@ -0,0 +1,113 @@
1
+ /** Input validation and sanitization utilities. */
2
+ /**
3
+ * Validate and sanitize an IMAP mailbox name.
4
+ * Rejects names containing IMAP wildcard characters (`*`, `%`) or empty strings.
5
+ * @param name - The mailbox name to validate.
6
+ * @returns The trimmed mailbox name.
7
+ */
8
+ export function sanitizeMailboxName(name) {
9
+ const trimmed = name.trim();
10
+ if (trimmed.length === 0) {
11
+ throw new Error('Mailbox name must not be empty');
12
+ }
13
+ if (trimmed.includes('*') || trimmed.includes('%')) {
14
+ throw new Error('Mailbox name must not contain IMAP wildcard characters (* or %)');
15
+ }
16
+ return trimmed;
17
+ }
18
+ /**
19
+ * Strip control characters from an IMAP search query.
20
+ * Removes ASCII 0-31 except tab (0x09) and newline (0x0A).
21
+ * @param query - The raw search query.
22
+ * @returns The sanitized query string.
23
+ */
24
+ export function sanitizeSearchQuery(query) {
25
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional — sanitize control chars from user input
26
+ const cleaned = query.replace(/[\x00-\x08\x0B-\x1F]/g, '').trim(); // eslint-disable-line no-control-regex
27
+ if (cleaned.length === 0) {
28
+ throw new Error('Search query must not be empty after sanitization');
29
+ }
30
+ return cleaned;
31
+ }
32
+ /**
33
+ * Validate a webhook URL.
34
+ * Ensures the URL uses http(s) and does not point to a private or loopback address.
35
+ * @param url - The webhook URL to validate.
36
+ */
37
+ export function validateWebhookUrl(url) {
38
+ let parsed;
39
+ try {
40
+ parsed = new URL(url);
41
+ }
42
+ catch {
43
+ throw new Error(`Invalid webhook URL: ${url}`);
44
+ }
45
+ if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
46
+ throw new Error(`Webhook URL must use http or https protocol, got ${parsed.protocol}`);
47
+ }
48
+ const hostname = parsed.hostname.toLowerCase();
49
+ // new URL('https://[::1]') stores hostname as '[::1]'
50
+ const bare = hostname.replace(/^\[|\]$/g, '');
51
+ if (bare === 'localhost' || bare === '::1' || bare === '0.0.0.0') {
52
+ throw new Error(`Webhook URL must not point to a loopback or private address: ${bare}`);
53
+ }
54
+ const ipv4Match = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(bare);
55
+ if (ipv4Match) {
56
+ const [, a, b] = ipv4Match.map(Number);
57
+ if (a === 127 || a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168)) {
58
+ throw new Error(`Webhook URL must not point to a loopback or private address: ${bare}`);
59
+ }
60
+ }
61
+ }
62
+ /**
63
+ * Sanitize a value for use in a template.
64
+ * When `html` is true, HTML-special characters are escaped.
65
+ * @param value - The template variable value.
66
+ * @param html - Whether to apply HTML escaping.
67
+ * @returns The sanitized value.
68
+ */
69
+ export function sanitizeTemplateVariable(value, html) {
70
+ if (!html) {
71
+ return value;
72
+ }
73
+ return value
74
+ .replace(/&/g, '&amp;')
75
+ .replace(/</g, '&lt;')
76
+ .replace(/>/g, '&gt;')
77
+ .replace(/"/g, '&quot;')
78
+ .replace(/'/g, '&#39;');
79
+ }
80
+ /**
81
+ * Validate an email label name.
82
+ * Rejects labels with control characters or that exceed 200 characters.
83
+ * @param name - The label name to validate.
84
+ * @returns The trimmed label name.
85
+ */
86
+ export function validateLabelName(name) {
87
+ const trimmed = name.trim();
88
+ if (trimmed.length === 0) {
89
+ throw new Error('Label name must not be empty');
90
+ }
91
+ if (trimmed.length > 200) {
92
+ throw new Error('Label name must not exceed 200 characters');
93
+ }
94
+ /* eslint-disable no-control-regex */
95
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional — reject control chars in label names
96
+ if (/[\x00-\x1F]/.test(trimmed)) {
97
+ throw new Error('Label name must not contain control characters');
98
+ }
99
+ /* eslint-enable no-control-regex */
100
+ return trimmed;
101
+ }
102
+ /**
103
+ * Validate that an input string does not exceed a maximum length.
104
+ * @param input - The input string to check.
105
+ * @param maxLength - The maximum allowed length.
106
+ * @param fieldName - The field name for the error message.
107
+ */
108
+ export function validateInputLength(input, maxLength, fieldName) {
109
+ if (input.length > maxLength) {
110
+ throw new Error(`${fieldName} exceeds maximum length of ${maxLength} characters`);
111
+ }
112
+ }
113
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/safety/validation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * MCP Server factory.
3
+ *
4
+ * Creates and configures the McpServer instance with capabilities.
5
+ */
6
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
7
+ export declare const PKG_NAME = "email-mcp";
8
+ export declare const PKG_VERSION: string;
9
+ export default function createServer(): McpServer;
10
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,eAAO,MAAM,QAAQ,cAAc,CAAC;AACpC,eAAO,MAAM,WAAW,QAAc,CAAC;AAEvC,MAAM,CAAC,OAAO,UAAU,YAAY,IAAI,SAAS,CAehD"}
package/dist/server.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * MCP Server factory.
3
+ *
4
+ * Creates and configures the McpServer instance with capabilities.
5
+ */
6
+ import { createRequire } from 'node:module';
7
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
8
+ const esmRequire = createRequire(import.meta.url);
9
+ const pkg = esmRequire('../package.json');
10
+ export const PKG_NAME = 'email-mcp';
11
+ export const PKG_VERSION = pkg.version;
12
+ export default function createServer() {
13
+ return new McpServer({
14
+ name: PKG_NAME,
15
+ version: PKG_VERSION,
16
+ }, {
17
+ capabilities: {
18
+ tools: { listChanged: true },
19
+ prompts: { listChanged: true },
20
+ resources: { subscribe: true, listChanged: true },
21
+ logging: {},
22
+ },
23
+ });
24
+ }
25
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,GAAG,GAAG,UAAU,CAAC,iBAAiB,CAAwB,CAAC;AAEjE,MAAM,CAAC,MAAM,QAAQ,GAAG,WAAW,CAAC;AACpC,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC;AAEvC,MAAM,CAAC,OAAO,UAAU,YAAY;IAClC,OAAO,IAAI,SAAS,CAClB;QACE,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,WAAW;KACrB,EACD;QACE,YAAY,EAAE;YACZ,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE;YAC5B,OAAO,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE;YAC9B,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE;YACjD,OAAO,EAAE,EAAE;SACZ;KACF,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Calendar service — ICS/iCalendar parsing.
3
+ *
4
+ * Extracts structured calendar events from ICS content using node-ical.
5
+ */
6
+ import type { CalendarEvent } from '../types/index.js';
7
+ export default class CalendarService {
8
+ /**
9
+ * Parse ICS text content into structured CalendarEvent objects.
10
+ */
11
+ static parseIcs(content: string): CalendarEvent[];
12
+ /**
13
+ * Extract calendar events from raw email body parts.
14
+ * Accepts an array of ICS content strings (from inline text/calendar parts
15
+ * or downloaded .ics attachments).
16
+ */
17
+ extractFromParts(icsContents: string[]): CalendarEvent[];
18
+ private static parseOrganizer;
19
+ private static parseAttendees;
20
+ private static normalizeStatus;
21
+ }
22
+ //# sourceMappingURL=calendar.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendar.service.d.ts","sourceRoot":"","sources":["../../src/services/calendar.service.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAgB,MAAM,mBAAmB,CAAC;AAIrE,MAAM,CAAC,OAAO,OAAO,eAAe;IAClC;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE;IA2CjD;;;;OAIG;IAEH,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE;IAsBxD,OAAO,CAAC,MAAM,CAAC,cAAc;IAiB7B,OAAO,CAAC,MAAM,CAAC,cAAc;IAoB7B,OAAO,CAAC,MAAM,CAAC,eAAe;CAQ/B"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Calendar service — ICS/iCalendar parsing.
3
+ *
4
+ * Extracts structured calendar events from ICS content using node-ical.
5
+ */
6
+ import ical from 'node-ical';
7
+ export default class CalendarService {
8
+ /**
9
+ * Parse ICS text content into structured CalendarEvent objects.
10
+ */
11
+ static parseIcs(content) {
12
+ const parsed = ical.sync.parseICS(content);
13
+ const events = [];
14
+ // Extract the VCALENDAR METHOD if present
15
+ let calendarMethod;
16
+ Object.values(parsed).forEach((comp) => {
17
+ if (comp?.type === 'VCALENDAR') {
18
+ calendarMethod = comp.method;
19
+ }
20
+ });
21
+ Object.values(parsed).forEach((comp) => {
22
+ if (!comp || comp.type !== 'VEVENT')
23
+ return;
24
+ const event = comp;
25
+ const start = event.start;
26
+ const end = event.end;
27
+ const organizer = CalendarService.parseOrganizer(event.organizer);
28
+ const attendees = CalendarService.parseAttendees(event.attendee);
29
+ const rrule = event.rrule;
30
+ events.push({
31
+ uid: event.uid ?? '',
32
+ summary: event.summary ?? '(No title)',
33
+ description: event.description,
34
+ start: start ? start.toISOString() : '',
35
+ end: end ? end.toISOString() : '',
36
+ location: event.location,
37
+ organizer,
38
+ attendees,
39
+ status: CalendarService.normalizeStatus(event.status),
40
+ method: calendarMethod,
41
+ recurrence: rrule?.toString(),
42
+ });
43
+ });
44
+ // Sort by start time
45
+ events.sort((a, b) => a.start.localeCompare(b.start));
46
+ return events;
47
+ }
48
+ /**
49
+ * Extract calendar events from raw email body parts.
50
+ * Accepts an array of ICS content strings (from inline text/calendar parts
51
+ * or downloaded .ics attachments).
52
+ */
53
+ // eslint-disable-next-line class-methods-use-this
54
+ extractFromParts(icsContents) {
55
+ const allEvents = [];
56
+ const seenUids = new Set();
57
+ icsContents.forEach((content) => {
58
+ const events = CalendarService.parseIcs(content);
59
+ events.forEach((event) => {
60
+ if (!seenUids.has(event.uid)) {
61
+ seenUids.add(event.uid);
62
+ allEvents.push(event);
63
+ }
64
+ });
65
+ });
66
+ allEvents.sort((a, b) => a.start.localeCompare(b.start));
67
+ return allEvents;
68
+ }
69
+ // ---------------------------------------------------------------------------
70
+ // Private helpers
71
+ // ---------------------------------------------------------------------------
72
+ static parseOrganizer(organizer) {
73
+ if (!organizer)
74
+ return undefined;
75
+ if (typeof organizer === 'string') {
76
+ const email = organizer.replace(/^mailto:/i, '');
77
+ return { address: email };
78
+ }
79
+ const org = organizer;
80
+ const val = org.val ?? '';
81
+ const email = val.replace(/^mailto:/i, '');
82
+ return {
83
+ name: org.params?.CN,
84
+ address: email,
85
+ };
86
+ }
87
+ static parseAttendees(attendees) {
88
+ if (!attendees)
89
+ return [];
90
+ const list = Array.isArray(attendees) ? attendees : [attendees];
91
+ return list
92
+ .map((att) => {
93
+ if (typeof att === 'string') {
94
+ return { address: att.replace(/^mailto:/i, '') };
95
+ }
96
+ const a = att;
97
+ const val = a.val ?? '';
98
+ const email = val.replace(/^mailto:/i, '');
99
+ return {
100
+ name: a.params?.CN,
101
+ address: email,
102
+ };
103
+ })
104
+ .filter((a) => a.address);
105
+ }
106
+ static normalizeStatus(status) {
107
+ const upper = (status ?? 'CONFIRMED').toUpperCase();
108
+ if (upper === 'TENTATIVE')
109
+ return 'TENTATIVE';
110
+ if (upper === 'CANCELLED')
111
+ return 'CANCELLED';
112
+ return 'CONFIRMED';
113
+ }
114
+ }
115
+ //# sourceMappingURL=calendar.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendar.service.js","sourceRoot":"","sources":["../../src/services/calendar.service.ts"],"names":[],"mappings":"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"}