A Model Context Protocol (MCP) server that enables AI assistants to manage multiple Gmail accounts simultaneously with built-in OAuth authentication.
Unlike existing Gmail MCP servers, this implementation offers:
- Multi-Account Support: Manage multiple Gmail accounts from a single MCP server instance
- Intelligent Aggregation: Read and search across all accounts simultaneously
- Built-in OAuth Flow: Complete authentication setup through MCP tools - no manual token generation
- Type-Safe: Built with TypeScript for reliability and better IDE support
- Auto Token Refresh: Handles token expiration automatically and saves refreshed tokens
- Comprehensive API: Full Gmail API coverage including labels, threads, drafts, and more
- Features
- Prerequisites
- Installation
- Google Cloud Setup
- Configuration
- Railway / SSE Deployment
- OAuth Onboarding
- Usage Examples
- API Reference
- Troubleshooting
- Contributing
- License
list_accounts- View all configured accounts and their statusread_emails- Fetch recent emails (aggregates across all accounts by default)search_emails- Search using Gmail query syntax across multiple accountsget_email_thread- Retrieve complete conversation threadsget_labels- List all labels for an account
send_email- Send emails from any configured account, with optional local file attachmentscreate_draft- Create draft messages, with optional local file attachmentsdelete_drafts- Permanently delete one or more drafts by draft IDmark_as_read- Mark messages as readarchive_emails- Archive messages (remove from inbox)trash_emails- Move messages to trash
add_labels- Apply labels to messagesremove_labels- Remove labels from messagescreate_label- Create new custom labelsdelete_label- Delete existing labels
begin_account_auth- Start OAuth flow for new accountfinish_account_auth- Complete OAuth and save credentials
- Node.js 20+ (Download)
- A Google Cloud Project with Gmail API enabled
- OAuth 2.0 Credentials (Desktop application type)
# Clone the repository
git clone https://github.com/tszaks/gmail-multi-inbox-mcp.git
cd gmail-multi-inbox-mcp
# Install dependencies
npm install
# Build the TypeScript code
npm run buildBefore using this MCP server, you need to set up a Google Cloud project:
- Go to Google Cloud Console
- Create a new project or select an existing one
- Note your project ID
- In your project, go to APIs & Services > Library
- Search for "Gmail API"
- Click Enable
- Go to APIs & Services > Credentials
- Click + CREATE CREDENTIALS > OAuth client ID
- If prompted, configure the OAuth consent screen:
- Choose "External" user type
- Fill in required fields (app name, support email)
- Add your email to test users
- For application type, select Desktop app
- Give it a name (e.g., "Gmail MCP Client")
- Click Create
- Download the JSON file - you'll need this for authentication
- Go to APIs & Services > OAuth consent screen
- Add the following scopes:
https://www.googleapis.com/auth/gmail.modifyhttps://www.googleapis.com/auth/gmail.sendhttps://www.googleapis.com/auth/userinfo.email
- Add your Gmail address(es) as test users
Add this server to your .mcp.json configuration file (adjust path to match your installation):
{
"mcpServers": {
"gmail-multi": {
"command": "node",
"args": [
"/path/to/gmail-multi-inbox-mcp/dist/index.js"
]
}
}
}To use a custom directory for storing account data:
{
"mcpServers": {
"gmail-multi": {
"command": "node",
"args": [
"/path/to/gmail-multi-inbox-mcp/dist/index.js"
],
"env": {
"GMAILMCPCONFIG_DIR": "/custom/path/.gmail-multi-mcp"
}
}
}
}The server also accepts the legacy GMAIL_MCP_CONFIG_DIR env var.
Default configuration location: ~/.gmail-multi-mcp/
~/.gmail-multi-mcp/
├── accounts.json # Master account list
└── accounts/
├── personal/
│ ├── credentials.json # OAuth client credentials
│ ├── token.json # Access/refresh tokens
│ └── meta.json # Account metadata
└── work/
├── credentials.json
├── token.json
└── meta.json
{
"defaultAccount": "personal",
"accounts": [
{
"id": "personal",
"email": "user@gmail.com",
"displayName": "Personal Gmail",
"enabled": true,
"credentialPath": "~/.gmail-multi-mcp/accounts/personal/credentials.json",
"tokenPath": "~/.gmail-multi-mcp/accounts/personal/token.json"
},
{
"id": "work",
"email": "user@company.com",
"displayName": "Work Email",
"enabled": true,
"credentialPath": "~/.gmail-multi-mcp/accounts/work/credentials.json",
"tokenPath": "~/.gmail-multi-mcp/accounts/work/token.json"
}
]
}This server can run as either stdio MCP or an SSE-backed HTTP server.
- Set
MCP_TRANSPORT=sseto force HTTP/SSE mode. - If
PORTis present, the server automatically switches to SSE mode. - The SSE endpoint is served at
/sseand message posts are accepted at/messages. - Configuration still respects
GMAILMCPCONFIG_DIRandGMAIL_MCP_CONFIG_DIR.
For Railway, point the start command at npm start or node dist/index.js and let Railway provide PORT.
Authenticate accounts directly through MCP tools:
Call the begin_account_auth tool with your OAuth credentials:
{
"account_id": "personal",
"email": "user@gmail.com",
"credentials_json": {
"installed": {
"client_id": "YOUR_CLIENT_ID.apps.googleusercontent.com",
"client_secret": "YOUR_CLIENT_SECRET",
"redirect_uris": ["http://localhost"]
}
}
}Or use a file path:
{
"account_id": "personal",
"email": "user@gmail.com",
"credentials_path": "/path/to/credentials.json"
}The tool returns a Google OAuth URL. Open this URL in your browser.
- In your browser, sign in with the Gmail account
- Grant the requested permissions
- Google redirects you to a localhost URL with a
codeparameter - Copy the authorization code from the URL
Call finish_account_auth:
{
"account_id": "personal",
"authorization_code": "4/0AfJoh..."
}Your account is now authenticated and ready to use.
// Aggregates across all enabled accounts
{
"max_results": 20,
"include_body": true
}
// Returns emails with source account indicated{
"query": "from:boss@company.com is:unread",
"max_results": 10
}
// Searches all enabled accounts, merges and sorts results{
"account": "work",
"to": "colleague@company.com",
"subject": "Project Update",
"body": "Here's the latest on the project...",
"html": false
}{
"account": "personal",
"max_results": 10,
"query": "label:important"
}// Create a new label
{
"account": "personal",
"name": "Urgent-2026"
}
// Add label to messages
{
"account": "personal",
"message_ids": ["msg123", "msg456"],
"label_ids": ["Label_789"]
}// Delete one or more drafts by their draft IDs (returned by create_draft)
{
"account": "personal",
"draft_ids": ["r5457071851533655344", "r774137312565667821"]
}Returns all configured accounts with health status.
Parameters: None
Returns:
{
accounts: Array<{
id: string
email: string
displayName: string
enabled: boolean
hasValidToken: boolean
}>
}Fetch recent emails with optional filtering.
Parameters:
account(optional): Account ID to read from. Omit to aggregate all accounts.max_results(optional, default: 20): Number of emails to return (1-100)query(optional): Gmail search queryinclude_body(optional, default: false): Include plaintext body extraction
Returns: Array of email objects with metadata, headers, and optional body
Search emails using Gmail query syntax.
Parameters:
query(required): Gmail search queryaccount(optional): Account ID to search. Omit to search all accounts.max_results(optional, default: 25): Maximum results (1-100)
Returns: Array of matching emails
Retrieve a complete email thread.
Parameters:
account(required): Account IDthread_id(required): Gmail thread ID
Returns: Thread object with all messages
List all labels for an account.
Parameters:
account(required): Account ID
Returns: Array of label objects with IDs and names
Send an email from a specific account.
Parameters:
account(required): Account IDto(required): Recipient email address(es)subject(required): Email subjectbody(required): Email bodycc(optional): CC recipientsbcc(optional): BCC recipientshtml(optional, default: false): Send as HTMLattachments(optional): Array of local file attachments. Each item supports:path(required): Absolute or local filesystem pathfilename(optional): Override the filename shown in Gmailcontent_type(optional): Override the MIME type, for exampleapplication/pdf
Returns: Sent message details
Create a draft email.
Parameters: Same as send_email
Returns: Draft details including draft_id and thread_id
Permanently delete one or more drafts. Uses draft IDs as returned by create_draft. Note: draft IDs differ from message IDs and cannot be used with trash_emails.
Parameters:
account(required): Account IDdraft_ids(required): Array of draft IDs to delete
Returns: Count of deleted drafts
Mark messages as read.
Parameters:
account(required): Account IDmessage_ids(required): Array of message IDs
Archive messages (remove INBOX label).
Parameters:
account(required): Account IDmessage_ids(required): Array of message IDs
Move messages to trash.
Parameters:
account(required): Account IDmessage_ids(required): Array of message IDs
Add labels to messages.
Parameters:
account(required): Account IDmessage_ids(required): Array of message IDslabel_ids(required): Array of label IDs
Remove labels from messages.
Parameters:
account(required): Account IDmessage_ids(required): Array of message IDslabel_ids(required): Array of label IDs
Create a new Gmail label.
Parameters:
account(required): Account IDname(required): Label namelabel_list_visibility(optional, default: "labelShow")message_list_visibility(optional, default: "show")
Delete a Gmail label.
Parameters:
account(required): Account IDlabel_id(required): Label ID to delete
This usually means your authorization code has expired. Authorization codes are single-use and expire after a few minutes.
Solution: Run begin_account_auth again to get a fresh OAuth URL and authorization code.
Your refresh token is no longer valid.
Solution:
- Delete the
token.jsonfile for the affected account - Run the OAuth flow again (
begin_account_auththenfinish_account_auth)
The OAuth token doesn't have the required scopes.
Solution:
- Check your Google Cloud OAuth consent screen has all required scopes
- Re-run the OAuth flow to grant new permissions
- Required scopes:
https://www.googleapis.com/auth/gmail.modifyhttps://www.googleapis.com/auth/gmail.sendhttps://www.googleapis.com/auth/userinfo.email
The specified account ID doesn't exist in accounts.json.
Solution:
- Run
list_accountsto see available accounts - Ensure you completed OAuth onboarding for the account
- Check that the account is
enabled: trueinaccounts.json
Gmail API has rate limits (daily quota and per-user quotas).
Solution:
- Check your quota at Google Cloud Console
- Implement exponential backoff in your application
- Consider applying for increased quota if needed
Contributions are welcome. Here's how:
- Fork the repository
- Create a feature branch (
git checkout -b feature/your-feature) - Commit your changes (
git commit -m 'Add your feature') - Push to the branch (
git push origin feature/your-feature) - Open a Pull Request
# Watch mode for development
npm run dev
# Type checking
npm run typecheck
# Build for production
npm run build
# Run the server
npm run startThis project is licensed under the MIT License - see the LICENSE file for details.
- Built with the Model Context Protocol SDK
- Uses Google APIs Node.js Client
Built by Tyler Szakacs
npm install
npm run build
node dist/index.jsThen add the server to MCP config and onboard each account with begin_account_auth + finish_account_auth.
- Server stores account-level credentials/tokens in local config directory
- OAuth flow is handled via MCP tools
- Read/search can aggregate across all enabled accounts
- Gmail API calls execute per selected account
Use the copy button on this code block in GitHub.
Repo: gmail-multi-inbox-mcp
Goal: Multi-account Gmail MCP with built-in OAuth onboarding.
Setup:
1) npm install && npm run build
2) Add to MCP config
3) Run begin_account_auth + finish_account_auth for each inbox
Use:
- list_accounts to verify health
- read_emails/search_emails aggregated or per account
- send_email/create_draft/delete_drafts/label tools for write actions
How it works:
- Node MCP server maintains per-account token files and calls Gmail API