Google Workspace
Gmail, Calendar, Contacts, Drive, Sheets, Docs, Slides
Google Workspace MCP Server
A Model Context Protocol server that gives Claude access to Google Workspace: Gmail, Calendar, Drive, Contacts, Sheets, Docs, Slides, Tasks, and the other Google Workspace APIs through one generic tool.
This server powers the Google Workspace connector of DataToRAG, a hosted MCP gateway with per-user OAuth, multi-account support, and Atlassian tools alongside these: add https://datatorag.com/mcp to your MCP client. There is nothing to install or set up here. An earlier Claude Desktop extension built from this code has been retired.
Tools
| Service | Tools | Operations |
|---|---|---|
| Gmail | 18 | send, reply, forward, read, search, list, create draft, update draft, send draft, delete draft, mark read, list filters, create label, list labels, update label, delete label, label message, save attachment to Drive |
| Calendar | 6 | list events, get event, create, update, delete, freebusy |
| Contacts | 7 | search, get, list, create, update, delete, directory search |
| Drive | 5 | search, read file, create folder, rename, copy |
| Sheets | 14 | read, query, update, append, create, delete, add tab, rename tab, delete tab, clear, find rows, format range, format table, batch update |
| Docs | 5 | get, write, batch update, create, delete |
| Slides | 4 | get, create, batch update, delete |
| Tasks | 7 | list task lists, create task list, list tasks, create, update, complete, delete |
| Generic | 1 | gws_run — fallback for any GWS API not covered above |
67 tools total. All tools support shared (team) Drives.
Key tool details
gmail_send / gmail_reply / gmail_forward / gmail_create_draft / gmail_update_draft / gmail_send_draft — The account's Gmail signature is appended when a message is sent AND when a draft is written, so a model composing a message should not write a sign-off of its own. The signature comes from users.settings.sendAs, the isDefault entry (or the entry matching a draft's From), read fresh on every send and never cached — a client is built per tool call with that caller's token, so a remembered value would go out on someone else's mail. Gmail's API exposes only the new-email signature, so replies and forwards use it too. Pass signature: false to send without it and skip the lookup. Every response carries a signature field: applied, none_set, suppressed, already_present, unavailable or skipped_unsupported_draft. A failed lookup never blocks the send.
The signature goes in the message's HTML part only, as the stored markup unchanged — so an image signature is simply a hosted URL that passes through, and nothing can mangle it. The plain-text part is built from the body alone and never carries it. A plain body send is therefore promoted to multipart/alternative when the account has a signature, with the plain half exactly as the caller wrote it; with no signature a plain send is byte-identical to before. The accepted cost is that a text-only reader sees the message without the signature.
gmail_create_draft / gmail_update_draft — Create or replace a Gmail draft. Constructs RFC 2822 MIME messages from structured parameters (to, subject, body, cc, bcc) and base64url-encodes them. gmail_update_draft preserves threading automatically — if no thread_id is provided, it fetches the existing draft's thread ID before replacing the message. Both sign the draft exactly as the send tools sign a message (HTML part only, signature: false to leave it out, a signature field in the response). Gmail's own Compose signs when a draft is written, and a draft made here and then sent from Gmail's UI never passes through gmail_send_draft, so signing only at send time left that draft with no signature at all.
Attachments (gmail_send / gmail_reply / gmail_forward / gmail_create_draft / gmail_update_draft) — attachments takes up to 10 Drive files, 25 MB of raw bytes in total. An entry is a Drive file id, or {file_id, as} to send a Google Doc, Sheet or Slides deck as a file in one of Google's export formats: Docs as pdf, docx, txt, html, md, rtf, odt or epub; Sheets as pdf, xlsx, csv, tsv, html or ods; Slides as pdf, pptx, txt or odp. A pair outside that table is refused naming both halves. csv and tsv are one tab each: tab picks it by title, the first tab goes otherwise, and the response always says which (exported tab Sheet1 of 3). They are read through the Sheets API as the tab displays, because files.export renders only the first tab in those formats. A Sheet as html arrives as a .zip of pages; Slides as txt is the visible text only. Files come from Drive and nowhere else: bytes typed into a tool call are not accepted. A Google Doc, Sheet or Slides deck passed by id alone goes as a link in the note, in both the plain and HTML parts, the way Gmail sends one; nothing changes who can open it. Every file gets a Content-ID from its filename (characters other than letters, digits, dot, dash and underscore become _); a file the HTML references as cid:<that id> is sent inline in multipart/related and renders in place, any other is a multipart/mixed attachment. Two of the caller's files may not share a filename, or names that would share a Content-ID. gmail_forward carries the original's own attachments, inline images included under their original Content-ID, and originals that share a name are kept apart rather than refused; include_original_attachments: false forwards the text alone. Every refusal (a malformed entry, a folder, a format a file cannot take, an unknown tab, a missing or unshared id, a duplicate name, a total over the limit) happens before any file is fetched and before the signature lookup. The message is built as a stream and sent as one message/rfc822 upload, so no file is held whole in memory; an export, whose size exists only once Google renders it, is counted as it streams, and crossing the limit then stops the upload before its last chunk. The response's attachments field reports each entry as attached, inline, linked or exported, with its size or link. A call with no attachments is sent exactly as before. gmail_update_draft replaces the whole draft: files not passed again are dropped.
gmail_read — Full MIME payload by default. Pass text_only: true for a compact view (flattened from/to/cc/subject/date, decoded text body with HTML fallback, attachment metadata) that avoids base64 payloads overflowing the response — typically ~2% of the full size. max_body_chars truncates the body with a marker (implies text_only). When a message has no text/plain part, its HTML is flattened to text; at most 512KB of that markup is read, and a message over the limit says so in the returned body. Ordinary mail is far below it.
gmail_search / gmail_list — Results are flattened to {id, threadId, from, to, subject, date, snippet, labelIds} per message instead of the raw metadata payload.
gmail_send_draft / gmail_delete_draft — Send or permanently delete an existing draft by its draft ID. gmail_send_draft sends a reviewed draft and removes it from Drafts (no orphaned draft left behind), completing the create → review → send loop. A draft written by the draft tools already carries its signature, so it is sent unchanged and reports already_present: a draft is signed once. For a draft that does not (one written in Gmail's UI with signatures off, or with signature: false), it rewrites the stored MIME to insert the signature into the HTML part, keeping every header and the plain part untouched, and inserting above a quoted reply; a text/plain-only draft gains an HTML part the same way a plain send does. A draft holding files (multipart/mixed or multipart/related) is signed in its first, text part only and its files are carried back byte for byte, never decoded. A draft it will not rewrite is sent exactly as written and reports skipped_unsupported_draft: a first part that is not text, nesting past mixed > related > text, an encoding it cannot re-emit, a part declaring a charset other than UTF-8 or US-ASCII, or a message whose bytes are not valid UTF-8 (both would be mangled by the rewrite). Nothing in the signature path can cost you the send — a failed read, a rewrite Gmail rejects and a rewrite too large to transmit all fall through to sending the draft untouched. When the signature does not fit the encoding the HTML part declares (an emoji in a 7bit part, or an over-long line), that part is re-encoded as base64 rather than shipped as invalid MIME. gmail_delete_draft deletes immediately (does not move to Trash).
gmail_mark_read — Marks messages as read by removing the UNREAD label. Also supports adding/removing arbitrary labels (STARRED, IMPORTANT, etc.) via add_labels and remove_labels arrays. Pass message_id for a single message, or message_ids (up to 1000) to modify a batch in one API call via users.messages.batchModify. Removes UNREAD by default when no label arrays are given.
gmail_label_message: Labels many messages in one call. Pass message_ids (up to 1000) with add_labels and/or remove_labels and every message is modified by a single users.messages.batchModify request; the label-and-mark-read pair is one call (add_labels: ["<label id>"], remove_labels: ["UNREAD"]). Returns a per-message outcome (results[]: id, ok, error), and if the batch request is refused each id is retried on its own so a partial batch is visible. message_id is for a single message only.
gmail_list_filters — Reads the filters a mailbox already has, so you can see
what automation exists before adding more. Reading filters works under
gmail.modify.
Creating and deleting filters is not currently exposed. Google accepts only
gmail.settings.basic on users.settings.filters.create and .delete, and
gmail.modify does not carry it, so those calls fail with insufficient scopes
regardless of what the caller does. Rather than ship two tools that can only
fail, they are withheld until that scope is granted. Gmail filters are also
immutable, so when they return, "editing" one means create new + delete old.
gmail_save_attachment_to_drive — Fetches an attachment from Gmail and uploads it directly to Drive server-side. No base64 data flows through the conversation. Uses async file I/O with guaranteed temp file cleanup via try/finally.
calendar_list_events — Compact view by default: per event you get id, title, times, location, a plain-text description (HTML stripped, truncated at 500 chars, tune with max_description_chars), the organizer, an attendee count plus your own response status, video join links (Meet, or Zoom and friends from conference data), a recurring flag, and attachments. Meetings with 10 or fewer attendees keep their full roster, so a 1:1 still tells you who it's with; larger meetings collapse to the count. Roughly 85% smaller than the raw payload on a busy calendar. Pass full: true for the raw Calendar API response.
calendar_get_event — Full event details with the description converted to plain text. Pass full: true to keep the original HTML.
drive_search — Searches across both personal and shared Drives. Supports full Drive query syntax including folder parents, mimeType filters, and name matching.
drive_create_folder — Creates a folder in Drive, optionally inside a parent folder.
drive_rename_file — Renames a file or folder. Sends only name, so nothing else about the file changes; a blank or whitespace-only name is rejected rather than written, because Drive accepts an empty name and the file then cannot be found by name.
drive_copy_file — Copies a file and names the copy in the same call, optionally into parent_id. This is the template path: the copy carries the original's tabs, formatting and formulas, where a hand-rebuild drifts from the template. Folders cannot be copied — Drive returns 403 cannotCopyFile: "This file cannot be copied by the user", which reads like a permissions problem and is not one.
drive_read_file — Reads the text content of any file in Drive by file ID. Routes by mimeType:
- Google Docs → plain text extraction
- Google Sheets → row/column data (A1:Z1000)
- Google Slides → slide structure with placeholder maps and text
- Office formats (.docx, .xlsx, .pptx) → server-side conversion to native Google format, read converted copy, then delete temp copy (guaranteed cleanup via try/finally)
- Plain text / CSV (
text/plain,text/csv) → raw content fetch - Unsupported types (PDF, images, etc.) → returns
{ error: "Unsupported file type: <mimeType>" }
docs_get — Three modes:
text(default): plain text with[image:<id>]placeholders for inline images, best for reading/summarizingindex: text with startIndex/endIndex character positions plus inline object references, use before positional editsfull: raw API response, for debugging or style operations
All modes include the inlineObjects metadata map (contentUri, size, margins, crop, border) when images are present.
docs_create / sheets_create — Return stripped responses with only essential fields:
- docs_create →
{ documentId, title } - sheets_create →
{ spreadsheetId, title, spreadsheetUrl }
slides_get / slides_create — Return trimmed responses (no masters, layouts, geometry, styling). Each slide includes:
placeholder_map: maps standard types (TITLE, BODY, SUBTITLE) to objectIdselements: all shapes — both standard placeholders and custom text boxes- Empty placeholders are included so callers can insert text immediately after create without a redundant get call
sheets_read — Returns normalized data:
columnCountderived from the widest row (handles empty leading rows correctly)- All rows padded to uniform column count with empty strings
sheets_append — Uses direct Sheets API (spreadsheets.values.append) to preserve 2D array structure. Each inner array becomes a separate row.
docs_write — Uses batchUpdate API with insertText, correctly handles newlines, em dashes, and unicode characters.
gws_run — Fallback tool for any Google Workspace API not covered by the dedicated tools. Accepts service, resource, method, params, and JSON body. Use only when no dedicated tool exists.
Running under the gateway
This is a service plugin of the DataToRAG gateway, not a server to run on its own. The gateway starts server/index.js as a child process, sets PORT, and sends each user's Google access token in the X-User-Token header; every call then goes to the Google REST API directly, and the gws CLI is never run. For development, from the repository root:
pnpm install
pnpm --filter @datatorag-mcp/gws-mcp run build
pnpm --filter @datatorag-mcp/gws-mcp run test
Environment Variables
| Variable | Default | Description |
|---|---|---|
PORT |
39147 |
HTTP server port (the gateway sets this) |
The plugin reads no OAuth client credentials and no other setting. The gateway gives it none.
Architecture
src/
├── create-server.ts # Shared MCP server factory (accepts optional per-session client)
├── index.ts # HTTP entry point (StreamableHTTP, /health + /mcp endpoints, the private file-bytes route)
├── internal/
│ ├── file-bytes.ts # Private route for the gateway: the bytes of a file reference, by a registry of types
│ └── file-bytes/ # One resolver per reference type (gmail-message.ts, gmail-attachment.ts) and what they share
├── gws-client.ts # The client every tool calls; it needs the user's token and says so without one
├── google-api/
│ ├── method-table.ts # GENERATED from Google's Discovery documents (scripts/generate-method-table.mjs)
│ ├── request.ts # (service, resource, method, params) -> verb, URL, query, body
│ ├── direct-transport.ts # fetch against the REST endpoints, error and paging shapes
│ ├── direct-upload.ts # Media upload (multipart, or resumable in bounded chunks), streaming attachment decode
│ ├── oracle.fixtures.json # What the gws CLI reported it would send, recorded for every method
│ └── oracle.test.ts # Holds the request builder equal to that recording, method for method
├── mime/
│ └── build.ts # A message with attachments as a byte stream (mixed, related, base64 as it passes)
├── attachments/
│ ├── resolve.ts # Each entry to attached / inline / linked / exported, the export table; every refusal before any fetch
│ └── fetch.ts # Byte sources (Drive media, Drive export, one Sheet tab, Gmail attachment) and the running 25 MB budget
└── tools/
├── response.ts # Response helpers (JSON formatting, 900KB truncation)
├── gmail.ts # Gmail tools (drafts, mark read, attachments to Drive, compose with attachments)
├── gmail-signature.ts # Signature lookup, HTML insertion, already-present check
├── gmail-draft-send.ts # Signs a draft's stored MIME (parse, insert, re-encode)
├── calendar.ts # Calendar tools
├── contacts.ts # Contacts / People API tools
├── drive.ts # Drive tools (search, read file, create folder)
├── sheets.ts # Sheets tools (normalized reads, direct API append)
├── docs.ts # Docs tools (text/index/full modes, inline image metadata)
├── slides.ts # Slides tools (trimmed responses, placeholder maps)
├── tasks.ts # Google Tasks tools (lists, CRUD, complete)
├── generic.ts # Generic gws_run fallback
└── index.ts # Tool registry (flat Map<name, handler>)
Every tool calls client.api(service, resource, method, { params, jsonBody }). The call is a fetch to the Google REST endpoint, built from a method table generated from Google's Discovery documents, with the bearer token the gateway sent in X-User-Token for the user the call is for. No process is started. A call with no token is refused before any request; there is no stored login to fall back to.
The token travels in the Authorization header only: never in a URL, a log line or an error. Requests go only to *.googleapis.com, and for the one Sheets query endpoint to docs.google.com by exact host; redirects are refused, and a resumable upload's session URL is checked against the same rule before anything is sent to it.
Rate-limit refusals are retried, for reads. When Google answers a GET with 429, or with 403 and a rate reason (rateLimitExceeded, userRateLimitExceeded), the direct transport sends it again: up to three requests per call, exponential backoff with full jitter, at most 8 seconds of added wait, and Google's Retry-After honoured (a longer one stops the retry). A refusal that outlasts the attempts surfaces as the same error, with its transient hint. Writes and uploads are never retried, and neither are gateway failures (502, 503, 504) or daily limits. The rule and its reasons are in src/google-api/rate-limit-retry.ts.
A private route hands a file's bytes to the gateway. POST /internal/file-bytes on the HTTP server is not an MCP tool and no model can call it. A gateway that moves a file from one connector to another calls it, with the same X-User-Token header /mcp receives, so the bytes travel between services instead of through the conversation. It takes a file reference and a byte cap, and answers with the file's bytes and a suggested name in X-File-Name. Two reference types are supported. gmail_message (message_id) is a Gmail message, returned as its original (message/rfc822, an .eml file) exactly as Gmail holds it. gmail_attachment (message_id, part_id) is one attachment of a message, returned under the sender's file name and type. An attachment is named by its part id, which gmail_read lists and which stays the same on every read, because Gmail issues a new attachment id each time the message is read. The size is checked before the file is read and again as it is decoded, nothing is written to disk, and nothing about the file is logged. Failures are JSON with a code. The route is src/internal/file-bytes.ts; each reference type is one file under src/internal/file-bytes/, and adding a type is a file there and a line in the route's registry.
To refresh the method table after Google changes an API: node scripts/generate-method-table.mjs, then pnpm test. The oracle test fails by name for a method the recording does not cover; its header says what to do then.
The HTTP server (index.ts) is the only entry point, and it is what the gateway starts.
Key implementation details
- Shared Drive support: All Drive API calls include
supportsAllDrives: true(andincludeItemsFromAllDrives: truefor list operations) so files on team Drives are accessible - No process is started and nothing is written to disk: every call is a
fetch; the plugin runs as an unprivileged user with no home directory - X-User-Token support: HTTP server accepts
X-User-Tokenheader to create per-session clients with pre-obtained access tokens; those clients call Google directly - Response truncation: All responses capped at 900KB to stay within context limits
- Context optimization: docs_get, slides_get, and sheets_read aggressively strip metadata to minimize context usage. docs_get text mode reduces ~50KB API responses to ~2-3KB. slides_get strips masters/layouts/geometry/styling. sheets_read uses the values-only API endpoint.
- Inline image metadata: docs_get includes
inlineObjectsmap with image metadata (contentUri, size, margins) without embedding actual image bytes - Slides trimming: Strips masters, layouts, geometry, and styling from API responses — returns only objectIds, placeholder types, and text content
- Office file reading:
drive_read_filecopies Office files with explicit target mimeType to trigger server-side conversion, reads the native copy, then deletes it (guaranteed cleanup via try/finally) - Unsupported type guard:
drive_read_fileonly fetches raw content fortext/plainandtext/csv— all other non-native types return a clean error instead of binary data - Platform support: macOS (arm64, x64), Linux (x64), Windows (x64)
Development
pnpm run dev # Watch mode — recompiles on change
License
MIT
Capabilities
gws-mcp__sheets_queryRun a query in the Google Sheets QUERY() language over a spreadsheet range and get back only the matching rows, in ONE call. Use this instead of reading a whole tab and filtering the values yourself: select A, D where I = 'open' order by A desc limit 20 returns the twenty rows you wanted and nothing else. The language supports select, where, group by, pivot, order by, limit, offset, label and format, with count, sum, avg, min and max. Columns are addressed by their SHEET letter (A, B, C), the same letter the column has in the tab, not its position inside the range; Col1-style names are also accepted and mapped to the range's columns in order (on Tab!C1:F500, Col1 is C). Text values are compared with quotes: where B = 'high' or where B contains 'urgent'. A column that mixes numbers and text is read as whichever type most of its cells hold, and the cells of the other type come back empty: that is the query endpoint's own rule and cannot be turned off, so read such a column with sheets_read or sheets_find_rows instead. Read-only: nothing in the spreadsheet changes. The result carries the header the query produced, the rows, the row count and the echoed query; an empty match is an empty rows array.
Parameters
querystring/ The query, in the QUERY() language, e.g. "select A, C, F where F = 'open' and C > 3 order by C desc" or "select B, count(A) group by B". Columns by sheet letter; strings in single quotes.rangestring/ What the query runs over: a tab name on its own queries that whole tab (the simplest correct call), or A1 notation naming a real tab, e.g. "TabName!A1:F500". A bare word that is the name of a tab is that tab, even when it also reads as a cell ("Q3", "Log"). A tab that does not exist is refused with the list of tabs. Omitted, the first tab is queried.has_header_rowboolean/ Default true: the first row of the range is headers, so the result names its columns by those headers and the header row is never returned as data. Set false when the range is pure data; columns are then named by letter.spreadsheet_idstring/ The spreadsheet IDgws-mcp__gmail_delete_labelDelete a Gmail label. Takes the label ID (from gmail_list_labels), not the label name. This removes the label from every message that carries it; the messages themselves are not deleted. System labels (INBOX, UNREAD, SENT) cannot be deleted.
Parameters
label_idstring/ The label ID to delete (from gmail_list_labels)gws-mcp__gmail_list_labelsList every label in the mailbox, system and user-created, with each label's ID, name and type. Use this to find the label ID that gmail_label_message, gmail_update_label and gmail_delete_label need.
gws-mcp__gmail_update_labelRename an existing Gmail label, or change its visibility. Takes the label ID (from gmail_list_labels), not the label name. Renaming a label keeps it on every message already labelled with it.
Parameters
namestring/ New label name. Nested labels use '/' (e.g. 'Alerts/Invoices')label_idstring/ The label ID to update (from gmail_list_labels)label_list_visibilitystring/ Whether the label shows in the label list: labelShow, labelShowIfUnread, or labelHidemessage_list_visibilitystring/ Whether the label shows on messages in the message list: show or hidegws-mcp__sheets_clearClear the values in a range, leaving the tab and its formatting in place. This is the non-destructive way to empty a tab or a block of cells — use it instead of deleting and recreating a tab, which throws away the tab's structure along with its data.
Parameters
rangestring/ Range to clear in A1 notation. A bare tab name clears the whole tab (e.g. "Inventory"), or clear a block with "Inventory!A2:D"spreadsheet_idstring/ The spreadsheet IDgws-mcp__sheets_delete_tabDelete a tab (sheet) and everything in it from a spreadsheet. Takes the tab's current title. THIS DESTROYS EVERY ROW IN THE TAB and cannot be undone through the API. To empty a tab without losing it, use sheets_clear instead. To delete the whole spreadsheet file, use sheets_delete.
Parameters
titlestring/ Title of the tab to delete, with all of its rowsspreadsheet_idstring/ The spreadsheet IDgws-mcp__sheets_rename_tabRename a tab (sheet) inside a Google Sheets spreadsheet. Takes the tab's current title, not its sheetId. Renaming changes only the label: every row, formula and value in the tab is untouched. Ranges that name the old title will stop resolving, so update any saved ranges afterwards.
Parameters
titlestring/ Current title of the tab to renamenew_titlestring/ New title for the tabspreadsheet_idstring/ The spreadsheet IDgws-mcp__gmail_label_messageLabel many messages in ONE call: pass message_ids (up to 1000) with add_labels and/or remove_labels, and every message is modified by a single users.messages.batchModify request. Do not call this once per message. The label-and-mark-read pair is one call: add_labels: ["<label id>"], remove_labels: ["UNREAD"]. Removing INBOX archives; removing UNREAD marks read. Label IDs come from gmail_list_labels. Returns a per-message outcome (results[]: id, ok, error) so a partial batch is visible; if the batch request is refused, each id is retried on its own and reported. message_id is for a single message only.
Parameters
add_labelsarray/ Label IDs to add, e.g. ["Label_12"]message_idstring/ A single message ID, for the one-message case only. Provide either this or message_ids.message_idsarray/ The message IDs to modify together in one call, up to 1000. Prefer this over message_id whenever there is more than one message.remove_labelsarray/ Label IDs to remove, e.g. ["INBOX"] to archive or ["UNREAD"] to mark read; combine with add_labels to label and mark read in the same callgws-mcp__sheets_find_rowsFind the rows in a spreadsheet range whose value in one column matches what you are looking for, and get back their ROW NUMBERS. Use this instead of reading a whole sheet and filtering the values yourself: on any real sheet that burns context on rows nobody asked for. Searches MANY values in a single call, so looking up twenty customers is one call and not twenty. Each match comes back with its 1-based sheet row number and a ready-made A1 range for that row, which is what lets a find be followed directly by a sheets_update. Values that matched nothing are listed separately, so an empty result cannot be mistaken for a broken call.
Parameters
matchstring/ "exact" (default) compares the whole cell, case-sensitively, after trimming both sides. "contains" and "prefix" are substring tests and are case-INsensitive. Exact is the default because a lookup that quietly folded case would make two different rows interchangeable without ever saying so.rangestring/ The range to search: a tab name on its own searches that whole tab (the simplest correct call), or A1 notation naming a real tab, e.g. "TabName!A:D" for whole columns or "TabName!A1:D500" for a block — TabName stands for a tab the spreadsheet actually has. Row numbers in the result are absolute sheet rows, so a range starting at A10 reports its first data row as 11.columnstring/ Which column to match on: a header name taken from the first row of the range ("Email"), or an A1 column letter ("B").valuesarray/ The values to look for. Pass every value you need in ONE call, e.g. ["[email protected]", "[email protected]"].max_resultsnumber/ Maximum matching rows returned per searched value. Default 50. A capped result says truncated: true, because a silent truncation reads exactly like a complete answer.has_header_rowboolean/ Default true: the first row of the range is headers, is used to resolve a column name, and is never returned as a match. Set false when the range is pure data.spreadsheet_idstring/ The spreadsheet IDgws-mcp__sheets_format_tableMake a spreadsheet table readable in one call: column widths, wrapped and top-aligned cells, a styled and frozen header row, and hairline borders. This is the pass to run on ANY sheet a person is going to open. Values written through sheets_update and sheets_append carry no formatting at all, and the untouched default is columns 100px wide with every long cell truncated to a slit, so a sheet with entirely correct data is routinely unreadable and the reader never learns there was more text. Applies everything as one atomic batch. Use sheets_format_range for specific styling on top, or instead of this when you do not want the whole opinionated pass.
Parameters
wrapboolean/ Default true: wrap and top-align the body so rows grow to fit their content. Row heights are deliberately never set, because a fixed height overrides auto-fit and re-clips the text wrapping just unclipped.rangestring/ The table: a bare tab name for the whole tab, or A1 notation like "TabName!A1:E60" naming a tab the spreadsheet actually has. Include the header row.bandedboolean/ Default false. Tint alternating body rows.trim_gridboolean/ Default false. DELETES the rows and columns outside the range, which is what makes a sheet look authored rather than dumped. Destructive and opt-in, and it needs a fully bounded range so there is an end to trim from.header_rowsnumber/ How many rows at the top of the range are headers. Default 1. Use 0 for a table with no header, which skips both the header styling and the freeze.column_widthsarray/ Pixel widths, left to right, e.g. [250, 400, 400]. Columns you do not name get default_width. This is the single highest-value change on any sheet with prose in it.default_widthnumber/ Width for columns not named in column_widths. Default 200.freeze_headerboolean/ Default true: keep the header rows visible while scrolling. Only rows are ever frozen, never columns, because a frozen column makes merging across it illegal and would take an entire later batch down with it.spreadsheet_idstring/ The spreadsheet IDgws-mcp__gmail_send_draftSend an existing Gmail draft by its draft ID. Use this to send a draft that was previously created with gmail_create_draft and reviewed — it sends the draft as stored and removes it from the Drafts folder (no orphaned draft). Returns the sent message metadata. The account's Gmail signature is added only if the draft is not already signed: a draft written by gmail_create_draft or gmail_update_draft carries it already and is sent unchanged, reporting already_present. Pass signature: false to send a draft without adding one. The response's signature field reports what happened. A draft holding files is signed in its text part and its files are sent as they are. A draft in any other shape is sent untouched and reports skipped_unsupported_draft.
Parameters
draft_idstring/ The Gmail draft ID to sendsignatureboolean/ Set false to send without the account's Gmail signature, and to skip the lookup entirely. Defaults to true.gws-mcp__docs_getGet the content of a Google Doc. Three modes: "text" (default) returns plain text — use for reading/summarizing. "index" returns text with startIndex/endIndex — use before positional edits (insertText at index, deleteContentRange). "full" returns the raw API response — use only for debugging or style operations. To read part of a long document, pass start_index/end_index — the SAME character indices that mode "index" reports and docs_batch_update consumes. A ranged response also reports totalEndIndex, clipped, and nextStartIndex (when more content follows), so a caller can page instead of guessing whether it saw everything. Without a range, behaviour is unchanged and the whole document is returned.
Parameters
modestring/ "text" (default): plain text. "index": text with character positions for edits. "full": raw API response.end_indexnumber/ Return content up to this character index (exclusive), same index space as start_index. Omit to read to the end.document_idstring/ The Google Docs document ID (from the URL)start_indexnumber/ Return content from this character index (inclusive), in the same index space mode "index" reports and docs_batch_update consumes — a document body starts at index 1. Omit to read from the start. Not valid with mode "full".gws-mcp__sheets_createCreate a new Google Sheets spreadsheet.
Parameters
titlestring/ Title for the new spreadsheetheadersarray/ Optional header row values, e.g., ["Name", "Email", "Date"]gws-mcp__sheets_add_tabAdd a new tab (sheet) to an existing Google Sheets spreadsheet. Use sheets_create to make a whole new spreadsheet file; use this to add a tab inside one. Returns the new tab's sheetId and title.
Parameters
titlestring/ Title for the new tabheadersarray/ Optional header row values written to row 1 of the new tab, e.g., ["Name", "Email", "Date"]spreadsheet_idstring/ The spreadsheet IDgws-mcp__sheets_deleteDelete a Google Sheets spreadsheet. This permanently removes the file from Drive.
Parameters
spreadsheet_idstring/ The spreadsheet ID to deletegws-mcp__docs_writeInsert text at the beginning of a Google Doc. To append or edit at a specific position, use docs_get (mode 'index') then docs_batch_update.
Parameters
textstring/ Text content to write to the documentdocument_idstring/ The Google Docs document IDgws-mcp__docs_createCreate a new Google Doc.
Parameters
titlestring/ Title for the new documentgws-mcp__docs_deleteDelete a Google Doc. This permanently removes the document from Drive.
Parameters
document_idstring/ The document ID to deletegws-mcp__gmail_searchSearch Gmail messages using Gmail search syntax. Returns matching messages with flattened from/to/subject/date fields plus snippet and labels. Supports queries like "from:[email protected]", "subject:proposal", "after:2024/01/01", "has:attachment", "label:important".
Parameters
querystring/ Gmail search query (e.g., "from:[email protected] subject:Q4 proposal", "is:unread after:2024/06/01")max_resultsnumber/ Maximum number of messages to return (default: 10)gws-mcp__gmail_listList recent emails from the inbox. Optionally filter by label. Returns message IDs with flattened from/to/subject/date fields plus snippet and labels.
Parameters
labelstring/ Label to filter by (e.g., "INBOX", "SENT", "STARRED", "IMPORTANT", or custom label). Defaults to INBOX.max_resultsnumber/ Maximum number of messages to return (default: 10)gws-mcp__gmail_delete_draftPermanently delete a Gmail draft by its draft ID. This does not move the draft to Trash — it is removed immediately. Use gmail_send_draft to send a draft instead of deleting it.
Parameters
draft_idstring/ The Gmail draft ID to deletegws-mcp__gmail_forwardForward an existing email to another recipient. The account's Gmail signature is appended automatically, so do not write a sign-off or signature in the body yourself; pass signature: false to send without it. The signature goes in the message's HTML part, so a plain-text body is sent as multipart/alternative when the account has one. The response's signature field reports what happened.
Parameters
tostring/ Recipient email address to forward tobodystring/ Optional plain-text note included above the forwarded messagehtml_bodystring/ Optional HTML note included above the forwarded message. The forward goes out with both an HTML part and a plain-text part, and the forwarded block is formatted with Gmail styling. Provide body or html_body, not both.signatureboolean/ Set false to send without the account's Gmail signature, and to skip the lookup entirely. Defaults to true.message_idstring/ The Gmail message ID to forwardattachmentsarray/ Drive files to send with the message, at most 10, 25 MB in total. Each entry is a Drive file id as a string, or {"file_id": "<id>", "as": "<format>"} to send a Google Doc, Sheet or Slides deck as a file. Formats: Docs pdf, docx, txt, html, md, rtf, odt, epub; Sheets pdf, xlsx, csv, tsv, html, ods; Slides pdf, pptx, txt, odp. csv and tsv export one tab: add "tab": "<title>" to choose it, otherwise the first tab goes, and the response says which. A Sheet as html arrives as a .zip of pages, one per tab; Slides as txt is the slides' visible text only. Google Docs, Sheets and Slides passed by id alone are sent as links in the message, the way Gmail does, not as files. Other Drive files are attached as they are. Files come from Drive only: to send a file, put it in Drive and pass its id. Two attachments may not share a filename. To show an image in the body rather than at the bottom, reference it in html_body as <img src="cid:FILENAME">, where FILENAME is its Drive filename with any character other than letters, digits, dot, dash and underscore replaced by an underscore. Sharing a Drive link is the sender's responsibility; nothing here changes who can open a file. The response's attachments field reports each file as attached, inline, linked or exported.include_original_attachmentsboolean/ The original message's own attachments are forwarded with it, counted in the 25 MB limit. Set false to forward the text alone. Defaults to true.gws-mcp__gmail_readRead a specific email message by its ID. By default returns the full message including headers, body, and metadata. Use text_only for a compact view (flattened headers, decoded text body, attachment metadata) that avoids large MIME/base64 payloads.
Parameters
text_onlyboolean/ Return a compact view instead of the raw MIME payload: flattened from/to/cc/subject/date headers, the decoded text/plain body (falls back to tag-stripped text/html), and attachment metadata (partId, filename, mimeType, attachmentId). Recommended for triage: it avoids base64 attachment data overflowing the response.message_idstring/ The Gmail message ID to readmax_body_charsnumber/ Truncate the returned body text to this many characters (adds a truncation marker). Implies text_only.gws-mcp__gmail_save_attachment_to_driveSave a Gmail attachment directly to Google Drive. Use gmail_read first to get attachment metadata (filename, mimeType, attachmentId) from the message parts. The file is fetched from Gmail and uploaded to Drive server-side — no base64 data flows through the conversation. Returns the Drive file metadata including a web link.
Parameters
filenamestring/ Filename to save as in Drive (e.g., 'report.xlsx')message_idstring/ The Gmail message ID that contains the attachmentattachment_idstring/ The attachment ID from the message part's body.attachmentId fieldparent_folder_idstring/ Optional Drive folder ID to save into. If omitted, saves to the root of My Drive.gws-mcp__calendar_list_eventsList upcoming events from a Google Calendar. By default returns a compact view per event: id, title, start/end, location, plain-text description (HTML stripped, truncated), organizer email, attendee count plus your own response status, the full attendee roster when the meeting has 10 or fewer people, a recurring flag, attachments, and the video join link (Meet or conference-data providers like Zoom). Only large-meeting rosters, reminders, and raw HTML are dropped — use full for the raw Calendar API payload, or calendar_get_event for one event's complete detail.
Parameters
fullboolean/ Return the raw Calendar API response instead of the compact view: full attendee rosters, reminders, conferenceData, htmlLink, and original (often HTML) descriptions. On busy calendars this can be very large — prefer the default compact view for triage and agenda use.querystring/ Free-text search query to filter eventstime_maxstring/ End of time range (ISO 8601, e.g., "2024-06-30T23:59:59Z"). Defaults to 7 days from now.time_minstring/ Start of time range (ISO 8601, e.g., "2024-06-01T00:00:00Z"). Defaults to now.calendar_idstring/ Calendar ID (default: "primary" for the user's main calendar)max_resultsnumber/ Maximum number of events to return (default: 20)max_description_charsnumber/ In the compact view, truncate each event's plain-text description to this many characters (default: 500; adds a truncation marker). Set to 0 to omit descriptions entirely. Ignored when full is true.gws-mcp__gmail_create_labelCreate a Gmail label, or find it if it already exists. Nested labels use '/' in the name (e.g. 'Alerts/Invoices'). Returns the label including its ID, which can be used with gmail_label_message; when a label with that name already exists the existing label is returned as found (existed: true), so it is safe to call once per run without listing labels first.
Parameters
namestring/ The label name to creategws-mcp__gmail_create_draftCreate a draft email in Gmail without sending it. The draft can be reviewed and sent later from Gmail. Returns the draft ID and a link to open it in Gmail. Accepts a plain-text body, an HTML html_body, or both — HTML is stored as multipart/alternative with a plain-text fallback. The account's Gmail signature is added to the draft automatically, as Gmail's own Compose does, so do not write a sign-off or signature in the body yourself; pass signature: false to leave it out. The signature goes in the draft's HTML part, so a plain-text body is stored as multipart/alternative when the account has one. The response's signature field reports what happened.
Parameters
ccstring/ CC recipients, comma-separatedtostring/ Recipient email address(es), comma-separatedbccstring/ BCC recipients, comma-separatedbodystring/ Plain-text email body. When html_body is also given, this becomes the text/plain alternative part shown by plain-text clients. One of body or html_body is required.subjectstring/ Email subject linehtml_bodystring/ HTML email body. The message is sent as multipart/alternative with a text/plain fallback part (body if provided, otherwise text derived from the HTML), so plain-text clients still render something readable.signatureboolean/ Set false to write the draft without the account's Gmail signature, and to skip the lookup entirely. Defaults to true.attachmentsarray/ Drive files to send with the message, at most 10, 25 MB in total. Each entry is a Drive file id as a string, or {"file_id": "<id>", "as": "<format>"} to send a Google Doc, Sheet or Slides deck as a file. Formats: Docs pdf, docx, txt, html, md, rtf, odt, epub; Sheets pdf, xlsx, csv, tsv, html, ods; Slides pdf, pptx, txt, odp. csv and tsv export one tab: add "tab": "<title>" to choose it, otherwise the first tab goes, and the response says which. A Sheet as html arrives as a .zip of pages, one per tab; Slides as txt is the slides' visible text only. Google Docs, Sheets and Slides passed by id alone are sent as links in the message, the way Gmail does, not as files. Other Drive files are attached as they are. Files come from Drive only: to send a file, put it in Drive and pass its id. Two attachments may not share a filename. To show an image in the body rather than at the bottom, reference it in html_body as <img src="cid:FILENAME">, where FILENAME is its Drive filename with any character other than letters, digits, dot, dash and underscore replaced by an underscore. Sharing a Drive link is the sender's responsibility; nothing here changes who can open a file. The response's attachments field reports each file as attached, inline, linked or exported.gws-mcp__gmail_list_filtersList all Gmail filters (settings > filters) with their criteria and actions. Read-only: use it to see what automation a mailbox already applies to incoming mail, for example when triaging why a message was archived or labelled before it was seen. This connector cannot create or delete filters, and neither can a raw API call through gws_run — Google accepts only the gmail.settings.basic scope on those, which is not granted. Filters must be changed in Gmail's own settings.
gws-mcp__calendar_get_eventGet details of a specific calendar event by its event ID. Returns the full event (all attendees, reminders, conference data), with the description converted to plain text; use full for the original (often HTML) description.
Parameters
fullboolean/ Keep the description exactly as stored (often multi-KB HTML from Zoom/scheduling tools) instead of converting it to plain text.event_idstring/ The calendar event IDcalendar_idstring/ Calendar ID (default: "primary")gws-mcp__calendar_create_eventCreate a new calendar event. Supports setting title, time, attendees, description, location, and Google Meet links.
Parameters
endstring/ End time in ISO 8601 format (e.g., "2024-06-15T15:00:00-07:00")startstring/ Start time in ISO 8601 format (e.g., "2024-06-15T14:00:00-07:00")summarystring/ Event titleadd_meetboolean/ Attach a Google Meet video conference link to the event (default: false)locationstring/ Event location (physical address or room name)attendeesstring/ Comma-separated email addresses of attendeescalendar_idstring/ Calendar ID (default: "primary")descriptionstring/ Event description or agendasend_updatesstring/ Who to send invite notifications to (default: "all")gws-mcp__calendar_update_eventUpdate an existing calendar event. Only provided fields are changed.
Parameters
endstring/ New end time (ISO 8601)startstring/ New start time (ISO 8601)summarystring/ New event titleevent_idstring/ The calendar event ID to updatelocationstring/ New event locationattendeesstring/ Comma-separated email addresses (replaces existing attendees)calendar_idstring/ Calendar ID (default: "primary")descriptionstring/ New event descriptionsend_updatesstring/ Who to send update notifications to (default: "all")gws-mcp__calendar_delete_eventDelete a calendar event by its event ID.
Parameters
event_idstring/ The calendar event ID to deletecalendar_idstring/ Calendar ID (default: "primary")send_updatesstring/ Who to send cancellation notifications to (default: "all")gws-mcp__calendar_freebusyCheck availability (free/busy) for one or more people over a time range. Useful for finding open slots to schedule meetings.
Parameters
emailsstring/ Comma-separated email addresses to check availability fortime_maxstring/ End of the time range to check (ISO 8601)time_minstring/ Start of the time range to check (ISO 8601)gws-mcp__contacts_searchSearch Google Contacts by name, email, or phone number. Returns matching contacts with their details.
Parameters
querystring/ Search query (name, email, phone number, or company name)max_resultsnumber/ Maximum number of contacts to return (default: 10)gws-mcp__contacts_getGet full details of a specific contact by their resource name.
Parameters
resource_namestring/ Contact resource name (e.g., "people/c1234567890")gws-mcp__contacts_listList contacts from the user's Google Contacts. Returns names, emails, phone numbers, and organizations.
Parameters
max_resultsnumber/ Maximum number of contacts to return (default: 20)gws-mcp__sheets_batch_updateApply a batch of structural and formatting changes to a spreadsheet, in ONE atomic call. This is the full Google Sheets batchUpdate pass-through and the escape hatch beneath the job-shaped tools: reach for sheets_format_range or sheets_format_table first, and come here for anything they do not cover (merges, borders, banding, copyPaste, inserting or deleting COLUMNS, duplicateSheet, protected ranges). TWO PROPERTIES THAT BITE. (1) Ranges here are a GridRange, which is 0-BASED and END-EXCLUSIVE: spreadsheet row 7 is startRowIndex 6, endRowIndex 7. That is the opposite convention from the A1 notation used to write the values. (2) The batch is ATOMIC: if one request is rejected the whole batch applies nothing, and the error names the failing request's index. Build the whole pass as one batch, fix the named index, re-send the whole thing. Note that empty reply objects are normal for formatting requests and mean ACCEPTED, not applied to what you meant.
Parameters
requestsarray/ Array of Sheets API batchUpdate request objects, applied in order. Common ones: updateSheetProperties (freeze rows, hide gridlines, tab colour), updateDimensionProperties (column width, row height), insertDimension and deleteDimension (add or remove rows and columns, and these SHIFT every index after them, so put them first), repeatCell (fonts, colour, wrap, alignment, number format), mergeCells, updateBorders, copyPaste, duplicateSheet. Example: [{ "updateDimensionProperties": { "range": { "sheetId": 0, "dimension": "COLUMNS", "startIndex": 1, "endIndex": 4 }, "properties": { "pixelSize": 400 }, "fields": "pixelSize" } }]spreadsheet_idstring/ The spreadsheet IDgws-mcp__sheets_format_rangeSet fonts, colours, wrapping, alignment, padding, number formats and merges on spreadsheet cells. Takes a LIST of formatting instructions and applies all of them in one atomic call, so a whole formatting pass is one tool call rather than one call per range. Colours are ordinary hex (#RRGGBB) and are converted for you. Only the properties you actually set are touched; anything you leave out keeps whatever the cell already had. If you want a table to simply look readable, use sheets_format_table instead, which does the whole standard pass. For anything not covered here (borders, banding, protected ranges) use sheets_batch_update.
Parameters
formatsarray/ One or more formatting instructions, each applied to one or more ranges. Every field except "ranges" is optional and only the ones you set are changed. Fields: ranges (array of A1 strings, required, e.g. ["TabName!A1:D1", "TabName!A20:D20"]), bold (boolean), italic (boolean), strikethrough (boolean), font_family (string, e.g. "Inter"), font_size (number), text_color ("#RRGGBB"), background_color ("#RRGGBB"), horizontal_align (LEFT|CENTER|RIGHT), vertical_align (TOP|MIDDLE|BOTTOM), wrap (OVERFLOW|CLIP|WRAP), number_format (a pattern string such as "#,##0.00", "yyyy-mm-dd" or "0.0%"), padding ({top,right,bottom,left} in pixels), merge (boolean, merges each named range into a single cell). Example: [{"ranges":["TabName!A1:D1"],"bold":true,"background_color":"#F1F1F1"},{"ranges":["TabName!A2:D99"],"wrap":"WRAP"}]spreadsheet_idstring/ The spreadsheet IDgws-mcp__gmail_replyReply to an existing email in its thread. The reply is addressed the way Gmail's own Reply does it: to the original's Reply-To when it has one, otherwise to its sender; when you sent the original yourself, to the people it was sent to. Pass reply_all: true to include everyone else on the original as well. The response reports the To and Cc the reply went to; when a Reply-To meant the original's sender was not among them, it also carries reply_to_used: true and original_from, so check to against who you meant to answer, or pass expected_to and the reply is refused instead of sent when it would go anywhere else. A body is required: pass body or html_body. The account's Gmail signature is appended automatically, so do not write a sign-off or signature in the body yourself; pass signature: false to send without it. The signature goes in the message's HTML part, so a plain-text body is sent as multipart/alternative when the account has one. The response's signature field reports what happened.
Parameters
bodystring/ Reply body text (plain). One of body or html_body is required.html_bodystring/ HTML reply body. The reply goes out with both an HTML part and a plain-text part derived from it, and the original message is quoted beneath with Gmail styling. Provide body or html_body, not both; one of them is required.reply_allboolean/ Default false: the reply goes to one party, the original's Reply-To or sender (or, for a message you sent, its recipients). Set true to reply to everyone: the original's other To recipients are added to To and its Cc recipients to Cc, without your own addresses.signatureboolean/ Set false to send without the account's Gmail signature, and to skip the lookup entirely. Defaults to true.message_idstring/ The Gmail message ID to reply toattachmentsarray/ Drive files to send with the message, at most 10, 25 MB in total. Each entry is a Drive file id as a string, or {"file_id": "<id>", "as": "<format>"} to send a Google Doc, Sheet or Slides deck as a file. Formats: Docs pdf, docx, txt, html, md, rtf, odt, epub; Sheets pdf, xlsx, csv, tsv, html, ods; Slides pdf, pptx, txt, odp. csv and tsv export one tab: add "tab": "<title>" to choose it, otherwise the first tab goes, and the response says which. A Sheet as html arrives as a .zip of pages, one per tab; Slides as txt is the slides' visible text only. Google Docs, Sheets and Slides passed by id alone are sent as links in the message, the way Gmail does, not as files. Other Drive files are attached as they are. Files come from Drive only: to send a file, put it in Drive and pass its id. Two attachments may not share a filename. To show an image in the body rather than at the bottom, reference it in html_body as <img src="cid:FILENAME">, where FILENAME is its Drive filename with any character other than letters, digits, dot, dash and underscore replaced by an underscore. Sharing a Drive link is the sender's responsibility; nothing here changes who can open a file. The response's attachments field reports each file as attached, inline, linked or exported.expected_tostring/ Optional guard: the address or addresses, comma separated, you mean this reply to reach. The reply is refused before anything is sent unless it would go to exactly these (every To address, and every Cc address under reply_all; order and case are ignored). Use it when it matters who receives the reply: the recipients come from the original message's headers, and a Reply-To can name someone other than the sender you see.gws-mcp__gmail_update_draftUpdate an existing draft email in Gmail. This fully replaces the draft's message content (Gmail API does not support partial edits), attachments included: files the draft held are dropped unless they are passed again in attachments. If thread_id is omitted, the tool preserves the existing thread automatically. The account's Gmail signature is added to the draft automatically, as Gmail's own Compose does, so do not write a sign-off or signature in the body yourself; pass signature: false to leave it out. The signature goes in the draft's HTML part, so a plain-text body is stored as multipart/alternative when the account has one. The response's signature field reports what happened.
Parameters
ccstring/ CC recipients, comma-separatedtostring/ Recipient email address(es), comma-separatedbccstring/ BCC recipients, comma-separatedbodystring/ Plain-text email body. When html_body is also given, this becomes the text/plain alternative part shown by plain-text clients. One of body or html_body is required.subjectstring/ Email subject linedraft_idstring/ The Gmail draft ID to updatehtml_bodystring/ HTML email body. The message is sent as multipart/alternative with a text/plain fallback part (body if provided, otherwise text derived from the HTML), so plain-text clients still render something readable.signatureboolean/ Set false to write the draft without the account's Gmail signature, and to skip the lookup entirely. Defaults to true.thread_idstring/ Thread ID to preserve threading. If omitted, the existing draft's thread is preserved automatically.attachmentsarray/ Drive files to send with the message, at most 10, 25 MB in total. Each entry is a Drive file id as a string, or {"file_id": "<id>", "as": "<format>"} to send a Google Doc, Sheet or Slides deck as a file. Formats: Docs pdf, docx, txt, html, md, rtf, odt, epub; Sheets pdf, xlsx, csv, tsv, html, ods; Slides pdf, pptx, txt, odp. csv and tsv export one tab: add "tab": "<title>" to choose it, otherwise the first tab goes, and the response says which. A Sheet as html arrives as a .zip of pages, one per tab; Slides as txt is the slides' visible text only. Google Docs, Sheets and Slides passed by id alone are sent as links in the message, the way Gmail does, not as files. Other Drive files are attached as they are. Files come from Drive only: to send a file, put it in Drive and pass its id. Two attachments may not share a filename. To show an image in the body rather than at the bottom, reference it in html_body as <img src="cid:FILENAME">, where FILENAME is its Drive filename with any character other than letters, digits, dot, dash and underscore replaced by an underscore. Sharing a Drive link is the sender's responsibility; nothing here changes who can open a file. The response's attachments field reports each file as attached, inline, linked or exported.gws-mcp__gmail_mark_readMark one or more Gmail messages as read by removing the UNREAD label. With add_labels or remove_labels given, it applies exactly those and nothing else, so add_labels alone does not mark anything read and add_labels ["UNREAD"] marks unread. Pass message_id for a single message (returns the modified message) or message_ids for a batch (up to 1000, single API call via users.messages.batchModify).
Parameters
add_labelsarray/ Label IDs to add (e.g., ["STARRED", "IMPORTANT"]). Optional.message_idstring/ A single Gmail message ID to modify. Provide either this or message_ids.message_idsarray/ Multiple Gmail message IDs to modify in one batch call (max 1000). Provide either this or message_id.remove_labelsarray/ Label IDs to remove (e.g., ["UNREAD", "INBOX"]). Defaults to ["UNREAD"] if neither add_labels nor remove_labels is provided.gws-mcp__tasks_create_tasklistCreate a new task list. A task list is the container tasks live in — use this when a workflow needs its own list rather than adding to '@default'.
Parameters
titlestring/ Title of the new task list, as it appears in the Tasks UIgws-mcp__drive_rename_fileRename a file or folder in Google Drive. Changes the name only: content, location, and sharing are untouched. Works on any Drive item the account can edit, including Docs, Sheets, Slides, and folders.
Parameters
namestring/ The new namefile_idstring/ The Google Drive file or folder ID to renamegws-mcp__drive_copy_fileCopy a file in Google Drive, naming the copy in the same call. Use this to instantiate a template instead of rebuilding it: the copy carries the original's tabs, formatting, and formulas, so it cannot drift from the template the way a hand-rebuild does. Folders cannot be copied: Drive rejects that with a 403 "This file cannot be copied by the user", which is a limitation of the API and not a permissions problem to retry.
Parameters
namestring/ Name for the new copyfile_idstring/ The Google Drive file ID to copyparent_idstring/ Folder ID to place the copy in (optional, defaults to the original's folder)gws-mcp__docs_batch_updateApply batch updates to a Google Doc. Supports inserting text, replacing text, deleting content ranges, and other document modifications. Uses the Google Docs API batchUpdate format.
Parameters
requestsarray/ Array of Google Docs API request objects, applied in order. Common shapes, with the nesting exactly as the API requires: insertText { insertText: { location: { index: 1 }, text: "Hello" } }; replaceAllText { replaceAllText: { containsText: { text: "old", matchCase: true }, replaceText: "new" } } — note matchCase sits INSIDE containsText, never at the top level of replaceAllText; deleteContentRange { deleteContentRange: { range: { startIndex: 1, endIndex: 10 } } }; updateTextStyle { updateTextStyle: { range: { startIndex: 1, endIndex: 10 }, textStyle: { bold: true }, fields: "bold" } } — fields is required and names which textStyle properties to apply. Indices are the character positions docs_get mode "index" reports.document_idstring/ The Google Docs document IDgws-mcp__gmail_sendSend a new email via Gmail. Composes and sends an email message to the specified recipients. Accepts a plain-text body, an HTML html_body, or both — HTML is sent as multipart/alternative with a plain-text fallback. The account's Gmail signature is appended automatically, so do not write a sign-off or signature in the body yourself; pass signature: false to send without it. The signature goes in the message's HTML part, so a plain-text body is sent as multipart/alternative when the account has one. The response's signature field reports what happened.
Parameters
ccstring/ CC recipients, comma-separatedtostring/ Recipient email address(es), comma-separatedbccstring/ BCC recipients, comma-separatedbodystring/ Plain-text email body. When html_body is also given, this becomes the text/plain alternative part shown by plain-text clients. One of body or html_body is required.subjectstring/ Email subject linehtml_bodystring/ HTML email body. The message is sent as multipart/alternative with a text/plain fallback part (body if provided, otherwise text derived from the HTML), so plain-text clients still render something readable.signatureboolean/ Set false to send without the account's Gmail signature, and to skip the lookup entirely. Defaults to true.attachmentsarray/ Drive files to send with the message, at most 10, 25 MB in total. Each entry is a Drive file id as a string, or {"file_id": "<id>", "as": "<format>"} to send a Google Doc, Sheet or Slides deck as a file. Formats: Docs pdf, docx, txt, html, md, rtf, odt, epub; Sheets pdf, xlsx, csv, tsv, html, ods; Slides pdf, pptx, txt, odp. csv and tsv export one tab: add "tab": "<title>" to choose it, otherwise the first tab goes, and the response says which. A Sheet as html arrives as a .zip of pages, one per tab; Slides as txt is the slides' visible text only. Google Docs, Sheets and Slides passed by id alone are sent as links in the message, the way Gmail does, not as files. Other Drive files are attached as they are. Files come from Drive only: to send a file, put it in Drive and pass its id. Two attachments may not share a filename. To show an image in the body rather than at the bottom, reference it in html_body as <img src="cid:FILENAME">, where FILENAME is its Drive filename with any character other than letters, digits, dot, dash and underscore replaced by an underscore. Sharing a Drive link is the sender's responsibility; nothing here changes who can open a file. The response's attachments field reports each file as attached, inline, linked or exported.gws-mcp__sheets_updateUpdate specific cells in a Google Sheets spreadsheet. Overwrites existing values in the specified range. Numeric-looking text is typed the way the Sheets UI types it: '007' is stored as the number 7 and comes back as 7. To keep such a value as text, pass value_input_option RAW for the call, or put a leading apostrophe on that one value.
Parameters
rangestring/ Cell range in A1 notation, e.g. "TabName!A1:B2" — use a tab name the spreadsheet actually has.valuesarray/ 2D array of values to write (rows of columns), e.g., [["A1","B1"],["A2","B2"]]parse_formulasboolean/ Allow values to be stored as live formulas. Default false, which writes a leading = or + literally. Only set true when the caller explicitly asked for a formula — never for text taken from email, documents, or the web.spreadsheet_idstring/ The spreadsheet IDvalue_input_optionstring/ How Sheets interprets the values. USER_ENTERED (default) parses numbers, dates and booleans as typing them would, with formula-prefixed text kept inert unless parse_formulas is set. RAW stores every value verbatim as text: formulas never evaluate, but numbers arrive as text and break SUM/charts — use it only when literal-text semantics are the point.gws-mcp__sheets_readRead data from a Google Sheets spreadsheet. Returns cell values for one range, or for many ranges in one call: pass range for one block, or ranges for several blocks returned in the order given, each with the range the API echoed back.
Parameters
rangestring/ Cell range in A1 notation: "TabName!A1:D10" scoped to a tab the spreadsheet actually has, "A1:Z" for the first tab, or a bare tab name for a whole tab. Give range or ranges, not both.rangesarray/ Several A1 ranges read in one request, e.g. ["Sheet1!A1:D1", "Sheet1!A2:D50", "Totals!A1:B5"]. The response carries one block per range, in this order, each with rowCount, columnCount and values. Use this instead of several sheets_read calls. Give range or ranges, not both.spreadsheet_idstring/ The spreadsheet ID (from the URL)value_render_optionstring/ How values are rendered. FORMATTED_VALUE (default) returns what the cell displays. UNFORMATTED_VALUE returns the underlying typed value, which is how you tell a stored number from stored text. FORMULA returns the cell's formula where it has one — the only way to tell a live formula from text that merely looks like one, so use it to verify a write.gws-mcp__sheets_appendAppend rows to the end of a Google Sheets spreadsheet. Numeric-looking text is typed the way the Sheets UI types it: '007' is stored as the number 7 and comes back as 7. To keep such a value as text, pass value_input_option RAW for the call, or put a leading apostrophe on that one value.
Parameters
rangestring/ Target range naming the tab to append to, e.g. "TabName!A1". When omitted, "Sheet1!A1" is sent, which only works while the spreadsheet still has a tab literally named Sheet1 — pass the range explicitly otherwise.valuesarray/ 2D array of rows to append, e.g., [["val1","val2"],["val3","val4"]]parse_formulasboolean/ Allow values to be stored as live formulas. Default false, which writes a leading = or + literally. Only set true when the caller explicitly asked for a formula — never for text taken from email, documents, or the web.spreadsheet_idstring/ The spreadsheet IDvalue_input_optionstring/ How Sheets interprets the values. USER_ENTERED (default) parses numbers, dates and booleans as typing them would, with formula-prefixed text kept inert unless parse_formulas is set. RAW stores every value verbatim as text: formulas never evaluate, but numbers arrive as text and break SUM/charts — use it only when literal-text semantics are the point.gws-mcp__tasks_createCreate one task, or many tasks in one call. Pass title (with notes and due) for one task, or tasks for several: each task is inserted into the shared task list, or into its own tasklist_id where it names one, and the response carries one outcome per task in order (results[]: index, title, ok, id or error) so a partial batch is visible. Create every task of a run in one call rather than one call per task. Give title or tasks, not both.
Parameters
duestring/ Due date in RFC 3339 format (e.g., 2026-03-28T00:00:00Z)notesstring/ Notes/description for the tasktasksarray/ Several tasks created in one request, in this order. Each carries its own title, optional notes and due, and may name its own tasklist_id; otherwise the shared tasklist_id applies. Give title or tasks, not both.titlestring/ Title of the task, for the one-task case. Give title or tasks, not both.tasklist_idstring/ The task list ID (or '@default' for the default list). The shared list for a tasks batch.gws-mcp__contacts_createCreate a new contact in Google Contacts.
Parameters
namestring/ Full name of the contactemailstring/ Email addressnotesstring/ Notes about the contactphonestring/ Phone numbertitlestring/ Job titlecompanystring/ Company or organization namegws-mcp__contacts_updateUpdate an existing contact. Only provided fields are changed.
Parameters
namestring/ Updated full nameemailstring/ Updated email addressnotesstring/ Updated notesphonestring/ Updated phone numbertitlestring/ Updated job titlecompanystring/ Updated company nameresource_namestring/ Contact resource name (e.g., "people/c1234567890")gws-mcp__contacts_deleteDelete a contact from Google Contacts.
Parameters
resource_namestring/ Contact resource name (e.g., "people/c1234567890")gws-mcp__contacts_directory_searchSearch the company's Google Workspace directory (all users in the organization). Useful for finding colleagues' contact info.
Parameters
querystring/ Search query (name, email, or department)max_resultsnumber/ Maximum number of results (default: 10)gws-mcp__drive_create_folderCreate a new folder in Google Drive.
Parameters
namestring/ Name for the new folderparent_idstring/ Parent folder ID to create inside (optional, defaults to root)gws-mcp__drive_searchSearch for files in Google Drive. Returns file names, IDs, types, and modification dates.
Parameters
querystring/ Search query (Drive query syntax, e.g., "name contains 'report'" or "mimeType='application/vnd.google-apps.spreadsheet'")page_sizenumber/ Maximum number of results to return (default: 20)gws-mcp__drive_read_fileRead the text content of any file in Google Drive by file ID. Supports Google Docs, Sheets, Slides, Office formats (.docx/.xlsx/.pptx — auto-converted), and plain text files. Returns extracted text directly — no local filesystem needed.
Parameters
file_idstring/ The Google Drive file ID to readgws-mcp__slides_getGet the content of a Google Slides presentation. Returns slide objectIds, placeholder types (TITLE/BODY/SUBTITLE), and text content — stripped of layout/styling data to fit context windows. Use the returned objectIds with slides_batch_update for edits.
Parameters
presentation_idstring/ The Google Slides presentation ID (from the URL)gws-mcp__slides_createCreate a new Google Slides presentation. Returns the presentationId and a placeholder_map for each slide mapping placeholder types (TITLE, BODY, SUBTITLE) to their objectIds — use these with slides_batch_update insertText.
Parameters
titlestring/ Title for the new presentationgws-mcp__slides_batch_updateApply batch updates to a Google Slides presentation. Supports inserting text, replacing text, creating slides, deleting objects, and other modifications. Uses the Google Slides API batchUpdate format.
Parameters
requestsarray/ Array of update request objects. Examples: createSlide ({ createSlide: { slideLayoutReference: { predefinedLayout: "TITLE_AND_BODY" } } }), insertText ({ insertText: { objectId: "slideId", text: "Hello", insertionIndex: 0 } }), replaceAllText ({ replaceAllText: { containsText: { text: "old" }, replaceText: "new" } }), deleteObject ({ deleteObject: { objectId: "elementId" } })presentation_idstring/ The presentation IDgws-mcp__slides_deleteDelete a Google Slides presentation. This permanently removes the file from Drive.
Parameters
presentation_idstring/ The presentation ID to deletegws-mcp__tasks_listList all task lists for the authenticated user.
gws-mcp__tasks_list_tasksList tasks in a specific task list. Returns task titles, statuses, due dates, and notes.
Parameters
show_hiddenboolean/ Include hidden/deleted tasks (default: false)tasklist_idstring/ The task list ID (use tasks_list to find IDs, or '@default' for the default list)show_completedboolean/ Include completed tasks (default: true)gws-mcp__tasks_updateUpdate an existing task's title, notes, or due date.
Parameters
duestring/ New due date in RFC 3339 formatnotesstring/ New notes for the tasktitlestring/ New title for the tasktask_idstring/ The task ID to updatetasklist_idstring/ The task list IDgws-mcp__tasks_completeMark a task as completed.
Parameters
task_idstring/ The task ID to mark as completetasklist_idstring/ The task list IDgws-mcp__tasks_deleteDelete a task from a task list.
Parameters
task_idstring/ The task ID to deletetasklist_idstring/ The task list IDgws-mcp__gws_runFALLBACK ONLY — use dedicated tools first (gmail_*, calendar_*, drive_*, sheets_*, docs_*, slides_*, contacts_*). Only use gws_run when no dedicated tool exists for the operation, e.g. Chat, Admin, Tasks, or advanced API calls not covered by other tools. Commands follow the pattern: gws <service> <resource> <method>.
Parameters
bodyobject/ Request body for create/update operations (alias for json_body)methodstring/ API method (e.g., list, get, create, update, delete)paramsobject/ Query parameters as key-value pairs (e.g., { pageSize: 10, q: "search query" })dry_runboolean/ Preview the request without executing itservicestring/ Google Workspace service (e.g., calendar, chat, admin, classroom, contacts, drive, gmail, sheets, docs, slides)page_allboolean/ Fetch all pages of results (default: false, max 10 pages)resourcestring/ API resource. Nested resources use dotted paths matching the API's REST structure — Gmail resources live under users (users.messages, users.drafts, users.labels, users.messages.attachments), not bare names like 'drafts'. Top-level examples: events (calendar), files (drive), spaces (chat).json_bodyobject/ Request body for create/update operations (alias: body)Connect
Add this to your MCP client config to access all integrations through the gateway.
{
"mcpServers": {
"datatorag": {
"url": "https://datatorag.com/mcp"
}
}
}