Skip to main content
Glama
draft-only-google

draft-only-google

draft-only-google-mcp

This is a small MCP server that gives Claude access to one Gmail mailbox and its Google Calendar. Claude can read mail, manage labels, write drafts and work with calendar events. It has no way to send email, so you review every draft and send it yourself from Gmail.

It is about 560 lines of Python. It depends only on Anthropic's mcp SDK, Google's auth libraries and requests, and it talks only to the Gmail API, the Calendar API and Google OAuth.

macOS only. Tokens are stored in the macOS Keychain and nowhere else, and the server refuses to start a login or read a token on any other system. Linux and Windows are not supported today.

What the guarantee covers

There is no code path in this server that sends mail. It has no send tool, it never calls Gmail's send endpoints, and it does not speak SMTP. Every id goes through one check before it reaches a URL, so an id cannot steer a request to another endpoint. Calendar writes always pass sendUpdates=none, and no tool has a parameter that changes that.

tests/test_no_send.py enforces this. It fails if the source mentions a send endpoint, a send scope or SMTP, if the OAuth scopes or the tool list change from a fixed list, if a tool gains a parameter with send or notify in its name, if a calendar write asks Google to notify guests, or if the final URL of any tool call leaves the Gmail and Calendar read and draft endpoints.

Related MCP server: gmail-mcp-server

What the guarantee does not cover

The guarantee is about this server's code, not about the Google token, so these limits matter:

  • The token itself can send. Gmail has no scope that allows drafts without also allowing send, so this server uses gmail.modify, which Google describes as read, compose and send. Other software holding this token could send mail.

  • An agent with a shell can reach the token. The token is stored in the Keychain by the security command, so a process running as your user can read it back with that same command without a prompt. If you need the limit to hold, do not also give the agent unrestricted shell access.

  • Calendar guests can still hear about events. attendees is refused unless you turn on DRAFT_ONLY_GOOGLE_ALLOW_ATTENDEES. Once it is on, an event with attendees can appear on their calendars even though no invite is requested, and Google's documentation says some emails might still be sent. Changing an event that already has guests can reach them the same way. Treat adding a guest as a visible action.

  • Drafts can hold private content. create_draft can attach any regular file inside the allowed folders, and mail you ask Claude to read can contain instructions written by strangers. Read each draft and its recipients before you send it.

Tools

  • Gmail: search_threads, get_thread, get_message, download_attachment, list_labels, modify_thread_labels, create_draft, update_draft, list_drafts

  • Calendar: list_calendars, list_events, get_event, create_event, update_event

Setup

You need macOS, uv and a Google account. You create your own Google OAuth client, so no third party is involved and your token only works with your client.

1. Create a Google OAuth client

  1. Open the Google Cloud console and create a new project.

  2. Under APIs & Services, open Library, then enable the Gmail API and the Google Calendar API.

  3. Open Google Auth Platform and fill in the app name and your email address when asked. Choose the audience:

    • With a Google Workspace account, choose Internal. Only people in your organization can use the app, and no review is needed.

    • With a personal Gmail account, choose External, then add your own address as a test user under Audience.

  4. Under Clients, create an OAuth client with the application type Desktop app, then download its JSON file.

An External app in Testing status gets a refresh token that expires after 7 days, so you would need to log in again each week. To avoid that, publish the app from the Audience page. Google will then show an unverified app warning at login, which you can click through because it is your own app.

2. Sign in

git clone https://github.com/elliothimmelfarb/draft-only-google-mcp.git
cd draft-only-google-mcp
uv run draft-only-google-mcp login /path/to/client_secret.json

A browser opens for Google's consent screen. When you are signed in, the token is in the Keychain and you should delete the JSON file. Run uv run draft-only-google-mcp whoami to check which account is stored.

3. Add it to Claude Code

claude mcp add -s user draft-only-google -- uv run --directory "$PWD" draft-only-google-mcp

Configuration

All settings are optional environment variables. Set them for both the login command and the server, for example with claude mcp add -e NAME=value.

Variable

Default

Purpose

DRAFT_ONLY_GOOGLE_ACCOUNT

none

Makes login refuse any other Google account. Also the Keychain account name; without it the name is default.

DRAFT_ONLY_GOOGLE_KEYCHAIN_SERVICE

draft-only-google-mcp

The Keychain service name.

DRAFT_ONLY_GOOGLE_SERVER_NAME

draft-only-google

The name the MCP server reports.

DRAFT_ONLY_GOOGLE_ATTACHMENT_DIR

~/Downloads/draft-only-google-mcp

Where download_attachment saves files.

DRAFT_ONLY_GOOGLE_ATTACHMENT_ROOTS

the attachment folder

Folders create_draft may attach from, separated by :. Links are followed first, and a draft's attachments are capped at 25 MB in total.

DRAFT_ONLY_GOOGLE_TIME_ZONE

UTC

The default time zone for calendar events.

DRAFT_ONLY_GOOGLE_ALLOW_ATTENDEES

off

Set to 1, true, yes or on to let create_event and update_event set guests.

To use two mailboxes, register the server twice with a different DRAFT_ONLY_GOOGLE_ACCOUNT for each and run login once for each.

Development

uv run pytest

License

MIT. See LICENSE.

Available Tools

14 tools
create_draftA

Create a draft in the mailbox (it is NOT sent). For a reply, pass reply_to_message_id and leave subject empty to get "Re: " threaded correctly. Write each paragraph as one line; lines wrapped by hand are joined so the email fills the reader's window. attachments: absolute paths of local files to attach. Each must be a regular file inside the attachment folder or DRAFT_ONLY_GOOGLE_ATTACHMENT_ROOTS, and together they must fit in 25 MB.

Inputs: to (required), body (required), cc, bcc, +3 more.

create_eventB

Create a calendar event. start/end are RFC 3339 datetimes or YYYY-MM-DD for all-day. attendees is refused unless DRAFT_ONLY_GOOGLE_ALLOW_ATTENDEES is on. This server asks Google not to notify guests, but the event may still appear on their calendars and Google may still email them, so treat adding a guest as a visible action.

Inputs: end (required), start (required), summary (required), location, +4 more.

download_attachmentB

Save an attachment to the attachment folder under a new unique name and return the local path.

Inputs: filename (required), message_id (required), attachment_id (required). Returns: result

get_eventC

Get one calendar event.

Inputs: event_id (required), calendar_id.

get_messageB

Get one Gmail message with headers, plain-text body and attachment list.

Inputs: message_id (required).

get_threadB

Get every message in a Gmail thread with headers, plain-text body and attachment list.

Inputs: thread_id (required).

list_calendarsB

List calendars visible to the account.

Inputs: no named parameters. Returns: result

list_draftsC

List drafts in the mailbox, including any Bcc recipients.

Inputs: max_results. Returns: result

list_eventsA

List calendar events between two RFC 3339 times (e.g. 2026-01-15T00:00:00+01:00). Recurring events are expanded into single instances, ordered by start time.

Inputs: time_max (required), time_min (required), query, calendar_id, +1 more. Returns: result

list_labelsA

List Gmail labels with their ids.

Inputs: no named parameters. Returns: result

modify_thread_labelsA

Add or remove label ids on a thread. Archive = remove INBOX. Mark read = remove UNREAD.

Inputs: thread_id (required), add_label_ids, remove_label_ids.

search_threadsA

Search Gmail threads with Gmail search syntax (from:, to:, subject:, newer_than:7d, is:unread, in:sent, label:...). Returns each thread's latest message summary. Use get_thread for full bodies.

Inputs: query, page_token, max_results.

update_draftC

Replace the contents of an existing draft.

Inputs: to (required), body (required), draft_id (required), cc, +3 more.

update_eventA

Change fields on a calendar event; omitted fields stay as they are. attendees replaces the whole list and is refused unless DRAFT_ONLY_GOOGLE_ALLOW_ATTENDEES is on. This server asks Google not to notify guests, but Google may still email them about a change.

Inputs: event_id (required), end, start, summary, +5 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.5/5.0

    Scored across 14 tools

    Disambiguation4/5

    Each tool targets a distinct resource+action pair (thread vs message vs draft vs event), and the descriptions explicitly cross-reference each other (search_threads points to get_thread for full bodies). The only borderline pair is get_thread vs get_message, which requires the agent to know Gmail's thread/message model, but the descriptions make the split workable.

    Naming Consistency5/5

    All 14 tools follow a strict snake_case verb_noun pattern (list_/get_/search_/create_/update_/modify_/download_). No mixed conventions or vague verbs anywhere in the set.

    Tool Count5/5

    14 tools is well within a comfortable range and each maps to a concrete Gmail or Calendar operation. The split across threads, messages, attachments, labels, drafts, events, and calendars is proportionate to the two-API scope.

    Completeness3/5

    Read/create/update lifecycle is covered for drafts and events, and send is intentionally excluded by design (draft-only). However, there is no delete_draft, no delete_event, no get_draft, and no way to modify labels on individual messages rather than threads, leaving dead ends for cleanup workflows.

    Maintenance

    ActivityMaintained
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      C
      maintenance
      Enables Claude to manage Gmail, Google Calendar, and Google Contacts through a safety-first interface with soft-delete protections and draft-only email capabilities. Supports multiple Google accounts with comprehensive mutation logging and mandatory manual confirmation for destructive actions.
      16
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables reading, sending, searching, and managing Gmail through Claude using the official Google Gmail API.
      123 npm
      3
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables Claude to read, search, send, and manage Gmail messages and threads through natural language.
      282 npm
      MIT