Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

Gmail Oauth

БесплатноНе проверен

A self-hostable Gmail MCP server that enables Gmail search/read, sending, replies, drafts, labels, and attachment downloads via MCP tools with OAuth authorizati

GitHubEmbed

Описание

A self-hostable Gmail MCP server that enables Gmail search/read, sending, replies, drafts, labels, and attachment downloads via MCP tools with OAuth authorization. Supports stdio and streamable HTTP transports.

README

A self-hostable Gmail MCP server with one executable, one SQLite storage implementation, and two selectable transports:

  • stdio for a local MCP client;
  • Streamable HTTP for localhost development or a remotely hosted MCP endpoint (remote is an alias for http).

The server lets one Google account be connected per authenticated principal and exposes Gmail search/read, sending, replies, forwards, drafts, labels, message state, Trash, and bounded attachment downloads. It does not expose an authentication, connection, or status tool. Authorization stays in the protocol control plane.

This is an early self-hosted implementation. Read Security before exposing it to the internet. Dynamically registered MCP clients are explicitly unverified, and the current SQLite deployment model is single-process.

What “receiving mail” means

Gmail receives the mail. This server reads it on demand:

  • gmail_search_messages runs a Gmail search when the tool is called;
  • gmail_get_message and gmail_get_thread fetch current content from Gmail when called.

There is no background mailbox synchronization, SMTP receiver, Gmail Pub/Sub watch, webhook, or proactive notification in this release. An MCP client must call search/read again to see newly delivered messages.

Authorization model

There are two different authorization relationships. Google credentials are never used as MCP bearer tokens and never pass through MCP messages.

Mode MCP client → this server This server → Google
stdio Local process/stdio trust; no transport bearer token Experimental negotiated stdio authorization extension, Google Desktop OAuth client, PKCE, ephemeral loopback callback
http / remote MCP OAuth authorization code flow, S256 PKCE, resource binding, DCR, rotating refresh tokens Google Web OAuth client, PKCE, fixed callback (HTTPS except explicitly enabled loopback development HTTP)

In stdio mode, authorization is exposed through the negotiated co.com.flujo/mcp-stdio-oauth control-plane methods, not tools/list. See STDIO-OAUTH-EXTENSION.md.

In HTTP mode, the server publishes protected-resource and authorization-server metadata. MCP clients dynamically register as public clients, the user confirms the exact callback on a local consent page, and Google sign-in establishes the principal bound to the resulting MCP tokens.

Requirements

  • Node.js 22.13 or newer

Local stdio installs use the bundled FLUJO Desktop OAuth identity and do not need a Google client file. Self-hosted OAuth projects and HTTP/remote deployments have the additional Google Cloud requirements described below.

The server requests openid, email, profile, and https://www.googleapis.com/auth/gmail.modify. Google classifies gmail.modify as a restricted scope. Public deployments may require OAuth app verification and, when restricted-scope data is stored or transmitted through a server, a security assessment. Review the current Google Gmail scope requirements.

Install from this repository

npm ci
npm run check
npm test
npm run build

Run the compiled executable with:

node dist/cli.js --help

The npm binary name is mcp-gmail-oauth when the package is installed as a package.

Google OAuth clients

Google treats installed/desktop and web-server OAuth clients as different application types. This server enforces that distinction, so one client cannot be reused for both transports.

Stdio: bundled by default

Stdio uses the public FLUJO Desktop OAuth client ID bundled with this package. A native-app client ID identifies the Google consent screen; it is not a secret, and the authorization-code exchange does not require a client secret. Each authorization still uses PKCE, random state and nonce values, and an ephemeral loopback listener on 127.0.0.1 with a runtime redirect such as:

http://127.0.0.1:49152

The port changes per authorization attempt. This is Google’s desktop loopback flow; the listener accepts only the root callback, atomically binds state to that exact attempt, times out after ten minutes, and closes after completion. Stray or mismatched localhost requests cannot consume another attempt or disable timeout cleanup.

The packaged google-desktop-client.json contains that same public ID without a client secret. It keeps older FLUJO configurations that explicitly named the file working; new installs do not need to pass it.

To use a separate Google Cloud project, create a Desktop app OAuth client and pass its downloaded JSON with --google-client or GOOGLE_OAUTH_CLIENT_FILE. The Gmail API, consent screen, publishing status, test users, and restricted-scope verification for that override belong to the self-hoster.

HTTP/remote: Web application client

Create a separate OAuth client with application type Web application. Add this exact authorized redirect URI in Google Cloud:

<public-base-url>/oauth/google/callback

Examples:

http://127.0.0.1:3000/oauth/google/callback
https://gmail-mcp.example.com/oauth/google/callback

Scheme, hostname, port, path, and trailing-slash behavior matter. Google requires the redirect URI to exactly match a configured URI, and the server refuses to start if the callback derived from --public-base-url is not present in the Web client JSON. See Google’s web-server OAuth guidance.

The intended hosting origin is required because it defines all externally visible security identifiers and endpoints, including the OAuth issuer, MCP resource audience, metadata URLs, and Google callback. The server deliberately does not infer them from Host or forwarded headers.

Keep downloaded override JSON outside the repository. A Web client secret is a server credential. The bundled stdio default contains only a public Desktop client ID, not a client secret.

Run over stdio

node dist/cli.js --transport stdio --local-user-id my-local-profile

Example MCP server configuration after building the repository:

{
  "mcpServers": {
    "gmail": {
      "command": "node",
      "args": [
        "dist/cli.js",
        "--transport",
        "stdio",
        "--local-user-id",
        "my-local-profile"
      ]
    }
  }
}

FLUJO can install this configuration, start the process, complete the MCP handshake, and then offer Google authorization without asking for a credential path. The user must still explicitly approve opening Google and grant Gmail access; authorization is never launched by an unattended connection test.

Other MCP clients must support and negotiate the stdio OAuth extension plus URL elicitation to initiate the first Google connection. A client without that support can see the Gmail business tools but has no supported way to start authorization; calls return an ordinary authorization_required tool result until the account is connected. A negotiated client receives the extension’s namespaced -32042 JSON-RPC recovery error when a Gmail call discovers missing, expired, or revoked authorization, after which it should request fresh status and offer a user-initiated start flow.

Use a stable, private --local-user-id. Profiles using the same value and database resolve to the same local principal.

Run HTTP on localhost

Local HTTP is useful for development and protocol testing only. It still requires a Web application Google OAuth client because it uses the hosted callback flow.

Register this Google redirect URI:

http://127.0.0.1:3000/oauth/google/callback

Then run:

node dist/cli.js \
  --transport http \
  --google-client /absolute/path/to/google-web-client.json \
  --listen 127.0.0.1:3000 \
  --public-base-url http://127.0.0.1:3000 \
  --allow-insecure-http

The MCP resource URL is http://127.0.0.1:3000/mcp. Plain HTTP is rejected by default. --allow-insecure-http permits it only for localhost, 127.0.0.1, or ::1, logs a dangerous-development warning, and produces a non-compliant development endpoint. It is not a production hosting option.

Host remotely

Terminate TLS at a hardened reverse proxy and keep the Node listener private when possible:

MCP_GMAIL_TRANSPORT=http \
MCP_GMAIL_DB_PATH=/var/lib/mcp-gmail-oauth/mcp-gmail-oauth.sqlite \
GOOGLE_OAUTH_CLIENT_FILE=/run/secrets/google-web-client.json \
MCP_GMAIL_LISTEN=127.0.0.1:3000 \
MCP_GMAIL_PUBLIC_BASE_URL=https://gmail-mcp.example.com \
MCP_GMAIL_ENCRYPTION_KEY='<32-byte-base64url-or-64-hex-secret>' \
node dist/cli.js

The externally configured Google callback must be:

https://gmail-mcp.example.com/oauth/google/callback

Production requirements:

  • expose only HTTPS publicly and redirect or reject public HTTP;
  • preserve the external Host header, or explicitly configure the required hostname with --allowed-host;
  • do not rewrite /mcp, /.well-known/*, or /oauth/* paths;
  • allow streaming responses and use suitable request/idle timeouts at the proxy;
  • rate-limit DCR, authorization, token, callback, and MCP routes;
  • set MCP_GMAIL_ENCRYPTION_KEY from a secret manager even when the private listener is loopback-bound;
  • back up the SQLite database consistently and back up its encryption key separately;
  • run one application process against the database and persistent local disk.

Any HTTP deployment whose public base URL has a non-loopback hostname requires MCP_GMAIL_ENCRYPTION_KEY at startup, even when its private listener is loopback-bound.

Remote OAuth endpoints

For https://gmail-mcp.example.com, the server publishes:

Purpose URL
MCP resource https://gmail-mcp.example.com/mcp
Protected-resource metadata https://gmail-mcp.example.com/.well-known/oauth-protected-resource/mcp
Authorization-server metadata https://gmail-mcp.example.com/.well-known/oauth-authorization-server
Dynamic client registration https://gmail-mcp.example.com/oauth/register
Authorization https://gmail-mcp.example.com/oauth/authorize
Token https://gmail-mcp.example.com/oauth/token
Revocation https://gmail-mcp.example.com/oauth/revoke
Google callback https://gmail-mcp.example.com/oauth/google/callback

The only MCP scope is mcp. The implementation supports public clients (token_endpoint_auth_method=none), authorization code plus S256 PKCE, optional refresh-token grants, exact redirect matching, RFC 8707-style resource binding, transaction-atomic code redemption and refresh issuance, refresh-token rotation, family revocation that cascades to linked live access tokens, and bearer access on /mcp.

Important DCR trust warning

DCR registration is open and does not verify a client’s claimed name or website. Every dynamically registered client is stored with trust level unverified, and the consent page labels it that way while showing the exact client ID, claimed website origin, redirect URI, and requested scope.

Registration means “syntactically accepted,” not “trusted.” Users must inspect the exact callback before approving. The server applies bounded, process-local request/concurrency limits and a separate DCR limit. By default, DCR is limited to 10 registrations per IP and 100 total per hour, with at most 1,000 unverified dynamic clients stored. Inactive clients become eligible for pruning after 30 days, but a client with a live authorization request, code, access token, or refresh token is retained.

These controls protect one process; they are not a distributed edge defense. Public operators should retain proxy-level rate/connection limits and abuse monitoring, and may need an admission policy or allowlist before treating this as an unrestricted public service. Client ID Metadata Documents, attestation, administrative approval, and a DCR client-management UI are not implemented.

Without an external admission layer, any network user who can reach the service can register a client and attempt to connect a Google account permitted by the Google project’s consent-screen policy. This release has no server-side user/domain allowlist.

Gmail tools

tools/list contains only these 18 Gmail business tools:

Area Tools
Search/read gmail_search_messages, gmail_get_message, gmail_get_thread
Compose/send gmail_send_message, gmail_reply_message, gmail_forward_message
Drafts gmail_create_draft, gmail_list_drafts, gmail_get_draft, gmail_update_draft, gmail_send_draft
Labels/state gmail_list_labels, gmail_create_label, gmail_modify_labels, gmail_set_message_state
Trash gmail_trash_message, gmail_untrash_message
Attachments gmail_download_attachment

There is intentionally no gmail_auth, gmail_connect, gmail_status, disconnect, permanent-delete, settings, filter, forwarding-rule, or mailbox-watch tool.

All Gmail API calls use userId=me; a tool cannot nominate a different Google account. Sending always uses the connected address. Attachments are accepted only as in-memory canonical base64—there are no filesystem-path or URL attachment sources.

Current safety limits include:

  • up to 50 search results or drafts per page;
  • up to 10 outgoing attachments;
  • up to 10 MiB decoded per attachment and 20 MiB decoded in aggregate;
  • attachment downloads default to 5 MiB and cannot request more than 10 MiB;
  • bounded MIME traversal and body output;
  • message reads return sanitized text and attachment metadata rather than active HTML.

Moving a message to Trash is reversible. Immediate permanent deletion is not implemented and is not permitted by gmail.modify.

Configuration

CLI options take precedence over environment variables.

Setting CLI Environment Default
Transport --transport stdio|http|remote MCP_GMAIL_TRANSPORT stdio
SQLite path --database <path> MCP_GMAIL_DB_PATH ~/.mcp-gmail-oauth/mcp-gmail-oauth.sqlite
Google client JSON override --google-client <path> GOOGLE_OAUTH_CLIENT_FILE bundled Desktop client in stdio; required Web client in HTTP
Stdio local identity --local-user-id <id> MCP_GMAIL_LOCAL_USER_ID local
HTTP listener --listen <host:port> MCP_GMAIL_LISTEN 127.0.0.1:3000
External origin --public-base-url <origin> MCP_GMAIL_PUBLIC_BASE_URL derived listener origin in HTTP mode
Insecure loopback HTTP --allow-insecure-http MCP_GMAIL_ALLOW_INSECURE_HTTP (true|false|1|0) false
Requests per IP/minute --oauth-rate-limit-per-minute <count> MCP_GMAIL_OAUTH_RATE_LIMIT_PER_MINUTE 120
Requests total/minute --oauth-global-rate-limit-per-minute <count> MCP_GMAIL_OAUTH_GLOBAL_RATE_LIMIT_PER_MINUTE 10000
DCR requests per IP/hour --oauth-dcr-rate-limit-per-hour <count> MCP_GMAIL_OAUTH_DCR_RATE_LIMIT_PER_HOUR 10
DCR requests total/hour --oauth-global-dcr-rate-limit-per-hour <count> MCP_GMAIL_OAUTH_GLOBAL_DCR_RATE_LIMIT_PER_HOUR 100
In-flight requests per IP --oauth-max-concurrent-per-ip <count> MCP_GMAIL_OAUTH_MAX_CONCURRENT_PER_IP 8
In-flight requests total --oauth-max-concurrent <count> MCP_GMAIL_OAUTH_MAX_CONCURRENT 128
Trusted proxy hops --oauth-trusted-proxy-hops <count> MCP_GMAIL_OAUTH_TRUSTED_PROXY_HOPS 0
Unverified DCR client cap --oauth-dynamic-client-limit <count> MCP_GMAIL_OAUTH_DYNAMIC_CLIENT_LIMIT 1000
DCR inactive age --oauth-dynamic-client-max-idle-days <days> MCP_GMAIL_OAUTH_DYNAMIC_CLIENT_MAX_IDLE_DAYS 30
Extra allowed hostname repeat --allowed-host <hostname> MCP_GMAIL_ALLOWED_HOSTS (comma-separated) public-origin hostname
Data encryption key no CLI option MCP_GMAIL_ENCRYPTION_KEY generated sidecar key when allowed
Log level --log-level <level> MCP_GMAIL_LOG_LEVEL info

remote is normalized to http. The external base URL must be an origin without a path, credentials, query, or fragment. It must use HTTPS. The only exception is loopback HTTP when dangerous development mode is explicitly enabled. Allowed-host entries are hostnames only—no scheme, port, or path.

Rate and concurrency counters are fixed-window, bounded, and held in this process only. Client IP is taken from the socket by default; forwarding headers are ignored. Set trusted proxy hops only when the service is reachable exclusively through that exact, trusted proxy chain. Multi-process deployments need a shared limiter at the edge, although this SQLite release supports only one application process.

The application reads process environment variables; it does not load .env itself. Use your process manager, container runtime, shell, or Node’s env-file support, for example:

node --env-file=.env dist/cli.js

See .env.example.

SQLite and deployment limits

Both transports use the same SqliteStore, schema, migrations, and default database path. You can stop one mode and start the other against the same file. One executable invocation selects one transport; it does not serve stdio and HTTP simultaneously.

Reusing the file does not make a Google grant portable between modes. Stdio local identities and HTTP Google identities use different principal namespaces, and each stored Gmail connection is tied to the Google OAuth client ID that created it. Switching modes/client types can therefore require a separate authorization.

The current supported deployment is one application process using a database on local persistent disk. Do not place the SQLite files on a network filesystem and do not run multiple replicas or simultaneous stdio/HTTP processes against the same database. There is no PostgreSQL adapter, distributed lock, or multi-process coordination yet.

The schema isolates remote Google connections by authenticated principal, and multiple Google users can use one hosted process. It currently has one logical default tenant, one Gmail account per principal, and no organization administration, domain allowlist, or tenant-provisioning UI. Host/path → tenant routing and tenant-bound clients/grants are future work. “Multi-user capable” should not be confused with a complete enterprise multi-tenancy control plane.

Data storage

Google access and refresh tokens and pending Google OAuth secrets are encrypted with AES-256-GCM and associated data. Google provider state, MCP authorization codes/tokens, and browser-session binding material are stored as hashes. The MCP client’s own state is stored as grant metadata so it can be echoed to the client. Principal email addresses, Google subject identifiers, scopes, DCR client metadata, and other grant metadata are not encrypted as secrets.

Without MCP_GMAIL_ENCRYPTION_KEY, a 32-byte key is generated at <database-path>.key. On POSIX systems the code requests mode 0600; Windows ACLs must be managed separately. Losing the key makes encrypted Google credentials unreadable. Copying the live database without its WAL can produce an inconsistent backup; stop the process or use a SQLite-aware backup procedure.

The server does not persist Gmail message bodies or attachment content as a mailbox cache. They pass through memory for the requested operation.

Development

npm run check
npm test
npm run build

Logs are structured JSON on stderr so stdio stdout remains reserved for JSON-RPC.

Security

See SECURITY.md for the threat model, hosted checklist, secrets guidance, DCR caveats, untrusted email-content warning, and known limitations.

License

MIT

from github.com/flujo-app/mcp-gmail-oauth

Установить Gmail Oauth в Claude Desktop, Claude Code, Cursor

Рекомендуется · одна команда, все IDE
unyly install mcp-gmail-oauth

Ставит в Claude Desktop, Claude Code, Cursor и VS Code — сам разбирается с npx, uvx и сборкой из исходников.

Впервые? Поставь CLI: curl -fsSL https://unyly.org/install | sh

Или настроить вручную

Выполни в терминале:

claude mcp add mcp-gmail-oauth -- npx -y github:flujo-app/mcp-gmail-oauth

Пошаговые гайды: как установить Gmail Oauth

FAQ

Gmail Oauth MCP бесплатный?

Да, Gmail Oauth MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для Gmail Oauth?

Нет, Gmail Oauth работает без API-ключей и переменных окружения.

Gmail Oauth — hosted или self-hosted?

Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.

Как установить Gmail Oauth в Claude Desktop, Claude Code или Cursor?

Открой Gmail Oauth на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.

Похожие MCP

Compare Gmail Oauth with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории communication