che-ical-mcp

Servidor MCP nativo de macOS para Calendario y Recordatorios con 24 herramientas que utiliza Swift EventKit: admite eventos recurrentes, activadores de ubicación, búsqueda y operaciones por lotes

Documentación

che-ical-mcp

License: MIT macOS Swift MCP

Dale a Claude control nativo de Calendario y Recordatorios de macOS. Un servidor MCP en Swift construido directamente sobre EventKit: 29 herramientas para eventos, recordatorios, etiquetas, operaciones por lotes, detección de conflictos y deshacer/rehacer. No solo eventos de calendario: también controla Recordatorios y tareas.

English | 繁體中文


Instalación

Claude Code — registra este repositorio como marketplace y luego instala el plugin. El plugin incluye los comandos de barra /today, /week, /quick-event, /remind y un hook de PreToolUse que verifica el día de la semana en cada escritura de evento:

claude plugin marketplace add PsychQuant/che-ical-mcp
claude plugin install che-ical-mcp@che-ical-mcp

Claude Desktop — descarga el .mcpb más reciente desde Releases y haz doble clic para instalarlo.

MCP independiente — el servidor de 29 herramientas por sí solo, sin extras del plugin:

mkdir -p ~/bin
curl -L https://github.com/PsychQuant/che-ical-mcp/releases/latest/download/CheICalMCP -o ~/bin/CheICalMCP && chmod +x ~/bin/CheICalMCP
claude mcp add --scope user --transport stdio che-ical-mcp -- ~/bin/CheICalMCP

En el primer uso, macOS solicitará acceso a Calendario y Recordatorios — haz clic en Permitir. ¿Compilando desde el código fuente, actualizando en el lugar, o ejecutando bajo SSH / launchd / VS Code? Consulta Instalación para la guía completa.


¿Por qué che-ical-mcp?

CaracterísticaOtros MCP de Calendarioche-ical-mcp
Eventos de CalendarioSíSí
Recordatorios/TareasNoSí
Etiquetas # de RecordatoriosNoSí (a nivel MCP)
Búsqueda Multi-palabra claveNoSí
Detección de DuplicadosNoSí
Detección de ConflictosNoSí
Operaciones por LotesNoSí
Zona Horaria LocalNoSí
Desambiguación de FuenteNoSí
Crear CalendarioAlgunosSí
Eliminar CalendarioAlgunosSí
Recordatorios de EventosAlgunosSí
Ubicación y URLAlgunosSí
LenguajePythonSwift (Nativo)

Las 29 Herramientas

Calendarios (4)
HerramientaDescripción
list_calendarsLista todos los calendarios y listas de recordatorios (incluye source_type)
create_calendarCrea un nuevo calendario
delete_calendarElimina un calendario
update_calendarRenombra un calendario o cambia su color (v0.9.0)
Eventos (4)
HerramientaDescripción
list_eventsLista eventos con filtro/orden/límite (v1.0.0)
create_eventCrea un evento (con recordatorios, ubicación, URL, zona horaria por evento, recurrencia con fechas excluidas)
update_eventActualiza un evento (incluyendo zona horaria, recurrencia, alcance para recurrentes)
delete_eventElimina un evento (con soporte de ocurrencias para recurrentes)

Exclusiones de recurrencia (#182): create_event (y cada elemento de create_events_batch) acepta recurrence.excluded_occurrence_dates para omitir fechas específicas en una serie recurrente al momento de la creación — aplicado con semántica de todo-o-nada de mejor esfuerzo (cualquier fallo elimina toda la nueva serie mediante eliminación compensatoria; el fallo de reversión se informa, nunca es silencioso; la primera ocurrencia no puede excluirse), y un undo elimina toda la serie incluyendo exclusiones. Limitación conocida: en reintentos idempotentes, una serie recurrente duplicada con cada fecha solicitada ya ausente es skipped, pero las exclusiones adicionales ya presentes en la serie existente (más allá de las solicitadas) no se detectan.

Recordatorios (8)
HerramientaDescripción
list_remindersLista recordatorios con filtro/orden/límite, extracción de etiquetas (v1.0.0)
create_reminderCrea un recordatorio con fecha de vencimiento, etiquetas (v1.3.0)
update_reminderActualiza un recordatorio (incluyendo etiquetas, clear_due_date) (v1.3.0)
complete_reminderMarca como completado/incompleto
delete_reminderElimina un recordatorio
search_remindersBusca recordatorios por palabra(s) clave o etiqueta (v1.3.0)
list_reminder_tagsLista todas las etiquetas únicas con conteos de uso (v1.3.0)
cleanup_completed_remindersElimina todos los recordatorios completados en una llamada, vista previa dry_run por defecto (v1.7.2)

Recordatorios recurrentes (#194): listar/buscar incluye has_recurrence, completar público recurrence_rules, y due consciente de precisión. La finalización añade operation (resultado de escritura) y next_occurrence (confirmado/desconocido/no_aplica). Usa operation.status en lugar del heredado is_completed: una finalización exitosa puede dejar la siguiente ocurrencia incompleta. La información de sucesor desconocida no debe desencadenar una segunda escritura. Un sucesor con ID diferente o no confirmable se reporta como unknown, nunca como si la serie hubiera terminado. Deshacer una finalización recurrente está protegido por identidad (#204): una vez que el identificador ya no resuelve a la ocurrencia registrada, se niega explícitamente y elimina su entrada de historial en lugar de saturar la pila. Cambio importante (#205): completed debe ser un booleano JSON en complete_reminder / list_reminders / search_reminders — las cadenas y números se rechazan; omitir o null mantiene el significado anterior. Cambio importante (#207, no publicado): el mismo contrato ahora aplica a cada argumento booleano de herramienta (all_day, clear_*, include_completed, dry_run, delete_original). Consulta contrato de respuesta y limitaciones.

Características Avanzadas (10) ✨ Nuevo en v0.3.0+
HerramientaDescripción
search_eventsBusca eventos por palabra(s) clave con coincidencia AND/OR
list_events_quickAtajos rápidos: today, tomorrow, this_week, next_7_days, etc.
create_events_batchCrea múltiples eventos a la vez (con zona horaria por evento)
check_conflictsVerifica eventos superpuestos en un rango de tiempo
copy_eventCopia un evento a otro calendario (con movimiento opcional)
move_events_batchMueve múltiples eventos a otro calendario
delete_events_batchElimina eventos por IDs o rango de fechas, con vista previa dry-run (v1.0.0)
find_duplicate_eventsEncuentra eventos duplicados entre calendarios (v0.5.0)
create_reminders_batchCrea múltiples recordatorios a la vez (v0.9.0)
delete_reminders_batchElimina múltiples recordatorios a la vez (v0.9.0)
Deshacer/Rehacer (3) ✨ Nuevo en v1.4.0
HerramientaDescripción
undoDeshace la operación más reciente de calendario/recordatorio
redoRehace la última operación deshecha
undo_historyLista operaciones deshacibles con marcas de tiempo

Instalación

Las rutas rápidas están en la parte superior de este README. Esta es la referencia completa: configuración manual, compilación desde el código fuente, casos límite de permisos, actualizaciones en el lugar y modo CLI.

Requisitos

  • macOS 14.0+ (Sonoma o posterior — requerido desde v1.11.0 para la API completa de permisos TCC)
  • Xcode Command Line Tools (solo si compilas desde el código fuente)

Claude Desktop

Un clic (recomendado): descarga el che-ical-mcp-<version>.mcpb más reciente desde Releases, haz doble clic y reinicia Claude Desktop.

Configuración manual: descarga el binario y luego apunta claude_desktop_config.json hacia él.

curl -L https://github.com/PsychQuant/che-ical-mcp/releases/latest/download/CheICalMCP -o /usr/local/bin/che-ical-mcp
chmod +x /usr/local/bin/che-ical-mcp

Edita ~/Library/Application Support/Claude/claude_desktop_config.json y luego reinicia Claude Desktop:

{
  "mcpServers": {
    "che-ical-mcp": {
      "command": "/usr/local/bin/che-ical-mcp"
    }
  }
}

Claude Code — plugin (recomendado)

claude plugin marketplace add PsychQuant/che-ical-mcp
claude plugin install che-ical-mcp@che-ical-mcp
  • Dentro de Claude Code, los equivalentes de barra /plugin marketplace add PsychQuant/che-ical-mcp y /plugin install che-ical-mcp@che-ical-mcp funcionan de la misma manera.
  • Añade el marketplace a través de su repositorio Git (owner/repo), no una URL marketplace.json cruda — el source del plugin es una ruta relativa del mismo repositorio (./plugin) que solo se resuelve cuando se añade vía Git.
  • También incluido en el agregador psychquant-claude-plugins (claude plugin install che-ical-mcp@psychquant-claude-plugins); ambos sirven el mismo binario versionado.
  • El wrapper descarga automáticamente el binario a ~/bin/CheICalMCP en el primer uso si aún no está allí.

Claude Code — MCP independiente

mkdir -p ~/bin

# If upgrading, remove the old binary first. On macOS 26 the kernel can kill a
# fresh binary that inherits a stale code-signature cache from the old inode —
# one a running MCP process may still be holding open.
rm -f ~/bin/CheICalMCP

curl -L https://github.com/PsychQuant/che-ical-mcp/releases/latest/download/CheICalMCP -o ~/bin/CheICalMCP
chmod +x ~/bin/CheICalMCP

# --scope user: available in all projects  ·  --transport stdio: local stdin/stdout
claude mcp add --scope user --transport stdio che-ical-mcp -- ~/bin/CheICalMCP

💡 Consejo: Mantén el binario en un directorio local como ~/bin/. Las carpetas sincronizadas en la nube (Dropbox, iCloud, OneDrive) pueden provocar tiempos de espera de conexión MCP cuando la sincronización toca el archivo.

Compilar desde el Código Fuente (opcional)

git clone https://github.com/PsychQuant/che-ical-mcp.git
cd che-ical-mcp
make release && make install
claude mcp add --scope user --transport stdio che-ical-mcp -- ~/bin/CheICalMCP

⚠️ Usuarios de Swift 6 / Xcode 18: No ejecutes swift build directamente — el SDK MCP upstream tiene un error de concurrencia (swift-sdk#214). El Makefile lo detecta automáticamente y recurre al modo de lenguaje Swift 5.

Otorgar Permisos

En el primer uso, macOS solicitará acceso a Calendario y Recordatorios. Haz clic en Permitir para ambos.

⚠️ Nota sobre macOS Sequoia (15.x): El diálogo de permisos se atribuye a la aplicación principal que lanzó el servidor MCP, no al binario en sí. Esto significa:

EntornoPermiso Atribuido A
Claude DesktopClaude Desktop.app ✅ (funciona automáticamente)
Claude Code en Terminal.appTerminal.app ✅ (funciona automáticamente)
Claude Code en VS CodeVS Code ❌ (puede no mostrar el diálogo)
Claude Code en iTerm2iTerm2 ✅ (funciona automáticamente)

Si el diálogo de permisos no aparece (común con VS Code), necesitas añadir NSCalendarsFullAccessUsageDescription al Info.plist de VS Code:

# Añadir descripción de uso de calendario a VS Code
/usr/libexec/PlistBuddy -c "Add :NSCalendarsFullAccessUsageDescription string 'VS Code needs calendar access for MCP extensions.'" \
  "/Applications/Visual Studio Code.app/Contents/Info.plist"
/usr/libexec/PlistBuddy -c "Add :NSRemindersFullAccessUsageDescription string 'VS Code needs reminders access for MCP extensions.'" \
  "/Applications/Visual Studio Code.app/Contents/Info.plist"

# Volver a firmar VS Code (requerido después de modificar Info.plist)
codesign -s - -f --deep "/Applications/Visual Studio Code.app"

# Reinicia VS Code, luego aparecerá el diálogo de permisos

Nota: Esta modificación se sobrescribirá cuando VS Code se actualice. Deberás volver a aplicarla después de cada actualización de VS Code.

Actualizar una instalación existente

El wrapper del plugin se descarga automáticamente en instalaciones nuevas pero no reemplaza un binario existente. Para actualizar en el lugar:

~/bin/CheICalMCP --self-update

Esto consulta GitHub Releases para la última etiqueta, descarga el nuevo binario, lo verifica contra el SHA-256 publicado, la firma de Developer ID del mantenedor y la notarización de Apple, y solo entonces reemplaza atómicamente el actual (cualquier verificación fallida deja tu instalación intacta). Si se está ejecutando como servidor MCP, reinicia tu host MCP (Claude Desktop / Claude Code) después para que tome la nueva versión. Respaldo manual: rm -f ~/bin/CheICalMCP && curl -L https://github.com/PsychQuant/che-ical-mcp/releases/latest/download/CheICalMCP -o ~/bin/CheICalMCP && chmod +x ~/bin/CheICalMCP.

Modo CLI (sin servidor MCP)

Las 29 herramientas se pueden invocar directamente desde la línea de comandos, sin necesidad de servidor MCP:

# Flag-based: --key value pairs
CheICalMCP --cli list_events --start_date 2026-03-29 --end_date 2026-03-30

# JSON via stdin
echo '{"tool":"list_calendars","arguments":{}}' | CheICalMCP --cli

# From Claude Code via shell
claude -p "Run: ~/bin/CheICalMCP --cli list_events_quick --range today"

Útil para trabajos launchd, scripts de shell, pipelines de CI y agentes que prefieren un subproceso sobre el protocolo MCP. Los permisos TCC siguen aplicando — ejecuta CheICalMCP --setup primero si es necesario.


Características de v1.0.0

Análisis Flexible de Fechas

Todos los parámetros de fecha ahora aceptan 4 formatos:

FormatoEjemploInterpretación
ISO8601 completo"2026-02-06T14:00:00+08:00"Fecha y hora exactas (desplazamiento preservado)
Sin zona horaria"2026-02-06T14:00:00"Usa el timezone del evento si se proporciona, de lo contrario la zona horaria del sistema
Solo fecha"2026-02-06"Medianoche en el timezone del evento o zona horaria del sistema
Solo hora"14:00"Hoy a esa hora

Zona Horaria por Evento (v1.5.0)

Establece la zona horaria de visualización para eventos individuales — esencial para itinerarios de viaje con múltiples zonas horarias.

"Create a flight departure at 09:14 Berlin time"
→ create_event(title: "Flight LH123", start_time: "2026-04-08T09:14:00", timezone: "Europe/Berlin", ...)

"Update the hotel check-in to Dubai time"
→ update_event(event_id: "...", timezone: "Asia/Dubai")

"Remove the custom timezone from an event"
→ update_event(event_id: "...", clear_timezone: true)
  • El parámetro timezone acepta identificadores IANA (p. ej., Europe/Berlin, America/New_York, Asia/Taipei)
  • Cuando se proporciona timezone, las fechas y horas ingenuas (sin desplazamiento) se interpretan en esa zona horaria
  • La salida de eventos incluye la zona horaria propia del evento en el campo timezone y formatea start_date_local/end_date_local en consecuencia
  • Disponible en create_event, update_event y create_events_batch
  • Deshacer/rehacer conserva la zona horaria de cada evento

Asistentes y Organizador (Solo Lectura)

Las respuestas de eventos incluyen información de participantes cuando está disponible. Estos campos son de solo lectura debido a las limitaciones de EventKit: no se pueden establecer ni modificar a través del MCP.

Disponible en: list_events, search_events, list_events_quick, check_conflicts

attendees (matriz, opcional) — Presente cuando el evento tiene participantes. Cada objeto de asistente contiene:

CampoTipoDescripción
namecadena o nuloNombre para mostrar, nulo si no está en la libreta de direcciones
emailcadenaDirección de correo electrónico extraída de la URL del participante
rolecadenaUno de: unknown, required, optional, chair, non_participant
statuscadenaUno de: unknown, pending, accepted, declined, tentative, delegated, completed, in_process
typecadenaUno de: unknown, person, room, resource, group
is_current_userbooleanoSi este participante es el usuario actual

organizer (objeto, opcional) — Presente cuando el evento tiene un organizador. Contiene:

CampoTipoDescripción
namecadena o nuloNombre para mostrar
emailcadenaDirección de correo electrónico
is_current_userbooleanoSi el organizador es el usuario actual

Nota: Ambos campos se omiten cuando el evento no tiene participantes ni organizador (p. ej., eventos de calendario local creados sin invitados).

Coincidencia Difusa de Calendarios

Los nombres de calendarios ahora se comparan sin distinguir mayúsculas y minúsculas. Si no se encuentra, el mensaje de error enumera todos los calendarios disponibles.

Herramientas Mejoradas de Listado/Eliminación

  • list_events: filter (todos/pasados/futuros/todo_el_día), sort (asc/desc), limit
  • list_reminders: filter (todos/incompletos/completados/vencidos), sort (fecha_vencimiento/fecha_creación/prioridad/título), limit
  • delete_events_batch: modo de rango de fechas (before_date/after_date) + vista previa de dry_run

Cambio Importante: list_events y list_reminders ahora devuelven {events/reminders: [...], metadata: {...}} en lugar de una matriz simple.


Ejemplos de Uso

Gestión de Calendarios

"List all my calendars"
"What's on my schedule next week?"
"Create a meeting tomorrow at 2 PM titled 'Team Sync'"
"Add a dentist appointment on Friday at 10 AM with location '123 Main St'"
"Delete the meeting called 'Cancelled Meeting'"

Gestión de Recordatorios

"List my incomplete reminders"
"Show all reminders in my Shopping list"
"Add a reminder: Buy milk"
"Create a reminder to call mom tomorrow at 5 PM"
"Mark 'Buy milk' as completed"
"Delete the reminder about groceries"

Gestión de Recordatorios (v1.5.0)

"Remove the due date from 'Buy groceries'"
→ update_reminder(reminder_id: "...", clear_due_date: true)

Funciones Avanzadas (v0.3.0+)

"Search for events containing 'meeting'"
"Search for events with both 'project' AND 'review'"
"What do I have today?"
"Show me this week's schedule"
"Are there any conflicts if I schedule a meeting from 2-3 PM?"
"Create 3 weekly team meetings for the next 3 weeks"
"Copy the dentist appointment to my Work calendar"
"Move all events from 'Old Calendar' to 'New Calendar'"
"Delete all the cancelled events"
"Find duplicate events between 'IDOL' and 'Idol' calendars"

Mejoras de DX (v1.0.0)

"Show my next 5 upcoming events"
→ list_events(start_date: "2026-02-06", end_date: "2026-12-31", filter: "future", sort: "asc", limit: 5)

"Show my overdue reminders"
→ list_reminders(filter: "overdue")

"Preview which events would be deleted from 'Old Calendar' before 2025"
→ delete_events_batch(calendar_name: "Old Calendar", before_date: "2025-01-01", dry_run: true)

"Create an event at 2 PM" (no need for full ISO8601!)
→ create_event(start_time: "14:00", end_time: "15:00", ...)

Fuentes de Calendario Compatibles

Funciona con cualquier calendario sincronizado con la app Calendario de macOS:

  • Calendario de iCloud
  • Google Calendar
  • Microsoft Outlook/Exchange
  • Calendarios CalDAV
  • Calendarios locales

Desambiguación de Calendarios con el Mismo Nombre (v0.6.0+)

Si tienes calendarios con el mismo nombre de diferentes fuentes (p. ej., "Trabajo" tanto en iCloud como en Google), usa el parámetro calendar_source:

"Create an event in my iCloud Work calendar"
→ create_event(calendar_name: "Work", calendar_source: "iCloud", ...)

"Show events from my Google Work calendar"
→ list_events(calendar_name: "Work", calendar_source: "Google", ...)

Si se detecta ambigüedad, el mensaje de error enumerará todas las fuentes disponibles.


Solución de Problemas

ProblemaSolución
Servidor desconectadoReconstruir con make release && make install
Permiso denegadoConceder acceso a Calendario/Recordatorios en Configuración del Sistema > Privacidad y Seguridad
El diálogo de permisos nunca apareceVer Conceder Permisos para la solución en macOS Sequoia
Permiso denegado por SSHVer Acceso SSH a continuación
Permiso denegado bajo launchdVer launchd / Automatización a continuación
Un servicio denegado mientras todos los diagnósticos reportan verdeVer Denegación permanente silenciosa tras la actualización a continuación
Calendario/Recordatorios se rompen de nuevo tras cada actualización de Claude CodeVer Las actualizaciones de Claude Code rotan la concesión del lado del host a continuación
Calendario no encontradoAsegúrate de que el calendario sea visible en la app Calendario de macOS
Recordatorios no se sincronizanVerifica la sincronización de iCloud en Configuración del Sistema

Denegación permanente silenciosa tras la actualización (#154)

Si un servicio (típicamente Calendario) devuelve access denied mientras el otro funciona, y --print-tcc-path y Configuración del Sistema reportan el permiso como concedido, probablemente estés ante la firma #154: una fila TCC creada por una compilación previa a v1.7.1 (firmada ad-hoc) está anclada a los hashes de código de esa compilación antigua. El binario actualizado con Developer ID nunca puede coincidir, y en macOS 26.5+ el sistema operativo solo permite el re-prompt de reparación cuando el binario lleva la entitlement com.apple.security.personal-information.* correspondiente.

A partir de v1.14.0+, el banner de inicio muestra esto directamente — una línea [drift] TCC.db <service> entry pins a code requirement this binary no longer satisfies (#155) — cuando la verificación del framework de Seguridad puede confirmar el desajuste de csreq. Antes de eso, todos los diagnósticos de la API de estado (incluido el banner) reportaban verde, que es exactamente lo que hacía esta clase silenciosa. Si te encuentras con la denegación a través de la instalación de .mcpb de Claude Desktop, el mensaje de denegación ahora nombra el bloqueador real y las rutas de trabajo en lugar del callejón sin salida --setup (#158).

Solución: actualiza a v1.11.0 o posterior (el binario ahora incluye ambas entitlements), reinicia la app host (Cmd+Q completo para Claude Desktop) y aprueba el diálogo de permisos que aparece en el primer acceso a Calendario/Recordatorios. Aprobar reescribe la fila TCC anclada al requisito de Developer ID, por lo que sobrevive a todas las actualizaciones futuras. Si accidentalmente deniegas el diálogo, vuelve a habilitar el interruptor correspondiente en Configuración del Sistema → Privacidad y Seguridad → Calendarios o Recordatorios.

⚠️ Fe de erratas para la solución de la era #108: tccutil reset Calendar com.checheng.CheICalMCP no funciona para un binario desnudo (sin paquete) — falla con OSStatus error -10814 porque el binario no tiene registro de LaunchServices. Y no ejecutes un tccutil reset Calendar desnudo (sin un ID de paquete): borra las concesiones de Calendario para todas las apps de la máquina y, en un binario sin entitlements, deja a CheICalMCP permanentemente incapaz de volver a solicitar permiso.

Las actualizaciones de Claude Code rotan la concesión del lado del host (#170)

Bajo una instalación nativa de Claude Code, el ejecutable real reside en una ruta versionada (~/.local/share/claude/versions/<version>; ~/.local/bin/claude es solo un enlace simbólico), y TCC de macOS ancla la concesión de Calendario/Recordatorios del lado del host a esa ruta. Cada actualización automática de Claude Code rota la ruta e invalida silenciosamente la concesión — el síntoma clásico es "funcionaba ayer, roto justo después de una actualización", con Configuración del Sistema acumulando entradas obsoletas con números de versión desnudos (2.1.202, 2.1.203, …).

Solución: dispara cualquier llamada de herramienta de calendario desde Claude Code para que macOS vuelva a solicitar (o recree la entrada), luego activa la entrada más nueva con número de versión en Configuración del Sistema → Privacidad y Seguridad → Calendarios / Recordatorios. Lista de verificación completa: la habilidad troubleshoot-tcc (/che-ical-mcp:check-tcc). La causa raíz es ascendente (seguida en #170 — Claude Code necesitaría una identidad TCC estable); este repositorio solo puede detectarla y documentarla.

Acceso SSH

TCC de macOS (Transparencia, Consentimiento y Control) otorga permisos de privacidad por aplicación. Las sesiones SSH se ejecutan bajo sshd, que es un contexto de seguridad diferente — por lo que los permisos concedidos a Terminal o Claude Code localmente no se transfieren a SSH.

Solución A — Ejecutar localmente primero (recomendado):

  1. Ejecuta CheICalMCP una vez en la Mac de destino localmente (no por SSH)
  2. Concede acceso a Calendario y Recordatorios cuando aparezca el diálogo TCC
  3. Las sesiones SSH deberían entonces heredar la concesión para el binario CheICalMCP

Solución B — Conceder Acceso Total al Disco a sshd:

  1. Abre Configuración del Sistema → Privacidad y Seguridad → Acceso Total al Disco
  2. Haz clic en +, presiona ⌘⇧G, escribe /usr/sbin/sshd y agrégalo
  3. Reinicia la sesión SSH

⚠️ La Solución B concede a sshd acceso amplio a archivos — úsala solo en máquinas que controles por completo.

launchd / Automatización

Al ejecutar CheICalMCP desde launchd, cron u otra automatización no interactiva, TCC de macOS no puede mostrar diálogos de permisos. Usa --setup para pre-conceder permisos:

# Step 1: Run once from Terminal (triggers TCC permission dialog)
CheICalMCP --setup

# Step 2: Grant Calendar & Reminders access in the dialog that appears
# Step 3: The binary now has permission — launchd jobs can use it

Detección: CheICalMCP detecta automáticamente sesiones no interactivas (falta la variable de entorno TERM o hijo directo de launchd) y proporciona mensajes de error específicos con instrucciones de --setup. Esto funciona incluso para cadenas de lanzamiento indirectas (launchd → Claude Code → CheICalMCP).

--setup en sesiones no interactivas (#143): si ejecutas --setup desde una sesión no interactiva (sin TERM / hijo directo de launchd) y el permiso aún no está determinado, --setup ahora omite la solicitud y sale con código distinto de cero en lugar de colgarse — un diálogo TCC no puede aparecer allí, así que imprime instrucciones de concesión manual en lugar de bloquear. Ejecuta --setup desde una Terminal real para disparar el diálogo. (Un binario ya autorizado aún reporta éxito incluso si se vuelve a ejecutar de forma no interactiva.)

Nota: Si --setup concede permiso pero el MCP aún falla bajo launchd, TCC puede haber asociado el permiso con el proceso padre. En ese caso, agrega manualmente CheICalMCP en Configuración del Sistema → Privacidad y Seguridad → Calendario/Recordatorios.


Detalles Técnicos

  • Versión Actual: v1.15.0
  • Framework: MCP Swift SDK v0.12.0
  • API de Calendario: EventKit (framework nativo de macOS)
  • Transporte: stdio
  • Plataforma: macOS 14.0+ (Sonoma y posteriores — elevado desde 13.0 en el clúster posterior a 1.10 según #119)
  • Herramientas: 29 herramientas para calendarios, eventos, recordatorios, etiquetas, deshacer/rehacer, limpieza y operaciones avanzadas

Historial de Versiones

VersiónCambios
v1.18.0Deshacer/rehacer completo para recordatorios, discard_id explícito, booleanos estrictos en todas partes (#196/#197/#198/#199/#202/#203/#206/#207/#208/#209/#211/#212/#214/#215/#216): deshacer escribe de vuelta el completion_date registrado y rehacer restaura el instante guardado en lugar de reinferir el estado; la finalización repetida conserva el instante original; undo(discard_id) elimina explícitamente un registro de cabecera bloqueado mediante su ID undo_history estable (los objetivos no encontrados ya no bloquean la pila para recordatorios eliminados o eventos muertos); los movimientos de eventos registran la ocurrencia de origen para que deshacer restaure en el calendario original; event_recurrence_rules / reminder_recurrence_rules distinguen los dos formatos (los alias heredados se conservan); cada anotación de herramienta tiene una política probada explícita, con complete_reminder, las herramientas de actualización y movimiento opcional ahora son destructiveHint: true. ROMPE CAMBIOS: cada argumento booleano de herramienta es un booleano JSON estricto (cadenas/números rechazados con <key> must be a boolean). Internos: lista de recordatorios/búsqueda/filtro/orden/limite dentro del actor pre-instantánea, list_reminder_tags en la costura de instantánea, escaneo de etiquetas lineal (sin retroceso exponencial), resultados de escritura de recordatorios Sendable inmutables, métodos de finalización/historial recurrente en una extensión de actor dedicada.
v1.17.0Recurrencia de recordatorios en lectura, resultados de finalización explícitos, deshacer protegido por identidad, completed booleano estricto (#194/#204/#205): list_reminders / search_reminders exponen has_recurrence, recurrence_rules completo (incl. frequency_raw_value) y un objeto due; complete_reminder separa el resultado de escritura (operation) del objeto guardado (observed) e informa el sucesor como next_occurrence — observado una vez, sincrónicamente, después de guardar (iCloud en el dispositivo: el mismo ID avanza en el lugar, la ocurrencia finalizada se archiva bajo un nuevo ID), mensaje en el reloj de pared del propio recordatorio. Deshacer/rehacer de una finalización recurrente está protegido por identidad: una vez que el ID ya no resuelve a la ocurrencia registrada, se niega explícitamente y descarta la entrada para que las operaciones más antiguas sigan siendo deshacibles; no encontrado permanece transitorio (#191). ROMPE CAMBIOS: completed debe ser un booleano JSON en las tres herramientas de recordatorios (cadenas/números rechazados antes de cualquier lectura o escritura; omitir/null conservan su significado; --cli JSON null → omitido). Tres rondas de verificación 6-AI en el PR #195 más rondas en #200 / #201; dos sondas stdio en el dispositivo. 582 pruebas.
v1.16.1Correcciones de seguridad de tipos + verificación en el dispositivo (#184/#190/#191): recurrence no objeto ahora rechazado (antes se descartaba silenciosamente); all_day + emparejamiento timezone rechazado (antes eliminaba silenciosamente la bandera de todo el día y anulaba exclusiones a través de la línea de fecha); deshacer de una eliminación de serie recurrente reconstruye reglas desde instantáneas de valor (corrige EKCADErrorDomain 1010) y un deshacer/rehacer fallido ya no consume la entrada. 529 pruebas.
v1.16.0Exclusiones de recurrencia + completitud de deshacer + habilidad de archivo de eventos (#182/#185/#180): excluded_occurrence_dates en create_event/batch (creación de dos pasos luego eliminación, eliminación compensatoria, primera ocurrencia no excluible); las eliminaciones de lote/serie ahora registran entradas de deshacer (una unidad .batch); deshacer de una creación recurrente ahora elimina toda la serie; nueva habilidad archive-event — archivo de fuente a evento con seguimiento de corrección y configuración de proyecto .claude/.ical/. 514 pruebas.
v1.15.0Contexto de ejecución --print-tcc-path + señal de banner de host versionado. El diagnóstico TCC ahora imprime su cadena de procesos padre (self → … → launchd) con una advertencia de dependencia de contexto — el estado de autorización sigue el contexto del proceso responsable (#168), por lo que saber bajo qué host se ejecutó la consulta es crucial (#169). Pulido de diagnósticos de cadena padre: marcadores visibles de truncamiento/ciclo, enlace de comm vacío, ps -ww, informes de fallo de decodificación/salida, precisión de NOTA (#173). Nueva señal de detector de deriva: "host Claude Code versionado + EventKit no concedido" — explica la rotura de actualización #170 proactivamente al inicio, suprimiendo la pista contradictoria --setup en ese escenario (#175). Los tres verificados por conjuntos de modelos cruzados 6-AI; una brecha de escape terminal CWE-150 encontrada por verificación fue corregida antes de la fusión. Deriva de versión desde el lanzamiento del plugin v1.14.2 alineada en los cinco sitios de versión (#172). 490 pruebas.
v1.14.2Lanzamiento de capa de documentación/habilidad — modelo de autorización TCC de dos capas (#168): habilidad troubleshoot-tcc, /check-tcc, mcpb/README.md y plugin/CLAUDE.md ahora documentan la capa TCC de la aplicación anfitriona (proceso responsable), las entradas de Configuración del Sistema de número de versión simple (2.1.202 = binario versionado de Claude Code) y el procedimiento de verificación de alternar y observar. Binario byte-idéntico a v1.14.1 en el momento del lanzamiento (solo shell de plugin; sitios de versión de fuente alineados más tarde en #172).
v1.14.1Corrección de metadatos — consistencia de conteo de herramientas. server.json description decía "24 herramientas" y PROMOTION.md decía "20 herramientas"; el servidor realmente expone 29 herramientas (coincidiendo con mcpb/manifest.json long_description y el guardián de paridad de herramientas ManifestParityTests). Corregido el server.json orientado al registro, docs/COMPETITIVE_ANALYSIS.md y PROMOTION.md a 29. Sin cambios de código o superficie de herramientas — funcionalmente idéntico a v1.14.0; este lanzamiento existe únicamente para publicar metadatos de registro corregidos (las versiones de registro son inmutables).
v1.14.0Corrección de inyección de herramientas de Claude Desktop (#166): un & literal en mcpb/manifest.json display_name hizo que Desktop 1.18286.0 descartara silenciosamente todo el servidor de 29 herramientas de cada conversación (Claude Code no afectado); cambiado & → and, confirmado por intervención de variable única en la instalación fallida + un guardián de regresión ManifestParityTests. También alineado serverInfo.name al id de manifiesto kebab (higiene; refutado empíricamente como la causa). Lote hermano #154: señal de deriva TCC de desajuste csreq (#155, autocomprobación SecCodeCheckValidity para la clase de denegación silenciosa), mensaje de denegación .mcpb ya no termina en callejón sin salida en --setup para la firma ya .denied (#158), insignia macOS 13.0 → 14.0 (#157), swift-nio 2.96 → 2.101 (#159). 454 pruebas.
v1.13.0SwiftUI SetupWindow (#164): --setup interactivo presenta una ventana de estado en vivo (botones de Conceder por entidad + ruta de binario resuelta) dentro del NSApplication de primer plano #163. Corrección de Calendar denegado en Desktop (#165): isNonInteractive se disparó erróneamente en TERM == nil para servidores generados por aplicaciones GUI → falló rápido antes de requestFullAccess, por lo que el diálogo de primera concesión nunca apareció a través de Claude Desktop; ahora usa una señal de sesión GUI CGSession. 429 pruebas.
v1.12.0--setup de primer plano (#163): --setup interactivo ahora se ejecuta dentro de un NSApplication de primer plano para que el modal TCC de Calendar de EventKit realmente se presente (antes se denegaba silenciosamente desde un contexto CLI asíncrono simple). Mensajes de denegación + banner de inicio muestran la ruta de binario resuelta + un comando "<path>" --setup copiable para el binario .mcpb enterrado.
v1.11.1Validación de rango de tiempo create_event (#160): simétrico con update_event — rechaza eventos temporizados invertidos / de duración cero mediante un guardián compartido validateTimeRange. 405 pruebas.
v1.11.0Re-prompt de curación TCC desbloqueado (#154): Entitlements.plist incluye personal-information.calendars + .reminders — instalaciones de larga duración pre-v1.7.1 podían sufrir denegación permanente silenciosa de Calendar en macOS 26.5 (fila TCC fijada a cdhashes antiguos, re-prompt de curación bloqueado por política porque el binario no incluía entitlements, cada diagnóstico reportando verde); puerta de lanzamiento de binario firmado verifica ambas claves. Endurecimiento de EventKit no interactivo (#131 / #143 / #144 + #146–#150). ROMPE CAMBIOS: piso de despliegue elevado a macOS 14.0 (#119). 401 pruebas.
v1.10.0Detector de deriva TCC + banner de inicio (#122): banner stderr de un solo disparo al inicio del servidor MCP con versión/ruta/PID + señales de deriva (desajuste de ruta TCC.db por servicio, procesos obsoletos); exclusión voluntaria mediante CHE_ICAL_MCP_NO_BANNER=1. Corrección de interbloqueo de tubería en ayudantes de subprocesos; defensa de inyección stderr CWE-117 en todos los valores de banner interpolados.
v1.9.0Refactorización de puerta de acceso TCC (#108 Fase 2, cierra #109): eliminado el anti-patrón de caché has*Access de duración de proceso; EKEventStore.authorizationStatus(for:) por llamada mediante nueva costura AuthorizationGate + AuthorizationStatusSource (patrón Apple TN3153) — los cambios de estado aparecen inmediatamente en lugar de fallo silencioso de concesión obsoleta. Añade bandera de diagnóstico --print-tcc-path.
v1.8.1Documentación: guía de configuración de permisos TCC post-instalación / actualización mcpb/README.md (#108 Fase 1).
v1.8.0Ola de consistencia de formato de cable + parámetros de forma de respuesta (grupo #101 — 5 problemas cerrados en 3 días, todo Refs #N IDD + verificación de conjunto 6-AI). Parámetros de forma de respuesta de listado de eventos (#47 / #101): detail_level (summary/standard), lista de permitidos fields, display_timezone (IANA estricto), limit (límite 10000) — ajuste de verbosidad LLM. Unificación de envoltura (#102 / #107, formato de cable rompedor): list_events.metadata.returned + list_reminders.metadata.returned eliminados; las 5 envolturas de lista/búsqueda usan <entity>_count de nivel superior con semántica pre-límite; search_reminders.result_count → reminder_count; search_reminders gana parámetro limit (espejo search_events). Los clientes MCP que leen metadata.returned o result_count deben actualizar. Endurecimiento de validador (#101 F1–F3): requireOptionalInt usa Int(exactly:) cerrando la trampa DoS Int.max; los validadores detail_level / display_timezone distinguen ausente vs. no cadena (sin coerción silenciosa). Detección de deriva anclada en tiempo de ejecución (#103, fortaleciendo #101 M3): prueba de divergencia formatEventDict ↔ validEventFields ahora mediante costura EventFormattingSource + FakeFormattableEvent. Reclasificación de CHANGELOG (#106): renombres de formato de cable movidos de Fixed a Changed (Keep a Changelog 1.1.0). Corrección de tubería de lanzamiento: la comprobación de defensa previa al empaquetado ahora deriva el ID de Equipo del certificado DEVELOPER_ID (antes comparaba hash SHA contra cadena legible Authority=).
v1.7.2Ola de endurecimiento + características (30+ commits sobre v1.7.1, todo Refs #N IDD con verificación 6-AI). --self-update (#49) + verificación binaria SHA-256 (#98): ruta de actualización de instalación existente con garantía criptográfica contra lanzamientos corruptos. make install-signed (#50): flujo TCC de desarrollador mantenedor en macOS 26 — fallo rápido en ID de Desarrollador faltante + verificación forzada de codesign. Flujo de trabajo de pruebas CI (#51): swift build + swift test en tiempo de PR en macos-latest. Grupo de endurecimiento de sanitizadores: escapeForStderr cobertura completa C0+DEL (#73), sanitizeForInterpolation para interpolación de título executeUndo/executeRedo (#74), stderr de CLIRunner delegado a writeFailureLog para exención de rama confiable (#80), límite DoS de 1024 caracteres writeFailureLog (#86), documento de contrato controlado solo por autor CLIError.invalidJSON (#85), seguridad de hilos FileHandle.standardError.write + PIPE_BUF=512 de macOS documentado (#70 / #94). Pulido de distribución: fragmentos de instalación de caché de codesign obsoleta obtienen preámbulo rm -f (#90 paridad zh-TW para #62). Pulido post-v1.7.1 (#46 #57 #58 #60): paridad de interpolación de error de rehacer, renumeración de pasos build-mcpb.sh, documentación Entitlements.plist, nota de cwd Makefile release-signed:. Herramienta cleanup_completed_reminders (#21): limpieza de una sola llamada de todos los recordatorios completados, predeterminado dry_run=true.
v1.7.1Endurecimiento de seguridad (#20 #26): validación de entrada (límites de longitud + lista de permitidos de esquema URL) en todos los puntos de entrada de eventos/recordatorios, envoltura de inyección de prompt en respuestas de lectura MCP, validación de límite de análisis para days_of_week / days_of_month / alarms_minutes_offsets (lanza en lugar de descartar silenciosamente valores inválidos), puesta al día Info.plist, 42 nuevas pruebas de regresión.
v1.7.0Información de asistente y organizador (#17): array attendees de solo lectura y objeto organizer en respuestas de eventos. Método compartido formatEventDict refactorizado.
v1.6.0Indicador --setup (#13): preautoriza permisos de TCC para launchd/automatización. Detección de sesión no interactiva (TERM + ppid). Mensajes de error combinados de SSH+launchd. Modo --cli (#14): invoca las 28 herramientas directamente desde la línea de comandos sin servidor MCP. Modos basados en indicadores (--key value) y stdin JSON. Inferencia inteligente de tipos para parámetros bool/int/double/array. MCP Swift SDK 0.12.0 (compatibilidad Swift 6.3).
v1.5.0Zona horaria por evento (#12): parámetro timezone en create_event/update_event/create_events_batch, la salida del evento usa la zona horaria propia del evento, fechas/horas ingenuas analizadas en la zona horaria del evento. Borrar fecha de vencimiento (#9): clear_due_date en update_reminder. Validación de día de semana (#5): create_event/update_event validan el día de semana start_time contra days_of_week. Deshacer/rehacer (#8): 3 nuevas herramientas (undo, redo, undo_history). Correcciones de eventos recurrentes (#7): eliminación/actualización a nivel de ocurrencia con occurrence_date. Compilación Swift 6 (#11): README actualizado para el flujo de trabajo make release
v1.4.0Fiabilidad de LLM: corrige el rango de búsqueda predeterminado (±2 años en lugar de distantPast/Future), metadatos searched_range en la respuesta de search_events, sugerencias similar_events en create_events_batch, consejos de LLM en descripciones de herramientas
v1.3.1Corrección de documentación: se aclaró que las etiquetas son a nivel de MCP (no etiquetas nativas de Reminders.app); Apple no proporciona API pública para etiquetas nativas
v1.3.0Etiquetas de recordatorios (nivel MCP): texto #hashtag almacenado en notas para create_reminder/update_reminder/create_reminders_batch, filtrado basado en etiquetas en search_reminders, nueva herramienta list_reminder_tags; MCP SDK 0.11.0. Nota: las etiquetas son buscables a través de MCP pero no aparecen como etiquetas nativas de Reminders.app (Apple no proporciona API pública para esto)
v1.2.0Escrituras idempotentes: create_event, create_events_batch, create_reminder, create_reminders_batch, create_calendar ahora verifican antes de escribir para prevenir duplicados en reintentos; las respuestas incluyen el conteo de skipped
v1.1.0Recurrencia + Ubicación: eventos/recordatorios recurrentes (diario/semanal/mensual/anual), ubicaciones estructuradas con coordenadas, disparadores de recordatorios basados en ubicación (entrada/salida de geocerca), salida de recurrencia enriquecida
v1.0.0Mejoras de DX: análisis flexible de fechas (4 formatos), coincidencia difusa de calendarios, filtro/orden/límite de list_events/list_reminders, modo de prueba en seco + rango de fechas de delete_events_batch
v0.9.04 nuevas herramientas (20→24): update_calendar, search_reminders, create_reminders_batch, delete_reminders_batch
v0.8.2Soporte de semana i18n: parámetro week_starts_on para list_events_quick (lunes/domingo/sábado/sistema)
v0.8.1Corrección: error de validación de tiempo de update_event, preservación de duración al mover eventos
v0.8.0RUPTURA: calendar_name ahora es obligatorio para operaciones de creación (sin valores predeterminados implícitos)
v0.7.0Anotaciones de herramientas para el Directorio de Conectores de Anthropic, mecanismo de actualización automática, descripciones de herramientas por lotes mejoradas
v0.6.0Desambiguación de fuentes: parámetro calendar_source para calendarios con el mismo nombre
v0.5.0Eliminación por lotes, detección de duplicados, búsqueda de múltiples palabras clave, errores de permisos mejorados, PRIVACY.md
v0.4.0Copiar/mover eventos: copy_event, move_events_batch
v0.3.0Funciones avanzadas: búsqueda, rango rápido, creación por lotes, verificación de conflictos, visualización de zona horaria
v0.2.0Reescritura en Swift con soporte completo de Reminders
v0.1.xVersión en Python (obsoleta)

Contribución

¡Las contribuciones son bienvenidas! No dudes en enviar un Pull Request.

Proceso de lanzamiento (para mantenedores)

Los números de versión viven en tres lugares con semánticas diferentes:

ArchivoRolCuándo actualizar
Sources/CheICalMCP/Version.swift — AppVersion.currentFuente de verdad; aparece en --version, help y MCP serverInfo.versionCada lanzamiento
Sources/CheICalMCP/Info.plist — CFBundleVersionVersión del paquete macOSCada lanzamiento; debe coincidir con AppVersion.current
mcpb/manifest.json — versionManifiesto del paquete de Claude Desktop enviado dentro de .mcpbCada lanzamiento; debe coincidir con AppVersion.current
server.json — version + packages[].identifier + fileSha256Instantánea de envío al Registro MCPSolo al reenviar un nuevo .mcpb al Registro MCP (cadencia independiente)

scripts/build-mcpb.sh garantiza que los primeros tres coincidan; fallará la compilación si alguno se desvía. server.json está intencionalmente desacoplado porque actualizarlo requiere reconstruir .mcpb, un SHA256 nuevo y un reenvío — pasos que no ocurren en cada lanzamiento de código fuente.

Firma y Notarización (requerido para macOS 26+)

A partir de v1.7.1, los binarios de lanzamiento están firmados con un certificado de Developer ID Application y notarizados a través del notarytool de Apple. Esto es obligatorio en macOS 26 — los binarios firmados ad-hoc no pueden activar los diálogos de permisos TCC de Calendar / Reminders allí.

Requisitos previos (configuración única):

  1. Inscripción en el Programa de Desarrolladores de Apple.
  2. Certificado de Developer ID Application instalado en el llavero de inicio de sesión.
    • Verifica con: security find-identity -p codesigning -v (debe mostrar Developer ID Application: <Your Name> (<TeamID>)).
    • Tu Team ID es propio — encuéntralo en https://developer.apple.com/account → Detalles de Membresía. (El 6W377FS7BS del mantenedor mostrado en cualquier parte de este repositorio es solo de referencia.)
  3. Perfil de llavero notarytool (cualquier nombre; che-ical-mcp es el predeterminado que busca el script de compilación).
    • Crea interactivamente (recomendado — mantiene la contraseña fuera del historial del shell):
      xcrun notarytool store-credentials che-ical-mcp --apple-id <your-apple-id> --team-id <your-team-id>
      # notarytool will prompt for the app-specific password
      
    • Contraseña específica de la aplicación: genera en https://account.apple.com → Iniciar sesión y Seguridad → Contraseñas específicas de la aplicación. Usa una contraseña de un solo propósito (por ejemplo, nombrada che-ical-mcp); revoca y regenera si se filtra. Nunca la pases mediante --password en la línea de comandos — termina en ~/.zsh_history.
  4. Exporta tu identidad para el script de compilación:
    export DEVELOPER_ID='Developer ID Application: <Your Name> (<TeamID>)'
    export NOTARY_PROFILE='che-ical-mcp'   # match what you set up in step 3
    
    Persiste estas en ~/.zshrc o en un .envrc local del proyecto (ignorado por git). El script intencionalmente no tiene valores predeterminados para estos, para que un fork nuevo no falle con errores que se refieran a la identidad del mantenedor.

Flujo por lanzamiento:

make release-signed     # builds universal binary → signs + notarizes → packages .mcpb
gh release create vX.Y.Z mcpb/server/CheICalMCP mcpb/server/CheICalMCP.sha256 mcpb/che-ical-mcp-X.Y.Z.mcpb mcpb/che-ical-mcp-X.Y.Z.mcpb.sha256 --notes "..."

make release-signed ejecuta scripts/build-mcpb.sh, que después de crear el binario universal llama a scripts/sign-and-notarize.sh. El script de firma realiza verificaciones previas (certificado + perfil de notarytool) y falla rápidamente con mensajes amigables si falta algo. La notarización normalmente toma de 1 a 15 minutos (notarytool submit --wait bloquea hasta que Apple termine).

Verificación después de la compilación (ejecuta las tres para confirmar de extremo a extremo):

# 1. Signature properties (cert + hardened runtime + team ID)
codesign -dv --verbose=2 mcpb/server/CheICalMCP
# Expected:
#   Authority=Developer ID Application: <Your Name> (<TeamID>)
#   TeamIdentifier=<TeamID>
#   flags=0x10000(runtime)
#   Signature size in the few thousand bytes range (varies by cert chain)

# 2. Signature integrity
codesign --verify --deep --strict --verbose=2 mcpb/server/CheICalMCP
# Expected: exit 0, no warnings

# 3. Notarization end-to-end (this is the real "Gatekeeper would accept" gate)
spctl -a -vvv -t install mcpb/server/CheICalMCP
# Expected: <binary>: accepted; source=Notarized Developer ID
#
# Note on flag choice (verified empirically on macOS 26.4.1, 2026-05-04):
#   -t execute → rejected "code is valid but does not seem to be an app"
#                (Apple's "execute" type expects a .app bundle structure,
#                 not raw Mach-O CLI binaries)
#   -t install → accepted; source=Notarized Developer ID  ← use this
#   -t open    → rejected "Insufficient Context"
#
# Apple's Code Signing Guide describes -t execute as the assessment type for
# "applications and tools", but on macOS 26 raw Mach-O binaries fall through
# the .app bundle check. -t install is the documented assessment type for
# software being installed (which describes how a CLI binary lands in ~/bin),
# and is the type that returns the actual notarization verdict in practice.
# Re-test if Apple changes this behavior in a future macOS update.

Iteración de desarrollo local sin latencia de firma:

SKIP_CODESIGN=1 ./scripts/build-mcpb.sh   # ad-hoc signed; do NOT ship the result
make install                              # installs ad-hoc to ~/bin (dev only)

El script build-mcpb.sh también omite automáticamente la firma cuando DEVELOPER_ID no está configurado O el certificado no está en tu llavero — así los contribuyentes / CI / forks pueden compilar un .mcpb sin firmar funcional para pruebas sin configurar manualmente SKIP_CODESIGN. (Verás una advertencia clara de "Omitiendo codesign" cuando esto ocurra.)

Entorno de identidad de firma:

Variable de entornoPredeterminadoRequerido para
DEVELOPER_ID(sin configurar — omite firma automáticamente)Lanzamiento firmado
NOTARY_PROFILE(sin configurar — falla rápido en sign-and-notarize.sh)Lanzamiento firmado
ENTITLEMENTSSources/CheICalMCP/Entitlements.plistArchivo de entitlements personalizado
SKIP_CODESIGN(sin configurar)Forzar omisión de firma incluso con certificado presente (configurar a 1 o true)
REQUIRE_CODESIGN(sin configurar)Falla rápida si faltan requisitos de firma (configurar a 1 por make release-signed — la ruta de lanzamiento canónica no debe producir artefactos sin firmar silenciosamente; no configurar al ejecutar ./scripts/build-mcpb.sh directamente para compilaciones de desarrollo amigables con forks)

Limitación conocida — sin grapado: stapler staple no admite binarios Mach-O crudos (solo paquetes .app / .pkg / .dmg). Después de la notarización, Gatekeeper verificará el binario en línea en el primer lanzamiento en lugar de leer un ticket grapado. Los usuarios finales detrás de redes aisladas pueden ver advertencias de "no se puede verificar el desarrollador"; un lanzamiento con red lo resuelve (Apple almacena en caché el veredicto). Mitigación: xcrun stapler staple en un futuro envoltorio .pkg si es necesario.

Solución de problemas:

  • ¿Notarización rechazada? xcrun notarytool log <submission-id> --keychain-profile $NOTARY_PROFILE muestra la razón de Apple. El script de firma imprime el ID de envío en cada ejecución.
  • ¿codesign se queja de identidad faltante? security find-identity -p codesigning -v para confirmar que el certificado está presente y es válido; xcrun notarytool history --keychain-profile $NOTARY_PROFILE para confirmar que el perfil funciona.
  • ¿Certificado caducado? Reemite en https://developer.apple.com/account/resources/certificates, instala, reexporta DEVELOPER_ID.
  • Advertencia de seguridad: no desbloquees el llavero de firma en máquinas compartidas o no confiables. El artefacto de firma del certificado + clave privada es crítico para la cadena de suministro.

Licencia

Licencia MIT - consulta LICENSE para más detalles.


Autor

Creado por Che Cheng (@kiki830621)

Si te resulta útil, ¡considera darle una estrella!

Nombres de lectura de recurrencia (#198, no lanzado): usa event_recurrence_rules para eventos y reminder_recurrence_rules para recordatorios. Ambos son arreglos de reglas, pero los selectores faltantes y la representación de fechas de fin difieren. El alias heredado recurrence_rules se conserva. Los clientes que rechazan campos desconocidos necesitan actualizaciones de decodificador. Consulta la comparación de formatos.