Atlas / MCP servers / conorbronsdon / gws-mcp-server

gws-mcp-serverSAFE

mcp/conorbronsdon/gws-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.

Verdict
SAFE
Grade
B
Trust score
89 /100
Exposed tools
157 112r · 32w · 13d
Transport
stdio
License
MIT
Stars
11
01

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

Read from source at commit 4be33605caccOBSERVED · 2026-10-10
02

Connect

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-code (npm)
claude mcp add gws-mcp-server -- npx -y [email protected]
03

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.

ToolRiskDescription
actionwriteSet to change the parent comment
addLabelIdswriteJSON array of label IDs to add, e.g. [\
anchorreadregion
attendeesreadAttendees (JSON array as string, e.g.
calendarIdreadcalendar
calendar_events_deletedestructiveDelete a calendar event.
calendar_events_getreadGet a single calendar event by ID.
calendar_events_insertwritetest
calendar_events_listreadList events from a calendar.
calendar_events_updatewriteUpdate an existing calendar event with patch semantics (only supplied fields change).
calendar_freebusy_queryreadQuery free/busy information for one or more calendars over a time range.
commentIdreadThe comment to retrieve
completedMaxreadUpper bound on completion date (RFC 3339)
completedMinreadLower bound on completion date (RFC 3339)
contentreadComment text
countreadcount
descriptionreaddescription
destinationTasklistwriteDestination task list ID (omit to move within the source list)
docs_batchUpdatedestructiveApply updates to a Google Doc (insert, format, replace, or permanently delete content).
docs_createwriteCreate a new empty Google Doc.
docs_getreadGet a Google Doc
documentIdreadThe document ID
drive_comments_deletedestructivePermanently delete a comment. Only the comment
drive_comments_getreadGet a single comment on a file.
drive_comments_listreadList comments on a file.
drive_comments_updatewriteChange the text of an existing comment. This overwrites the comment
drive_files_copyreadCopy a file. Useful for converting formats (e.g. markdown to Google Doc).
drive_files_createwriteCreate a new file in Google Drive. Use with bodyParams for metadata and optionally upload a local file.
drive_files_deletedestructivePermanently delete a file.
drive_files_downloadread
drive_files_exportreadExport 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_getreadtest
drive_files_listreadList files in Google Drive. Supports search queries via the
drive_files_updatewriteUpdate a file
drive_permissions_createwriteShare a file by creating a permission.
drive_permissions_deletedestructiveRevoke a permission from a file, removing that user
drive_permissions_listreadList all permissions on a file. Use to audit sharing state — e.g. a permission with type \
drive_permissions_proposeOwnershipTransferread
drive_permissions_transferOwnershipread
drive_permissions_updatewriteChange an existing permission
drive_replies_createwriteAdd 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_deletedestructivePermanently delete a reply. Only the reply
drive_replies_getreadGet a single reply to a comment.
drive_replies_listreadList replies to a comment.
drive_replies_updatewriteChange the text of an existing reply. This overwrites the reply
duereadDue date (RFC 3339, e.g. \
dueMaxreadUpper bound on due date (RFC 3339)
dueMinreadLower bound on due date (RFC 3339)
emailAddressreadEmail of user/group (required for user/group type)
emailAddressesreadArray of EmailAddress objects as JSON string, e.g.
endreadEnd time JSON
etagreadThe contact
eventIdreadEvent ID
fieldsreadFields to include (e.g. \
fileIdreadThe file ID
flagreadflag
formatreadResponse format: full, metadata, minimal, raw
gmail_drafts_createwrite
gmail_messages_getreadGet a single Gmail message by ID.
gmail_messages_listreadList Gmail messages matching a query.
gmail_threads_getreadGet a full Gmail thread by ID (all messages in the conversation).
gmail_threads_listreadList Gmail threads matching a query.
gmail_threads_modifydestructiveModify a Gmail thread: add/remove labels. To archive, remove INBOX. To mark read, remove UNREAD. To star, add STARRED.
idreadID
includeDeletedreadWhether to include deleted comments (they have no content)
includeGridDatareadInclude grid data
itemsreadCalendars/groups to query, as JSON array string, e.g.
labelIdsreadLabel IDs to filter by
locationreadEvent location
majorDimensionreadROWS or COLUMNS
maxResultsreadMax events to return
mimeTypereadMIME type (e.g. \
namereadfile name
namesreadArray of Name objects as JSON string, e.g.
notesreadFree-text notes / body
optional_fieldreadoptional
orderBywriteSort order (e.g. \
organizationsreadArray of Organization objects as JSON string, e.g.
pageObjectIdreadThe object ID of the page (slide, layout, or master) to retrieve
pageSizereadpage size
pageTokenreadPage token from a previous call
parentwriteParent task ID (insert as a subtask under this task)
parentsreadParent folder IDs (JSON array as string, e.g.
people_connections_listreadList the authenticated user
people_people_createContactwriteCreate a new contact. The Person fields go directly at the top level of the request body — there is no wrapping \
people_people_deleteContactdestructivePermanently 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_getreadGet a contact by resource name (e.g. \
people_people_searchContactsreadSearch the authenticated user
people_people_updateContactwriteUpdate an existing contact. The Person fields go directly at the top level of the request body — there is no wrapping \
permissionIdwriteThe permission to update (get it from drive_permissions_list)
personFieldsreadField mask for the returned Person (optional — defaults to all fields)
phoneNumbersreadArray of PhoneNumber objects as JSON string, e.g.
presentationIdreadThe presentation ID
previouswriteSibling task ID (insert immediately after this task)
qreadquery
queryreadPlain-text search query, matched as a prefix against name/nickname/email/phone/organization fields
rangereadA1 notation range (e.g. \
readMaskreadComma-separated field mask for results (required by the API, same valid values as people_people_get
removeLabelIdsdestructiveJSON array of label IDs to remove, e.g. [\
replyIdreadThe reply to retrieve
requestsreadarray
required_fieldreadrequired
resourceNamereadResource name of the contact, e.g. \
rolereadPermission role: owner, organizer, fileOrganizer, writer, commenter, reader
sendUpdatesreadwho gets email
sheets_batchUpdatewriteApply 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_getreadtest
sheets_values_appendreadAppend values after the last row of a spreadsheet range.
sheets_values_getreadRead values from a spreadsheet range.
sheets_values_updatewritetest
showAssignedreadInclude tasks assigned from Docs/Chat Spaces
showCompletedreadInclude completed tasks (default true; ignored unless showHidden is also true)
showDeletedreadInclude deleted tasks
showHiddenreadInclude hidden (cleared) tasks
singleEventsreadExpand recurring events (usually true)
slides_batchUpdatedestructiveApply 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_createwriteCreate a new blank presentation.
slides_getreadGet a presentation
slides_pages_getreadGet a single page (slide, layout, or master) from a presentation.
slides_pages_getThumbnailreadGet a thumbnail image URL for a page (slide, layout, or master).
sortOrderwriteSort order for results
spreadsheetIdreadid
startwriteStart time JSON (e.g.
startModifiedTimereadOnly return comments modified at or after this time (RFC 3339)
statusreadTask status: needsAction or completed
summaryreadsummary
supportsAllDrivesreadoverride
taskreadTask ID
tasklistreadTask list ID (use \
tasks_tasklists_deletedestructiveDelete a task list. If it contains assigned tasks, the originals (in Docs/Chat Spaces) are also removed.
tasks_tasklists_getreadGet a task list by ID.
tasks_tasklists_insertwriteCreate a new task list.
tasks_tasklists_listreadList the authenticated user
tasks_tasklists_updatewriteUpdate a task list (only supplied fields change).
tasks_tasks_cleardestructiveHide all completed tasks in a list. Cleared tasks are not deleted but stop appearing in default list responses.
tasks_tasks_deletedestructiveDelete a task. If assigned from Docs/Chat Spaces, the original is also removed.
tasks_tasks_getreadGet a task by ID.
tasks_tasks_insertwriteCreate a new task. Use parent to nest as a subtask, previous to position after a sibling.
tasks_tasks_listwriteList tasks in a task list. Excludes hidden and assigned tasks by default; set showHidden/showAssigned to include them.
tasks_tasks_movewriteMove a task within its list or to another list. Use parent/previous to set position; destinationTasklist to change list.
tasks_tasks_updatewriteUpdate a task (only supplied fields change). Common use: complete a task by setting status to \
testreadtest
test_toolreadA test tool
thumbnailProperties.mimeTypereadThumbnail image format (PNG is the only supported value)
thumbnailProperties.thumbnailSizereadThumbnail size: THUMBNAIL_SIZE_UNSPECIFIED, LARGE, MEDIUM, SMALL, or WIDTH2000_PX
timeMaxreadUpper bound (RFC3339)
timeMinreadLower bound (RFC3339, e.g. \
timeZonereadIANA time zone for the response (e.g. \
titlereaddoc title
typereadGrantee type: user, group, domain, anyone
updatePersonFieldswriteComma-separated field mask of which fields to update (required by the API)
updatedMinreadLower bound on last-modified time (RFC 3339)
userIdreadUser ID (use
valueInputOptionreadRAW or USER_ENTERED
valueRenderOptionreadFORMATTED_VALUE, UNFORMATTED_VALUE, or FORMULA
valuesreadvalues
xreadx
04

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.

LayerWhat it checksResult
L0Provenance & inventoryPASS
L1Static analysis of the codePASS
L2Instruction surface (what it tells the agent)PASS
L3Class-specific surfaceWARN
L4Behavioural (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)

MEDIUMFilesystem / path · mcp.destructive_tools · CWE-22, CWE-59
calendar_events_delete, docs_batchUpdate, drive_comments_delete, drive_files_delete, drive_permissions_delete, drive_replies_delete, gmail_threads_modify, people_people_deleteContact, removeLabelIds,
Why it matters. 13 tool(s) can delete or overwrite
Fix. prefer a read-only mode or scoped tokens; the page states the blast radius
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/__tests__/annotations.e2e.test.ts:99
readFileSync(new URL("../../package.json", import.meta.url), "utf-8"),
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/__tests__/annotations.e2e.test.ts:178
const readme = readFileSync(new URL("../../README.md", import.meta.url), "utf-8");
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/__tests__/annotations.e2e.test.ts:184
readFileSync(new URL("../../server.json", import.meta.url), "utf-8"),
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/__tests__/annotations.e2e.test.ts:191
const readme = readFileSync(new URL("../../README.md", import.meta.url), "utf-8");
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/__tests__/annotations.e2e.test.ts:235
const readme = readFileSync(new URL("../../README.md", import.meta.url), "utf-8");
LOWSupply chain · supply.unpinned · CWE-829, CWE-1357
package.json
@modelcontextprotocol/sdk, zod, @types/node, typescript, vitest
Why it matters. 5 dependency range(s) float
Fix. pin exact versions or ship a lockfile
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
README.md:33
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
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine
INFOPrompt injection · prompt.credential_read · CWE-94, CWE-1427
README.md:49
**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
Why it matters. asks the agent to read credentials

Gates applied: no_behavioural_pass.

Audited 2026-10-10 · audit v0.4.1 · source sha 4be33605caccfull audit observations/trust-audit/mcp-server/conorbronsdon__gws-mcp-server.json · Report an issue / request a re-scan
05

Audit history

Every audit this server has had. A grade with a past is a grade somebody is still checking.

DateSourceVerdictGradeScoreChange
2026-10-104be33605caccSAFEB89first audit
06

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.

Advertisement