All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
build/ not produced on marketplace install — Claude Code installs path-source plugins by git clone only; it does not run npm install, so the prepare hook never fired and the cached plugin directory was missing build/index.js. The MCP server failed to start until the user manually ran npm run build inside the cached plugin dir. Build artifacts in build/ are now committed to the repository so they are present immediately after marketplace clone. A pre-commit hook keeps build/ in sync with src/, and CI fails if the committed build/ is stale. (#10).mcp.json plugin entrypoint resolved against user cwd — args referenced build/index.js as a relative path, so the MCP server only started when Claude Code was launched from inside a clone of this repo. Now uses ${CLAUDE_PLUGIN_ROOT}/build/index.js so the path resolves against the plugin's install location. (#10 by @natekettles)prepare script no longer rebuilds. It now runs husky only. Build artifacts are committed and kept current by the pre-commit hook; rebuilding on every npm install was producing churn in git status for daily development.list-attachments and save-attachment returned empty across all account types (iCloud, Google, Exchange) when attachments were embedded as MIME parts in the message source (a known limitation of Mail.app's AppleScript bridge). Both tools now use a two-attempt pattern: AppleScript first, then fall back to parsing the raw MIME source of the message. Path-traversal protection is preserved on the fallback save path. (#8 by @kevinmay-scoutsolutions)hasAttachments accuracy — get-message now detects MIME-embedded attachments via raw source scan when AppleScript reports zero. list-messages uses the fast AppleScript count path only (may false-negative on MIME-embedded; use get-message or list-attachments for authoritative info).multipart/alternative (text+html) or multipart/related (inline images) containers are now discovered by recursive descent.quoted-printable and 7bit/8bit/binary attachments in addition to base64.src/utils/mimeParse.ts — standalone MIME parser (zero dependencies) with 18 unit tests covering single/multi attachments, nested multipart, inline dispositions, and all supported transfer encodings.reply msg with opening window creates a GUI compose window that doesn't fully initialize from non-interactive processes. Switched to without opening window, which makes set content work immediately and reliably. In-Reply-To and References headers are still set correctly by Mail.app. (#7)forward msg with opening window → without opening window.& content of theReply / & content of theForward appended to the body was always empty (the quoted content lives in Mail.app's HTML layer, not the plaintext content property). Removed the dead concatenation.search-messages and list-messages now search across all mailboxes in an account when no mailbox is specified, instead of defaulting to INBOX. This dramatically improves results for Gmail accounts where messages live in labels rather than INBOX. Deduplication ensures each message appears only once.list-messages now iterates all accounts when no account is specified, matching the existing behavior of search-messages.dateFrom and dateTo now reject non-parseable date strings (e.g., "31", "abc") with a clear error message instead of crashing AppleScript. The existing regex security filter is preserved; a semantic .refine() check is added on top./^\d+$/) to prevent injection attacksescapeForAppleScript() call is applied before interpolationsave-attachment uses path.resolve and restricts save paths to the user's home directory, /tmp, /private/tmp, and /Volumes; attachment names containing /, \, null bytes, or .. are rejectedNumber(id) as an extra safeguardsend-email and create-draft enforce a maximum of 20 file attachmentssrc/security.test.ts with unit tests for all input validation schemas and path traversal preventiontest/integration.test.ts for live Mail.app testingtest:integration and test:all for running integration and combined test suites{{placeholder}} token support (max 100 recipients per batch) (PR #3 by @michaelhenze)send-email and create-draft now accept an optional attachments parameter (array of absolute file paths) (PR #2 by @michaelhenze)send-email and create-draft, preventing failures when Mail.app is slow to establish SMTP connectionssend-serial-email uses spawnSync("sleep") instead of CPU-burning busy-wait between sendssend-serial-email enforces safety limits: max 100 recipients, max 10s delay between sendsbatch-mark-as-unread, batch-flag-messages, batch-unflag-messagescreate-mailbox, delete-mailbox, rename-mailboxlist-rules, enable-rule, disable-rulesearch-contacts (Contacts.app integration)save-template, list-template, get-template, delete-template, use-templatepreferHtml option in get-messagefrom, offset) for list-messagesdateFrom, dateTo) for search-messagesunflag-message tool (was implemented but not wired up)First stable release with full Apple Mail integration.
Initial release - project skeleton.