@ideadesignmedia/gmail-mcp
v0.1.7
Published
Private MCP server and CLI for linking multiple Gmail inboxes and operating over them
Readme
@ideadesignmedia/gmail-mcp
Private MCP server and CLI that links multiple Gmail inboxes and exposes tools over stdio. Tokens are stored in SQLite and can be encrypted-at-rest with a password. When locked, every command and the MCP server require a password to run.
- Transport: stdio (suitable for MCP clients that launch a local command)
- Default DB:
~/.idm/gmail-mcp/db.sqlite - Node requirement: >= 18.17
Run with npx (recommended)
You do not need to install anything globally. Use npx to run the CLI and server. On first run, npx will download the package; later runs use the cached copy.
Show help:
npx @ideadesignmedia/gmail-mcp --helpAdd an account (loopback OAuth):
# Define or export your Google OAuth client credentials
export GOOGLE_CLIENT_ID=your_client_id
export GOOGLE_CLIENT_SECRET=your_client_secret
# Start the OAuth flow and link the Gmail account
npx -y @ideadesignmedia/gmail-mcp add \
--client-id "$GOOGLE_CLIENT_ID" \
--client-secret "$GOOGLE_CLIENT_SECRET"
# The CLI prints a URL; open it in your browser to consent.
# After consenting, the local callback will display "Authentication complete".Add an account (device code flow; headless-safe):
export GOOGLE_CLIENT_ID=your_client_id
export GOOGLE_CLIENT_SECRET=your_client_secret
npx -y @ideadesignmedia/gmail-mcp add \
--device \
--client-id "$GOOGLE_CLIENT_ID" \
--client-secret "$GOOGLE_CLIENT_SECRET"Lock the database with a password:
npx -y @ideadesignmedia/gmail-mcp passwd --pass 'your-strong-pass'Start the MCP server over stdio (for your MCP client to launch):
export GMAIL_MCP_DB_PASS='your-strong-pass' # only required if the DB is locked
npx -y @ideadesignmedia/gmail-mcp startNotes for npx usage:
-yauto-confirms the download prompt on first run.- You can pass
--db /custom/path.sqliteto store the database elsewhere. - Use
--read-onlywithstartto disable write actions (send/label modify).
Quick start (end‑to‑end)
Create a Google OAuth client (see "Google OAuth setup").
Link one or more Gmail accounts:
export GOOGLE_CLIENT_ID=...
export GOOGLE_CLIENT_SECRET=...
npx -y @ideadesignmedia/gmail-mcp add --client-id "$GOOGLE_CLIENT_ID" --client-secret "$GOOGLE_CLIENT_SECRET"- (Optional but recommended) Lock the DB with a password so tokens are encrypted at rest:
npx -y @ideadesignmedia/gmail-mcp passwd --pass 'your-strong-pass'- Point your MCP client to run the server command. Most clients let you configure a command, arguments, and environment variables. An example configuration looks like:
{
"command": "npx",
"args": ["-y", "@ideadesignmedia/gmail-mcp", "start", "--read-only"],
"env": { "GMAIL_MCP_DB_PASS": "your-strong-pass" }
}Exact configuration format depends on your MCP client. The server speaks stdio.
- Use the tools provided by the server from your MCP client (see "MCP tools").
CLI reference
Global options (apply to all commands unless stated otherwise):
--db <path>: SQLite DB path (default~/.idm/gmail-mcp/db.sqlite)--pass <pass>: Database password or new password (also available viaGMAIL_MCP_DB_PASS)--read-only: Start server with write operations disabled (only used bystart)
Commands:
# Start the MCP server over stdio
npx -y @ideadesignmedia/gmail-mcp start [--db <path>] [--pass <pass>] [--read-only]
# Link a Gmail account (loopback or device flow)
npx -y @ideadesignmedia/gmail-mcp add \
[--db <path>] [--pass <pass>] \
[--client-id <id>] [--client-secret <secret>] [--device] [--listen-port <port>]
# List linked accounts
npx -y @ideadesignmedia/gmail-mcp list [--db <path>] [--pass <pass>]
# Remove an account (by email or id)
npx -y @ideadesignmedia/gmail-mcp remove <email|id> [--db <path>] [--pass <pass>]
# Lock the DB, set password, or rotate existing password
npx -y @ideadesignmedia/gmail-mcp passwd [--db <path>] [--pass <new>] [--rotate] [--old-pass <old>] [--hint <text>]Behavioral details:
- When the DB is locked, all commands require a password (
--passorGMAIL_MCP_DB_PASS). - When unlocked, you can operate without a password and lock later with
passwd. passwd --rotate --old-pass <old> --pass <new>rotates the encryption KEK without re‑adding accounts.start --read-onlydisablesgmail-send_messageandgmail-label_messagetools and prevents modification APIs.
Google OAuth setup
Create a Web application OAuth client in Google Cloud Console and enable the Gmail API.
Loopback (default) flow requirements:
- Authorized redirect URI:
http://127.0.0.1:43112/oauth2/callback - The CLI prints the authorization URL; open it manually in your browser.
- Scopes requested by this tool:
https://www.googleapis.com/auth/gmail.readonlyhttps://www.googleapis.com/auth/gmail.modifyhttps://www.googleapis.com/auth/gmail.sendopenid,email,profile(used to fetch the account email/id on first link)
Device code flow (headless) requirements:
- No redirect URI needed. You will be shown a verification URL and user code to enter in a browser.
Tips:
- If you get no
refresh_token, ensureaccess_type=offlineandprompt=consentare set and you have not previously granted this client for the user. You can revoke previous grants at https://myaccount.google.com/permissions.
Environment variables
GMAIL_MCP_DB_PASS: Password for a locked DB (also accepted via--pass).GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET: Used byaddif flags are not provided.
MCP tools
The server exposes these tools. Tool names are prefixed with gmail.. Names and parameters are stable and validated with zod. Unless noted, responses mirror the Gmail REST API types.
gmail-list_accounts
- Input:
{} - Output:
{ accounts: Array<{ id: string, email: string, displayName: string | null }> }
gmail-search_mail
- Input:
{ query: string, account?: string, limit?: number } - Behavior:
- If the query contains only date operators (
after:,before:,older_than:,newer_than:) or is empty: results are sorted bytimestampdescending (most recent first). Across multiple accounts, they are merged and sorted together. - Otherwise (query includes free text or non-date operators): ordering matches Gmail’s native results per account; multi‑account queries preserve per‑account order without a global sort.
- If the query contains only date operators (
- Output: Enriched results to reduce follow‑ups:
{ messages: Array<{ accountId: string, accountEmail: string, messageId: string, threadId: string, subject: string | null, snippet: string, from: string | null, to: string | null, cc: string | null, bcc: string | null, timestamp: number, date: string, labels: Array<{ id: string, name: string }> }> }- Notes:
timestampis ms since epoch;dateis ISO8601 string. Usegmail-get_messagefor full decoded body and attachments. - Query operators: Supports standard Gmail search syntax, including common operators like
from:,to:,subject:,label:,in:,is:(e.g.is:unread,is:starred),has:(e.g.has:attachment), date filtersbefore:,after:, relativeolder_than:/newer_than:,cc:,bcc:,filename:, size filterslarger:/smaller:, booleanOR, and negation with-.
gmail-get_message
- Input:
{ account: string, messageId: string } - Output: A simplified, human-readable object:
{ id: string, thread: string, labels: Array<{ id: string, name: string }>, timestamp: number, subject: string | null, from: string | null, to: string | null, cc: string | null, bcc: string | null, body: string, bodyType: 'text' | 'html', attachments: Array<{ filename: string, mimeType: string, size: number, attachmentId: string }> }- Notes:
bodyis decoded (not base64url). Preferstext/plainwhen available, otherwise returnstext/html.timestampis ms since epoch.
gmail-get_thread
- Input:
{ account: string, threadId: string } - Output: A simplified thread with deduped messages ordered by time:
{ id: string, messages: Array<SimpleMessage> }, whereSimpleMessagematchesgmail-get_messageoutput.- Each message includes resolved label names and a
timestampfield.
gmail-download_attachment
- Input:
{ account: string, messageId: string, attachmentId: string } - Output: Gmail
MessagePartBodywith base64urldata. - Tip: Use
gmail-get_messagefirst to listattachmentsand obtain anattachmentId.
gmail-label_message
- Input:
{ account: string, messageId: string, add?: string[], remove?: string[] } - Writes: Adds/removes label IDs on a message. Disabled in
--read-onlymode. - Output: Gmail
Messageresource (post-modify).
gmail-send_message
- Input:
{ account: string, to: string, subject: string, text?: string, html?: string, replyToMessageId?: string } - Behavior: Builds a minimal RFC822 message (text or html) and sends via
users.messages.send. Attachments and custom headers are not yet supported.replyToMessageIdis currently accepted but not used to set threading headers. - Writes: Sends email on the selected account. Disabled in
--read-onlymode. - Output: Gmail
Messageresource for the sent message.
Account selection:
- For any tool,
accountaccepts either the account’s email or its internalid(seegmail-list_accounts).
Security model
- Stored tokens are encrypted-at-rest when the DB is locked with
passwd. - Losing the password means losing access to encrypted refresh tokens; there is no recovery.
- Anyone who can launch the server or CLI with the password can access all linked inboxes.
- No message bodies or tokens are logged.
Troubleshooting
- Node not recent enough: ensure Node >= 18.17 (
node -v). - No refresh token after OAuth: revoke prior grants and try again, ensuring
prompt=consentand first-time approval for this client. - Locked DB errors: provide
--passor setGMAIL_MCP_DB_PASSfor all commands, includinglistandremove. - Device flow polling errors: if you see
authorization_pendingorslow_down, just wait; the CLI automatically retries.
Optional: global install
You can install globally if you prefer shorter commands:
npm i -g @ideadesignmedia/gmail-mcp
gmail-mcp --helpAll examples above work the same without npx once installed globally.
