apple-mail-mcp 2.10.11 → 2.10.13
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 +23 -6
- package/build/index.js +112 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1375,21 +1375,33 @@ When sending content containing backslashes (`\`) to this MCP server, **you must
|
|
|
1375
1375
|
|
|
1376
1376
|
**Why:** The MCP protocol uses JSON for parameter passing. In JSON, a single backslash is an escape character. To include a literal backslash in content, it must be escaped as `\\`.
|
|
1377
1377
|
|
|
1378
|
-
**
|
|
1378
|
+
**Correct — email containing a shell path with an escaped space:**
|
|
1379
|
+
|
|
1379
1380
|
```json
|
|
1380
1381
|
{
|
|
1381
1382
|
"to": ["colleague@company.com"],
|
|
1382
1383
|
"subject": "File Location",
|
|
1383
|
-
"body": "
|
|
1384
|
+
"body": "Run: cp ~/Library/Mobile\\ Documents/report.pdf ~/Desktop/"
|
|
1384
1385
|
}
|
|
1385
1386
|
```
|
|
1386
1387
|
|
|
1387
|
-
|
|
1388
|
+
→ arrives as: `Run: cp ~/Library/Mobile\ Documents/report.pdf ~/Desktop/`
|
|
1389
|
+
|
|
1390
|
+
In a JSON string literal, `\\` — two characters — denotes **one** literal backslash. Four backslashes (`\\\\`) denote **two** literal backslashes, so send those only when the text genuinely contains `\\`.
|
|
1391
|
+
|
|
1392
|
+
**Incorrect — the unescaped backslash makes this invalid JSON:**
|
|
1393
|
+
|
|
1394
|
+
```text
|
|
1395
|
+
"body": "Run: cp ~/Library/Mobile\ Documents/report.pdf ~/Desktop/"
|
|
1396
|
+
```
|
|
1397
|
+
|
|
1398
|
+
`\ ` (backslash-space) is not a valid JSON escape sequence, so the call is rejected — or, with a laxer parser, the backslash is silently dropped.
|
|
1388
1399
|
|
|
1389
1400
|
**Common patterns requiring escaping:**
|
|
1390
|
-
|
|
1391
|
-
- Shell escaped spaces: `Mobile\ Documents` → `Mobile
|
|
1392
|
-
- Regex patterns: `\d+` →
|
|
1401
|
+
|
|
1402
|
+
- Shell escaped spaces: `Mobile\ Documents` → `Mobile\\ Documents` in JSON
|
|
1403
|
+
- Regex patterns: `\d+` → `\\d+` in JSON
|
|
1404
|
+
- A literal double backslash: `\\` → `\\\\` in JSON
|
|
1393
1405
|
|
|
1394
1406
|
**If you see errors** when sending emails with backslashes, double-check that backslashes are properly escaped in the JSON payload.
|
|
1395
1407
|
|
|
@@ -1427,6 +1439,11 @@ The `\\\\` in JSON becomes `\\` in the actual string, which represents a single
|
|
|
1427
1439
|
- Verify Mail.app can send emails manually
|
|
1428
1440
|
- Check if the account is configured correctly in Mail.app
|
|
1429
1441
|
|
|
1442
|
+
### "invalid outputSchema … unsupported dialect" — every tool is refused
|
|
1443
|
+
- Full text: `Tool '<name>' has an invalid outputSchema: JSON Schema declares an unsupported dialect ("$schema": "http://json-schema.org/draft-07/schema#"). The default validator supports JSON Schema 2020-12 only.` The server connects, but **no tool is usable**.
|
|
1444
|
+
- **Upgrade to 2.10.12 or later.** Earlier versions advertised their tool schemas in JSON Schema **draft-07** (the MCP SDK's converter default); MCP has since standardized on **2020-12** and clients reject anything else. 2.10.12 normalizes every advertised `inputSchema`/`outputSchema` to 2020-12 on the way out. See [issue #147](https://github.com/sweetrb/apple-mail-mcp/issues/147).
|
|
1445
|
+
- Nothing to configure — restart your host app after upgrading so it re-reads the tool list.
|
|
1446
|
+
|
|
1430
1447
|
### `apple-mail` server fails to connect when run from a clone
|
|
1431
1448
|
- 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.
|
|
1432
1449
|
- If you've been editing the source, rerun `npm run build` — the server is `build/index.js`, and the committed bundle only reflects your changes after a rebuild.
|
package/build/index.js
CHANGED
|
@@ -83329,6 +83329,117 @@ function isOrphaned(ppid = process.ppid) {
|
|
|
83329
83329
|
return ppid === 1;
|
|
83330
83330
|
}
|
|
83331
83331
|
|
|
83332
|
+
// src/utils/jsonSchemaDialect.ts
|
|
83333
|
+
var JSON_SCHEMA_2020_12 = "https://json-schema.org/draft/2020-12/schema";
|
|
83334
|
+
var DEFINITIONS_REF_PREFIX = "#/definitions/";
|
|
83335
|
+
var SCHEMA_MAP_KEYWORDS = /* @__PURE__ */ new Set([
|
|
83336
|
+
"properties",
|
|
83337
|
+
"patternProperties",
|
|
83338
|
+
"$defs",
|
|
83339
|
+
"dependentSchemas"
|
|
83340
|
+
]);
|
|
83341
|
+
var DATA_KEYWORDS = /* @__PURE__ */ new Set([
|
|
83342
|
+
"enum",
|
|
83343
|
+
"const",
|
|
83344
|
+
"default",
|
|
83345
|
+
"examples",
|
|
83346
|
+
"required",
|
|
83347
|
+
"dependentRequired"
|
|
83348
|
+
]);
|
|
83349
|
+
function isPlainObject3(value) {
|
|
83350
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
83351
|
+
}
|
|
83352
|
+
function convertSchemaMap(node) {
|
|
83353
|
+
if (!isPlainObject3(node)) return node;
|
|
83354
|
+
const out = {};
|
|
83355
|
+
for (const [name, subschema] of Object.entries(node)) out[name] = convertNode(subschema);
|
|
83356
|
+
return out;
|
|
83357
|
+
}
|
|
83358
|
+
function convertNode(node) {
|
|
83359
|
+
if (Array.isArray(node)) return node.map(convertNode);
|
|
83360
|
+
if (!isPlainObject3(node)) return node;
|
|
83361
|
+
const hasTupleItems = Array.isArray(node.items);
|
|
83362
|
+
const out = {};
|
|
83363
|
+
for (const [key, value] of Object.entries(node)) {
|
|
83364
|
+
switch (key) {
|
|
83365
|
+
case "$schema":
|
|
83366
|
+
break;
|
|
83367
|
+
case "definitions":
|
|
83368
|
+
out.$defs = convertSchemaMap(value);
|
|
83369
|
+
break;
|
|
83370
|
+
case "$ref":
|
|
83371
|
+
out.$ref = typeof value === "string" && value.startsWith(DEFINITIONS_REF_PREFIX) ? "#/$defs/" + value.slice(DEFINITIONS_REF_PREFIX.length) : value;
|
|
83372
|
+
break;
|
|
83373
|
+
case "items":
|
|
83374
|
+
if (hasTupleItems) out.prefixItems = value.map(convertNode);
|
|
83375
|
+
else out.items = convertNode(value);
|
|
83376
|
+
break;
|
|
83377
|
+
case "additionalItems":
|
|
83378
|
+
if (hasTupleItems) out.items = convertNode(value);
|
|
83379
|
+
break;
|
|
83380
|
+
case "dependencies": {
|
|
83381
|
+
const dependentRequired = {};
|
|
83382
|
+
const dependentSchemas = {};
|
|
83383
|
+
if (isPlainObject3(value)) {
|
|
83384
|
+
for (const [property, dependency] of Object.entries(value)) {
|
|
83385
|
+
if (Array.isArray(dependency)) dependentRequired[property] = dependency;
|
|
83386
|
+
else dependentSchemas[property] = convertNode(dependency);
|
|
83387
|
+
}
|
|
83388
|
+
}
|
|
83389
|
+
if (Object.keys(dependentRequired).length > 0) out.dependentRequired = dependentRequired;
|
|
83390
|
+
if (Object.keys(dependentSchemas).length > 0) out.dependentSchemas = dependentSchemas;
|
|
83391
|
+
break;
|
|
83392
|
+
}
|
|
83393
|
+
case "exclusiveMinimum":
|
|
83394
|
+
case "exclusiveMaximum": {
|
|
83395
|
+
const bound = key === "exclusiveMinimum" ? node.minimum : node.maximum;
|
|
83396
|
+
if (value === true && typeof bound === "number") out[key] = bound;
|
|
83397
|
+
else if (value !== false) out[key] = convertNode(value);
|
|
83398
|
+
break;
|
|
83399
|
+
}
|
|
83400
|
+
case "minimum":
|
|
83401
|
+
if (node.exclusiveMinimum === true) break;
|
|
83402
|
+
out.minimum = convertNode(value);
|
|
83403
|
+
break;
|
|
83404
|
+
case "maximum":
|
|
83405
|
+
if (node.exclusiveMaximum === true) break;
|
|
83406
|
+
out.maximum = convertNode(value);
|
|
83407
|
+
break;
|
|
83408
|
+
default:
|
|
83409
|
+
if (DATA_KEYWORDS.has(key)) out[key] = value;
|
|
83410
|
+
else if (SCHEMA_MAP_KEYWORDS.has(key)) out[key] = convertSchemaMap(value);
|
|
83411
|
+
else out[key] = convertNode(value);
|
|
83412
|
+
}
|
|
83413
|
+
}
|
|
83414
|
+
return out;
|
|
83415
|
+
}
|
|
83416
|
+
function toJsonSchema2020_12(schema) {
|
|
83417
|
+
if (!isPlainObject3(schema)) return schema;
|
|
83418
|
+
return {
|
|
83419
|
+
$schema: JSON_SCHEMA_2020_12,
|
|
83420
|
+
...convertNode(schema)
|
|
83421
|
+
};
|
|
83422
|
+
}
|
|
83423
|
+
function normalizeOutgoingMessage(message) {
|
|
83424
|
+
if (!isPlainObject3(message)) return message;
|
|
83425
|
+
const result = message.result;
|
|
83426
|
+
if (!isPlainObject3(result) || !Array.isArray(result.tools)) return message;
|
|
83427
|
+
const tools = result.tools.map((tool) => {
|
|
83428
|
+
if (!isPlainObject3(tool)) return tool;
|
|
83429
|
+
const next = { ...tool };
|
|
83430
|
+
if (isPlainObject3(tool.inputSchema)) next.inputSchema = toJsonSchema2020_12(tool.inputSchema);
|
|
83431
|
+
if (isPlainObject3(tool.outputSchema))
|
|
83432
|
+
next.outputSchema = toJsonSchema2020_12(tool.outputSchema);
|
|
83433
|
+
return next;
|
|
83434
|
+
});
|
|
83435
|
+
return { ...message, result: { ...result, tools } };
|
|
83436
|
+
}
|
|
83437
|
+
function withJsonSchema2020_12(transport2) {
|
|
83438
|
+
const originalSend = transport2.send.bind(transport2);
|
|
83439
|
+
transport2.send = (message, options) => originalSend(normalizeOutgoingMessage(message), options);
|
|
83440
|
+
return transport2;
|
|
83441
|
+
}
|
|
83442
|
+
|
|
83332
83443
|
// src/index.ts
|
|
83333
83444
|
loadFileConfig();
|
|
83334
83445
|
var MESSAGE_ID_SCHEMA = external_exports.string().regex(/^(\d+|imap:[A-Za-z0-9_-]+)$/, "Message ID must be numeric or an IMAP id (imap:\u2026)");
|
|
@@ -85569,7 +85680,7 @@ process.on("uncaughtException", (err) => {
|
|
|
85569
85680
|
process.on("unhandledRejection", (reason) => {
|
|
85570
85681
|
console.error("[unhandledRejection]", reason);
|
|
85571
85682
|
});
|
|
85572
|
-
var transport = new StdioServerTransport();
|
|
85683
|
+
var transport = withJsonSchema2020_12(new StdioServerTransport());
|
|
85573
85684
|
await server.connect(transport);
|
|
85574
85685
|
var idleWatcher;
|
|
85575
85686
|
if (/^(1|true|yes|on)$/i.test(process.env.APPLE_MAIL_MCP_IMAP_IDLE?.trim() ?? "")) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apple-mail-mcp",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.13",
|
|
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",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
55
55
|
"@typescript-eslint/parser": "^8.0.0",
|
|
56
56
|
"@vitest/coverage-v8": "^4.1.9",
|
|
57
|
+
"ajv": "^8.17.1",
|
|
57
58
|
"esbuild": "^0.28.1",
|
|
58
59
|
"eslint": "^9.0.0",
|
|
59
60
|
"globals": "^17.0.0",
|