Skip to main content
Glama

notes_create

Create formatted Apple Notes with markdown-like syntax for headings, bold, italic, lists, and links directly from macOS applications.

Instructions

[Apple Notes operations] Create a new note with optional formatting

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
titleYesTitle of the note
contentYesContent of the note, can include markdown-like syntax for formatting
formatNoFormatting options for the note content

Implementation Reference

  • The core handler function for the 'notes_create' tool. It extracts title, content, and format from arguments, generates formatted HTML content, and returns the AppleScript code to create a new note in the Apple Notes app.
    script: (args) => {
      const { title = "New Note", content = "", format = {} } = args;
      const htmlContent = generateNoteHtml(args);
      
      return `
        tell application "Notes"
          make new note with properties {body:"${htmlContent}", name:"${title}"}
        end tell
      `;
  • Input schema defining parameters for the notes_create tool: title (string), content (string), and optional format object with booleans for various markdown-like formatting options. Requires title and content.
    schema: {
      type: "object",
      properties: {
        title: {
          type: "string",
          description: "Title of the note"
        },
        content: {
          type: "string",
          description: "Content of the note, can include markdown-like syntax for formatting"
        },
        format: {
          type: "object",
          description: "Formatting options for the note content",
          properties: {
            headings: {
              type: "boolean",
              description: "Enable heading formatting (# Heading)"
            },
            bold: {
              type: "boolean",
              description: "Enable bold formatting (**text**)"
            },
            italic: {
              type: "boolean",
              description: "Enable italic formatting (*text*)"
            },
            underline: {
              type: "boolean",
              description: "Enable underline formatting (~text~)"
            },
            links: {
              type: "boolean",
              description: "Enable link formatting ([text](url))"
            },
            lists: {
              type: "boolean",
              description: "Enable list formatting (- item or 1. item)"
            }
          }
        }
      },
      required: ["title", "content"]
    }
  • Helper function that converts plain text or markdown-like content into formatted HTML based on the format options provided in args. Used by the notes_create handler to process note content.
    function generateNoteHtml(args: any): string {
      const {
        title = "New Note",
        content = "",
        format = {
          headings: false,
          bold: false,
          italic: false,
          underline: false,
          links: false,
          lists: false
        }
      } = args;
    
      // Process content based on format options
      let processedContent = content;
      
      // If content contains markdown-like syntax and formatting is enabled, convert it
      if (format.headings) {
        // Convert # Heading to <h1>Heading</h1>, ## Heading to <h2>Heading</h2>, etc.
        processedContent = processedContent.replace(/^# (.+)$/gm, '<h1>$1</h1>');
        processedContent = processedContent.replace(/^## (.+)$/gm, '<h2>$1</h2>');
        processedContent = processedContent.replace(/^### (.+)$/gm, '<h3>$1</h3>');
      }
      
      if (format.bold) {
        // Convert **text** or __text__ to <b>text</b>
        processedContent = processedContent.replace(/\*\*(.+?)\*\*/g, '<b>$1</b>');
        processedContent = processedContent.replace(/__(.+?)__/g, '<b>$1</b>');
      }
      
      if (format.italic) {
        // Convert *text* or _text_ to <i>text</i>
        processedContent = processedContent.replace(/\*(.+?)\*/g, '<i>$1</i>');
        processedContent = processedContent.replace(/_(.+?)_/g, '<i>$1</i>');
      }
      
      if (format.underline) {
        // Convert ~text~ to <u>text</u>
        processedContent = processedContent.replace(/~(.+?)~/g, '<u>$1</u>');
      }
      
      if (format.links) {
        // Convert [text](url) to <a href="url">text</a>
        processedContent = processedContent.replace(/\[(.+?)\]\((.+?)\)/g, '<a href="$2">$1</a>');
      }
      
      if (format.lists) {
        // Handle unordered lists
        // Look for lines starting with - or * and convert to <li> items
        const listItems = processedContent.match(/^[*-] (.+)$/gm);
        if (listItems) {
          let listHtml = '<ul>';
          for (const item of listItems) {
            const content = item.replace(/^[*-] /, '');
            listHtml += `<li>${content}</li>`;
          }
          listHtml += '</ul>';
          
          // Replace the original list items with the HTML list
          for (const item of listItems) {
            processedContent = processedContent.replace(item, '');
          }
          processedContent = processedContent.replace(/\n+/g, '\n') + listHtml;
        }
        
        // Handle ordered lists (1. Item)
        const orderedItems = processedContent.match(/^\d+\. (.+)$/gm);
        if (orderedItems) {
          let listHtml = '<ol>';
          for (const item of orderedItems) {
            const content = item.replace(/^\d+\. /, '');
            listHtml += `<li>${content}</li>`;
          }
          listHtml += '</ol>';
          
          // Replace the original list items with the HTML list
          for (const item of orderedItems) {
            processedContent = processedContent.replace(item, '');
          }
          processedContent = processedContent.replace(/\n+/g, '\n') + listHtml;
        }
      }
      
      // Wrap paragraphs in <p> tags if they aren't already wrapped in HTML tags
      const paragraphs = processedContent.split('\n\n');
      processedContent = paragraphs
        .map((p: string) => {
          if (p.trim() && !p.trim().startsWith('<')) {
            return `<p>${p}</p>`;
          }
          return p;
        })
        .join('\n');
      
      return processedContent;
    }
  • Registration of tool listing handler which dynamically generates tool names as '{category.name}_{script.name}', resulting in 'notes_create' for the notes category's create script. This is how the tool is exposed in MCP.
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: this.categories.flatMap((category) =>
        category.scripts.map((script) => ({
          name: `${category.name}_${script.name}`, // Changed from dot to underscore
          description: `[${category.description}] ${script.description}`,
          inputSchema: script.schema || {
            type: "object",
            properties: {},
          },
        })),
      ),
    }));
  • src/index.ts:12-35 (registration)
    Import and registration of the notesCategory, which contains the 'create' script, making it available to the framework for tool generation as 'notes_create'.
    import { notesCategory } from "./categories/notes.js";
    
    const server = new AppleScriptFramework({
      name: "applescript-server",
      version: "1.0.4",
      debug: false,
    });
    
    // Log startup information using stderr (server isn't connected yet)
    console.error(`[INFO] Starting AppleScript MCP server - PID: ${process.pid}`);
    
    // Add all categories
    console.error("[INFO] Registering categories...");
    server.addCategory(systemCategory);
    server.addCategory(calendarCategory);
    server.addCategory(finderCategory);
    server.addCategory(clipboardCategory);
    server.addCategory(notificationsCategory);
    server.addCategory(itermCategory);
    server.addCategory(mailCategory);
    server.addCategory(pagesCategory);
    server.addCategory(shortcutsCategory);
    server.addCategory(messagesCategory);
    server.addCategory(notesCategory);
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. While it mentions 'optional formatting,' it doesn't describe what happens after creation (e.g., where the note is stored, if it's immediately saved, permissions required, or error conditions). For a mutation tool with zero annotation coverage, this is insufficient.

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

Conciseness4/5

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

The description is a single, efficient sentence that gets straight to the point. It's appropriately sized for the tool's complexity, though it could be slightly more informative given the lack of annotations and output schema.

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

Completeness2/5

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

For a mutation tool with no annotations and no output schema, the description is inadequate. It doesn't explain what the tool returns, error handling, or behavioral nuances. The context signals show nested objects and required parameters, but the description doesn't address these complexities.

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?

Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description adds minimal value by mentioning 'optional formatting' which hints at the 'format' parameter, but doesn't provide additional context beyond what's in the schema descriptions.

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 action ('Create a new note') and resource ('note'), and specifies it's for Apple Notes operations. However, it doesn't differentiate from its sibling 'notes_createRawHtml' which presumably creates notes with raw HTML instead of markdown-like formatting.

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_createRawHtml' or other note-related tools. There's no mention of prerequisites, constraints, or typical use cases beyond the basic function.

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/joshrutkowski/applescript-mcp'

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