- applescript-mcp
- src
- categories
import { ScriptCategory } from "../types/index.js";
/**
* Mail-related scripts.
* * create_email: Create a new email in Mail.app with specified recipient, subject, and body
* * list_emails: List emails from a specified mailbox in Mail.app
* * get_email: Get a specific email by ID or search criteria from Mail.app
*/
export const mailCategory: ScriptCategory = {
name: "mail",
description: "Mail operations",
scripts: [
{
name: "create_email",
description: "Create a new email in Mail.app",
schema: {
type: "object",
properties: {
recipient: {
type: "string",
description: "Email recipient",
},
subject: {
type: "string",
description: "Email subject",
},
body: {
type: "string",
description: "Email body",
},
},
required: ["recipient", "subject", "body"],
},
script: (args) => `
set recipient to "${args.recipient}"
set subject to "${args.subject}"
set body to "${args.body}"
-- URL encode subject and body
set encodedSubject to my urlEncode(subject)
set encodedBody to my urlEncode(body)
-- Construct the mailto URL
set mailtoURL to "mailto:" & recipient & "?subject=" & encodedSubject & "&body=" & encodedBody
-- Use Apple Mail's 'mailto' command to create the email
tell application "Mail"
mailto mailtoURL
activate
end tell
-- Handler to URL-encode text
on urlEncode(theText)
set theEncodedText to ""
set theChars to every character of theText
repeat with aChar in theChars
set charCode to ASCII number aChar
if charCode = 32 then
set theEncodedText to theEncodedText & "%20" -- Space
else if (charCode ≥ 48 and charCode ≤ 57) or (charCode ≥ 65 and charCode ≤ 90) or (charCode ≥ 97 and charCode ≤ 122) or charCode = 45 or charCode = 46 or charCode = 95 or charCode = 126 then
-- Allowed characters: A-Z, a-z, 0-9, -, ., _, ~
set theEncodedText to theEncodedText & aChar
else
-- Convert to %HH format
set hexCode to do shell script "printf '%02X' " & charCode
set theEncodedText to theEncodedText & "%" & hexCode
end if
end repeat
return theEncodedText
end urlEncode
`,
},
{
name: "list_emails",
description: "List emails from a specified mailbox in Mail.app",
schema: {
type: "object",
properties: {
mailbox: {
type: "string",
description: "Name of the mailbox to list emails from (e.g., 'Inbox', 'Sent')",
default: "Inbox"
},
account: {
type: "string",
description: "Name of the account to search in (e.g., 'iCloud', 'Gmail', 'Exchange'). If not specified, searches all accounts with preference for iCloud.",
default: "iCloud"
},
count: {
type: "number",
description: "Maximum number of emails to retrieve",
default: 10
},
unreadOnly: {
type: "boolean",
description: "Only show unread emails if true"
}
}
},
script: (args) => `
set mailboxName to "${args.mailbox || 'Inbox'}"
set accountName to "${args.account || 'iCloud'}"
set messageCount to ${args.count || 10}
set showUnreadOnly to ${args.unreadOnly ? 'true' : 'false'}
set searchAllAccounts to ${!args.account ? 'true' : 'false'}
tell application "Mail"
-- Get all messages if no specific mailbox is found
set foundMailbox to false
set emailMessages to {}
set targetAccount to missing value
-- First try to find the specified account
if not searchAllAccounts then
try
set allAccounts to every account
repeat with acct in allAccounts
if name of acct is accountName then
set targetAccount to acct
exit repeat
end if
end repeat
end try
-- If account not found, set to search all accounts
if targetAccount is missing value then
set searchAllAccounts to true
end if
end if
-- If specific account is found, search in that account
if not searchAllAccounts and targetAccount is not missing value then
try
set acctMailboxes to every mailbox of targetAccount
repeat with m in acctMailboxes
if name of m is mailboxName then
set targetMailbox to m
set foundMailbox to true
-- Get messages from the found mailbox
if showUnreadOnly then
set emailMessages to (messages of targetMailbox whose read status is false)
else
set emailMessages to (messages of targetMailbox)
end if
exit repeat
end if
end repeat
-- If mailbox not found in specified account, try to get inbox
if not foundMailbox then
try
set inboxMailbox to inbox of targetAccount
set targetMailbox to inboxMailbox
set foundMailbox to true
if showUnreadOnly then
set emailMessages to (messages of targetMailbox whose read status is false)
else
set emailMessages to (messages of targetMailbox)
end if
end try
end if
end try
else
-- Search all accounts, with preference for iCloud
set iCloudAccount to missing value
set allAccounts to every account
-- First look for iCloud account
repeat with acct in allAccounts
if name of acct is "iCloud" then
set iCloudAccount to acct
exit repeat
end if
end repeat
-- Try to find the mailbox directly
try
set allMailboxes to every mailbox
repeat with m in allMailboxes
if name of m is mailboxName then
set targetMailbox to m
set foundMailbox to true
-- Get messages from the found mailbox
if showUnreadOnly then
set emailMessages to (messages of targetMailbox whose read status is false)
else
set emailMessages to (messages of targetMailbox)
end if
exit repeat
end if
end repeat
end try
-- If not found directly, try to find it in each account (prioritize iCloud)
if not foundMailbox and iCloudAccount is not missing value then
try
set acctMailboxes to every mailbox of iCloudAccount
repeat with m in acctMailboxes
if name of m is mailboxName then
set targetMailbox to m
set foundMailbox to true
-- Get messages from the found mailbox
if showUnreadOnly then
set emailMessages to (messages of targetMailbox whose read status is false)
else
set emailMessages to (messages of targetMailbox)
end if
exit repeat
end if
end repeat
end try
end if
-- If still not found in iCloud, check other accounts
if not foundMailbox then
repeat with acct in allAccounts
if acct is not iCloudAccount then
try
set acctMailboxes to every mailbox of acct
repeat with m in acctMailboxes
if name of m is mailboxName then
set targetMailbox to m
set foundMailbox to true
-- Get messages from the found mailbox
if showUnreadOnly then
set emailMessages to (messages of targetMailbox whose read status is false)
else
set emailMessages to (messages of targetMailbox)
end if
exit repeat
end if
end repeat
if foundMailbox then exit repeat
end try
end if
end repeat
end if
end if
-- If still not found, get messages from all inboxes
if not foundMailbox then
set emailMessages to {}
set allAccounts to every account
set accountsChecked to 0
-- First check iCloud if available
repeat with acct in allAccounts
if name of acct is "iCloud" then
try
-- Try to get the inbox for iCloud
set inboxMailbox to inbox of acct
-- Add messages from this inbox
if showUnreadOnly then
set acctMessages to (messages of inboxMailbox whose read status is false)
else
set acctMessages to (messages of inboxMailbox)
end if
set emailMessages to emailMessages & acctMessages
set accountsChecked to accountsChecked + 1
end try
exit repeat
end if
end repeat
-- Then check other accounts if needed
if accountsChecked is 0 then
repeat with acct in allAccounts
try
-- Try to get the inbox for this account
set inboxMailbox to inbox of acct
-- Add messages from this inbox
if showUnreadOnly then
set acctMessages to (messages of inboxMailbox whose read status is false)
else
set acctMessages to (messages of inboxMailbox)
end if
set emailMessages to emailMessages & acctMessages
end try
end repeat
end if
-- Sort combined messages by date (newest first)
set emailMessages to my sortMessagesByDate(emailMessages)
set mailboxName to "All Inboxes"
end if
-- Limit the number of messages
if (count of emailMessages) > messageCount then
set emailMessages to items 1 thru messageCount of emailMessages
end if
-- Format the results
set accountInfo to ""
if not searchAllAccounts and targetAccount is not missing value then
set accountInfo to " (" & accountName & ")"
end if
set emailList to "Recent emails in " & mailboxName & accountInfo & ":" & return & return
if (count of emailMessages) is 0 then
set emailList to emailList & "No messages found."
else
repeat with theMessage in emailMessages
try
set msgSubject to subject of theMessage
set msgSender to sender of theMessage
set msgDate to date received of theMessage
set msgRead to read status of theMessage
-- Try to get account name for this message
set msgAccount to ""
try
set msgMailbox to mailbox of theMessage
set msgAcct to account of msgMailbox
set msgAccount to " [" & name of msgAcct & "]"
end try
set emailList to emailList & "From: " & msgSender & return
set emailList to emailList & "Subject: " & msgSubject & return
set emailList to emailList & "Date: " & msgDate & msgAccount & return
set emailList to emailList & "Read: " & msgRead & return & return
on error errMsg
set emailList to emailList & "Error processing message: " & errMsg & return & return
end try
end repeat
end if
return emailList
end tell
-- Helper function to sort messages by date
on sortMessagesByDate(messageList)
tell application "Mail"
set sortedMessages to {}
-- Simple bubble sort by date received (newest first)
repeat with i from 1 to count of messageList
set currentMsg to item i of messageList
set currentDate to date received of currentMsg
set inserted to false
if (count of sortedMessages) is 0 then
set sortedMessages to {currentMsg}
else
repeat with j from 1 to count of sortedMessages
set compareMsg to item j of sortedMessages
set compareDate to date received of compareMsg
if currentDate > compareDate then
if j is 1 then
set sortedMessages to {currentMsg} & sortedMessages
else
set sortedMessages to (items 1 thru (j - 1) of sortedMessages) & currentMsg & (items j thru (count of sortedMessages) of sortedMessages)
end if
set inserted to true
exit repeat
end if
end repeat
if not inserted then
set sortedMessages to sortedMessages & {currentMsg}
end if
end if
end repeat
return sortedMessages
end tell
end sortMessagesByDate
`,
},
{
name: "get_email",
description: "Get a specific email by search criteria from Mail.app",
schema: {
type: "object",
properties: {
mailbox: {
type: "string",
description: "Name of the mailbox to search in (e.g., 'Inbox', 'Sent')",
default: "Inbox"
},
account: {
type: "string",
description: "Name of the account to search in (e.g., 'iCloud', 'Gmail', 'Exchange'). If not specified, searches all accounts with preference for iCloud.",
default: "iCloud"
},
subject: {
type: "string",
description: "Subject text to search for (partial match)"
},
sender: {
type: "string",
description: "Sender email or name to search for (partial match)"
},
dateReceived: {
type: "string",
description: "Date received to search for (format: YYYY-MM-DD)"
},
unreadOnly: {
type: "boolean",
description: "Only search unread emails if true"
},
includeBody: {
type: "boolean",
description: "Include email body in the result if true",
default: false
}
},
required: []
},
script: (args) => `
set mailboxName to "${args.mailbox || 'Inbox'}"
set accountName to "${args.account || 'iCloud'}"
set searchSubject to "${args.subject || ''}"
set searchSender to "${args.sender || ''}"
set searchDate to "${args.dateReceived || ''}"
set showUnreadOnly to ${args.unreadOnly ? 'true' : 'false'}
set includeBody to ${args.includeBody ? 'true' : 'false'}
set searchAllAccounts to ${!args.account ? 'true' : 'false'}
tell application "Mail"
-- Get all messages if no specific mailbox is found
set foundMailbox to false
set emailMessages to {}
set targetAccount to missing value
-- First try to find the specified account
if not searchAllAccounts then
try
set allAccounts to every account
repeat with acct in allAccounts
if name of acct is accountName then
set targetAccount to acct
exit repeat
end if
end repeat
end try
-- If account not found, set to search all accounts
if targetAccount is missing value then
set searchAllAccounts to true
end if
end if
-- If specific account is found, search in that account
if not searchAllAccounts and targetAccount is not missing value then
try
set acctMailboxes to every mailbox of targetAccount
repeat with m in acctMailboxes
if name of m is mailboxName then
set targetMailbox to m
set foundMailbox to true
-- Get messages from the found mailbox
if showUnreadOnly then
set emailMessages to (messages of targetMailbox whose read status is false)
else
set emailMessages to (messages of targetMailbox)
end if
exit repeat
end if
end repeat
end try
else
-- Search all accounts, with preference for iCloud
set iCloudAccount to missing value
set allAccounts to every account
-- First look for iCloud account
repeat with acct in allAccounts
if name of acct is "iCloud" then
set iCloudAccount to acct
exit repeat
end if
end repeat
-- Try to find the mailbox directly
try
set allMailboxes to every mailbox
repeat with m in allMailboxes
if name of m is mailboxName then
set targetMailbox to m
set foundMailbox to true
-- Get messages from the found mailbox
if showUnreadOnly then
set emailMessages to (messages of targetMailbox whose read status is false)
else
set emailMessages to (messages of targetMailbox)
end if
exit repeat
end if
end repeat
end try
end if
-- Filter messages based on search criteria
set filteredMessages to {}
repeat with theMessage in emailMessages
try
set matchesSubject to true
set matchesSender to true
set matchesDate to true
-- Check subject if specified
if searchSubject is not "" then
set msgSubject to subject of theMessage
if msgSubject does not contain searchSubject then
set matchesSubject to false
end if
end if
-- Check sender if specified
if searchSender is not "" then
set msgSender to sender of theMessage
if msgSender does not contain searchSender then
set matchesSender to false
end if
end if
-- Check date if specified
if searchDate is not "" then
set msgDate to date received of theMessage
set msgDateString to (year of msgDate as string) & "-" & my padNumber(month of msgDate as integer) & "-" & my padNumber(day of msgDate as integer)
if msgDateString is not searchDate then
set matchesDate to false
end if
end if
-- Add to filtered list if all criteria match
if matchesSubject and matchesSender and matchesDate then
set end of filteredMessages to theMessage
end if
end try
end repeat
-- Format the results
set emailList to "Search results:" & return & return
if (count of filteredMessages) is 0 then
set emailList to emailList & "No matching emails found."
else
repeat with theMessage in filteredMessages
try
set msgSubject to subject of theMessage
set msgSender to sender of theMessage
set msgDate to date received of theMessage
set msgRead to read status of theMessage
-- Try to get account name for this message
set msgAccount to ""
try
set msgMailbox to mailbox of theMessage
set msgAcct to account of msgMailbox
set msgAccount to " [" & name of msgAcct & "]"
end try
set emailList to emailList & "From: " & msgSender & return
set emailList to emailList & "Subject: " & msgSubject & return
set emailList to emailList & "Date: " & msgDate & msgAccount & return
set emailList to emailList & "Read: " & msgRead & return
-- Include body if requested
if includeBody then
set msgContent to content of theMessage
set emailList to emailList & "Content: " & return & msgContent & return
end if
set emailList to emailList & return
on error errMsg
set emailList to emailList & "Error processing message: " & errMsg & return & return
end try
end repeat
end if
return emailList
end tell
-- Helper function to pad numbers with leading zero if needed
on padNumber(num)
if num < 10 then
return "0" & num
else
return num as string
end if
end padNumber
`,
},
],
};