Gmail & Google Calendar MCP Server
Manage Gmail messages, threads, drafts, and labels; send, reply, forward, archive, delete, and filter emails.
Interact with Google Calendar: list, create, update, delete events; check free/busy; respond to invitations; add conference details.
Create and edit Google Docs: insert text, images, tables; replace text; perform batch updates; export to other formats.
Manage files and folders on Google Drive: list, upload, download, share, trash, restore, copy, and create shortcuts.
Work with Google Sheets: read, update, append values; add, delete, rename sheets; clear data; batch update cells.
Manage Google Tasks: list task lists and tasks; create, update, complete, move, and delete tasks.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Gmail & Google Calendar MCP Servershow my calendar and inbox for today"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-google-workspace — Google Workspace CLI + MCP
Status: beta
gwcli is a CLI-first Google Workspace tool for local automation. It exposes Gmail, Google Calendar, Google Drive, Google Docs, and Google Sheets workflows from the terminal, with the existing MCP server available as mcp-google-workspace for assistant clients.
The direction is deliberate: the CLI is the primary product surface; MCP is a compatibility adapter.
What it includes today
gwcliCLI binary for account, config, Gmail, Calendar, Drive, Docs, Sheets, and local diagnosticsmcp-google-workspacebinary preserving the existing stdio MCP server (gws-mcpkept as a legacy alias)OAuth2 for personal Gmail and Google Workspace accounts
Multi-account state via
~/.config/gws/state.jsonJSON-first output for scripts, plus table/jsonl/tsv formats
Gmail list/search/read/send/reply/forward/delete/archive/labels/drafts/threads/attachments/read-status commands
Calendar list/upcoming/search/freebusy/create/update/delete/respond/quickadd/conference/instances commands, plus secondary calendar create/delete
Drive list/get/upload/download/mkdir/share/trash/restore/copy/batch-delete/revisions/shared-drives/shortcut commands (full CLI↔MCP parity)
Docs get/export/create plus insert-text/replace-text/insert-table/insert-image/batch-update commands (CLI↔MCP parity via
documents.batchUpdate)Sheets get/values (get/update/append)/add-sheet/delete-sheet/rename-sheet/clear/batch-update commands
Tasks task-list and task list/get/create/update/complete/move/delete commands
Contacts (People) list/search/get/create/update/delete and contact-group list/get commands
Existing MCP toolset for Gmail, Calendar, attachments, templates, and Sheets
Related MCP server: MCP Google Workspace Server
Migrating from gws (2.0.0)
Version 2.0.0 renames the binaries. The old gws name collided with Google's official Workspace CLI.
Before | Now |
|
|
|
|
|
|
The config directory (~/.config/gws) and the GWS_* environment variables are unchanged, so existing accounts and tokens keep working. Run npm link again from the new checkout to install gwcli.
Install
git clone https://github.com/ftaricano/mcp-google-workspace.git
cd mcp-google-workspace
npm install
npm run build
npm linkPrerequisites:
Node.js 20+
Google Cloud OAuth credentials
Google APIs enabled for the surfaces you use:
Gmail API
Google Calendar API
Google Drive API
Google Docs API
Google Sheets API
Google Tasks API
Google People API
If you already authenticated before Drive/Docs/Sheets support existed, run gwcli auth login --account you@example.com --type workspace again so Google grants the expanded OAuth scopes.
⚠️ Tasks requires re-consent. Google Tasks adds a brand-new OAuth scope (
https://www.googleapis.com/auth/tasks). Any account authenticated before Tasks support existed must re-rungwcli auth login --account you@example.com --type workspaceto grant it. Without re-consent, every Tasks call returns HTTP 403.gwcli auth logindetects when a stored account is missing a current scope and re-triggers the Google consent screen automatically (it is no longer a no-op for already-known accounts).
⚠️ Contacts requires re-consent. Google People / Contacts adds a brand-new OAuth scope (
https://www.googleapis.com/auth/contacts). Any account authenticated before Contacts support existed must re-rungwcli auth login --account you@example.com --type workspaceto grant it. Without re-consent, every Contacts call returns HTTP 403. As with Tasks,gwcli auth loginauto-detects the missing scope and re-triggers the Google consent screen.
Configure environment:
cp .env.example .envSet at least:
GOOGLE_CREDENTIALS_PATH=/absolute/path/to/credentials.json
TOKENS_PATH=/absolute/path/to/tokens
OAUTH_CALLBACK_PORT=3000
LOG_LEVEL=infoCLI quickstart
Authenticate:
gwcli auth login --account you@example.com --type workspaceList accounts:
gwcli auth listSet the default account:
gwcli auth switch you@example.comShow current account:
gwcli auth currentInspect CLI paths:
gwcli config pathSet config values:
gwcli config set timezone America/Sao_Paulo
gwcli config listRun local diagnostics without printing secrets:
gwcli doctor --format jsonGmail examples
List recent mail:
gwcli mail list --query "is:unread" --limit 10Read and search:
gwcli mail read MESSAGE_ID
gwcli mail search "from:client@example.com has:attachment" --limit 20Send, reply, forward:
gwcli mail send --to client@example.com --subject "Proposal" --body "Attached." --attachment ./proposal.pdf
gwcli mail reply MESSAGE_ID --body "Recebido, obrigado."
gwcli mail forward MESSAGE_ID --to teammate@example.com --body "Please review."Archive and delete:
gwcli mail archive MESSAGE_ID
gwcli mail delete MESSAGE_ID # move to trashDrafts:
gwcli mail drafts list --query "is:draft" --limit 10
gwcli mail drafts get DRAFT_ID
gwcli mail drafts create --to client@example.com --subject "Proposal" --body "Draft body"
gwcli mail drafts send DRAFT_ID
gwcli mail drafts delete DRAFT_IDThreads:
gwcli mail threads list --query "from:client@example.com" --label INBOX --limit 10
gwcli mail threads get THREAD_ID
gwcli mail threads modify THREAD_ID --add-label LABEL_ID --remove-label INBOX
gwcli mail threads trash THREAD_IDDry-run destructive or mutating actions:
gwcli --dry-run mail delete MESSAGE_ID
gwcli --dry-run mail archive MESSAGE_ID
gwcli --dry-run mail drafts create --to client@example.com --subject "Proposal" --body "Draft body"
gwcli --dry-run mail drafts send DRAFT_ID
gwcli --dry-run mail threads modify THREAD_ID --add-label LABEL_ID
gwcli --dry-run mail mark-read MESSAGE_IDLabels and attachments:
gwcli mail labels
gwcli mail labels create "Clients"
gwcli mail labels add MESSAGE_ID LABEL_ID
gwcli mail labels remove MESSAGE_ID LABEL_ID
gwcli mail attachments list MESSAGE_ID
gwcli mail attachments download MESSAGE_ID ATTACHMENT_ID --output ./downloads/file.pdfLegacy aliases remain available:
gwcli mail attachments-list MESSAGE_ID
gwcli mail attachment-download MESSAGE_ID ATTACHMENT_ID --output ./downloads/file.pdfCalendar examples
List calendars and events:
gwcli cal calendars
gwcli cal events upcoming --days 7 --limit 10
gwcli cal events list --from 2026-05-01T00:00:00-03:00 --to 2026-05-08T00:00:00-03:00
gwcli cal events search "planning" --limit 5Secondary calendars (create is mutating, delete is destructive — both support --dry-run):
gwcli --dry-run cal calendars create --summary "Project X" --description "Tracking" --timezone America/Sao_Paulo
gwcli cal calendars create --summary "Project X"
gwcli --dry-run cal calendars delete CALENDAR_IDRecurring event occurrences (read-only):
gwcli cal events instances RECURRING_EVENT_ID --from 2026-05-01T00:00:00-03:00 --to 2026-06-01T00:00:00-03:00 --limit 20Each instance returned by cal events instances has its own id. To edit or delete a single occurrence, pass that instance id to the regular update/delete commands — the Calendar API treats each instance as an independent event:
gwcli cal events update INSTANCE_ID --summary "Moved this one only"
gwcli cal events delete INSTANCE_IDCreate and update events:
gwcli cal events create --summary "Client call" --start 2026-05-03T10:00:00-03:00 --end 2026-05-03T11:00:00-03:00 --attendee client@example.com --meet
gwcli cal events update EVENT_ID --summary "Updated title" --send-notificationsFree/busy, invitation response, quick add, Meet conference:
gwcli cal freebusy --from 2026-05-01T00:00:00-03:00 --to 2026-05-02T00:00:00-03:00 --calendar primary
gwcli --dry-run cal events respond EVENT_ID --response accepted --comment "Confirmado"
gwcli cal events quickadd "Lunch with Ana tomorrow noon"
gwcli cal events conference EVENT_ID --type hangoutsMeetDrive examples
gwcli drive list --query "mimeType != 'application/vnd.google-apps.folder'" --limit 20
gwcli drive get FILE_ID
gwcli --dry-run drive upload --path ./proposal.pdf --name "Proposal.pdf" --parent FOLDER_ID
gwcli drive download FILE_ID --output ./downloads/proposal.pdf
gwcli drive mkdir "Client Docs" --parent PARENT_FOLDER_ID
gwcli --dry-run drive share FILE_ID --role reader --type user --email client@example.com
gwcli --dry-run drive trash FILE_ID
gwcli --dry-run drive restore FILE_ID
gwcli --dry-run drive copy FILE_ID --name "Copy.pdf" --parent FOLDER_ID
gwcli --dry-run drive batch-delete FILE_ID_1 FILE_ID_2 FILE_ID_3
gwcli --dry-run drive shortcut TARGET_FILE_ID --name "Shortcut" --parent FOLDER_ID
gwcli drive revisions FILE_ID
gwcli drive shared-drives --limit 20Allowed Drive share roles: reader, commenter, writer.
Allowed Drive share types: user, group, domain, anyone.
drive trash, drive restore, drive copy, drive batch-delete, and drive shortcut are mutating and support --dry-run. For safety, drive trash and drive batch-delete move files to the Drive trash (recoverable) rather than deleting permanently; batch-delete continues past per-file errors and reports a per-id status. drive revisions and drive shared-drives are read-only.
Docs examples
gwcli docs get DOCUMENT_ID
gwcli docs export DOCUMENT_ID --mime-type pdf --output ./doc.pdf
gwcli docs create --title "Meeting Notes" --content "Initial notes"
gwcli --dry-run docs insert-text DOCUMENT_ID --text "Appended line" --index 1
gwcli --dry-run docs replace-text DOCUMENT_ID --find "{{name}}" --replace "Ada" --match-case
gwcli --dry-run docs insert-table DOCUMENT_ID --rows 3 --columns 2 --index 1
gwcli --dry-run docs insert-image DOCUMENT_ID --uri https://example.com/logo.png --index 1
gwcli --dry-run docs batch-update DOCUMENT_ID --requests '[{"insertText":{"location":{"index":1},"text":"raw"}}]'Export MIME aliases include pdf, docx, txt, and html.
The edit commands (insert-text, replace-text, insert-table, insert-image, batch-update)
are mutating and support --dry-run, returning a { "dryRun": true, "would": ... } envelope. They
are backed by Google Docs documents.batchUpdate; batch-update accepts a raw request array for
operations not covered by the typed helpers (text styling, paragraph formatting, etc.).
Sheets examples
gwcli sheets get SPREADSHEET_ID
gwcli sheets values SPREADSHEET_ID --range "Sheet1!A1:C10"
gwcli --dry-run sheets update SPREADSHEET_ID --range "Sheet1!A1:B2" --values '[["a","b"],["c","d"]]' --value-input-option USER_ENTERED
gwcli sheets append SPREADSHEET_ID --range "Sheet1!A:B" --values '[["new","row"]]'
gwcli --dry-run sheets add-sheet SPREADSHEET_ID --title "Q3" --rows 200 --columns 12
gwcli --dry-run sheets rename-sheet SPREADSHEET_ID --sheet-id 0 --title "Summary"
gwcli --dry-run sheets delete-sheet SPREADSHEET_ID --sheet-id 123456
gwcli --dry-run sheets clear SPREADSHEET_ID --range "Sheet1!A1:Z100"
gwcli --dry-run sheets batch-update SPREADSHEET_ID --requests '[{"repeatCell":{"range":{"sheetId":0},"cell":{"userEnteredFormat":{"textFormat":{"bold":true}}},"fields":"userEnteredFormat.textFormat.bold"}}]'Allowed value input options: RAW, USER_ENTERED.
Formulas are written through the regular values update/values append commands with
--value-input-option USER_ENTERED, which tells Sheets to parse formulas and numbers
instead of storing literal strings. add-sheet, delete-sheet, rename-sheet, clear,
and batch-update are mutating and support --dry-run, returning a
{ "dryRun": true, "would": ... } envelope. batch-update takes a raw JSON array of
Sheets API Request objects (formatting, data validation, conditional formatting, etc.);
malformed JSON fails with a clear requests must be valid JSON validation error.
Tasks examples
Tasks needs the
tasksOAuth scope. If you authenticated before Tasks support existed, re-rungwcli auth login --account you@example.com --type workspacefirst, otherwise calls return 403.
gwcli tasks lists list
gwcli tasks lists get LIST_ID
gwcli --dry-run tasks lists create --title "Groceries"
gwcli --dry-run tasks lists update LIST_ID --title "Renamed"
gwcli --dry-run tasks lists delete LIST_ID
gwcli tasks list LIST_ID --show-completed --limit 50
gwcli tasks get LIST_ID TASK_ID
gwcli --dry-run tasks create LIST_ID --title "Buy milk" --notes "whole" --due 2026-06-20T00:00:00Z
gwcli --dry-run tasks update LIST_ID TASK_ID --title "New title" --status needsAction
gwcli --dry-run tasks complete LIST_ID TASK_ID
gwcli --dry-run tasks move LIST_ID TASK_ID --parent PARENT_ID --previous SIBLING_ID
gwcli --dry-run tasks delete LIST_ID TASK_IDAllowed task status values: needsAction, completed.
Contacts examples
Contacts needs the
contactsOAuth scope. If you authenticated before Contacts support existed, re-rungwcli auth login --account you@example.com --type workspacefirst, otherwise calls return 403.
gwcli contacts list --page-size 50
gwcli contacts search "Ada"
gwcli contacts get people/c123
gwcli --dry-run contacts create --json '{"names":[{"givenName":"Ada","familyName":"Lovelace"}],"emailAddresses":[{"value":"ada@example.com"}]}'
gwcli --dry-run contacts update people/c123 --json '{"names":[{"givenName":"Grace"}]}' --fields names
gwcli --dry-run contacts delete people/c123
gwcli contacts groups list
gwcli contacts groups get contactGroups/abcThe --json payload is a People API Person resource. contacts update resolves the required etag automatically (it reuses etag from the payload when present, otherwise fetches the current one before patching).
Output formats
Default output is JSON and stdout is kept data-only for successful commands.
gwcli mail list --format json
gwcli mail list --format table
gwcli mail list --format jsonl
gwcli mail list --format tsvyaml is reserved but not bundled yet.
Account resolution
Commands resolve the active account in this order:
--account you@example.comGWS_ACCOUNTenvironment variable~/.config/gws/state.jsoncurrent accountfirst authenticated account
auth error
Safety and automation conventions
Mutating commands support
--dry-runwhere practical and return{ "dryRun": true, "would": ... }.CLI output redacts token/secret-like values in error details.
Attachment downloads preserve the existing account sandbox protections.
Tests use injected fake services and do not require real Google credentials.
MCP compatibility
The MCP server is available as mcp-google-workspace (the gws-mcp binary remains as a legacy alias) and preserves the current tool names.
Example MCP config:
{
"mcpServers": {
"google-workspace": {
"command": "mcp-google-workspace",
"env": {
"GOOGLE_CREDENTIALS_PATH": "/absolute/path/to/credentials.json",
"TOKENS_PATH": "/absolute/path/to/tokens",
"OAUTH_CALLBACK_PORT": "3000"
}
}
}
}Existing direct path usage also works:
node /absolute/path/to/mcp-google-workspace/dist/index.jsMCP tool groups
Account:
authenticate,list_accounts,switch_account,remove_account,get_current_accountGmail:
email_list,email_read,email_send,email_reply,email_forward,email_delete,email_mark_read,email_mark_unread,email_search, labels, batch operationsAttachments:
email_list_attachments,email_download_attachmentCalendar:
calendar_list,event_list,event_get,event_create,event_update,event_delete, availability, invitation response, quick add, upcomingDrive:
drive_list,drive_get,drive_upload,drive_download,drive_mkdir,drive_share,drive_trash,drive_restore,drive_copy,drive_batch_delete,drive_revisions,drive_shared_drives,drive_shortcutTemplates: list, render, create
Docs:
docs_get,docs_create,docs_export,docs_batch_update,docs_insert_text,docs_replace_text,docs_insert_table,docs_insert_imageSheets:
sheets_get,sheets_values_get,sheets_values_update,sheets_values_append,sheets_batch_update,sheets_add_sheet,sheets_delete_sheet,sheets_rename_sheet,sheets_clear
Docs has full CLI↔MCP parity: every gwcli docs <command> maps to a docs_* MCP tool with the same
underlying service method. Mutating MCP tools validate arguments with zod and reject malformed input
with InvalidParams; the CLI exposes the same operations behind --dry-run.
Drive now has CLI↔MCP parity: every gwcli drive command has a matching drive_* MCP tool. As in the CLI, drive_trash and drive_batch_delete move files to the trash (recoverable) instead of deleting permanently.
The Sheets surface is at parity between the CLI (gwcli sheets ...) and MCP (sheets_*
tools): both cover values get/update/append, structural mutations (add/delete/rename
sheet), range clears, and raw batchUpdate requests. Use --value-input-option USER_ENTERED (CLI) or valueInputOption: "USER_ENTERED" (MCP) to write formulas.
Tasks:
tasks_lists_list,tasks_lists_get,tasks_lists_create,tasks_lists_update,tasks_lists_delete,tasks_list,tasks_get,tasks_create,tasks_update,tasks_complete,tasks_move,tasks_deleteContacts (People):
people_contacts_list,people_contacts_search,people_contacts_get,people_contacts_create,people_contacts_update,people_contacts_delete,people_groups_list,people_groups_get
Development
npm run cli -- --help
npm run build
npm test
npm run lint
npm pack --dry-run --jsonCI runs lint, build, and tests on Node 20 and 22.
Roadmap
Split the large CLI program into command modules as the command surface grows
Expand People/Contacts support
Add incremental OAuth scopes per Workspace surface
Add richer Drive/Docs/Sheets operations after real-world CLI usage
Security
See SECURITY.md. Tokens stay local and are never printed by CLI account commands.
This server cannot be deployed
Maintenance
Related MCP Connectors
Streamable HTTP MCP server for Google Calendar and Sheets with OAuth login.
Permissioned access to Gmail, Drive and Calendar via the user's own Google account
MCP server for Nylas — read email, calendars, events and contacts, and send email or create events.
Hosted MCP server with managed OAuth for 15+ toolkits: Google Workspace, Fitbit, Oura, Kalshi, etc.
Related MCP Servers
- AlicenseAqualityDmaintenanceProvides tools for interacting with Gmail and Calendar APIs. This server enables you to manage your emails and calendar events programmatically through the MCP interface.831MIT
- AlicenseNot gradedqualityDmaintenanceEnables interaction with Gmail and Google Calendar using the MCP protocol, supporting multiple Google accounts, email management, and calendar operations through natural language.30 npm32MIT
- AlicenseNot gradedqualityDmaintenanceMCP server to interact with Google Gmail and Calendar APIs. Supports multiple accounts, email search and drafting, and calendar event management.MIT
- AlicenseNot gradedqualityCmaintenanceMCP server that connects Gmail and Google Calendar to AI assistants, enabling email search, reading, sending, and calendar management across multiple accounts with secure OAuth.1MIT