Skip to main content
Glama
connerkward
by connerkward

MCP Apple Notes

MCP Apple Notes

mcp-apple-notes is an MCP server for semantic search and connection-discovery across your own Apple Notes — hybrid search, Swanson-ABC bridges, entity threads, and cited synthesis over everything you've written.

It traverses your accumulated mass of notes: semantic + BM25 hybrid search, non-obvious bridge connections, entity threads, related-notes, and grounded synthesis across everything you've ever written in Apple Notes. Embeddings, search, BM25, clustering, and bridges run on-device; only synthesis generation calls an LLM (local via LM Studio/Ollama, or cloud via OpenAI — your choice). Built on the Model Context Protocol (MCP).

Forked from RafalWilinski/mcp-apple-notes — this fork reads the SQLite store + protobuf directly and adds bridges, entities, feed, and synthesis.

Install as a Claude Code plugin

/plugin marketplace add connerkward/ckw-skills
/plugin install apple-notes@connerkward

This registers the MCP server and bundles the apple-notes-search agent skill. Or install the MCP server manually — see Installation below (requires macOS + bun + Full Disk Access).

Other MCP Notes servers break at scale. They use JXA (AppleScript automation) to read notes one-by-one — fine for 50 notes, unusable at 500+. At 1,800 notes, JXA takes ~49 minutes just to fetch content. On macOS Sequoia it's worse: Apple silently denies Automation permission to processes without a bundle ID, so JXA-based servers fail entirely. This fork reads the SQLite database directly, decodes the protobuf blobs for real note text, and indexes 1,800 notes in under 5 seconds.

MCP Apple Notes

Comparison

Feature

This fork

RafalWilinski (base)

disco-trooper

sirmews

dhravya

Notes access

SQLite (direct)

JXA

JXA

SQLite

JXA

Fetch 1800 notes

~430ms

~49 min (est.)

~49 min (est.)

fast

slow

Sequoia compatible

⚠️ JXA denied

⚠️ JXA denied

⚠️

Content quality

Protobuf decoded

Raw HTML

Raw HTML

Partial

Raw

Semantic search

Auto re-index on search

Folder-aware search

✅ Full path

Note chunking

✅ 1500 chars

Re-ranking (RRF × title × recency)

RRF only

RRF only

Non-blocking index UI

✅ Live progress

Incremental indexing

Update existing notes

Tags + wikilinks

Related notes

✅ Multi-signal

JXA on macOS Sequoia: processes without a bundle ID are silently auto-denied Automation permission. This fork reads SQLite directly.

Related MCP server: MCP Apple Notes

Features

  • 🔍 Semantic search via all-MiniLM-L6-v2 on-device embeddings

  • 📝 BM25 full-text search combined via Reciprocal Rank Fusion

  • 🧠 Re-ranking: RRF × title_boost × recency_factor

  • 📂 Full folder hierarchy — filter by any path segment

  • 🕐 Auto re-index: every search runs ~1ms change detection and, if notes changed, kicks ONE background index job (single-flight) — search itself never blocks on indexing

  • ✂️ 1500-char chunking for long notes

  • 🍎 Direct SQLite + protobuf decode — real note text, not garbled HTML

  • 🏃‍♂️ On-device — embeddings, search, BM25, clustering, and bridges run locally with no API keys; only optional synthesis generation calls an LLM (local LM Studio/Ollama, or cloud OpenAI — your choice)

Local web app — not just search, but connections & synthesis

Run the server directly and open the browser UI — no MCP client needed:

bun index.ts            # → http://localhost:3741/   (also serves /mcp)

Six modes, building from retrieval toward sensemaking:

  • Feed (default) — one ranked, evidence-first stream mixing three connection kinds: bridges (A ↔ C via B, with the connecting phrase mined from B as plain-text evidence), abstraction pairs (two notes whose LLM-consolidated abstractions from ~/.mcp-apple-notes/consolidated.jsonl make the same underlying move), and entity threads ("X notes across Y folders mention Z", from the entity graph). Every item shows its evidence in plain text. 👍/👎 on any item tunes the ranking: a tiny online logistic regression (pure TS, no deps) takes a few SGD steps per vote; votes append to ~/.mcp-apple-notes/votes.jsonl (with the item's feature vector) and replay on boot, so the ranking survives restarts. Diversity constraints: ≤2 consecutive items of one kind, no note more than twice per page of 20; "show more" paginates. Endpoints: GET /api/feed?offset=&limit=, POST /api/vote {id, vote: 1|-1}.

  • Search — the hybrid semantic + BM25 search, in a paper-themed UI with query highlighting and folder filter.

  • Map — a topic map of every note: spherical k-means clusters (TF-IDF labels) over the embeddings, projected to 2D with PCA. Hover a note for the neighbor-lens — lines to its nearest-by-meaning notes, which often cross clusters (the connection-finding payload). Endpoint: GET /api/clusters?k=.

  • Synthesize"what do I think about X across everything I've written?" Query-expansion → relevance-gated retrieval (no recency bias) → MMR diversification → an LLM writes a grounded answer with inline [n] citations back to the source notes; provenance is post-checked. Endpoint: GET /api/synthesize?q=.

  • Bridges — Swanson-ABC discovery over your own notes: pairs (A, C) that are not directly similar but are both strongly similar to a shared bridge note B — connections the corpus only makes through an intermediary. Pure arithmetic over the embeddings (score = sim(A,B)·sim(B,C)·(1−sim(A,C)); cross-folder, no shared tags/wikilinks; thresholds auto-relax; hub-penalized selection so no note dominates the list). No LLM, $0. Endpoint: GET /api/bridges?limit=40.

  • Entities"where else do I talk about Mercedes?" Ranked entity chips (people, orgs, concepts) → the notes that mention them, by mention weight. Reads an optional sqlite graph db (see below). Endpoints: GET /api/entities?q=&limit=, GET /api/entity-notes?entity=.

Precomputed disk caches (instant bridges & map)

Bridge mining (~2 min of embedding) and clustering are computed once and persisted, keyed by a corpus fingerprint (note count + max modification date):

  • ~/.mcp-apple-notes/bridges_cache.json — the full mined bridge pool

  • ~/.mcp-apple-notes/clusters_cache.json — cluster output per k

Fresh cache → served from disk instantly (<300ms). Stale (notes changed) → the stale copy is served instantly with a "recomputing…" pill in the UI while ONE background job (single-flight) refreshes it. Absent → computed inline once.

Enabling synthesis (needs an LLM)

Embeddings/search/clustering/bridges/entities are fully local. Only synthesis generation needs an LLM. Point at a local OpenAI-compatible server to keep notes private:

# LM Studio / Ollama (zero API cost, notes stay local):
SYNTH_BASE_URL=http://localhost:1234/v1 SYNTH_MODEL=<loaded-model> OPENAI_API_KEY=local bun index.ts
# …or real OpenAI: set a funded OPENAI_API_KEY (defaults to gpt-4o-mini).

The entity graph (optional)

The entity layer reads ~/.mcp-apple-notes/layered_graph.db (override with LAYERED_DB) — a plain sqlite file produced by the companion exp-notes-indexing benchmark harness (layered_graph.py). No extra dependencies (bun ships bun:sqlite); if the file is absent the entity tools and tab simply report how to generate it.

Installation

  1. Clone and install:

git clone https://github.com/connerkward/mcp-apple-notes
cd mcp-apple-notes
bun install
  1. Add bun (~/.bun/bin/bun) to Full Disk Access in System Settings.

  2. Add to claude_desktop_config.json:

{
  "mcpServers": {
    "apple-notes": {
      "command": "/Users/<YOUR_USER_NAME>/.bun/bin/bun",
      "args": ["/Users/<YOUR_USER_NAME>/mcp-apple-notes/index.ts", "--stdio"]
    }
  }
}
  1. Restart Claude Desktop and ask: "Index my Apple Notes".

Claude Code / CLI registration

// .mcp.json (project) or `claude mcp add apple-notes -- bun /path/to/mcp-apple-notes/index.ts --stdio`
{
  "mcpServers": {
    "apple-notes": {
      "command": "bun",
      "args": ["/path/to/mcp-apple-notes/index.ts", "--stdio"]
    }
  }
}

The same bridges/entities tools power the web UI tabs at the local app (bun index.tshttp://localhost:3741/); the entity graph db is optional, generated by the exp-notes-indexing benchmark harness.

Tools

Tool

Description

index-notes

Background indexing with live progress UI

search-notes

Semantic + FTS search; optional folder, modifiedAfter, modifiedBefore

find-notes

Exact substring search (like Apple Notes built-in); optional folder, date range

get-note

Full note by title; fuzzy fallback on no exact match

list-notes

Notes sorted by recency; optional folder, date range, limit

list-folders

All folders with note counts

list-tags

All #hashtags across notes, sorted by frequency

search-by-tag

Notes containing a specific hashtag

related-notes

Related notes via shared tags, [[wikilinks]], and vector similarity

bridge-notes

Swanson-ABC bridges: non-similar note pairs connected via a shared intermediary; optional folder, limit

feed

The ranked evidence-first connection feed (bridges + abstraction pairs + entity threads) as JSON; optional limit

entity-notes

Notes mentioning an entity (e.g. "Mercedes"), by mention weight — needs the optional entity graph db

list-entities

Entities ranked by mention count; optional substring query, limit

get-tables

Extract pipe/tab-separated tables from a note

create-note

Create a note

update-note

Edit an existing note

check-changes

Check if notes changed since last index (without triggering re-index)

index-health

Sync status, last indexed time, note count

Search & Ranking

Every search detects changes (~1ms). If notes changed, ONE background incremental index job is kicked (single-flight — duplicate triggers are dropped) and the search returns immediately from the current index; results catch up when the job lands. The last-indexed watermark persists to ~/.mcp-apple-notes/index_state.json across restarts. Change detection compares the per-title set of modification dates (Apple Notes titles are not unique — "TODO" ×10 — and a title→single-date map can never converge for duplicates).

score = RRF(vector, BM25) × title_boost × recency_factor

Temporal queries (recent, latest, today) automatically shift to a 1-day recency half-life at 70% weight. Normal queries use 90-day half-life at 10% so relevance stays primary.

Benchmarks

1,806 notes, Apple Silicon:

Approach

Time

Body included

JXA metadata only

4,463ms

No

JXA with body

~49 min

Yes

SQLite direct

430ms

Yes

Troubleshooting

tail -n 50 -f ~/Library/Logs/Claude/mcp-server-apple-notes.log

Permissions error → ensure bun (~/.bun/bin/bun) has Full Disk Access in System Settings → Privacy & Security.


🧭 ckw-skills — part of Conner K. Ward's collection of Claude Code skills & MCP servers.

A
license - permissive license
-
quality - not tested
A
maintenance

Maintenance

Maintainers
Response time
0dRelease cycle
3Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Search your Obsidian vault to quickly find notes by title or keyword, summarize related content, a…

  • Search everything you save: YouTube, articles, podcasts, PDFs, Notion, Obsidian. API key or OAuth.

  • Token-efficient MCP memory for Markdown vaults. Tiered search, GraphRAG, AI memories.

View all MCP Connectors

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/connerkward/mcp-apple-notes'

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