applescript-mcp

import { ScriptCategory } from "../types/index.js"; /** * Generates HTML content for a note based on user input * @param args The arguments containing content specifications * @returns HTML string for the note */ function generateNoteHtml(args: any): string { const { title = "New Note", content = "", format = { headings: false, bold: false, italic: false, underline: false, links: false, lists: false } } = args; // Process content based on format options let processedContent = content; // If content contains markdown-like syntax and formatting is enabled, convert it if (format.headings) { // Convert # Heading to <h1>Heading</h1>, ## Heading to <h2>Heading</h2>, etc. processedContent = processedContent.replace(/^# (.+)$/gm, '<h1>$1</h1>'); processedContent = processedContent.replace(/^## (.+)$/gm, '<h2>$1</h2>'); processedContent = processedContent.replace(/^### (.+)$/gm, '<h3>$1</h3>'); } if (format.bold) { // Convert **text** or __text__ to <b>text</b> processedContent = processedContent.replace(/\*\*(.+?)\*\*/g, '<b>$1</b>'); processedContent = processedContent.replace(/__(.+?)__/g, '<b>$1</b>'); } if (format.italic) { // Convert *text* or _text_ to <i>text</i> processedContent = processedContent.replace(/\*(.+?)\*/g, '<i>$1</i>'); processedContent = processedContent.replace(/_(.+?)_/g, '<i>$1</i>'); } if (format.underline) { // Convert ~text~ to <u>text</u> processedContent = processedContent.replace(/~(.+?)~/g, '<u>$1</u>'); } if (format.links) { // Convert [text](url) to <a href="url">text</a> processedContent = processedContent.replace(/\[(.+?)\]\((.+?)\)/g, '<a href="$2">$1</a>'); } if (format.lists) { // Handle unordered lists // Look for lines starting with - or * and convert to <li> items const listItems = processedContent.match(/^[*-] (.+)$/gm); if (listItems) { let listHtml = '<ul>'; for (const item of listItems) { const content = item.replace(/^[*-] /, ''); listHtml += `<li>${content}</li>`; } listHtml += '</ul>'; // Replace the original list items with the HTML list for (const item of listItems) { processedContent = processedContent.replace(item, ''); } processedContent = processedContent.replace(/\n+/g, '\n') + listHtml; } // Handle ordered lists (1. Item) const orderedItems = processedContent.match(/^\d+\. (.+)$/gm); if (orderedItems) { let listHtml = '<ol>'; for (const item of orderedItems) { const content = item.replace(/^\d+\. /, ''); listHtml += `<li>${content}</li>`; } listHtml += '</ol>'; // Replace the original list items with the HTML list for (const item of orderedItems) { processedContent = processedContent.replace(item, ''); } processedContent = processedContent.replace(/\n+/g, '\n') + listHtml; } } // Wrap paragraphs in <p> tags if they aren't already wrapped in HTML tags const paragraphs = processedContent.split('\n\n'); processedContent = paragraphs .map((p: string) => { if (p.trim() && !p.trim().startsWith('<')) { return `<p>${p}</p>`; } return p; }) .join('\n'); return processedContent; } export const notesCategory: ScriptCategory = { name: "notes", description: "Apple Notes operations", scripts: [ { name: "create", description: "Create a new note with optional formatting", script: (args) => { const { title = "New Note", content = "", format = {} } = args; const htmlContent = generateNoteHtml(args); return ` tell application "Notes" make new note with properties {body:"${htmlContent}", name:"${title}"} end tell `; }, schema: { type: "object", properties: { title: { type: "string", description: "Title of the note" }, content: { type: "string", description: "Content of the note, can include markdown-like syntax for formatting" }, format: { type: "object", description: "Formatting options for the note content", properties: { headings: { type: "boolean", description: "Enable heading formatting (# Heading)" }, bold: { type: "boolean", description: "Enable bold formatting (**text**)" }, italic: { type: "boolean", description: "Enable italic formatting (*text*)" }, underline: { type: "boolean", description: "Enable underline formatting (~text~)" }, links: { type: "boolean", description: "Enable link formatting ([text](url))" }, lists: { type: "boolean", description: "Enable list formatting (- item or 1. item)" } } } }, required: ["title", "content"] } }, { name: "createRawHtml", description: "Create a new note with direct HTML content", script: (args) => { const { title = "New Note", html = "" } = args; return ` tell application "Notes" make new note with properties {body:"${html.replace(/"/g, '\\"')}", name:"${title}"} end tell `; }, schema: { type: "object", properties: { title: { type: "string", description: "Title of the note" }, html: { type: "string", description: "Raw HTML content for the note" } }, required: ["title", "html"] } }, { name: "list", description: "List all notes or notes in a specific folder", script: (args) => { const { folder = "" } = args; if (folder) { return ` tell application "Notes" set folderList to folders whose name is "${folder}" if length of folderList > 0 then set targetFolder to item 1 of folderList set noteNames to name of notes of targetFolder return noteNames as string else return "Folder not found: ${folder}" end if end tell `; } else { return ` tell application "Notes" set noteNames to name of notes return noteNames as string end tell `; } }, schema: { type: "object", properties: { folder: { type: "string", description: "Optional folder name to list notes from" } } } }, { name: "get", description: "Get a specific note by title", script: (args) => { const { title, folder = "" } = args; if (folder) { return ` tell application "Notes" set folderList to folders whose name is "${folder}" if length of folderList > 0 then set targetFolder to item 1 of folderList set matchingNotes to notes of targetFolder whose name is "${title}" if length of matchingNotes > 0 then set n to item 1 of matchingNotes set noteTitle to name of n set noteBody to body of n set noteCreationDate to creation date of n set noteModDate to modification date of n set jsonResult to "{\\"title\\": \\"" set jsonResult to jsonResult & noteTitle & "\\"" set jsonResult to jsonResult & ", \\"body\\": \\"" & noteBody & "\\"" set jsonResult to jsonResult & ", \\"creationDate\\": \\"" & noteCreationDate & "\\"" set jsonResult to jsonResult & ", \\"modificationDate\\": \\"" & noteModDate & "\\"}" return jsonResult else return "Note not found: ${title}" end if else return "Folder not found: ${folder}" end if end tell `; } else { return ` tell application "Notes" set matchingNotes to notes whose name is "${title}" if length of matchingNotes > 0 then set n to item 1 of matchingNotes set noteTitle to name of n set noteBody to body of n set noteCreationDate to creation date of n set noteModDate to modification date of n set jsonResult to "{\\"title\\": \\"" set jsonResult to jsonResult & noteTitle & "\\"" set jsonResult to jsonResult & ", \\"body\\": \\"" & noteBody & "\\"" set jsonResult to jsonResult & ", \\"creationDate\\": \\"" & noteCreationDate & "\\"" set jsonResult to jsonResult & ", \\"modificationDate\\": \\"" & noteModDate & "\\"}" return jsonResult else return "Note not found: ${title}" end if end tell `; } }, schema: { type: "object", properties: { title: { type: "string", description: "Title of the note to retrieve" }, folder: { type: "string", description: "Optional folder name to search in" } }, required: ["title"] } }, { name: "search", description: "Search for notes containing specific text", script: (args) => { const { query, folder = "", limit = 5, includeBody = true } = args; if (folder) { return ` tell application "Notes" set folderList to folders whose name is "${folder}" if length of folderList > 0 then set targetFolder to item 1 of folderList set matchingNotes to {} set allNotes to notes of targetFolder repeat with n in allNotes if name of n contains "${query}" or body of n contains "${query}" then set end of matchingNotes to n end if end repeat set resultCount to length of matchingNotes if resultCount > ${limit} then set resultCount to ${limit} set jsonResult to "[" repeat with i from 1 to resultCount set n to item i of matchingNotes set noteTitle to name of n set noteCreationDate to creation date of n set noteModDate to modification date of n ${includeBody ? 'set noteBody to body of n' : ''} set noteJson to "{\\"title\\": \\"" set noteJson to noteJson & noteTitle & "\\"" ${includeBody ? 'set noteJson to noteJson & ", \\"body\\": \\"" & noteBody & "\\""' : ''} set noteJson to noteJson & ", \\"creationDate\\": \\"" & noteCreationDate & "\\"" set noteJson to noteJson & ", \\"modificationDate\\": \\"" & noteModDate & "\\"}" set jsonResult to jsonResult & noteJson if i < resultCount then set jsonResult to jsonResult & ", " end repeat set jsonResult to jsonResult & "]" return jsonResult else return "Folder not found: ${folder}" end if end tell `; } else { return ` tell application "Notes" set matchingNotes to {} set allNotes to notes repeat with n in allNotes if name of n contains "${query}" or body of n contains "${query}" then set end of matchingNotes to n end if end repeat set resultCount to length of matchingNotes if resultCount > ${limit} then set resultCount to ${limit} set jsonResult to "[" repeat with i from 1 to resultCount set n to item i of matchingNotes set noteTitle to name of n set noteCreationDate to creation date of n set noteModDate to modification date of n ${includeBody ? 'set noteBody to body of n' : ''} set noteJson to "{\\"title\\": \\"" set noteJson to noteJson & noteTitle & "\\"" ${includeBody ? 'set noteJson to noteJson & ", \\"body\\": \\"" & noteBody & "\\""' : ''} set noteJson to noteJson & ", \\"creationDate\\": \\"" & noteCreationDate & "\\"" set noteJson to noteJson & ", \\"modificationDate\\": \\"" & noteModDate & "\\"}" set jsonResult to jsonResult & noteJson if i < resultCount then set jsonResult to jsonResult & ", " end repeat set jsonResult to jsonResult & "]" return jsonResult end tell `; } }, schema: { type: "object", properties: { query: { type: "string", description: "Text to search for in notes (title and body)" }, folder: { type: "string", description: "Optional folder name to search in" }, limit: { type: "number", description: "Maximum number of results to return (default: 5)" }, includeBody: { type: "boolean", description: "Whether to include note body in results (default: true)" } }, required: ["query"] } } ] };
ID: 0t5gydjcqw