@sempervirens-labs/apple-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/.claude/settings.local.json +13 -0
- package/LICENSE +21 -0
- package/README.md +261 -0
- package/bun.lock +203 -0
- package/package.json +43 -0
- package/src/applescript/mail.ts +356 -0
- package/src/index.ts +176 -0
- package/src/tools.ts +156 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"WebFetch(domain:github.com)",
|
|
5
|
+
"mcp__trigger__search_docs",
|
|
6
|
+
"WebFetch(domain:raw.githubusercontent.com)",
|
|
7
|
+
"WebFetch(domain:www.npmjs.com)",
|
|
8
|
+
"Bash(bun install)",
|
|
9
|
+
"Bash(osascript:*)",
|
|
10
|
+
"Bash(npm publish:*)"
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
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,261 @@
|
|
|
1
|
+
# Apple Mail MCP
|
|
2
|
+
|
|
3
|
+
An MCP (Model Context Protocol) server for Apple Mail on macOS. Allows AI assistants like Claude to read, search, and send emails through Apple Mail.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **List Accounts** - Get all configured email accounts
|
|
8
|
+
- **List Mailboxes** - Get all mailboxes/folders for any account
|
|
9
|
+
- **Get Emails** - Retrieve recent emails from any mailbox
|
|
10
|
+
- **Search Emails** - Search by subject, sender, or content
|
|
11
|
+
- **Unread Count** - Get unread email counts
|
|
12
|
+
- **Send Email** - Compose and send emails with CC/BCC support
|
|
13
|
+
|
|
14
|
+
## Requirements
|
|
15
|
+
|
|
16
|
+
- macOS (uses AppleScript to communicate with Apple Mail)
|
|
17
|
+
- [Bun](https://bun.sh/) runtime
|
|
18
|
+
- Apple Mail app configured with at least one email account
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
### From Source
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
git clone https://github.com/yourusername/apple-mail-mcp.git
|
|
26
|
+
cd apple-mail-mcp
|
|
27
|
+
bun install
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Using bunx (no installation required)
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bunx @sempervirens-labs/apple-mail-mcp
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Configuration
|
|
37
|
+
|
|
38
|
+
### Claude Code
|
|
39
|
+
|
|
40
|
+
Add to your `~/.claude/settings.json`:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"apple-mail": {
|
|
46
|
+
"command": "bun",
|
|
47
|
+
"args": ["run", "/path/to/apple-mail-mcp/src/index.ts"]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or if published to npm:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"mcpServers": {
|
|
58
|
+
"apple-mail": {
|
|
59
|
+
"command": "bunx",
|
|
60
|
+
"args": ["apple-mail-mcp"]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
After adding the configuration, restart Claude Code for the changes to take effect.
|
|
67
|
+
|
|
68
|
+
### Cursor
|
|
69
|
+
|
|
70
|
+
Add to your Cursor MCP settings (Settings > MCP Servers):
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"mcpServers": {
|
|
75
|
+
"apple-mail": {
|
|
76
|
+
"command": "bun",
|
|
77
|
+
"args": ["run", "/path/to/apple-mail-mcp/src/index.ts"]
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Or via Cursor's settings UI:
|
|
84
|
+
1. Open Cursor Settings (`Cmd + ,`)
|
|
85
|
+
2. Search for "MCP" or navigate to Extensions > MCP Servers
|
|
86
|
+
3. Click "Add Server"
|
|
87
|
+
4. Enter:
|
|
88
|
+
- **Name**: `apple-mail`
|
|
89
|
+
- **Command**: `bun`
|
|
90
|
+
- **Arguments**: `run`, `/path/to/apple-mail-mcp/src/index.ts`
|
|
91
|
+
|
|
92
|
+
### Claude Desktop
|
|
93
|
+
|
|
94
|
+
Add to your `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"mcpServers": {
|
|
99
|
+
"apple-mail": {
|
|
100
|
+
"command": "bun",
|
|
101
|
+
"args": ["run", "/path/to/apple-mail-mcp/src/index.ts"]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Available Tools
|
|
108
|
+
|
|
109
|
+
### `mail_list_accounts`
|
|
110
|
+
|
|
111
|
+
List all email accounts configured in Apple Mail.
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
No parameters required
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Example response:**
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"accounts": ["personal@gmail.com", "iCloud", "work@company.com"]
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### `mail_list_mailboxes`
|
|
125
|
+
|
|
126
|
+
List all mailboxes for a specific account or all accounts.
|
|
127
|
+
|
|
128
|
+
| Parameter | Type | Required | Description |
|
|
129
|
+
|-----------|------|----------|-------------|
|
|
130
|
+
| `account` | string | No | Account name to list mailboxes for |
|
|
131
|
+
|
|
132
|
+
**Example response:**
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"mailboxes": [
|
|
136
|
+
{
|
|
137
|
+
"account": "iCloud",
|
|
138
|
+
"mailboxes": ["INBOX", "Drafts", "Sent Messages", "Archive", "Junk"]
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### `mail_get_emails`
|
|
145
|
+
|
|
146
|
+
Get recent emails from a mailbox.
|
|
147
|
+
|
|
148
|
+
| Parameter | Type | Required | Default | Description |
|
|
149
|
+
|-----------|------|----------|---------|-------------|
|
|
150
|
+
| `account` | string | No | - | Account name |
|
|
151
|
+
| `mailbox` | string | No | "INBOX" | Mailbox name |
|
|
152
|
+
| `limit` | number | No | 10 | Max emails to retrieve |
|
|
153
|
+
| `includeContent` | boolean | No | false | Include email body |
|
|
154
|
+
|
|
155
|
+
**Example response:**
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"emails": [
|
|
159
|
+
{
|
|
160
|
+
"id": 12345,
|
|
161
|
+
"subject": "Meeting tomorrow",
|
|
162
|
+
"sender": "John Doe <john@example.com>",
|
|
163
|
+
"dateSent": "Monday, 10. January 2025 at 09:30:00",
|
|
164
|
+
"isRead": false
|
|
165
|
+
}
|
|
166
|
+
],
|
|
167
|
+
"count": 1
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### `mail_search`
|
|
172
|
+
|
|
173
|
+
Search emails by subject, sender, or content.
|
|
174
|
+
|
|
175
|
+
| Parameter | Type | Required | Default | Description |
|
|
176
|
+
|-----------|------|----------|---------|-------------|
|
|
177
|
+
| `query` | string | **Yes** | - | Search query |
|
|
178
|
+
| `account` | string | No | - | Limit search to account |
|
|
179
|
+
| `mailbox` | string | No | - | Limit search to mailbox |
|
|
180
|
+
| `limit` | number | No | 10 | Max results |
|
|
181
|
+
|
|
182
|
+
### `mail_get_unread_count`
|
|
183
|
+
|
|
184
|
+
Get the count of unread emails.
|
|
185
|
+
|
|
186
|
+
| Parameter | Type | Required | Description |
|
|
187
|
+
|-----------|------|----------|-------------|
|
|
188
|
+
| `account` | string | No | Account name |
|
|
189
|
+
| `mailbox` | string | No | Mailbox name |
|
|
190
|
+
|
|
191
|
+
**Example response:**
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"unreadCount": 42
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### `mail_send`
|
|
199
|
+
|
|
200
|
+
Send an email using Apple Mail.
|
|
201
|
+
|
|
202
|
+
| Parameter | Type | Required | Description |
|
|
203
|
+
|-----------|------|----------|-------------|
|
|
204
|
+
| `to` | string or string[] | **Yes** | Recipient email(s) |
|
|
205
|
+
| `subject` | string | **Yes** | Email subject |
|
|
206
|
+
| `body` | string | **Yes** | Email body |
|
|
207
|
+
| `cc` | string or string[] | No | CC recipient(s) |
|
|
208
|
+
| `bcc` | string or string[] | No | BCC recipient(s) |
|
|
209
|
+
| `from` | string | No | Sender (must be configured account) |
|
|
210
|
+
|
|
211
|
+
**Example response:**
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"success": true,
|
|
215
|
+
"message": "Message sent successfully"
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Permissions
|
|
220
|
+
|
|
221
|
+
On first use, macOS will prompt you to grant permissions:
|
|
222
|
+
1. **Automation** - Allow the terminal/app to control Apple Mail
|
|
223
|
+
2. **Mail Access** - Allow access to your email data
|
|
224
|
+
|
|
225
|
+
You can manage these in System Settings > Privacy & Security > Automation.
|
|
226
|
+
|
|
227
|
+
## Development
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
# Install dependencies
|
|
231
|
+
bun install
|
|
232
|
+
|
|
233
|
+
# Run in development mode
|
|
234
|
+
bun run dev
|
|
235
|
+
|
|
236
|
+
# Run the server
|
|
237
|
+
bun run start
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Troubleshooting
|
|
241
|
+
|
|
242
|
+
### "No accounts found"
|
|
243
|
+
- Ensure Apple Mail is running and has at least one account configured
|
|
244
|
+
- Check that automation permissions are granted in System Settings
|
|
245
|
+
|
|
246
|
+
### "Operation not permitted"
|
|
247
|
+
- Grant automation permissions: System Settings > Privacy & Security > Automation
|
|
248
|
+
- Ensure the terminal/app running the MCP server has permission to control Mail
|
|
249
|
+
|
|
250
|
+
### Server not connecting
|
|
251
|
+
- Restart your AI client (Claude Code, Cursor, etc.) after adding the MCP configuration
|
|
252
|
+
- Verify the path to the server is correct in your configuration
|
|
253
|
+
- Check that Bun is installed and accessible from your PATH
|
|
254
|
+
|
|
255
|
+
## License
|
|
256
|
+
|
|
257
|
+
MIT
|
|
258
|
+
|
|
259
|
+
## Credits
|
|
260
|
+
|
|
261
|
+
Built as an alternative to [apple-mcp](https://github.com/Dhravya/apple-mcp) with working mail operations using direct AppleScript execution.
|
package/bun.lock
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"configVersion": 1,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "apple-mail-mcp",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@types/node": "^20.0.0",
|
|
12
|
+
"typescript": "^5.0.0",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
"packages": {
|
|
17
|
+
"@hono/node-server": ["@hono/node-server@1.19.8", "", { "peerDependencies": { "hono": "^4" } }, "sha512-0/g2lIOPzX8f3vzW1ggQgvG5mjtFBDBHFAzI5SFAi2DzSqS9luJwqg9T6O/gKYLi+inS7eNxBeIFkkghIPvrMA=="],
|
|
18
|
+
|
|
19
|
+
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="],
|
|
20
|
+
|
|
21
|
+
"@types/node": ["@types/node@20.19.28", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-VyKBr25BuFDzBFCK5sUM6ZXiWfqgCTwTAOK8qzGV/m9FCirXYDlmczJ+d5dXBAQALGCdRRdbteKYfJ84NGEusw=="],
|
|
22
|
+
|
|
23
|
+
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
|
24
|
+
|
|
25
|
+
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
|
26
|
+
|
|
27
|
+
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
|
|
28
|
+
|
|
29
|
+
"body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
|
|
30
|
+
|
|
31
|
+
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
|
32
|
+
|
|
33
|
+
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
|
34
|
+
|
|
35
|
+
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
|
36
|
+
|
|
37
|
+
"content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="],
|
|
38
|
+
|
|
39
|
+
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
|
|
40
|
+
|
|
41
|
+
"cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
|
42
|
+
|
|
43
|
+
"cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
|
|
44
|
+
|
|
45
|
+
"cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="],
|
|
46
|
+
|
|
47
|
+
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
|
48
|
+
|
|
49
|
+
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
|
50
|
+
|
|
51
|
+
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
|
52
|
+
|
|
53
|
+
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
|
54
|
+
|
|
55
|
+
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
|
56
|
+
|
|
57
|
+
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
|
58
|
+
|
|
59
|
+
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
|
60
|
+
|
|
61
|
+
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
|
62
|
+
|
|
63
|
+
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
|
64
|
+
|
|
65
|
+
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
|
|
66
|
+
|
|
67
|
+
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
|
|
68
|
+
|
|
69
|
+
"eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="],
|
|
70
|
+
|
|
71
|
+
"eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="],
|
|
72
|
+
|
|
73
|
+
"express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
|
|
74
|
+
|
|
75
|
+
"express-rate-limit": ["express-rate-limit@7.5.1", "", { "peerDependencies": { "express": ">= 4.11" } }, "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw=="],
|
|
76
|
+
|
|
77
|
+
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
|
78
|
+
|
|
79
|
+
"fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
|
|
80
|
+
|
|
81
|
+
"finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
|
|
82
|
+
|
|
83
|
+
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
|
84
|
+
|
|
85
|
+
"fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
|
|
86
|
+
|
|
87
|
+
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
|
88
|
+
|
|
89
|
+
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
|
90
|
+
|
|
91
|
+
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
|
92
|
+
|
|
93
|
+
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
|
94
|
+
|
|
95
|
+
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
|
96
|
+
|
|
97
|
+
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
|
98
|
+
|
|
99
|
+
"hono": ["hono@4.11.3", "", {}, "sha512-PmQi306+M/ct/m5s66Hrg+adPnkD5jiO6IjA7WhWw0gSBSo1EcRegwuI1deZ+wd5pzCGynCcn2DprnE4/yEV4w=="],
|
|
100
|
+
|
|
101
|
+
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
|
102
|
+
|
|
103
|
+
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
|
104
|
+
|
|
105
|
+
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
|
106
|
+
|
|
107
|
+
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
|
108
|
+
|
|
109
|
+
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
|
110
|
+
|
|
111
|
+
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
|
112
|
+
|
|
113
|
+
"jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
|
|
114
|
+
|
|
115
|
+
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
|
116
|
+
|
|
117
|
+
"json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
|
|
118
|
+
|
|
119
|
+
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
|
120
|
+
|
|
121
|
+
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
|
|
122
|
+
|
|
123
|
+
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
|
|
124
|
+
|
|
125
|
+
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
|
126
|
+
|
|
127
|
+
"mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
|
128
|
+
|
|
129
|
+
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
|
130
|
+
|
|
131
|
+
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
|
132
|
+
|
|
133
|
+
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
|
134
|
+
|
|
135
|
+
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
|
136
|
+
|
|
137
|
+
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
|
138
|
+
|
|
139
|
+
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
|
140
|
+
|
|
141
|
+
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
|
|
142
|
+
|
|
143
|
+
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
|
144
|
+
|
|
145
|
+
"path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
|
|
146
|
+
|
|
147
|
+
"pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="],
|
|
148
|
+
|
|
149
|
+
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
|
150
|
+
|
|
151
|
+
"qs": ["qs@6.14.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ=="],
|
|
152
|
+
|
|
153
|
+
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
|
154
|
+
|
|
155
|
+
"raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
|
|
156
|
+
|
|
157
|
+
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
|
158
|
+
|
|
159
|
+
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
|
|
160
|
+
|
|
161
|
+
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
|
162
|
+
|
|
163
|
+
"send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
|
|
164
|
+
|
|
165
|
+
"serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="],
|
|
166
|
+
|
|
167
|
+
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
|
|
168
|
+
|
|
169
|
+
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
|
170
|
+
|
|
171
|
+
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
|
172
|
+
|
|
173
|
+
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
|
174
|
+
|
|
175
|
+
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
|
|
176
|
+
|
|
177
|
+
"side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
|
|
178
|
+
|
|
179
|
+
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
|
180
|
+
|
|
181
|
+
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
|
182
|
+
|
|
183
|
+
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
|
184
|
+
|
|
185
|
+
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
|
|
186
|
+
|
|
187
|
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
188
|
+
|
|
189
|
+
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
|
190
|
+
|
|
191
|
+
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
|
192
|
+
|
|
193
|
+
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
|
194
|
+
|
|
195
|
+
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
|
196
|
+
|
|
197
|
+
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
|
198
|
+
|
|
199
|
+
"zod": ["zod@4.3.5", "", {}, "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g=="],
|
|
200
|
+
|
|
201
|
+
"zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
|
|
202
|
+
}
|
|
203
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sempervirens-labs/apple-mail-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Apple Mail - list accounts, mailboxes, search emails, and send messages",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"apple-mail-mcp": "./src/index.ts"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "bun run src/index.ts",
|
|
12
|
+
"dev": "bun run --watch src/index.ts"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mcp",
|
|
16
|
+
"apple",
|
|
17
|
+
"mail",
|
|
18
|
+
"email",
|
|
19
|
+
"macos",
|
|
20
|
+
"applescript",
|
|
21
|
+
"claude"
|
|
22
|
+
],
|
|
23
|
+
"author": "Robert Bouschery",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/rbouschery/apple-mail-mcp.git"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/rbouschery/apple-mail-mcp#readme",
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^20.0.0",
|
|
38
|
+
"typescript": "^5.0.0"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Execute an AppleScript and return the result
|
|
5
|
+
*/
|
|
6
|
+
export function runAppleScript(script: string): string {
|
|
7
|
+
try {
|
|
8
|
+
// Use osascript with heredoc to handle complex scripts
|
|
9
|
+
const result = execSync(`osascript <<'APPLESCRIPT'
|
|
10
|
+
${script}
|
|
11
|
+
APPLESCRIPT`, {
|
|
12
|
+
encoding: "utf-8",
|
|
13
|
+
maxBuffer: 10 * 1024 * 1024, // 10MB buffer for large results
|
|
14
|
+
});
|
|
15
|
+
return result.trim();
|
|
16
|
+
} catch (error: any) {
|
|
17
|
+
throw new Error(`AppleScript error: ${error.message}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* List all email accounts configured in Apple Mail
|
|
23
|
+
*/
|
|
24
|
+
export function listAccounts(): string[] {
|
|
25
|
+
const script = `
|
|
26
|
+
tell application "Mail"
|
|
27
|
+
set accountList to {}
|
|
28
|
+
repeat with acc in accounts
|
|
29
|
+
set end of accountList to name of acc
|
|
30
|
+
end repeat
|
|
31
|
+
return accountList
|
|
32
|
+
end tell
|
|
33
|
+
`;
|
|
34
|
+
const result = runAppleScript(script);
|
|
35
|
+
if (!result) return [];
|
|
36
|
+
|
|
37
|
+
// AppleScript returns comma-separated list
|
|
38
|
+
return result.split(", ").map(s => s.trim()).filter(Boolean);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* List mailboxes for a specific account or all accounts
|
|
43
|
+
*/
|
|
44
|
+
export function listMailboxes(accountName?: string): { account: string; mailboxes: string[] }[] {
|
|
45
|
+
const script = accountName
|
|
46
|
+
? `
|
|
47
|
+
tell application "Mail"
|
|
48
|
+
set results to {}
|
|
49
|
+
try
|
|
50
|
+
set acc to account "${accountName}"
|
|
51
|
+
set mailboxList to {}
|
|
52
|
+
repeat with mb in mailboxes of acc
|
|
53
|
+
set end of mailboxList to name of mb
|
|
54
|
+
end repeat
|
|
55
|
+
return mailboxList
|
|
56
|
+
on error
|
|
57
|
+
return {}
|
|
58
|
+
end try
|
|
59
|
+
end tell
|
|
60
|
+
`
|
|
61
|
+
: `
|
|
62
|
+
tell application "Mail"
|
|
63
|
+
set results to ""
|
|
64
|
+
repeat with acc in accounts
|
|
65
|
+
set accName to name of acc
|
|
66
|
+
set mailboxList to {}
|
|
67
|
+
repeat with mb in mailboxes of acc
|
|
68
|
+
set end of mailboxList to name of mb
|
|
69
|
+
end repeat
|
|
70
|
+
set results to results & accName & ":" & (mailboxList as string) & "|||"
|
|
71
|
+
end repeat
|
|
72
|
+
return results
|
|
73
|
+
end tell
|
|
74
|
+
`;
|
|
75
|
+
|
|
76
|
+
const result = runAppleScript(script);
|
|
77
|
+
|
|
78
|
+
if (accountName) {
|
|
79
|
+
// Single account result
|
|
80
|
+
const mailboxes = result ? result.split(", ").map(s => s.trim()).filter(Boolean) : [];
|
|
81
|
+
return [{ account: accountName, mailboxes }];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Multiple accounts result
|
|
85
|
+
const accountResults: { account: string; mailboxes: string[] }[] = [];
|
|
86
|
+
const parts = result.split("|||").filter(Boolean);
|
|
87
|
+
|
|
88
|
+
for (const part of parts) {
|
|
89
|
+
const [accName, ...mailboxParts] = part.split(":");
|
|
90
|
+
const mailboxes = mailboxParts.join(":").split(", ").map(s => s.trim()).filter(Boolean);
|
|
91
|
+
if (accName) {
|
|
92
|
+
accountResults.push({ account: accName.trim(), mailboxes });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return accountResults;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface Email {
|
|
100
|
+
id: number;
|
|
101
|
+
subject: string;
|
|
102
|
+
sender: string;
|
|
103
|
+
dateSent: string;
|
|
104
|
+
isRead: boolean;
|
|
105
|
+
content?: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get emails from a mailbox
|
|
110
|
+
*/
|
|
111
|
+
export function getEmails(options: {
|
|
112
|
+
account?: string;
|
|
113
|
+
mailbox?: string;
|
|
114
|
+
limit?: number;
|
|
115
|
+
includeContent?: boolean;
|
|
116
|
+
}): Email[] {
|
|
117
|
+
const { account, mailbox = "INBOX", limit = 10, includeContent = false } = options;
|
|
118
|
+
|
|
119
|
+
const contentPart = includeContent
|
|
120
|
+
? `set msgContent to content of msg`
|
|
121
|
+
: `set msgContent to ""`;
|
|
122
|
+
|
|
123
|
+
const accountPart = account
|
|
124
|
+
? `mailbox "${mailbox}" of account "${account}"`
|
|
125
|
+
: `mailbox "${mailbox}"`;
|
|
126
|
+
|
|
127
|
+
const script = `
|
|
128
|
+
tell application "Mail"
|
|
129
|
+
set results to ""
|
|
130
|
+
try
|
|
131
|
+
set theMailbox to ${accountPart}
|
|
132
|
+
set msgList to messages of theMailbox
|
|
133
|
+
set msgCount to count of msgList
|
|
134
|
+
if msgCount > ${limit} then set msgCount to ${limit}
|
|
135
|
+
|
|
136
|
+
repeat with i from 1 to msgCount
|
|
137
|
+
set msg to item i of msgList
|
|
138
|
+
set msgId to id of msg
|
|
139
|
+
set msgSubject to subject of msg
|
|
140
|
+
set msgSender to sender of msg
|
|
141
|
+
set msgDate to date sent of msg
|
|
142
|
+
set msgRead to read status of msg
|
|
143
|
+
${contentPart}
|
|
144
|
+
|
|
145
|
+
set results to results & msgId & "<<>>" & msgSubject & "<<>>" & msgSender & "<<>>" & (msgDate as string) & "<<>>" & msgRead & "<<>>" & msgContent & "|||"
|
|
146
|
+
end repeat
|
|
147
|
+
on error errMsg
|
|
148
|
+
return "ERROR:" & errMsg
|
|
149
|
+
end try
|
|
150
|
+
return results
|
|
151
|
+
end tell
|
|
152
|
+
`;
|
|
153
|
+
|
|
154
|
+
const result = runAppleScript(script);
|
|
155
|
+
|
|
156
|
+
if (result.startsWith("ERROR:")) {
|
|
157
|
+
throw new Error(result.substring(6));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const emails: Email[] = [];
|
|
161
|
+
const parts = result.split("|||").filter(Boolean);
|
|
162
|
+
|
|
163
|
+
for (const part of parts) {
|
|
164
|
+
const [id, subject, sender, dateSent, isRead, content] = part.split("<<>>");
|
|
165
|
+
emails.push({
|
|
166
|
+
id: parseInt(id) || 0,
|
|
167
|
+
subject: subject || "(No Subject)",
|
|
168
|
+
sender: sender || "(Unknown)",
|
|
169
|
+
dateSent: dateSent || "",
|
|
170
|
+
isRead: isRead === "true",
|
|
171
|
+
content: content || undefined,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return emails;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Search emails by query
|
|
180
|
+
*/
|
|
181
|
+
export function searchEmails(options: {
|
|
182
|
+
query: string;
|
|
183
|
+
account?: string;
|
|
184
|
+
mailbox?: string;
|
|
185
|
+
limit?: number;
|
|
186
|
+
}): Email[] {
|
|
187
|
+
const { query, account, mailbox, limit = 10 } = options;
|
|
188
|
+
|
|
189
|
+
// Build the mailbox selection part
|
|
190
|
+
let mailboxPart: string;
|
|
191
|
+
if (account && mailbox) {
|
|
192
|
+
mailboxPart = `{mailbox "${mailbox}" of account "${account}"}`;
|
|
193
|
+
} else if (account) {
|
|
194
|
+
mailboxPart = `mailboxes of account "${account}"`;
|
|
195
|
+
} else if (mailbox) {
|
|
196
|
+
mailboxPart = `{mailbox "${mailbox}"}`;
|
|
197
|
+
} else {
|
|
198
|
+
mailboxPart = `inbox`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const script = `
|
|
202
|
+
tell application "Mail"
|
|
203
|
+
set results to ""
|
|
204
|
+
set foundCount to 0
|
|
205
|
+
set searchQuery to "${query.replace(/"/g, '\\"')}"
|
|
206
|
+
|
|
207
|
+
try
|
|
208
|
+
set searchMailboxes to ${mailboxPart}
|
|
209
|
+
repeat with mb in searchMailboxes
|
|
210
|
+
if foundCount >= ${limit} then exit repeat
|
|
211
|
+
|
|
212
|
+
set msgList to (messages of mb whose subject contains searchQuery or sender contains searchQuery or content contains searchQuery)
|
|
213
|
+
repeat with msg in msgList
|
|
214
|
+
if foundCount >= ${limit} then exit repeat
|
|
215
|
+
|
|
216
|
+
set msgId to id of msg
|
|
217
|
+
set msgSubject to subject of msg
|
|
218
|
+
set msgSender to sender of msg
|
|
219
|
+
set msgDate to date sent of msg
|
|
220
|
+
set msgRead to read status of msg
|
|
221
|
+
|
|
222
|
+
set results to results & msgId & "<<>>" & msgSubject & "<<>>" & msgSender & "<<>>" & (msgDate as string) & "<<>>" & msgRead & "|||"
|
|
223
|
+
set foundCount to foundCount + 1
|
|
224
|
+
end repeat
|
|
225
|
+
end repeat
|
|
226
|
+
on error errMsg
|
|
227
|
+
return "ERROR:" & errMsg
|
|
228
|
+
end try
|
|
229
|
+
return results
|
|
230
|
+
end tell
|
|
231
|
+
`;
|
|
232
|
+
|
|
233
|
+
const result = runAppleScript(script);
|
|
234
|
+
|
|
235
|
+
if (result.startsWith("ERROR:")) {
|
|
236
|
+
throw new Error(result.substring(6));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const emails: Email[] = [];
|
|
240
|
+
const parts = result.split("|||").filter(Boolean);
|
|
241
|
+
|
|
242
|
+
for (const part of parts) {
|
|
243
|
+
const [id, subject, sender, dateSent, isRead] = part.split("<<>>");
|
|
244
|
+
emails.push({
|
|
245
|
+
id: parseInt(id) || 0,
|
|
246
|
+
subject: subject || "(No Subject)",
|
|
247
|
+
sender: sender || "(Unknown)",
|
|
248
|
+
dateSent: dateSent || "",
|
|
249
|
+
isRead: isRead === "true",
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return emails;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Get unread email count
|
|
258
|
+
*/
|
|
259
|
+
export function getUnreadCount(options: {
|
|
260
|
+
account?: string;
|
|
261
|
+
mailbox?: string;
|
|
262
|
+
}): number {
|
|
263
|
+
const { account, mailbox } = options;
|
|
264
|
+
|
|
265
|
+
let script: string;
|
|
266
|
+
|
|
267
|
+
if (account && mailbox) {
|
|
268
|
+
script = `
|
|
269
|
+
tell application "Mail"
|
|
270
|
+
return unread count of mailbox "${mailbox}" of account "${account}"
|
|
271
|
+
end tell
|
|
272
|
+
`;
|
|
273
|
+
} else if (account) {
|
|
274
|
+
script = `
|
|
275
|
+
tell application "Mail"
|
|
276
|
+
set total to 0
|
|
277
|
+
repeat with mb in mailboxes of account "${account}"
|
|
278
|
+
set total to total + (unread count of mb)
|
|
279
|
+
end repeat
|
|
280
|
+
return total
|
|
281
|
+
end tell
|
|
282
|
+
`;
|
|
283
|
+
} else if (mailbox) {
|
|
284
|
+
script = `
|
|
285
|
+
tell application "Mail"
|
|
286
|
+
set total to 0
|
|
287
|
+
repeat with acc in accounts
|
|
288
|
+
try
|
|
289
|
+
set total to total + (unread count of mailbox "${mailbox}" of acc)
|
|
290
|
+
end try
|
|
291
|
+
end repeat
|
|
292
|
+
return total
|
|
293
|
+
end tell
|
|
294
|
+
`;
|
|
295
|
+
} else {
|
|
296
|
+
script = `
|
|
297
|
+
tell application "Mail"
|
|
298
|
+
set total to 0
|
|
299
|
+
repeat with acc in accounts
|
|
300
|
+
repeat with mb in mailboxes of acc
|
|
301
|
+
set total to total + (unread count of mb)
|
|
302
|
+
end repeat
|
|
303
|
+
end repeat
|
|
304
|
+
return total
|
|
305
|
+
end tell
|
|
306
|
+
`;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const result = runAppleScript(script);
|
|
310
|
+
return parseInt(result) || 0;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Send an email
|
|
315
|
+
*/
|
|
316
|
+
export function sendEmail(options: {
|
|
317
|
+
to: string | string[];
|
|
318
|
+
subject: string;
|
|
319
|
+
body: string;
|
|
320
|
+
cc?: string | string[];
|
|
321
|
+
bcc?: string | string[];
|
|
322
|
+
from?: string;
|
|
323
|
+
}): { success: boolean; message: string } {
|
|
324
|
+
const { to, subject, body, cc, bcc, from } = options;
|
|
325
|
+
|
|
326
|
+
const toList = Array.isArray(to) ? to : [to];
|
|
327
|
+
const ccList = cc ? (Array.isArray(cc) ? cc : [cc]) : [];
|
|
328
|
+
const bccList = bcc ? (Array.isArray(bcc) ? bcc : [bcc]) : [];
|
|
329
|
+
|
|
330
|
+
// Build recipient parts
|
|
331
|
+
const toRecipients = toList.map(addr => `make new to recipient at end of to recipients with properties {address:"${addr}"}`).join("\n ");
|
|
332
|
+
const ccRecipients = ccList.map(addr => `make new cc recipient at end of cc recipients with properties {address:"${addr}"}`).join("\n ");
|
|
333
|
+
const bccRecipients = bccList.map(addr => `make new bcc recipient at end of bcc recipients with properties {address:"${addr}"}`).join("\n ");
|
|
334
|
+
|
|
335
|
+
const fromPart = from ? `, sender:"${from}"` : "";
|
|
336
|
+
|
|
337
|
+
const script = `
|
|
338
|
+
tell application "Mail"
|
|
339
|
+
set newMessage to make new outgoing message with properties {subject:"${subject.replace(/"/g, '\\"')}", content:"${body.replace(/"/g, '\\"').replace(/\n/g, "\\n")}"${fromPart}}
|
|
340
|
+
tell newMessage
|
|
341
|
+
${toRecipients}
|
|
342
|
+
${ccRecipients ? ccRecipients : ""}
|
|
343
|
+
${bccRecipients ? bccRecipients : ""}
|
|
344
|
+
end tell
|
|
345
|
+
send newMessage
|
|
346
|
+
return "Message sent successfully"
|
|
347
|
+
end tell
|
|
348
|
+
`;
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
const result = runAppleScript(script);
|
|
352
|
+
return { success: true, message: result || "Message sent successfully" };
|
|
353
|
+
} catch (error: any) {
|
|
354
|
+
return { success: false, message: error.message };
|
|
355
|
+
}
|
|
356
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import {
|
|
6
|
+
CallToolRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
|
|
10
|
+
import { tools } from "./tools.js";
|
|
11
|
+
import {
|
|
12
|
+
listAccounts,
|
|
13
|
+
listMailboxes,
|
|
14
|
+
getEmails,
|
|
15
|
+
searchEmails,
|
|
16
|
+
getUnreadCount,
|
|
17
|
+
sendEmail,
|
|
18
|
+
} from "./applescript/mail.js";
|
|
19
|
+
|
|
20
|
+
// Create MCP server
|
|
21
|
+
const server = new Server(
|
|
22
|
+
{
|
|
23
|
+
name: "apple-mail-mcp",
|
|
24
|
+
version: "1.0.0",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
capabilities: {
|
|
28
|
+
tools: {},
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
// Handler for listing available tools
|
|
34
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
35
|
+
return { tools };
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Handler for tool calls
|
|
39
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
40
|
+
const { name, arguments: args } = request.params;
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
switch (name) {
|
|
44
|
+
case "mail_list_accounts": {
|
|
45
|
+
const accounts = listAccounts();
|
|
46
|
+
return {
|
|
47
|
+
content: [
|
|
48
|
+
{
|
|
49
|
+
type: "text",
|
|
50
|
+
text: JSON.stringify({ accounts }, null, 2),
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
case "mail_list_mailboxes": {
|
|
57
|
+
const account = args?.account as string | undefined;
|
|
58
|
+
const result = listMailboxes(account);
|
|
59
|
+
return {
|
|
60
|
+
content: [
|
|
61
|
+
{
|
|
62
|
+
type: "text",
|
|
63
|
+
text: JSON.stringify({ mailboxes: result }, null, 2),
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
case "mail_get_emails": {
|
|
70
|
+
const emails = getEmails({
|
|
71
|
+
account: args?.account as string | undefined,
|
|
72
|
+
mailbox: (args?.mailbox as string) || "INBOX",
|
|
73
|
+
limit: (args?.limit as number) || 10,
|
|
74
|
+
includeContent: (args?.includeContent as boolean) || false,
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
content: [
|
|
78
|
+
{
|
|
79
|
+
type: "text",
|
|
80
|
+
text: JSON.stringify({ emails, count: emails.length }, null, 2),
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
case "mail_search": {
|
|
87
|
+
const query = args?.query as string;
|
|
88
|
+
if (!query) {
|
|
89
|
+
throw new Error("Search query is required");
|
|
90
|
+
}
|
|
91
|
+
const emails = searchEmails({
|
|
92
|
+
query,
|
|
93
|
+
account: args?.account as string | undefined,
|
|
94
|
+
mailbox: args?.mailbox as string | undefined,
|
|
95
|
+
limit: (args?.limit as number) || 10,
|
|
96
|
+
});
|
|
97
|
+
return {
|
|
98
|
+
content: [
|
|
99
|
+
{
|
|
100
|
+
type: "text",
|
|
101
|
+
text: JSON.stringify({ emails, count: emails.length, query }, null, 2),
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
case "mail_get_unread_count": {
|
|
108
|
+
const count = getUnreadCount({
|
|
109
|
+
account: args?.account as string | undefined,
|
|
110
|
+
mailbox: args?.mailbox as string | undefined,
|
|
111
|
+
});
|
|
112
|
+
return {
|
|
113
|
+
content: [
|
|
114
|
+
{
|
|
115
|
+
type: "text",
|
|
116
|
+
text: JSON.stringify({ unreadCount: count }, null, 2),
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
case "mail_send": {
|
|
123
|
+
const to = args?.to as string | string[];
|
|
124
|
+
const subject = args?.subject as string;
|
|
125
|
+
const body = args?.body as string;
|
|
126
|
+
|
|
127
|
+
if (!to || !subject || !body) {
|
|
128
|
+
throw new Error("Required fields: to, subject, body");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const result = sendEmail({
|
|
132
|
+
to,
|
|
133
|
+
subject,
|
|
134
|
+
body,
|
|
135
|
+
cc: args?.cc as string | string[] | undefined,
|
|
136
|
+
bcc: args?.bcc as string | string[] | undefined,
|
|
137
|
+
from: args?.from as string | undefined,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
content: [
|
|
142
|
+
{
|
|
143
|
+
type: "text",
|
|
144
|
+
text: JSON.stringify(result, null, 2),
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
default:
|
|
151
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
152
|
+
}
|
|
153
|
+
} catch (error: any) {
|
|
154
|
+
return {
|
|
155
|
+
content: [
|
|
156
|
+
{
|
|
157
|
+
type: "text",
|
|
158
|
+
text: JSON.stringify({ error: error.message }, null, 2),
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
isError: true,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Start the server
|
|
167
|
+
async function main() {
|
|
168
|
+
const transport = new StdioServerTransport();
|
|
169
|
+
await server.connect(transport);
|
|
170
|
+
console.error("Apple Mail MCP server running on stdio");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
main().catch((error) => {
|
|
174
|
+
console.error("Fatal error:", error);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
});
|
package/src/tools.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
|
|
3
|
+
export const MAIL_LIST_ACCOUNTS: Tool = {
|
|
4
|
+
name: "mail_list_accounts",
|
|
5
|
+
description: "List all email accounts configured in Apple Mail",
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: "object",
|
|
8
|
+
properties: {},
|
|
9
|
+
required: [],
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const MAIL_LIST_MAILBOXES: Tool = {
|
|
14
|
+
name: "mail_list_mailboxes",
|
|
15
|
+
description: "List all mailboxes (folders) for a specific account or all accounts in Apple Mail",
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: {
|
|
19
|
+
account: {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "The name of the email account to list mailboxes for. If not provided, lists mailboxes for all accounts.",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
required: [],
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const MAIL_GET_EMAILS: Tool = {
|
|
29
|
+
name: "mail_get_emails",
|
|
30
|
+
description: "Get recent emails from a mailbox in Apple Mail",
|
|
31
|
+
inputSchema: {
|
|
32
|
+
type: "object",
|
|
33
|
+
properties: {
|
|
34
|
+
account: {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "The name of the email account",
|
|
37
|
+
},
|
|
38
|
+
mailbox: {
|
|
39
|
+
type: "string",
|
|
40
|
+
description: "The name of the mailbox/folder (default: INBOX)",
|
|
41
|
+
default: "INBOX",
|
|
42
|
+
},
|
|
43
|
+
limit: {
|
|
44
|
+
type: "number",
|
|
45
|
+
description: "Maximum number of emails to retrieve (default: 10)",
|
|
46
|
+
default: 10,
|
|
47
|
+
},
|
|
48
|
+
includeContent: {
|
|
49
|
+
type: "boolean",
|
|
50
|
+
description: "Whether to include the email body content (default: false)",
|
|
51
|
+
default: false,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
required: [],
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const MAIL_SEARCH: Tool = {
|
|
59
|
+
name: "mail_search",
|
|
60
|
+
description: "Search emails in Apple Mail by subject, sender, or content",
|
|
61
|
+
inputSchema: {
|
|
62
|
+
type: "object",
|
|
63
|
+
properties: {
|
|
64
|
+
query: {
|
|
65
|
+
type: "string",
|
|
66
|
+
description: "The search query to match against email subject, sender, or content",
|
|
67
|
+
},
|
|
68
|
+
account: {
|
|
69
|
+
type: "string",
|
|
70
|
+
description: "The name of the email account to search in",
|
|
71
|
+
},
|
|
72
|
+
mailbox: {
|
|
73
|
+
type: "string",
|
|
74
|
+
description: "The name of the mailbox/folder to search in",
|
|
75
|
+
},
|
|
76
|
+
limit: {
|
|
77
|
+
type: "number",
|
|
78
|
+
description: "Maximum number of emails to return (default: 10)",
|
|
79
|
+
default: 10,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
required: ["query"],
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const MAIL_GET_UNREAD_COUNT: Tool = {
|
|
87
|
+
name: "mail_get_unread_count",
|
|
88
|
+
description: "Get the count of unread emails in Apple Mail",
|
|
89
|
+
inputSchema: {
|
|
90
|
+
type: "object",
|
|
91
|
+
properties: {
|
|
92
|
+
account: {
|
|
93
|
+
type: "string",
|
|
94
|
+
description: "The name of the email account",
|
|
95
|
+
},
|
|
96
|
+
mailbox: {
|
|
97
|
+
type: "string",
|
|
98
|
+
description: "The name of the mailbox/folder",
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
required: [],
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const MAIL_SEND: Tool = {
|
|
106
|
+
name: "mail_send",
|
|
107
|
+
description: "Send an email using Apple Mail",
|
|
108
|
+
inputSchema: {
|
|
109
|
+
type: "object",
|
|
110
|
+
properties: {
|
|
111
|
+
to: {
|
|
112
|
+
oneOf: [
|
|
113
|
+
{ type: "string" },
|
|
114
|
+
{ type: "array", items: { type: "string" } },
|
|
115
|
+
],
|
|
116
|
+
description: "Email address(es) of the recipient(s)",
|
|
117
|
+
},
|
|
118
|
+
subject: {
|
|
119
|
+
type: "string",
|
|
120
|
+
description: "The email subject line",
|
|
121
|
+
},
|
|
122
|
+
body: {
|
|
123
|
+
type: "string",
|
|
124
|
+
description: "The email body content",
|
|
125
|
+
},
|
|
126
|
+
cc: {
|
|
127
|
+
oneOf: [
|
|
128
|
+
{ type: "string" },
|
|
129
|
+
{ type: "array", items: { type: "string" } },
|
|
130
|
+
],
|
|
131
|
+
description: "Email address(es) for CC recipients",
|
|
132
|
+
},
|
|
133
|
+
bcc: {
|
|
134
|
+
oneOf: [
|
|
135
|
+
{ type: "string" },
|
|
136
|
+
{ type: "array", items: { type: "string" } },
|
|
137
|
+
],
|
|
138
|
+
description: "Email address(es) for BCC recipients",
|
|
139
|
+
},
|
|
140
|
+
from: {
|
|
141
|
+
type: "string",
|
|
142
|
+
description: "The sender email address (must be a configured account)",
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
required: ["to", "subject", "body"],
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export const tools: Tool[] = [
|
|
150
|
+
MAIL_LIST_ACCOUNTS,
|
|
151
|
+
MAIL_LIST_MAILBOXES,
|
|
152
|
+
MAIL_GET_EMAILS,
|
|
153
|
+
MAIL_SEARCH,
|
|
154
|
+
MAIL_GET_UNREAD_COUNT,
|
|
155
|
+
MAIL_SEND,
|
|
156
|
+
];
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"outDir": "dist",
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"types": ["node"]
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"],
|
|
14
|
+
"exclude": ["node_modules", "dist"]
|
|
15
|
+
}
|