[BUG] iMessage plugin: self-chat messages never delivered — is_from_me=1 dropped before self-chat detection
Description
The iMessage MCP plugin's inbound poll drops all is_from_me=1 messages at line 788 of server.ts before checking whether the message is from a self-chat DM. On newer macOS versions, self-chat messages are only stored as is_from_me=1 rows (no is_from_me=0 copy is created), so self-chat messages are silently dropped and never delivered to Claude.
Root cause
In handleInbound():
// Line 788 — drops before self-chat check
if (r.is_from_me) return
if (!r.handle_id) return
const sender = r.handle_id
// Line 795 — self-chat detection never reached
const isSelfChat = !isGroup && SELF.has(sender.toLowerCase())
The code assumes self-chat creates dual rows per the comment on line 786-787:
"In self-chat the is_from_me=1 rows are empty sent-receipts anyway — the content lands on the is_from_me=0 copy below."
This assumption no longer holds on newer macOS (tested on Darwin 25.4.0). All self-chat messages arrive as is_from_me=1 with content in attributedBody, and no is_from_me=0 duplicate is created.
Evidence
-- All recent self-chat messages are is_from_me=1, no is_from_me=0 copies
SELECT ROWID, text, is_from_me, length(attributedBody)
FROM message m
JOIN chat_message_join cmj ON cmj.message_id = m.ROWID
JOIN chat c ON c.ROWID = cmj.chat_id
WHERE c.guid = 'any;-;<self-email>'
AND m.ROWID >= 624540
ORDER BY m.ROWID;
-- Result: all rows have is_from_me=1, empty text, non-null attributedBody
Suggested fix
Move the self-chat detection above the is_from_me early return, and allow self-chat is_from_me=1 messages through when they have content (non-empty attributedBody or text).
Environment
- macOS: Darwin 25.4.0
- Chat GUID format:
any;-;(notiMessage;-;) - Claude Code: latest
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗