Skip to content

Fix upload_file's decode, cancellation, 412 and expiry parsing (0.15.1) - #83

Merged
shalim786 merged 1 commit into
mainfrom
drive/upload-fixes
Oct 15, 2026
Merged

shalim786 merged 1 commit into
mainfrom
drive/upload-fixes

Conversation

@shalim786

Copy link
Copy Markdown
Contributor

TL;DR

Fixes four problems in upload_file (0.15.0) that showed up in the review of agentmail-to/agentmail-mcp#88, which serves this toolkit on the hosted MCP server. Bumps to 0.15.1.

Changes

  • Decode (fileBytes, src/functions.ts): Uint8Array.from(atob(content), fn) goes through the iterator. On a 7.5 MB file that held about 12x the file in live heap (it ran out of memory at an 80 MB old space) and blocked for about 250 ms. An indexed loop takes about 9 ms and fits in 30 MB. atob still rejects invalid base64.
  • Cancellation (src/mcp.ts, src/util.ts, src/tools.ts, src/functions.ts): invoke(name, client, args, { signal }) threads an optional abort signal through runTool and safeFunc to the tool function. upload_file's presigned PUT and get_attachment's download combine it with their existing timeouts. Before this, a host's signal bound into the SDK client never reached either fetch, so on the hosted server a PUT outlived a client abort and the request timeout. The other adapters don't pass a signal and behave as before.
  • 412 (uploadFile): the PUT is signed If-None-Match: *, so a PUT that loses to another PUT of the same bytes gets 412. That case now goes on to complete, which verifies the stored bytes, instead of failing the call.
  • expiresAt (src/schemas.ts): now z.iso.datetime({ offset: true }). A time without a timezone is refused with an error that says so, rather than being read in the host's own zone. before/after/sendAt keep their documented lenient parse.

Tests

tests/drive.test.ts: every byte value round-trips through base64; an expiry without a timezone is refused; a 412 PUT completes the file; aborting invoke's signal stops the PUT without completing. tests/security.test.ts: get_attachment passes the caller's signal to its download. vitest run: 678 passed.

agentmail-mcp#88 adds a hosted-server test that fails on 0.15.0 (orphaned PUT, 412 surfaced as an error) and passes against this build.

After merge

Publish 0.15.1 to npm, then agentmail-mcp#88 pins it and passes { signal } to invoke.

🤖 Generated with Claude Code

A review of agentmail-mcp#88, which serves 0.15.0, found four problems in
the code the hosted MCP server now runs:

- The base64 decode went through Uint8Array.from's iterator: about twelve
  times the file in live heap and ~250 ms of blocking on a 7.5 MB file. An
  indexed loop takes ~9 ms and no extra heap. atob still validates.
- upload_file's presigned PUT and get_attachment's download use the global
  fetch, so a host's abort signal bound into the SDK client never reached
  them. invoke() now takes { signal }, threaded through runTool and
  safeFunc into the tool function, and both fetches combine it with their
  own timeout.
- The PUT is signed If-None-Match: *, so a PUT that loses to another PUT
  of the same bytes gets 412. That now goes on to complete, which checks
  the stored bytes, instead of failing the call.
- expiresAt accepted times without a timezone and read them in the host's
  zone. It now requires an ISO 8601 offset, with an error that says so.

Bumps to 0.15.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shalim786
shalim786 merged commit 8b2c679 into main Oct 15, 2026
2 checks passed
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