Skip to main content
Glama

Folder Contents

folders.contents
Read-only

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

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
notesYes
folderYes
subfoldersNo

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;
      }
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, indicating this is a safe read operation with limited scope. The description adds minimal behavioral context beyond this - it mentions listing contents but doesn't describe return format, pagination, error conditions, or performance characteristics. With annotations covering safety and scope, this is adequate but not rich.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that states the core functionality without any wasted words. It's front-loaded with the essential information and has perfect conciseness for this level of tool complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given that this tool has annotations covering safety/scope and an output schema exists (so return values are documented elsewhere), the description is minimally adequate. However, for a tool with 3 parameters (one required) and 0% schema coverage, the description should provide more parameter context and usage guidance to be truly complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the schema provides no parameter documentation. The description mentions 'folder path' which maps to the 'path' parameter, but doesn't explain the 'recursive' or 'limit' parameters. It adds some value by clarifying what 'path' represents, but doesn't compensate for the complete lack of documentation on the other two parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb ('List') and resources ('notes and subfolders') with the scope ('for a folder path'), making the purpose immediately understandable. However, it doesn't explicitly differentiate from sibling tools like 'notes.list' or 'notes.list_folders' which might have overlapping functionality.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives like 'notes.list' or 'notes.list_folders'. It states what the tool does but offers no context about appropriate use cases, prerequisites, or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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