Skip to content

fix(mcp): strip metadata from images agents attach - #3311

Merged
tlongwell-block merged 2 commits into
mainfrom
eva/agent-attachment-metadata
Nov 11, 2026
Merged

tlongwell-block merged 2 commits into
mainfrom
eva/agent-attachment-metadata

Conversation

@tlongwell-block

@tlongwell-block tlongwell-block commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Images attached by humans in the composer go through prepareAttachment, which removes their metadata. When an agent attaches files (send / reply_in_subthread files), the bytes go to client.upload raw. So an image's EXIF, PNG text chunks or GIF comments reach the relay unchanged. The relay's validate_image_metadata_free (block/buzz crates/buzz-media/src/validation.rs) then refuses the upload with "media contains metadata". Real photos and screenshots almost always carry metadata, so agents can't attach them. On a relay without that check, the metadata, including location, would be stored.

I reproduced this live on buzz.block.builderlab.xyz: a JPEG with an EXIF APP1 segment, attached by an agent, was refused. The same JPEG with only the metadata removed went through.

Change

appClient.upload runs images (by magic bytes, same imageType the composer uses) through prepareAttachment(file, signal), which is the composer's default path. That default keeps .agent.png / .team.png snapshot manifests, as human attachments do. Non-image files upload byte-for-byte, as before. Picture drafts (uploadPicture) are untouched.

Not in scope: the standalone Node MCP client (src/buzz-mcp/node.ts) has no image encoder. It still uploads raw bytes, and the relay's check stays its guard.

Tests

  • New app-client.test.ts case: a GIF carrying a comment extension uploads without it, and a text file uploads unchanged. It fails on main (the comment bytes are sent) and passes with the fix.
  • Turn-end boundary (signal abort and check()-only retirement): ending the turn while the prepared image is read uploads nothing. The turn is checked right before agent.upload. Both cases fail on f8ed680 and pass now.
  • codex/runtime.test.ts attached a 3-byte fake JPEG (ff d8 ff) that only passed because uploads were raw. It now attaches a 1x1 two-colour, metadata-free GIF that decodes, so it still asserts the attachment is uploaded as read.
  • At c2aeb85: biome check, tsc --noEmit, full vitest run (1066 files, 20931 tests) green.

tlongwell-block and others added 2 commits November 11, 2026 13:49
Agent files attachments uploaded raw bytes, so an image's EXIF, text
chunks or comments reached the relay, which refuses them (or, without that
check, would store them). Prepare attached images as the composer does;
other files upload unchanged.

Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Signed-off-by: Tyler Longwell <[email protected]>
A turn that ended while the prepared image was read could still start its
upload. Check the turn immediately before uploading, and use a decodable
GIF in the attachment tests.

Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Signed-off-by: Tyler Longwell <[email protected]>
@tlongwell-block
tlongwell-block marked this pull request as ready for review November 11, 2026 22:43
@tlongwell-block
tlongwell-block requested a review from a team as a code owner November 11, 2026 22:43
@tlongwell-block
tlongwell-block merged commit 8697bd2 into main Nov 11, 2026
124 of 125 checks passed
@tlongwell-block
tlongwell-block deleted the eva/agent-attachment-metadata branch November 11, 2026 23:08
joahg added a commit to joahg/buzz-app that referenced this pull request Nov 12, 2026
* origin/main: (38 commits)
  Fix reminder counts with compact dock corner tabs (block#3126)
  Add conditional undo for explicit reads (block#3291)
  fix(composer): edit channel links as atomic chips (block#3312)
  fix(agents): show remote instruction limit (block#3325)
  NEXUS-4033: Offer session skills in the core task composer (block#2790)
  feat(github): name pasted repository and commit links (block#3268)
  Let plugins give standing instructions to every agent turn (block#3285)
  fix(relay): read a channel's creation once, not per metadata version (block#3244)
  fix(messages): remove unconfirmed sends from this device (block#3317)
  fix: expose proven non-delivery for artifacts (block#3300)
  feat(plugins): let plugins mark a person's name in @mention suggestions (block#3260)
  fix(mcp): strip metadata from images agents attach (block#3311)
  fix(identity): name a linked key that isn't here, and keep Restore after a failed backup check (block#3238)
  feat(changesets): show the files a local agent changed on its reply (block#3214)
  fix(identity): show a backed-up key as backed up, and drop dead retries (block#3223)
  fix(browser): open Builderlab account connections in the system browser (block#3287)
  Expose exact captured-message reads (block#3264)
  docs(agents): keep internal discussion out of public repo content (block#3258)
  fix(identity): clear a copied private key, name restore failures, fix storage copy (block#3236)
  fix(channels): list workspaces that load after `#` opens (block#3277)
  ...
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.

1 participant