folders.contents
List notes and subfolders within an Apple Notes folder to organize and access content. Specify a folder path to retrieve items, with options for recursive listing and result limits.
Instructions
List notes and subfolders for a folder path.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | ||
| recursive | No | ||
| limit | No |
Implementation Reference
- src/notes.ts:364-382 (handler)The listFolderContents function that implements the core logic for the 'folders.contents' tool. It finds a folder by path, retrieves its notes, and optionally retrieves subfolders when recursive mode is enabled.
export async function listFolderContents(params: { path: string; recursive?: boolean; limit?: number }): Promise<{ folder: FolderInfo; notes: NoteInfo[]; subfolders?: FolderInfo[] }> { const { path, recursive = false, limit = 500 } = params; const script = ` const Notes = Application('Notes'); function find(p){ const parts=String(p).split('/').filter(Boolean); let parent=Notes.defaultAccount(); let folder=null; for(const part of parts){ folder=null; const list=parent.folders(); for (let i=0;i<list.length;i++){ if (String(list[i].name())===part){ folder=list[i]; break; } } if(!folder) return null; parent=folder; } return folder; } const f = find("${esc(path)}"); if (!f) { JSON.stringify(null); return; } const folder = { id: f.id(), name: f.name(), account: f.account().name() }; const notes = f.notes().map(n => ({ id: n.id(), name: n.name(), modificationDate: (n.modificationDate() ? n.modificationDate().toISOString() : undefined), folderId: f.id() })); let subfolders = []; if (${recursive ? 'true' : 'false'}) { subfolders = f.folders().map(sf => ({ id: sf.id(), name: sf.name(), account: sf.account().name() })); } JSON.stringify({ folder, notes: notes.slice(0, ${limit}), subfolders }); `; const out = await runJxa<{ folder: FolderInfo; notes: NoteInfo[]; subfolders?: FolderInfo[] } | null>(script); if (!out) throw new Error('folder not found'); return out; } - src/index.ts:194-207 (registration)Registration of the 'folders.contents' tool with MCP server, including input schema (path, recursive, limit), output schema, and annotations.
server.registerTool( "folders.contents", { title: "Folder Contents", description: "List notes and subfolders for a folder path.", inputSchema: z.object({ path: z.string(), recursive: z.boolean().optional(), limit: z.number().int().positive().max(2000).optional() }), outputSchema: z.object({ folder: ZFolder, notes: z.array(ZNoteInfo), subfolders: z.array(ZFolder).optional() }), annotations: { readOnlyHint: true, openWorldHint: false }, }, async (args) => { const out = await listFolderContents({ path: args.path, recursive: args.recursive, limit: args.limit }); return { content: [], structuredContent: out }; } ); - src/notes.ts:3-18 (schema)Type definitions for FolderInfo and NoteInfo interfaces that define the structure of data returned by the folders.contents tool.
export interface FolderInfo { id: string; name: string; account: string; } export interface NoteInfo { id: string; name: string; modificationDate?: string; folderId?: string; } export interface NoteDetail extends NoteInfo { body: string; } - src/jxa.ts:9-43 (helper)The runJxa helper function that executes JavaScript for Automation (JXA) scripts via osascript, used by listFolderContents to interact with Apple Notes.
export async function runJxa<T = unknown>(script: string): Promise<T> { const proc = spawn("osascript", ["-l", "JavaScript"], { stdio: ["pipe", "pipe", "pipe"], }); const chunks: Buffer[] = []; const errChunks: Buffer[] = []; proc.stdout.on("data", (d) => chunks.push(Buffer.from(d))); proc.stderr.on("data", (d) => errChunks.push(Buffer.from(d))); // Write script directly; JXA prints the value of the last expression. proc.stdin.write(script); proc.stdin.end(); const code: number = await new Promise((resolve, reject) => { proc.on("error", reject); proc.on("close", (code) => resolve(code ?? 1)); }); const stdout = Buffer.concat(chunks).toString("utf8").trim(); const stderr = Buffer.concat(errChunks).toString("utf8").trim(); if (code !== 0) { throw new JxaError(`osascript exited with code ${code}`, stderr || stdout); } // Allow scripts to print plain text or JSON. try { return JSON.parse(stdout) as T; } catch { // If not JSON, return as any string return stdout as unknown as T; } }