Repository navigation
Releases
v0.3.0
Large attachments now move in both directions without passing through the conversation.
Download links. get_attachment used to refuse any file too large to return inline, about 150 KB of base64, and told the assistant to open a mail client. It now returns a downloadUrl that curl -o saves straight to disk, so the bytes never enter the context window. A link works for an hour while its MCP session stays open, and covers files up to what one Gmail read returns, about 4.5 MB. Files are always served as an opaque download (application/octet-stream, Content-Disposition: attachment, nosniff, CSP: sandbox), because the same origin serves the sign-in pages.
Forwards carry their files or do not go. forward_message with includeAttachments used to skip every file over 1.5 MB, a limit meant for downloads, and send the forward anyway. It now carries each file that fits one Gmail read, within the 8 MB a message can hold. When a file can't go, the forward is refused before anything is sent.
日本語 https://github.com/mkpoli/gmail-mcp/blob/main/README.ja.md · 简体中文 https://github.com/mkpoli/gmail-mcp/blob/main/README.zh.md
v0.2.1
A sign-in that goes wrong now explains itself and offers the way back.
Consent recovers in place. The approval dialog warns, before Google's screen, that every permission box has to stay ticked. When one is unticked anyway — or Google returns no refresh token — the callback lands on a page that says which permission was withheld and carries a "Choose permissions again" button that reopens Google's consent screen directly, with nothing to restart on the client side.
A dead grant names its cause. When Google refuses a token refresh with invalid_grant, tool calls now answer with what to do: reconnect and sign in again, and — when it recurs every few days — publish the OAuth app to In production, since Google expires every refresh token of a Testing-status app after 7 days.
Setup docs corrected. The deploy guide used to leave the OAuth consent screen in Testing with test users, which is exactly the configuration whose tokens die weekly. It now says to publish the app: sign-in shows an unverified-app warning, up to 100 accounts connect, and refresh tokens live until revoked.
日本語 https://github.com/mkpoli/gmail-mcp/blob/main/README.ja.md · 简体中文 https://github.com/mkpoli/gmail-mcp/blob/main/README.zh.md
v0.2.0
Reply drafts, field-by-field draft updates, and a path for attachments too large to pass through tool arguments.
Staged attachments. stage_attachment_begin opens a staging area in the session and returns a one-time upload URL; curl -T <file> '<url>' carries the raw bytes straight to the Worker, so a 2MB file costs the assistant a few hundred tokens. stage_attachment_append takes base64 in chunks where no shell is available. Every attachments and inlineImages field accepts the resulting stagingId. A staging lives 24 hours; uploads are capped at 6MB and gated by a per-staging secret.
Reply drafts. create_draft takes replyToMessageId: the draft joins the original's thread, carries In-Reply-To and the References chain, quotes the original under the body, and derives the reply-all recipients, the send-as identity, and the Re: subject when they are not given. Threading holds in Gmail's own UI and in clients that thread on headers alone.
Draft updates keep what they are not given. update_draft changes only the fields passed. Recipients, subject, the body and its HTML alternative, files added by hand in the Gmail UI, inline images, and the thread the draft answers are read back and kept. A file that cannot be read back stops the update with an error.
27 tools · 250 unit tests. Each new flow also ran against live Gmail accounts, staging included, with a 1.9MB single-PUT upload.
Upgrading an existing deployment: git pull && bun run deploy. claude.ai caches connector tool schemas; reconnect the connector to see the staging tools.
日本語 https://github.com/mkpoli/gmail-mcp/blob/main/README.ja.md · 简体中文 https://github.com/mkpoli/gmail-mcp/blob/main/README.zh.md
v0.1.0
First tagged release.
gmail-mcp connects Gmail to Claude and any other MCP client, running as a remote server on a Cloudflare Worker you deploy yourself. Each connection signs in to one Google account, and the Google refresh token stays in your own Cloudflare account. One deployment serves several mailboxes through /mcp/<label>.
24 tools. Search and read mail, send, reply-all with quoted history, forward, attachments and inline cid: images, drafts, labels, threads, trash.
Encoding. Subjects and display names go out as RFC 2047, filenames as RFC 2231, and each incoming part is decoded by its own charset, so ISO-2022-JP and Shift_JIS mail arrives readable.
Auth. OAuth 2.1 with PKCE on the MCP side, authorization code with offline access on the Google side. gmail.modify is the only mail scope requested: it excludes permanent deletion and all of gmail.settings.*. Sign-in is gated by ALLOWED_EMAILS, checked against the address Google reports as verified.
Tested. 233 unit tests, plus every tool run against real Gmail accounts with a second account checking what arrived.
Deploy takes about ten minutes and needs a Cloudflare account with a domain, bun, and a Google OAuth client: https://github.com/mkpoli/gmail-mcp#deploy-it
日本語 https://github.com/mkpoli/gmail-mcp/blob/main/README.ja.md · 简体中文 https://github.com/mkpoli/gmail-mcp/blob/main/README.zh.md