gws-mcp-serverSAFE
Curated MCP server for Google Workspace: Gmail, Calendar, Drive, Sheets, Docs, and Tasks via the official gws CLI. 67 focused tools instead of 400, side effects declared on every one. The permissions leg of trust infrastructure for agents.
Overview
From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.
Maintenance mode. This project is stable and receives security fixes. New features aren't planned, but issues and pull requests are still welcome.
Google Workspace for AI agents: Gmail, Calendar, Drive, Sheets, Docs, Slides, Tasks, and Contacts as a curated set of 67 Model Context Protocol tools, built on the official Google Workspace CLI (`gws`).
[](https://www.npmjs.com/package/gws-mcp-server) [](LICENSE) [](https://nodejs.org/) [](https://chainofthought.show/?utmsource=github&utmmedium=referral&utmcampaign=repo-readme&utmcontent=gws-mcp-server) [](https://x.com/ConorBronsdon)
Why?
The gws CLI had a built-in MCP server that was removed in v0.8.0 because it exposed 200-400 tools — causing context window bloat in MCP clients. This server takes a curated approach: you choose which Google services to expose, and only a focused set of high-value, narrowly scoped operations are registered as tools. Every tool declares all four MCP annotation hints — readOnlyHint, destructiveHint, idempotentHint, openWorldHint — so clients can reason about side effects, know which writes are safe to retry, and surface clearer consent prompts. This is the permissions leg of trust infrastructure for agents: a deliberately narrow t
4be33605caccOBSERVED · 2026-10-10Connect
Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control.
claude mcp add gws-mcp-server -- npx -y [email protected]
Exposed tools (157)
112 read · 32 write · 13 destructive. Blast radius: 13 tools can delete or overwrite — an agent that can be talked into calling a tool can be talked into calling this one.
| Tool | Risk | Description |
|---|---|---|
action | write | Set to change the parent comment |
addLabelIds | write | JSON array of label IDs to add, e.g. [\ |
anchor | read | region |
attendees | read | Attendees (JSON array as string, e.g. |
calendarId | read | calendar |
calendar_events_delete | destructive | Delete a calendar event. |
calendar_events_get | read | Get a single calendar event by ID. |
calendar_events_insert | write | test |
calendar_events_list | read | List events from a calendar. |
calendar_events_update | write | Update an existing calendar event with patch semantics (only supplied fields change). |
calendar_freebusy_query | read | Query free/busy information for one or more calendars over a time range. |
commentId | read | The comment to retrieve |
completedMax | read | Upper bound on completion date (RFC 3339) |
completedMin | read | Lower bound on completion date (RFC 3339) |
content | read | Comment text |
count | read | count |
description | read | description |
destinationTasklist | write | Destination task list ID (omit to move within the source list) |
docs_batchUpdate | destructive | Apply updates to a Google Doc (insert, format, replace, or permanently delete content). |
docs_create | write | Create a new empty Google Doc. |
docs_get | read | Get a Google Doc |
documentId | read | The document ID |
drive_comments_delete | destructive | Permanently delete a comment. Only the comment |
drive_comments_get | read | Get a single comment on a file. |
drive_comments_list | read | List comments on a file. |
drive_comments_update | write | Change the text of an existing comment. This overwrites the comment |
drive_files_copy | read | Copy a file. Useful for converting formats (e.g. markdown to Google Doc). |
drive_files_create | write | Create a new file in Google Drive. Use with bodyParams for metadata and optionally upload a local file. |
drive_files_delete | destructive | Permanently delete a file. |
drive_files_download | read | |
drive_files_export | read | Export a Google Workspace file (Doc, Sheet, Slide) to a specific format. Returns JSON with export metadata. Use drive_files_download for automatic export with content returned inline. |
drive_files_get | read | test |
drive_files_list | read | List files in Google Drive. Supports search queries via the |
drive_files_update | write | Update a file |
drive_permissions_create | write | Share a file by creating a permission. |
drive_permissions_delete | destructive | Revoke a permission from a file, removing that user |
drive_permissions_list | read | List all permissions on a file. Use to audit sharing state — e.g. a permission with type \ |
drive_permissions_proposeOwnershipTransfer | read | |
drive_permissions_transferOwnership | read | |
drive_permissions_update | write | Change an existing permission |
drive_replies_create | write | Add a reply to a comment, or resolve/reopen it — a comment can only be resolved or reopened by posting a reply with action set. content is required unless action is set; combine both to leave closing/reopening text. Setting action changes the parent comment |
drive_replies_delete | destructive | Permanently delete a reply. Only the reply |
drive_replies_get | read | Get a single reply to a comment. |
drive_replies_list | read | List replies to a comment. |
drive_replies_update | write | Change the text of an existing reply. This overwrites the reply |
due | read | Due date (RFC 3339, e.g. \ |
dueMax | read | Upper bound on due date (RFC 3339) |
dueMin | read | Lower bound on due date (RFC 3339) |
emailAddress | read | Email of user/group (required for user/group type) |
emailAddresses | read | Array of EmailAddress objects as JSON string, e.g. |
end | read | End time JSON |
etag | read | The contact |
eventId | read | Event ID |
fields | read | Fields to include (e.g. \ |
fileId | read | The file ID |
flag | read | flag |
format | read | Response format: full, metadata, minimal, raw |
gmail_drafts_create | write | |
gmail_messages_get | read | Get a single Gmail message by ID. |
gmail_messages_list | read | List Gmail messages matching a query. |
gmail_threads_get | read | Get a full Gmail thread by ID (all messages in the conversation). |
gmail_threads_list | read | List Gmail threads matching a query. |
gmail_threads_modify | destructive | Modify a Gmail thread: add/remove labels. To archive, remove INBOX. To mark read, remove UNREAD. To star, add STARRED. |
id | read | ID |
includeDeleted | read | Whether to include deleted comments (they have no content) |
includeGridData | read | Include grid data |
items | read | Calendars/groups to query, as JSON array string, e.g. |
labelIds | read | Label IDs to filter by |
location | read | Event location |
majorDimension | read | ROWS or COLUMNS |
maxResults | read | Max events to return |
mimeType | read | MIME type (e.g. \ |
name | read | file name |
names | read | Array of Name objects as JSON string, e.g. |
notes | read | Free-text notes / body |
optional_field | read | optional |
orderBy | write | Sort order (e.g. \ |
organizations | read | Array of Organization objects as JSON string, e.g. |
pageObjectId | read | The object ID of the page (slide, layout, or master) to retrieve |
pageSize | read | page size |
pageToken | read | Page token from a previous call |
parent | write | Parent task ID (insert as a subtask under this task) |
parents | read | Parent folder IDs (JSON array as string, e.g. |
people_connections_list | read | List the authenticated user |
people_people_createContact | write | Create a new contact. The Person fields go directly at the top level of the request body — there is no wrapping \ |
people_people_deleteContact | destructive | Permanently delete a contact. Only affects contact data — any linked non-contact data (such as a Google Account profile for that person) is not deleted. |
people_people_get | read | Get a contact by resource name (e.g. \ |
people_people_searchContacts | read | Search the authenticated user |
people_people_updateContact | write | Update an existing contact. The Person fields go directly at the top level of the request body — there is no wrapping \ |
permissionId | write | The permission to update (get it from drive_permissions_list) |
personFields | read | Field mask for the returned Person (optional — defaults to all fields) |
phoneNumbers | read | Array of PhoneNumber objects as JSON string, e.g. |
presentationId | read | The presentation ID |
previous | write | Sibling task ID (insert immediately after this task) |
q | read | query |
query | read | Plain-text search query, matched as a prefix against name/nickname/email/phone/organization fields |
range | read | A1 notation range (e.g. \ |
readMask | read | Comma-separated field mask for results (required by the API, same valid values as people_people_get |
removeLabelIds | destructive | JSON array of label IDs to remove, e.g. [\ |
replyId | read | The reply to retrieve |
requests | read | array |
required_field | read | required |
resourceName | read | Resource name of the contact, e.g. \ |
role | read | Permission role: owner, organizer, fileOrganizer, writer, commenter, reader |
sendUpdates | read | who gets email |
sheets_batchUpdate | write | Apply updates to a spreadsheet (conditional formatting, cell/border formatting, adding sheets, and more). Can also permanently delete content — e.g. removing a sheet or a conditional formatting rule — with no undo via the API. |
sheets_get | read | test |
sheets_values_append | read | Append values after the last row of a spreadsheet range. |
sheets_values_get | read | Read values from a spreadsheet range. |
sheets_values_update | write | test |
showAssigned | read | Include tasks assigned from Docs/Chat Spaces |
showCompleted | read | Include completed tasks (default true; ignored unless showHidden is also true) |
showDeleted | read | Include deleted tasks |
showHidden | read | Include hidden (cleared) tasks |
singleEvents | read | Expand recurring events (usually true) |
slides_batchUpdate | destructive | Apply updates to a presentation (insert/update/delete slides, text, shapes, tables, etc). Delete requests in a batch are permanent — the API has no undo. |
slides_create | write | Create a new blank presentation. |
slides_get | read | Get a presentation |
slides_pages_get | read | Get a single page (slide, layout, or master) from a presentation. |
slides_pages_getThumbnail | read | Get a thumbnail image URL for a page (slide, layout, or master). |
sortOrder | write | Sort order for results |
spreadsheetId | read | id |
start | write | Start time JSON (e.g. |
startModifiedTime | read | Only return comments modified at or after this time (RFC 3339) |
status | read | Task status: needsAction or completed |
summary | read | summary |
supportsAllDrives | read | override |
task | read | Task ID |
tasklist | read | Task list ID (use \ |
tasks_tasklists_delete | destructive | Delete a task list. If it contains assigned tasks, the originals (in Docs/Chat Spaces) are also removed. |
tasks_tasklists_get | read | Get a task list by ID. |
tasks_tasklists_insert | write | Create a new task list. |
tasks_tasklists_list | read | List the authenticated user |
tasks_tasklists_update | write | Update a task list (only supplied fields change). |
tasks_tasks_clear | destructive | Hide all completed tasks in a list. Cleared tasks are not deleted but stop appearing in default list responses. |
tasks_tasks_delete | destructive | Delete a task. If assigned from Docs/Chat Spaces, the original is also removed. |
tasks_tasks_get | read | Get a task by ID. |
tasks_tasks_insert | write | Create a new task. Use parent to nest as a subtask, previous to position after a sibling. |
tasks_tasks_list | write | List tasks in a task list. Excludes hidden and assigned tasks by default; set showHidden/showAssigned to include them. |
tasks_tasks_move | write | Move a task within its list or to another list. Use parent/previous to set position; destinationTasklist to change list. |
tasks_tasks_update | write | Update a task (only supplied fields change). Common use: complete a task by setting status to \ |
test | read | test |
test_tool | read | A test tool |
thumbnailProperties.mimeType | read | Thumbnail image format (PNG is the only supported value) |
thumbnailProperties.thumbnailSize | read | Thumbnail size: THUMBNAIL_SIZE_UNSPECIFIED, LARGE, MEDIUM, SMALL, or WIDTH2000_PX |
timeMax | read | Upper bound (RFC3339) |
timeMin | read | Lower bound (RFC3339, e.g. \ |
timeZone | read | IANA time zone for the response (e.g. \ |
title | read | doc title |
type | read | Grantee type: user, group, domain, anyone |
updatePersonFields | write | Comma-separated field mask of which fields to update (required by the API) |
updatedMin | read | Lower bound on last-modified time (RFC 3339) |
userId | read | User ID (use |
valueInputOption | read | RAW or USER_ENTERED |
valueRenderOption | read | FORMATTED_VALUE, UNFORMATTED_VALUE, or FORMULA |
values | read | values |
x | read | x |
Trust audit
SAFEgrade B · trust 89/100 Nothing in the source contradicts what it says it does. Grade A is reserved for packages that have also passed the behavioural sandbox.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | PASS |
| L1 | Static analysis of the code | PASS |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | WARN |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- declared (1 observation(s))
- Network
- declared (1 observation(s))
- Shell
- none-observed
- Dependencies
- not all pinned
- Secrets in source
- none-found
Findings (9)
calendar_events_delete, docs_batchUpdate, drive_comments_delete, drive_files_delete, drive_permissions_delete, drive_replies_delete, gmail_threads_modify, people_people_deleteContact, removeLabelIds,
readFileSync(new URL("../../package.json", import.meta.url), "utf-8"),const readme = readFileSync(new URL("../../README.md", import.meta.url), "utf-8");readFileSync(new URL("../../server.json", import.meta.url), "utf-8"),const readme = readFileSync(new URL("../../README.md", import.meta.url), "utf-8");const readme = readFileSync(new URL("../../README.md", import.meta.url), "utf-8");@modelcontextprotocol/sdk, zod, @types/node, typescript, vitest
This server exposes **no freestanding send tool** — `gmail_drafts_create` explicitly does not send, and the only outbound email an agent can trigger is calendar invite/update notifications via `sendUp
**On Linux there is no keyring, and the encryption key is a file next to the data it encrypts.** `gws` enables the `keyring` crate's native backends only for macOS and Windows; on every other platform
Gates applied: no_behavioural_pass.
4be33605caccfull audit observations/trust-audit/mcp-server/conorbronsdon__gws-mcp-server.json · Report an issue / request a re-scanAudit history
Every audit this server has had. A grade with a past is a grade somebody is still checking.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-10 | 4be33605cacc | SAFE | B | 89 | first audit |
Questions
What is the gws-mcp-server MCP server?
Curated MCP server for Google Workspace: Gmail, Calendar, Drive, Sheets, Docs, and Tasks via the official gws CLI. 67 focused tools instead of 400, side effects declared on every one. The permissions leg of trust infrastructure for agents.
What tools does gws-mcp-server expose?
157 in total: 112 read-only, 32 that write, and 13 that can delete or overwrite (calendar_events_delete, docs_batchUpdate, drive_comments_delete, drive_files_delete, drive_permissions_delete). Every one is listed on this page with its risk.
Is gws-mcp-server safe to connect to an agent?
The audit found nothing in the source that contradicts what it says it does, and graded it B (89/100). Grade A is held back for packages that have also passed a sandboxed behavioural run, which is why a clean server reads B. Separately from the audit: 13 of its tools can destroy data, so scope the token you give it to what you actually need.
What credentials does gws-mcp-server need?
No credential environment variables were found in its source, so it appears to need none.
How does gws-mcp-server run?
It speaks stdio, so it runs as a local process your client starts. It is published on npm as gws-mcp-server at 0.4.1.
How current is this page?
The grade is for one exact copy of the source (4be33605cacc), read on 2026-10-10. The repository is watched and re-audited when it changes.