npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

safe-gmail-mcp

v0.2.1

Published

Security-focused local MCP server for reading Gmail headers and explicitly confirming Gmail sends.

Downloads

507

Readme

Safe Gmail MCP

Safe Gmail MCP is a local Model Context Protocol server for safely reading Gmail headers and sending Gmail messages through an explicit two-step confirmation flow.

It requests:

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

That scope is required to read messages and apply a processed label. It does not request delete, settings, forwarding, attachment download, or mail.google.com scopes.

Project Note

Safe Gmail MCP was initially built as an internal tool for BLISS-AI. Much of the implementation was AI-assisted, and not every line has been thoroughly vetted. The tool was created with good intentions and a security-first design, but you should still review it before relying on it for sensitive workflows.

Please use it, let us know if you face any issues, and send improvements if you find something that should be tightened. We plan to keep maintaining it.

Try Bliss AI: https://www.MeditatewithBliss.com

Security Model

  • OAuth uses a localhost-only browser flow with PKCE and CSRF state.
  • The redirect URI is loopback only: http://127.0.0.1:<port>/oauth/callback.
  • Tokens stay on the user's machine under ~/.safe-gmail-mcp/.
  • Token files are written with restrictive permissions, 0600 where the OS supports it.
  • Access tokens, refresh tokens, client secrets, email bodies, and attachment contents are never logged.
  • Unread checks return headers and snippets only. Reading a body requires a separate read_email_body tool call.
  • After a body is read, the server applies the Gmail label Safe Gmail MCP/Processed and leaves Gmail's unread state unchanged.
  • Sending is disabled unless SAFE_GMAIL_MCP_ENABLE_SEND=true.
  • Bulk sending is disabled unless both SAFE_GMAIL_MCP_ENABLE_SEND=true and SAFE_GMAIL_MCP_ENABLE_BULK_SEND=true are set.
  • Email cannot be sent from a single tool call. The client must call prepare_send_email, inspect the returned digest and preview, then call confirm_send_email with the exact digest.
  • Bulk email follows the same prepare/confirm pattern with a separate batch digest and a 25-message maximum batch size.
  • Pending sends expire after 10 minutes by default.
  • v1 does not support arbitrary local file attachments.

Install from npm

Safe Gmail MCP is published on npm:

https://www.npmjs.com/package/safe-gmail-mcp

Requirements:

  • Node.js 20 or newer
  • npm
npm install -g safe-gmail-mcp

Then connect Gmail:

safegmail connect

By default, this fetches Safe Gmail's default Google OAuth app metadata from a BLISS-controlled HTTPS endpoint and opens Google login directly. The local browser UI also has a Use my own Google OAuth app option for users who prefer their own Google Cloud project.

You can also run it without a global install:

npx -y safe-gmail-mcp serve

For a quick CLI check:

npx -y safe-gmail-mcp --help

Google OAuth Setup

Public/native OAuth clients cannot keep a client secret as a real package secret. This package does not commit or publish the default Google OAuth client ID or client secret. Instead, it fetches default OAuth app metadata at runtime from:

https://admin.meditatewithbliss.com/api/safe-gmail-mcp/oauth-client

That endpoint is a plain unauthenticated GET API that returns only OAuth app metadata, not user tokens. Safe Gmail MCP still uses the OAuth Authorization Code flow with PKCE, and Gmail tokens stay on the user's machine.

Expected endpoint shape:

{
  "clientId": "YOUR_DEFAULT_CLIENT_ID.apps.googleusercontent.com",
  "clientSecret": "YOUR_DEFAULT_CLIENT_SECRET",
  "scopes": ["https://www.googleapis.com/auth/gmail.modify"]
}

The simplest path is:

safegmail connect

Click Connect Gmail and complete Google login. Gmail tokens stay only on your machine.

If you do not want to trust the fetched default OAuth app, click Use my own Google OAuth app in the local UI. Paste your Google Desktop OAuth client ID and client secret there; they will be saved only to ~/.safe-gmail-mcp/config.json with restrictive permissions.

When BYO credentials are saved, safegmail connect shows the saved client ID, whether a secret is saved, and actions to change it, delete it, or return to the fetched default Safe Gmail OAuth app.

You can also provide it with an environment variable:

export SAFE_GMAIL_MCP_GOOGLE_CLIENT_ID="YOUR_CLIENT_ID.apps.googleusercontent.com"
export SAFE_GMAIL_MCP_GOOGLE_CLIENT_SECRET="YOUR_CLIENT_SECRET"

or directly in ~/.safe-gmail-mcp/config.json:

{
  "googleClientId": "YOUR_CLIENT_ID.apps.googleusercontent.com",
  "googleClientSecret": "YOUR_CLIENT_SECRET"
}

Broad public distribution with the Gmail modify scope may require Google OAuth app verification. This project does not include a hosted token broker; tokens stay local.

Auth Flow

safegmail connect

The long-form command is also supported:

safe-gmail-mcp auth

The CLI starts a web server bound only to 127.0.0.1 on a random available port and opens:

http://127.0.0.1:<port>

The page shows a Continue with Google button and a short safety note. If default OAuth metadata can be fetched, Continue with Google opens Google login directly. If it cannot be fetched, the page falls back to BYO OAuth app fields. The page also offers Use my own Google OAuth app for local-only BYO credentials. After Google redirects back to the loopback callback, the CLI verifies state, exchanges the code using PKCE, writes tokens under ~/.safe-gmail-mcp/, and exits.

Check status:

safe-gmail-mcp auth status

Disconnect Gmail tokens only:

safegmail disconnect

This deletes ~/.safe-gmail-mcp/tokens.json. It does not delete saved BYO OAuth app credentials, recipient policy, pending sends, or the audit log.

Delete all local Safe Gmail MCP state before uninstalling:

safegmail disconnect --all

This deletes ~/.safe-gmail-mcp/, including Gmail tokens, saved BYO OAuth app credentials, pending sends, config, and audit log.

The long-form token-only logout remains available:

safe-gmail-mcp auth logout

MCP Client Config

Claude Desktop example:

{
  "mcpServers": {
    "safe-gmail": {
      "command": "npx",
      "args": ["-y", "safe-gmail-mcp", "serve"],
      "env": {
        "SAFE_GMAIL_MCP_ENABLE_SEND": "false",
        "SAFE_GMAIL_MCP_ENABLE_BULK_SEND": "false"
      }
    }
  }
}

Set SAFE_GMAIL_MCP_ENABLE_SEND=true only after you are comfortable with the confirmation flow and local recipient policy. Set SAFE_GMAIL_MCP_ENABLE_BULK_SEND=true only if you also want confirmed bulk batches to send.

Tools

prepare_send_email

Inputs:

{
  "to": ["[email protected]"],
  "cc": ["[email protected]"],
  "bcc": ["[email protected]"],
  "subject": "Subject",
  "body": "Plain text body",
  "htmlBody": "<p>Optional HTML alternative</p>"
}

This validates recipients, applies allowlist/blocklist rules, writes a local pending send record, and returns:

  • pending ID
  • SHA-256 digest of the canonical payload
  • preview with recipients, subject, body length, HTML presence, and expiry

It does not send email.

confirm_send_email

Inputs:

{
  "pendingId": "PENDING_ID_FROM_PREPARE",
  "digest": "DIGEST_FROM_PREPARE"
}

This sends only when:

  • SAFE_GMAIL_MCP_ENABLE_SEND=true
  • Gmail is authenticated
  • the pending record exists
  • the pending record has not expired
  • the digest exactly matches the canonical payload

Returns the Gmail message ID on success.

list_pending_sends

Lists pending IDs, recipients, subject, created time, expiry time, and digest. It does not show the full body by default.

discard_pending_send

Deletes a pending send without sending it.

prepare_bulk_send

Inputs:

{
  "messages": [
    {
      "to": ["[email protected]"],
      "subject": "Subject",
      "body": "Plain text body",
      "htmlBody": "<p>Optional HTML alternative</p>"
    }
  ]
}

This validates and stages up to 25 email messages as one batch. It applies the same recipient allowlist/blocklist rules to every message, stores a local pending bulk record, and returns:

  • pending bulk ID
  • SHA-256 digest of the canonical batch payload
  • preview with message count, recipients, subjects, and expiry

It does not send email.

confirm_bulk_send

Inputs:

{
  "pendingBulkId": "PENDING_BULK_ID_FROM_PREPARE",
  "digest": "DIGEST_FROM_PREPARE"
}

This sends the prepared batch sequentially only when:

  • SAFE_GMAIL_MCP_ENABLE_SEND=true
  • SAFE_GMAIL_MCP_ENABLE_BULK_SEND=true
  • Gmail is authenticated
  • the pending bulk record exists
  • the pending bulk record has not expired
  • the digest exactly matches the canonical batch payload

Returns Gmail message IDs and sent count on success.

list_pending_bulk_sends

Lists pending bulk IDs, message counts, recipients, subjects, created time, expiry time, and digest. It does not show full bodies by default.

discard_pending_bulk_send

Deletes a pending bulk send without sending it.

list_unread_email_headers

Inputs:

{
  "maxResults": 10
}

Lists unread Gmail messages that have not been labeled Safe Gmail MCP/Processed. It returns message ID, thread ID, sender, recipients, subject, date, snippet, and label IDs. It does not return the email body.

read_email_body

Inputs:

{
  "messageId": "GMAIL_MESSAGE_ID"
}

Reads one email body by message ID, returns headers plus text and HTML body content, then applies the Safe Gmail MCP/Processed Gmail label. It does not remove Gmail's UNREAD label.

Config Reference

Config file:

~/.safe-gmail-mcp/config.json

Example:

{
  "googleClientId": "YOUR_CLIENT_ID.apps.googleusercontent.com",
  "googleClientSecret": "YOUR_CLIENT_SECRET",
  "allowedRecipients": ["example.com", "[email protected]"],
  "blockedRecipients": ["[email protected]", "blocked-domain.example"],
  "pendingTtlMinutes": 10,
  "fromEmail": "[email protected]"
}

Allowlist behavior:

  • exact email entries match one address
  • domain entries match any address at that exact domain
  • if an allowlist exists, all other recipients are blocked

Blocklist behavior:

  • exact email entries block one address
  • domain entries block any address at that exact domain
  • blocklist takes precedence over allowlist

fromEmail is optional and only controls the RFC 822 From header. Gmail still sends as the authenticated account or configured Gmail alias.

Environment Variables

| Variable | Purpose | | --- | --- | | SAFE_GMAIL_MCP_GOOGLE_CLIENT_ID | Overrides the config or fetched default OAuth client ID. | | SAFE_GMAIL_MCP_GOOGLE_CLIENT_SECRET | Overrides the config or fetched default Google Desktop app client secret. | | SAFE_GMAIL_MCP_DEFAULT_OAUTH_URL | Overrides the default OAuth metadata endpoint. | | SAFE_GMAIL_MCP_ENABLE_SEND=true | Enables confirm_send_email to actually call Gmail. | | SAFE_GMAIL_MCP_ENABLE_BULK_SEND=true | Enables confirm_bulk_send; also requires SAFE_GMAIL_MCP_ENABLE_SEND=true. |

Local Files

Safe Gmail MCP stores local state outside the repository:

~/.safe-gmail-mcp/
  config.json
  tokens.json
  pending/
  pending-bulk/
  audit.log

The audit log records timestamp, action, recipients, subject, digest, result, and a short error summary. It does not record full bodies or tokens.

Limitations

  • Gmail access uses gmail.modify so the server can read messages and apply its processed label.
  • Header listing is separate from body reading; list tools do not return full email bodies.
  • No delete, filters, forwarding rules, or settings tools.
  • The only label mutation is applying Safe Gmail MCP/Processed after read_email_body.
  • No arbitrary local file attachments in v1.
  • Bulk sends are capped at 25 messages per prepared batch and are sent sequentially.
  • No hosted token broker.
  • First-time default auth depends on the default OAuth metadata endpoint being reachable. BYO OAuth credentials work without that endpoint.
  • Inbox placement is not guaranteed by Gmail API usage.

Threat Model

Primary risks:

  • a malicious MCP client trying to send email without user review
  • token theft from local disk
  • prompt injection asking the model to exfiltrate email content or send to an attacker-controlled address
  • accidental sends to the wrong recipient

Mitigations:

  • two-step prepare/confirm with digest binding
  • sending disabled by default
  • bulk sending has an additional opt-in and batch size cap
  • recipient allowlist/blocklist
  • short pending-send expiry
  • local-only OAuth callback
  • PKCE and CSRF state
  • restrictive local token file permissions
  • no email body in pending list or audit log
  • separate header-list and body-read tools
  • processed label filtering for emails the MCP has already handled

Residual risks:

  • any local process running as the same OS user may be able to access that user's files
  • an MCP client with send enabled can still ask for confirmation, so users must inspect the preview and digest
  • an MCP client with Gmail access can read email bodies when it calls read_email_body; use a trusted MCP client and review tool calls
  • the default OAuth app metadata endpoint is public and requires trust in the BLISS-controlled endpoint unless users bring their own OAuth client ID

Development

npm ci
npm run typecheck
npm test
npm run build
npm pack --dry-run

Tests use a mock Gmail client and must not call Google APIs.

Release Notes

Use npm provenance where feasible:

npm publish --provenance

Before publishing, run:

npm ci
npm test
npm run build
npm pack --dry-run
npm run smoke:pack