Skip to main content
Glama
Arshdeep54

gmail-mcp-server

by Arshdeep54

gmail-mcp-server

A self-hosted MCP server for Gmail, built on the official Gmail API. No third-party relay, no data leaving your own infrastructure, your Google OAuth token and email data stay on whatever machine you run this on.

Works with any MCP-compatible client: Claude Code over local stdio, or a hosted client like ChatGPT's custom connectors over HTTP with a built-in OAuth 2.1 authorization server.

Why

Most "Gmail for AI" integrations are either a paid SaaS connector or require handing a third party your inbox. This is neither: it's a small server you run yourself, talking directly to Google's API with your own OAuth client.

Related MCP server: Gmail MCP

Tools

Tool

Description

gmail_search

Search messages with Gmail's query syntax (from:, is:unread, after:, ...)

gmail_get_message

Fetch one message in full: headers, plain-text and HTML body, attachment metadata

gmail_get_thread

Fetch every message in a thread

gmail_create_draft

Create a draft (optionally as a threaded reply). Never sends anything

gmail_list_labels

List labels, with per-label message/thread counts

Every response includes to/cc/bcc, labelIds, and (for get_message/get_thread) attachment filenames and sizes. This server requests gmail.readonly and gmail.compose, enough to read mail and create drafts, and never anything more: there is no send, modify, or delete tool, and it never returns the contents of credentials.json or any token through a tool. gmail_create_draft only ever leaves a draft sitting in Drafts, a human has to open Gmail and hit send themselves.

Quickstart: local use with Claude Code

Requirements: Python 3.10+, a Google Cloud project with the Gmail API enabled.

git clone https://github.com/<you>/gmail-mcp-server.git
cd gmail-mcp-server
python -m venv .venv && source .venv/bin/activate
pip install -e .
  1. In the Google Cloud Console, enable the Gmail API, then go to APIs & Services → Credentials → Create Credentials → OAuth client ID, choose Desktop app, and download the JSON as secrets/credentials.json.

  2. Add yourself as a test user under OAuth consent screen → Test users if the app is in Testing mode.

  3. Authorize once:

    python authorize.py

    This opens a browser, and writes secrets/token.json.

  4. Add it to Claude Code's MCP config:

    {
      "mcpServers": {
        "gmail": {
          "command": "python",
          "args": ["-m", "gmail_mcp.server"],
          "cwd": "/path/to/gmail-mcp-server"
        }
      }
    }

That's it for local, stdio-based use, no auth server, no network exposure.

Deploying for a hosted client (e.g. ChatGPT connectors)

Hosted MCP clients need a real HTTPS endpoint and typically support only None or OAuth authentication, not a plain bearer header. This project ships a minimal OAuth 2.1 authorization server for exactly that case: a standard authorization-code + PKCE flow, gated by a single consent screen that asks for a passphrase you set yourself, so a token is only ever issued after you personally approve it.

  1. Copy .env.example to .env and fill it in (see the comments in that file).

  2. Put this behind a reverse proxy that terminates HTTPS (Caddy, Nginx, Traefik, your load balancer of choice) and point a real domain at it.

  3. docker compose up -d --build

  4. In your MCP client, add a custom connector:

    • Server URL: https://your-domain/mcp

    • Authentication: OAuth, with your own client ID (from .env), no client secret, token endpoint auth method none

    • It should auto-discover the authorization/token endpoints from https://your-domain/.well-known/oauth-authorization-server

  5. On first connect you'll land on a small consent page on your own server, enter the passphrase from .env to approve.

Access tokens are short-lived (1 hour) with long-lived refresh tokens, both persisted to secrets/oauth_state.json so a container restart doesn't force re-authorization.

Security notes

  • Only gmail.readonly and gmail.compose are ever requested, nothing broader.

  • credentials.json, token.json, .env, and secrets/ are git-ignored, never commit them.

  • The OAuth flow's consent step requires a passphrase only you know, so a leaked connector URL alone can't silently mint a token.

  • DNS-rebinding protection (GMAIL_MCP_ALLOWED_HOSTS) locks the server to the hostname(s) you configure.

  • There is no send, modify, or delete tool, by construction: the only write path is gmail_create_draft, and a draft is inert until a human opens it in Gmail and sends it.

  • This is single-tenant: it authenticates as one Gmail account (yours), not a per-user login. Anyone who reaches your deployed instance and gets past the consent passphrase reads and drafts as you, not as themselves, don't share the deployed URL and passphrase together.

Project layout

gmail_mcp/
  auth.py            Google OAuth (installed-app flow) for the Gmail API itself
  gmail_client.py     Thin wrapper around the Gmail API for the four tools
  server.py           MCP tool definitions
  http_app.py          HTTP transport entrypoint (used for Docker/hosted deployments)
  oauth_provider.py   Minimal OAuth 2.1 authorization server (auth code + PKCE + refresh)
  consent.py           The passphrase-gated consent page
authorize.py           One-time local script to obtain secrets/token.json

License

MIT, see LICENSE.

Available Tools

5 tools
gmail_create_draftA

Create a Gmail draft. Never sends anything, the draft sits in Drafts for the user to review and send themselves. Set reply_to_message_id to draft a reply within an existing thread (sets In-Reply-To/References/threadId correctly).

ParametersJSON Schema
NameRequiredDescriptionDefault
ccNo
toYes
bccNo
bodyYes
subjectYes
reply_to_message_idNo

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden of behavioral disclosure. It clearly states the tool never sends anything, which is a critical safety behavior. It also explains the reply_to_message_id side-effect on In-Reply-To/References/threadId. It does not cover auth requirements, error handling, or return values, but for a draft creation tool, the primary behavioral trait (non-sending) is well disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences long, with the most important information (purpose and non-sending) front-loaded. Every sentence earns its place: the first establishes the core behavior, the second explains the reply use-case. No filler or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity (6 params, no output schema, no annotations), the description covers the essential context: what it does, what it does not do, and how to use the reply feature. It does not describe return values or error scenarios, but for a draft-creation tool, an agent can infer that the draft is created successfully without explicit return details. The description is adequate for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must add meaning to parameters. It does this for reply_to_message_id by explaining its function, which is a meaningful addition. The other parameters (to, subject, body, cc, bcc) are self-explanatory given their names and types, so they need no elaboration. The description adds value where needed but does not comprehensively document all parameters, hence a baseline of 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a clear verb-resource pair ('Create a Gmail draft') and immediately clarifies the key distinction: 'Never sends anything, the draft sits in Drafts.' This makes the tool's purpose unambiguous and separates it from sending actions. It also hints at a specific use case (replying in a thread) without naming siblings, but the core purpose is explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides a concrete usage guideline for the reply_to_message_id parameter, explaining that it sets the draft as a reply in an existing thread. It implicitly tells the agent when to use this tool (when a draft is needed) versus sending, but does not explicitly name alternatives or state when not to use it. The non-sending behavior is a strong contextual cue.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

gmail_get_messageA

Fetch a single Gmail message by id, including its plain-text body.

ParametersJSON Schema
NameRequiredDescriptionDefault
message_idYes

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It reveals that the operation is a read ('Fetch') and that the output includes the plain-text body, which is useful. However, it omits error behavior, what else is returned beyond the body, and whether authentication scopes are needed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single, front-loaded sentence with no filler. Every word contributes to conveying the operation and its output scope.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter get operation, the description covers the core action and one key output aspect (plain-text body). However, with no output schema, it should more fully describe the returned object and handle common edge cases (e.g., message not found, auth requirements). It is minimally sufficient but not thorough.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. The phrase 'by id' clarifies that message_id is a Gmail message identifier, distinguishing it from a thread id. Yet it does not explain the expected format of the id or how to obtain it, leaving some ambiguity.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Fetch'), a clear resource ('a single Gmail message by id'), and a scoping detail ('including its plain-text body'). This distinguishes it from siblings like gmail_search (which returns multiple messages) and gmail_get_thread (which operates on threads, not individual messages).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies the tool should be used when you have a message_id and need the message's plain-text body, but it does not explicitly contrast with siblings or state when not to use it. No alternative tools or exclusions are mentioned.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

gmail_get_threadA

Fetch every message in a Gmail thread by thread id.

ParametersJSON Schema
NameRequiredDescriptionDefault
thread_idYes

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden. 'Fetch every message' accurately conveys a read-only operation, but the description does not disclose error behavior, authentication needs, response structure, or ordering of messages. For a simple getter this is adequate but not rich.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, front-loaded sentence with no filler. It states the action, resource, and required input efficiently.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with one required parameter and no output schema, the description covers the essential invocation. However, it omits details about return format, error cases, and how thread_id relates to results from sibling tools like gmail_search, leaving some ambiguity for an agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It only mentions 'thread id' without adding format, source, or usage detail beyond the parameter name. The phrase 'Gmail thread' gives minimal context but does not meaningfully expand on the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('Fetch') and resource ('every message in a Gmail thread') and identifies the required identifier ('thread id'). It clearly distinguishes this tool from gmail_get_message by emphasizing that it returns the whole thread rather than a single message.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage when an agent needs all messages in a thread and has a thread_id, but it does not explicitly state when to use this tool versus alternatives such as gmail_get_message. No when-not or alternative guidance is provided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

gmail_list_labelsA

List all Gmail labels on the account.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the burden of behavioral disclosure. 'List' implies a read-only action and 'all' defines scope, but it does not mention whether system labels are included, potential pagination, or authentication requirements. The risk is low, but transparency is minimal.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The entire description is one tight, front-loaded sentence with no filler. Every word contributes meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, read-only listing operation with an output schema available, the description is nearly complete. It could be improved only by a note on label scope (e.g., system vs user labels) or an explicit read-only guarantee.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are zero parameters, so the schema fully covers invocation needs. The description adds useful scope context by specifying all account labels.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('List') and resource ('Gmail labels') with explicit scope ('all ... on the account'), making it easy to distinguish from siblings that target messages, threads, or drafts.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The intended use is implied by the resource name, but the description does not explicitly state when to prefer this tool, nor does it name alternatives or exclusions. Sibling tools are clearly different, so guidance is not misleading, just absent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

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

  1. 5 tool updatesv0.1.0
    • First observedgmail_create_draft
    • First observedgmail_get_message
    • First observedgmail_get_thread
    • First observedgmail_list_labels
    • First observedgmail_search

TDQS

A3.8/5.0

Scored across 5 tools

Disambiguation5/5

Each tool targets a distinct Gmail resource and action: searching, fetching a message, fetching a thread, creating a draft, and listing labels. There is no overlap or ambiguity between them.

Naming Consistency5/5

All tool names follow a uniform `gmail_<verb>_<noun>` pattern (search, get_message, get_thread, create_draft, list_labels). The naming is fully consistent and predictable.

Tool Count5/5

Five tools is a well-scoped size for a focused Gmail integration covering search, read, drafts, and labels. The count feels intentional rather than sparse or bloated.

Completeness3/5

The set covers search, retrieval, thread viewing, draft creation, and label listing, but lacks common Gmail operations like sending, updating/deleting drafts, modifying labels on messages, or marking read/unread. The read-and-draft focus is coherent, but the domain is only partially covered.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers