Repository navigation
Presigned single-use links for downloads and draft uploads; base64 is best effort - #27
Merged
Merged
Conversation
Hosts that cannot send this connection's Authorization header with their own
HTTP requests had no way to move a file without writing it through the model.
- get_attachment_download_url takes presigned: true and also returns a
single-use GET link valid for 5 minutes, with its size, sha256 (computed
from the attachment), expires_at and a ready-to-run curl command. If the
attachment changes after the link is issued, the download is refused, or
cut off before its last bytes because the last chunk is held back until
the whole body is verified.
- The new get_attachment_upload_url returns a single-use PUT link valid for 5
minutes into one draft, bound to the declared size and sha256, with a curl
command. Exact size and digest are enforced while streaming, one upload
succeeds, and failed attempts leave the link usable (streamIntoDraft in
src/upload.ts).
Links live at /mcp/links/{token}: an HMAC-signed capability for one
transfer that never contains an API key or access token. The caller they
act for stays in the issuing process, so a link works only on the instance
that issued it. FMSG_MCP_STATE_SECRET sets the key.
data_base64 is now described as best effort, because the base64 passes
through the model: fine for small files (verified exact up to 16 KB), always
with sha256, and path or a presigned link for anything larger. Tool text
names only the routes the server registered.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 22, 2026
markmnl
marked this pull request as ready for review
October 22, 2026 06:25
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.
Replaces #26. That PR had to be split from the file-uploads extension (#25) so it can land first. Rebasing the stack made GitHub mark #26 as merged into #25's branch and delete its head branch, so it can't be reopened. Nothing from it reached
main. Same commit, now onpresigned-links.Presigned links
get_attachment_download_urland the draft upload URL both needed the connection's bearer token. Hosts that cannot attach it to their own HTTP requests had to fall back to base64 through the model.get_attachment_download_urltakespresigned: trueand addspresigned: {url, method, expires_at, size, sha256, curl}. The server streams the attachment once to compute its sha256.get_attachment_upload_urlwith{id, filename, size, sha256, content_type?}. It returns{draft_id, filename, url, method: "PUT", expires_at, size, sha256, curl}. It refuses sent messages, other people's drafts, and names already taken. The OAuth scope is read+write./mcp/links/{token}: an HMAC-signed capability for one transfer, valid for 5 minutes, bound to the message or draft, file name, size and sha256.FMSG_MCP_STATE_SECRET(at least 32 bytes) sets the key.streamIntoDraftinsrc/upload.ts, which enforces exact size and sha256 while streaming. The file-uploads extension (Reference implementation of the MCP file-uploads extension #25) reuses it.409before any byte is sent; a large one is cut off.404with a message saying so. AGENTS.md's "keep credentials out of links" rule now names this one exception.data_base64 wording
The
data_base64andsha256descriptions, the instructions, the tool guidance and thesend_fileprompt now say three things. Base64 passes through the model, so it is best effort and suited to small files (verified exact up to 16 KB). Always pass sha256. For anything larger, preferpathor a presigned link. Text names only the routes the running server registered, soFileRoutesgainedpresigned. #25 adds the extension to that wording.Tests
test/links.test.ts(11 tests). It runs the returnedcurlcommands for real, over HTTPS through a TLS proxy (test/tls.ts, with a throwaway openssl certificate made at test time). Covered:send_draftnpm run typecheck && npm run build && npm test: 273 passed.🤖 Generated with Claude Code