Skip to main content
Glama
pyrodie18

Gmail MCP

by pyrodie18

Gmail MCP

Private multi-account Gmail MCP server for Codex.

Development

Open this repository through Remote SSH and choose Dev Containers: Reopen in Container. The container exposes only this repository, runs as the unprivileged vscode user, and uses a repository-specific Codex home on the VM. Its sole Docker security override relaxes seccomp because the real Codex workspace sandbox was verified to require nested user namespaces on this VM. No capabilities, privileged mode, AppArmor override, or Docker socket are used.

The central development image provides Python, uv, Codex, and generic build tools. Project dependencies are installed from the checked-in uv.lock into this repository's ignored .venv. The devcontainer creates that environment automatically and keeps it isolated from every other repository and worktree.

The project commands are:

uv lock --check
uv sync --frozen --group dev
uv run --frozen pytest
uv run --frozen flake8 src tests
uv build

The first time this repository is opened, authenticate its isolated Codex state with the primary development ChatGPT account by running codex login in the container. Do not put API keys in the devcontainer configuration.

Related MCP server: gmail-mcp

Google Cloud configuration

This server accesses each Gmail account with a user-authorized OAuth 2.0 Web application client. It does not use an API key or a service account. Complete these steps in the Google Cloud console before starting the server.

  1. Create and select a project. Use a dedicated project for this server; keeping development and production projects separate makes it less likely that test credentials or consent-screen settings leak into production.

  2. Enable the Gmail API. Open APIs & Services > Library, search for Gmail API, select it, and click Enable. The OAuth client cannot request Gmail scopes until this API is enabled.

  3. Configure the OAuth consent screen. Open Google Auth platform and complete the setup screens (the first visit may show Get started):

    • On Branding, provide an accurate application name, a user-support email address, and developer contact email. Do not use Google trademarks or a Google product logo in the app name or logo.

    • On Audience, choose Internal only when every account is in the same Google Workspace organization. Otherwise choose External. For a private personal or development deployment, leave an External app in Testing and add every Gmail account to Test users.

    • On Data Access, use Add or remove scopes and add the scopes this service requests:

      https://www.googleapis.com/auth/gmail.modify
      https://www.googleapis.com/auth/gmail.compose
      https://www.googleapis.com/auth/gmail.send

      The service requests these by default; retain the same list if you set GOOGLE_OAUTH_SCOPES yourself. gmail.modify permits reading and changing mailbox state, gmail.compose manages drafts and sending, and gmail.send permits sending. Do not add broader scopes such as https://mail.google.com/ unless the code is deliberately changed to need them.

  4. Create the OAuth client. Open Google Auth platform > Clients, click Create client, choose Web application, and give it a descriptive name such as gmail-mcp-local.

    Under Authorized redirect URIs, add exactly:

    http://localhost:8080/admin/oauth/callback

    No Authorized JavaScript origin is needed for this server-side flow. The value in Google Cloud must exactly equal GOOGLE_REDIRECT_URI, including its scheme, host, port, path, case, and trailing slash. A redirect_uri_mismatch error means those values differ.

    http is permitted for localhost during local development. For any non-localhost hostname, use a domain you control and an HTTPS callback endpoint (normally behind a TLS reverse proxy); do not register a raw LAN IP address as the callback. Add that same HTTPS URL both here and in GOOGLE_REDIRECT_URI.

  5. Copy the client ID and client secret. After creating the client, copy its values from the client details page. Treat the secret as a password: keep it out of Git, the repository, shell history, and screenshots. You will place the values in the VM-only secrets file in the next section.

Testing versus production

The Gmail scopes above are sensitive or restricted. For an External app in Testing, Google allows only the Gmail addresses listed as test users and their authorizations (including offline refresh tokens) expire after seven days; re-run the account authorization flow when that happens. This is the simplest path for a private deployment.

To authorize arbitrary external users without the testing limit, publish the app and complete Google's OAuth verification. Restricted scopes can require scope justification, a demo video, and—when restricted data is stored on or transmitted through servers—a security assessment. An Internal Workspace app does not require public OAuth verification, but its Workspace administrator may still need to allow the app's Gmail access. Review Google's Gmail scope classifications, app audience guidance, and verification requirements before publishing.

LAN deployment and account authorization

The server may run on a LAN VM while the browser and Codex client run on a different computer. Do not expose the unencrypted Docker port to an untrusted network: both the admin token and MCP bearer token are credentials.

Choose one of these deployment models:

  • SSH tunnel (recommended for a private deployment): no DNS, certificate, or application changes. The service stays on the LAN VM, while the Windows workstation reaches it securely as localhost through SSH. This is the simplest way to use the normal Google OAuth flow.

  • LAN HTTPS endpoint: use this when multiple clients must reach the service directly. Use a domain that you control, LAN DNS, a TLS reverse proxy, and a publicly trusted certificate obtained with DNS-01. This is also required if Google OAuth must redirect directly to the LAN endpoint rather than through an SSH tunnel.

This option does not move the service to the Windows PC. It securely forwards the Windows PC's localhost:8080 to the VM's localhost:8080. Google therefore sees the allowed http://localhost callback, and the VM receives the callback over the tunnel.

  1. On the VM, keep the standard localhost callback in both Google Cloud and ~/.config/dev-secrets/gmail_mcp.env:

    GOOGLE_REDIRECT_URI=http://localhost:8080/admin/oauth/callback

    Start the service on the VM as described in Quick Start. The current Compose configuration publishes port 8080 on the VM, which also makes it reachable at the VM's loopback address for SSH forwarding.

  2. On the Windows workstation, open PowerShell or Windows Terminal. Verify the built-in OpenSSH client is available:

    ssh -V

    If the command is unavailable, install OpenSSH Client from Settings > System > Optional features. Do not install or configure an SSH server on Windows for this workflow; Windows is the SSH client and the LAN VM is the SSH server. See Microsoft's OpenSSH overview if the optional feature is managed by your organization.

  3. Create the tunnel. Replace the placeholder with the SSH user and LAN DNS name or private IP address of the VM:

    ssh -N -o ExitOnForwardFailure=yes -L 127.0.0.1:8080:127.0.0.1:8080 <vm-user>@<vm-host-or-ip>

    Keep this terminal open. -N opens no remote shell, and binding the local side to 127.0.0.1 prevents other LAN devices from using the tunnel.

    If TCP port 8080 is already in use on Windows, use a different local port, such as 18080, in the command and change the registered callback and GOOGLE_REDIRECT_URI to http://localhost:18080/admin/oauth/callback.

  4. In a second PowerShell window, confirm that the tunnel reaches the service:

    curl.exe http://127.0.0.1:8080/health
  5. Complete account authorization in a browser on the Windows workstation:

    http://localhost:8080/admin/oauth/start?alias=personal&token=<ADMIN_TOKEN>

    Google redirects the browser to localhost; SSH delivers that request to the VM, which exchanges the authorization code and stores the refresh token. The tunnel need only be present whenever an account is added or reauthorized.

  6. To keep normal MCP traffic encrypted without setting up LAN TLS, leave the tunnel running and add the MCP server from Windows PowerShell:

    $env:MCP_BEARER_TOKEN = "<same MCP_BEARER_TOKEN from the VM secrets file>"
    codex mcp add gmail --url http://localhost:8080/mcp --bearer-token-env-var MCP_BEARER_TOKEN

    $env: sets the token for the current PowerShell session only. Do not use setx for this secret. Stop the tunnel with Ctrl+C when it is no longer needed.

Option 2: Direct LAN HTTPS endpoint

Use this model only when clients must reach the server without an SSH tunnel. Google requires a non-localhost OAuth callback to use HTTPS and a domain name; do not register a raw LAN IP address or an internal-only suffix such as .local.

  1. Choose a domain that you control, for example gmail-mcp.example.com. Configure LAN (split-horizon) DNS so that name resolves to the VM's private IP address for every client that will use it.

  2. Put a TLS reverse proxy such as Caddy on the VM. Obtain a publicly trusted certificate using the DNS-01 challenge through your DNS provider's API. The challenge proves control of the DNS name with a TXT record, so the VM does not need to accept connections from the public internet. Caddy needs the DNS provider module for the provider you use; follow that provider module's installation guidance. See Caddy's automatic HTTPS and tls directive documentation.

    A representative Caddyfile (shown with Cloudflare's DNS module) is:

    gmail-mcp.example.com {
        tls {
            dns cloudflare {env.CLOUDFLARE_API_TOKEN}
        }
        reverse_proxy 127.0.0.1:8080
    }

    Keep Caddy's DNS API token outside this repository. If Caddy runs on the VM host, bind the Compose application port to loopback so the unencrypted backend cannot bypass Caddy:

    ports:
      - "127.0.0.1:8080:8080"

    Alternatively, enforce the same restriction with the VM firewall: permit LAN TCP/443 to Caddy and deny LAN TCP/8080. Caddy terminates TLS; the loopback connection from Caddy to this application is plain HTTP.

  3. Register this exact redirect URI in Google Cloud and set the same value in the VM secrets file:

    GOOGLE_REDIRECT_URI=https://gmail-mcp.example.com/admin/oauth/callback
    MCP_ALLOWED_HOSTS=gmail-mcp.example.com,gmail-mcp.example.com:443

    Restart the application after changing its environment, and reload Caddy after changing its configuration.

  4. Authorize an account and connect Codex using the HTTPS hostname:

    https://gmail-mcp.example.com/admin/oauth/start?alias=personal&token=<ADMIN_TOKEN>
    $env:MCP_BEARER_TOKEN = "<same MCP_BEARER_TOKEN from the VM secrets file>"
    codex mcp add gmail --url https://gmail-mcp.example.com/mcp --bearer-token-env-var MCP_BEARER_TOKEN

    MCP_ALLOWED_ORIGINS remains empty for a native Codex client. Set it only when intentionally allowing a browser-based MCP client, and restrict it to the exact browser origin or origins.

Authorization alternatives

The standard OAuth flow above is the only supported path in this repository for personal Gmail accounts and the server's full feature set (mailbox access, labels, drafts, and sending). A Gmail API key cannot access private mailbox data, and a standalone service account does not have a personal Gmail mailbox.

  • A Google Workspace service account with domain-wide delegation can avoid per-user consent screens, but only a Workspace super administrator can grant it and the application must be changed to impersonate each mailbox. It is not configured by this project and gives the service broad administrative authority.

  • Google Workspace SMTP relay can send mail based on the server's allowed IP address, but it cannot read mail or manage Gmail labels and drafts. It would require a send-only redesign.

  • IMAP/SMTP app passwords are static credentials with account and policy restrictions. They do not work with the current Gmail API implementation and would also require a redesign. Do not put an account password in this service.

Quick Start

For a Windows workstation connecting to a LAN VM, follow either deployment option above when completing steps 5 and 6. The localhost URLs below are the default for a browser and Codex running on the same machine as the service.

  1. Complete the Google Cloud configuration and record the OAuth client ID and secret.

  2. Generate the remaining server secrets:

uv run --frozen python - <<'PY'
from cryptography.fernet import Fernet
import secrets
print("GMAIL_MCP_ENCRYPTION_KEY=" + Fernet.generate_key().decode())
print("ADMIN_TOKEN=" + secrets.token_urlsafe(32))
print("MCP_BEARER_TOKEN=" + secrets.token_urlsafe(32))
PY
  1. On the VM (not inside the dev container), create ~/.config/dev-secrets/gmail_mcp.env from .env.example. Set GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and GOOGLE_REDIRECT_URI from the Google Cloud steps, then add the generated values. Set mode 0600.

  2. Start the server with command-scoped secret injection:

./scripts/dev-compose up --build
  1. Add accounts through the admin OAuth flow. Use a browser that can reach the callback URL registered in Google Cloud, then sign in as a listed test user (or an allowed Internal/verified user):

http://localhost:8080/admin/oauth/start?alias=personal&token=<ADMIN_TOKEN>
  1. Connect Codex:

export MCP_BEARER_TOKEN="<same token>"
codex mcp add gmail --url http://localhost:8080/mcp --bearer-token-env-var MCP_BEARER_TOKEN

Required Environment

  • GOOGLE_CLIENT_ID

  • GOOGLE_CLIENT_SECRET

  • GOOGLE_REDIRECT_URI

  • GMAIL_MCP_ENCRYPTION_KEY

  • ADMIN_TOKEN

  • MCP_BEARER_TOKEN

Optional:

  • DATABASE_URL, default sqlite:////data/gmail_mcp.sqlite3

  • MCP_ALLOWED_HOSTS, comma-separated, default localhost,localhost:*,127.0.0.1,127.0.0.1:*

  • MCP_ALLOWED_ORIGINS, comma-separated, default empty

  • LOG_LEVEL, default INFO

Safety Model

Draft creation happens immediately. Sending mail, sending drafts, label changes, archive, mark read/unread, star/unstar, and trash create pending actions first. A caller must execute gmail_pending_action_confirm with the returned pending_action_id before Gmail state changes.

The committed .env.example is a variable-name contract only. Real values stay outside Git and are passed to Docker Compose only for the command being run.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables interaction with Gmail through the MCP protocol, supporting sending, reading, searching, replying, forwarding, managing drafts and labels, and saving attachments.
    15
    3
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables interacting with multiple Gmail accounts through a single MCP server, supporting search, labels, drafts, and thread management with per-account OAuth.
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables Gmail operations such as reading, sending, searching, and managing emails, threads, labels, and drafts via MCP tools.
    1
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pyrodie18/gmail_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server