apple-mail-mcp 2.10.25 → 2.10.27

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
@@ -824,6 +824,11 @@ List attachments on a message.
824
824
 
825
825
  Save a message attachment to disk.
826
826
 
827
+ The destination must not already exist: `save-attachment` fails closed instead
828
+ of overwriting an existing file. AppleScript and MIME fallback paths stage the
829
+ bytes privately, commit with an exclusive create, and leave the saved file
830
+ owner-readable/writable (`0600`).
831
+
827
832
  | Parameter | Type | Required | Description |
828
833
  |-----------|------|----------|-------------|
829
834
  | `id` | string | Yes | Message ID |
@@ -1080,10 +1085,15 @@ Create a Mail rule with one or more conditions and actions.
1080
1085
  | `conditions` | object[] | Yes | One or more `{field, operator, value}` (see below) |
1081
1086
  | `actions` | object | Yes | At least one of `markRead`, `markFlagged`, `delete`, `moveTo` |
1082
1087
  | `matchAll` | boolean | No | `true` (default) = all conditions must match; `false` = any |
1083
- | `enabled` | boolean | No | Whether the rule is enabled on creation (default `true`) |
1088
+ | `enabled` | boolean | No | Whether the rule is enabled on creation (default `false`) |
1084
1089
 
1085
1090
  Each condition is `{ field, operator, value }` where `field` is one of `from`, `to`, `cc`, `subject`, `content` and `operator` is one of `contains`, `notContains`, `equals`, `beginsWith`, `endsWith`. Actions: `markRead` / `markFlagged` / `delete` (booleans), `moveTo` (mailbox name) with optional `moveToAccount`.
1086
1091
 
1092
+ New rules are created **disabled by default**, including rules that delete or move
1093
+ messages. Review the conditions and actions with `list-rules` and in Mail.app,
1094
+ then call `enable-rule` explicitly when the rule is approved. Set
1095
+ `enabled: true` only when immediate activation is deliberate.
1096
+
1087
1097
  **Example:**
1088
1098
  ```json
1089
1099
  {
package/build/index.js CHANGED
@@ -77516,6 +77516,8 @@ var StdioServerTransport = class {
77516
77516
  // src/services/appleMailManager.ts
77517
77517
  import { spawnSync as spawnSync2 } from "child_process";
77518
77518
  import {
77519
+ constants as fsConstants,
77520
+ chmodSync,
77519
77521
  existsSync as existsSync3,
77520
77522
  writeFileSync as writeFileSync3,
77521
77523
  readFileSync as readFileSync2,
@@ -78375,8 +78377,11 @@ function resolveAttachmentSaveTarget(savePath, attachmentName) {
78375
78377
  if (!isPathWithinAllowedRoots(savedPath)) {
78376
78378
  throw new Error(`Output path "${savedPath}" is outside allowed directories`);
78377
78379
  }
78378
- if (existsSync3(savedPath) && lstatSync(savedPath).isSymbolicLink()) {
78379
- throw new Error(`Refusing to overwrite symbolic link "${savedPath}"`);
78380
+ if (existsSync3(savedPath)) {
78381
+ if (lstatSync(savedPath).isSymbolicLink()) {
78382
+ throw new Error(`Refusing to overwrite symbolic link "${savedPath}"`);
78383
+ }
78384
+ throw new Error(`Refusing to overwrite existing file "${savedPath}"`);
78380
78385
  }
78381
78386
  return { saveDirectory, savedPath };
78382
78387
  }
@@ -81162,7 +81167,21 @@ ${this.errorEmit(" ")}
81162
81167
  return false;
81163
81168
  }
81164
81169
  const safeName = escapeForAppleScript(attachmentName);
81165
- const safePath = escapeForAppleScript(target.saveDirectory);
81170
+ let temporaryDirectory;
81171
+ try {
81172
+ temporaryDirectory = mkdtempSync2(join4(target.saveDirectory, ".apple-mail-mcp-"));
81173
+ } catch (error2) {
81174
+ console.error(`Failed to create attachment staging directory: ${error2}`);
81175
+ return false;
81176
+ }
81177
+ const temporaryPath = join4(temporaryDirectory, "attachment");
81178
+ const safeTemporaryPath = escapeForAppleScript(temporaryPath);
81179
+ const cleanupTemporaryDirectory = () => {
81180
+ try {
81181
+ rmSync2(temporaryDirectory, { recursive: true, force: true });
81182
+ } catch {
81183
+ }
81184
+ };
81166
81185
  const numericId = Number(id);
81167
81186
  const script = buildAppLevelScript(`
81168
81187
  try
@@ -81174,7 +81193,7 @@ ${this.errorEmit(" ")}
81174
81193
  set msg to item 1 of matchingMsgs
81175
81194
  repeat with att in mail attachments of msg
81176
81195
  if name of att is "${safeName}" then
81177
- set savePath to POSIX file "${safePath}/${safeName}"
81196
+ set savePath to POSIX file "${safeTemporaryPath}"
81178
81197
  save att in savePath
81179
81198
  return "ok"
81180
81199
  end if
@@ -81191,8 +81210,18 @@ ${this.errorEmit(" ")}
81191
81210
  `);
81192
81211
  const result = executeAppleScript(script, { timeoutMs: 6e4 });
81193
81212
  if (result.success && result.output === "ok") {
81194
- return true;
81213
+ try {
81214
+ copyFileSync(temporaryPath, target.savedPath, fsConstants.COPYFILE_EXCL);
81215
+ chmodSync(target.savedPath, 384);
81216
+ cleanupTemporaryDirectory();
81217
+ return true;
81218
+ } catch (err) {
81219
+ cleanupTemporaryDirectory();
81220
+ console.error(`Failed to commit attachment to disk: ${err}`);
81221
+ return false;
81222
+ }
81195
81223
  }
81224
+ cleanupTemporaryDirectory();
81196
81225
  const rawSource = this.getRawSource(id);
81197
81226
  if (!rawSource) {
81198
81227
  console.error(`Failed to save attachment: could not retrieve message source`);
@@ -81203,12 +81232,24 @@ ${this.errorEmit(" ")}
81203
81232
  console.error(`Failed to save attachment: "${attachmentName}" not found in MIME source`);
81204
81233
  return false;
81205
81234
  }
81235
+ let mimeTemporaryDirectory;
81206
81236
  try {
81207
- writeFileSync3(target.savedPath, attachment.data);
81237
+ mimeTemporaryDirectory = mkdtempSync2(join4(target.saveDirectory, ".apple-mail-mcp-"));
81238
+ const mimeTemporaryPath = join4(mimeTemporaryDirectory, "attachment");
81239
+ writeFileSync3(mimeTemporaryPath, attachment.data, { flag: "wx", mode: 384 });
81240
+ copyFileSync(mimeTemporaryPath, target.savedPath, fsConstants.COPYFILE_EXCL);
81241
+ chmodSync(target.savedPath, 384);
81208
81242
  return true;
81209
81243
  } catch (err) {
81210
81244
  console.error(`Failed to write attachment to disk: ${err}`);
81211
81245
  return false;
81246
+ } finally {
81247
+ if (mimeTemporaryDirectory) {
81248
+ try {
81249
+ rmSync2(mimeTemporaryDirectory, { recursive: true, force: true });
81250
+ } catch {
81251
+ }
81252
+ }
81212
81253
  }
81213
81254
  }
81214
81255
  /**
@@ -82027,7 +82068,7 @@ end tell`;
82027
82068
  if (!actionStmts.length) {
82028
82069
  return { success: false, error: "A rule needs at least one action." };
82029
82070
  }
82030
- const enabled = opts.enabled !== false;
82071
+ const enabled = opts.enabled === true;
82031
82072
  const matchAll = opts.matchAll !== false;
82032
82073
  const script = buildAppLevelScript(`
82033
82074
  try
@@ -85676,7 +85717,7 @@ registerTool(
85676
85717
  if (!r.success || !r.base64) {
85677
85718
  return errorResponse(r.error || `Failed to fetch attachment "${attachmentName}"`);
85678
85719
  }
85679
- writeFileSync4(target.savedPath, Buffer.from(r.base64, "base64"));
85720
+ writeFileSync4(target.savedPath, Buffer.from(r.base64, "base64"), { flag: "wx", mode: 384 });
85680
85721
  return successResponse(`Attachment "${attachmentName}" saved to ${savePath}`, {
85681
85722
  ok: true,
85682
85723
  attachmentName,
@@ -86208,7 +86249,7 @@ registerTool(
86208
86249
  "At least one action is required (markRead, markFlagged, delete, or moveTo)"
86209
86250
  ),
86210
86251
  matchAll: external_exports.boolean().default(true),
86211
- enabled: external_exports.boolean().default(true)
86252
+ enabled: external_exports.boolean().default(false).describe("Enable immediately; defaults to false so the rule must be reviewed first")
86212
86253
  },
86213
86254
  outputSchema: {
86214
86255
  name: external_exports.string().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-mail-mcp",
3
- "version": "2.10.25",
3
+ "version": "2.10.27",
4
4
  "description": "MCP server for Apple Mail - read, search, send, and manage emails via Claude and other AI assistants",
5
5
  "type": "module",
6
6
  "main": "build/index.js",