mcp-icloud 2.1.3 → 2.1.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/email/index.js CHANGED
@@ -64,7 +64,7 @@ async function handleReadEmail(args) {
64
64
  const local = isLocalMode();
65
65
 
66
66
  const email = local
67
- ? await localClient.readEmail(args.uid)
67
+ ? await localClient.readEmail(args.uid, folder)
68
68
  : await cloudClient.readEmail(args.uid, folder);
69
69
 
70
70
  // Mail.app returns the body as `body`; mailparser returns `text`.
@@ -167,7 +167,7 @@ async function handleMarkAsRead(args) {
167
167
  const isRead = args.isRead !== false;
168
168
 
169
169
  if (isLocalMode()) {
170
- await localClient.markAsRead(args.uid, isRead);
170
+ await localClient.markAsRead(args.uid, isRead, folder);
171
171
  } else {
172
172
  await cloudClient.markAsRead(args.uid, folder, isRead);
173
173
  }
@@ -50,15 +50,42 @@ async function listEmails(folder = 'inbox', count = 25) {
50
50
  return result ? JSON.parse(result) : [];
51
51
  }
52
52
 
53
+ /**
54
+ * JXA statements that set `mail` and `msg` to message `emailId`.
55
+ * Mail has no application-level messages collection, so
56
+ * `mail.messages.byId(id)` fails with -1728 (issue #15). A mailbox's
57
+ * `messages.byId(id)` works; the named folder is tried first.
58
+ * @param {string|number} emailId - Email ID
59
+ * @param {string} folder - Folder name
60
+ * @returns {string}
61
+ */
62
+ function findMessageJXA(emailId, folder) {
63
+ const id = asInt(emailId);
64
+ return `
65
+ const mail = Application('Mail');
66
+ let msg = null;
67
+ for (const account of mail.accounts()) {
68
+ const mailboxes = [account.mailboxes.byName('${escapeJXA(getMailboxName(folder || 'inbox'))}')].concat(account.mailboxes());
69
+ for (const mailbox of mailboxes) {
70
+ if (!mailbox.exists()) continue;
71
+ const candidate = mailbox.messages.byId(${id});
72
+ if (candidate.exists()) { msg = candidate; break; }
73
+ }
74
+ if (msg) break;
75
+ }
76
+ if (!msg) throw new Error('Email not found: ${id}');
77
+ `;
78
+ }
79
+
53
80
  /**
54
81
  * Read a specific email
55
82
  * @param {string} emailId - Email ID
83
+ * @param {string} [folder] - Folder to look in first
56
84
  * @returns {Promise<Object>} - Email content
57
85
  */
58
- async function readEmail(emailId) {
86
+ async function readEmail(emailId, folder = 'inbox') {
59
87
  const script = `
60
- const mail = Application('Mail');
61
- const msg = mail.messages.byId(${asInt(emailId)});
88
+ ${findMessageJXA(emailId, folder)}
62
89
 
63
90
  JSON.stringify({
64
91
  id: msg.id(),
@@ -170,18 +197,18 @@ async function searchEmails({ query, from, subject, folder = 'inbox', count = 25
170
197
  * Mark email as read/unread
171
198
  * @param {string} emailId - Email ID
172
199
  * @param {boolean} isRead - Read status
200
+ * @param {string} [folder] - Folder to look in first
173
201
  * @returns {Promise<Object>} - Result
174
202
  */
175
- async function markAsRead(emailId, isRead = true) {
203
+ async function markAsRead(emailId, isRead = true, folder = 'inbox') {
176
204
  const script = `
177
- tell application "Mail"
178
- set theMessage to message id ${asInt(emailId)}
179
- set read status of theMessage to ${asBool(isRead, true)}
180
- end tell
181
- return "done"
205
+ ${findMessageJXA(emailId, folder)}
206
+ msg = msg.mailbox().messages.byId(${asInt(emailId)});
207
+ msg.readStatus = ${asBool(isRead, true)};
208
+ 'done';
182
209
  `;
183
210
 
184
- await runAppleScript(script);
211
+ await runJXA(script);
185
212
  return { success: true, message: `Email marked as ${isRead ? 'read' : 'unread'}` };
186
213
  }
187
214
 
@@ -216,18 +243,18 @@ async function listFolders() {
216
243
  /**
217
244
  * Delete an email
218
245
  * @param {string} emailId - Email ID
246
+ * @param {string} [folder] - Folder to look in first
219
247
  * @returns {Promise<Object>} - Result
220
248
  */
221
- async function deleteEmail(emailId) {
249
+ async function deleteEmail(emailId, folder = 'inbox') {
222
250
  const script = `
223
- tell application "Mail"
224
- set theMessage to message id ${asInt(emailId)}
225
- delete theMessage
226
- end tell
227
- return "deleted"
251
+ ${findMessageJXA(emailId, folder)}
252
+ msg = msg.mailbox().messages.byId(${asInt(emailId)});
253
+ mail.delete(msg);
254
+ 'deleted';
228
255
  `;
229
256
 
230
- await runAppleScript(script);
257
+ await runJXA(script);
231
258
  return { success: true, message: 'Email deleted' };
232
259
  }
233
260
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mcp-icloud",
3
3
  "mcpName": "io.github.MrGo2/icloud-mcp",
4
- "version": "2.1.3",
4
+ "version": "2.1.4",
5
5
  "description": "MCP server for Apple services - Email, Calendar, Contacts, Reminders, Notes, Messages and Safari - with dual local (AppleScript) and cloud (IMAP/CalDAV/CardDAV) modes",
6
6
  "main": "index.js",
7
7
  "bin": {
@@ -27,7 +27,7 @@
27
27
  ],
28
28
  "scripts": {
29
29
  "start": "node index.js",
30
- "test": "node test/security.test.js && node test/local-output.test.js",
30
+ "test": "node test/security.test.js && node test/local-output.test.js && node test/email-read.test.js",
31
31
  "test-mode": "USE_TEST_MODE=true node index.js",
32
32
  "inspect": "npx @modelcontextprotocol/inspector node index.js",
33
33
  "test:local": "node test/local-output.test.js"