apple-tools-mcp 2.0.2 → 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 +6 -2
- package/bin/apple-tools-indexer.js +15 -0
- package/bin/apple-tools-mcp.js +7 -0
- package/index.js +0 -0
- package/lib/mailWrite.js +62 -9
- package/lib/processMode.js +3 -2
- package/lib/writeTools.js +2 -2
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -317,7 +317,7 @@ The indexer does **not** post product Notification Center / `display notificatio
|
|
|
317
317
|
The bridge is created before the daemon touches the vector index, so writes stay available even when the index is missing, locked, or mid-rebuild. Confirm it after an upgrade with `ls -l ~/.apple-tools-mcp/writer.sock` (it should be a `srw-------` socket); the daemon removes it on shutdown.
|
|
318
318
|
|
|
319
319
|
**Entrypoint:** `node index.js --mode=indexer`
|
|
320
|
-
**Convenience bin:** `apple-tools-indexer` (
|
|
320
|
+
**Convenience bin:** `apple-tools-indexer` (`bin/apple-tools-indexer.js`; npm global install provides it)
|
|
321
321
|
**npm script (clone only):** `npm run indexer`
|
|
322
322
|
**One-shot rebuild:** `npm run build-index` (stop the indexer daemon first)
|
|
323
323
|
|
|
@@ -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"`
|
|
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
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* npm bin for the indexer daemon.
|
|
4
|
+
* Dedicated wrapper so npm 12 pack/publish keeps the CLI — `index.js` as a
|
|
5
|
+
* bin target is rewritten with "script name index.js was invalid and removed".
|
|
6
|
+
* Injects `--mode=indexer` so a realpath to this file still starts the daemon.
|
|
7
|
+
* `permissions` still wins (same contract as the apple-tools-indexer bin name).
|
|
8
|
+
*/
|
|
9
|
+
if (!process.argv.includes('--mode=indexer')) {
|
|
10
|
+
const modeIdx = process.argv.indexOf('--mode');
|
|
11
|
+
if (modeIdx === -1 || process.argv[modeIdx + 1] !== 'indexer') {
|
|
12
|
+
process.argv.splice(2, 0, '--mode=indexer');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
await import('../index.js');
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* npm bin for the MCP stdio server (and `permissions` subcommand).
|
|
4
|
+
* Dedicated wrapper so npm 12 pack/publish keeps the CLI — `index.js` as a
|
|
5
|
+
* bin target is rewritten with "script name index.js was invalid and removed".
|
|
6
|
+
*/
|
|
7
|
+
import '../index.js';
|
package/index.js
CHANGED
|
File without changes
|
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
|
-
*
|
|
155
|
-
*
|
|
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
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
|
172
|
-
|
|
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/processMode.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Process mode detection for apple-tools-mcp.
|
|
3
3
|
*
|
|
4
4
|
* Canonical indexer entrypoint: `node index.js --mode=indexer`
|
|
5
|
-
* Convenience bin: `apple-tools-indexer` (
|
|
5
|
+
* Convenience bin: `apple-tools-indexer` (`bin/apple-tools-indexer.js`;
|
|
6
|
+
* detected via argv[1] or `--mode=indexer`).
|
|
6
7
|
* Permissions CLI: `apple-tools-mcp permissions` / `--mode=permissions`.
|
|
7
8
|
* MCP stdio remains the default when neither is present.
|
|
8
9
|
*/
|
|
@@ -71,5 +72,5 @@ export function isIndexerMode(argv = process.argv) {
|
|
|
71
72
|
return true;
|
|
72
73
|
}
|
|
73
74
|
const entry = argv[1] ? path.basename(argv[1]) : "";
|
|
74
|
-
return entry === "apple-tools-indexer";
|
|
75
|
+
return entry === "apple-tools-indexer" || entry === "apple-tools-indexer.js";
|
|
75
76
|
}
|
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: "
|
|
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: "
|
|
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,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apple-tools-mcp",
|
|
3
|
-
"version": "2.0.
|
|
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",
|
|
7
7
|
"bin": {
|
|
8
|
-
"apple-tools-mcp": "
|
|
9
|
-
"apple-tools-indexer": "
|
|
8
|
+
"apple-tools-mcp": "bin/apple-tools-mcp.js",
|
|
9
|
+
"apple-tools-indexer": "bin/apple-tools-indexer.js"
|
|
10
10
|
},
|
|
11
11
|
"author": "Peter Coates",
|
|
12
12
|
"license": "MIT",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"search.js",
|
|
46
46
|
"contacts.js",
|
|
47
47
|
"lib/",
|
|
48
|
+
"bin/",
|
|
48
49
|
"scripts/audit-index.js",
|
|
49
50
|
"scripts/smoke-writes.js",
|
|
50
51
|
"scripts/postinstall.js",
|