A side-by-side look at the Calendar MCP and Google Workspace MCP MCP servers: transport, auth, tools, quality signals, and the config to install either one in Claude, Cursor, Windsurf or VS Code.
Verdict: choose Calendar MCP for workplace & productivity work as a local process; choose Google Workspace MCP for workplace & productivity as a local process. You can also run both side by side in one mcpServers block.
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET.list_accounts, get_preferences, set_preferences.start_google_auth, search_gmail_messages, get_gmail_message_content.Rows where the two servers differ are marked.
| Spec | ||
|---|---|---|
| Category | Workplace & Productivity | Workplace & Productivity |
| Quality signal | 61/100 · Good | 67/100 · Great |
| Install command | Confirmed | Confirmed |
| Transport | Local subprocess (stdio) | Local subprocess (stdio) |
| Auth | Not declared | OAuth 2.0 |
| Pricing | Not declared | Free |
| Required env vars | GOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRET | None listed |
| Verified owner | Community listing | Community listing |
| GitHub stars | 26 | 3,339 |
| Directory activity | 1 views 0 installs 0 upvotes | 1 views 0 installs 0 upvotes |
| Tools | 23 | 123 |
| Full listing | View Calendar MCP | View Google Workspace MCP |
list_accountsList the Google accounts calendar-mcp can use, and which is the default.
Every other tool takes an optional `account` argument naming one of these.
An account whose `valid` is false needs `calendar-mcp auth --account <name>`
run once in a terminal before its calendars can be read.
get_preferencesRead the user's scheduling preferences: working hours, lunch, buffers, focus.
Consult this before proposing meeting times or hunting for focus blocks,
rather than assuming a nine-to-five. Never configured returns the defaults
(Mon-Fri 09:00-17:00, no lunch break, no buffer), which is a reasonable
guess but worth confirming with the user before you rely on it.
set_preferencesUpdate the user's scheduling preferences and save them. Returns the merged result.
Only the arguments you pass change; everything else keeps its current value,
so a single correction ("I actually start at 8") does not have to restate
the whole schedule. The merged result is validated before it is written, so
a rejected change leaves the saved preferences untouched.
Args:
timezone: IANA timezone the working hours are expressed in, e.g.
'Europe/Berlin'. Must be a real zone name.
working_hours: Whole-schedule replacement, keyed by weekday
('mon'..'sun'), each value a list of ['HH:MM', 'HH:MM'] pairs. Days
you omit become non-working, so pass every working day at once, e.g.
{"mon": [["09:00", "12:00"], ["13:00", "18:00"]], "fri": [["09:00", "15:00"]]}.
buffer_minutes: Minimum gap to leave before and after each meeting when
proposing times. 0 disables it.
min_focus_block_minutes: Shortest free stretch that still counts as
usable focus time.
lunch: Daily lunch break as ['HH:MM', 'HH:MM'], carved out of the
working hours.
focus_calendar_id: Calendar that focus blocks are booked on; 'primary'
for the user's own.
clear_lunch: Set true to remove an existing lunch break (pass this
instead of `lunch` when the user says they no longer want one).
analyze_busynessSummarise how loaded each day is: event count and total scheduled minutes.
Use this to answer "how busy is my week" without listing every event.
Args:
time_min: Start of the window, ISO 8601.
time_max: End of the window, ISO 8601.
calendar_id: Calendar to analyse.
account: Account name from 'calendar-mcp accounts'; omit for the default.
project_recurring_eventsCompute future occurrences of recurring events from their recurrence rules.
Unlike `find_events`, this expands the RRULEs locally, so it reaches past the
horizon Google materialises instances for -- useful for "when do my birthdays
/ standups land next year".
Args:
time_min: Start of the projection window, ISO 8601.
time_max: End of the projection window, ISO 8601.
calendar_id: Calendar whose recurring events should be projected.
event_query: Only project recurring events matching this text, e.g. 'Birthday'.
account: Account name from 'calendar-mcp accounts'; omit for the default.
time_auditReport where the user's time went: meeting hours, who with, and what focus time is left.
Answers "how much of my week is meetings?", "who am I spending my time
with?" and "where did my focus time go?" in one pass. Working hours, lunch,
buffer and the minimum focus block come from the saved preferences
(see get_preferences), so the percentages reflect this user's actual day.
Declined meetings, events marked 'free', and all-day entries are excluded by
default; the 'excluded' field says how many were skipped.
Args:
time_min: Start of the window to audit, ISO 8601.
time_max: End of the window to audit, ISO 8601.
calendar_ids: Calendars to include. Defaults to ['primary'].
group_by: Break the window down by 'day' or by ISO 'week'.
include_all_day: Count all-day events as meeting time.
include_declined: Count meetings the user declined.
account: Account name from 'calendar-mcp accounts'; omit for the default.
list_calendarsList the calendars the signed-in user can see, with their IDs and timezones.
Start here whenever the user mentions a calendar other than their own: the
`id` values returned are what every other tool's `calendar_id` expects.
Args:
min_access_role: Only return calendars where the user has at least this
role: 'freeBusyReader', 'reader', 'writer' or 'owner'. Omit for all.
account: Account name from 'calendar-mcp accounts'; omit for the default.
create_calendarCreate a new secondary calendar owned by the user.
Args:
summary: Name for the new calendar, e.g. 'Client work'.
account: Account name from 'calendar-mcp accounts'; omit for the default.
find_focus_timeFind the uninterrupted blocks in a window that could be used for deep work.
Searches inside the user's working hours (`get_preferences`), removes lunch,
everything already booked on their calendars, and the buffer they like
around meetings, then reports what is left -- longest block first, because
that is the one worth protecting. Blocks shorter than
`min_focus_block_minutes` are not reported at all.
Availability must be readable for every requested calendar; otherwise this
returns an error rather than unverified free time.
Read-only: use `block_focus_time` to actually defend the time.
Args:
time_min: Start of the window to search, ISO 8601. Without a UTC offset
it is read in the calendar's own timezone.
time_max: End of the window to search, ISO 8601.
hours_needed: How many focus hours the user is trying to find. Used to
report whether the window can supply them; pass 0 to just look.
calendar_ids: Calendars whose events count as busy. Omit for every
calendar the account has selected.
account: Account name from 'calendar-mcp accounts'; omit for the default.
block_focus_timeBook the best free blocks in a window as focus time, until the hours add up.
Takes the blocks `find_focus_time` would report, longest first, and creates
an event on each until `hours_needed` is covered. The last block is trimmed
to what is still needed rather than swallowing a whole afternoon. Events are
created busy, with reminders off and without notifying anyone.
Run it with `dry_run` first when the user has not yet agreed to the times.
Both preview and booking fail before writing if any checked calendar has
unknown availability. The destination calendar is always checked too.
Args:
time_min: Start of the window to book inside, ISO 8601.
time_max: End of the window, ISO 8601.
hours_needed: How many focus hours to book.
title: Title for the blocks. Default 'Focus time'.
calendar_id: Calendar to book on. Omit for the user's configured
focus_calendar_id (see `get_preferences`).
check_calendar_ids: Calendars whose events count as busy. Omit for every
calendar the account has selected.
description: Optional note to put in each block.
dry_run: True to report the blocks that would be booked without writing
anything to the calendar.
account: Account name from 'calendar-mcp accounts'; omit for the default.
detect_conflictsFind double-booked events in a window, across every signed-in account.
Reports two things. `conflicts` are genuine overlaps -- the user cannot be
in both places. `tight` transitions do not overlap but leave less than the
buffer from `get_preferences`, which is what makes a day feel impossible.
Events the user declined, events marked free, cancelled events and (unless
`include_all_day` is set) all-day events are ignored, because none of them
actually occupy the user.
Args:
time_min: Start of the window to check, ISO 8601.
time_max: End of the window to check, ISO 8601.
accounts: Account names to check. Omit for every signed-in account,
which is the point of the tool -- a work meeting clashing with a
personal one is invisible from either calendar alone.
calendar_ids: Restrict to these calendars (in every account checked).
Omit for each account's selected calendars.
include_all_day: True to let all-day events clash with timed ones.
max_events_per_calendar: Safety limit per calendar. Default 250.
suggest_reschedulePropose better times for an existing meeting, ranked, keeping its length.
Reads the event, then looks for slots of the same duration inside the user's
working hours where the organiser is free, and ranks them: fewest attendee
conflicts first, the event's current day next, then earliest. Slots that sit
closer to another meeting than the user's buffer allows are penalised, not
hidden.
Read-only by default -- it suggests, the user decides. Set `apply` to move
the event to the top suggestion once they have agreed to it.
Args:
event_id: The event to reschedule (from `find_events`).
calendar_id: Calendar the event lives on.
search_days: How many days ahead to search. Default 7, maximum 60.
max_suggestions: How many times to propose. Default 5, maximum 20.
search_from: Start searching from this time, ISO 8601. Defaults to now.
apply: True to actually move the event to the best suggestion. Only set
this when the user has agreed to the time.
account: Account name from 'calendar-mcp accounts'; omit for the default.
start_google_authManually initiate Google OAuth authentication flow.
NOTE: This is a legacy OAuth 2.0 tool and is disabled when OAuth 2.1 is enabled.
The authentication system automatically handles credential checks and prompts for
authentication when needed. Only use this tool if:
1. You need to re-authenticate with different credentials
2. You want to proactively authenticate before using other tools
3. The automatic authentication flow failed and you need to retry
In most cases, simply try calling the Google Workspace tool you need - it will
automatically handle authentication if required.search_gmail_messagesSearches messages in a user's Gmail account based on a query.
Returns both Message IDs and Thread IDs for each found message, along with Gmail web interface links for manual verification.
Supports pagination via page_token parameter.get_gmail_message_contentRetrieves the full content (subject, sender, recipients, body) of a specific Gmail message.
Bodies are returned inline and truncated at 20,000 characters. Set full=True to
get the complete, untruncated message instead: it is exported to disk and the
response carries a short-lived download URL (HTTP transport) or file path (stdio
transport) rather than the body, so large messages never stream through the model
context. Stateless deployments have no file storage, so there full=True returns the
untruncated body inline.get_gmail_messages_content_batchRetrieves the content of multiple Gmail messages in a single batch request.
Supports up to 25 messages per batch to prevent SSL connection exhaustion.get_gmail_attachment_contentDownloads an email attachment and saves it to local disk.
In stdio mode, returns the local file path for direct access.
In HTTP mode, returns a temporary download URL (valid for 1 hour).
May re-fetch message metadata to resolve filename and MIME type.send_gmail_messageSends an email using the user's Gmail account. Supports new emails, replies, and
forwards, with optional attachments. Supports Gmail's "Send As" feature to send
from configured alias addresses.
To forward an existing message, pass forward_message_id. The original subject,
body (quoted with a "Forwarded message" header), and attachments are carried over.
In forward mode, body (if any) is prepended as a note and subject is optional.
Threading, reply, and signature options do not apply when forwarding.
THIS TOOL SENDS IMMEDIATELY AND CANNOT SCHEDULE. Gmail's REST API exposes no
send-time parameter; Schedule send is a web-UI feature with no API equivalent,
so no argument to this tool can defer delivery. Never tell a user a message
was scheduled. For "prepare now, deliver later", create a draft with
draft_gmail_message. An external scheduler must retain the message data to
create and send a new message via send_gmail_message at the chosen time, or
call users.drafts.send with the draft ID returned by draft_gmail_message.
Alternatively, let the user schedule that draft in the Gmail UI.Paste either block (or both) into your client config, e.g. claude_desktop_config.json or ~/.cursor/mcp.json. We only show a config when the install command is confirmed.
{
"mcpServers": {
"calendar-mcp-2": {
"command": "uvx",
"args": [
"calendar-mcp-server"
],
"env": {
"GOOGLE_CLIENT_ID": "YOUR_GOOGLE_CLIENT_ID_HERE",
"GOOGLE_CLIENT_SECRET": "YOUR_GOOGLE_CLIENT_SECRET_HERE"
}
}
}
}
{
"mcpServers": {
"taylorwilsdon-google-workspace-mcp": {
"command": "uvx",
"args": [
"workspace-mcp"
]
}
}
}
draft_gmail_messageCreates a draft email in the user's Gmail account. Supports both new drafts and reply drafts with optional attachments.
Supports Gmail's "Send As" feature to draft from configured alias addresses.
SCHEDULED SEND IS NOT AVAILABLE. Gmail's REST API exposes no send-time
parameter; the Schedule send feature is web-UI only, and a message cannot be
placed in the Scheduled folder through the API. Do not claim a message was
scheduled. To deliver at a chosen time, create a draft with
draft_gmail_message. An external scheduler must retain the message data to
create and send a new message via send_gmail_message then, or call
users.drafts.send with the draft ID returned by draft_gmail_message.
Alternatively, let the user schedule the draft in the Gmail UI.get_gmail_thread_contentRetrieves the complete content of a Gmail conversation thread, including all messages.
Optionally also returns structured ownership analysis so a caller can
determine who sent the last message and who owes whom a response without
re-parsing the formatted string or making a second tool call.get_gmail_threads_content_batchRetrieves the content of multiple Gmail threads in a single batch request.
Supports up to 25 threads per batch to prevent SSL connection exhaustion.list_gmail_labelsLists labels in the user's Gmail account.manage_gmail_labelManages Gmail labels: create, update, or delete labels.list_gmail_filtersLists all Gmail filters configured in the user's mailbox.