Help us improve
Share bugs, ideas, or general feedback.
From cosmic-grackle
Use when the user wants to create, update, or delete a contact in the macOS Contacts app (Apple Contacts) on their local Mac. Triggers on phrases like "add a contact on my Mac", "create a new contact for X", "update Jane's job title", "change Bob's organization", "delete the contact for Alice", "remove a duplicate contact from Contacts.app". Applies only to the native macOS Contacts app backed by the CNContactStore framework — NOT Google Contacts, iCloud.com, CRMs, or address books in third-party apps. Requires the `cosmic-grackle` MCP server (installed separately — see https://github.com/ngerakines/cosmic-grackle) to be registered with the user's MCP client; the server exposes `contacts_search`, `contacts_get`, `contacts_create`, `contacts_update`, `contacts_delete`, `contacts_list`, `groups_list`, and `groups_members`. If those tools are not available in the session, tell the user the MCP server is not installed or registered and link them to the install instructions rather than guessing. The first tool invocation prompts the user to grant Contacts access in System Settings.
npx claudepluginhub ngerakines/cosmic-grackle --plugin cosmic-grackleHow this skill is triggered — by the user, by Claude, or both
Slash command
/cosmic-grackle:macos-contactsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill covers create / update / delete workflows against the macOS Contacts app using the `cosmic-grackle` MCP server's `contacts_create`, `contacts_update`, and `contacts_delete` tools. Read-only tools (`contacts_list`, `contacts_search`, `contacts_get`, `groups_list`, `groups_members`) are documented here only insofar as they are prerequisites for mutating operations.
Creates p5.js generative art with seeded randomness, noise fields, and interactive parameter exploration. Use for algorithmic art, flow fields, or particle systems.
Share bugs, ideas, or general feedback.
This skill covers create / update / delete workflows against the macOS Contacts app using the cosmic-grackle MCP server's contacts_create, contacts_update, and contacts_delete tools. Read-only tools (contacts_list, contacts_search, contacts_get, groups_list, groups_members) are documented here only insofar as they are prerequisites for mutating operations.
The MCP server is distributed independently of this skill and is registered under whatever name the user chose in their MCP client config (typically contacts). If the contacts_* tools are not present in the current session the server is not installed or not registered — point the user at https://github.com/ngerakines/cosmic-grackle for install instructions instead of attempting the workflow.
Every contact has an opaque identifier returned in the id field. contacts_update and contacts_delete require this id — they do NOT accept a name.
Before any update or delete, resolve the id with contacts_search (by name substring) or contacts_list. If the search returns multiple matches, ask the user which one to act on; do not guess.
contacts_get returns the full contact record for a known id and is useful for confirming "am I about to edit the right person?" before a mutation, and for reading the exact stored form of phones / emails / URLs / addresses before using the _from ops on update.
Tool: contacts_create
At least one of first_name, last_name, or organization is required. Fields, grouped:
contact_type ("person" default, or "organization"), name_prefix, first_name, middle_name, last_name, name_suffix, nickname, plus phonetic variants phonetic_given_name, phonetic_middle_name, phonetic_family_name, phonetic_organization_name (primarily for CJK / kana sort order).organization, department, job_title.email (work), phone (mobile), url (homepage), postal_address (home). postal_address is a structured object with optional street, city, state, postal_code, country sub-fields — any subset may be provided.note, birthday as "YYYY-MM-DD" or "--MM-DD" (month/day without year).Only one phone, email, url, and postal address can be set on create. For additional entries or non-default labels, create first and then layer on more with contacts_update (each Add call uses the same default label — Contacts.app is the only place to rename labels).
Returned JSON: {"id": "<opaque-id>", "success": true}. Store the id if subsequent operations are planned.
Example call:
{
"first_name": "Ada",
"middle_name": "Augusta",
"last_name": "Lovelace",
"organization": "Analytical Engine Co.",
"job_title": "Mathematician",
"email": "ada@example.org",
"url": "https://example.org",
"birthday": "1815-12-10",
"postal_address": {
"street": "12 St. James's Square",
"city": "London",
"country": "UK"
},
"note": "First programmer"
}
Length caps (MCP rejects over-limit input, byte counts): names 256, email 320, phone 64, url 2048, organization 256, department 256, job title 256, note 4096, birthday 32, contact_type 32, each postal sub-field 512.
Tool: contacts_update
Requires id. Partial update — omitted fields are left alone.
Any of these may be supplied to overwrite the stored value:
contact_type, name_prefix, first_name, middle_name, last_name, name_suffix, nickname, the four phonetic_* fields, organization, department, job_title, note, birthday.
_from / _to ops)Four field families are edited via paired fields:
phone_from / phone_toemail_from / email_tourl_from / url_topostal_from / postal_toSemantics per pair:
_from | _to | Effect |
|---|---|---|
| unset | set | Add _to as a new entry with the default label |
| set | unset | Remove the entry matching _from |
| set | set | Replace: match on _from, overwrite with _to, keep the original label |
Matching is exact against the stored value, so call contacts_get first to see the canonical form. postal_from matches against the joined-string shown in Contact.addresses (e.g., "123 Main St, Austin, TX, 78701, USA"), while postal_to is a structured PostalAddressInput (same shape as postal_address on create). Any _from or _to provided must be non-empty — a deliberate "clear this entry" has to be a Remove (_from only), not a Replace to an empty string.
Workflow:
contacts_search with the user's description to resolve the id.contacts_get on each or ask the user.contacts_get to read the stored form of any phone/email/URL/address you're about to replace or remove.contacts_update with id + changed scalars and/or _from / _to pairs.{"success": true|false}.Examples:
Add a second phone:
{ "id": "ABCD-1234", "phone_to": "+1-555-0101" }
Remove an email:
{ "id": "ABCD-1234", "email_from": "old@example.org" }
Replace a URL (label preserved):
{ "id": "ABCD-1234", "url_from": "https://old.example.org", "url_to": "https://new.example.org" }
Replace a postal address:
{
"id": "ABCD-1234",
"postal_from": "123 Main St, Austin, TX, 78701, USA",
"postal_to": { "street": "456 Elm St", "city": "Austin", "state": "TX", "postal_code": "78702", "country": "USA" }
}
Change a scalar alongside a phone replacement:
{
"id": "ABCD-1234",
"job_title": "Senior Mathematician",
"phone_from": "+1-555-0100",
"phone_to": "+1-555-0200"
}
Tool: contacts_delete
Requires id. Deletion is permanent through this MCP — there is no undo tool. Before deleting:
contacts_search.contacts_get and show the user the full record (name, organization, emails, phones).contacts_delete. Do not delete on the first request for a named contact without confirmation.Returned JSON: {"success": true|false}.
groups_list returns every group with member counts; groups_members returns contacts in a named group (case-insensitive). This MCP cannot create groups, rename them, or add/remove members — those require Contacts.app.
The first tool call after the MCP server is registered triggers macOS TCC (Transparency, Consent, and Control) to prompt the user to grant Contacts access. If they deny, subsequent calls return an internal error. Recovery path:
cosmic-grackle or the parent MCP client, depending on how the server is invoked).contacts_get before a Replace or Remove. The _from value must exactly match the stored form. If the user says "change Bob's 555-0100 to 555-0200", read the contact first — it may be stored as "+1 (555) 010-0000" and an unnormalized _from will silently fail to match._to creates a new entry with the default label; it does not modify an existing one. To change an existing entry, always pair _from with _to.contacts_create, contacts_search for the same name usually returns the new contact immediately, but if it doesn't, fall back to the id returned from contacts_create.