# Gmail MCP (npm · mcp-google-gmail)

MCP server for the Gmail API: search, read and send email, manage drafts, labels and the trash.

- Trust score: 79/100 (medium)
- Change this week: +3
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-09-20

## Components

- npm · `mcp-google-gmail`: 79/100 (this document), [markdown](https://verifymcp.io/servers/a1-x-tech-mcp-google-gmail/mcp-google-gmail.md), [page](https://verifymcp.io/servers/a1-x-tech-mcp-google-gmail/mcp-google-gmail)

## Channel facts

- Registry: `npm`
- Package: `mcp-google-gmail`
- Version: `0.1.0`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-09-20.

- **Supply Chain Security**: 98/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - No install/post-install scripts declared.
  - 31 of 96 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 45/100
  - Source repository is publicly reachable at the declared URL.
  - Provenance check failed: no build-provenance attestation is published.
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 21 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 71/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 4010 tokens (~222/item across 18 items; 18 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 70/100
  - Stability observed for 21 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - All 3 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.
  - An AI judge read all 19 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

## Install

### How do I install the Gmail MCP server?

Gmail MCP runs locally as an npm package, launched with npx -y mcp-google-gmail. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add a1-x-tech-mcp-google-gmail -- npx -y mcp-google-gmail
```

### Cursor

```json
{
  "mcpServers": {
    "a1-x-tech-mcp-google-gmail": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-google-gmail"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "a1-x-tech-mcp-google-gmail": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-google-gmail"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add a1-x-tech-mcp-google-gmail -- npx -y mcp-google-gmail
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "a1-x-tech-mcp-google-gmail": {
      "type": "local",
      "command": [
        "npx",
        "-y",
        "mcp-google-gmail"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add a1-x-tech-mcp-google-gmail --command npx --arg -y --arg mcp-google-gmail
```

### Hermes

```yaml
mcp_servers:
  a1-x-tech-mcp-google-gmail:
    command: "npx"
    args: ["-y", "mcp-google-gmail"]
```

### Netclaw

```json
{
  "McpServers": {
    "a1-x-tech-mcp-google-gmail": {
      "Transport": "stdio",
      "Command": "npx",
      "Arguments": [
        "-y",
        "mcp-google-gmail"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add a1-x-tech-mcp-google-gmail -t stdio -c npx -a -y mcp-google-gmail
```

### Other

```json
{
  "mcpServers": {
    "a1-x-tech-mcp-google-gmail": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-google-gmail"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-09-19 (score 79, +1)

No change was recorded against any check on this day. Stability & Change Management went from 63 to 67. That category is still filling its 30-day observation window: 19 days of observed history at the previous scan, 20 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-17 (score 78, +1)

No change was recorded against any check on this day. Stability & Change Management went from 57 to 60. That category is still filling its 30-day observation window: 17 days of observed history at the previous scan, 18 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-15 (score 77, +1)

No change was recorded against any check on this day. Stability & Change Management went from 50 to 53. That category is still filling its 30-day observation window: 15 days of observed history at the previous scan, 16 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-13 (score 76, +1)

No change was recorded against any check on this day. Stability & Change Management went from 43 to 47. That category is still filling its 30-day observation window: 13 days of observed history at the previous scan, 14 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-11 (score 75, +1)

No change was recorded against any check on this day. Stability & Change Management went from 37 to 40. That category is still filling its 30-day observation window: 11 days of observed history at the previous scan, 12 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-09 (score 74, +1)

No change was recorded against any check on this day. Stability & Change Management went from 30 to 33. That category is still filling its 30-day observation window: 9 days of observed history at the previous scan, 10 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-07 (score 73, +4)

- [functional improvement] Stability: unverified → 0.27

### 2026-08-31 (score 69, +15)

- [security improvement] Malware scan: unverified → pass

## MCP tools (18)

### `list_messages` (~352 tokens)

Search and list messages

Searches the mailbox with Gmail query syntax and returns one summary per message: id, threadId, labelIds, snippet, from, to, subject, date, internalDate. Filter with query (same operators as the Gmail search box: from:, to:, subject:, is:unread, is:starred, has:attachment, label:, newer_than:7d, before:/after:) and/or label_ids (all must match). Spam and trash are excluded unless include_spam_trash=true. Paginate with page_token from nextPageToken; page_size defaults to 25 (max 100 — each summary costs one metadata read, throttled to a few at a time). A message deleted between the search and its metadata read is skipped, so a page can hold slightly fewer summaries than page_size. Set include_metadata=false to get bare ids only (cheapest). resultSizeEstimate is an estimate, not an exact count. Read a full body with get_message; read a whole conversation with get_thread.

Input parameters:

- `include_metadata` (boolean): false = bare ids only, no per-message metadata reads (default true).
- `include_spam_trash` (boolean): Also search SPAM and TRASH (default false).
- `label_ids` (array): Only messages carrying ALL of these label ids (see list_labels).
- `page_size` (integer): Messages per page (1..100, default 25).
- `page_token` (string): nextPageToken from the previous page.
- `query` (string): Gmail query syntax, e.g. "from:amy@example.com is:unread newer_than:7d has:attachment subject:invoice". Same operators as the Gmail search box.

### `get_message` (~249 tokens)

Read a message

Fetches one message fully decoded: headers (from, to, cc, subject, date, messageId, inReplyTo, references — RFC 2047 words decoded), the plain-text body (base64/charset decoded), the HTML body when there is no text part or include_html=true, and attachment METADATA (filename, mimeType, sizeBytes, attachmentId — never the content; download bytes via raw_request users/me/messages/<id>/attachments/<attachmentId> if truly needed). Bodies are truncated at max_body_chars (default 50000) with textTruncated/htmlTruncated flags. metadata_only=true skips bodies entirely. To reply later, keep headers.messageId (for in_reply_to) and threadId.

Input parameters:

- `include_html` (boolean): Return the decoded HTML body even when a text body exists (default false).
- `max_body_chars` (integer): Truncate each decoded body at this many characters (default 50000; a truncation flag is set when cut).
- `message_id` (string, required): The message id from list_messages/get_thread output (not the RFC Message-ID header).
- `metadata_only` (boolean): Headers and structure only, no body content.

### `send_message` (~361 tokens)

Send an email

Sends an email from the authenticated mailbox, immediately and irreversibly. Requires at least one recipient across to/cc/bcc (a bcc-only send is fine — to may be omitted) and at least a subject or a body; body_text and body_html together become multipart/alternative. TO REPLY IN A THREAD: call get_message on the message being answered first, then pass its threadId as thread_id, its headers.messageId as in_reply_to, and the same subject prefixed with "Re: " — Gmail threads the reply only when all three line up. Returns the sent message's id, threadId and labelIds. NEVER retried after a timeout or 5xx (a duplicate email cannot be unsent): if the outcome is unclear, search list_messages with in:sent before considering a re-send. Everyday accounts can send ~500 emails/day (Workspace ~2000); exceeding it disables sending for hours.

Input parameters:

- `bcc` (array): Blind-copy recipients.
- `body_html` (string): HTML body (sent as multipart/alternative when body_text is also set).
- `body_text` (string): Plain-text body.
- `cc` (array): Carbon-copy recipients.
- `in_reply_to` (string): RFC Message-ID of the message being replied to (headers.messageId from get_message).
- `references` (string): Explicit References header chain (defaults to in_reply_to).
- `subject` (string): The subject line. For replies use the original subject with "Re: ".
- `thread_id` (string): Reply into this thread (pair with in_reply_to and a matching subject).
- `to` (array): Primary recipients. Optional when cc or bcc carries at least one recipient (bcc-only send).

### `modify_message` (~216 tokens)

Change message labels / state

Changes a message's state via Gmail labels: read=true/false marks read/unread, starred=true/false stars/unstars, archived=true removes it from the inbox (archived=false moves it back), and add_label_ids/remove_label_ids apply or strip any labels from list_labels (e.g. a user label, or IMPORTANT). At least one change is required; applying the same change twice is harmless. This never deletes anything — use manage_trash for the trash. Returns the message's new id/labelIds.

Input parameters:

- `add_label_ids` (array): Label ids to add.
- `archived` (boolean): true = archive (remove from inbox), false = move back to inbox.
- `message_id` (string, required): The message id from list_messages/get_thread output (not the RFC Message-ID header).
- `read` (boolean): true = mark read, false = mark unread.
- `remove_label_ids` (array): Label ids to remove.
- `starred` (boolean): true = star, false = unstar.

### `manage_trash` (~183 tokens)

Trash or restore mail

Moves a message or a whole thread to the Gmail trash, or restores it. action=trash is REVERSIBLE: Gmail keeps trashed mail for about 30 days, then deletes it permanently; action=untrash restores it before that happens. target=message (default) uses a message id, target=thread trashes/restores every message in the thread. This server intentionally has no permanent-delete tool — the trash is the safety net. Note: untrash does not re-add INBOX; follow up with modify_message archived=false if it should reappear in the inbox.

Input parameters:

- `action` (string, required): trash = move to trash (reversible), untrash = restore.
- `id` (string, required): The message id (target=message) or thread id (target=thread).
- `target` (string): What the id refers to (default message).

### `list_threads` (~216 tokens)

Search and list threads

Searches conversations (threads) with the same Gmail query syntax as list_messages and returns id, snippet (of the latest message) and historyId per thread. Use this instead of list_messages when the unit of work is a conversation — triaging an inbox, finding a discussion to reply into. Filter with query and/or label_ids; paginate with page_token from nextPageToken (page_size max 500). Read the full conversation with get_thread.

Input parameters:

- `include_spam_trash` (boolean): Also search SPAM and TRASH (default false).
- `label_ids` (array): Only threads carrying ALL of these label ids (see list_labels).
- `page_size` (integer): Threads per page (1..500, API default 100).
- `page_token` (string): nextPageToken from the previous page.
- `query` (string): Gmail query syntax, e.g. "from:amy@example.com is:unread newer_than:7d has:attachment subject:invoice". Same operators as the Gmail search box.

### `get_thread` (~188 tokens)

Read a whole conversation

Fetches a conversation with every message decoded like get_message: headers, text bodies (HTML only when a message has no text part or include_html=true), attachment metadata and truncation flags. Messages come oldest-first. To reply to the conversation, take the LAST message's threadId, headers.messageId and subject and pass them to send_message (thread_id, in_reply_to, subject with "Re: "). Long threads can be large — lower max_body_chars (it applies per message) when only the gist is needed.

Input parameters:

- `include_html` (boolean): Return decoded HTML bodies even when a text body exists (default false).
- `max_body_chars` (integer): Truncate each decoded body at this many characters (default 50000; a truncation flag is set when cut).
- `thread_id` (string, required): The thread id from list_threads or from a message's threadId field.

### `modify_thread` (~195 tokens)

Change thread labels / state

Applies the same normalized state changes as modify_message — read/unread, starred, archived, add_label_ids/remove_label_ids — to EVERY message in a conversation at once. Use it to mark a whole conversation read or archive it in one call instead of looping over messages. At least one change is required; repeating the same change is harmless. Returns the thread's new id and message label state.

Input parameters:

- `add_label_ids` (array): Label ids to add to every message.
- `archived` (boolean): true = archive (remove from inbox), false = move back to inbox.
- `read` (boolean): true = mark the whole thread read, false = unread.
- `remove_label_ids` (array): Label ids to remove from every message.
- `starred` (boolean): true = star, false = unstar.
- `thread_id` (string, required): The thread id from list_threads or from a message's threadId field.

### `create_draft` (~276 tokens)

Create a draft

Creates a draft email in the mailbox without sending anything. All fields are optional — an empty draft is legal — but a useful one carries to[], subject and a body. For a reply draft set thread_id, in_reply_to (headers.messageId of the message being answered, via get_message) and the original subject with "Re: ". Returns the draft id (needed by update_draft/send_draft/delete_draft) and the underlying message id/threadId. Drafting first and sending with send_draft after a human look is the safe path for consequential mail — prefer it over send_message when in doubt.

Input parameters:

- `bcc` (array): Blind-copy recipients.
- `body_html` (string): HTML body (multipart/alternative when body_text is also set).
- `body_text` (string): Plain-text body.
- `cc` (array): Carbon-copy recipients.
- `in_reply_to` (string): RFC Message-ID of the message being replied to (headers.messageId from get_message).
- `references` (string): Explicit References header chain (defaults to in_reply_to).
- `subject` (string): The subject line.
- `thread_id` (string): Make it a reply draft in this thread (pair with in_reply_to and a matching subject).
- `to` (array): Primary recipients (optional for a draft).

### `list_drafts` (~139 tokens)

List drafts

Lists the mailbox's drafts: draft id plus the underlying message's id and threadId (no subjects — read one with get_draft). query filters with Gmail query syntax (e.g. subject:invoice); paginate with page_token from nextPageToken.

Input parameters:

- `page_size` (integer): Drafts per page (1..500, API default 100).
- `page_token` (string): nextPageToken from the previous page.
- `query` (string): Gmail query syntax, e.g. "from:amy@example.com is:unread newer_than:7d has:attachment subject:invoice". Same operators as the Gmail search box.

### `get_draft` (~147 tokens)

Read a draft

Fetches one draft with its message decoded like get_message: headers (to, cc, subject, ...), text body, HTML only on request, attachment metadata. Use it to show the user what send_draft would send, or to read the current content before update_draft (updates REPLACE the whole draft).

Input parameters:

- `draft_id` (string, required): The draft id from list_drafts or create_draft output.
- `include_html` (boolean): Return the decoded HTML body even when a text body exists (default false).
- `max_body_chars` (integer): Truncate each decoded body at this many characters (default 50000; a truncation flag is set when cut).

### `update_draft` (~252 tokens)

Update a draft

REPLACES a draft's entire message — the Gmail API has no partial draft edit, so omitted fields are dropped, not kept. Read the current content with get_draft first, then pass the complete new state (recipients, subject, body, and thread_id/in_reply_to for reply drafts). The draft id stays the same; the underlying message id changes. Returns the updated draft.

Input parameters:

- `bcc` (array): Blind-copy recipients.
- `body_html` (string): HTML body (multipart/alternative when body_text is also set).
- `body_text` (string): Plain-text body.
- `cc` (array): Carbon-copy recipients.
- `draft_id` (string, required): The draft id from list_drafts or create_draft output.
- `in_reply_to` (string): RFC Message-ID of the message being replied to (headers.messageId from get_message).
- `references` (string): Explicit References header chain (defaults to in_reply_to).
- `subject` (string): The subject line.
- `thread_id` (string): Make it a reply draft in this thread (pair with in_reply_to and a matching subject).
- `to` (array): Primary recipients (optional for a draft).

### `send_draft` (~139 tokens)

Send a draft

Sends an existing draft exactly as it is stored, immediately and irreversibly; the draft disappears from the drafts list and becomes a sent message (returned id/threadId). Verify the content with get_draft before calling this. NEVER retried after a timeout or 5xx (a duplicate email cannot be unsent): if the outcome is unclear, check list_drafts (the draft is gone if it was sent) or list_messages in:sent before considering anything else. Daily sending limits apply (~500/day consumer, ~2000/day Workspace).

Input parameters:

- `draft_id` (string, required): The draft id from list_drafts or create_draft output.

### `delete_draft` (~73 tokens)

Delete a draft

PERMANENTLY deletes a draft — drafts skip the trash, so there is no undo and no manage_trash recovery. Confirm with get_draft before deleting anything the user might still want. Returns empty on success.

Input parameters:

- `draft_id` (string, required): The draft id from list_drafts or create_draft output.

### `list_labels` (~120 tokens)

List labels

Without label_id: lists every label in the mailbox — system labels (INBOX, SENT, DRAFT, SPAM, TRASH, UNREAD, STARRED, IMPORTANT, CATEGORY_*) and user labels with their ids — the vocabulary that list_messages label_ids and modify_message add/remove_label_ids speak. With label_id: fetches that one label including its counts (messagesTotal, messagesUnread, threadsTotal, threadsUnread), which the plain list does not carry.

Input parameters:

- `label_id` (string): Fetch one label with counts instead of listing all.

### `manage_labels` (~257 tokens)

Create, rename or delete a label

Manages user labels. action=create needs name (nest with "/", e.g. "Clients/Acme" — the parent must already exist). action=update needs label_id plus at least one of name, label_list_visibility (show | show_if_unread | hide — the label in the sidebar) or message_list_visibility (show | hide — its messages in the list); only the provided fields change. action=delete needs label_id and removes the label from EVERY message it was applied to — the messages survive, the label and its message-associations do not; this cannot be undone. System labels (INBOX, STARRED, ...) cannot be created, renamed or deleted. To apply/remove labels on mail, use modify_message or modify_thread, not this tool.

Input parameters:

- `action` (string, required): What to do with the label.
- `label_id` (string): update/delete: the user label to target.
- `label_list_visibility` (string): Sidebar visibility of the label itself.
- `message_list_visibility` (string): Whether the label's messages show in the message list.
- `name` (string): create (required) / update: the label name, e.g. "Invoices" or nested "Clients/Acme".

### `get_profile` (~58 tokens)

Get the mailbox profile

Returns the authenticated mailbox's profile: emailAddress (the user's own address — useful for send-to-self checks and for recognizing the user's messages in threads), messagesTotal, threadsTotal and historyId. The cheapest way to verify that the OAuth credentials work.

### `raw_request` (~219 tokens)

Raw Gmail API call

Escape hatch to call any Gmail API v1 path directly, for requests the typed tools don't cover — e.g. downloading attachment content ("gmail/v1/users/me/messages/<messageId>/attachments/<attachmentId>", returns base64url data), history.list for incremental sync ("gmail/v1/users/me/history?startHistoryId=..."), batchModify, or settings endpoints (filters, forwarding, vacation). The path may carry a query string. The Bearer token is added automatically; the method defaults to GET. CAUTION: this bypasses the typed tools' guard rails — users/me/messages/<id> DELETE is a PERMANENT delete that skips the trash (needs the full https://mail.google.com/ scope); prefer manage_trash.

Input parameters:

- `body` (object): JSON request body (POST/PUT/PATCH).
- `method` (string): HTTP method. Defaults to GET.
- `path` (string, required): API path relative to https://gmail.googleapis.com, e.g. "gmail/v1/users/me/history?startHistoryId=123".

## Diagnostics

Captured diagnostic sections: Provenance, Dependencies. The full working is on the page: https://verifymcp.io/servers/a1-x-tech-mcp-google-gmail/mcp-google-gmail#diagnostics

## Score history

- 2026-09-20: 79
- 2026-09-19: 79
- 2026-09-18: 78
- 2026-09-17: 78
- 2026-09-16: 77
- 2026-09-15: 77
- 2026-09-14: 76
- 2026-09-13: 76
- 2026-09-12: 75
- 2026-09-11: 75
- 2026-09-10: 74
- 2026-09-09: 74
- 2026-09-08: 73
- 2026-09-07: 73
- 2026-09-06: 69
- 2026-09-05: 69
- 2026-09-04: 69
- 2026-09-03: 69
- 2026-09-02: 69
- 2026-09-01: 69
- 2026-08-31: 69
- 2026-08-30: 54

## Common questions

### What is the Gmail MCP server?

Gmail MCP is listed in the public MCP registry as io.github.A1-x-Tech/mcp-google-gmail. MCP server for the Gmail API: search, read and send email, manage drafts, labels and the trash. This page covers its npm package (mcp-google-gmail).

### Is the Gmail MCP server safe to use?

Gmail MCP scores 79 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 20 September 2026. It declares no install or post-install scripts. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the Gmail MCP server expose?

Gmail MCP exposes 18 tools: list_messages, get_message, send_message, modify_message, manage_trash, and 13 more. Their descriptions and schemas cost roughly 3,640 tokens of context every time the server is loaded.

### Is the Gmail MCP server still maintained?

Gmail MCP is still listed as active in the MCP registry. We last reached this channel on 20 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the Gmail MCP server under?

Gmail MCP declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- npm package: https://www.npmjs.com/package/mcp-google-gmail
- Socket report: https://socket.dev/npm/package/mcp-google-gmail
- Repository: https://github.com/A1-x-Tech/mcp-google-gmail
- Changelog RSS feed: https://verifymcp.io/servers/a1-x-tech-mcp-google-gmail/mcp-google-gmail.xml
- Changelog JSON feed: https://verifymcp.io/servers/a1-x-tech-mcp-google-gmail/mcp-google-gmail.json
- HTML version of this page: https://verifymcp.io/servers/a1-x-tech-mcp-google-gmail/mcp-google-gmail
