Cupertino
Cupertino
Pon a trabajar a tu agente en tus apps de Apple de todos los días.
Servidores MCP para las apps de Apple que ya tienes en tu Mac, y la app firmada que les concede sus permisos una sola vez en lugar de una por cada una — para cualquier agente que hable MCP, no para un solo host.
No oficial. No está afiliado a Apple. Estos controlan las apps que ya están en tu Mac.
En uso
Fíjate en cómo escribo en mi bandeja de entrada del trabajo y redacta esta respuesta con el mismo tono.
Reúne todo lo relacionado con el lanzamiento de Atlas de mi correo, mis notas y mi calendario. ¿Qué les sigo debiendo a los demás?
Convierte los elementos de acción del hilo de ayer con el cliente en recordatorios, para el viernes.
Cada una de esas frases lleva una restricción — una cuenta, un límite de fecha, un filtro. Esa es la
parte que la vía ingenua de osascript responde en 74 segundos o responde mal, y la razón por la que
un servidor se gana su lugar: guarda lo que el modelo tendría que volver a deducir en cada sesión.
Las mediciones están en docs/verify.md; lo que cuestan las alternativas está en
docs/alternatives.md.
La última necesita la puerta de escritura abierta en Recordatorios. Las escrituras están desactivadas por superficie hasta que las actives, y el interruptor decide si las herramientas de mutación se registran siquiera — un agente con las escrituras desactivadas no puede ver que existen.
Related MCP server: apple-mcp
Superficies
Superficie | Paquete | Estado |
implementado — 18 herramientas, búsqueda/lectura/adjuntos + escrituras con puerta | ||
Notas | implementado — 12 herramientas, búsqueda/lectura/adjuntos + escrituras con puerta | |
Recordatorios | implementado — 11 herramientas, listas/búsqueda/fechas + escrituras con puerta | |
Calendario | implementado — 9 herramientas, rangos/búsqueda/recurrencia + escrituras con puerta | |
Contactos | implementado — 7 herramientas, resuelve identificadores a nombres + escrituras con puerta | |
Mensajes | implementado — 6 herramientas, chats/búsqueda/texto decodificado + envío con puerta | |
Safari | implementado — 6 herramientas, historial/pestañas/lista de lectura, solo lectura | |
— | compartido: el límite de osascript, errores compatibles con TCC, SQLite de solo lectura |
Cada superficie es su propio servidor y su propio paquete npm, de modo que un host carga solo las herramientas que quiere. Comparten un mismo bundle y una misma concesión de Acceso total al disco, que es la razón de que vivan juntos — ver docs/distribution.md.
Inicio rápido
Cada servidor está en npm y se ejecuta directamente desde npx — para Claude Code, un .mcp.json
junto a tu proyecto:
{
"mcpServers": {
"apple-mail": {
"command": "npx",
"args": ["-y", "@mgcrea/mcp-apple-mail"]
},
"apple-notes": {
"command": "npx",
"args": ["-y", "@mgcrea/mcp-apple-notes"]
}
}
}Los paquetes son MIT y no necesitan clave de licencia. Lo que sí necesitan es un permiso, y en npm
se lo concedes a quien los lanza — tu editor, tu terminal — que es el intercambio que la
Cupertino.app firmada existe para evitar: una sola concesión de
Acceso total al disco en manos de un binario notarizado, en lugar de una por host. Ver
docs/licensing.md.
O ejecútalos desde el código fuente:
git clone https://github.com/mgcrea/mcp-cupertino.git
cd mcp-cupertino
pnpm install
pnpm buildy luego apunta tu host a packages/<surface>/dist/cli.js por ruta absoluta.
Las escrituras están desactivadas a menos que las pidas — ver Configuración.
Si los ejecutas a través de la app de la barra de menús, en cambio, cada servidor pasa por el puente, de modo que el Acceso total al disco se concede a Cupertino y no al editor que haya lanzado el servidor:
make run # build Cupertino.app, point it at packages/*/dist, launch it
make smoke # handshake every server through the bridgeEl .mcp.json incluido en el repositorio está configurado para esa ruta. make por sí
solo lista todos los objetivos.
Observa los distintos nombres de servidor. Configurado a mano como arriba, un servidor se llama
apple-mail y se ejecuta bajo la concesión que tenga su proceso host. Configurado por Cupertino se
llama cupertino-mail, porque esa entrada apunta al puente de la app y se ejecuta bajo la concesión
de la app. Dos nombres para dos despliegues, y puedes tener ambos. La app solo toca sus propias
claves cupertino-* — una entrada apple-mail que pertenezca a otro servidor se deja intacta.
Cupertino es configuración de la máquina, no una dependencia del proyecto, así que pertenece a una
configuración por usuario: un archivo para cada uno de Claude Desktop, Cursor, LM Studio y Windsurf,
y --scope user para las CLI, que es lo que usan los comandos copiables de la app. Deliberadamente
no --scope project, que escribe un .mcp.json pensado para ser commiteado — esa entrada es una
ruta absoluta a un bundle en un Mac concreto, respaldada por la concesión de Acceso total al disco de
una persona, y sería inútil para un compañero de equipo y poco prudente ofrecérsela.
Qué clientes reciben una escritura y cuáles reciben un comando no es cuestión de popularidad. La app
fusiona en una configuración solo cuando es JSON estricto con servidores bajo mcpServers; el de
Visual Studio Code es JSONC y el de Codex es TOML, y re-serializar cualquiera de los dos borraría los
comentarios de un archivo mantenido a mano. El ~/.claude.json de Claude Code es JSON estricto, pero
contiene credenciales y las sesiones en ejecución escriben en él de forma concurrente, así que una
lectura-modificación-escritura desde la barra de menús podría descartar el cambio de otra persona.
Esos tres reciben una línea para pegar. ChatGPT está ausente por completo: acepta conectores HTTP
remotos y no puede lanzar un servidor stdio local en absoluto.
El .mcp.json propio de este repositorio es la excepción, y nombra a sus servidores
cupertino-*-dev a propósito: apunta a apps/apple/.build, así que trabajar en la app significa
tener la compilación de desarrollo y la instalada una al lado de la otra. Claude Code reporta los
servidores del mismo nombre en dos ámbitos como un conflicto en lugar de elegir uno, así que el sufijo
es lo que mantiene ambos utilizables.
Cupertino.app necesita macOS 26 o posterior — su icono es un bundle de Icon Composer, que nada
más antiguo puede renderizar. Los servidores en sí son Node puro y no tienen ese requisito mínimo;
solo la app de la barra de menús lo tiene.
Permisos
Dos concesiones separadas de macOS, y recaen sobre el proceso que haya lanzado el servidor — tu editor, tu terminal o Cupertino — nunca sobre Mail, Notas o Recordatorios en sí.
Concesión | Necesaria para |
Acceso total al disco | el carril del índice: búsqueda de Mail, bytes de adjuntos |
Automatización (por app de destino, con aviso) | el carril de Apple Events: cuentas, buzones, todas las escrituras |
Contactos (con aviso) | la superficie de Contactos — su almacén no está detrás del Acceso total al disco |
Ajustes del Sistema → Privacidad y seguridad → Acceso total al disco → añade la app que lanza, y reiníciala. Concedérselo a Mail.app no hace nada; el lector necesita el permiso, no Mail.
Qué funciona sin Acceso total al disco:
Superficie | Sin la concesión |
solo cuentas, buzones y escrituras — la búsqueda recurre a Apple Events en ~74 s | |
Notas | totalmente utilizable por debajo de unas 5 000 notas; solo los bytes de adjuntos necesitan la concesión |
Recordatorios | utilizable, pero las fechas de día completo y las subtareas necesitan el almacén — el contenedor ni siquiera se puede listar sin ella |
Calendario | nada. La única superficie sin una vía de lectura de Apple Events lo bastante rápida como para ser un respaldo — una consulta de rango de 90 días cuesta 3,4 s — así que toda lectura necesita la concesión. Las escrituras siguen funcionando. |
Contactos | nada — pero no quiere Acceso total al disco. Su almacén está detrás del permiso separado de Contactos, que macOS solicita en lugar de hacerte buscar un panel de ajustes. Las escrituras necesitan Automatización además. |
Mensajes | nada en absoluto. No existe una vía de lectura de Apple Events — Mensajes responde "La aplicación no se está ejecutando" incluso mientras se ejecuta — así que no hay segundo carril ni modo degradado. Un envío aún puede intentarse, pero sin almacén del que elegir el destino o con el que conciliar, normalmente no se puede direccionar en absoluto. |
Safari | pestañas en vivo, y solo esas. La única superficie cuyos carriles no son respaldos el uno del otro: Apple Events ve lo que está abierto ahora, el carril de archivos ve todo lo demás. El historial, los marcadores y la Lista de lectura necesitan la concesión. |
Las herramientas que necesitan el índice no desaparecen cuando falta — la lista de herramientas es
una función pura de allowWrites y nada más, porque los clientes MCP la guardan en caché. Devuelven
un resultado estructurado degraded que nombra lo que falta. apple_mail_diagnostics /
apple_notes_diagnostics informan de qué carril está activo y de cómo conceder lo que falta.
Herramientas
Las herramientas de lectura siempre están registradas. Las de escritura son invisibles a menos que las escrituras estén activadas — no solo rechazadas.
Siempre disponibles | Con puerta de escritura |
|
|
|
|
|
|
Notas
Siempre disponibles | Con puerta de escritura |
|
|
|
Recordatorios
Siempre disponibles | Con permiso de escritura |
|
|
|
Calendario
Siempre disponibles | Con permiso de escritura |
|
|
|
list_events expande los eventos periódicos, de modo que una reunión semanal se devuelve una vez por semana. Cada resultado incluye la ventana que se sabe que cubre la expansión y establece truncated cuando un rango la supera en lugar de devolver menos resultados: una lista corta de eventos es indistinguible de una tarde libre.
Contactos
Siempre disponibles | Con permiso de escritura |
|
|
|
resolve_handles convierte números de teléfono y direcciones de correo electrónico en nombres, que es lo que hace legible la superficie de Messages. Lee status en cada resultado en lugar de asumir que se devolvió un nombre: unknown es habitual y no es un error, y ambiguous significa que dos contactos comparten el número, por lo que no se devuelve ningún nombre en lugar de una suposición.
No hay herramienta de borrado. El diccionario de scripting de Contacts no tiene ningún comando de borrado de ningún tipo, y las escrituras pasan por Apple Events en todas las superficies porque el almacén es de solo lectura por política. Consulta docs/contacts.md.
Mensajes
Siempre disponibles | Con permiso de escritura |
|
|
|
Una sola herramienta de escritura, porque el diccionario tiene un único comando utilizable. sdef lista send, login y logout; los otros dos cerrarían la sesión del usuario de iMessage en todos sus dispositivos. No hay ningún verbo de editar, borrar, marcar como leído o reaccionar que exponer.
Esta es la única superficie donde Apple Events es un carril de escritura y nada más: cualquier lectura a través de él falla, así que con APPLE_MESSAGES_ALLOW_WRITES desactivado nunca se envía ningún Apple Event y nunca se solicita ningún permiso de Automation.
send_message prefiere un chatRef de list_chats antes que un identificador bruto, porque Messages se niega a enumerar participantes para un script: una conversación existente es el único destino al que se puede dirigir de forma fiable. Apple Events no devuelve ningún identificador de lo que envió, así que el resultado se concilia contra chat.db — reconciliation: "matched" lleva una referencia de mensaje real, y "pending" significa que Messages aceptó el envío pero aún no ha escrito la fila. Pendiente no es un fallo, y reintentarlo envía el mensaje dos veces.
Alrededor del 3% de los mensajes de toda la historia conservan su texto solo en un blob NSArchiver archivado al que SQL no puede acceder, y Apple dejó de escribir la columna de texto plano a principios de 2026, así que para cualquier cosa reciente es ~100%. Este servidor los decodifica; textSource en cada resultado indica qué carril respondió. Consulta docs/messages.md.
Safari
Siempre disponibles | Con permiso de escritura |
| — ninguna |
|
De solo lectura, y la columna de escritura está vacía a propósito. Abrir una URL o añadir a la Lista de lectura es un Apple Event que navega en un navegador real y visible, y ninguna escritura en esta superficie se probó jamás.
list_tabs es la única herramienta de todo el paquete que funciona sin Acceso a disco completo: en su lugar necesita un permiso de Automation, y Safari tiene que estar en ejecución. Que el campo history de una pestaña sea nulo significa no encontrado en el historial, nunca "no visitada": solo alrededor del 55% de las pestañas abiertas coinciden con una fila del historial, porque las redirecciones, los parámetros de sesión y las páginas nunca confirmadas en el historial producen una URL que simplemente no está.
No existe la herramienta do JavaScript. Ese verbo necesita "Permitir JavaScript desde Apple Events", una opción del menú de desarrollo de Safari que no es un permiso de TCC y cuyo propio estado no se puede leer de forma fiable. Publicarlo significaría informar de una superficie sana cuya capacidad más potente falla silenciosamente. Consulta docs/safari.md.
Todos los nombres llevan el prefijo apple_mail_ / apple_notes_ / apple_reminders_ / apple_calendar_ / apple_contacts_ / apple_messages_ / apple_safari_.
Configuración
Solo variables de entorno: estos servidores no guardan ningún secreto propio, así que no hay archivo de configuración. El prefijo es APPLE_MAIL_, APPLE_NOTES_, APPLE_REMINDERS_, APPLE_CALENDAR_, APPLE_CONTACTS_, APPLE_MESSAGES_ o APPLE_SAFARI_.
Variable | Por defecto | Qué hace |
|
| registrar o no las herramientas de mutación |
| todas | lista blanca de cuentas (nombres o UUID): el control del lado de lectura |
|
| el único directorio en el que se pueden escribir adjuntos |
|
| límite en cualquier listado |
|
|
|
|
| tiempo de espera por llamada a Apple Events |
|
| registro verboso en stderr |
allowWrites controla la mutación, pero en Mail el mayor radio de daño es leer un archivo completo: para eso sirve *_ACCOUNTS, y se aplica en un único lugar para que ninguna ruta de consulta pueda esquivarlo. Mail también acepta *_ROOT, *_ENVELOPE_INDEX, *_DEGRADED_MAX_MESSAGES, *_BODY_MAX_BYTES, *_BODY_SCAN_MAX, *_BODY_SCAN_BYTES y *_MAILBOX_CACHE_TTL_MS; consulta packages/mail/src/config.ts.
La app
La barra de menús es toda la superficie de Cupertino: no hay icono en el Dock ni ventana principal.
Sección | Qué responde |
Acceso a disco completo | Concedido o no, con el botón que abre el panel de Ajustes correcto |
Un panel por superficie | Estado de Automation por app, el aviso de consentimiento y el interruptor de escrituras |
Conexiones | Qué cliente está hablando con qué servidor ahora mismo y a cuántas herramientas ha llamado |
Clientes MCP | Configuración con un clic para Claude Desktop, Cursor, LM Studio y Windsurf; un comando copiable para Claude Code, VS Code y Codex |
Actividad… | Abre una ventana que lista cada llamada a herramienta, en vivo |
La ventana de Actividad registra solo los nombres de las herramientas, nunca argumentos, contenidos de mensajes ni resultados. Es la respuesta a "¿qué acaba de hacer el asistente con mi correo?" y la razón por la que los servidores se ejecutan bajo una app visible en lugar de dentro del editor que los haya lanzado.
Las escrituras están desactivadas por superficie hasta que las actives, y el interruptor decide si las herramientas de mutación se registran siquiera: un asistente con las escrituras desactivadas no puede ver que existen.
Por qué una sola app
El Acceso a disco completo es un permiso indivisible de todo el disco. Concederlo por superficie no aporta ningún aislamiento y cuesta una visita a Ajustes del Sistema cada vez, así que cada superficie se distribuye dentro de una única app firmada y notarizada llamada Cupertino. docs/distribution.md también documenta por qué la Mac App Store no puede alojar nada de esto, para que no se vuelva a plantear la cuestión.
Pero el permiso no es lo único que posee la app, ni es la única razón para ejecutar los servidores bajo ella en lugar de bajo un editor:
El permiso recae en Cupertino | no en el editor que haya lanzado el servidor, ni en cada extensión y tarea que ese editor ejecute |
Un rastro de auditoría visible | la ventana de Actividad lista cada llamada a herramienta, en vivo; un servidor dentro de un editor es inobservable |
Las escrituras están desactivadas por superficie | y el interruptor decide si las herramientas de mutación se registran siquiera |
| el radio de daño en Mail es el archivo, no las mutaciones |
Los resultados dicen cuánto fiarte de ellos |
|
Cuatro superficies, un permiso | que es la verdadera recompensa de la indivisibilidad anterior |
docs/alternatives.md es la versión honesta de esa lista: qué más lee Apple Mail para un asistente y en qué aspectos esas herramientas van por delante.
Documentación
cómo se distribuye esto, y por qué no en la App Store | |
qué superficies, y cuánto cuesta cada una | |
qué es abierto, qué se vende, qué compra confianza | |
qué más lee Apple Mail, y dónde perdemos | |
el carril de búsqueda en el cuerpo, y cómo se decide | |
Mediciones de fase 0 de Apple Notes | |
Mediciones de fase 0 de Apple Reminders | |
Apple Messages: mediciones, decodificador, envío | |
Mediciones de fase 0 de Apple Calendar | |
Mediciones de fase 0 de Safari | |
Esquema observado de | |
comprobando el servidor de Mail contra un índice real |
Trabajando en ello
pnpm build # every package
pnpm test # every package
pnpm typecheck
pnpm lint
pnpm formatEl sitio de marketing es su propio espacio de trabajo y se despliega a mano:
pnpm --filter @mgcrea/cupertino-website dev # astro dev
pnpm --filter @mgcrea/cupertino-website build # static buildSe construye desde el lienzo de diseño en .idea/design/, y lee sus recuentos de herramientas de packages/*/src/tools/ en lugar de este archivo — ver apps/website.
La mitad Swift es xcodebuild, nombrada por el Makefile en lugar de estar envuelta por él:
make app # build Cupertino.app (Debug)
make run # build, point at packages/*/dist, launch
make smoke # handshake every server through the bridge
make stop # quit and remove the socketLas capturas de pantalla de la aplicación se capturan en lugar de tomarse a mano — apps/apple/Screenshots/ contiene la configuración y los goldens confirmados, y el sitio web renderiza la salida:
make screenshots # capture, gate against the goldens, compose both sets
make screenshots-check # gate only — fails if the UI drifted
make screenshots-update # accept the captures as the new goldens (review the diffs first)
make screenshots-selftest # prove the gate fails when it shouldUna ejecución toma el control del puntero y de la aplicación activa en el momento de cada captura, así que no uses la máquina mientras se ejecuta. Necesita permiso de Grabación de Pantalla para la terminal, nunca para Cupertino en sí. make screenshots-doctor comprueba eso y las otras dos cosas que de otro modo fallan silenciosamente: si la fuente del título se resuelve, y si el tamaño de salida es uno que una tienda aceptaría.
Lo que muestran las capturas son datos de fixture de apps/apple/Cupertino/DemoSeed.swift, no este Mac: en -ScreenshotMode la aplicación no inicia ningún host, siembra su propio registro y sesiones, y responde a las preguntas de permiso y tienda desde una tabla. Sin eso, las imágenes reportarían el estado TCC de un portátil e imprimirían su directorio personal en el sitio de marketing.
Las sondas de fase 0 son de todo el repositorio y de solo lectura. Necesitan el permiso de la superficie que miden, y redactan su salida a recuentos, tiempos y DDL:
pnpm probe:mail # Envelope Index — needs Full Disk Access
pnpm probe:mail-body # which lane can search message bodies — needs Full Disk Access
pnpm probe:notes # Notes — the Apple Events half runs without it
pnpm probe:reminders # Reminders — the store path is a glob, so finding it is itself privileged
pnpm probe:messages # chat.db — no Apple Events read lane exists, so this one needs the grant
# --send-target=<handle> also checks the send lane's targeting, without sending
pnpm probe:calendar # settles whether Calendar has a file lane at all
pnpm probe:safari # History.db, and the Reading List hiding inside Bookmarks.plist
pnpm probe:contacts # the resolver Messages needs — its own TCC grant, not Full Disk AccessCada superficie sondeada ahora tiene un paquete. Safari es de solo lectura — no registra ninguna herramienta mutadora, y eso es una decisión registrada en lugar de una omisión: abrir una URL o añadir a la Lista de Lectura navega un navegador real y visible, y nunca fue sondeado. Ver docs/safari.md. Messages registra exactamente una herramienta de escritura, send_message, que es todo lo que su diccionario de scripting puede hacer.
Cada sonda se degrada en lugar de salir — una aplicación que no se está ejecutando, o un permiso que no se ha concedido, se reporta como un hallazgo — y ninguna de ellas lanza una aplicación a menos que pases --launch. Su mecanismo compartido vive en scripts/lib/probe-kit.mjs.
Los lanzamientos se etiquetan por paquete, así que una etiqueta nombra lo que publica: mail-v1.1.0, reminders-v1.1.0, calendar-v1.1.0, core-v1.1.0. La aplicación se etiqueta app-v1.1.0 y se lanza en su propio carril — un Cupertino.zip firmado y notarizado adjunto al lanzamiento de GitHub, más su SHA-256. Ver docs/distribution.md.
El repositorio es
mcp-cupertino; los paquetes npm siguen siendo@mgcrea/mcp-apple-*, porque eso es lo que la gente busca en npm. Ninguno de los nombres es esencial. El identificador de paqueteio.mgcrea.cupertinoes la cadena que realmente importa, porque cambiarlo invalidaría la concesión de Acceso Total al Disco de cada usuario.
Licencia
Dos, porque las mitades no son lo mismo.
Parte | Licencia |
| MIT — bibliotecas, inclúyelas libremente |
| source-available — leer, auditar, compilar; redistribución binaria reservada |
el binario firmado y notarizado | vendido, bajo el EULA incluido con él |
Cupertino pide Acceso Total al Disco, así que el código fuente permanece legible — eso es lo que se debe a tal concesión, y leerlo es el punto. Ejecutarlo es una cuestión aparte: la comprobación de licencia vive en el código fuente, así que cualquier compilación pide una clave, la tuya o la nuestra. Lo que se vende es el binario notarizado y el mantenimiento detrás de él. Los servidores son MIT y funcionan por sí solos sin ninguna clave. El razonamiento está en docs/licensing.md.
This server cannot be installed
Maintenance
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
- AlicenseNot gradedqualityCmaintenanceA local MCP server that provides a secure bridge for automating macOS applications like Notes, Calendar, and Mail through AppleScript. It uses template-based execution and policy-based allowlists to enable safe, structured interaction with system tools.MIT
- AlicenseBqualityDmaintenanceMCP server for macOS Apple apps. Enables read/write access to Notes, Reminders, Calendar, Contacts, and Safari using SQLite and JXA, all running locally.428122MIT
- AlicenseAqualityCmaintenanceMCP server for privacy-gated local Apple data access including Mail, Messages, Notes, Calendar, Contacts, Photos, Reminders, Voice Memos, and iCloud Drive.301MIT
- AlicenseNot gradedqualityAmaintenanceA collection of MCP servers for Apple macOS apps (Mail, Contacts, Notes, Memory, Messages, Calendar, Reminders) enabling AI assistants to read, search, create, and update data via JXA, SQLite, and EventKit.12MIT
Related MCP Connectors
Search, read, and write your Apple Notes from ChatGPT/Claude via a local Mac agent + MCP relay.
Personal assistant MCP server with search, execute, packages, jobs, secrets, and integrations.
MCP connector for iMessage & Contacts via a local Mac agent + Vercel relay
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/mgcrea/mcp-cupertino'
If you have feedback or need assistance with the MCP directory API, please join our Discord server