Apple Notes MCP

Local stdio server for reading, searching, editing, organising and exporting Apple Notes on a Mac

By Rob Sweet (individual) TypeScript Local · stdio Productivity

62 popularity score ★ 180 ↓ 3.0k/wk

Connect your client

Local (npm)

Add in one click

Opens the app, which asks before it adds the server.

Or add it by command or config

Claude CodeTerminal
claude mcp add apple-notes -- npx -y apple-notes-mcp@2.15.1
Claude Desktopclaude_desktop_config.json
{
  "mcpServers": {
    "apple-notes": {
      "command": "npx",
      "args": [
        "-y",
        "apple-notes-mcp@2.15.1"
      ]
    }
  }
}
Cursor~/.cursor/mcp.json
{
  "mcpServers": {
    "apple-notes": {
      "command": "npx",
      "args": [
        "-y",
        "apple-notes-mcp@2.15.1"
      ]
    }
  }
}
VS Code.vscode/mcp.json
{
  "servers": {
    "apple-notes": {
      "type": "stdio",
      "command": "npx",
      "args": [
        "-y",
        "apple-notes-mcp@2.15.1"
      ]
    }
  }
}
Codex~/.codex/config.toml
[mcp_servers.apple-notes]
command = "npx"
args = ["-y", "apple-notes-mcp@2.15.1"]
Gemini CLITerminal
gemini mcp add --scope user apple-notes npx -- -y apple-notes-mcp@2.15.1
Local (from source)

Add in one click

Opens the app, which asks before it adds the server. Then replace each <NAME> in its settings with your value.

Or add it by command or config

FirstTerminal
git clone --branch v2.15.1 https://github.com/sweetrb/apple-notes-mcp.git
Claude CodeTerminal
claude mcp add apple-notes -- node '<ABSOLUTE_PATH_TO_CLONE>/build/index.js'
Claude Desktopclaude_desktop_config.json
{
  "mcpServers": {
    "apple-notes": {
      "command": "node",
      "args": [
        "<ABSOLUTE_PATH_TO_CLONE>/build/index.js"
      ]
    }
  }
}
Cursor~/.cursor/mcp.json
{
  "mcpServers": {
    "apple-notes": {
      "command": "node",
      "args": [
        "<ABSOLUTE_PATH_TO_CLONE>/build/index.js"
      ]
    }
  }
}
VS Code.vscode/mcp.json
{
  "servers": {
    "apple-notes": {
      "type": "stdio",
      "command": "node",
      "args": [
        "<ABSOLUTE_PATH_TO_CLONE>/build/index.js"
      ]
    }
  }
}
Codex~/.codex/config.toml
[mcp_servers.apple-notes]
command = "node"
args = ["<ABSOLUTE_PATH_TO_CLONE>/build/index.js"]
Gemini CLITerminal
gemini mcp add --scope user apple-notes node -- '<ABSOLUTE_PATH_TO_CLONE>/build/index.js'

Settings 12

APPLE_NOTES_MCP_TIMEOUT_MS Optional: Total time for a Notes call, retries included. A write's timeoutSeconds overrides it. Default 30000. For AppleScript calls.
APPLE_NOTES_MCP_MAX_RETRIES Optional: Retries for transient errors on read-only calls. Writes run once. Default 2. For read tools.
APPLE_NOTES_MCP_MAX_BUFFER Optional: Most bytes kept from one AppleScript call (64 MB). Default 67108864.
APPLE_NOTES_MCP_MAX_ATTACHMENT_BYTES Optional: Largest attachment returned inline (25 MB). Default 26214400. For fetch-attachment.
APPLE_NOTES_MCP_MAX_INLINE_IMAGE_BYTES Optional: Largest image kept inline per image. Bigger ones become placeholders. Default 262144. For get-note-content.
APPLE_NOTES_MCP_EXPORT_MAX_BYTES Optional: Size cap on exports (8 MB). Inline Markdown export gets half. Default 8388608. For export-notes-json, export-notes-markdown.
APPLE_NOTES_MCP_BLOCKS_MAX_BYTES Optional: Page size cap for block, paragraph and link listings (4 MB). Default 4194304. For get-note-blocks, list-note-paragraphs, list-note-links.
APPLE_NOTES_MCP_CONFIG_FILE Optional: JSON settings file. Only string values fill settings left unset. Brokered servers skip it. Default ~/Library/Application Support/apple-notes-mcp/config.json.
APPLE_NOTES_MCP_ENABLE_PRIVATE Optional: Set to 1 to turn on the private helper, which you build with setup --native-helper. Any other value leaves it off. Not passed to a brokered server. For native-note-state, native-checklist-state.
APPLE_NOTES_MCP_ALLOW_PRIVATE_CONTENT_PATHS Optional: Set to 1 to let file-reading tools open hidden folders and ~/Library outside iCloud Drive. Not passed to a brokered server. For create-note contentPath.
APPLE_NOTES_MCP_PUBLIC_HELPER_TIMEOUT_MS Optional: Time limit for one public helper call. Default 30000. For drawing and audio tools.
APPLE_NOTES_MCP_PRIVATE_HELPER_TIMEOUT_MS Optional: Time limit for one private helper call. Default 20000. For private helper.

What it does

Apple Notes MCP runs on your Mac as a stdio server and gives an assistant read and write access to the Notes library there. Plain reads and edits go through AppleScript. A set of signed Shortcuts, which you import once, handles the things AppleScript cannot reach, such as native tags, checklists, tables and pinning. With Full Disk Access it also reads the Notes database directly, read-only, which is what powers the boolean query tool, link listing and checklist state.

The README documents 89 tools. Most of them write. Edits and deletes need the content hash from a recent read, so a note that changed since the assistant looked at it is left alone and reported as a conflict. Deleting a note moves it to Recently Deleted, and the server refuses to delete a note that is already there because that would be permanent.

Recent releases add an opt-in permission broker (`apple-notes-mcp setup --broker`). It is a small signed app with a LaunchAgent that holds the Full Disk Access and Automation grants, so replacing Node no longer means granting them again. The client side only relays bytes over a local Unix socket. In src/services/brokerProxy.ts the relay waits 5 seconds for the broker and then quietly runs the server in-process. Only ten numeric settings cross that socket: the TIMEOUT_MS, MAX_RETRIES, RETRY_DELAY_MS, MAX_BUFFER, MAX_ATTACHMENT_BYTES, MAX_INLINE_IMAGE_BYTES, EXPORT_MAX_BYTES and BLOCKS_MAX_BYTES limits plus the two helper timeouts, all with the APPLE_NOTES_MCP_ prefix. Anything else you set for a brokered session is dropped, and the brokered server ignores the JSON settings file too.

What it needs

RuntimeNode.js 20+
Runs onmacOS
Apple NotesFreeat least one Notes account set up on the Mac
Also
  • Notes.app
  • Full Disk Access for the database tools
  • Xcode Command Line Tools for the optional helpers

Grant the launching app macOS Automation access to Notes. Full Disk Access goes to the responsible process: the Node binary under Claude Desktop, or Apple Notes MCP Broker.app when the broker is in use. For native tags, checklists, tables and pinning, run `npx -y apple-notes-mcp@2.15.1 setup`, import the Shortcuts and run each one once in Shortcuts.app. Drawing decoding and audio transcription need the helper built by `setup --public-helper`. `setup --broker` installs the broker as the LaunchAgent `apple-notes-mcp.broker` and needs a Node runtime linked only to system libraries. `--sign-identity` signs it with your own identity. Signed ad hoc, it needs fresh grants after a rebuild.

Limits

  • Platform macOS only, run on the Mac that holds the library. There is no HTTP, SSE or OAuth endpoint, so cloud-hosted clients cannot reach it.
  • Not covered Without Full Disk Access the query, link, checklist-state and other database tools are unavailable. Treat that as missing data, not an empty result.
  • Not covered Password-protected notes cannot be read.
  • Limit A Shortcut-based write can stall on an unanswered consent prompt, and after a timeout the write may already have happened.
  • Size cap fetch-attachment returns at most 25 MB inline by default (APPLE_NOTES_MCP_MAX_ATTACHMENT_BYTES). save-attachment writes to disk with no such cap.
  • Size cap export-notes-markdown keeps inline output to half of APPLE_NOTES_MCP_EXPORT_MAX_BYTES, which is 8 MB by default.
  • Limit A brokered session loads neither the public nor the private helper. Set APPLE_NOTES_MCP_BROKER=off for that client before using drawing, audio or private-state tools.
  • Limit If the broker does not answer within 5 seconds the server runs in-process, under the client's own grants (src/services/brokerProxy.ts).
  • Limit Broker setup refuses a Node runtime linked to libraries outside /usr/lib or /System/Library, which rules out common Homebrew builds.
  • Limit The README pins every command to 2.15.1, the release it calls audited, behind the published one. The Codex plugin runs npx without a pinned version.

macOS only, and it must run on the Mac holding the library: it has no HTTP, SSE or OAuth endpoint, so cloud-only clients cannot use it. Without Full Disk Access the query, checklist-state, link, transcript and other database tools are unavailable, and an unavailable feature must not be read as an empty result. Password-protected notes cannot be read. A native Shortcut write can stall on an unanswered consent prompt; after a timeout the write may already have happened. `fetch-attachment` returns at most 25 MB inline by default (`APPLE_NOTES_MCP_MAX_ATTACHMENT_BYTES`). The private helper (`native-note-state`) relies on Apple's private API and is off unless `APPLE_NOTES_MCP_ENABLE_PRIVATE=1`.

Who it suits

Suits someone who keeps notes in Apple Notes and wants Claude Code, Claude Desktop, Codex or another local stdio client to search, file, edit and export them, tags and checklists included. Plan on some setup: macOS permissions, Shortcut imports and Full Disk Access for much of it. If Node changes under you often, the broker saves repeat grants, at the cost of letting any process running as your user use them. It is a community project with no tie to Apple. Look elsewhere if your assistant runs in the cloud or on another OS, or if you only want reads, since many tools write and you would have to rely on your client's tool approval.

Tools 60 of 89

38 only read, 19 change things, 3 can delete or overwrite. A ? marks an optional parameter.

create-note changes
Make a note from plain text, HTML, Markdown or a local file in a chosen folder or accounttitle: stringcontent?: stringcontentPath?: stringformat?: enummarkdownRoute?: enumtags?: arrayfolder?: stringaccount?: stringtimeoutSeconds?: integer
search-notes
Find notes by title, or by body text when searchContent is setquery: stringsearchContent?: booleanaccount?: stringfolder?: stringmodifiedSince?: stringlimit?: integerincludeWordCount?: boolean
query-notes
Run a boolean query over text, folder, tag, attachment, checklist and date fields (Full Disk Access)query: stringlimit?: integerscanLimit?: integerincludeDeleted?: booleanincludeWordCount?: boolean
get-note-content
Read a note's full body and the content hash later edits must quoteid?: stringtitle?: stringaccount?: stringtimeoutSeconds?: integer
get-note-plaintext
Read a note's body as plain textid?: stringtitle?: stringaccount?: string
get-note-details
Look up a note by title and read its dates, sharing and lock state
get-note-markdown
Read one note converted to Markdownid?: stringtitle?: stringaccount?: string
get-note-link
Get a link that opens the note in Notes.appid?: stringtitle?: stringaccount?: string
get-native-objects
Read a note's checklist items, native tags and tables from the database
get-note-tables
Read a note's native tables page by pageid: stringoffset?: integerlimit?: integermaxBytes?: integer
get-note-blocks
Read a note's body as typed blocks, one page at a timeid: stringoffset?: integerlimit?: integer
get-note-structure
Outline a note's headings and blocks, with their text if askedid: stringincludeText?: boolean
list-note-paragraphs
List a note's paragraphs with their positions for linkinglinkableOnly?: booleanoffset?: integerlimit?: integerrecordAnchors?: boolean
get-paragraph-link
Get a link to one paragraph, picked by text or block indexcontains?: stringmatch?: stringblockIndex?: integeroccurrence?: integerrecordAnchor?: boolean
create-paragraph-anchor changes
Save an anchor to a paragraph so its link survives editscontains?: stringmatch?: stringblockIndex?: integeroccurrence?: integer
resolve-paragraph-anchor
Find where a saved anchor points now, with a confidence scoreanchorId: stringminConfidence?: numberrefresh?: boolean
list-paragraph-anchors
List saved paragraph anchors, optionally for one notenoteIdentifier: anyoffset?: integerlimit?: integer
get-paragraph-anchor
Read one saved anchor record
prune-paragraph-anchors deletes
Remove anchors that no longer resolve (dry run by default)anchorIds?: arraynoteIdentifier: anystatuses: enumdryRun?: boolean
list-note-links
List the note links and web links in one note or a folderid?: anyaccount?: stringfolder?: stringincludeSubfolders?: booleanincludeInline?: booleankinds?: enumoffset?: integerlimit?: integer
list-native-tags
List the native tags in use, optionally within an account or folderaccount?: stringfolder?: string
update-note deletes
Replace a note's title or body, guarded by its content hashid: stringexpectedContentHash: stringallowLinkChanges?: booleannewTitle?: stringnewContent: stringformat?: enumtimeoutSeconds?: integer
append-to-note changes
Add text at the end, the start or after a matched passageid: stringexpectedContentHash: stringcontent: stringscopeText?: stringposition?: enumseparator?: stringformat?: enumtimeoutSeconds?: integer
insert-link changes
Insert a web or note link into a note's bodyid: stringexpectedContentHash: stringurl: stringmode?: enumlabel?: stringlinked?: booleanposition?: enumblankLine?: booleanscopeText?: string
append-native changes
Append rich content through the imported Shortcut, keeping native objects intactid: stringexpectedContentHash: stringscopeText: stringcontent: stringformat?: enum
create-checklist-items changes
Add checklist items to a note through a Shortcutid: stringexpectedContentHash: stringscopeText: stringitems: array
create-table changes
Add a native table to a note through a Shortcutid: stringexpectedContentHash: stringscopeText: stringrows?: array
set-note-pinned changes
Pin or unpin a noteid: stringexpectedContentHash: stringscopeText: stringexpectedPinned: booleanpinned: boolean
remove-native-tags changes
Take native tags off a noteid: stringexpectedContentHash: stringscopeText: stringtags: array
replace-native-tag changes
Swap one native tag for another across a set of notesnotes: arrayoldTag: stringnewTag: string
insert-note-link changes
Insert a native link to another noteid: stringexpectedContentHash: stringscopeText: stringlinkedNoteId: stringlabel?: string
delete-note changes
Move a note to Recently Deleted, after a content-hash checkid: stringexpectedContentHash: stringguardNoteId?: anyexpectedGuardContentHash?: anyrequireActiveNoteId?: anytimeoutSeconds?: integer
batch-delete-notes changes
Move several notes to Recently Deleted, each checked against its content hashnotes: array
move-note changes
Move a note to another folder or accountid: stringfolder: stringaccount?: stringtimeoutSeconds?: integer
batch-move-notes changes
Move several notes into one folderids: arrayfolder: stringaccount?: string
list-notes
List note titles and ids in an account or folderaccount?: stringfolder?: stringmodifiedSince?: stringlimit?: integerincludeRecentlyDeleted?: boolean
get-selected-notes
Read which notes are selected in the Notes.app window
list-folders
List folders with their ids and pathsaccount?: string
list-smart-folders
List smart folders and their decoded rulesincludeMatchingNotes?: booleanlimit?: integer
create-folder changes
Make a folder in an accountname: stringaccount?: string
delete-folder deletes
Delete a folder by name or path, refused while it still holds notes
list-accounts
List the Notes accounts on the Mac
get-default-location
Report the account and folder new notes go to
list-shared-notes
List notes shared with other people
get-sync-status
Check whether iCloud sync is running
doctor
Check Notes access, permissions, helpers, the broker and Full Disk Access
get-capabilities
Report which Shortcut operations are installed and usable
get-notes-stats
Summarise the library with per-account and per-folder counts
show-note
Open a note in Notes.appid: stringseparately?: boolean
show-folder
Open a folder in Notes.appid: stringseparately?: boolean
show-account
Open an account in Notes.appid: stringseparately?: boolean
list-attachments
List a note's attachments, with file paths if askedid?: stringtitle?: stringaccount?: stringincludePaths?: booleanfirstImage?: boolean
save-attachment changes
Write one attachment to a path on disknoteId: stringattachmentId: stringsavePath: string
export-attachments changes
Copy a note's attachment files into a foldernoteId: stringexportDir: stringfirstImageOnly?: boolean
fetch-attachment
Return an attachment's bytes inline as base64noteId: stringattachmentId: string
show-attachment
Open an attachment in Notes.appnoteId: stringattachmentId: stringseparately?: boolean
list-paper-attachments
List the Paper and classic drawings in a note
export-paper-image changes
Save Notes' rendered image of a drawing to a filenoteId: stringsavePath: stringattachmentId?: string
export-notes-json
Export notes as JSON, 50 per page by defaultoffset?: integerlimit?: integermodifiedSince?: string
export-notes-markdown
Export one note or a folder as a single Markdown documentid?: anyfolder: any

Resources 4

notes://accounts
The Notes accounts on the Mac
notes://folders
Folders across accounts
notes://stats
Library statistics
notes://note/{id}
One note as Markdown

Prompts 3

find-note
Prompt for finding a note
weekly-review
Prompt for a weekly review of notes
new-meeting-note
Prompt for starting a meeting note

Similar servers