From 20c2367e7689188263fd4bf866e50c8e9d9fb2e1 Mon Sep 17 00:00:00 2001 From: Vatsal Solanki Date: Sun, 1 Feb 2026 13:36:38 -0800 Subject: [PATCH 01/10] feat: display note IDs in CLI output Add note ID display to formatter output so users can easily find IDs for use with the `read` command: - formatNote(): show ID on folder line - formatSearchResult(): show ID on folder line - Update read command help text to clarify where to find IDs --- src/cli.ts | 2 +- src/formatter.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index 71943b8..2ce3f23 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -72,7 +72,7 @@ program program .command('read ') - .description('Read a note by ID') + .description('Read a note by ID (shown in search/recent output)') .action(async (id: string) => { try { const noteId = parseInt(id, 10); diff --git a/src/formatter.ts b/src/formatter.ts index 4bff8ce..137d5e5 100644 --- a/src/formatter.ts +++ b/src/formatter.ts @@ -23,8 +23,8 @@ export function formatNote(note: IndexedNote, showBody = false): string { const lockSuffix = note.isLocked ? chalk.red(' 🔒') : ''; lines.push(titlePrefix + chalk.bold.cyan(note.title || 'Untitled') + lockSuffix); - // Folder - lines.push(chalk.dim(`📁 ${note.folder}`)); + // Folder and ID + lines.push(chalk.dim(`📁 ${note.folder} | ID: ${note.id}`)); // Snippet if (note.snippet) { @@ -55,8 +55,8 @@ export function formatSearchResult( const highlightedTitle = highlightMatches(result.title || 'Untitled', query); lines.push(chalk.green('▶ ') + titlePrefix + chalk.bold.cyan(highlightedTitle) + lockSuffix); - // Folder - lines.push(chalk.dim(` 📁 ${result.folder}`)); + // Folder and ID + lines.push(chalk.dim(` 📁 ${result.folder} | ID: ${result.id}`)); // Highlighted snippet if (result.snippet) { From 152222ce8cad74a1a91f43b3fc702764d26e42f3 Mon Sep 17 00:00:00 2001 From: Vatsal Solanki Date: Sun, 1 Feb 2026 13:52:03 -0800 Subject: [PATCH 02/10] feat: add edit command to update existing notes Add ability to edit Apple Notes while preserving the original created timestamp. Supports editing by note ID or title. - Add editNote() function in applescript.ts - Add 'notes edit' CLI command with --body, --title, --folder options - Add edit_note MCP tool for Claude Code integration - Add /notes:edit slash command documentation Co-Authored-By: Claude Opus 4.5 --- commands/edit.md | 33 +++++++++++++++++++++ src/applescript.ts | 73 ++++++++++++++++++++++++++++++++++++++++++++++ src/cli.ts | 54 +++++++++++++++++++++++++++++++++- src/mcp.ts | 62 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 commands/edit.md diff --git a/commands/edit.md b/commands/edit.md new file mode 100644 index 0000000..410e7e0 --- /dev/null +++ b/commands/edit.md @@ -0,0 +1,33 @@ +--- +description: Edit an existing Apple Note +allowed-tools: Bash(notes:*) +argument-hint: --body "new content" [--folder "Folder"] +--- + +# Edit Note + +Edit an existing note in Apple Notes. The created timestamp is preserved. + +## Instructions + +1. Check if the notes CLI is installed: +```bash +command -v notes || pnpm add -g @cardmagic/notes +``` + +2. Edit the note: +```bash +notes edit $ARGUMENTS +``` + +## Examples + +- `/notes:edit 123 --body "Updated content"` - Edit note by ID +- `/notes:edit --title "Meeting Notes" --body "New agenda"` - Edit by title +- `/notes:edit --title "Todo" --body "New tasks" --folder "Work"` - Edit with folder disambiguation + +## Workflow + +1. Find the note: `notes search "keyword"` or `notes recent` +2. Read current content: `notes read ` +3. Edit the note: `notes edit --body "new content"` diff --git a/src/applescript.ts b/src/applescript.ts index 65b199f..607f05a 100644 --- a/src/applescript.ts +++ b/src/applescript.ts @@ -17,6 +17,18 @@ export interface DeleteNoteResult { name: string; } +export interface EditNoteOptions { + title: string; + body: string; + folder?: string; +} + +export interface EditNoteResult { + success: boolean; + name: string; + folder: string; +} + function escapeAppleScript(str: string): string { return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); } @@ -110,6 +122,67 @@ export function deleteNote(title: string, folder?: string): DeleteNoteResult { } } +export function editNote(options: EditNoteOptions): EditNoteResult { + const { title, body, folder } = options; + + const escapedTitle = escapeAppleScript(title); + const escapedBody = escapeAppleScript(body); + + let script: string; + const targetFolder = folder || 'Notes'; + + if (folder) { + const escapedFolder = escapeAppleScript(folder); + script = ` + tell application "Notes" + set targetFolder to folder "${escapedFolder}" + set matchingNotes to notes of targetFolder whose name is "${escapedTitle}" + if (count of matchingNotes) is 0 then + error "Note not found" + end if + set targetNote to item 1 of matchingNotes + set body of targetNote to "${escapedBody}" + return name of targetNote + end tell + `; + } else { + script = ` + tell application "Notes" + set matchingNotes to notes whose name is "${escapedTitle}" + if (count of matchingNotes) is 0 then + error "Note not found" + end if + set targetNote to item 1 of matchingNotes + set body of targetNote to "${escapedBody}" + return name of targetNote + end tell + `; + } + + try { + const result = execSync(`osascript -e '${script.replace(/'/g, "'\"'\"'")}'`, { + encoding: 'utf-8', + timeout: 30000, + }); + + return { + success: true, + name: result.trim(), + folder: targetFolder, + }; + } catch (error) { + const message = (error as Error).message; + if (message.includes('Note not found')) { + const folderInfo = folder ? ` in folder "${folder}"` : ''; + throw new Error(`Note "${title}" not found${folderInfo}.`); + } + if (message.includes('get folder')) { + throw new Error(`Folder "${folder}" not found. Use 'notes folders' to list available folders.`); + } + throw new Error(`Failed to edit note: ${message}`); + } +} + export function listNoteFolders(): string[] { const script = ` tell application "Notes" diff --git a/src/cli.ts b/src/cli.ts index 2ce3f23..cb241cd 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -23,7 +23,7 @@ import { formatIndexProgress, formatNote, } from './formatter.js'; -import { createNote, deleteNote } from './applescript.js'; +import { createNote, deleteNote, editNote } from './applescript.js'; const program = new Command(); @@ -224,6 +224,58 @@ program } }); +program + .command('edit [id]') + .description('Edit an existing note by ID or title') + .option('-t, --title ', 'Edit by title instead of ID') + .option('-b, --body ', 'New body content') + .option('-f, --folder ', 'Folder containing the note (for disambiguation)') + .action(async (id: string | undefined, options: { title?: string; body?: string; folder?: string }) => { + try { + if (!options.body) { + console.error('Error: --body is required'); + process.exit(1); + } + + let title: string; + let folder: string | undefined = options.folder; + + if (id) { + const noteId = parseInt(id, 10); + if (isNaN(noteId)) { + console.error('Invalid note ID'); + process.exit(1); + } + + const note = await getNoteById(noteId); + if (!note) { + console.error('Note not found'); + process.exit(1); + } + + title = note.title; + folder = folder || note.folder; + } else if (options.title) { + title = options.title; + } else { + console.error('Error: Either or --title is required'); + process.exit(1); + } + + const result = editNote({ + title, + body: options.body, + folder, + }); + console.log(`Updated note "${result.name}" in folder "${result.folder}"`); + } catch (error) { + console.error('Error:', (error as Error).message); + process.exit(1); + } finally { + closeConnections(); + } + }); + export function runCli(): void { program.parse(process.argv); } diff --git a/src/mcp.ts b/src/mcp.ts index a4a06fc..a784126 100644 --- a/src/mcp.ts +++ b/src/mcp.ts @@ -12,7 +12,7 @@ import { listFolders, getNoteStats, } from './searcher.js'; -import { createNote, deleteNote } from './applescript.js'; +import { createNote, deleteNote, editNote } from './applescript.js'; import type { IndexedNote, SearchResult } from './types.js'; function formatNoteForMcp(note: IndexedNote): string { @@ -204,6 +204,32 @@ export async function runMcpServer(): Promise { required: ['title'], }, }, + { + name: 'edit_note', + description: 'Edit an existing note in Apple Notes (preserves created timestamp)', + inputSchema: { + type: 'object', + properties: { + id: { + type: 'number', + description: 'Note ID to edit (from search/recent results)', + }, + title: { + type: 'string', + description: 'Edit by title instead of ID', + }, + body: { + type: 'string', + description: 'New body content for the note', + }, + folder: { + type: 'string', + description: 'Folder containing the note (for disambiguation when editing by title)', + }, + }, + required: ['body'], + }, + }, ], })); @@ -375,6 +401,40 @@ export async function runMcpServer(): Promise { }; } + case 'edit_note': { + const id = args?.id as number | undefined; + const titleArg = args?.title as string | undefined; + const body = args?.body as string; + let folder = args?.folder as string | undefined; + + let title: string; + + if (id !== undefined) { + const note = await getNoteById(id); + if (!note) { + return { + content: [{ type: 'text', text: `Note with ID ${id} not found.` }], + isError: true, + }; + } + title = note.title; + folder = folder || note.folder; + } else if (titleArg) { + title = titleArg; + } else { + return { + content: [{ type: 'text', text: 'Either id or title is required.' }], + isError: true, + }; + } + + const result = editNote({ title, body, folder }); + + return { + content: [{ type: 'text', text: `✅ Updated note "${result.name}" in folder "${result.folder}"` }], + }; + } + default: return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], From dd57c014fef58e5ae5afe2dcd19888e90a01038d Mon Sep 17 00:00:00 2001 From: Vatsal Solanki Date: Sun, 1 Feb 2026 14:11:55 -0800 Subject: [PATCH 03/10] preserve title --- src/applescript.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/applescript.ts b/src/applescript.ts index 607f05a..6cd1b19 100644 --- a/src/applescript.ts +++ b/src/applescript.ts @@ -126,7 +126,10 @@ export function editNote(options: EditNoteOptions): EditNoteResult { const { title, body, folder } = options; const escapedTitle = escapeAppleScript(title); - const escapedBody = escapeAppleScript(body); + // Apple Notes uses the first line of the body as the title, so we prepend the title + // as an HTML heading to preserve it when setting the body + const fullBody = `

${title}


${body}`; + const escapedBody = escapeAppleScript(fullBody); let script: string; const targetFolder = folder || 'Notes'; From 0b6e45364449bb7fc5ac8e3339faba46ebaa14f2 Mon Sep 17 00:00:00 2001 From: Vatsal Solanki Date: Sun, 8 Feb 2026 19:02:51 -0800 Subject: [PATCH 04/10] revert formatter change --- src/formatter.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/formatter.ts b/src/formatter.ts index 137d5e5..4bff8ce 100644 --- a/src/formatter.ts +++ b/src/formatter.ts @@ -23,8 +23,8 @@ export function formatNote(note: IndexedNote, showBody = false): string { const lockSuffix = note.isLocked ? chalk.red(' 🔒') : ''; lines.push(titlePrefix + chalk.bold.cyan(note.title || 'Untitled') + lockSuffix); - // Folder and ID - lines.push(chalk.dim(`📁 ${note.folder} | ID: ${note.id}`)); + // Folder + lines.push(chalk.dim(`📁 ${note.folder}`)); // Snippet if (note.snippet) { @@ -55,8 +55,8 @@ export function formatSearchResult( const highlightedTitle = highlightMatches(result.title || 'Untitled', query); lines.push(chalk.green('▶ ') + titlePrefix + chalk.bold.cyan(highlightedTitle) + lockSuffix); - // Folder and ID - lines.push(chalk.dim(` 📁 ${result.folder} | ID: ${result.id}`)); + // Folder + lines.push(chalk.dim(` 📁 ${result.folder}`)); // Highlighted snippet if (result.snippet) { From 1f7f9530199868f99dc4aa65ee95f0cff5776c6d Mon Sep 17 00:00:00 2001 From: Vatsal Solanki Date: Sun, 8 Feb 2026 19:04:37 -0800 Subject: [PATCH 05/10] revert more --- src/cli.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli.ts b/src/cli.ts index cb241cd..cd70f20 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -72,7 +72,7 @@ program program .command('read ') - .description('Read a note by ID (shown in search/recent output)') + .description('Read a note by ID') .action(async (id: string) => { try { const noteId = parseInt(id, 10); From 9d073cb6c770db5f1ed75c4ca59ea37170dea0f0 Mon Sep 17 00:00:00 2001 From: Vatsal Solanki Date: Sun, 8 Feb 2026 19:11:10 -0800 Subject: [PATCH 06/10] mcp.ts description --- src/mcp.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mcp.ts b/src/mcp.ts index a784126..4801487 100644 --- a/src/mcp.ts +++ b/src/mcp.ts @@ -224,10 +224,13 @@ export async function runMcpServer(): Promise { }, folder: { type: 'string', - description: 'Folder containing the note (for disambiguation when editing by title)', + description: 'Folder containing the note (optional and only for disambiguation when editing by title)', }, }, - required: ['body'], + anyOf: [ + { required: ['id', 'body'] }, + { required: ['title', 'body'] }, + ], }, }, ], From be8d0a19286b4e40501bcbe52bf44b5349b8031a Mon Sep 17 00:00:00 2001 From: Vatsal Solanki Date: Sun, 8 Feb 2026 19:31:27 -0800 Subject: [PATCH 07/10] buildNoteOperationScript --- src/applescript.ts | 112 ++++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/src/applescript.ts b/src/applescript.ts index 6cd1b19..228756c 100644 --- a/src/applescript.ts +++ b/src/applescript.ts @@ -33,6 +33,50 @@ function escapeAppleScript(str: string): string { return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); } +/** + * Builds an AppleScript that finds a note by title and performs an operation on it. + * Handles folder scoping when provided. + * + * @param title - The note title to search for + * @param operation - AppleScript code to execute on the found note. The note is available + * as the `targetNote` variable. Should include a `return` statement. + * Example: `delete targetNote\nreturn "deleted"` + * @param folder - Optional folder name to scope the search + */ +function buildNoteOperationScript( + title: string, + operation: string, + folder?: string +): string { + const escapedTitle = escapeAppleScript(title); + + if (folder) { + const escapedFolder = escapeAppleScript(folder); + return ` + tell application "Notes" + set targetFolder to folder "${escapedFolder}" + set matchingNotes to notes of targetFolder whose name is "${escapedTitle}" + if (count of matchingNotes) is 0 then + error "Note not found" + end if + set targetNote to item 1 of matchingNotes + ${operation} + end tell + `; + } else { + return ` + tell application "Notes" + set matchingNotes to notes whose name is "${escapedTitle}" + if (count of matchingNotes) is 0 then + error "Note not found" + end if + set targetNote to item 1 of matchingNotes + ${operation} + end tell + `; + } +} + export function createNote(options: CreateNoteOptions): CreateNoteResult { const { title, body, folder = 'Notes' } = options; @@ -70,34 +114,12 @@ export function createNote(options: CreateNoteOptions): CreateNoteResult { export function deleteNote(title: string, folder?: string): DeleteNoteResult { const escapedTitle = escapeAppleScript(title); + const operation = ` + delete targetNote + return "${escapedTitle}" + `; - let script: string; - - if (folder) { - const escapedFolder = escapeAppleScript(folder); - script = ` - tell application "Notes" - set targetFolder to folder "${escapedFolder}" - set matchingNotes to notes of targetFolder whose name is "${escapedTitle}" - if (count of matchingNotes) is 0 then - error "Note not found" - end if - delete item 1 of matchingNotes - return "${escapedTitle}" - end tell - `; - } else { - script = ` - tell application "Notes" - set matchingNotes to notes whose name is "${escapedTitle}" - if (count of matchingNotes) is 0 then - error "Note not found" - end if - delete item 1 of matchingNotes - return "${escapedTitle}" - end tell - `; - } + const script = buildNoteOperationScript(title, operation, folder); try { const result = execSync(`osascript -e '${script.replace(/'/g, "'\"'\"'")}'`, { @@ -125,42 +147,18 @@ export function deleteNote(title: string, folder?: string): DeleteNoteResult { export function editNote(options: EditNoteOptions): EditNoteResult { const { title, body, folder } = options; - const escapedTitle = escapeAppleScript(title); // Apple Notes uses the first line of the body as the title, so we prepend the title // as an HTML heading to preserve it when setting the body const fullBody = `

${title}


${body}`; const escapedBody = escapeAppleScript(fullBody); - - let script: string; const targetFolder = folder || 'Notes'; - if (folder) { - const escapedFolder = escapeAppleScript(folder); - script = ` - tell application "Notes" - set targetFolder to folder "${escapedFolder}" - set matchingNotes to notes of targetFolder whose name is "${escapedTitle}" - if (count of matchingNotes) is 0 then - error "Note not found" - end if - set targetNote to item 1 of matchingNotes - set body of targetNote to "${escapedBody}" - return name of targetNote - end tell - `; - } else { - script = ` - tell application "Notes" - set matchingNotes to notes whose name is "${escapedTitle}" - if (count of matchingNotes) is 0 then - error "Note not found" - end if - set targetNote to item 1 of matchingNotes - set body of targetNote to "${escapedBody}" - return name of targetNote - end tell - `; - } + const operation = ` + set body of targetNote to "${escapedBody}" + return name of targetNote + `; + + const script = buildNoteOperationScript(title, operation, folder); try { const result = execSync(`osascript -e '${script.replace(/'/g, "'\"'\"'")}'`, { From 6be7a392ee93bdf71e261e9f2cdfb104cbed6a69 Mon Sep 17 00:00:00 2001 From: Vatsal Solanki Date: Sun, 8 Feb 2026 19:37:33 -0800 Subject: [PATCH 08/10] escape html --- src/applescript.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/applescript.ts b/src/applescript.ts index 228756c..2dd84a3 100644 --- a/src/applescript.ts +++ b/src/applescript.ts @@ -33,6 +33,15 @@ function escapeAppleScript(str: string): string { return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); } +function escapeHtml(str: string): string { + return str + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + /** * Builds an AppleScript that finds a note by title and performs an operation on it. * Handles folder scoping when provided. @@ -149,7 +158,7 @@ export function editNote(options: EditNoteOptions): EditNoteResult { // Apple Notes uses the first line of the body as the title, so we prepend the title // as an HTML heading to preserve it when setting the body - const fullBody = `

${title}


${body}`; + const fullBody = `

${escapeHtml(title)}


${escapeHtml(body)}`; const escapedBody = escapeAppleScript(fullBody); const targetFolder = folder || 'Notes'; From 470c50553509b51f6a7bfbafab0750348f5af319 Mon Sep 17 00:00:00 2001 From: Vatsal Solanki Date: Sun, 8 Feb 2026 19:56:29 -0800 Subject: [PATCH 09/10] escaping --- src/applescript.ts | 49 ++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/applescript.ts b/src/applescript.ts index 2dd84a3..54848f8 100644 --- a/src/applescript.ts +++ b/src/applescript.ts @@ -29,8 +29,18 @@ export interface EditNoteResult { folder: string; } +/** + * Escapes a string for safe use in AppleScript string literals. + * Handles backslashes, quotes, newlines, and control characters. + */ function escapeAppleScript(str: string): string { - return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + return str + .replace(/\\/g, '\\\\') // Backslash must be first + .replace(/"/g, '\\"') // Double quotes + .replace(/\n/g, '\\n') // Newlines + .replace(/\r/g, '\\r') // Carriage returns + .replace(/\t/g, '\\t') // Tabs + .replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F]/g, ''); // Strip other control chars } function escapeHtml(str: string): string { @@ -42,6 +52,23 @@ function escapeHtml(str: string): string { .replace(/'/g, '''); } +/** + * Executes an AppleScript by passing it via stdin to avoid shell injection. + * This is more secure than using -e flag with shell escaping. + * + * @param script - The complete AppleScript to execute + * @returns The output from the script + * @throws Error if execution fails + */ +function executeAppleScript(script: string): string { + return execSync('osascript -', { + input: script, + encoding: 'utf-8', + timeout: 30000, + maxBuffer: 10 * 1024 * 1024, // 10MB for large note bodies + }); +} + /** * Builds an AppleScript that finds a note by title and performs an operation on it. * Handles folder scoping when provided. @@ -102,10 +129,7 @@ export function createNote(options: CreateNoteOptions): CreateNoteResult { `; try { - const result = execSync(`osascript -e '${script.replace(/'/g, "'\"'\"'")}'`, { - encoding: 'utf-8', - timeout: 30000, - }); + const result = executeAppleScript(script); return { success: true, @@ -131,10 +155,7 @@ export function deleteNote(title: string, folder?: string): DeleteNoteResult { const script = buildNoteOperationScript(title, operation, folder); try { - const result = execSync(`osascript -e '${script.replace(/'/g, "'\"'\"'")}'`, { - encoding: 'utf-8', - timeout: 30000, - }); + const result = executeAppleScript(script); return { success: true, @@ -170,10 +191,7 @@ export function editNote(options: EditNoteOptions): EditNoteResult { const script = buildNoteOperationScript(title, operation, folder); try { - const result = execSync(`osascript -e '${script.replace(/'/g, "'\"'\"'")}'`, { - encoding: 'utf-8', - timeout: 30000, - }); + const result = executeAppleScript(script); return { success: true, @@ -206,10 +224,7 @@ export function listNoteFolders(): string[] { `; try { - const result = execSync(`osascript -e '${script.replace(/'/g, "'\"'\"'")}'`, { - encoding: 'utf-8', - timeout: 30000, - }); + const result = executeAppleScript(script); return result.trim().split('\n').filter(Boolean); } catch (error) { From e94185239a00a61334c8944df7cbf5d30cfc735f Mon Sep 17 00:00:00 2001 From: Vatsal Solanki Date: Sun, 8 Feb 2026 20:13:49 -0800 Subject: [PATCH 10/10] nesting --- src/cli.ts | 13 ++++++++----- src/mcp.ts | 17 ++++++++++------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index cd70f20..354a398 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -226,7 +226,7 @@ program program .command('edit [id]') - .description('Edit an existing note by ID or title') + .description('Edit an existing note by ID or title (ID takes precedence if both provided)') .option('-t, --title ', 'Edit by title instead of ID') .option('-b, --body ', 'New body content') .option('-f, --folder ', 'Folder containing the note (for disambiguation)') @@ -237,9 +237,15 @@ program process.exit(1); } + if (!id && !options.title) { + console.error('Error: Either or --title is required'); + process.exit(1); + } + let title: string; let folder: string | undefined = options.folder; + // ID takes precedence if both are provided if (id) { const noteId = parseInt(id, 10); if (isNaN(noteId)) { @@ -255,11 +261,8 @@ program title = note.title; folder = folder || note.folder; - } else if (options.title) { - title = options.title; } else { - console.error('Error: Either or --title is required'); - process.exit(1); + title = options.title!; } const result = editNote({ diff --git a/src/mcp.ts b/src/mcp.ts index 4801487..9fc3545 100644 --- a/src/mcp.ts +++ b/src/mcp.ts @@ -206,7 +206,7 @@ export async function runMcpServer(): Promise { }, { name: 'edit_note', - description: 'Edit an existing note in Apple Notes (preserves created timestamp)', + description: 'Edit an existing note in Apple Notes (preserves created timestamp). If both id and title are provided, id takes precedence.', inputSchema: { type: 'object', properties: { @@ -410,8 +410,16 @@ export async function runMcpServer(): Promise { const body = args?.body as string; let folder = args?.folder as string | undefined; + if (id === undefined && !titleArg) { + return { + content: [{ type: 'text', text: 'Either id or title is required.' }], + isError: true, + }; + } + let title: string; + // ID takes precedence if both are provided if (id !== undefined) { const note = await getNoteById(id); if (!note) { @@ -422,13 +430,8 @@ export async function runMcpServer(): Promise { } title = note.title; folder = folder || note.folder; - } else if (titleArg) { - title = titleArg; } else { - return { - content: [{ type: 'text', text: 'Either id or title is required.' }], - isError: true, - }; + title = titleArg!; } const result = editNote({ title, body, folder });