@honest-magic/mail-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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 honest-magic
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,178 @@
1
+ # mail-mcp
2
+
3
+ MCP server for IMAP/SMTP email access — works with Claude and other MCP clients.
4
+
5
+ ## Requirements
6
+
7
+ - Node.js >=18
8
+ - macOS (credentials stored in Keychain)
9
+
10
+ ## Installation
11
+
12
+ ### Run without installing (recommended)
13
+
14
+ ```bash
15
+ npx @honest-magic/mail-mcp
16
+ ```
17
+
18
+ ### Global install
19
+
20
+ ```bash
21
+ npm install -g @honest-magic/mail-mcp
22
+ mail-mcp
23
+ ```
24
+
25
+ ## Configuration
26
+
27
+ ### 1. Add an account (interactive)
28
+
29
+ ```bash
30
+ npx @honest-magic/mail-mcp accounts add
31
+ ```
32
+
33
+ This prompts for IMAP/SMTP settings, stores the account in `~/.config/mail-mcp/accounts.json`, and saves the password in macOS Keychain.
34
+
35
+ ### Manage accounts
36
+
37
+ ```bash
38
+ mail-mcp accounts list # show configured accounts
39
+ mail-mcp accounts remove ID # remove an account and its keychain entry
40
+ ```
41
+
42
+ ### Manual setup
43
+
44
+ Alternatively, create `~/.config/mail-mcp/accounts.json` by hand:
45
+
46
+ ```json
47
+ [
48
+ {
49
+ "id": "work",
50
+ "name": "Work Email",
51
+ "host": "imap.example.com",
52
+ "port": 993,
53
+ "smtpHost": "smtp.example.com",
54
+ "smtpPort": 587,
55
+ "user": "you@example.com",
56
+ "authType": "login",
57
+ "useTLS": true
58
+ }
59
+ ]
60
+ ```
61
+
62
+ Then store the password in macOS Keychain:
63
+
64
+ ```bash
65
+ security add-generic-password \
66
+ -s ch.honest-magic.config.mail-server \
67
+ -a <account-id> \
68
+ -w <password-or-app-password>
69
+ ```
70
+
71
+ ### Account fields
72
+
73
+ | Field | Type | Required | Description |
74
+ |-------|------|----------|-------------|
75
+ | `id` | string | yes | Unique identifier used by MCP tools |
76
+ | `name` | string | yes | Human-readable label |
77
+ | `host` | string | yes | IMAP hostname |
78
+ | `port` | number | yes | IMAP port (993 for TLS, 143 for STARTTLS) |
79
+ | `smtpHost` | string | no | SMTP hostname (omit for read-only use) |
80
+ | `smtpPort` | number | no | SMTP port (587 for STARTTLS, 465 for TLS) |
81
+ | `user` | string | yes | Login username / email address |
82
+ | `authType` | string | yes | `login` or `oauth2` |
83
+ | `useTLS` | boolean | yes | `true` for implicit TLS on IMAP; `false` for STARTTLS |
84
+
85
+ **OAuth2** — after starting the server, call the `register_oauth2_account` MCP tool:
86
+
87
+ ```json
88
+ {
89
+ "tool": "register_oauth2_account",
90
+ "arguments": {
91
+ "accountId": "work",
92
+ "clientId": "<oauth2-client-id>",
93
+ "clientSecret": "<oauth2-client-secret>",
94
+ "refreshToken": "<oauth2-refresh-token>",
95
+ "tokenEndpoint": "https://oauth2.googleapis.com/token"
96
+ }
97
+ }
98
+ ```
99
+
100
+ The credentials are stored in Keychain under the same service name. Token refresh is handled automatically.
101
+
102
+ ### 3. Add to your MCP client
103
+
104
+ **Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
105
+
106
+ ```json
107
+ {
108
+ "mcpServers": {
109
+ "mail": {
110
+ "command": "npx",
111
+ "args": ["-y", "@honest-magic/mail-mcp"]
112
+ }
113
+ }
114
+ }
115
+ ```
116
+
117
+ **Generic MCP client**:
118
+
119
+ ```json
120
+ {
121
+ "mcpServers": {
122
+ "mail": {
123
+ "command": "mail-mcp"
124
+ }
125
+ }
126
+ }
127
+ ```
128
+
129
+ Use `"command": "mail-mcp"` if installed globally, or `"command": "npx", "args": ["-y", "@honest-magic/mail-mcp"]` otherwise.
130
+
131
+ ## Available Tools
132
+
133
+ | Tool | Description |
134
+ |------|-------------|
135
+ | `list_accounts` | List all configured email accounts |
136
+ | `list_emails` | List recent emails from a folder with metadata and snippets |
137
+ | `search_emails` | Search emails by sender, subject, date range, or keywords |
138
+ | `read_email` | Fetch the full content of an email as Markdown |
139
+ | `send_email` | Send a new email via SMTP |
140
+ | `create_draft` | Save a draft to the Drafts folder without sending |
141
+ | `list_folders` | List all available folders and labels in a mailbox |
142
+ | `move_email` | Move a message to another folder (Archive, Trash, Spam, etc.) |
143
+ | `modify_labels` | Add or remove IMAP flags / provider labels on a message |
144
+ | `get_thread` | Fetch all messages in a conversation thread |
145
+ | `get_attachment` | Download attachment content via MCP Resource URI |
146
+ | `extract_attachment_text` | Extract plain text from PDF and document attachments |
147
+ | `register_oauth2_account` | Store OAuth2 tokens in Keychain for an account |
148
+ | `batch_operations` | Apply move, delete, or label actions to multiple emails at once |
149
+
150
+ ## Read-Only Mode
151
+
152
+ Start the server with `--read-only` to disable all write operations:
153
+
154
+ ```bash
155
+ npx @honest-magic/mail-mcp --read-only
156
+ ```
157
+
158
+ In read-only mode:
159
+ - Write tools (`send_email`, `create_draft`, `move_email`, `modify_labels`, `batch_operations`, `register_oauth2_account`) are removed from the tool list entirely and return a descriptive error if called directly.
160
+ - SMTP authentication is skipped — only IMAP connects.
161
+ - The active mode is advertised to the MCP client at handshake via `InitializeResult.instructions`.
162
+
163
+ Claude Desktop read-only config:
164
+
165
+ ```json
166
+ {
167
+ "mcpServers": {
168
+ "mail-readonly": {
169
+ "command": "npx",
170
+ "args": ["-y", "@honest-magic/mail-mcp", "--read-only"]
171
+ }
172
+ }
173
+ }
174
+ ```
175
+
176
+ ## License
177
+
178
+ MIT
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Handle `mail-mcp accounts <subcommand>` CLI commands.
3
+ *
4
+ * Returns true if a CLI subcommand was handled (caller should process.exit),
5
+ * false if not a CLI command (caller should start the MCP server).
6
+ */
7
+ export declare function handleAccountsCommand(args: string[]): Promise<boolean>;
@@ -0,0 +1,171 @@
1
+ import { createInterface } from 'node:readline/promises';
2
+ import { getAccounts, saveAccounts, ACCOUNTS_PATH } from '../config.js';
3
+ import { saveCredentials, removeCredentials } from '../security/keychain.js';
4
+ /**
5
+ * Handle `mail-mcp accounts <subcommand>` CLI commands.
6
+ *
7
+ * Returns true if a CLI subcommand was handled (caller should process.exit),
8
+ * false if not a CLI command (caller should start the MCP server).
9
+ */
10
+ export async function handleAccountsCommand(args) {
11
+ if (args[0] !== 'accounts') {
12
+ return false;
13
+ }
14
+ const subcommand = args[1];
15
+ switch (subcommand) {
16
+ case 'list':
17
+ await listAccounts();
18
+ return true;
19
+ case 'remove':
20
+ await removeAccount(args[2]);
21
+ return true;
22
+ case 'add':
23
+ await addAccount();
24
+ return true;
25
+ default:
26
+ console.log('Usage: mail-mcp accounts <add|list|remove>');
27
+ process.exit(1);
28
+ }
29
+ }
30
+ async function listAccounts() {
31
+ const accounts = getAccounts();
32
+ if (accounts.length === 0) {
33
+ console.log('No accounts configured.');
34
+ console.log(`Config file: ${ACCOUNTS_PATH}`);
35
+ return;
36
+ }
37
+ const colWidths = {
38
+ id: Math.max(2, ...accounts.map((a) => a.id.length)),
39
+ name: Math.max(4, ...accounts.map((a) => a.name.length)),
40
+ host: Math.max(4, ...accounts.map((a) => a.host.length)),
41
+ user: Math.max(4, ...accounts.map((a) => a.user.length)),
42
+ };
43
+ const pad = (s, n) => s.padEnd(n);
44
+ const header = `${pad('ID', colWidths.id)} ${pad('Name', colWidths.name)} ${pad('Host', colWidths.host)} ${pad('User', colWidths.user)}`;
45
+ const divider = `${'-'.repeat(colWidths.id)} ${'-'.repeat(colWidths.name)} ${'-'.repeat(colWidths.host)} ${'-'.repeat(colWidths.user)}`;
46
+ console.log(header);
47
+ console.log(divider);
48
+ for (const account of accounts) {
49
+ console.log(`${pad(account.id, colWidths.id)} ${pad(account.name, colWidths.name)} ${pad(account.host, colWidths.host)} ${pad(account.user, colWidths.user)}`);
50
+ }
51
+ }
52
+ async function removeAccount(id) {
53
+ if (!id) {
54
+ console.error('Usage: mail-mcp accounts remove <id>');
55
+ process.exit(1);
56
+ }
57
+ const accounts = getAccounts();
58
+ const index = accounts.findIndex((a) => a.id === id);
59
+ if (index === -1) {
60
+ console.error(`Account '${id}' not found.`);
61
+ process.exit(1);
62
+ }
63
+ accounts.splice(index, 1);
64
+ saveAccounts(accounts);
65
+ try {
66
+ await removeCredentials(id);
67
+ }
68
+ catch {
69
+ console.error(`Warning: could not remove keychain entry for '${id}' (may not exist).`);
70
+ }
71
+ console.log(`Account '${id}' removed.`);
72
+ }
73
+ async function addAccount() {
74
+ const rl = createInterface({
75
+ input: process.stdin,
76
+ output: process.stdout,
77
+ });
78
+ try {
79
+ const existingAccounts = getAccounts();
80
+ const existingIds = new Set(existingAccounts.map((a) => a.id));
81
+ // id
82
+ let id = '';
83
+ while (!id) {
84
+ const raw = await rl.question('Account ID (required, unique): ');
85
+ const trimmed = raw.trim();
86
+ if (!trimmed) {
87
+ console.log(' ID is required.');
88
+ continue;
89
+ }
90
+ if (existingIds.has(trimmed)) {
91
+ console.log(` ID '${trimmed}' already exists. Choose a different ID.`);
92
+ continue;
93
+ }
94
+ id = trimmed;
95
+ }
96
+ // name
97
+ const nameRaw = await rl.question(`Name [${id}]: `);
98
+ const name = nameRaw.trim() || id;
99
+ // host (IMAP)
100
+ let host = '';
101
+ while (!host) {
102
+ const raw = await rl.question('IMAP host (e.g. imap.gmail.com): ');
103
+ const trimmed = raw.trim();
104
+ if (!trimmed) {
105
+ console.log(' Host is required.');
106
+ continue;
107
+ }
108
+ host = trimmed;
109
+ }
110
+ // port
111
+ const portRaw = await rl.question('IMAP port [993]: ');
112
+ const port = parseInt(portRaw.trim(), 10) || 993;
113
+ // user
114
+ let user = '';
115
+ while (!user) {
116
+ const raw = await rl.question('Email address (user): ');
117
+ const trimmed = raw.trim();
118
+ if (!trimmed) {
119
+ console.log(' Email address is required.');
120
+ continue;
121
+ }
122
+ user = trimmed;
123
+ }
124
+ // authType
125
+ const authTypeRaw = await rl.question('Auth type (login/oauth2) [login]: ');
126
+ const authType = authTypeRaw.trim() === 'oauth2' ? 'oauth2' : 'login';
127
+ // useTLS
128
+ const tlsRaw = await rl.question('Use TLS? (y/n) [y]: ');
129
+ const useTLS = tlsRaw.trim().toLowerCase() !== 'n';
130
+ // smtpHost
131
+ const defaultSmtpHost = host.includes('imap') ? host.replace('imap', 'smtp') : '';
132
+ const smtpHostRaw = await rl.question(`SMTP host [${defaultSmtpHost || 'press enter to skip'}]: `);
133
+ const smtpHost = smtpHostRaw.trim() || defaultSmtpHost || undefined;
134
+ // smtpPort
135
+ let smtpPort;
136
+ if (smtpHost) {
137
+ const smtpPortRaw = await rl.question('SMTP port [587]: ');
138
+ smtpPort = parseInt(smtpPortRaw.trim(), 10) || 587;
139
+ }
140
+ // password (only for login auth)
141
+ let password;
142
+ if (authType === 'login') {
143
+ const passwordRaw = await rl.question('Password (will be stored in macOS Keychain, NOT in config file): ');
144
+ password = passwordRaw || undefined;
145
+ }
146
+ const account = {
147
+ id,
148
+ name,
149
+ host,
150
+ port,
151
+ user,
152
+ authType,
153
+ useTLS,
154
+ ...(smtpHost !== undefined ? { smtpHost } : {}),
155
+ ...(smtpPort !== undefined ? { smtpPort } : {}),
156
+ };
157
+ existingAccounts.push(account);
158
+ saveAccounts(existingAccounts);
159
+ if (authType === 'login' && password) {
160
+ await saveCredentials(id, password);
161
+ console.log(`Account '${id}' added. Password stored in macOS Keychain.`);
162
+ }
163
+ else {
164
+ console.log(`Account '${id}' added.`);
165
+ }
166
+ }
167
+ finally {
168
+ rl.close();
169
+ }
170
+ }
171
+ //# sourceMappingURL=accounts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/cli/accounts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { EmailAccount } from './types/index.js';
2
+ export declare const ACCOUNTS_PATH: string;
3
+ export declare const config: {
4
+ serviceName: string;
5
+ logLevel: string;
6
+ };
7
+ /**
8
+ * Writes account definitions to ~/.config/mail-mcp/accounts.json.
9
+ * Creates the directory if it does not exist.
10
+ */
11
+ export declare function saveAccounts(accounts: EmailAccount[]): void;
12
+ /**
13
+ * Reads account definitions from ~/.config/mail-mcp/accounts.json.
14
+ * Returns an empty array if the file does not exist or cannot be parsed.
15
+ */
16
+ export declare function getAccounts(): EmailAccount[];
package/dist/config.js ADDED
@@ -0,0 +1,48 @@
1
+ import { z } from 'zod';
2
+ import * as fs from 'node:fs';
3
+ import * as path from 'node:path';
4
+ import * as os from 'node:os';
5
+ export const ACCOUNTS_PATH = path.join(os.homedir(), '.config', 'mail-mcp', 'accounts.json');
6
+ const configSchema = z.object({
7
+ serviceName: z.string().default('ch.honest-magic.config.mail-server'),
8
+ logLevel: z.string().default('info'),
9
+ });
10
+ export const config = configSchema.parse({
11
+ serviceName: process.env.SERVICE_NAME,
12
+ logLevel: process.env.LOG_LEVEL,
13
+ });
14
+ /**
15
+ * Writes account definitions to ~/.config/mail-mcp/accounts.json.
16
+ * Creates the directory if it does not exist.
17
+ */
18
+ export function saveAccounts(accounts) {
19
+ const configPath = ACCOUNTS_PATH;
20
+ const dir = path.dirname(configPath);
21
+ fs.mkdirSync(dir, { recursive: true });
22
+ fs.writeFileSync(configPath, JSON.stringify(accounts, null, 2), 'utf-8');
23
+ }
24
+ /**
25
+ * Reads account definitions from ~/.config/mail-mcp/accounts.json.
26
+ * Returns an empty array if the file does not exist or cannot be parsed.
27
+ */
28
+ export function getAccounts() {
29
+ const configPath = ACCOUNTS_PATH;
30
+ if (!fs.existsSync(configPath)) {
31
+ console.error('No accounts config found at ~/.config/mail-mcp/accounts.json');
32
+ return [];
33
+ }
34
+ try {
35
+ const raw = fs.readFileSync(configPath, 'utf-8');
36
+ const parsed = JSON.parse(raw);
37
+ if (!Array.isArray(parsed)) {
38
+ console.error(`Failed to parse accounts config at ${configPath}: expected an array`);
39
+ return [];
40
+ }
41
+ return parsed;
42
+ }
43
+ catch (error) {
44
+ console.error(`Failed to parse accounts config at ${configPath}:`, error);
45
+ return [];
46
+ }
47
+ }
48
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAG9B,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC;AAE7F,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,oCAAoC,CAAC;IACrE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;CACrC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC;IACvC,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY;IACrC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS;CAChC,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,QAAwB;IACnD,MAAM,UAAU,GAAG,aAAa,CAAC;IACjC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACrC,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AAC3E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,UAAU,GAAG,aAAa,CAAC;IAEjC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,KAAK,CAAC,8DAA8D,CAAC,CAAC;QAC9E,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,KAAK,CAAC,sCAAsC,UAAU,qBAAqB,CAAC,CAAC;YACrF,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,OAAO,MAAwB,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,sCAAsC,UAAU,GAAG,EAAE,KAAK,CAAC,CAAC;QAC1E,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}