Skip to main content
Glama
apple-notes-mcp

apple-notes-mcp

by ml-lubich

inotes — Apple Notes CLI

inotes is a Typer command line for Apple Notes — list, search, read, create, edit, delete, pin, unpin, attach local files, and inventory note links. Part of the imsg / imail / inotes / wa local-first tool family.

inotes list --limit 20
inotes search "standup" --json
inotes show "Project Plan"
inotes create --title "Idea" --body "…"
inotes edit --title "Idea" --body "Revised text"
inotes pin "Project Plan"
inotes unpin "Project Plan"
inotes attach "Project Plan" --file ./demo.wav
inotes attachments "Project Plan" --json
inotes links --output ./inotes-links.md
inotes update

macOS only

inotes uses Notes.app through AppleScript (osascript) on macOS. For the all-notes link inventory, it first reads the local Notes database in read-only mode for speed, then falls back to AppleScript if that database layout is unavailable. It never writes to the Notes database. There is no Linux or Windows build, and none is planned — the tool has nothing to talk to without Notes.app.


Related MCP server: Apple Notes MCP Server

Install

# Homebrew
brew install ml-lubich/tap/inotes

# pip
pip install inotes-mcp

# from a clone
git clone https://github.com/ml-lubich/inotes-mcp.git
cd inotes-mcp
uv tool install .

The PyPI/Homebrew package is inotes-mcp; the installed command is always inotes.

Permissions

inotes needs Automation access to Notes.app. The first time it runs a command, macOS will prompt you to allow it — or grant it up front in System Settings → Privacy & Security → Automation (allow your terminal app to control Notes). Verify it's working with:

inotes doctor

Quick start

inotes doctor                                        # verify Notes.app access
inotes list --limit 20                               # recent note titles
inotes list -j                                        # {"notes": [...]}
inotes search "standup" --limit 10 --json             # search titles + bodies
inotes show "My Note"                                 # exact-title lookup
inotes create --title "Idea" --body "…"               # new note
inotes create --title "Work" --body "…" --folder "Projects"
inotes edit --title "Idea" --body "Revised text"      # replace body
inotes edit --title "Idea" --new-title "New Idea"    # rename only
inotes edit --title "Shared" --body "Revised" --folder "Work" # disambiguate duplicate title
inotes delete --title "Old Note" --yes                # permanent; explicit confirmation required
inotes pin "Important Note" --folder "Work"         # pin exact title in folder
inotes unpin "Important Note"                       # unpin; safe to repeat
inotes attach "Project Plan" --file ./demo.wav      # attach a local media file
inotes attachments "Project Plan" --json             # inspect file attachments
inotes links --output ./inotes-links.md              # inventory links across notes
inotes update                                           # upgrade the installed CLI
inotes version
inotes -h                                             # help at every level

show, edit, and delete require one exact title match. If a title is missing or ambiguous, inotes reports an error instead of choosing a note silently. When editing, omitted fields stay as they are; --body replaces the whole body, and --body "" intentionally clears it. Use --folder on show, edit, or delete to scope an exact-title match when duplicate titles exist; it does not move the note. A successful edit is read back and verified. delete requires --yes and removes only a uniquely matched note. Reads and searches never write to Notes. create --folder targets a named folder; omit it to use the default account. inotes update upgrades a uv-managed installation with uv tool upgrade inotes-mcp; Homebrew/pip installations should use their package manager's upgrade command.

pin and unpin use the same exact-title matching and optional --folder disambiguation as show. They are idempotent: repeating the requested state does not toggle it back. AppleScript does not expose a pin property, so these commands select the note and use Notes.app's File → Pin Note menu through System Events. This requires UI scripting access: allow the terminal to control Notes and System Events in Automation settings, and enable the terminal under System Settings → Privacy & Security → Accessibility. The command briefly brings Notes forward for its menu, then restores the app that was frontmost. Pinning is supported for upgraded iCloud notes and notes stored on the Mac; Recently Deleted notes cannot be pinned. A missing permission or unavailable menu action is an error, not a success. See Apple's pinning guide.

attach takes one local file per invocation and verifies it appears on the uniquely matched note. Notes accepts common files and media; support depends on the account type. Apple documents that Exchange notes cannot attach files. See Apple's attachment guide. Use attachments to list attachment names and IDs for one note.

links is the only command that scans every note body. It extracts external hyperlinks and plain HTTP(S) URLs, returning the source note title, link label, and URL without including note bodies or following links. On the standard macOS Notes store it uses a read-only SQLite fast path; other layouts use the slower AppleScript fallback. A report can contain private URLs; --output writes it to a file with owner-only permissions. No report is created unless that option is supplied.


Commands

Command

Description

doctor

Verify Notes.app Automation access

list

List recent note titles (--limit, --json)

search

Search note titles and bodies (--limit, --json)

show

Show a note by exact title

create

Create a note (--title, --body, --folder)

edit

Edit a uniquely matched note's title/body (--folder scopes the source match)

delete

Permanently delete a uniquely matched note (--yes required)

pin

Pin one uniquely matched note (--folder scopes duplicate titles)

unpin

Unpin one uniquely matched note (--folder scopes duplicate titles)

attach

Attach a local file to one uniquely matched note (--file, optional --folder)

attachments

List file attachments on one note (--json, optional --folder)

links

Inventory note hyperlinks and plain URLs; optionally write a private report (--output)

update

Upgrade the installed CLI with uv

version

Print the installed version

mcp

Run the built-in MCP server over stdio

agent schema

Print a JSON command contract (version, tool, params per command)

agent guide

Print a markdown playbook for LLM agents

Help is available at every level: inotes -h, inotes <command> -h, inotes agent -h.


Using inotes from an agent

inotes is CLI-first by design — cheaper on tokens than a full MCP round trip for the common case of list/search/show/create/edit/delete/pin/unpin/attach/attachments. inotes agent schema gives an agent a machine-readable contract of every command and its parameters; inotes agent guide gives it a short playbook (the same rules documented above: run doctor first, --json on list/search, exit code 0 on success). Root and agent help only render command usage; they do not contact Notes.app. Notes automation runs only when a Notes command is invoked.

Built-in MCP server

inotes mcp runs a stdio MCP server exposing the CLI's Notes operations (doctor, list_notes, search_notes, show_note, create_note, edit_note, delete_note, pin_note, unpin_note, attach_file_to_note, list_note_attachments, list_note_links) as MCP tools — no separate package needed. inotes agent schema lists every CLI command and registered MCP tool. Point any MCP client at the installed inotes command:

{
  "mcpServers": {
    "inotes": {
      "command": "inotes",
      "args": ["mcp"]
    }
  }
}

Development

git clone https://github.com/ml-lubich/inotes-mcp.git
cd inotes-mcp
python3 -m pip install -e ".[dev]"
pytest -q
inotes -h

A legacy bash implementation is preserved as inotes.bash.bak for reference.


MIT License · Copyright (c) 2026 Misha Lubich (ml-lubich)

Related MCP Connectors

Related MCP Servers