apple-tools-mcp 2.0.3 → 2.0.4

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
@@ -461,7 +461,11 @@ Two arguments are available on **every** write tool:
461
461
  | `mail_archive` | `message_id` or `file_path` | none |
462
462
  | `mail_trash` | `message_id` or `file_path` | **`confirm` required** |
463
463
 
464
- `body_format: "html"` sets Mail's `html content` and keeps a tag-stripped plain-text alternative in `content`; the default is plain text. HTML support is whatever Mail.app offers — if a Mail version rejects the property, the message still goes out as plain text.
464
+ `body_format: "html"` uses the same mailto compose path with a tag-stripped body. It does **not** set Mail's `html content` — that setter cite-wraps the whole message (`>` prefixes and `<blockquote type="cite">`, same iOS purple bar). Mail may still generate its own HTML alternative from the native compose. The default is plain text.
465
+
466
+ **Compose is not quoted.** `mail_send` / `mail_draft` (plain and html) create the outgoing message with Mail's `mailto` command (native compose) instead of setting AppleScript `content` or `html content` on `make new outgoing message`. On current Mail (Ventura+, FB11734014) those setters store the body as a citation: every plain-text line prefixed with `>`, plus a `multipart/alternative` HTML part wrapped in `<blockquote type="cite">`. Desktop Mail often hides the bar with inline styles; iOS Mail paints the whole body purple with a left quote bar — even when the subject is not `Re:`/`Fwd:` and there is no `In-Reply-To`. Reply and forward still quote the original, which is expected.
467
+
468
+ **Manual prove (after 2.0.4 is installed on the Mac host — post-publish, does not block this PR):** `mail_send` a short plain message and a short `body_format: "html"` message whose body looks like ordinary paragraphs (for example `<p>Quick note</p>`), neither with a `Re:`/`Fwd:` subject. Inspect each Sent `.emlx`: the text/plain part must not prefix every body line with `>`, and any HTML alternative must not wrap the whole body in `<blockquote type="cite">`.
465
469
 
466
470
  Emails are addressed by their RFC822 **Message-ID**. Pass `message_id`, or pass the `file_path` from `mail_search` / `mail_recent` and the server reads the Message-ID out of the `.emlx` headers for you. `mail_archive` moves the message to its account's Archive (or All Mail) mailbox; `mail_trash` moves it to that account's Trash.
467
471
 
package/lib/mailWrite.js CHANGED
@@ -148,11 +148,62 @@ export function probeMailAutomation() {
148
148
  };
149
149
  }
150
150
 
151
+ /**
152
+ * Percent-encode a mailto query value (RFC 6068).
153
+ *
154
+ * Newlines become %0D%0A so Mail keeps paragraphs. Characters that
155
+ * encodeURIComponent leaves unescaped (`!'()*`) are encoded too, because
156
+ * they can break the URL or the AppleScript string around it.
157
+ *
158
+ * Does not prefix lines with `>` — Mail's native mailto compose path
159
+ * already treats the decoded body as original text, not a citation.
160
+ */
161
+ export function encodeMailtoQueryValue(value) {
162
+ const text = value === undefined || value === null ? "" : String(value);
163
+ const normalized = text.replace(/\r\n|\r|\n/g, "\r\n");
164
+ return encodeURIComponent(normalized).replace(/[!'()*]/g, (ch) => {
165
+ return `%${ch.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}`;
166
+ });
167
+ }
168
+
169
+ /**
170
+ * Body text for Mail's mailto compose.
171
+ *
172
+ * HTML is tag-stripped so a plain-looking HTML body (`<p>…</p>`) becomes
173
+ * native compose text. Never prefixes lines with `>` and never emits
174
+ * `<blockquote>` — AppleScript `html content` is unused because Mail
175
+ * cite-wraps it (FB11734014).
176
+ */
177
+ export function composeMailtoBody(body, { html = false } = {}) {
178
+ if (html) return stripHtmlTags(body);
179
+ return body === undefined || body === null ? "" : String(body);
180
+ }
181
+
182
+ /**
183
+ * Build a mailto: URL for Mail's `mailto` command.
184
+ *
185
+ * Recipients stay off this URL and are added with `make new … recipient`
186
+ * so cc/bcc keep the proven AppleScript path and a To is not duplicated.
187
+ * Subject and body go here so Mail uses its native compose pipeline
188
+ * (FB11734014: setting AppleScript `content` wraps the whole body in a
189
+ * cite-blockquote — `>` prefixes on text/plain, `<blockquote type="cite">`
190
+ * on text/html — which iOS Mail paints as purple text with a left bar).
191
+ */
192
+ export function buildMailtoComposeUrl({ subject = "", body = "" } = {}) {
193
+ return `mailto:?subject=${encodeMailtoQueryValue(subject)}&body=${encodeMailtoQueryValue(body)}`;
194
+ }
195
+
151
196
  /**
152
197
  * Build the outgoing-message script shared by send and draft.
153
198
  *
154
- * Mail renders `html content` when it is set; `content` stays populated as
155
- * the plain-text alternative for clients that do not display HTML.
199
+ * `mail_send` / `mail_draft` (plain and html) must not set AppleScript
200
+ * `content` or `html content`. Those properties route through Mail's
201
+ * share-insert path and serialize as a citation. Mail's `mailto` command
202
+ * returns the outgoing message so we can still add recipients, hide the
203
+ * window, and send or save — without Accessibility / System Events, and
204
+ * without a quote-wrapped body. HTML format uses the same path with a
205
+ * tag-stripped mailto body; Mail may generate its own HTML alternative
206
+ * without wrapping the whole message in `<blockquote type="cite">`.
156
207
  */
157
208
  export function buildComposeScript({ to, cc, bcc, subject, body, send, html = false }) {
158
209
  const recipients = [
@@ -161,15 +212,17 @@ export function buildComposeScript({ to, cc, bcc, subject, body, send, html = fa
161
212
  recipientLines(bcc, "bcc recipient")
162
213
  ].filter((block) => block.length > 0).join("\n");
163
214
 
164
- const htmlLine = html
165
- ? ` try
166
- set html content of newMessage to ${asString(body)}
167
- end try\n`
168
- : "";
215
+ const mailtoUrl = buildMailtoComposeUrl({
216
+ subject,
217
+ body: composeMailtoBody(body, { html })
218
+ });
169
219
 
170
220
  return `tell application "Mail"
171
- set newMessage to make new outgoing message with properties {subject:${asString(subject)}, content:${asString(html ? stripHtmlTags(body) : body)}, visible:false}
172
- ${htmlLine} tell newMessage
221
+ set newMessage to mailto ${asString(mailtoUrl)}
222
+ try
223
+ set visible of newMessage to false
224
+ end try
225
+ tell newMessage
173
226
  ${recipients}
174
227
  end tell
175
228
  ${send ? "send newMessage" : "save newMessage"}
package/lib/writeTools.js CHANGED
@@ -57,7 +57,7 @@ export const WRITE_TOOL_DEFINITIONS = [
57
57
  bcc: { type: "array", items: { type: "string" }, description: "BCC email addresses" },
58
58
  subject: { type: "string", description: "Subject line (required)" },
59
59
  body: { type: "string", description: "Message body (required)" },
60
- body_format: { type: "string", enum: ["plain", "html"], description: "Send body as plain text (default) or HTML, where Mail supports it" },
60
+ body_format: { type: "string", enum: ["plain", "html"], description: "Plain text (default) or HTML. HTML is tag-stripped onto Mail's mailto compose; AppleScript html content is not set because it quote-wraps the Sent body" },
61
61
  ...CONFIRM_PROPS
62
62
  },
63
63
  required: ["to", "subject", "body"]
@@ -74,7 +74,7 @@ export const WRITE_TOOL_DEFINITIONS = [
74
74
  bcc: { type: "array", items: { type: "string" }, description: "BCC email addresses" },
75
75
  subject: { type: "string", description: "Subject line" },
76
76
  body: { type: "string", description: "Message body" },
77
- body_format: { type: "string", enum: ["plain", "html"], description: "Save body as plain text (default) or HTML, where Mail supports it" },
77
+ body_format: { type: "string", enum: ["plain", "html"], description: "Plain text (default) or HTML. HTML is tag-stripped onto Mail's mailto compose; AppleScript html content is not set because it quote-wraps the Sent body" },
78
78
  ...CONFIRM_PROPS
79
79
  },
80
80
  required: ["to"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-tools-mcp",
3
- "version": "2.0.3",
3
+ "version": "2.0.4",
4
4
  "description": "MCP server for semantic search and write actions across Apple Mail, Messages, Calendar, and Contacts",
5
5
  "type": "module",
6
6
  "main": "index.js",