
Apple Contacts
io.github.mgcreaPublished by GitHub account mgcreav1.37.0Updated Nov 7, 2026
Search and read Apple Contacts — resolves numbers and emails to names, writes off by default
Overview
Reads the macOS Apple Contacts address book so an assistant can resolve phone numbers and emails to names, search and list contacts, with writes off by default.
- What you can do
- Resolve batches of up to 500 phone numbers and email addresses to names, with ambiguity reported as a status rather than a guess
- Search, list and fetch individual contacts from the local address book
- Run diagnostics on the contacts store and lookup index
- Create and update contacts, only when writes are explicitly enabled
- When to use it
- Useful when an assistant needs to put names to phone numbers or email addresses from the user's own Mac address book, or to look up and edit contact records locally. It is macOS-only and reads the address book already on the machine.
- Requirements
- Runs locally as an npm package via npx on macOS. The Contacts permission must be granted, which triggers a prompt on first use; writes additionally need the Automation to Contacts permission. Optional environment variables configure write access, phone suffix length, index mode, store path and script timeout.
Installation
In SourceWeft
- Open Apple Contacts in the dashboard and add it to a workspace.
- Enable the server for the chats that should use its tools.
Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.
Other MCP clients
Follow the launch instructions in the repository.
README
@mgcrea/mcp-apple-contacts
Model Context Protocol server for the macOS Apple Contacts address book.
Unofficial. Not affiliated with Apple. It reads the address book already on your Mac.
Why it exists: resolving handles
apple_contacts_resolve_handles turns phone numbers and email addresses into names, in batches of up
to 500. Everything else here is in service of that.
This is the surface where the file-lane rule is about capability, not speed. Contacts' scripting dictionary answers in ~65 ms — comfortably fast, and still useless for this: resolving a phone number means a suffix-keyed index over every stored number, and no quantity of Apple Events round trips produces one. So reads go through read-only SQLite, and there is no Apple Events read lane.
The lookup builds in 3 ms over 947 phone keys and 295 email keys, then is kept for the life of the process.
Permissions — this one is different
Contacts fails differently from every other surface in this family: it sits behind its own TCC service rather than behind Full Disk Access, and unlike Full Disk Access that permission prompts. So the fix is usually "answer the dialog", not "go to System Settings" — and nothing here asks you to hand over whole-disk access for an address book.
Tools
Read: diagnostics, resolve_handles, search_contacts, list_contacts, get_contact.
Write, registered only when APPLE_CONTACTS_ALLOW_WRITES=1 — with the flag off they are
invisible to the model, not merely refused: create_contact, update_contact.
resolveHandles() is also exported from the package root, for callers already inside Node that
would rather not go through MCP to reach it.
Configuration
Notes that will bite you
- The store is plural.
locate.tsreturns a list and every query fans out — on a real machine,stores=2/2, contacts=421, matching Apple Events exactly. A server reading only the first store silently loses an account. - Nine-digit suffix keys, and the length travels inside the lookup object so the index and the query cannot diverge. A self-join over a real address book at 7, 9 and 10 digits settles it: 7 and 9 collide identically (10 of 947 keys, 1.1%), and 10 drops a key while losing the national-to-E.164 join.
- Ambiguity is a status, never a guess —
resolved/unknown/ambiguous/shortcode.unknownis documented in the tool description as expected, not as an error. Z_ENTfilters toABCDContact. Without it, a list tool returns your groups as people.- A write can succeed and still not persist. Contacts holds changes in an unsaved buffer, so a mutation can read back correctly inside the same script and never reach the store. Every write script saves and then re-reads.
Licence
MIT.
Source: packages/contacts/README.md at commit 2398a54
Tools
0Version history
18- v1.37.0LatestNov 7, 2026
- v1.36.2Nov 4, 2026
- v1.36.1Nov 3, 2026
- v1.36.0Oct 30, 2026
- v1.35.0Oct 30, 2026
- v1.34.0Oct 29, 2026
- v1.31.0Oct 26, 2026
- v1.30.0Oct 22, 2026
- v1.29.1Oct 21, 2026
- v1.28.0Oct 13, 2026
- v1.27.0Oct 13, 2026
- v1.26.0Oct 6, 2026
- v1.25.0Oct 3, 2026
- v1.24.1Sep 29, 2026
- v1.24.0Sep 27, 2026
- v1.23.0Sep 19, 2026
- v1.22.1Sep 16, 2026
- v1.21.1Sep 16, 2026
Score
50% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
- Reached in the last 30 days25 pts
Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.
- Auth declared10 pts
Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.
- Tools listed15 pts
Answer tools/list without a credential, or ship a package that starts in a sandbox without one.
- DescriptionRequired15 pts
- Logo10 pts
- Documentation15 pts
- License10 pts