Skip to content

feat: Add isRead and dateRead fields to Message model#10

Closed
michaelfarrell76 wants to merge 1 commit into
mattt:mainfrom
michaelfarrell76:add-read-status
Closed

feat: Add isRead and dateRead fields to Message model#10
michaelfarrell76 wants to merge 1 commit into
mattt:mainfrom
michaelfarrell76:add-read-status

Conversation

@michaelfarrell76

Copy link
Copy Markdown

Summary

The message table in Apple's chat.db includes is_read and date_read columns that are not currently selected or exposed by Madrid. This PR adds support for them:

  • Message.isRead: Bool — whether the message has been read (is_read column)
  • Message.dateRead: Date? — when the message was read (date_read column, nil if unread or 0)
  • fetchMessages(unreadOnly:) — new optional parameter that filters to incoming unread messages (is_read = 0 AND is_from_me = 0) when set to true

Motivation

AI assistants using iMCP's messages_fetch tool currently cannot distinguish read from unread messages. This is one of the most natural queries a user would make ("show me my unread messages"). The underlying data is already in chat.db — Madrid just needs to thread it through.

Implementation

  1. Message.swift — Added isRead: Bool and dateRead: Date? properties
  2. Database.swift — Added m.is_read (column index 7) and m.date_read (column index 8) to the SELECT in fetchMessages(). Added unreadOnly parameter that appends m.is_read = 0 AND m.is_from_me = 0 conditions when true. dateRead uses the same nanoseconds-since-reference-date conversion as date.

Caveats

  • is_read and date_read are reverse-engineered columns — Apple does not document the chat.db schema. The columns are well-established across community tooling (imessage-exporter, imessage-mcp, SketchyBar).
  • is_read = 0 is a best-effort approximation and may not exactly match the Messages app badge count in all cases.

Companion PR

An iMCP PR to expose this via messages_fetch will follow.

Made with Cursor

Add `isRead` (Bool) and `dateRead` (Date?) properties to the Message
struct, populated from the `is_read` and `date_read` columns in chat.db.

Also add an `unreadOnly` parameter to `fetchMessages()` that filters
to incoming unread messages (is_read = 0 AND is_from_me = 0) when set.

These columns have been present in Apple's Messages database schema
but were not previously selected or exposed.

Made-with: Cursor
@mattt

mattt commented Mar 23, 2026

Copy link
Copy Markdown
Owner

@michaelfarrell76 Thanks so much for putting this together. I took an alternate pass in #11 that keeps fetchMessages free of an unread filter flag and has readAt as the canonical stored value with isRead computed from it.

@mattt mattt closed this Mar 23, 2026
@mattt

mattt commented Mar 23, 2026

Copy link
Copy Markdown
Owner

This is now available in 0.3.0. I'll follow up in iMCP for the feature there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants