apple-mail-mcp 1.5.3 → 1.5.5
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
CHANGED
|
@@ -700,6 +700,22 @@ If installed from source, use this configuration:
|
|
|
700
700
|
}
|
|
701
701
|
```
|
|
702
702
|
|
|
703
|
+
#### Running from a clone in Claude Code (project-scope `.mcp.json`)
|
|
704
|
+
|
|
705
|
+
This repo ships a `.mcp.json` at its root so that, when you run `claude` from inside a clone, the server is registered automatically as a **project-scope** server — no manual config needed. After `npm run build`, just launch Claude Code from the repo directory and approve the server when prompted.
|
|
706
|
+
|
|
707
|
+
The entrypoint is written as:
|
|
708
|
+
|
|
709
|
+
```json
|
|
710
|
+
"args": ["${CLAUDE_PROJECT_DIR:-.}/build/index.js"]
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
`CLAUDE_PROJECT_DIR` is the variable Claude Code injects into a project/user-scoped server's environment, and it resolves to the repo root. **You must launch `claude` from inside the repo** for this to work — the bare `.` fallback is only a last resort and is *not* reliable, because it resolves against the launching process's working directory, not the repo.
|
|
714
|
+
|
|
715
|
+
> **Why not `${CLAUDE_PLUGIN_ROOT}`?** `CLAUDE_PLUGIN_ROOT` is set **only** for marketplace plugin installs, never for a project-scope clone, so it can't drive the clone workflow. Conversely, a plugin install can't use `CLAUDE_PROJECT_DIR` (in a plugin, that points at the *user's* project, not the plugin's own directory). Claude Code does **not** support nested defaults like `${CLAUDE_PLUGIN_ROOT:-${CLAUDE_PROJECT_DIR:-.}}`, so a single entrypoint string cannot serve both contexts. The two distribution paths are therefore decoupled: the **plugin** carries its own MCP config in `.claude-plugin/plugin.json` (using `${CLAUDE_PLUGIN_ROOT}`), while the root `.mcp.json` is dedicated to the **clone** workflow (using `${CLAUDE_PROJECT_DIR:-.}`). Because `plugin.json` declares its own `mcpServers`, the plugin does not also auto-load the root `.mcp.json`, so there is no double-registration.
|
|
716
|
+
|
|
717
|
+
> **Heads-up on scope precedence:** project-scope (`.mcp.json`) outranks user-scope. If you *also* have an `apple-mail` entry registered at user scope (e.g. an absolute path in `~/.claude.json`), the project-scope entry wins and the user-scope one is ignored entirely. Pick one — for local development on this repo, the project-scope `.mcp.json` is the intended source. To pin a specific local build instead, register it at **local** scope (`claude mcp add apple-mail -s local -- node /abs/path/build/index.js`), which outranks project scope.
|
|
718
|
+
|
|
703
719
|
---
|
|
704
720
|
|
|
705
721
|
## Security and Privacy
|
|
@@ -788,6 +804,12 @@ The `\\\\` in JSON becomes `\\` in the actual string, which represents a single
|
|
|
788
804
|
- Verify Mail.app can send emails manually
|
|
789
805
|
- Check if the account is configured correctly in Mail.app
|
|
790
806
|
|
|
807
|
+
### `apple-mail` server fails to connect when run from a clone
|
|
808
|
+
- The root `.mcp.json` resolves its entrypoint via `${CLAUDE_PROJECT_DIR:-.}/build/index.js`. **Launch `claude` from inside the repo directory** — `CLAUDE_PROJECT_DIR` only resolves to the repo root in that case; the bare `.` fallback uses the launching shell's working directory and will point at the wrong place otherwise.
|
|
809
|
+
- Run `npm run build` first — the server is `build/index.js`, which doesn't exist until you build.
|
|
810
|
+
- Run `claude mcp list` to check status. If you see a *conflicting scopes* warning for `apple-mail`, you have it registered at more than one scope; project-scope wins. See [Running from a clone](#running-from-a-clone-in-claude-code-project-scope-mcpjson) for how scope precedence resolves.
|
|
811
|
+
- If `claude mcp get apple-mail` shows **⏸ Pending approval**, approve the project-scope server (Claude Code prompts on startup, or run it again after approving).
|
|
812
|
+
|
|
791
813
|
---
|
|
792
814
|
|
|
793
815
|
## Development
|
|
@@ -13,6 +13,23 @@
|
|
|
13
13
|
* @module services/appleMailManager
|
|
14
14
|
*/
|
|
15
15
|
import type { Message, MessageContent, Mailbox, Account, Attachment, HealthCheckResult, MailStats, BatchOperationResult, SyncStatus, RecentlyReceivedStats, MailRule, Contact, EmailTemplate, SerialEmailRecipient, SerialEmailResult } from "../types.js";
|
|
16
|
+
/**
|
|
17
|
+
* Emits AppleScript that builds a date into the variable `varName` from numeric
|
|
18
|
+
* components.
|
|
19
|
+
*
|
|
20
|
+
* This is locale-independent, unlike `date "May 30, 2026"` string coercion,
|
|
21
|
+
* which AppleScript parses using the system locale. On a non-English locale
|
|
22
|
+
* (e.g. pt_PT) the English month name throws "Invalid date and time (-30720)";
|
|
23
|
+
* because the comparison happens inside the per-message `try` in searchMessages,
|
|
24
|
+
* that error is swallowed and every message is skipped, so the search returns
|
|
25
|
+
* zero results even when matches exist. See issue #15.
|
|
26
|
+
*
|
|
27
|
+
* `day` is reset to 1 before assigning month/year so an existing day-of-month
|
|
28
|
+
* (e.g. 31) cannot overflow into the next month when the month is changed.
|
|
29
|
+
*
|
|
30
|
+
* Exported for unit testing.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildAppleScriptDate(varName: string, d: Date): string;
|
|
16
33
|
/**
|
|
17
34
|
* Manager class for Apple Mail operations.
|
|
18
35
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appleMailManager.d.ts","sourceRoot":"","sources":["../../src/services/appleMailManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,OAAO,EACP,OAAO,EACP,UAAU,EACV,iBAAiB,EACjB,SAAS,EAET,oBAAoB,EACpB,UAAU,EACV,qBAAqB,EACrB,QAAQ,EACR,OAAO,EACP,aAAa,EACb,oBAAoB,EACpB,iBAAiB,EAClB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"appleMailManager.d.ts","sourceRoot":"","sources":["../../src/services/appleMailManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -89,6 +89,34 @@ function parseAppleScriptDate(dateStr) {
|
|
|
89
89
|
const parsed = new Date(normalized);
|
|
90
90
|
return isNaN(parsed.getTime()) ? new Date() : parsed;
|
|
91
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Emits AppleScript that builds a date into the variable `varName` from numeric
|
|
94
|
+
* components.
|
|
95
|
+
*
|
|
96
|
+
* This is locale-independent, unlike `date "May 30, 2026"` string coercion,
|
|
97
|
+
* which AppleScript parses using the system locale. On a non-English locale
|
|
98
|
+
* (e.g. pt_PT) the English month name throws "Invalid date and time (-30720)";
|
|
99
|
+
* because the comparison happens inside the per-message `try` in searchMessages,
|
|
100
|
+
* that error is swallowed and every message is skipped, so the search returns
|
|
101
|
+
* zero results even when matches exist. See issue #15.
|
|
102
|
+
*
|
|
103
|
+
* `day` is reset to 1 before assigning month/year so an existing day-of-month
|
|
104
|
+
* (e.g. 31) cannot overflow into the next month when the month is changed.
|
|
105
|
+
*
|
|
106
|
+
* Exported for unit testing.
|
|
107
|
+
*/
|
|
108
|
+
export function buildAppleScriptDate(varName, d) {
|
|
109
|
+
return [
|
|
110
|
+
`set ${varName} to current date`,
|
|
111
|
+
`set day of ${varName} to 1`,
|
|
112
|
+
`set year of ${varName} to ${d.getFullYear()}`,
|
|
113
|
+
`set month of ${varName} to ${d.getMonth() + 1}`,
|
|
114
|
+
`set day of ${varName} to ${d.getDate()}`,
|
|
115
|
+
`set hours of ${varName} to ${d.getHours()}`,
|
|
116
|
+
`set minutes of ${varName} to ${d.getMinutes()}`,
|
|
117
|
+
`set seconds of ${varName} to ${d.getSeconds()}`,
|
|
118
|
+
].join("\n ");
|
|
119
|
+
}
|
|
92
120
|
/**
|
|
93
121
|
* Builds an AppleScript command scoped to a specific account.
|
|
94
122
|
*/
|
|
@@ -326,18 +354,28 @@ export class AppleMailManager {
|
|
|
326
354
|
// are additional AND filters. Date filtering stays post-fetch below — `whose`
|
|
327
355
|
// date comparisons are unreliable in Mail.app AppleScript. See buildSearchCondition.
|
|
328
356
|
const searchCondition = buildSearchCondition({ query, from, subject, isRead, isFlagged });
|
|
329
|
-
// Build date
|
|
330
|
-
//
|
|
331
|
-
//
|
|
332
|
-
//
|
|
357
|
+
// Build the date-bound comparison. The comparison dates are constructed in
|
|
358
|
+
// AppleScript from numeric components (see buildAppleScriptDate) and compared
|
|
359
|
+
// against `msgDate` (set per-message below) rather than coerced from a
|
|
360
|
+
// locale-formatted string — `date "May 30, 2026"` throws on non-English system
|
|
361
|
+
// locales, and that swallowed error silently zeroes out results. See issue #15.
|
|
362
|
+
// dateFrom/dateTo are already validated by DATE_FILTER_SCHEMA as parseable dates.
|
|
363
|
+
let dateSetup = "";
|
|
333
364
|
let dateFilter = "";
|
|
334
365
|
if (dateFrom || dateTo) {
|
|
335
366
|
const dateChecks = [];
|
|
336
367
|
if (dateFrom) {
|
|
337
|
-
|
|
368
|
+
dateSetup += buildAppleScriptDate("_dateFrom", new Date(dateFrom)) + "\n ";
|
|
369
|
+
dateChecks.push("msgDate >= _dateFrom");
|
|
338
370
|
}
|
|
339
371
|
if (dateTo) {
|
|
340
|
-
|
|
372
|
+
const to = new Date(dateTo);
|
|
373
|
+
// A date-only upper bound (no time component) is treated as end-of-day so
|
|
374
|
+
// messages received later that same day are still included.
|
|
375
|
+
if (!/\d:\d/.test(dateTo))
|
|
376
|
+
to.setHours(23, 59, 59, 0);
|
|
377
|
+
dateSetup += buildAppleScriptDate("_dateTo", to) + "\n ";
|
|
378
|
+
dateChecks.push("msgDate <= _dateTo");
|
|
341
379
|
}
|
|
342
380
|
dateFilter = dateChecks.join(" and ");
|
|
343
381
|
}
|
|
@@ -346,7 +384,7 @@ export class AppleMailManager {
|
|
|
346
384
|
// Search a specific mailbox
|
|
347
385
|
const targetMailbox = this.resolveMailbox(mailbox, targetAccount);
|
|
348
386
|
searchCommand = `
|
|
349
|
-
set outputText to ""
|
|
387
|
+
${dateSetup}set outputText to ""
|
|
350
388
|
set theMailbox to mailbox "${escapeForAppleScript(targetMailbox)}"
|
|
351
389
|
set allMessages to messages of theMailbox ${searchCondition}
|
|
352
390
|
set msgCount to 0
|
|
@@ -373,7 +411,7 @@ export class AppleMailManager {
|
|
|
373
411
|
else {
|
|
374
412
|
// Search ALL mailboxes — iterate every mailbox in the account, dedup by message ID
|
|
375
413
|
searchCommand = `
|
|
376
|
-
set outputText to ""
|
|
414
|
+
${dateSetup}set outputText to ""
|
|
377
415
|
set msgCount to 0
|
|
378
416
|
set seenIds to {}
|
|
379
417
|
repeat with mb in mailboxes
|