Repository navigation
fix(mcp): strip metadata from images agents attach - #3311
Merged
Merged
Conversation
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
marked this pull request as ready for review
November 11, 2026 22:43
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) ...
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Images attached by humans in the composer go through
prepareAttachment, which removes their metadata. When an agent attaches files (send/reply_in_subthreadfiles), the bytes go toclient.uploadraw. So an image's EXIF, PNG text chunks or GIF comments reach the relay unchanged. The relay'svalidate_image_metadata_free(block/buzzcrates/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.uploadruns images (by magic bytes, sameimageTypethe composer uses) throughprepareAttachment(file, signal), which is the composer's default path. That default keeps.agent.png/.team.pngsnapshot 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
app-client.test.tscase: a GIF carrying a comment extension uploads without it, and a text file uploads unchanged. It fails onmain(the comment bytes are sent) and passes with the fix.check()-only retirement): ending the turn while the prepared image is read uploads nothing. The turn is checked right beforeagent.upload. Both cases fail on f8ed680 and pass now.codex/runtime.test.tsattached 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.biome check,tsc --noEmit, fullvitest run(1066 files, 20931 tests) green.