Skip to main content
Glama

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
NameRequiredDescriptionDefault
pathYes
recursiveNo
limitNo

Implementation Reference

  • 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 };
      }
    );
  • 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;
    }
  • 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;
      }
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/renatoaraujo/apple-notes-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server