Apple Notes search & connection-discovery
apple-notes is an MCP server for semantic search and connection-discovery across the
user's own Apple Notes — hybrid search, Swanson-ABC bridges, entity threads, and cited
synthesis over everything they've written. Embeddings, search, BM25, clustering, and
bridges run on-device; only synthesis generation calls an LLM (local OR cloud,
the user's choice).
When to Use
- Use when the user wants to find, recall, or look up something from their own Apple Notes ("search my notes for X", "what did I write about X", "did I ever note Y").
- Use when the user wants to surface non-obvious connections across their notes ("find bridges/connections across my notes", "what links X and Y", "show related notes").
- Use when the user wants to synthesize a position from their notes ("summarize what I think about X from my notes", "pull together everything I've written on X").
- Also use for "index my Apple Notes", tag/folder queries, and "what's connected to X".
- Do not use for creating reminders, or for non-Apple-Notes note systems.
Prerequisites
- macOS with Apple Notes.
buninstalled (brew install oven-sh/bun/bun).- Full Disk Access granted to the
bunbinary. - MCP server registered in the client (Claude Code or Claude Desktop).
Procedure
- Check if the MCP is connected. If
apple-notestools are not available, the server isn't registered yet — do the Setup below before anything else. If tools exist but a search returns "not indexed" or empty, runindex-notesfirst. - Setup (walk the user through this):
a. Install bun (if absent):
brew install oven-sh/bun/bunb. Clone + install deps:
c. Grant Full Disk Access to bun. Rungit clone https://github.com/connerkward/mcp-apple-notes cd mcp-apple-notes git checkout <reviewed-tag-or-commit> bun installwhich bun, then open System Settings → Privacy & Security → Full Disk Access, click+, and add that exactbunbinary path (commonly/opt/homebrew/bin/bunor/usr/local/bin/bun). Without this the server cannot read NoteStore.sqlite and every call fails with a permissions error. d. Register the MCP server (pick the user's client):- Claude Code:
claude mcp add apple-notes -- bun /absolute/path/to/mcp-apple-notes/index.ts --stdio - Claude Desktop: add to
claude_desktop_config.json:{ "mcpServers": { "apple-notes": { "command": "/Users/<you>/.bun/bin/bun", "args": ["/Users/<you>/mcp-apple-notes/index.ts", "--stdio"] } } } - As a Claude Code plugin (bundles this skill too):
/plugin marketplace add connerkward/ckw-skillsthen/plugin install apple-notes@connerkward. e. Restart the client, then tell the user to ask "Index my Apple Notes" (or callindex-notes). First index of ~1,800 notes takes a few seconds.
- Claude Code:
- Tool selection:
index-notes: First run, or to force a rebuild. Background job with live progress.search-notes: Default search. Hybrid semantic + BM25, re-ranked. Optionalfolder,modifiedAfter,modifiedBefore. "What did I write about X."find-notes: Exact substring match (like the Apple Notes search box). Use when the user wants a literal string, not meaning. Optionalfolder, date range.get-note: Fetch one full note by title (fuzzy fallback).list-notes: Notes by recency. Optionalfolder, date range,limit.list-folders: All folders + note counts.list-tags/search-by-tag:#hashtaginventory / notes carrying a given tag.related-notes: Notes related to a given one via shared tags,[[wikilinks]], and vector similarity. "Show me related notes."bridge-notes: Swanson-ABC bridges — non-obvious connections: pairs (A, C) not directly similar but both strongly tied to a shared intermediary B. "Find non-obvious connections across my notes." Optionalfolder,limit. No LLM.feed: Ranked evidence-first connection stream (bridges + abstraction pairs + entity threads) as JSON. Optionallimit.entity-notes/list-entities: "Where else do I talk about Mercedes?" Entity chips → notes by mention weight. Needs the optional entity graph db (~/.mcp-apple-notes/layered_graph.db); if absent these report how to generate it.get-tables: Pull pipe/tab tables out of a note.create-note/update-note: Create or edit a note.check-changes: Did notes change since last index? (does not trigger re-index)index-health: Sync status, last-indexed time, note count. Run this if results seem stale.
- Synthesis: For "synthesize what I think about X" the synthesis lives in the web app endpoint (
GET /api/synthesize?q=athttp://localhost:3741/when run withbun index.ts), which writes a grounded answer with inline[n]citations back to source notes.
Pitfalls
- Index before the first search. No index → empty/garbage results; run
index-notes. - Auto re-index: each search does ~1ms change detection and kicks ONE background incremental index if notes changed — search returns immediately from the current index and catches up when the job lands. If a just-edited note is missing, it's the catch-up lag; re-run the search.
- Score:
score = RRF(vector, BM25) × title_boost × recency_factor. - Temporal queries (
recent,latest,today) auto-shift to a 1-day recency half-life at 70% weight; normal queries keep relevance primary (90-day half-life, 10%). - Synthesis is the only cloud-capable part. It needs an LLM: local via LM Studio / Ollama (
SYNTH_BASE_URL=http://localhost:1234/v1 SYNTH_MODEL=<model> OPENAI_API_KEY=local, notes stay on-device) or real OpenAI (fundedOPENAI_API_KEY, defaults togpt-4o-mini). Everything else — embeddings, search, BM25, clustering, bridges, entities — is on-device. - Entity tools require the optional layered graph database; without it, use hybrid search, exact search, related notes, or bridges instead.
- macOS and Apple Notes only; it does not search Obsidian, Notion, Google Docs, or other note stores.
- Local filesystem permissions: The MCP server needs local filesystem permissions to read Apple Notes data, so setup cannot be completed purely inside a remote shell.
Verification
- Check MCP connection: Attempt to call
index-health. If it returns sync status, last-indexed time, and note count, the MCP is connected. - Check index status: If search returns "not indexed" or empty, run
index-notesand checkindex-healthagain. - Verify synthesis endpoint: If the web app is running (
bun index.ts), verify the endpoint withcurl "http://localhost:3741/api/synthesize?q=test"and check for a grounded answer with inline citations.