Scan Notes
scan_notesRetrieve metadata and text previews from multiple Apple Notes in bulk. Filter by folder or control pagination for efficient access.
Instructions
Bulk scan notes returning metadata and a text preview for each.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| folder | No | Filter by folder name. Omit to scan all notes. | |
| limit | No | Max number of notes to return (default: 100) | |
| offset | No | Number of notes to skip for pagination (default: 0) | |
| previewLength | No | Preview text length in characters (default: 300) |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| total | Yes | ||
| offset | Yes | ||
| returned | Yes | ||
| notes | Yes |
Implementation Reference
- src/notes/tools.ts:421-487 (registration)Registration of the scan_notes tool on the MCP server with input/output schemas, description, and handler.
server.registerTool( "scan_notes", { title: "Scan Notes", description: "Bulk scan notes returning metadata and a text preview for each. Supports pagination via offset. Optionally filter by folder. Use this to get an overview before organizing.", inputSchema: { folder: z.string().max(500).optional().describe("Filter by folder name. Omit to scan all notes."), limit: z .number() .int() .min(1) .max(LIMITS.NOTES_BULK_SCAN) .optional() .default(100) .describe("Max number of notes to return (default: 100)"), offset: z .number() .int() .min(0) .optional() .default(0) .describe("Number of notes to skip for pagination (default: 0)"), previewLength: z .number() .int() .min(1) .max(5000) .optional() .default(300) .describe("Preview text length in characters (default: 300)"), }, outputSchema: { total: z.number(), offset: z.number(), returned: z.number(), notes: z.array( z.object({ id: z.string(), name: z.string(), folder: z.string(), creationDate: z.string(), modificationDate: z.string(), preview: z.string(), charCount: z.number(), shared: z.boolean(), }), ), }, annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false, }, }, async ({ folder, limit, offset, previewLength }) => { try { const result = await runJxa<ScanResult>(scanNotesScript(limit, previewLength, offset, folder)); result.notes = filterSharedAccess(result.notes, config, "notes"); result.returned = result.notes.length; return okUntrustedLinkedStructured("scan_notes", result); } catch (e) { return errJxaFor("scan notes", e); } }, ); - src/notes/tools.ts:477-487 (handler)The async handler function for scan_notes. It calls scanNotesScript via JXA to bulk scan notes, filters shared access, and returns structured results.
async ({ folder, limit, offset, previewLength }) => { try { const result = await runJxa<ScanResult>(scanNotesScript(limit, previewLength, offset, folder)); result.notes = filterSharedAccess(result.notes, config, "notes"); result.returned = result.notes.length; return okUntrustedLinkedStructured("scan_notes", result); } catch (e) { return errJxaFor("scan notes", e); } }, ); - src/notes/scripts.ts:273-330 (helper)The scanNotesScript function generates a JXA (JavaScript for Automation) script string that scans Apple Notes with pagination (limit/offset), optional folder filtering, and preview text extraction with character count.
export function scanNotesScript(limit: number, previewLength: number, offset: number, folder?: string): string { if (folder) { return ` const Notes = Application('Notes'); const folders = Notes.folders.whose({name: '${esc(folder)}'})(); if (folders.length === 0) throw new Error('Folder not found: ${esc(folder)}'); const f = folders[0]; const names = f.notes.name(); const ids = f.notes.id(); const creationDates = f.notes.creationDate(); const modificationDates = f.notes.modificationDate(); const shareds = f.notes.shared(); const start = Math.min(${offset}, names.length); const end = Math.min(start + ${limit}, names.length); const result = []; for (let i = start; i < end; i++) { const pt = f.notes[i].plaintext(); result.push({ id: ids[i], name: names[i], folder: '${esc(folder)}', creationDate: creationDates[i].toISOString(), modificationDate: modificationDates[i].toISOString(), preview: pt.substring(0, ${previewLength}), charCount: pt.length, shared: shareds[i] }); } JSON.stringify({total: names.length, offset: start, returned: result.length, notes: result}); `; } return ` const Notes = Application('Notes'); const names = Notes.notes.name(); const ids = Notes.notes.id(); const creationDates = Notes.notes.creationDate(); const modificationDates = Notes.notes.modificationDate(); const containers = Notes.notes.container(); const shareds = Notes.notes.shared(); const start = Math.min(${offset}, names.length); const end = Math.min(start + ${limit}, names.length); const result = []; for (let i = start; i < end; i++) { const pt = Notes.notes[i].plaintext(); result.push({ id: ids[i], name: names[i], folder: containers[i].name(), creationDate: creationDates[i].toISOString(), modificationDate: modificationDates[i].toISOString(), preview: pt.substring(0, ${previewLength}), charCount: pt.length, shared: shareds[i] }); } JSON.stringify({total: names.length, offset: start, returned: result.length, notes: result}); `; } - src/notes/tools.ts:427-468 (schema)Input and output schemas for the scan_notes tool, defining parameters (folder, limit, offset, previewLength) and return shape (total, offset, returned, notes array with metadata + preview + charCount).
inputSchema: { folder: z.string().max(500).optional().describe("Filter by folder name. Omit to scan all notes."), limit: z .number() .int() .min(1) .max(LIMITS.NOTES_BULK_SCAN) .optional() .default(100) .describe("Max number of notes to return (default: 100)"), offset: z .number() .int() .min(0) .optional() .default(0) .describe("Number of notes to skip for pagination (default: 0)"), previewLength: z .number() .int() .min(1) .max(5000) .optional() .default(300) .describe("Preview text length in characters (default: 300)"), }, outputSchema: { total: z.number(), offset: z.number(), returned: z.number(), notes: z.array( z.object({ id: z.string(), name: z.string(), folder: z.string(), creationDate: z.string(), modificationDate: z.string(), preview: z.string(), charCount: z.number(), shared: z.boolean(), }), ), - src/shared/constants.ts:177-178 (helper)Constant LIMITS.NOTES_BULK_SCAN = 500, which is the max limit enforced in the scan_notes input schema.
/** Max notes for scan_notes bulk operation */ NOTES_BULK_SCAN: 500,