Skip to content

Messages: list attachments in messages_fetch and add messages_attachment_read - #214

Closed
patp wants to merge 2 commits into
mattt:mainfrom
patp:pr/messages-attachments
Closed

patp wants to merge 2 commits into
mattt:mainfrom
patp:pr/messages-attachments

Conversation

@patp

@patp patp commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Problem

messages_fetch returns text only. Photos, voice memos, PDFs and other files that arrive over iMessage are invisible to a client: a message that is just a picture is dropped (its text is the empty string or the U+FFFC placeholder), and there is no way to get at the bytes at all. For anything that indexes or summarises a conversation, that is a large part of the history missing.

Change

  • messages_fetch takes a new optional attachments: true. Each message then carries an attachment array (@id, name, encodingFormat, contentSize, sticker), hidden attachments excluded, and attachment-only messages are kept instead of being filtered out. The U+FFFC placeholder and the at_<n>_<UUID> guid lines Messages writes into the body are stripped from text. Without the flag the tool behaves exactly as before.
  • New tool messages_attachment_read {id, maxBytes}: returns one attachment as base64 with its name, type and size. It resolves the stored path against the granted Messages folder and refuses anything outside it, so the grant cannot be turned into a general file reader. Files not yet downloaded from iCloud produce a clear error.
  • The attachment join (message_attachment_join / attachment) is not exposed by Madrid, so it is read with a small read-only sqlite3 wrapper over the same DatabaseAccess (one query per fetched page).

Dependency

Stacked on #198: reading attachment files needs the folder grant that PR introduces (the single-file chat.db grant cannot reach ~/Library/Messages/Attachments). Only the last commit is new here.

Testing

  • Compiles with Xcode 26.6 on macOS 26 (Release); swift format lint --strict clean.
  • Running on a Mac since 2026-09-09 behind a sync client: a 180-day backfill listed 539 attachments (HEIC, JPEG, PNG, AMR voice memos, MP3, PDF) and read 436 of them; the rest were stickers, videos over the client's size cap, or one file not downloaded from iCloud. Voice memos and images fetched this way were transcribed / OCR'd downstream, so the bytes are intact.

🤖 Generated with Claude Code

https://claude.ai/code/session_01ARr1A7UfPmRfBS2NYynnvA

patp and others added 2 commits September 3, 2026 22:03
The service asked for chat.db alone. Messages keeps that database in WAL
mode: every new message is committed to chat.db-wal first and only reaches
chat.db when SQLite checkpoints the log, every few MB of writes. With a
grant on the single file the log was unreadable, so Madrid opened the
database with immutable=1 and messages_fetch could not see anything newer
than the last checkpoint, often hours behind, then everything at once.

The file picker now asks for ~/Library/Messages (directories only,
constrained to a folder named Messages) and the bookmark covers the
folder, which lets SQLite read chat.db together with its -wal and -shm
companions (Madrid's Database.AccessMode.live). The security scope now
stays open until the fetch is done, since SQLite opens the log lazily.

A bookmark stored by an earlier version (chat.db alone) keeps working in
immutable mode; the first fetch of a launch offers to re-select the folder,
and declining is remembered for that launch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0187kUP2mjhwMfJkwRbP5DG7
- attachments: true on messages_fetch returns each message's attachments
  ({@id, name, encodingFormat, contentSize, sticker}, hidden ones excluded)
  and keeps attachment-only messages (U+FFFC / at_<n>_<UUID> placeholders
  stripped from the text)
- messages_attachment_read {id, maxBytes}: base64 bytes of one attachment,
  only from under the granted Messages folder
- RawDatabase: read-only sqlite3 access for the attachment join

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARr1A7UfPmRfBS2NYynnvA
@mattt

mattt commented Sep 17, 2026

Copy link
Copy Markdown
Owner

Hi @patp. Same plan as #197: this goes into 1.6.0, after today's 1.5.0. It shows as conflicting now because #198 was squash-merged and this branch still carries the original commit; a rebase onto main should leave only your last commit. No rush.

@zeke

zeke commented Sep 24, 2026

Copy link
Copy Markdown

Hey party people! I just started using iMCP. Love it. Thank you @mattt for making it, and @patp for contributing.

I bumped up against this issue today. Would love to see this feature land!

@mattt

mattt commented Sep 24, 2026

Copy link
Copy Markdown
Owner

Hi @patp. Thanks for this, and sorry for the wait. Your branch still had the commit from #198, so I cherry-picked your attachments commit onto main in #244, with you credited as co-author, and added a formatting pass for CI. It'll go out in 1.6.0. @zeke, it's on its way!

@mattt mattt closed this Sep 24, 2026
@mattt

mattt commented Sep 26, 2026

Copy link
Copy Markdown
Owner

Hi @zeke! 1.6.0 is out, and messages_fetch can now list each message's attachments (name, type, and size). Reading the files themselves didn't make it, though: in the sandboxed app, macOS won't let iMCP open anything under ~/Library/Messages/Attachments, even with the Messages folder granted. So I pulled messages_attachment_read for now (#247), and I'll bring it back once we find a way in.

@zeke

zeke commented Sep 26, 2026

Copy link
Copy Markdown

Thanks for the update! That's probably enough to unblock me if I can get the filenames.🤞

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.

3 participants