@n24q02m/better-email-mcp 1.0.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.
Files changed (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +162 -0
  3. package/bin/cli.mjs +13 -0
  4. package/build/scripts/start-server.d.ts +6 -0
  5. package/build/scripts/start-server.d.ts.map +1 -0
  6. package/build/scripts/start-server.js +21 -0
  7. package/build/scripts/start-server.js.map +1 -0
  8. package/build/src/docs/attachments.md +48 -0
  9. package/build/src/docs/folders.md +45 -0
  10. package/build/src/docs/messages.md +92 -0
  11. package/build/src/docs/send.md +52 -0
  12. package/build/src/init-server.d.ts +40 -0
  13. package/build/src/init-server.d.ts.map +1 -0
  14. package/build/src/init-server.js +57 -0
  15. package/build/src/init-server.js.map +1 -0
  16. package/build/src/tools/composite/attachments.d.ts +17 -0
  17. package/build/src/tools/composite/attachments.d.ts.map +1 -0
  18. package/build/src/tools/composite/attachments.js +77 -0
  19. package/build/src/tools/composite/attachments.js.map +1 -0
  20. package/build/src/tools/composite/attachments.test.d.ts +2 -0
  21. package/build/src/tools/composite/attachments.test.d.ts.map +1 -0
  22. package/build/src/tools/composite/attachments.test.js +138 -0
  23. package/build/src/tools/composite/attachments.test.js.map +1 -0
  24. package/build/src/tools/composite/folders.d.ts +14 -0
  25. package/build/src/tools/composite/folders.d.ts.map +1 -0
  26. package/build/src/tools/composite/folders.js +57 -0
  27. package/build/src/tools/composite/folders.js.map +1 -0
  28. package/build/src/tools/composite/folders.test.d.ts +2 -0
  29. package/build/src/tools/composite/folders.test.d.ts.map +1 -0
  30. package/build/src/tools/composite/folders.test.js +61 -0
  31. package/build/src/tools/composite/folders.test.js.map +1 -0
  32. package/build/src/tools/composite/messages.d.ts +20 -0
  33. package/build/src/tools/composite/messages.d.ts.map +1 -0
  34. package/build/src/tools/composite/messages.js +244 -0
  35. package/build/src/tools/composite/messages.js.map +1 -0
  36. package/build/src/tools/composite/messages.test.d.ts +2 -0
  37. package/build/src/tools/composite/messages.test.d.ts.map +1 -0
  38. package/build/src/tools/composite/messages.test.js +217 -0
  39. package/build/src/tools/composite/messages.test.js.map +1 -0
  40. package/build/src/tools/composite/send.d.ts +21 -0
  41. package/build/src/tools/composite/send.d.ts.map +1 -0
  42. package/build/src/tools/composite/send.js +127 -0
  43. package/build/src/tools/composite/send.js.map +1 -0
  44. package/build/src/tools/composite/send.test.d.ts +2 -0
  45. package/build/src/tools/composite/send.test.d.ts.map +1 -0
  46. package/build/src/tools/composite/send.test.js +232 -0
  47. package/build/src/tools/composite/send.test.js.map +1 -0
  48. package/build/src/tools/helpers/config.d.ts +33 -0
  49. package/build/src/tools/helpers/config.d.ts.map +1 -0
  50. package/build/src/tools/helpers/config.js +167 -0
  51. package/build/src/tools/helpers/config.js.map +1 -0
  52. package/build/src/tools/helpers/config.test.d.ts +2 -0
  53. package/build/src/tools/helpers/config.test.d.ts.map +1 -0
  54. package/build/src/tools/helpers/config.test.js +141 -0
  55. package/build/src/tools/helpers/config.test.js.map +1 -0
  56. package/build/src/tools/helpers/errors.d.ts +34 -0
  57. package/build/src/tools/helpers/errors.d.ts.map +1 -0
  58. package/build/src/tools/helpers/errors.js +152 -0
  59. package/build/src/tools/helpers/errors.js.map +1 -0
  60. package/build/src/tools/helpers/errors.test.d.ts +2 -0
  61. package/build/src/tools/helpers/errors.test.d.ts.map +1 -0
  62. package/build/src/tools/helpers/errors.test.js +203 -0
  63. package/build/src/tools/helpers/errors.test.js.map +1 -0
  64. package/build/src/tools/helpers/html-utils.d.ts +10 -0
  65. package/build/src/tools/helpers/html-utils.d.ts.map +1 -0
  66. package/build/src/tools/helpers/html-utils.js +29 -0
  67. package/build/src/tools/helpers/html-utils.js.map +1 -0
  68. package/build/src/tools/helpers/html-utils.test.d.ts +2 -0
  69. package/build/src/tools/helpers/html-utils.test.d.ts.map +1 -0
  70. package/build/src/tools/helpers/html-utils.test.js +103 -0
  71. package/build/src/tools/helpers/html-utils.test.js.map +1 -0
  72. package/build/src/tools/helpers/imap-client.d.ts +90 -0
  73. package/build/src/tools/helpers/imap-client.d.ts.map +1 -0
  74. package/build/src/tools/helpers/imap-client.js +321 -0
  75. package/build/src/tools/helpers/imap-client.js.map +1 -0
  76. package/build/src/tools/helpers/imap-client.test.d.ts +2 -0
  77. package/build/src/tools/helpers/imap-client.test.d.ts.map +1 -0
  78. package/build/src/tools/helpers/imap-client.test.js +401 -0
  79. package/build/src/tools/helpers/imap-client.test.js.map +1 -0
  80. package/build/src/tools/helpers/smtp-client.d.ts +38 -0
  81. package/build/src/tools/helpers/smtp-client.d.ts.map +1 -0
  82. package/build/src/tools/helpers/smtp-client.js +124 -0
  83. package/build/src/tools/helpers/smtp-client.js.map +1 -0
  84. package/build/src/tools/helpers/smtp-client.test.d.ts +2 -0
  85. package/build/src/tools/helpers/smtp-client.test.d.ts.map +1 -0
  86. package/build/src/tools/helpers/smtp-client.test.js +210 -0
  87. package/build/src/tools/helpers/smtp-client.test.js.map +1 -0
  88. package/build/src/tools/registry.d.ts +11 -0
  89. package/build/src/tools/registry.d.ts.map +1 -0
  90. package/build/src/tools/registry.js +262 -0
  91. package/build/src/tools/registry.js.map +1 -0
  92. package/build/src/tools/registry.test.d.ts +2 -0
  93. package/build/src/tools/registry.test.d.ts.map +1 -0
  94. package/build/src/tools/registry.test.js +135 -0
  95. package/build/src/tools/registry.test.js.map +1 -0
  96. package/build/tsconfig.tsbuildinfo +1 -0
  97. package/package.json +81 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 n24q02m
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,162 @@
1
+ # Better Email MCP
2
+
3
+ **IMAP/SMTP MCP Server for Email - Optimized for AI Agents**
4
+
5
+ [![CI](https://github.com/n24q02m/better-email-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/n24q02m/better-email-mcp/actions/workflows/ci.yml)
6
+ [![npm](https://img.shields.io/npm/v/@n24q02m/better-email-mcp)](https://www.npmjs.com/package/@n24q02m/better-email-mcp)
7
+ [![Docker](https://img.shields.io/docker/v/n24q02m/better-email-mcp?label=docker)](https://hub.docker.com/r/n24q02m/better-email-mcp)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
9
+
10
+ ## Why "Better"?
11
+
12
+ **5 composite tools** that provide full email operations (search, read, send, reply, forward, organize) across multiple accounts using IMAP/SMTP with App Passwords.
13
+
14
+ ### Key Features
15
+
16
+ | Feature | Description |
17
+ |---------|-------------|
18
+ | **Multi-Account** | Manage 6+ email accounts simultaneously |
19
+ | **App Passwords** | No OAuth2 setup required - clone and run in 1 minute |
20
+ | **Auto-Discovery** | Gmail, Outlook, Yahoo, iCloud, Zoho, ProtonMail auto-configured |
21
+ | **Clean Text** | HTML stripped for LLM token savings |
22
+ | **Thread Support** | Reply/forward maintains In-Reply-To and References headers |
23
+ | **Composite Tools** | 5 tools with 15 actions (not 15+ separate endpoints) |
24
+
25
+ ---
26
+
27
+ ## Quick Start
28
+
29
+ ### Prerequisites
30
+
31
+ Create App Passwords (NOT your regular password):
32
+ - **Gmail**: Enable 2FA, then <https://myaccount.google.com/apppasswords>
33
+ - **Outlook**: Enable 2FA, then Security settings > App passwords
34
+
35
+ ### Option 1: npx (Recommended)
36
+
37
+ ```jsonc
38
+ {
39
+ "mcpServers": {
40
+ "better-email": {
41
+ "command": "npx",
42
+ "args": ["-y", "@n24q02m/better-email-mcp@latest"],
43
+ "env": {
44
+ "EMAIL_CREDENTIALS": "user@gmail.com:abcd-efgh-ijkl-mnop"
45
+ }
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ ### Option 2: Docker
52
+
53
+ ```jsonc
54
+ {
55
+ "mcpServers": {
56
+ "better-email": {
57
+ "command": "docker",
58
+ "args": [
59
+ "run", "-i", "--rm",
60
+ "--name", "mcp-email",
61
+ "-e", "EMAIL_CREDENTIALS",
62
+ "n24q02m/better-email-mcp:latest"
63
+ ],
64
+ "env": {
65
+ "EMAIL_CREDENTIALS": "user@gmail.com:abcd-efgh-ijkl-mnop"
66
+ }
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ ### Multiple Accounts
73
+
74
+ ```bash
75
+ EMAIL_CREDENTIALS=user1@gmail.com:pass1,user2@outlook.com:pass2,user3@yahoo.com:pass3
76
+ ```
77
+
78
+ ### Custom IMAP Host
79
+
80
+ ```bash
81
+ EMAIL_CREDENTIALS=user@custom.com:password:imap.custom.com
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Tools
87
+
88
+ | Tool | Actions |
89
+ |------|---------|
90
+ | `messages` | search, read, mark_read, mark_unread, flag, unflag, move, archive, trash |
91
+ | `folders` | list |
92
+ | `attachments` | list, download |
93
+ | `send` | new, reply, forward |
94
+ | `help` | Get full documentation for any tool |
95
+
96
+ ### Search Query Language
97
+
98
+ | Query | Description |
99
+ |-------|-------------|
100
+ | `UNREAD` | Unread emails |
101
+ | `FLAGGED` | Starred emails |
102
+ | `SINCE 2024-01-01` | Emails after date |
103
+ | `FROM boss@company.com` | Emails from sender |
104
+ | `SUBJECT meeting` | Emails matching subject |
105
+ | `UNREAD SINCE 2024-06-01` | Compound filter |
106
+ | `UNREAD FROM boss@company.com` | Compound filter |
107
+
108
+ ---
109
+
110
+ ## Token Optimization
111
+
112
+ **Tiered descriptions** for minimal context usage:
113
+
114
+ | Tier | Purpose | When |
115
+ |------|---------|------|
116
+ | **Tier 1** | Compressed descriptions | Always loaded |
117
+ | **Tier 2** | Full docs via `help` tool | On-demand |
118
+ | **Tier 3** | MCP Resources | Supported clients |
119
+
120
+ ```json
121
+ {"name": "help", "tool_name": "messages"}
122
+ ```
123
+
124
+ ### MCP Resources (Tier 3)
125
+
126
+ | URI | Description |
127
+ |-----|-------------|
128
+ | `email://docs/messages` | Messages tool docs |
129
+ | `email://docs/folders` | Folders tool docs |
130
+ | `email://docs/attachments` | Attachments tool docs |
131
+ | `email://docs/send` | Send tool docs |
132
+
133
+ ---
134
+
135
+ ## Supported Providers
136
+
137
+ | Provider | Auto-Discovery | IMAP | SMTP |
138
+ |----------|---------------|------|------|
139
+ | Gmail | `imap.gmail.com:993` | TLS | TLS (465) |
140
+ | Outlook/Hotmail/Live | `outlook.office365.com:993` | TLS | STARTTLS (587) |
141
+ | Yahoo | `imap.mail.yahoo.com:993` | TLS | TLS (465) |
142
+ | iCloud/Me.com | `imap.mail.me.com:993` | TLS | STARTTLS (587) |
143
+ | Zoho | `imap.zoho.com:993` | TLS | TLS (465) |
144
+ | ProtonMail | `imap.protonmail.ch:993` | TLS | TLS (465) |
145
+ | Custom | Via `email:pass:imap.host` format | Configurable | Auto-derived |
146
+
147
+ ---
148
+
149
+ ## Build from Source
150
+
151
+ ```bash
152
+ git clone https://github.com/n24q02m/better-email-mcp
153
+ cd better-email-mcp
154
+ mise run setup
155
+ pnpm build
156
+ ```
157
+
158
+ **Requirements:** Node.js 24+, pnpm
159
+
160
+ ## License
161
+
162
+ MIT - See [LICENSE](LICENSE)
package/bin/cli.mjs ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'module';const require = createRequire(import.meta.url);
3
+ import{readFileSync as Le}from"node:fs";import{dirname as xe,join as Pe}from"node:path";import{fileURLToPath as je}from"node:url";import{Server as Me}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as Ue}from"@modelcontextprotocol/sdk/server/stdio.js";var C={"gmail.com":{imap:{host:"imap.gmail.com",port:993,secure:!0},smtp:{host:"smtp.gmail.com",port:465,secure:!0}},"googlemail.com":{imap:{host:"imap.gmail.com",port:993,secure:!0},smtp:{host:"smtp.gmail.com",port:465,secure:!0}},"outlook.com":{imap:{host:"outlook.office365.com",port:993,secure:!0},smtp:{host:"smtp.office365.com",port:587,secure:!1}},"hotmail.com":{imap:{host:"outlook.office365.com",port:993,secure:!0},smtp:{host:"smtp.office365.com",port:587,secure:!1}},"live.com":{imap:{host:"outlook.office365.com",port:993,secure:!0},smtp:{host:"smtp.office365.com",port:587,secure:!1}},"yahoo.com":{imap:{host:"imap.mail.yahoo.com",port:993,secure:!0},smtp:{host:"smtp.mail.yahoo.com",port:465,secure:!0}},"icloud.com":{imap:{host:"imap.mail.me.com",port:993,secure:!0},smtp:{host:"smtp.mail.me.com",port:587,secure:!1}},"me.com":{imap:{host:"imap.mail.me.com",port:993,secure:!0},smtp:{host:"smtp.mail.me.com",port:587,secure:!1}},"zoho.com":{imap:{host:"imap.zoho.com",port:993,secure:!0},smtp:{host:"smtp.zoho.com",port:465,secure:!0}},"protonmail.com":{imap:{host:"imap.protonmail.ch",port:993,secure:!0},smtp:{host:"smtp.protonmail.ch",port:465,secure:!0}}};function ee(t){let e=t.split("@")[1]?.toLowerCase();if(!e)return null;if(C[e])return C[e];for(let[o,n]of Object.entries(C))if(e.endsWith(`.${o}`))return n;return null}function te(t){return t.replace(/[@.]/g,"_").toLowerCase()}function re(t){if(!t||t.trim()==="")return[];let e=[],o=t.split(",");for(let n of o){let r=n.trim();if(!r)continue;let s=r.split(":");if(s.length<2){console.error(`Skipping invalid credential entry (expected email:password): ${r.substring(0,20)}...`);continue}let i=s[0].trim(),c,l;if(s.length===2)c=s[1];else if(s.length===3){let u=s[2];u.includes(".")?(c=s[1],l=u):c=`${s[1]}:${s[2]}`}else{let u=s[s.length-1];u.includes(".")?(c=s.slice(1,-1).join(":"),l=u):c=s.slice(1).join(":")}let m,f;if(l)m={host:l,port:993,secure:!0},f={host:l.replace("imap.","smtp."),port:587,secure:!1};else{let u=ee(i);if(!u){console.error(`Cannot auto-discover settings for ${i}. Use format: email:password:imap.server.com`);continue}m=u.imap,f=u.smtp}e.push({id:te(i),email:i,password:c,imap:m,smtp:f})}return e}function v(){let t=process.env.EMAIL_CREDENTIALS;return t?re(t):[]}import{readFileSync as X}from"node:fs";import{dirname as Re,join as E}from"node:path";import{fileURLToPath as Oe}from"node:url";import{CallToolRequestSchema as Se,ListResourcesRequestSchema as Te,ListToolsRequestSchema as Ne,ReadResourceRequestSchema as ve}from"@modelcontextprotocol/sdk/types.js";var a=class extends Error{constructor(o,n,r,s){super(o);this.code=n;this.suggestion=r;this.details=s;this.name="EmailMCPError"}toJSON(){return{error:this.name,code:this.code,message:this.message,suggestion:this.suggestion,details:this.details}}};function D(t){if(!t||typeof t!="object")return t;let e={message:t.message,name:t.name,code:t.code};return t.status&&(e.status=t.status),t.responseCode&&(e.responseCode=t.responseCode),e}function I(t){let e=t.message||"Unknown error occurred";return e.includes("Invalid credentials")||e.includes("AUTHENTICATIONFAILED")||t.authenticationFailed?new a("Email authentication failed","AUTH_FAILED","Check that your email and App Password are correct. For Gmail: enable 2FA then create an App Password at https://myaccount.google.com/apppasswords. For Outlook: enable 2FA then create an App Password in security settings."):e.includes("ECONNREFUSED")||e.includes("ENOTFOUND")||e.includes("ETIMEDOUT")?new a("Cannot connect to email server","CONNECTION_ERROR","Check your internet connection and verify the email server address is correct."):e.includes("CERT")||e.includes("SSL")||e.includes("TLS")?new a("TLS/SSL connection error","TLS_ERROR","The email server certificate could not be verified. Check the server address and port."):e.includes("Mailbox not found")||e.includes("NO [NONEXISTENT]")?new a("Mailbox/folder not found","FOLDER_NOT_FOUND","Check the folder name. Use the folders tool to list available folders."):t.responseCode?oe(t):e.includes("EMAIL_CREDENTIALS")?new a("EMAIL_CREDENTIALS environment variable is required","CONFIG_ERROR","Set EMAIL_CREDENTIALS in format: email1:password1,email2:password2"):new a(e,"UNKNOWN_ERROR","Please check your request and try again",D(t))}function oe(t){let e=t.responseCode;switch(e){case 535:return new a("SMTP authentication failed","SMTP_AUTH_FAILED","Check your email and App Password for the sending account.");case 550:return new a("Recipient address rejected","RECIPIENT_REJECTED","Check the recipient email address is correct and exists.");case 552:case 554:return new a("Message rejected by server","MESSAGE_REJECTED","The email was rejected. It may be too large or flagged as spam.");default:return new a(t.message||`SMTP error ${e}`,`SMTP_${e}`,"Check the SMTP error code and try again.",D(t))}}function L(t){let e=`Error: ${t.message}`;return t.suggestion&&(e+=`
4
+
5
+ Suggestion: ${t.suggestion}`),t.details&&(e+=`
6
+
7
+ Details: ${JSON.stringify(t.details,null,2)}`),e}function p(t){return async(...e)=>{try{return await t(...e)}catch(o){throw I(o)}}}import{ImapFlow as se}from"imapflow";import{simpleParser as P}from"mailparser";import{convert as ne}from"html-to-text";function x(t){return t?ne(t,{wordwrap:!1,preserveNewlines:!0,selectors:[{selector:"style",format:"skip"},{selector:"script",format:"skip"},{selector:"img",format:"skip"},{selector:"a",options:{hideLinkHrefIfSameAsText:!0,ignoreHref:!1}},{selector:"table",format:"dataTable"}]}).trim():""}function ae(t){return new se({host:t.imap.host,port:t.imap.port,secure:t.imap.secure,auth:{user:t.email,pass:t.password},logger:!1})}async function h(t,e){let o=ae(t);try{return await o.connect(),await e(o)}finally{try{await o.logout()}catch{}}}function ie(t){let e=t.toUpperCase().trim();if(e==="UNREAD"||e==="UNSEEN")return{seen:!1};if(e==="READ"||e==="SEEN")return{seen:!0};if(e==="FLAGGED"||e==="STARRED")return{flagged:!0};if(e==="UNFLAGGED"||e==="UNSTARRED")return{flagged:!1};if(e==="ALL"||e==="*")return{};let o=t.match(/^SINCE\s+(\d{4}-\d{2}-\d{2})$/i);if(o)return{since:new Date(o[1])};let n=t.match(/^FROM\s+(.+)$/i);if(n)return{from:n[1]};let r=t.match(/^SUBJECT\s+(.+)$/i);if(r)return{subject:r[1]};let s=t.match(/^UNREAD\s+SINCE\s+(\d{4}-\d{2}-\d{2})$/i);if(s)return{seen:!1,since:new Date(s[1])};let i=t.match(/^UNREAD\s+FROM\s+(.+)$/i);return i?{seen:!1,from:i[1]}:{subject:t}}function ce(t,e=200){let o=t.replace(/\s+/g," ").trim();return o.length<=e?o:`${o.substring(0,e)}...`}function A(t){return t?typeof t=="string"?t:t.text?t.text:Array.isArray(t.value)?t.value.map(e=>e.name?`${e.name} <${e.address}>`:e.address).join(", "):"":""}async function j(t,e,o,n){let r=[],s=ie(e);for(let i of t)try{let c=await h(i,async l=>{let m=await l.getMailboxLock(o);try{let f=[],u=0;for await(let d of l.fetch(s,{uid:!0,flags:!0,envelope:!0,bodyStructure:!0,source:{start:0,maxLength:500}})){if(u>=n)break;let Q=d.source?ce(d.source.toString("utf-8")):"";f.push({account_id:i.id,account_email:i.email,uid:d.uid,message_id:d.envelope?.messageId,subject:d.envelope?.subject||"(No subject)",from:d.envelope?.from?.[0]?`${d.envelope.from[0].name||""} <${d.envelope.from[0].address||""}>`.trim():"",to:d.envelope?.to?.map(Z=>Z.address).join(", ")||"",date:d.envelope?.date?.toISOString()||"",flags:Array.from(d.flags||[]),snippet:Q}),u++}return f}finally{m.release()}});r.push(...c)}catch(c){r.push({account_id:i.id,account_email:i.email,uid:0,subject:`[ERROR] ${c.message}`,from:"",to:"",date:"",flags:[],snippet:`Failed to search ${i.email}: ${c.message}`})}return r}async function y(t,e,o){return h(t,async n=>{let r=await n.getMailboxLock(o);try{let s=await n.fetchOne(`${e}`,{uid:!0,flags:!0,source:!0});if(!s||!s.source)throw new a(`Email UID ${e} not found in ${o}`,"NOT_FOUND","Check the UID and folder");let i=s,c=await P(i.source),l=c.text||(c.html?x(c.html):"(Empty body)");return{account_id:t.id,account_email:t.email,uid:i.uid,message_id:c.messageId,in_reply_to:c.inReplyTo,references:Array.isArray(c.references)?c.references.join(" "):c.references,subject:c.subject||"(No subject)",from:A(c.from),to:A(c.to),cc:A(c.cc),bcc:A(c.bcc),date:c.date?.toISOString()||"",flags:Array.from(i.flags||[]),body_text:l,attachments:(c.attachments||[]).map(m=>({filename:m.filename||"unnamed",content_type:m.contentType||"application/octet-stream",size:m.size||0,content_id:m.contentId}))}}finally{r.release()}})}async function w(t,e,o,n,r){return h(t,async s=>{let i=await s.getMailboxLock(o);try{let c=e.join(",");return r==="add"?await s.messageFlagsAdd({uid:c},n):await s.messageFlagsRemove({uid:c},n),{success:!0,modified:e.length}}finally{i.release()}})}async function _(t,e,o,n){return h(t,async r=>{let s=await r.getMailboxLock(o);try{let i=e.join(",");return await r.messageMove({uid:i},n),{success:!0,moved:e.length}}finally{s.release()}})}async function M(t,e,o){return h(t,async n=>{let r=await n.getMailboxLock(o);try{let s=e.join(",");return await n.messageDelete({uid:s}),{success:!0,trashed:e.length}}finally{r.release()}})}async function b(t){return h(t,async e=>(await e.list()).map(n=>({name:n.name,path:n.path,flags:Array.from(n.flags||[]),delimiter:n.delimiter||"/",children:n.folders?Array.from(n.folders).map(([,r])=>({name:r.name,path:r.path,flags:Array.from(r.flags||[]),delimiter:r.delimiter||"/"})):void 0})))}async function U(t,e,o,n){return h(t,async r=>{let s=await r.getMailboxLock(o);try{let i=await r.fetchOne(`${e}`,{uid:!0,source:!0});if(!i||!i.source)throw new a(`Email UID ${e} not found`,"NOT_FOUND","Check the UID and folder");let c=await P(i.source),l=c.attachments?.find(m=>m.filename?.toLowerCase()===n.toLowerCase());if(!l)throw new a(`Attachment "${n}" not found`,"ATTACHMENT_NOT_FOUND",`Available: ${c.attachments?.map(m=>m.filename).join(", ")||"none"}`);return{filename:l.filename||"unnamed",content_type:l.contentType||"application/octet-stream",size:l.size||0,content_base64:l.content.toString("base64")}}finally{s.release()}})}function k(t,e){let o=e.toLowerCase(),n=t.filter(r=>r.email.toLowerCase()===o||r.id===o||r.email.toLowerCase().includes(o));if(n.length===0)throw new a(`Account not found: ${e}`,"ACCOUNT_NOT_FOUND",`Available accounts: ${t.map(r=>r.email).join(", ")}`);if(n.length>1)throw new a("Multiple accounts matched. Specify the exact account email.","AMBIGUOUS_ACCOUNT",`Matched: ${n.map(r=>r.email).join(", ")}`);return n[0]}async function F(t,e){return p(async()=>{if(!e.account)throw new a("account is required for attachment operations","VALIDATION_ERROR","Provide the account email address");if(!e.uid)throw new a("uid is required for attachment operations","VALIDATION_ERROR","Provide the email UID from search/read");switch(e.action){case"list":return await le(t,e);case"download":return await me(t,e);default:throw new a(`Unknown action: ${e.action}`,"VALIDATION_ERROR","Supported actions: list, download")}})()}async function le(t,e){let o=k(t,e.account),n=e.folder||"INBOX",r=await y(o,e.uid,n);return{action:"list",account:o.email,uid:e.uid,folder:n,subject:r.subject,total:r.attachments.length,attachments:r.attachments}}async function me(t,e){if(!e.filename)throw new a("filename is required for download action","VALIDATION_ERROR","Use attachments list action first to see available filenames");let o=k(t,e.account),n=e.folder||"INBOX",r=await U(o,e.uid,n,e.filename);return{action:"download",account:o.email,uid:e.uid,folder:n,...r}}async function $(t,e){return p(async()=>{switch(e.action){case"list":return await ue(t,e);default:throw new a(`Unknown action: ${e.action}`,"VALIDATION_ERROR","Supported actions: list")}})()}async function ue(t,e){let o=t;if(e.account){let r=e.account.toLowerCase();if(o=t.filter(s=>s.email.toLowerCase()===r||s.id===r||s.email.toLowerCase().includes(r)),o.length===0)throw new a(`Account not found: ${e.account}`,"ACCOUNT_NOT_FOUND",`Available accounts: ${t.map(s=>s.email).join(", ")}`)}let n=[];for(let r of o)try{let s=await b(r);n.push({account_id:r.id,account_email:r.email,folders:s})}catch(s){n.push({account_id:r.id,account_email:r.email,error:s.message,folders:[]})}return{action:"list",total_accounts:n.length,accounts:n}}function q(t,e){if(!e)return t;let o=e.toLowerCase(),n=t.filter(r=>r.email.toLowerCase()===o||r.id===o||r.email.toLowerCase().includes(o));if(n.length===0)throw new a(`Account not found: ${e}`,"ACCOUNT_NOT_FOUND",`Available accounts: ${t.map(r=>r.email).join(", ")}`);return n}function g(t,e){let o=q(t,e);if(o.length>1)throw new a("Multiple accounts matched. Specify the exact account email.","AMBIGUOUS_ACCOUNT",`Matched: ${o.map(n=>n.email).join(", ")}`);return o[0]}async function H(t,e){return p(async()=>{switch(e.action){case"search":return await de(t,e);case"read":return await fe(t,e);case"mark_read":return await pe(t,e);case"mark_unread":return await ge(t,e);case"flag":return await he(t,e);case"unflag":return await ye(t,e);case"move":return await we(t,e);case"archive":return await Ae(t,e);case"trash":return await be(t,e);default:throw new a(`Unknown action: ${e.action}`,"VALIDATION_ERROR","Supported actions: search, read, mark_read, mark_unread, flag, unflag, move, archive, trash")}})()}async function de(t,e){let o=q(t,e.account),n=e.query||"UNSEEN",r=e.folder||"INBOX",s=e.limit||20,i=await j(o,n,r,s);return{action:"search",query:n,folder:r,total:i.length,accounts_searched:o.map(c=>c.email),messages:i}}async function fe(t,e){if(!e.uid)throw new a("uid is required for read action","VALIDATION_ERROR","Provide the email UID from search");let o=g(t,e.account),n=e.folder||"INBOX";return{action:"read",...await y(o,e.uid,n)}}async function pe(t,e){let o=e.uids||(e.uid?[e.uid]:[]);if(o.length===0)throw new a("uid or uids required","VALIDATION_ERROR","Provide at least one email UID");let n=g(t,e.account),r=e.folder||"INBOX",s=await w(n,o,r,["\\Seen"],"add");return{action:"mark_read",account:n.email,folder:r,...s}}async function ge(t,e){let o=e.uids||(e.uid?[e.uid]:[]);if(o.length===0)throw new a("uid or uids required","VALIDATION_ERROR","Provide at least one email UID");let n=g(t,e.account),r=e.folder||"INBOX",s=await w(n,o,r,["\\Seen"],"remove");return{action:"mark_unread",account:n.email,folder:r,...s}}async function he(t,e){let o=e.uids||(e.uid?[e.uid]:[]);if(o.length===0)throw new a("uid or uids required","VALIDATION_ERROR","Provide at least one email UID");let n=g(t,e.account),r=e.folder||"INBOX",s=await w(n,o,r,["\\Flagged"],"add");return{action:"flag",account:n.email,folder:r,...s}}async function ye(t,e){let o=e.uids||(e.uid?[e.uid]:[]);if(o.length===0)throw new a("uid or uids required","VALIDATION_ERROR","Provide at least one email UID");let n=g(t,e.account),r=e.folder||"INBOX",s=await w(n,o,r,["\\Flagged"],"remove");return{action:"unflag",account:n.email,folder:r,...s}}async function we(t,e){let o=e.uids||(e.uid?[e.uid]:[]);if(o.length===0)throw new a("uid or uids required","VALIDATION_ERROR","Provide at least one email UID");if(!e.destination)throw new a("destination is required for move action","VALIDATION_ERROR","Provide the target folder name. Use folders tool to list available folders.");let n=g(t,e.account),r=e.folder||"INBOX",s=await _(n,o,r,e.destination);return{action:"move",account:n.email,from_folder:r,to_folder:e.destination,...s}}async function Ae(t,e){let o=e.uids||(e.uid?[e.uid]:[]);if(o.length===0)throw new a("uid or uids required","VALIDATION_ERROR","Provide at least one email UID");let n=g(t,e.account),r=e.folder||"INBOX",s="[Gmail]/All Mail";(n.imap.host.includes("office365")||n.imap.host.includes("outlook")||n.imap.host.includes("yahoo"))&&(s="Archive");try{let l=(await b(n)).find(m=>m.path.toLowerCase().includes("archive")||m.path.toLowerCase().includes("all mail")||m.flags.some(f=>f.toLowerCase().includes("archive")||f.toLowerCase().includes("all")));l&&(s=l.path)}catch{}let i=await _(n,o,r,s);return{action:"archive",account:n.email,from_folder:r,archive_folder:s,...i}}async function be(t,e){let o=e.uids||(e.uid?[e.uid]:[]);if(o.length===0)throw new a("uid or uids required","VALIDATION_ERROR","Provide at least one email UID");let n=g(t,e.account),r=e.folder||"INBOX",s=await M(n,o,r);return{action:"trash",account:n.email,folder:r,...s}}import{createTransport as Ee}from"nodemailer";function R(t){return Ee({host:t.smtp.host,port:t.smtp.port,secure:t.smtp.secure,auth:{user:t.email,pass:t.password}})}function O(t){return t.split(`
8
+ `).map(e=>e.startsWith("# ")?`<h1>${e.substring(2)}</h1>`:e.startsWith("## ")?`<h2>${e.substring(3)}</h2>`:e.startsWith("### ")?`<h3>${e.substring(4)}</h3>`:e.startsWith("- ")?`<li>${e.substring(2)}</li>`:e.startsWith("**")&&e.endsWith("**")?`<b>${e.slice(2,-2)}</b>`:e.trim()===""?"<br>":`<p>${e}</p>`).join(`
9
+ `)}async function V(t,e){let o=R(t);try{return{success:!0,message_id:(await o.sendMail({from:t.email,to:e.to,cc:e.cc,bcc:e.bcc,subject:e.subject,text:e.body,html:O(e.body)})).messageId||""}}finally{o.close()}}async function B(t,e){if(!e.in_reply_to)throw new a("in_reply_to is required for reply","MISSING_PARAM","Use email_read to get the message_id of the email you want to reply to");let o=R(t);try{let n=e.subject.startsWith("Re:")?e.subject:`Re: ${e.subject}`;return{success:!0,message_id:(await o.sendMail({from:t.email,to:e.to,cc:e.cc,bcc:e.bcc,subject:n,text:e.body,html:O(e.body),inReplyTo:e.in_reply_to,references:e.references||e.in_reply_to})).messageId||""}}finally{o.close()}}async function G(t,e){let o=R(t);try{let n=e.subject.startsWith("Fwd:")?e.subject:`Fwd: ${e.subject}`,r=`${e.body}
10
+
11
+ ---------- Forwarded message ----------
12
+ ${e.original_body}`;return{success:!0,message_id:(await o.sendMail({from:t.email,to:e.to,cc:e.cc,bcc:e.bcc,subject:n,text:r,html:O(r)})).messageId||""}}finally{o.close()}}function S(t,e){let o=e.toLowerCase(),n=t.filter(r=>r.email.toLowerCase()===o||r.id===o||r.email.toLowerCase().includes(o));if(n.length===0)throw new a(`Account not found: ${e}`,"ACCOUNT_NOT_FOUND",`Available accounts: ${t.map(r=>r.email).join(", ")}`);if(n.length>1)throw new a("Multiple accounts matched. Specify the exact account email.","AMBIGUOUS_ACCOUNT",`Matched: ${n.map(r=>r.email).join(", ")}`);return n[0]}async function W(t,e){return p(async()=>{if(!e.account)throw new a("account is required for send operations","VALIDATION_ERROR","Provide the sender account email address");if(!e.to)throw new a("to is required","VALIDATION_ERROR","Provide recipient email address");if(!e.body)throw new a("body is required","VALIDATION_ERROR","Provide the email body text");switch(e.action){case"new":return await Ce(t,e);case"reply":return await Ie(t,e);case"forward":return await _e(t,e);default:throw new a(`Unknown action: ${e.action}`,"VALIDATION_ERROR","Supported actions: new, reply, forward")}})()}async function Ce(t,e){if(!e.subject)throw new a("subject is required for new email","VALIDATION_ERROR","Provide the email subject");let o=S(t,e.account),n=await V(o,{to:e.to,subject:e.subject,body:e.body,cc:e.cc,bcc:e.bcc});return{action:"new",from:o.email,to:e.to,subject:e.subject,...n}}async function Ie(t,e){if(!e.uid)throw new a("uid is required for reply action","VALIDATION_ERROR","Provide the UID of the email to reply to (from search/read)");let o=S(t,e.account),n=e.folder||"INBOX",r=await y(o,e.uid,n),s=await B(o,{to:e.to,subject:e.subject||r.subject,body:e.body,cc:e.cc,bcc:e.bcc,in_reply_to:r.message_id,references:r.references||r.message_id});return{action:"reply",from:o.email,to:e.to,subject:e.subject||`Re: ${r.subject}`,in_reply_to:r.message_id,...s}}async function _e(t,e){if(!e.uid)throw new a("uid is required for forward action","VALIDATION_ERROR","Provide the UID of the email to forward (from search/read)");let o=S(t,e.account),n=e.folder||"INBOX",r=await y(o,e.uid,n),s=await G(o,{to:e.to,subject:e.subject||r.subject,body:e.body,cc:e.cc,bcc:e.bcc,original_body:r.body_text});return{action:"forward",from:o.email,to:e.to,subject:e.subject||`Fwd: ${r.subject}`,...s}}var De=Oe(import.meta.url),T=Re(De),z=T.endsWith("bin")?E(T,"..","build","src","docs"):E(T,"..","docs"),N=[{uri:"email://docs/messages",name:"Messages Tool Docs",file:"messages.md"},{uri:"email://docs/folders",name:"Folders Tool Docs",file:"folders.md"},{uri:"email://docs/attachments",name:"Attachments Tool Docs",file:"attachments.md"},{uri:"email://docs/send",name:"Send Tool Docs",file:"send.md"}],J=[{name:"messages",description:"Email messages: search, read, mark_read, mark_unread, flag, unflag, move, archive, trash. Search across all accounts or filter by account. Query supports: UNREAD, FLAGGED, SINCE YYYY-MM-DD, FROM x, SUBJECT x.",annotations:{title:"Messages",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{type:"object",properties:{action:{type:"string",enum:["search","read","mark_read","mark_unread","flag","unflag","move","archive","trash"],description:"Action to perform"},account:{type:"string",description:"Account email filter (optional, defaults to all for search)"},query:{type:"string",description:"Search query: UNREAD, FLAGGED, SINCE YYYY-MM-DD, FROM email, SUBJECT text, or combined (default: UNSEEN)"},folder:{type:"string",description:"Mailbox folder (default: INBOX)"},limit:{type:"number",description:"Max results for search (default: 20)"},uid:{type:"number",description:"Email UID (for read/modify single email)"},uids:{type:"array",items:{type:"number"},description:"Multiple UIDs for batch operations"},destination:{type:"string",description:"Target folder for move action"}},required:["action"]}},{name:"folders",description:"List mailbox folders for one or all email accounts. Returns folder names, paths, and flags.",annotations:{title:"Folders",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{type:"object",properties:{action:{type:"string",enum:["list"],description:"Action to perform"},account:{type:"string",description:"Account email filter (optional, defaults to all)"}},required:["action"]}},{name:"attachments",description:"Email attachments: list, download. List shows all attachments for an email. Download returns base64-encoded content.",annotations:{title:"Attachments",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{type:"object",properties:{action:{type:"string",enum:["list","download"],description:"Action to perform"},account:{type:"string",description:"Account email (required)"},uid:{type:"number",description:"Email UID (required)"},folder:{type:"string",description:"Mailbox folder (default: INBOX)"},filename:{type:"string",description:"Attachment filename (required for download)"}},required:["action","account","uid"]}},{name:"send",description:"Send emails: new, reply, forward. Reply maintains thread headers (In-Reply-To, References). Forward includes original body.",annotations:{title:"Send",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0},inputSchema:{type:"object",properties:{action:{type:"string",enum:["new","reply","forward"],description:"Action to perform"},account:{type:"string",description:"Sender account email (required)"},to:{type:"string",description:"Recipient email address (required)"},subject:{type:"string",description:"Email subject (required for new)"},body:{type:"string",description:"Email body text (required)"},cc:{type:"string",description:"CC recipients (comma-separated)"},bcc:{type:"string",description:"BCC recipients (comma-separated)"},uid:{type:"number",description:"Original email UID (required for reply/forward)"},folder:{type:"string",description:"Folder of original email (default: INBOX)"}},required:["action","account","to","body"]}},{name:"help",description:"Get full documentation for a tool. Use when compressed descriptions are insufficient.",annotations:{title:"Help",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{type:"object",properties:{tool_name:{type:"string",enum:["messages","folders","attachments","send"],description:"Tool to get documentation for"}},required:["tool_name"]}}];function Y(t,e){t.setRequestHandler(Ne,async()=>({tools:J})),t.setRequestHandler(Te,async()=>({resources:N.map(o=>({uri:o.uri,name:o.name,mimeType:"text/markdown"}))})),t.setRequestHandler(ve,async o=>{let{uri:n}=o.params,r=N.find(i=>i.uri===n);if(!r)throw new a(`Resource not found: ${n}`,"RESOURCE_NOT_FOUND",`Available: ${N.map(i=>i.uri).join(", ")}`);let s=X(E(z,r.file),"utf-8");return{contents:[{uri:n,mimeType:"text/markdown",text:s}]}}),t.setRequestHandler(Se,async o=>{let{name:n,arguments:r}=o.params;if(!r)return{content:[{type:"text",text:"Error: No arguments provided"}],isError:!0};try{let s;switch(n){case"messages":s=await H(e,r);break;case"folders":s=await $(e,r);break;case"attachments":s=await F(e,r);break;case"send":s=await W(e,r);break;case"help":{let i=r.tool_name,c=`${i}.md`;try{let l=X(E(z,c),"utf-8");s={tool:i,documentation:l}}catch{throw new a(`Documentation not found for: ${i}`,"DOC_NOT_FOUND","Check tool_name")}break}default:throw new a(`Unknown tool: ${n}`,"UNKNOWN_TOOL",`Available tools: ${J.map(i=>i.name).join(", ")}`)}return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}catch(s){let i=s instanceof a?s:I(s);return{content:[{type:"text",text:L(i)}],isError:!0}}})}var ke=je(import.meta.url),Fe=xe(ke);function $e(){try{let t=Pe(Fe,"..","package.json");return JSON.parse(Le(t,"utf-8")).version??"0.0.0"}catch{return"0.0.0"}}async function K(){let t=v();t.length===0&&(console.error("EMAIL_CREDENTIALS environment variable is required"),console.error("Format: email1:password1,email2:password2"),console.error(""),console.error("Examples:"),console.error(" EMAIL_CREDENTIALS=user@gmail.com:abcd-efgh-ijkl-mnop"),console.error(" EMAIL_CREDENTIALS=user1@gmail.com:pass1,user2@outlook.com:pass2"),console.error(""),console.error("For Gmail: Enable 2FA, then create App Password at https://myaccount.google.com/apppasswords"),console.error("For Outlook: Enable 2FA, then create App Password in security settings"),process.exit(1)),console.error(`Loaded ${t.length} email account(s): ${t.map(n=>n.email).join(", ")}`);let e=new Me({name:"@n24q02m/better-email-mcp",version:$e()},{capabilities:{tools:{},resources:{}}});Y(e,t);let o=new Ue;return await e.connect(o),e}async function qe(){try{await K(),process.on("SIGINT",()=>{console.error(`
13
+ Shutting down Better Email MCP Server`),process.exit(0)})}catch(t){console.error("Failed to start server:",t),process.exit(1)}}qe();
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Better Email MCP Server Starter
3
+ * Simplified to use composite tools only
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=start-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start-server.d.ts","sourceRoot":"","sources":["../../scripts/start-server.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Better Email MCP Server Starter
3
+ * Simplified to use composite tools only
4
+ */
5
+ import { initServer } from '../src/init-server.js';
6
+ async function startServer() {
7
+ try {
8
+ await initServer();
9
+ // Keep process running
10
+ process.on('SIGINT', () => {
11
+ console.error('\nShutting down Better Email MCP Server');
12
+ process.exit(0);
13
+ });
14
+ }
15
+ catch (error) {
16
+ console.error('Failed to start server:', error);
17
+ process.exit(1);
18
+ }
19
+ }
20
+ startServer();
21
+ //# sourceMappingURL=start-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start-server.js","sourceRoot":"","sources":["../../scripts/start-server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAElD,KAAK,UAAU,WAAW;IACxB,IAAI,CAAC;QACH,MAAM,UAAU,EAAE,CAAA;QAElB,uBAAuB;QACvB,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;YACxB,OAAO,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAA;YACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAA;QAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC;AAED,WAAW,EAAE,CAAA"}
@@ -0,0 +1,48 @@
1
+ # Attachments Tool - Full Documentation
2
+
3
+ ## Overview
4
+ List and download email attachments.
5
+
6
+ ## Important
7
+ - **list** returns metadata only (filename, content_type, size)
8
+ - **download** returns base64-encoded content
9
+ - Large attachments may consume significant tokens - check size before downloading
10
+ - `account` and `uid` are required for all actions
11
+
12
+ ## Actions
13
+
14
+ ### list
15
+ List all attachments for an email.
16
+ ```json
17
+ {"action": "list", "account": "user@gmail.com", "uid": 12345}
18
+ ```
19
+ ```json
20
+ {"action": "list", "account": "user@gmail.com", "uid": 12345, "folder": "INBOX"}
21
+ ```
22
+
23
+ ### download
24
+ Download a specific attachment by filename. Returns base64-encoded content.
25
+ ```json
26
+ {"action": "download", "account": "user@gmail.com", "uid": 12345, "filename": "report.pdf"}
27
+ ```
28
+
29
+ ## Parameters
30
+ - `action` - Action to perform (required): list, download
31
+ - `account` - Account email (required)
32
+ - `uid` - Email UID (required)
33
+ - `folder` - Mailbox folder (default: INBOX)
34
+ - `filename` - Attachment filename (required for download, case-insensitive)
35
+
36
+ ## Response Fields
37
+
38
+ ### list response
39
+ - `attachments[].filename` - Attachment filename
40
+ - `attachments[].content_type` - MIME type (e.g. application/pdf)
41
+ - `attachments[].size` - Size in bytes
42
+ - `attachments[].content_id` - Content-ID for inline attachments
43
+
44
+ ### download response
45
+ - `filename` - Attachment filename
46
+ - `content_type` - MIME type
47
+ - `size` - Size in bytes
48
+ - `content_base64` - Base64-encoded file content
@@ -0,0 +1,45 @@
1
+ # Folders Tool - Full Documentation
2
+
3
+ ## Overview
4
+ List mailbox folders for one or all email accounts.
5
+
6
+ ## Important
7
+ - Returns folder **names, paths, flags, and children**
8
+ - Gmail uses labels (e.g. `[Gmail]/All Mail`, `[Gmail]/Trash`)
9
+ - Outlook uses standard folders (Inbox, Sent, Drafts, Archive, Junk)
10
+ - Folder paths are case-sensitive
11
+
12
+ ## Actions
13
+
14
+ ### list
15
+ List all folders for all accounts or a specific account.
16
+ ```json
17
+ {"action": "list"}
18
+ ```
19
+ ```json
20
+ {"action": "list", "account": "user@gmail.com"}
21
+ ```
22
+
23
+ ## Parameters
24
+ - `action` - Action to perform (required): list
25
+ - `account` - Account email filter (optional, defaults to all)
26
+
27
+ ## Common Folder Names
28
+
29
+ ### Gmail
30
+ - `INBOX`
31
+ - `[Gmail]/All Mail`
32
+ - `[Gmail]/Drafts`
33
+ - `[Gmail]/Important`
34
+ - `[Gmail]/Sent Mail`
35
+ - `[Gmail]/Spam`
36
+ - `[Gmail]/Starred`
37
+ - `[Gmail]/Trash`
38
+
39
+ ### Outlook
40
+ - `Inbox`
41
+ - `Sent`
42
+ - `Drafts`
43
+ - `Archive`
44
+ - `Junk`
45
+ - `Deleted`
@@ -0,0 +1,92 @@
1
+ # Messages Tool - Full Documentation
2
+
3
+ ## Overview
4
+ Email messages: search, read, mark_read, mark_unread, flag, unflag, move, archive, trash.
5
+
6
+ ## Important
7
+ - **search** defaults to all configured accounts. Filter with `account` param.
8
+ - **read** returns clean plain text body (HTML stripped for LLM token savings)
9
+ - **UIDs are per-account and per-folder** - always specify account for modify operations
10
+ - Query language supports compound filters: `UNREAD SINCE 2024-01-01`
11
+
12
+ ## Actions
13
+
14
+ ### search
15
+ Search emails across all or filtered accounts.
16
+ ```json
17
+ {"action": "search", "query": "UNREAD", "folder": "INBOX", "limit": 20}
18
+ ```
19
+ ```json
20
+ {"action": "search", "query": "UNREAD SINCE 2024-06-01", "account": "user@gmail.com"}
21
+ ```
22
+ ```json
23
+ {"action": "search", "query": "FROM boss@company.com", "limit": 5}
24
+ ```
25
+
26
+ Query shortcuts:
27
+ - `UNREAD` / `UNSEEN` - unread emails
28
+ - `READ` / `SEEN` - read emails
29
+ - `FLAGGED` / `STARRED` - flagged emails
30
+ - `ALL` / `*` - all emails
31
+ - `SINCE YYYY-MM-DD` - emails after date
32
+ - `FROM email` - emails from sender
33
+ - `SUBJECT text` - emails matching subject
34
+ - `UNREAD SINCE YYYY-MM-DD` - compound filter
35
+ - `UNREAD FROM email` - compound filter
36
+ - Any other text is treated as subject search
37
+
38
+ ### read
39
+ Read a single email by UID. Returns full body as clean text.
40
+ ```json
41
+ {"action": "read", "account": "user@gmail.com", "uid": 12345, "folder": "INBOX"}
42
+ ```
43
+
44
+ ### mark_read
45
+ ```json
46
+ {"action": "mark_read", "account": "user@gmail.com", "uids": [123, 456, 789]}
47
+ ```
48
+
49
+ ### mark_unread
50
+ ```json
51
+ {"action": "mark_unread", "account": "user@gmail.com", "uid": 123}
52
+ ```
53
+
54
+ ### flag
55
+ Star/flag emails.
56
+ ```json
57
+ {"action": "flag", "account": "user@gmail.com", "uids": [123, 456]}
58
+ ```
59
+
60
+ ### unflag
61
+ Remove star/flag from emails.
62
+ ```json
63
+ {"action": "unflag", "account": "user@gmail.com", "uid": 123}
64
+ ```
65
+
66
+ ### move
67
+ Move emails to another folder.
68
+ ```json
69
+ {"action": "move", "account": "user@gmail.com", "uids": [123], "destination": "[Gmail]/Important"}
70
+ ```
71
+
72
+ ### archive
73
+ Archive emails (auto-detects archive folder per provider).
74
+ ```json
75
+ {"action": "archive", "account": "user@gmail.com", "uids": [123, 456]}
76
+ ```
77
+
78
+ ### trash
79
+ Delete emails (moves to trash).
80
+ ```json
81
+ {"action": "trash", "account": "user@gmail.com", "uid": 123}
82
+ ```
83
+
84
+ ## Parameters
85
+ - `action` - Action to perform (required)
86
+ - `account` - Account email filter (optional for search, required for modify)
87
+ - `query` - Search query string (default: UNSEEN)
88
+ - `folder` - Mailbox folder (default: INBOX)
89
+ - `limit` - Max search results (default: 20)
90
+ - `uid` - Single email UID
91
+ - `uids` - Multiple email UIDs for batch operations
92
+ - `destination` - Target folder for move action
@@ -0,0 +1,52 @@
1
+ # Send Tool - Full Documentation
2
+
3
+ ## Overview
4
+ Send new emails, reply to threads, and forward emails via SMTP.
5
+
6
+ ## Important
7
+ - **reply** automatically sets In-Reply-To and References headers for threading
8
+ - **forward** includes original email body with separator
9
+ - `account` specifies which configured email to send from
10
+ - HTML is auto-generated from plain text body (basic markdown support)
11
+
12
+ ## Actions
13
+
14
+ ### new
15
+ Send a new email.
16
+ ```json
17
+ {"action": "new", "account": "user@gmail.com", "to": "recipient@example.com", "subject": "Hello", "body": "Hi there!"}
18
+ ```
19
+ ```json
20
+ {"action": "new", "account": "user@gmail.com", "to": "a@example.com", "subject": "Update", "body": "See details.", "cc": "b@example.com", "bcc": "c@example.com"}
21
+ ```
22
+
23
+ ### reply
24
+ Reply to an email. Reads original email to set threading headers.
25
+ ```json
26
+ {"action": "reply", "account": "user@gmail.com", "to": "sender@example.com", "body": "Thanks!", "uid": 12345}
27
+ ```
28
+ ```json
29
+ {"action": "reply", "account": "user@gmail.com", "to": "sender@example.com", "subject": "Re: Custom subject", "body": "Got it.", "uid": 12345, "folder": "INBOX"}
30
+ ```
31
+
32
+ ### forward
33
+ Forward an email. Original body is appended with separator.
34
+ ```json
35
+ {"action": "forward", "account": "user@gmail.com", "to": "colleague@example.com", "body": "FYI, see below.", "uid": 12345}
36
+ ```
37
+
38
+ ## Parameters
39
+ - `action` - Action to perform (required): new, reply, forward
40
+ - `account` - Sender account email (required)
41
+ - `to` - Recipient email address (required)
42
+ - `subject` - Email subject (required for new, optional for reply/forward)
43
+ - `body` - Email body text (required)
44
+ - `cc` - CC recipients (comma-separated, optional)
45
+ - `bcc` - BCC recipients (comma-separated, optional)
46
+ - `uid` - Original email UID (required for reply/forward)
47
+ - `folder` - Folder of original email (default: INBOX, for reply/forward)
48
+
49
+ ## Notes
50
+ - Reply subject auto-prepends "Re:" if not already present
51
+ - Forward subject auto-prepends "Fwd:" if not already present
52
+ - Body supports basic markdown: `# heading`, `## heading`, `- list item`, `**bold**`
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Better Email MCP Server
3
+ * Using composite tools for human-friendly AI agent interactions
4
+ */
5
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
6
+ export declare function initServer(): Promise<Server<{
7
+ method: string;
8
+ params?: {
9
+ [x: string]: unknown;
10
+ _meta?: {
11
+ [x: string]: unknown;
12
+ progressToken?: string | number | undefined;
13
+ "io.modelcontextprotocol/related-task"?: {
14
+ taskId: string;
15
+ } | undefined;
16
+ } | undefined;
17
+ } | undefined;
18
+ }, {
19
+ method: string;
20
+ params?: {
21
+ [x: string]: unknown;
22
+ _meta?: {
23
+ [x: string]: unknown;
24
+ progressToken?: string | number | undefined;
25
+ "io.modelcontextprotocol/related-task"?: {
26
+ taskId: string;
27
+ } | undefined;
28
+ } | undefined;
29
+ } | undefined;
30
+ }, {
31
+ [x: string]: unknown;
32
+ _meta?: {
33
+ [x: string]: unknown;
34
+ progressToken?: string | number | undefined;
35
+ "io.modelcontextprotocol/related-task"?: {
36
+ taskId: string;
37
+ } | undefined;
38
+ } | undefined;
39
+ }>>;
40
+ //# sourceMappingURL=init-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-server.d.ts","sourceRoot":"","sources":["../../src/init-server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AAkBlE,wBAAsB,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAwC/B"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Better Email MCP Server
3
+ * Using composite tools for human-friendly AI agent interactions
4
+ */
5
+ import { readFileSync } from 'node:fs';
6
+ import { dirname, join } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
9
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
10
+ import { loadConfig } from './tools/helpers/config.js';
11
+ import { registerTools } from './tools/registry.js';
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = dirname(__filename);
14
+ function getVersion() {
15
+ try {
16
+ const pkgPath = join(__dirname, '..', 'package.json');
17
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
18
+ return pkg.version ?? '0.0.0';
19
+ }
20
+ catch {
21
+ return '0.0.0';
22
+ }
23
+ }
24
+ export async function initServer() {
25
+ // Load email accounts from environment
26
+ const accounts = loadConfig();
27
+ if (accounts.length === 0) {
28
+ console.error('EMAIL_CREDENTIALS environment variable is required');
29
+ console.error('Format: email1:password1,email2:password2');
30
+ console.error('');
31
+ console.error('Examples:');
32
+ console.error(' EMAIL_CREDENTIALS=user@gmail.com:abcd-efgh-ijkl-mnop');
33
+ console.error(' EMAIL_CREDENTIALS=user1@gmail.com:pass1,user2@outlook.com:pass2');
34
+ console.error('');
35
+ console.error('For Gmail: Enable 2FA, then create App Password at https://myaccount.google.com/apppasswords');
36
+ console.error('For Outlook: Enable 2FA, then create App Password in security settings');
37
+ process.exit(1);
38
+ }
39
+ console.error(`Loaded ${accounts.length} email account(s): ${accounts.map((a) => a.email).join(', ')}`);
40
+ // Create MCP server
41
+ const server = new Server({
42
+ name: '@n24q02m/better-email-mcp',
43
+ version: getVersion()
44
+ }, {
45
+ capabilities: {
46
+ tools: {},
47
+ resources: {}
48
+ }
49
+ });
50
+ // Register composite tools
51
+ registerTools(server, accounts);
52
+ // Connect stdio transport
53
+ const transport = new StdioServerTransport();
54
+ await server.connect(transport);
55
+ return server;
56
+ }
57
+ //# sourceMappingURL=init-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-server.js","sourceRoot":"","sources":["../../src/init-server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAEnD,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACjD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;AAErC,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,CAAA;QACrD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;QACtD,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAA;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAA;IAChB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,uCAAuC;IACvC,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAA;IAE7B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAA;QACnE,OAAO,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAA;QAC1D,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QACjB,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;QAC1B,OAAO,CAAC,KAAK,CAAC,wDAAwD,CAAC,CAAA;QACvE,OAAO,CAAC,KAAK,CAAC,mEAAmE,CAAC,CAAA;QAClF,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QACjB,OAAO,CAAC,KAAK,CAAC,8FAA8F,CAAC,CAAA;QAC7G,OAAO,CAAC,KAAK,CAAC,wEAAwE,CAAC,CAAA;QACvF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,UAAU,QAAQ,CAAC,MAAM,sBAAsB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAEvG,oBAAoB;IACpB,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB;QACE,IAAI,EAAE,2BAA2B;QACjC,OAAO,EAAE,UAAU,EAAE;KACtB,EACD;QACE,YAAY,EAAE;YACZ,KAAK,EAAE,EAAE;YACT,SAAS,EAAE,EAAE;SACd;KACF,CACF,CAAA;IAED,2BAA2B;IAC3B,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IAE/B,0BAA0B;IAC1B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAA;IAC5C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IAC/B,OAAO,MAAM,CAAA;AACf,CAAC"}