process_note_request
Submit text requests to create, update, delete, or search Apple Notes through the NotesY MCP server, enabling streamlined note management via natural language commands.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes |
Implementation Reference
- src/index.ts:16-80 (handler)Main MCP tool handler for 'process_note_request'. Parses natural language input, uses helpers to generate and execute AppleScript for Apple Notes operations, and formats MCP response.server.tool("process_note_request", { text: z.string().min(1) }, async ({ text }) => { try { // Handle JSON input if present let processedText = text; if (text.trim().startsWith('{') && text.includes('text')) { try { const jsonData = JSON.parse(text); if (jsonData.text) { processedText = jsonData.text; } } catch (err) { // Continue with original text } } // Process the text request const parsedRequest = parseNaturalLanguage(processedText); if (!parsedRequest) { return { content: [{ type: "text", text: "I couldn't understand your request. Try phrasing it like:\n- Create a note titled 'shopping list' with items: milk, eggs, bread\n- Add meeting notes to my 'work' note\n- Delete my 'old tasks' note\n- Search for notes with 'project'" }] }; } // Execute the AppleScript const script = generateAppleScript(parsedRequest); const result = await executeAppleScript(script); // Format the response based on action type let responseText = ""; switch (parsedRequest.action) { case 'create': responseText = `Successfully created note "${parsedRequest.title}"`; break; case 'update': responseText = `Successfully updated note "${parsedRequest.title}"`; break; case 'delete': responseText = `Successfully deleted note "${parsedRequest.title}"`; break; case 'search': if (result && result.trim()) { const notes = result.split(',').map(note => note.trim()); responseText = `Found ${notes.length} note(s) matching "${parsedRequest.title}":\n${notes.map(note => `- ${note}`).join('\n')}`; } else { responseText = `No notes found matching "${parsedRequest.title}"`; } break; } return { content: [{ type: "text", text: responseText }] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: "text", text: `Error: ${errorMessage}` }] }; } });
- Zod schema for internal NoteRequest structure used by parseNaturalLanguage and other functions.const NoteRequestSchema = z.object({ action: z.enum(['create', 'update', 'delete', 'search']), title: z.string().min(1), content: z.array(z.string()).optional(), folder: z.string().optional(), }); export type NoteRequest = z.infer<typeof NoteRequestSchema>;
- src/index.ts:16-80 (registration)MCP tool registration including schema and handler reference.server.tool("process_note_request", { text: z.string().min(1) }, async ({ text }) => { try { // Handle JSON input if present let processedText = text; if (text.trim().startsWith('{') && text.includes('text')) { try { const jsonData = JSON.parse(text); if (jsonData.text) { processedText = jsonData.text; } } catch (err) { // Continue with original text } } // Process the text request const parsedRequest = parseNaturalLanguage(processedText); if (!parsedRequest) { return { content: [{ type: "text", text: "I couldn't understand your request. Try phrasing it like:\n- Create a note titled 'shopping list' with items: milk, eggs, bread\n- Add meeting notes to my 'work' note\n- Delete my 'old tasks' note\n- Search for notes with 'project'" }] }; } // Execute the AppleScript const script = generateAppleScript(parsedRequest); const result = await executeAppleScript(script); // Format the response based on action type let responseText = ""; switch (parsedRequest.action) { case 'create': responseText = `Successfully created note "${parsedRequest.title}"`; break; case 'update': responseText = `Successfully updated note "${parsedRequest.title}"`; break; case 'delete': responseText = `Successfully deleted note "${parsedRequest.title}"`; break; case 'search': if (result && result.trim()) { const notes = result.split(',').map(note => note.trim()); responseText = `Found ${notes.length} note(s) matching "${parsedRequest.title}":\n${notes.map(note => `- ${note}`).join('\n')}`; } else { responseText = `No notes found matching "${parsedRequest.title}"`; } break; } return { content: [{ type: "text", text: responseText }] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: "text", text: `Error: ${errorMessage}` }] }; } });
- Helper function that parses natural language text into structured NoteRequest object.export function parseNaturalLanguage(text: string): NoteRequest | null { const lowerText = text.toLowerCase(); // Handle long text content as note creation if (text.length > 100 && !text.includes('"') && !text.includes("'")) { const firstLine = text.split('\n')[0].trim(); const title = firstLine.split(' ').slice(0, 5).join(' '); try { return NoteRequestSchema.parse({ action: 'create', title: title, content: [text] }); } catch (e) { // Continue to other patterns } } // Handle trip planning specifically if (lowerText.includes('trip') || lowerText.includes('travel') || lowerText.includes('vacation')) { const locationMatch = text.match(/(?:trip|travel|vacation)(?:\s+(?:to|for|in))?\s+([A-Za-z\s,]+)/) || text.match(/([A-Za-z\s,]+)(?:\s+(?:trip|travel|vacation))/); if (locationMatch) { const location = locationMatch[1].trim(); const title = `Trip plan: ${location}`; try { return NoteRequestSchema.parse({ action: 'create', title, content: [text] }); } catch (e) { // Continue to other patterns } } } // Handle note creation commands const createPatterns = [ /create\s+(?:a\s+)?note\s+(?:titled|called|named)\s+['"](.+?)['"](?:\s+with\s+(?:items|content|text)(?:\s*:|:|\s+of)?)?(?:\s+(.+))?/i, /(?:make|add)\s+(?:a\s+)?(?:new\s+)?note\s+(?:titled|called|named)\s+['"](.+?)['"](?:\s+with\s+(?:items|content|text)(?:\s*:|:|\s+of)?)?(?:\s+(.+))?/i, /(?:take|write)\s+(?:a\s+)?note\s+(?:titled|called|named)\s+['"](.+?)['"](?:\s+with\s+(?:items|content|text)(?:\s*:|:|\s+of)?)?(?:\s+(.+))?/i ]; for (const pattern of createPatterns) { const match = text.match(pattern); if (match) { const title = match[1]; let content: string[] = []; if (match[2]) { if (match[2].includes(',')) { content = match[2].split(',').map(item => item.trim()); } else if (match[2].includes('\n')) { content = match[2].split('\n').map(item => item.trim().replace(/^[-•*]\s*/, '')); } else { content = [match[2].trim()]; } } try { return NoteRequestSchema.parse({ action: 'create', title, content }); } catch (e) { return null; } } } // Handle update commands const updatePatterns = [ /add\s+(.+?)\s+to\s+(?:my\s+)?['"](.+?)['"](?:\s+note)?/i, /update\s+(?:my\s+)?['"](.+?)['"](?:\s+note)?\s+(?:with|to(?:\s+add)?)\s+(.+)/i, /append\s+(.+?)\s+to\s+(?:my\s+)?['"](.+?)['"](?:\s+note)?/i ]; for (const pattern of updatePatterns) { const match = text.match(pattern); if (match) { let content: string, title: string; if (lowerText.includes("update") && match[1]) { title = match[1]; content = match[2]; } else { content = match[1]; title = match[2]; } if (title && content) { try { return NoteRequestSchema.parse({ action: 'update', title, content: [content.trim()] }); } catch (e) { return null; } } } } // Handle delete commands const deletePatterns = [ /delete\s+(?:my\s+)?(?:note\s+)?['"](.+?)['"](?:\s+note)?/i, /remove\s+(?:my\s+)?(?:note\s+)?['"](.+?)['"](?:\s+note)?/i, /trash\s+(?:my\s+)?(?:note\s+)?['"](.+?)['"](?:\s+note)?/i, /get\s+rid\s+of\s+(?:my\s+)?(?:note\s+)?['"](.+?)['"](?:\s+note)?/i ]; for (const pattern of deletePatterns) { const match = text.match(pattern); if (match) { const title = match[1]; try { return NoteRequestSchema.parse({ action: 'delete', title }); } catch (e) { return null; } } } // Handle search commands const searchPatterns = [ /(?:search|find|look\s+for)\s+(?:my\s+)?(?:note\s+)?['"](.+?)['"](?:\s+note)?/i, /(?:search|find|look)\s+(?:for\s+)?(?:notes?\s+)?(?:with|containing|about)\s+['"](.+?)['"](?:\s+note)?/i ]; for (const pattern of searchPatterns) { const match = text.match(pattern); if (match) { const title = match[1]; try { return NoteRequestSchema.parse({ action: 'search', title }); } catch (e) { return null; } } } // Handle simple creation with quoted title if (lowerText.includes('create') || lowerText.includes('new') || lowerText.includes('make')) { const titleMatch = text.match(/['"](.+?)['"]/) || text.match(/note\s+(?:about|on|for)\s+(.+?)(?:\s|$)/i); if (titleMatch) { try { return NoteRequestSchema.parse({ action: 'create', title: titleMatch[1], content: [] }); } catch (e) { return null; } } } return null; }
- Helper function that generates AppleScript based on parsed NoteRequest.export function generateAppleScript(request: NoteRequest): string { const safeTitle = request.title.replace(/"/g, '\\"'); const safeContent = request.content?.map(item => item.replace(/"/g, '\\"')) || []; switch (request.action) { case 'create': return ` tell application "Notes" set newNote to make new note with properties {name:"${safeTitle}"} ${safeContent.map(item => `tell newNote to make new paragraph at the end with data "${item}"`).join('\n') || ''} end tell `; case 'update': return ` tell application "Notes" set noteFound to false repeat with theNote in notes if name of theNote is "${safeTitle}" then set noteFound to true tell theNote ${safeContent.map(item => `make new paragraph at the end with data "${item}"`).join('\n') || ''} end tell exit repeat end if end repeat if not noteFound then set newNote to make new note with properties {name:"${safeTitle}"} ${safeContent.map(item => `tell newNote to make new paragraph at the end with data "${item}"`).join('\n') || ''} end if end tell `; case 'delete': return ` tell application "Notes" set noteFound to false repeat with theNote in notes if name of theNote is "${safeTitle}" then set noteFound to true delete theNote exit repeat end if end repeat end tell `; case 'search': return ` tell application "Notes" set matchingNotes to {} repeat with theNote in notes if name of theNote contains "${safeTitle}" then set end of matchingNotes to name of theNote end if end repeat return matchingNotes end tell `; default: return ''; } }
- Helper function to execute generated AppleScript.export function executeAppleScript(script: string): Promise<string> { return new Promise((resolve, reject) => { const tempFile = `/tmp/notescript_${Date.now()}.scpt`; const writeCmd = `echo '${script.replace(/'/g, "'\\''")}' > ${tempFile}`; exec(writeCmd, writeErr => { if (writeErr) { return reject(`Error writing script: ${writeErr}`); } exec(`osascript ${tempFile}`, (execErr, stdout, stderr) => { exec(`rm ${tempFile}`); if (execErr) { return reject(`AppleScript execution error: ${stderr}`); } resolve(stdout); }); }); }); }