apple-mail-mcp 2.10.25 → 2.10.26

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 |
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
  /**
@@ -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,
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.26",
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",