apple-mail-mcp 2.10.1 → 2.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -17
- package/build/index.js +36 -17
- package/docs/IMAP-SETUP.md +27 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -147,6 +147,7 @@ tool, and troubleshooting. Verify any time by running the **`doctor`** tool.
|
|
|
147
147
|
|
|
148
148
|
- **macOS** - Apple Mail and AppleScript are macOS-only
|
|
149
149
|
- **Node.js 20+** - Required for the MCP server
|
|
150
|
+
- **Node.js 22.5+ and Full Disk Access** - Required by `search-contacts` only. It reads the Contacts database directly through Node's built-in `node:sqlite`, which does not exist before 22.5. On an older runtime, or without Full Disk Access for the Node binary, it logs one line to stderr and returns **an empty list rather than an error** — so "no contacts found" can mean "cannot read Contacts". Every other tool works on Node 20+. See [Node runtime & TCC permissions](https://github.com/sweetrb/apple-mail-mcp/blob/main/docs/NODE-RUNTIME-AND-TCC-PERMISSIONS.md).
|
|
150
151
|
- **Apple Mail** - Must have at least one account configured (iCloud, Gmail, Exchange, etc.)
|
|
151
152
|
|
|
152
153
|
## Features
|
|
@@ -264,6 +265,8 @@ Get the full content of a message.
|
|
|
264
265
|
|-----------|------|----------|-------------|
|
|
265
266
|
| `id` | string | Yes | Message ID |
|
|
266
267
|
| `preferHtml` | boolean | No | Return HTML source instead of plain text |
|
|
268
|
+
| `mailbox` | string | No | Mailbox holding the message (e.g. `"Sent Items"`). With `account`, opens that mailbox directly instead of scanning every mailbox — this is the fix for timeouts on large folders |
|
|
269
|
+
| `account` | string | No | Account holding the message. Pair with `mailbox` to skip the cross-mailbox scan |
|
|
267
270
|
|
|
268
271
|
**Returns:** Subject line and message body (plain text by default, HTML if `preferHtml` is true and HTML content is available).
|
|
269
272
|
|
|
@@ -668,7 +671,7 @@ Return an attachment's bytes as base64 (the read counterpart to inline-base64 se
|
|
|
668
671
|
|
|
669
672
|
| Parameter | Type | Required | Description |
|
|
670
673
|
|-----------|------|----------|-------------|
|
|
671
|
-
| `id` | string | Yes |
|
|
674
|
+
| `id` | string | Yes | Message ID (numeric or `imap:…`) |
|
|
672
675
|
| `attachmentName` | string | Yes | Attachment filename (from `list-attachments`) |
|
|
673
676
|
|
|
674
677
|
**Returns:** The attachment bytes, base64-encoded (also in `structuredContent.contentBase64`).
|
|
@@ -748,6 +751,8 @@ Flag or unflag a message. `flag-message` optionally takes a flag **color**; `unf
|
|
|
748
751
|
|
|
749
752
|
**Flag colors** are an Apple Mail feature — the message's `flag index` (0 red, 1 orange, 2 yellow, 3 green, 4 blue, 5 purple, 6 gray), which is the property a Mail smart mailbox can match on. **The color is applied on both routes** (since 2.10.0): AppleScript sets the flag index directly, and for an **IMAP-routed** id (`imap:…`) the color is written as Mail.app's `$MailFlagBit0/1/2` keywords — a 3-bit field holding the same palette index. `\Flagged` on its own really is colorless, but those keywords ride alongside it in an ordinary `UID STORE`, so a smart mailbox keyed on flag color matches an IMAP-flagged message too. You do **not** need to resolve to a numeric id just to color a flag.
|
|
750
753
|
|
|
754
|
+
To **read** a color, the IMAP read path returns `flagColorIndex` in `structuredContent` — the same 0-6 palette index, omitted when the message carries no color bits. The AppleScript read path does not populate it.
|
|
755
|
+
|
|
751
756
|
---
|
|
752
757
|
|
|
753
758
|
#### `delete-message`
|
|
@@ -853,8 +858,12 @@ Get unread message count.
|
|
|
853
858
|
|
|
854
859
|
| Parameter | Type | Required | Description |
|
|
855
860
|
|-----------|------|----------|-------------|
|
|
856
|
-
| `mailbox` | string | No | Mailbox to check (omit for
|
|
857
|
-
| `account` | string | No | Account to check |
|
|
861
|
+
| `mailbox` | string | No | Mailbox to check (omit for **INBOX**) |
|
|
862
|
+
| `account` | string | No | Account to check (omit to sum each account's INBOX) |
|
|
863
|
+
|
|
864
|
+
**Returns:** The unread count for the requested scope.
|
|
865
|
+
|
|
866
|
+
> Omitting `mailbox` counts **INBOX**, not a cross-mailbox total. This changed in 2.8.15: summing every mailbox was slow and wrong on Gmail, where one message appears in INBOX, All Mail and every label it carries. For account-wide totals use [`get-mail-stats`](#get-mail-stats).
|
|
858
867
|
|
|
859
868
|
---
|
|
860
869
|
|
|
@@ -896,6 +905,8 @@ Rename a mailbox (creates new, moves messages, deletes old).
|
|
|
896
905
|
|
|
897
906
|
### Smart Mailbox Operations (intelligente Postfächer)
|
|
898
907
|
|
|
908
|
+
> **Requires Full Disk Access.** These tools read and write `~/Library/Mail/V*/MailData/SyncedSmartMailboxes.plist`, which is TCC-protected. Without Full Disk Access for the server's Node runtime the read simply finds nothing, and the tools report "no smart mailboxes" or "launch Mail at least once" rather than a permission error — see [Node runtime & TCC permissions](https://github.com/sweetrb/apple-mail-mcp/blob/main/docs/NODE-RUNTIME-AND-TCC-PERMISSIONS.md).
|
|
909
|
+
|
|
899
910
|
Smart mailboxes are Apple Mail's **criteria-based virtual views** — not real folders, so no messages are moved. AppleScript's `smart mailbox` / `intelligentes Postfach` terms don't compile reliably on localized (e.g. German) macOS, so these tools read and edit `~/Library/Mail/V*/MailData/SyncedSmartMailboxes.plist` directly.
|
|
900
911
|
|
|
901
912
|
**How writes stay safe:** creating or deleting a smart mailbox first backs the plist up to `SyncedSmartMailboxes.plist.bak`, edits a temp copy with `plutil`/`PlistBuddy`, validates it with `plutil -lint`, and only then atomically renames it into place. Your **existing** smart mailboxes — including any with date/data criteria — are never rewritten, only the single target entry is added or removed. These tools do **not** quit or restart Mail: **quit Mail first** for reliable results, since a running Mail may not show a new smart mailbox until it's relaunched and can overwrite plist edits it didn't make.
|
|
@@ -1030,14 +1041,15 @@ Delete a mail rule by name.
|
|
|
1030
1041
|
|
|
1031
1042
|
#### `search-contacts`
|
|
1032
1043
|
|
|
1033
|
-
Search
|
|
1044
|
+
Search the macOS Contacts database by name, organization, nickname, or email substring.
|
|
1045
|
+
|
|
1046
|
+
Since 2.8.7 this reads the AddressBook SQLite files directly rather than driving Contacts.app over AppleScript, so **Contacts.app need not be running and no Automation grant is involved** — but the Node runtime does need **Full Disk Access**, and **Node 22.5+** (see [Requirements](#requirements)). Without either, the tool returns an empty list rather than an error.
|
|
1034
1047
|
|
|
1035
1048
|
| Parameter | Type | Required | Description |
|
|
1036
1049
|
|-----------|------|----------|-------------|
|
|
1037
|
-
| `query` | string | Yes |
|
|
1038
|
-
| `limit` | number | No | Max results (default: 10) |
|
|
1050
|
+
| `query` | string | Yes | Substring matched against full name, organization, nickname, or any email address |
|
|
1039
1051
|
|
|
1040
|
-
**Returns:** List of contacts with name, email addresses, and phone numbers.
|
|
1052
|
+
**Returns:** List of contacts with name, email addresses, and phone numbers. Results are **not** truncated — a broad query returns every match.
|
|
1041
1053
|
|
|
1042
1054
|
---
|
|
1043
1055
|
|
|
@@ -1130,7 +1142,9 @@ Run a full setup diagnostic: Mail.app automation permission, account state (flag
|
|
|
1130
1142
|
|
|
1131
1143
|
Get mail statistics.
|
|
1132
1144
|
|
|
1133
|
-
|
|
1145
|
+
| Parameter | Type | Required | Description |
|
|
1146
|
+
|-----------|------|----------|-------------|
|
|
1147
|
+
| `account` | string | No | Limit to one account (uses fast IMAP `STATUS` when that account is IMAP-configured). Omit to merge across all accounts. |
|
|
1134
1148
|
|
|
1135
1149
|
**Returns:** Total and per-account message/unread counts, plus recently received stats (24h, 7d, 30d).
|
|
1136
1150
|
|
|
@@ -1271,7 +1285,7 @@ The entrypoint is written as:
|
|
|
1271
1285
|
|
|
1272
1286
|
## Security and Privacy
|
|
1273
1287
|
|
|
1274
|
-
- **
|
|
1288
|
+
- **No third parties** - The server talks only to Mail.app on this Mac (AppleScript) and, when you configure them, directly to **your own** mail provider over TLS (IMAP/SMTP). Nothing is sent to this project or any other service. With the default AppleScript backend everything stays on-device; the opt-in IMAP/SMTP backends necessarily reach your provider, which is what they are for.
|
|
1275
1289
|
- **Permission required** - macOS will prompt for automation permission on first use.
|
|
1276
1290
|
- **No credential storage** - The server doesn't store any passwords or authentication tokens.
|
|
1277
1291
|
- **Email safety** - Use `create-draft` to review emails before sending.
|
|
@@ -1285,7 +1299,7 @@ The entrypoint is written as:
|
|
|
1285
1299
|
| macOS only | Apple Mail and AppleScript are macOS-specific |
|
|
1286
1300
|
| MCP `send-email` is plain-text | The `send-email` tool sends plain text (reading HTML content is supported). To send HTML, use the bundled `apple-mail-send` CLI with `--html-body-file` (sends `multipart/alternative` via SMTP) |
|
|
1287
1301
|
| Attachments require absolute paths | File attachments must use full absolute paths (e.g., `/Users/me/file.pdf`) |
|
|
1288
|
-
|
|
|
1302
|
+
| Smart mailboxes need Mail quit | Smart mailboxes are supported (see [Smart Mailbox Operations](#smart-mailbox-operations-intelligente-postfächer)), but `create-`/`delete-smart-mailbox` edit `SyncedSmartMailboxes.plist` directly — a running Mail may not show a new one until relaunched, and can overwrite plist edits it didn't make. Quit Mail first. Reading them needs Full Disk Access for the Node runtime |
|
|
1289
1303
|
| Very large mailboxes not searchable *via AppleScript* | Apple Mail's AppleScript bridge times out on mailboxes with tens of thousands of messages, so unscoped `search-messages` skips mailboxes above `APPLE_MAIL_MAX_SEARCH_MAILBOX` (default 5000) and reports them as a partial result. Scope with `mailbox` + a date window — or configure the [IMAP backend](#imap-backend--opt-in), which searches these server-side in well under a second. ([#24](https://github.com/sweetrb/apple-mail-mcp/issues/24)) |
|
|
1290
1304
|
| Can't delete/rename server-side mailboxes or mutate drafts *via AppleScript* | Mail.app's AppleScript bridge can only `delete`/`rename` **local "On My Mac"** mailboxes and cannot delete/move drafts — it throws `AppleEvent handler failed` for IMAP/Gmail/Workspace/iCloud/Exchange mailboxes (the GUI can do it). Without IMAP configured, `delete-mailbox`/`rename-mailbox`/`delete-message`/`move-message` return a clear "do it in Mail.app directly" error instead of a generic failure. With the [IMAP backend](#imap-backend--opt-in) configured for the account, these operations run via IMAP and succeed. ([#42](https://github.com/sweetrb/apple-mail-mcp/issues/42)) |
|
|
1291
1305
|
| Message ID format | Message IDs must be numeric (AppleScript ids) or `imap:…` tokens from the IMAP read path (validated by schema) |
|
|
@@ -1385,14 +1399,16 @@ The `\\\\` in JSON becomes `\\` in the actual string, which represents a single
|
|
|
1385
1399
|
|
|
1386
1400
|
## Development
|
|
1387
1401
|
|
|
1402
|
+
This repo is **pnpm-only** — `package.json`'s `preinstall` guard hard-fails an `npm install`, because npm resolves off-lockfile and the committed bundle would then mismatch CI.
|
|
1403
|
+
|
|
1388
1404
|
```bash
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1405
|
+
corepack enable && pnpm install --frozen-lockfile # Install dependencies
|
|
1406
|
+
pnpm run build # Typecheck, then bundle src/index.ts + src/cli.ts into build/ (esbuild)
|
|
1407
|
+
pnpm test # Run unit tests
|
|
1408
|
+
pnpm run test:integration # Run integration tests (requires Mail.app)
|
|
1409
|
+
pnpm run test:all # Run all tests (unit + integration)
|
|
1410
|
+
pnpm run lint # Check code style
|
|
1411
|
+
pnpm run format # Format code
|
|
1396
1412
|
```
|
|
1397
1413
|
|
|
1398
1414
|
---
|
package/build/index.js
CHANGED
|
@@ -3110,9 +3110,9 @@ var require_data = __commonJS({
|
|
|
3110
3110
|
}
|
|
3111
3111
|
});
|
|
3112
3112
|
|
|
3113
|
-
// node_modules/.pnpm/fast-uri@3.1.
|
|
3113
|
+
// node_modules/.pnpm/fast-uri@3.1.5/node_modules/fast-uri/lib/utils.js
|
|
3114
3114
|
var require_utils = __commonJS({
|
|
3115
|
-
"node_modules/.pnpm/fast-uri@3.1.
|
|
3115
|
+
"node_modules/.pnpm/fast-uri@3.1.5/node_modules/fast-uri/lib/utils.js"(exports, module) {
|
|
3116
3116
|
"use strict";
|
|
3117
3117
|
var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
|
|
3118
3118
|
var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
|
|
@@ -3423,9 +3423,9 @@ var require_utils = __commonJS({
|
|
|
3423
3423
|
}
|
|
3424
3424
|
});
|
|
3425
3425
|
|
|
3426
|
-
// node_modules/.pnpm/fast-uri@3.1.
|
|
3426
|
+
// node_modules/.pnpm/fast-uri@3.1.5/node_modules/fast-uri/lib/schemes.js
|
|
3427
3427
|
var require_schemes = __commonJS({
|
|
3428
|
-
"node_modules/.pnpm/fast-uri@3.1.
|
|
3428
|
+
"node_modules/.pnpm/fast-uri@3.1.5/node_modules/fast-uri/lib/schemes.js"(exports, module) {
|
|
3429
3429
|
"use strict";
|
|
3430
3430
|
var { isUUID } = require_utils();
|
|
3431
3431
|
var URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu;
|
|
@@ -3633,9 +3633,9 @@ var require_schemes = __commonJS({
|
|
|
3633
3633
|
}
|
|
3634
3634
|
});
|
|
3635
3635
|
|
|
3636
|
-
// node_modules/.pnpm/fast-uri@3.1.
|
|
3636
|
+
// node_modules/.pnpm/fast-uri@3.1.5/node_modules/fast-uri/index.js
|
|
3637
3637
|
var require_fast_uri = __commonJS({
|
|
3638
|
-
"node_modules/.pnpm/fast-uri@3.1.
|
|
3638
|
+
"node_modules/.pnpm/fast-uri@3.1.5/node_modules/fast-uri/index.js"(exports, module) {
|
|
3639
3639
|
"use strict";
|
|
3640
3640
|
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
|
|
3641
3641
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
@@ -3651,7 +3651,12 @@ var require_fast_uri = __commonJS({
|
|
|
3651
3651
|
}
|
|
3652
3652
|
function resolve2(baseURI, relativeURI, options) {
|
|
3653
3653
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3654
|
-
const
|
|
3654
|
+
const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
|
|
3655
|
+
const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
|
|
3656
|
+
if (baseMalformed || relativeMalformed) {
|
|
3657
|
+
throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
|
|
3658
|
+
}
|
|
3659
|
+
const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
|
|
3655
3660
|
schemelessOptions.skipEscape = true;
|
|
3656
3661
|
return serialize(resolved, schemelessOptions);
|
|
3657
3662
|
}
|
|
@@ -3777,6 +3782,7 @@ var require_fast_uri = __commonJS({
|
|
|
3777
3782
|
}
|
|
3778
3783
|
var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
|
|
3779
3784
|
var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
|
|
3785
|
+
var AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
|
|
3780
3786
|
function getParseError(parsed, matches) {
|
|
3781
3787
|
if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
|
|
3782
3788
|
return 'URI path must start with "/" when authority is present.';
|
|
@@ -3811,6 +3817,20 @@ var require_fast_uri = __commonJS({
|
|
|
3811
3817
|
parsed.error = "URI authority must not contain a literal backslash.";
|
|
3812
3818
|
malformedAuthorityOrPort = true;
|
|
3813
3819
|
}
|
|
3820
|
+
const introducerMatch = uri.match(AUTHORITY_INTRODUCER_REGION);
|
|
3821
|
+
if (introducerMatch !== null) {
|
|
3822
|
+
const region = introducerMatch[1];
|
|
3823
|
+
const normalizedRegion = region.replace(/[\t\n\r]/g, "");
|
|
3824
|
+
if (normalizedRegion.length >= 2) {
|
|
3825
|
+
if (normalizedRegion.slice(0, 2) !== "//") {
|
|
3826
|
+
parsed.error = parsed.error || "URI authority must not contain a literal backslash.";
|
|
3827
|
+
malformedAuthorityOrPort = true;
|
|
3828
|
+
} else if (region.length !== normalizedRegion.length) {
|
|
3829
|
+
parsed.error = parsed.error || "URI authority introducer must not contain whitespace.";
|
|
3830
|
+
malformedAuthorityOrPort = true;
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3814
3834
|
const matches = uri.match(URI_PARSE);
|
|
3815
3835
|
if (matches) {
|
|
3816
3836
|
parsed.scheme = matches[1];
|
|
@@ -76059,6 +76079,10 @@ function executeAppleScript(script, options = {}) {
|
|
|
76059
76079
|
return lastError;
|
|
76060
76080
|
}
|
|
76061
76081
|
|
|
76082
|
+
// src/utils/docsUrls.ts
|
|
76083
|
+
var SETUP_GUIDE_URL = "https://github.com/sweetrb/apple-mail-mcp/blob/main/docs/IMAP-SETUP.md";
|
|
76084
|
+
var SETUP_HINT = `Setup guide: ${SETUP_GUIDE_URL} \u2014 run the "doctor" tool to check your setup.`;
|
|
76085
|
+
|
|
76062
76086
|
// src/utils/mimeParse.ts
|
|
76063
76087
|
function extractBoundary(source) {
|
|
76064
76088
|
const match = source.match(/boundary="?([^";\s\r\n]+)"?/i);
|
|
@@ -76954,7 +76978,7 @@ var AppleMailManager = class {
|
|
|
76954
76978
|
serverSideCreateGuard(account, op) {
|
|
76955
76979
|
if (this.isServerSideAccount(account) === true) {
|
|
76956
76980
|
const verb = op === "rename" ? "rename" : "create";
|
|
76957
|
-
return `Account "${account}" stores its mailboxes on the server (IMAP / iCloud / Exchange), and Mail.app cannot ${verb} server-side mailboxes via AppleScript \u2014 a ${verb} would ${op === "rename" ? "leave a half-created orphan" : "orphan a mailbox that can never be removed"}. Configure IMAP for this account (APPLE_MAIL_MCP_IMAP_*) so mailbox create/delete/rename route through the server, or manage the folder in Mail.app directly
|
|
76981
|
+
return `Account "${account}" stores its mailboxes on the server (IMAP / iCloud / Exchange), and Mail.app cannot ${verb} server-side mailboxes via AppleScript \u2014 a ${verb} would ${op === "rename" ? "leave a half-created orphan" : "orphan a mailbox that can never be removed"}. Configure IMAP for this account (APPLE_MAIL_MCP_IMAP_*) so mailbox create/delete/rename route through the server, or manage the folder in Mail.app directly. ${SETUP_HINT}`;
|
|
76958
76982
|
}
|
|
76959
76983
|
return null;
|
|
76960
76984
|
}
|
|
@@ -79719,12 +79743,6 @@ var import_nodemailer = __toESM(require_nodemailer(), 1);
|
|
|
79719
79743
|
import { execFileSync } from "child_process";
|
|
79720
79744
|
import { isAbsolute as isAbsolute2 } from "path";
|
|
79721
79745
|
import { existsSync as existsSync4 } from "fs";
|
|
79722
|
-
|
|
79723
|
-
// src/utils/docsUrls.ts
|
|
79724
|
-
var SETUP_GUIDE_URL = "https://github.com/sweetrb/apple-mail-mcp/blob/main/docs/IMAP-SETUP.md";
|
|
79725
|
-
var SETUP_HINT = `Setup guide: ${SETUP_GUIDE_URL} \u2014 run the "doctor" tool to check your setup.`;
|
|
79726
|
-
|
|
79727
|
-
// src/services/smtpMailer.ts
|
|
79728
79746
|
var SMTP_ENV = {
|
|
79729
79747
|
host: "APPLE_MAIL_MCP_SMTP_HOST",
|
|
79730
79748
|
port: "APPLE_MAIL_MCP_SMTP_PORT",
|
|
@@ -80124,7 +80142,7 @@ function specToConfig(spec) {
|
|
|
80124
80142
|
}
|
|
80125
80143
|
if (!pass) {
|
|
80126
80144
|
throw new Error(
|
|
80127
|
-
`No IMAP password for account "${spec.accountLabel}". Set a password or a Keychain service/account
|
|
80145
|
+
`No IMAP password for account "${spec.accountLabel}". Set a password or a Keychain service/account. ${SETUP_HINT}`
|
|
80128
80146
|
);
|
|
80129
80147
|
}
|
|
80130
80148
|
return {
|
|
@@ -83232,7 +83250,7 @@ server.registerTool(
|
|
|
83232
83250
|
withErrorHandling(({ query }) => {
|
|
83233
83251
|
const contacts = mailManager.searchContacts(query);
|
|
83234
83252
|
const structured = {
|
|
83235
|
-
contacts: contacts.map((c) => ({ name: c.name, emails: c.emails })),
|
|
83253
|
+
contacts: contacts.map((c) => ({ name: c.name, emails: c.emails, phones: c.phones })),
|
|
83236
83254
|
count: contacts.length
|
|
83237
83255
|
};
|
|
83238
83256
|
if (contacts.length === 0) {
|
|
@@ -83240,7 +83258,8 @@ server.registerTool(
|
|
|
83240
83258
|
}
|
|
83241
83259
|
const contactList = contacts.map((c) => {
|
|
83242
83260
|
const emails = c.emails.length > 0 ? c.emails.join(", ") : "no email";
|
|
83243
|
-
|
|
83261
|
+
const phones = c.phones.length > 0 ? `; ${c.phones.join(", ")}` : "";
|
|
83262
|
+
return ` - ${c.name} (${emails}${phones})`;
|
|
83244
83263
|
}).join("\n");
|
|
83245
83264
|
return successResponse(`Found ${contacts.length} contact(s):
|
|
83246
83265
|
${contactList}`, structured);
|
package/docs/IMAP-SETUP.md
CHANGED
|
@@ -160,9 +160,22 @@ non-secret config goes here — passwords stay in the Keychain.**
|
|
|
160
160
|
### `APPLE_MAIL_MCP_IMAP_ACCOUNT` and routing
|
|
161
161
|
|
|
162
162
|
`APPLE_MAIL_MCP_IMAP_ACCOUNT` should equal the **Mail.app account name** (what
|
|
163
|
-
`list-accounts` shows)
|
|
164
|
-
argument
|
|
165
|
-
|
|
163
|
+
`list-accounts` shows), because that is how an **explicitly named** `account`
|
|
164
|
+
argument is matched to an IMAP config.
|
|
165
|
+
|
|
166
|
+
**Since v2.6.0 you do not need to pass `account` at all for reads.** When any
|
|
167
|
+
IMAP account is configured, the read tools (`search-messages`, `get-thread`,
|
|
168
|
+
`list-messages`, `list-mailboxes`, `get-unread-count`, `get-mail-stats`) prefer
|
|
169
|
+
IMAP automatically:
|
|
170
|
+
|
|
171
|
+
- explicit IMAP `account` → that account over IMAP;
|
|
172
|
+
- explicit non-IMAP `account` → AppleScript;
|
|
173
|
+
- **no `account` → fan out over every configured IMAP account**, with AppleScript
|
|
174
|
+
used only for the accounts no IMAP config covers.
|
|
175
|
+
|
|
176
|
+
The name still matters for targeting a specific account and for the
|
|
177
|
+
mailbox-write ops (`create`/`delete`/`rename-mailbox`), which route to IMAP only
|
|
178
|
+
for an explicitly named IMAP account.
|
|
166
179
|
|
|
167
180
|
### Multiple accounts
|
|
168
181
|
|
|
@@ -285,9 +298,17 @@ and a server started there can't read passwords either. Options:
|
|
|
285
298
|
restrict the config file's permissions to the service account.
|
|
286
299
|
|
|
287
300
|
**Calls aren't routing to IMAP even though `doctor` shows connected.**
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
301
|
+
For the six **read** tools this should not happen since v2.6.0 — they prefer IMAP
|
|
302
|
+
as soon as any account is configured, with no `account` argument needed. If reads
|
|
303
|
+
still look like AppleScript (slow, or partial-coverage warnings), the usual cause
|
|
304
|
+
is that the server never saw your settings at all: a host that strips the `env`
|
|
305
|
+
block (use the `config.json` method), or a Keychain service/account mismatch so
|
|
306
|
+
the password lookup fails. Run `doctor` — it reports each account separately.
|
|
307
|
+
|
|
308
|
+
For **targeting one specific account**, and for the mailbox-write ops
|
|
309
|
+
(`create`/`delete`/`rename-mailbox`), the `account` argument must match the
|
|
310
|
+
configured account's name/login: set `APPLE_MAIL_MCP_IMAP_ACCOUNT` to the exact
|
|
311
|
+
Mail.app account name from `list-accounts` and pass that same `account`.
|
|
291
312
|
|
|
292
313
|
**A disabled Mail account.** IMAP connects to the server directly, so it works
|
|
293
314
|
even if the account is disabled in Mail.app — useful for reaching an account the
|
package/package.json
CHANGED