# Google Workspace (npm · @foreverworks/mcp-google)

Search and work in Gmail, Calendar, Drive, Docs, Sheets and Slides across several Google accounts.

- Trust score: 81/100 (high trust)
- Change this week: +4
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-11-05

## Components

- npm · `@foreverworks/mcp-google`: 81/100 (this document), [markdown](https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google.md), [page](https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google)

## Channel facts

- Registry: `npm`
- Package: `@foreverworks/mcp-google`
- Version: `0.2.0`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-11-05.

- **Supply Chain Security**: 99/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - No install/post-install scripts declared.
  - 7 of 37 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 100/100
  - Source repository is publicly reachable at the declared URL.
  - Cryptographically verified build provenance (signed, bound to foreverworks/mcps).
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 2 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 57/100
  - AI-judged instruction clarity (good).
  - Context-footprint check failed: tool/resource definitions use about 23539 tokens (~204/item across 115 items; 115 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 33/100
  - Stability observed for 10 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 93/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 80% of tool parameters carry a description.
- **Tool Safety**: 75/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - 0 of 9 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "delete_comment" implies "delete" and declares no destructiveHint at all, which the MCP spec reads as destructive by default.
  - An AI judge read all 116 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a current MCP spec version (2026-07-28).

## Install

### How do I install the Google Workspace MCP server?

Google Workspace runs locally as an npm package, launched with npx -y @foreverworks/mcp-google. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add com-foreverworks-google -- npx -y @foreverworks/mcp-google
```

### Cursor

```json
{
  "mcpServers": {
    "com-foreverworks-google": {
      "command": "npx",
      "args": [
        "-y",
        "@foreverworks/mcp-google"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "com-foreverworks-google": {
      "command": "npx",
      "args": [
        "-y",
        "@foreverworks/mcp-google"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add com-foreverworks-google -- npx -y @foreverworks/mcp-google
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "com-foreverworks-google": {
      "type": "local",
      "command": [
        "npx",
        "-y",
        "@foreverworks/mcp-google"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add com-foreverworks-google --command npx --arg -y --arg @foreverworks/mcp-google
```

### Hermes

```yaml
mcp_servers:
  com-foreverworks-google:
    command: "npx"
    args: ["-y", "@foreverworks/mcp-google"]
```

### Netclaw

```json
{
  "McpServers": {
    "com-foreverworks-google": {
      "Transport": "stdio",
      "Command": "npx",
      "Arguments": [
        "-y",
        "@foreverworks/mcp-google"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add com-foreverworks-google -t stdio -c npx -a -y @foreverworks/mcp-google
```

### Other

```json
{
  "mcpServers": {
    "com-foreverworks-google": {
      "command": "npx",
      "args": [
        "-y",
        "@foreverworks/mcp-google"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-11-05 (score 81, +1)

No change was recorded against any check on this day. Stability & Change Management went from 30 to 33. That category is still filling its 30-day observation window: 9 days of observed history at the previous scan, 10 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-11-03 (score 80, +27)

- [security improvement] Malware scan: unverified → pass
- [security improvement] Known CVEs: unverified → pass
- [functional improvement] Dependency health: unverified → 0.90

### 2026-11-02 (score 53, −26)

- [security regression] Malware scan: pass → unverified
- [security regression] Known CVEs: pass → unverified
- [functional regression] Dependency health: 0.90 → unverified
- [functional] Package version: 0.1.1 → 0.2.0

### 2026-11-01 (score 79, +16)

- [security improvement] Malware scan: unverified → pass

### 2026-10-31 (score 63, −15)

- [security regression] Malware scan: pass → unverified
- [functional] Package version: 0.1.0 → 0.1.1

### 2026-10-30 (score 78, +1)

No change was recorded against any check on this day. Stability & Change Management went from 10 to 13. That category is still filling its 30-day observation window: 3 days of observed history at the previous scan, 4 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-10-28 (score 77, +1)

No change was recorded against any check on this day. Stability & Change Management went from 3 to 7. That category is still filling its 30-day observation window: 1 days of observed history at the previous scan, 2 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-10-26 (score 76)

First indexed and scored.

## MCP tools (115)

### `add_chart` (~209 tokens)

Add a chart

A chart over a data range: the first column is the domain (x axis, pie labels), the other columns (or series_columns) the series.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `anchor_cell` (string, required): Where the chart's top-left corner sits, e.g. "Sheet1!H2".
- `data_range` (string, required): A1 range, e.g. "Sheet1!A1:D20", "B:B" or "2:2". No sheet name means the first sheet.
- `headers` (boolean): Whether the data's first row names the series.
- `series_columns` (array): Column letters to plot, e.g. ["C","E"]. Empty means every column after the first.
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.
- `title` (string)
- `type` (string, required)

### `add_comment` (~158 tokens)

Comment on a file

A new comment thread on a Drive file, as the account. `quoted` shows the text it is about beside it. Google Docs shows a comment made through the API on the file, not anchored to text (Google's limit); `anchor` is for file types whose viewer reads one. Everyone with access to the file sees it.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `anchor` (string): Drive's anchor JSON, for non-Google files.
- `content` (string, required)
- `file_id` (string, required): The file's id, or its URL.
- `quoted` (string): The passage the comment is about.

### `agenda` (~361 tokens)

Agenda

Events across EVERY account at once, in time order, for a window: today and the next days by default. Also returns `conflicts` (busy events that overlap, including across accounts, e.g. a work meeting over a dentist appointment), `probableDuplicates` (one appointment booked into both accounts as two events) and `pendingInvites` (invitations not yet answered). Recurring events are expanded into their instances; an event seen through both accounts is listed once, with `alsoOn`. Declined events are left out unless asked for. Each event carries the account, calendarId and eventId that get_event, update_event, delete_event and respond_to_invite need. include_tasks adds `tasksDue`: the Google Tasks not yet done that fall due on the window's days (a task has a due date, never a time).

Input parameters:

- `account` (string): Which account: a label from list_accounts, or "*" for every account at once (the default).
- `calendars`: "selected" (what the Calendar app shows, the default), "primary" (each account's own calendar only), "all", or a list of calendar ids or names.
- `days` (integer): Window length when `to` is empty; at most 366.
- `from` (string): YYYY-MM-DD or a time. Empty means today (UTC).
- `include_declined` (boolean)
- `include_tasks` (boolean): Also the open tasks due on these days, from the same accounts' Google Tasks.
- `limit` (integer)
- `query` (string): Free-text search over title, description, location and guests.
- `to` (string): Exclusive end. Empty means `days` after from.

### `append_rows` (~184 tokens)

Append rows

Rows after the last one holding data, keyed by the header, formatted like the row above. One atomic batch.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `header_row` (integer): The row that names the columns (1-based).
- `rows` (array, required): Objects keyed by the header row's names. Numbers and booleans are stored as such, "=..." as a formula, "YYYY-MM-DD" (or with THH:MM) as a date, any other string as text; null clears a cell. A key the…
- `sheet` (string): The sheet (tab) name, from list_sheets. Empty means the first sheet.
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.

### `append_to_document` (~177 tokens)

Append to a Google Doc

Markdown added after the document's last paragraph, in one batch.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `document_id` (string, required): The document's id, or its URL.
- `expected_revision` (string): The revisionId read_document returned. When given, the edit is refused if the document has changed since.
- `markdown` (string, required): Markdown: # headings, paragraphs, - and 1. lists (nested by indent), **bold**, *italic*, ~~strike~~, `code`, ``` fences, [links](url), | tables |.
- `tab` (string): A tab other than the first: its id (t.abc) or title, from get_document. Empty means the first tab.

### `append_values` (~246 tokens)

Append rows after a table

Rows after the table Sheets finds at an A1 range (values.append), as new rows (INSERT_ROWS) or over empty ones (OVERWRITE). Returns where they went. Not retried after a lost answer, since a retry would add them twice. append_rows is the variant keyed by a header row.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `include_values` (boolean)
- `insert_data` (string)
- `major_dimension` (string)
- `range` (string, required): Where the table is, e.g. "Sheet1!A1" or "Sheet1!A:E".
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.
- `value_input` (string): USER_ENTERED parses as if typed ("=SUM(A:A)" a formula, "2026-10-08" a date, "12.5" a number); RAW stores strings as text.
- `values` (array, required): Rows of cells (columns with major_dimension COLUMNS). null leaves a cell as it is; "" empties it.

### `batch_update_document` (~641 tokens)

Apply Docs API requests

Any Google Docs API batchUpdate: text, styles, bullets, tables (insert, merge, row and column, cell style), images by URL, headers, footers, footnotes, page and section setup, named ranges, tabs, replace all text, and the rest; local images through `images`. One atomic batch, checked against the API's schema before anything is sent (a wrong field is refused with the nearest right name). Requests apply in order, each to the document as the ones before left it: work from the end of the document backwards so earlier indices stay valid. A batch that names an index needs expected_revision (refused if the document changed) or target_revision (Docs carries the positions over later edits), from get_document or find_in_document. In a tab other than the first, put tabId in each location and range. Returns the new revisionId and the replies that carry ids or counts, by request position.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `document_id` (string, required): The document's id, or its URL.
- `expected_revision` (string): The revisionId these requests were computed from; the batch is refused if the document is no longer at it.
- `images` (object): Local PNG, JPEG or GIF images by name; use "image:<name>" as the image URL in a request (insertInlineImage.uri, createImage.url, replaceImage, replaceAllShapesWithImage.imageUrl). Each is uploaded to…
- `requests` (array, required): docs batchUpdate requests, applied in order, all or none: each an object with ONE request kind, e.g. {"insertText": {"location": {"index": 1}, "text": "Hello\n"}}. Kinds: createFooter, deleteFooter,…
- `target_revision` (string): Instead: apply as of this revisionId, with Docs moving the positions over edits made since.

### `batch_update_presentation` (~587 tokens)

Apply Slides API requests

Any Google Slides API batchUpdate: slides (create, duplicate, reorder, delete, background), shapes, text boxes, images (by URL, or local files through `images`), videos, lines, tables (rows, columns, merges, cell and border styles), Sheets charts, text and its styles, bullets, replace all text or shapes with images, transforms, z-order, alt text and the rest. One atomic batch, checked against the API's schema before anything is sent. Ids you choose for new objects (5 to 50 characters, starting with a letter or _) can be referred to by later requests in the same batch. expected_revision (from get_presentation) refuses the batch if the deck changed. Returns the new revisionId and the replies that carry ids, by request position.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `expected_revision` (string): The revisionId these requests were computed from; the batch is refused if the deck is no longer at it.
- `images` (object): Local PNG, JPEG or GIF images by name; use "image:<name>" as the image URL in a request (insertInlineImage.uri, createImage.url, replaceImage, replaceAllShapesWithImage.imageUrl). Each is uploaded to…
- `presentation_id` (string, required): The presentation's id, or its URL.
- `requests` (array, required): slides batchUpdate requests, applied in order, all or none: each an object with ONE request kind, e.g. {"createSlide": {"objectId": "slide_intro", "slideLayoutReference": {"predefinedLayout": "TITLE_…

### `batch_update_spreadsheet` (~639 tokens)

Apply Sheets API requests

Any Google Sheets API batchUpdate: sheets (add, duplicate, delete, properties), rows and columns (insert, delete, move, resize, group, hide), cells (values with formats, repeat a format, borders, merges, copy/cut/paste, sort, find and replace, data validation, autofill), conditional formatting, filters and filter views, protected, named and banded ranges, charts, slicers, tables, pivots (as cell data) and developer metadata. One atomic batch, checked against the API's schema before anything is sent. Where a request takes a GridRange, GridCoordinate or DimensionRange, A1 naming the sheet works too ("Sheet1!A1:C10", "Sheet1!B3", "Sheet1!3:5" for rows, "Sheet1!B:D" for columns); a sheet added in the same batch is named by the sheetId set in its addSheet. Colours are {red, green, blue} from 0 to 1. Returns the replies that carry ids or counts, by request position.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `requests` (array, required): sheets batchUpdate requests, applied in order, all or none: each an object with ONE request kind, e.g. {"repeatCell": {"range": "Sheet1!A1:D1", "cell": {"userEnteredFormat": {"textFormat": {"bold": t…
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.

### `begin_consent` (~165 tokens)

Begin a Google consent

Start granting one product access to one account: gmail (its own gmail.modify grant), calendar, editors (Docs, Sheets, Slides), drive (Drive's one grant), tasks (Google Tasks), contacts (contacts, saved addresses and a Workspace directory, read only) or meet (Meet records and transcripts, read only). Returns the URL to open in a browser, where the user approves as the account named. The redirect then fails to load; pass the URL it tried to complete_consent within 15 minutes. No token ever leaves the server.

Input parameters:

- `account` (string, required): The account label, as list_accounts names it.
- `grant` (string): Gmail only: the grant to consent. modify is the only one this server holds.
- `product` (string, required)

### `clear_values` (~194 tokens)

Clear cell values

Empty the values of several A1 ranges (or the cells data_filters choose), keeping their formatting, validation and notes. Returns the ranges cleared.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `data_filters` (array): Instead of A1 ranges: Sheets DataFilters, each one of {"a1Range": "Sheet1!A1:B2"}, {"gridRange": {...}} or {"developerMetadataLookup": {"metadataKey": "...", ...}}: cells found by the developer metad…
- `ranges` (array): A1 ranges, each naming its sheet: "Sheet1!A1:D20", "Sheet1!B:B", "'Q3 plan'!2:2", or a sheet name alone.
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.

### `complete_consent` (~94 tokens)

Complete a Google consent

Finish a consent from the URL the approval redirected to (http://localhost:...?state=...&code=...). The grant is checked (the right account, every scope ticked, a refresh token) and saved where the product reads it, and the product uses it from the next call. Returns what was granted, never a token.

Input parameters:

- `redirect_url` (string, required): The whole URL from the browser's address bar.

### `complete_task` (~114 tokens)

Complete task

Mark a task done, or open again with completed: false. A task already in that state is left as it is.

Input parameters:

- `account` (string, required): Which account this writes to: a label from list_accounts. Required; writes have no default.
- `completed` (boolean)
- `task_id` (string, required): The taskId from list_tasks.
- `task_list` (string): The list's taskListId from list_task_lists or list_tasks; "@default" (the default) is the account's default list.

### `copy_file` (~94 tokens)

Copy a file

Copy one file to a folder, retaining native Google formats and formulas. Does not copy folders, comments or version history. Refuses existing names; inspect the destination after an uncertain response before retrying. Original is unchanged.

Input parameters:

- `destination_id` (string, required)
- `expected_modified` (string): Refuse if source modifiedTime differs from this observed value.
- `file_id` (string, required)
- `name` (string)

### `copy_sheet_to` (~144 tokens)

Copy a sheet to another spreadsheet

One sheet, with its formats, charts and validation, copied into another spreadsheet (or the same one when destination is empty). It arrives last, titled "Copy of <title>"; returns its sheetId and title.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `destination_spreadsheet_id` (string): The spreadsheet to copy into, by id or URL. Empty means the same one.
- `sheet` (string): The sheet's title or sheetId. Empty means the first sheet.
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.

### `create_deck_from_outline` (~187 tokens)

Create a deck from an outline

Copy a template deck through Drive, then add one slide per outline entry from the named layout, filling its title/subtitle/body placeholders and speaker notes, and delete the template's own slides unless kept. The outline is checked against the template before anything is copied.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `folder_id` (string): Drive folder id to create it in. Takes precedence over folder_path.
- `folder_path` (string): Folder path under My Drive, e.g. "Clients/Acme". Both empty means the root.
- `keep_template_slides` (boolean)
- `outline` (array, required)
- `template_id` (string, required): The template deck's id, or its URL.
- `title` (string, required): The new deck's name.

### `create_document` (~165 tokens)

Create a Google Doc

A new Google Doc from markdown, in a Drive folder or My Drive's root. Returns its id and link.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `folder_id` (string): Drive folder id to create it in. Takes precedence over folder_path.
- `folder_path` (string): Folder path under My Drive, e.g. "Clients/Acme". Both empty means the root.
- `markdown` (string): Markdown: # headings, paragraphs, - and 1. lists (nested by indent), **bold**, *italic*, ~~strike~~, `code`, ``` fences, [links](url), | tables |.
- `title` (string, required)

### `create_draft` (~393 tokens)

Create draft

Save a NEW message as a draft. The default path for anything addressed to someone: nothing leaves the mailbox, and the result carries the draft's webUrl for the user to review and send. For an answer to an existing message use reply (it drafts by default and threads properly), not this.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `attachments` (array): Files to attach. Never type or paste a file's base64 yourself: a retyped file can arrive changed. Give the path of a file in your workspace folders (the roots this client shares; without them /workdi…
- `bcc` (array): Bcc recipients (kept on a draft, stripped from what recipients see). Each entry is an address, "Name <address>", or a comma-separated list of those.
- `body_html` (string): HTML body. Given alone, a plain-text alternative is generated from it; give both to control each.
- `body_text` (string): Plain-text body: write paragraphs as single lines, never hard-wrapped. Given alone, an HTML alternative with the same lines is generated, so Gmail renders it as ordinary prose.
- `cc` (array): Cc recipients. Each entry is an address, "Name <address>", or a comma-separated list of those.
- `from` (string): Send as this address. It must be one of the account's verified send-as aliases; defaults to the account's default.
- `subject` (string)
- `to` (array): Recipients. Each entry is an address, "Name <address>", or a comma-separated list of those.

### `create_event` (~678 tokens)

Create event

Add an event to one account's calendar. Needs title and start; a timed event with no end lasts an hour. Times without an offset are in UTC. Guests are NOT emailed unless send_updates says so. meet: true gives it a new Google Meet link. event_type makes focus time or an out of office block instead of an appointment (title optional for those).

Input parameters:

- `account` (string, required): Which account this writes to: a label from list_accounts. Required; writes have no default.
- `attendees` (array): Guest email addresses. On an update this REPLACES the guest list; the result lists who that added and removed.
- `auto_decline` (string): Focus time and out of office only: which invitations Calendar declines while it lasts. "none" (the default), "new" (only those that arrive meanwhile) or "all" (every conflicting one, accepted ones to…
- `busy` (boolean): Whether it blocks time ("show as busy"). Default true.
- `calendar_id` (string): Calendar id, from list_calendars or from an agenda event's calendarId. "primary" is the account's own.
- `chat_status` (string): Focus time only: the status Google Chat shows meanwhile.
- `decline_message` (string): Focus time and out of office only: the note a decline carries.
- `description` (string)
- `duration_minutes` (integer): Length when end is omitted. Default: an hour for a new event; on an update, the length it already had.
- `end` (string): Same forms as start, and the same kind. All-day ends are EXCLUSIVE: a one-day event on the 29th ends on the 30th. Omit to use duration_minutes.
- `event_type` (string): "default" (an appointment), "focusTime" or "outOfOffice". The last two go on the primary calendar, need a start and an end with times (never all day), take no guests or Meet link, and always block th…
- `location` (string)
- `meet` (boolean): true: Google makes a new Meet call for the event, and the result carries its link (meetingUrl). On an update, adds one to an event that has none.
- `recurrence` (array): Repeat rule as RFC 5545 lines: ["RRULE:FREQ=YEARLY"] for a birthday, ["RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR"] for a gym slot. On an update of a series this REPLACES the rule; [] stops it repeating.
- `reminders_minutes` (array): Popup reminders, in minutes before the start, replacing the calendar's defaults. [] means none.
- `send_updates` (string): Who is emailed about this change: "none" (the default), "all" guests, or "externalOnly" (guests outside the domain).
- `start` (string): YYYY-MM-DD for an all-day event, YYYY-MM-DDTHH:MM for a time in the server's zone (UTC), or RFC 3339 with an offset.
- `title` (string): The event's title.

### `create_filter` (~348 tokens)

Create filter

Create a Gmail filter for mail that arrives from now on. Actions are labels, by name or id: add a label to file it; remove INBOX to skip the inbox, UNREAD to mark it read; add STARRED or IMPORTANT. Adding TRASH or SPAM needs discard: true, since such mail is never seen. No forwarding. It does not touch mail already there: the result's filter.query is the search to give modify_labels_matching for that. Gmail has no filter update; to change one, create the new one and delete the old. Needs the gmail.settings.basic scope on the account's grant, and says so if it is missing.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `add` (array): Labels to add (names or ids). Create a missing one first with create_label.
- `discard` (boolean): Allow add to hold TRASH or SPAM: arriving mail then goes there unseen.
- `from` (string): Sender: an address, a domain (e.g. news.example.com), or Gmail's from: syntax.
- `has_attachment` (boolean)
- `negated_query` (string): "Doesn't have": none of these words.
- `query` (string): Any Gmail search ("Has the words"), e.g. 'list:github.com OR from:notifications@github.com'.
- `remove` (array): Labels to remove: INBOX skips the inbox, UNREAD marks read.
- `subject` (string): Words in the subject.
- `to` (string): Recipient, as in Gmail's to:.

### `create_folder` (~58 tokens)

Create a folder

Create a folder and every missing folder above it, in one call. Returns which levels it had to create, so a typo shows up as a folder you did not expect to be new.

Input parameters:

- `path` (string, required): Full path under My Drive.

### `create_label` (~148 tokens)

Create label

Create a user label. Nest it under another with a slash in the name, e.g. 'Clients/Acme'.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `background_color` (string): Hex from Gmail's fixed palette, e.g. #16a766. Needs text_color too.
- `label_list_visibility` (string): In the label list on the left.
- `message_list_visibility` (string): On messages in the message list.
- `name` (string, required)
- `text_color` (string): Hex from Gmail's fixed palette, e.g. #ffffff.

### `create_presentation` (~141 tokens)

Create a blank deck

A new, blank Google Slides deck in a Drive folder or My Drive's root, to build with batch_update_presentation. Returns its id, link, revisionId and the object id of the title slide Slides gives every new deck.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `folder_id` (string): Drive folder id to create it in. Takes precedence over folder_path.
- `folder_path` (string): Folder path under My Drive, e.g. "Clients/Acme". Both empty means the root.
- `title` (string, required)

### `create_spreadsheet` (~141 tokens)

Create a spreadsheet

A new Google Sheet, optionally with named sheets and bold, frozen header rows, in a Drive folder or My Drive's root.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `folder_id` (string): Drive folder id to create it in. Takes precedence over folder_path.
- `folder_path` (string): Folder path under My Drive, e.g. "Clients/Acme". Both empty means the root.
- `sheets` (array): Sheets to create, in order. Empty keeps the one default sheet.
- `title` (string, required)

### `create_task` (~198 tokens)

Create task

Add a task to one account's list. Needs a title; notes and a due date are optional. parent makes it a subtask of another task in the same list; previous places it after that sibling (first otherwise).

Input parameters:

- `account` (string, required): Which account this writes to: a label from list_accounts. Required; writes have no default.
- `due` (string): The day it is due: YYYY-MM-DD. Google Tasks keeps a date only, never a time of day.
- `notes` (string)
- `parent` (string): A taskId in the same list: the new task becomes its subtask.
- `previous` (string): A sibling's taskId: the new task goes right after it.
- `task_list` (string): The list's taskListId from list_task_lists or list_tasks; "@default" (the default) is the account's default list.
- `title` (string, required)

### `create_task_list` (~56 tokens)

Create task list

A new Google Tasks list in one account; returns its taskListId.

Input parameters:

- `account` (string, required): Which account this writes to: a label from list_accounts. Required; writes have no default.
- `title` (string, required)

### `delete_comment` (~106 tokens)

Delete a comment or reply

Delete a comment thread with its replies, or one reply with reply_id. Drive lets an account delete only what it wrote (owners excepted).

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `comment_id` (string, required): The comment's id, from list_comments.
- `file_id` (string, required): The file's id, or its URL.
- `reply_id` (string)

### `delete_draft` (~69 tokens)

Delete draft

Delete a draft permanently (drafts do not go to Trash). The draft's message is gone with it.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `draft_id` (string, required)

### `delete_event` (~218 tokens)

Delete event

Delete an event (an instance id from agenda deletes that one occurrence; a series id, every occurrence, is refused unless scope is "series"). On an event someone else organised this removes it from this calendar only; to say no, use respond_to_invite with declined.

Input parameters:

- `account` (string, required): Which account this writes to: a label from list_accounts. Required; writes have no default.
- `calendar_id` (string): Calendar id, from list_calendars or from an agenda event's calendarId. "primary" is the account's own.
- `event_id` (string, required): The eventId from agenda.
- `scope` (string): "one" (the default): the event or the one occurrence the eventId names. "series": required when the eventId is a whole recurring series, which this then changes in every occurrence.
- `send_updates` (string): Who is emailed about this change: "none" (the default), "all" guests, or "externalOnly" (guests outside the domain).

### `delete_filter` (~64 tokens)

Delete filter

Delete a Gmail filter by id (list_filters). Mail it already filed keeps its labels.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `filter_id` (string, required)

### `delete_label` (~80 tokens)

Delete label

Delete a user label, given by id or name. It is removed from every message that carries it; the messages themselves are untouched. System labels are refused.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `label` (string, required): Id or name.

### `describe_file` (~64 tokens)

Describe one file

Everything Drive reports about a single file or folder, by id or path.

Input parameters:

- `file_id` (string): Drive file id. Takes precedence over path.
- `path` (string): Path under My Drive, e.g. "Reports/2026". Empty means the root.

### `download_file` (~288 tokens)

Download a file from Drive

Save a file from Drive into /tmp/.local/state/mcps/google/drive/downloads, which you can read, and prove it: the bytes are checked against Drive's own checksum before anything is written, so a damaged download is never saved. For a file no local copy has yet, or one known only by its id. A Google Doc, Sheet, Slides deck or Drawing has no bytes and is exported instead (a Doc as pdf, a Sheet as xlsx, Slides as pptx, a Drawing as png, unless format says otherwise). An earlier download of the same name in that folder is replaced. Returns the file with the saved path, size, sha256, md5 and verifiedBy (exportedAs for a Google file).

Input parameters:

- `file_id` (string): The file's id. Takes precedence over path.
- `filename` (string): The saved name; defaults to Drive's, with the export's extension.
- `format` (string): For a Google file only: what to save it as. Empty is its default.
- `path` (string): The file's path under My Drive, e.g. "Reports/summary.pdf".
- `save_dir` (string): A folder inside /tmp/.local/state/mcps/google/drive/downloads to save into; empty is /tmp/.local/state/mcps/google/drive/downloads itself.

### `download_revision` (~176 tokens)

Save an old revision

One revision of a file into the downloads folder: a file with bytes as it was, checked against the revision's MD5; a Google file exported (a Doc as PDF unless `format` says otherwise). Returns the path, size and sha256. To roll back, upload it as a new version.

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `file_id` (string, required): The file's id, or its URL.
- `filename` (string): Defaults to the file's name with the revision id.
- `format` (string): For a Google file: what to save it as.
- `revision_id` (string, required): The revision's id, from list_revisions.
- `save_dir` (string): A folder inside the downloads folder.

### `drive_changes` (~96 tokens)

What changed since the cursor

Every file created, edited, moved, trashed or removed since the cursor, and a new cursor to use next time. Only the delta comes back, so this costs the same whether the Drive holds 20 files or 20,000. Call it again while exhausted is false to drain the backlog.

Input parameters:

- `cursor` (string, required): A cursor from start_drive_cursor or from a previous drive_changes call.
- `limit` (integer)

### `edit_comment` (~123 tokens)

Edit a comment or reply

New text for a comment, or for one of its replies with reply_id. Drive lets an account edit only what it wrote.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `comment_id` (string, required): The comment's id, from list_comments.
- `content` (string, required)
- `file_id` (string, required): The file's id, or its URL.
- `reply_id` (string): A reply's id, to edit the reply rather than the comment.

### `edit_document_section` (~344 tokens)

Replace a section of a Google Doc

Replace everything under one heading (its subsections included) with markdown, keeping the heading. Read the section with read_document first and pass its revisionId: the edit is one batch, pinned to that revision. Headings in the new body must be deeper than the section's own. Refused when the section holds images, footnotes or other things read_document does not show (drop_hidden: true deletes them), and for an empty body (clear: true empties the section). Returns the document's title and the section's size before and after, in characters of markdown.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `clear` (boolean): With an empty markdown: delete everything under the heading.
- `document_id` (string, required): The document's id, or its URL.
- `drop_hidden` (boolean): Delete the images, footnotes and the like the section holds along with its text.
- `expected_revision` (string, required): The revisionId read_document returned for the text this replaces. The edit is refused if the document has changed since.
- `markdown` (string, required): Markdown: # headings, paragraphs, - and 1. lists (nested by indent), **bold**, *italic*, ~~strike~~, `code`, ``` fences, [links](url), | tables |.
- `section` (string, required): The section's id, path or unique heading title, from read_document.
- `tab` (string): A tab other than the first: its id (t.abc) or title, from get_document. Empty means the first tab.

### `editors_api_reference` (~177 tokens)

Docs, Sheets and Slides API reference

What a Google Docs, Sheets or Slides batchUpdate request takes, from the API's own schema (the revision batch_update_document, batch_update_spreadsheet and batch_update_presentation check against): each field's type, allowed values and meaning, with the types it refers to expanded `depth` levels down (`more` lists the rest by name). Name a request kind (updateTextStyle, repeatCell, createShape) or any type (TextStyle, CellFormat, PageElementProperties); with no name, every request kind and its one-line summary. Kinds marked Developer Preview need the Cloud project in Google's preview program.

Input parameters:

- `api` (string, required)
- `depth` (integer): How many levels of referred types to expand.
- `name` (string): A request kind or a type name. Empty lists the request kinds.

### `fill_placeholders` (~134 tokens)

Fill {{tags}} in a deck

Replace every {{tag}} with its value (exact case) across the deck, or only the slides named. Reports how many of each were found.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `presentation_id` (string, required): The presentation's id, or its URL.
- `slide_ids` (array): Slide object ids (read_deck) to limit it to. Empty means every slide.
- `values` (object, required): Tag to text, e.g. {"client": "Acme"} fills {{client}}.

### `find_files` (~169 tokens)

Search Drive

Search the whole Drive without listing it. Combine any of name_contains, full_text, mime_type, parent_id and modified_after; they are ANDed. This is the tool to reach for instead of walking folders, or a local copy of the Drive, which costs a full tree scan for the same answer.

Input parameters:

- `full_text` (string): Search inside document contents, Google's own index.
- `include_trashed` (boolean)
- `limit` (integer)
- `mime_type` (string): e.g. "application/vnd.google-apps.folder" or "application/pdf".
- `modified_after` (string): RFC 3339; a bare date means midnight UTC.
- `name_contains` (string)
- `parent_id` (string): Restrict to one folder's direct children.

### `find_in_document` (~240 tokens)

Find text in a Google Doc

Every place a text appears in one tab of a Google Doc (body, headers, footers and footnotes unless segments is body), as ranges [startIndex, endIndex) to use in batch_update_document (with tabId and, outside the body, segmentId), each with its paragraph's range and named style. Returns the revisionId the ranges are right for. With regex, text is a JavaScript regular expression (run under a time limit); a match never crosses a paragraph.

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `document_id` (string, required): The document's id, or its URL.
- `match_case` (boolean)
- `max_results` (integer)
- `regex` (boolean): text is a regular expression, e.g. "\{\{[a-z_]+\}\}" for placeholders.
- `segments` (string)
- `tab` (string): A tab's id (t.0) or its title. Empty means the first tab.
- `text` (string, required)
- `whole_word` (boolean)

### `find_people` (~213 tokens)

Find people

Find a person's email address by name, across every account at once: each account's contacts, the addresses Gmail saved from mail it sent or received ('other'), and a Workspace domain's directory. The query matches the start of a name, an address, a phone number or an organisation. One entry per person, merged by address, with every account and source it was found in; a source an account cannot read is listed under `unavailable` with the reason. Use it before addressing a draft or an invitation to someone named.

Input parameters:

- `account` (string): Which account: a label from list_accounts, or "*" for every account at once (the default).
- `limit` (integer): Per account and source, before merging.
- `query` (string, required): A name or its start ("Ana Pop"), or the start of an address or a phone number.
- `sources` (array): Where to look: "contacts", "other" (saved from mail) and "directory" (Workspace). Default all three.

### `find_time` (~557 tokens)

Find time

Candidate slots of `duration_minutes` across EVERY account (and optional `attendees`), inside working hours (default Mon-Fri 09:00-18:00 UTC), after a minimum notice, clear of existing events plus buffers, on a step grid, ranked earliest first or with mornings/afternoons first. Returns `slots` (overlapping candidates, e.g. 09:00 and 09:30 for an hour) and `ranges` (the free stretches they come from). Tentative events block time unless `tentative_is_free`. If some busy time could not be read, `complete` is false and `unknown` says what.

Input parameters:

- `account` (string): Which account: a label from list_accounts, or "*" for every account at once (the default).
- `attendees` (array): Other people's addresses whose free/busy an account may see (colleagues in the same organisation, anyone who shared their calendar). Needs the calendar.events.freebusy scope; without it they are list…
- `buffer_after_minutes` (integer): Free time kept after each existing event.
- `buffer_before_minutes` (integer): Free time kept before each existing event.
- `calendars`: "selected" (what the Calendar app shows, the default), "primary" (each account's own calendar only), "all", or a list of calendar ids or names.
- `days` (integer): Window length when `to` is empty; at most 62.
- `duration_minutes` (integer, required): Meeting length.
- `from` (string): YYYY-MM-DD or a time. Empty means today (UTC).
- `hours_end` (string): Working hours end, HH:MM local, exclusive; 24:00 is midnight. After hours_start.
- `hours_start` (string): Working hours start, HH:MM local.
- `max_results` (integer)
- `min_notice_minutes` (integer): No slot starts sooner than this from now.
- `prefer` (string): Rank: earliest first, or that half of the day (before/after 12:00 local) first.
- `step_minutes` (integer): Slots start on this grid from hours_start: 30 gives :00 and :30.
- `tentative_is_free` (boolean): Offer slots over tentative events and "maybe" answers.
- `time_zone` (string): IANA zone for the working hours, the window and the answer. Empty means the server's zone (UTC).
- `to` (string): Exclusive end. Empty means `days` after from.
- `working_days` (array): Days a slot may fall on, in the working-hours zone.

### `format_range` (~224 tokens)

Format a range

Number format, bold/italic, colours, alignment, wrapping, borders and frozen rows/columns for a range. Only what is given changes.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `background` (string): "#RRGGBB".
- `bold` (boolean)
- `borders` (object)
- `foreground` (string): "#RRGGBB".
- `freeze_columns` (integer)
- `freeze_rows` (integer): Rows frozen at the top of the range's sheet.
- `horizontal_alignment` (string)
- `italic` (boolean)
- `number_format` (object)
- `range` (string, required): A1 range, e.g. "Sheet1!A1:D20", "B:B" or "2:2". No sheet name means the first sheet.
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.
- `vertical_alignment` (string)
- `wrap` (string)

### `forward` (~491 tokens)

Forward

Forward a message WITH its attachments, under Gmail's 'Forwarded message' block, with an optional note above it (body_text / body_html). Creates a DRAFT unless send is true. Extra attachments can be added.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `attachments` (array): Files to attach. Never type or paste a file's base64 yourself: a retyped file can arrive changed. Give the path of a file in your workspace folders (the roots this client shares; without them /workdi…
- `bcc` (array): Bcc recipients (kept on a draft, stripped from what recipients see). Each entry is an address, "Name <address>", or a comma-separated list of those.
- `body_html` (string): HTML body. Given alone, a plain-text alternative is generated from it; give both to control each.
- `body_text` (string): Plain-text body: write paragraphs as single lines, never hard-wrapped. Given alone, an HTML alternative with the same lines is generated, so Gmail renders it as ordinary prose.
- `cc` (array): Cc recipients. Each entry is an address, "Name <address>", or a comma-separated list of those.
- `from` (string): Send as this address. It must be one of the account's verified send-as aliases; defaults to the account's default.
- `include_attachments` (boolean): Carry the original's attachments. Default true. The images its body shows travel either way.
- `message_id` (string, required): The message to forward (from get_thread).
- `send` (boolean): false (default) saves a DRAFT and returns its webUrl. true SENDS immediately. SENDING IS OUTWARD-FACING AND CANNOT BE UNDONE: only send when the user has explicitly approved THIS message (its recipie…
- `subject` (string)
- `to` (array): Recipients. Each entry is an address, "Name <address>", or a comma-separated list of those.

### `free_busy` (~286 tokens)

Free/busy

When the user is busy across EVERY account in a window, per calendar, plus one merged, de-duplicated `busy` timeline that also covers any `attendees`. Busy means: not cancelled, not "show as available", not declined; tentative events and "maybe" answers count as busy and are flagged `tentative`; an all-day event left as busy blocks its whole day. Anything that could not be read is listed under `unknown` and `complete` is false.

Input parameters:

- `account` (string): Which account: a label from list_accounts, or "*" for every account at once (the default).
- `attendees` (array): Other people's addresses whose free/busy an account may see (colleagues in the same organisation, anyone who shared their calendar). Needs the calendar.events.freebusy scope; without it they are list…
- `calendars`: "selected" (what the Calendar app shows, the default), "primary" (each account's own calendar only), "all", or a list of calendar ids or names.
- `days` (integer): Window length when `to` is empty; at most 62.
- `from` (string): YYYY-MM-DD or a time. Empty means today (UTC).
- `to` (string): Exclusive end. Empty means `days` after from.

### `get_attachment` (~213 tokens)

Download attachment

Download one attachment to disk by message_id + attachment_id (both come from get_thread). This is the capability Google's own Gmail MCP server does not have: it reports an attachment's name and id and gives you no way to fetch the bytes. Returns the path, size and sha256; a download whose size differs from the one Gmail reports for the part is refused and nothing is saved. A different file already saved under the name is kept, and this one is saved as "name (2).ext".

Input parameters:

- `account` (string): Which registered mailbox: a label from list_accounts; empty means the default account.
- `attachment_id` (string, required)
- `filename` (string): Defaults to <attachment_id>.bin. Path separators are stripped.
- `message_id` (string, required)
- `save_dir` (string): A folder INSIDE the account's attachments folder (e.g. "2026-09/invoices"); empty for the folder itself. Copy files from the result's path to where they belong.

### `get_document` (~455 tokens)

Read a Google Doc's structure

A Google Doc's structure with the indices batch_update_document needs: every tab, and for one tab its body as elements (paragraphs with named style, heading id, bullet, styled runs and objects such as images or footnote marks; tables with each cell's range and content; section breaks; tables of contents), each with range [startIndex, endIndex). A paragraph's `text` holds one U+FFFC per non-text index, so offset k of text is index range[0]+k; its newline is range[1]-1. Styles are shown short ("11pt", "#1a73e8"); in requests write {magnitude: 11, unit: "PT"} and {color: {rgbColor: {red, green, blue}}} (0 to 1). `parts` adds headers, footers, footnotes, lists, named ranges, objects (image sizes and sources) and styles (page setup, named styles); `notShown` says what exists beyond them. A long body is cut at an element (`nextIndex`: read on with start_index). format raw returns the API's JSON, narrowed by `fields`. Returns revisionId for expected_revision. For prose, read_document's markdown is cheaper.

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `document_id` (string, required): The document's id, or its URL.
- `end_index` (integer): Only body elements that start before this index.
- `fields` (string): format raw only: a field mask over the Document, e.g. "revisionId,tabs(tabProperties)".
- `format` (string)
- `max_chars` (integer)
- `parts` (array): What to include for the tab.
- `start_index` (integer): Only body elements that end after this index.
- `suggestions` (string): How suggested edits show: inline with their ids, as if accepted, or as if rejected. Default: Docs' choice for the account's access.
- `tab` (string): A tab's id (t.0) or its title. Empty means the first tab.

### `get_draft` (~71 tokens)

Get draft

One draft: its message (headers, Bcc included, bodies and attachments) and its webUrl.

Input parameters:

- `account` (string): Which registered mailbox: a label from list_accounts; empty means the default account.
- `detail` (string): As on get_message.
- `draft_id` (string, required)

### `get_event` (~95 tokens)

Get event

One event in full: description, guest list with each answer, reminders, meeting link, recurrence.

Input parameters:

- `account` (string): Which account: a label from list_accounts; empty means the default account.
- `calendar_id` (string): Calendar id, from list_calendars or from an agenda event's calendarId. "primary" is the account's own.
- `event_id` (string, required): The eventId from agenda.

### `get_message` (~164 tokens)

Get message

Fetch a single message by id. See get_thread for the detail levels and the account rule.

Input parameters:

- `account` (string): Which registered mailbox: a label from list_accounts; empty means the default account.
- `detail` (string): How much comes back. 'metadata' = headers and snippet only, the cheapest fetch; it carries NO attachment list, because Gmail's metadata format does not return the MIME tree at all. 'text' (default) a…
- `message_id` (string, required)

### `get_messages` (~255 tokens)

Get messages

Fetch several messages of one mailbox in one call, up to 25 ids, in the order given (a repeated id once). Use it after list_history or a search when you need the bodies of messages from different threads. A message that cannot be read comes back as {id, partial: true, reason} in its place and the others still arrive. See get_thread for the detail levels and the account rule; 'metadata' keeps a long list cheap.

Input parameters:

- `account` (string): Which registered mailbox: a label from list_accounts; empty means the default account.
- `detail` (string): How much comes back. 'metadata' = headers and snippet only, the cheapest fetch; it carries NO attachment list, because Gmail's metadata format does not return the MIME tree at all. 'text' (default) a…
- `message_ids` (array, required): Message ids from one mailbox, as search_threads, get_thread or list_history report them.

### `get_presentation` (~280 tokens)

Read a deck's structure

A Google Slides deck's structure with the object ids batch_update_presentation needs: per slide its layout, every element (shapes with type, placeholder and full text, images, tables cell by cell, lines, videos, Sheets charts, groups) with size and transform, and the speaker notes' shape id; plus layouts, masters and the page size. A shape's text is exact, so offset k is index k for insertText and text ranges. Sizes show as "3000000 EMU"; requests take {magnitude, unit}. A long deck is cut at a slide (`nextSlide`: read on with from_slide). format raw returns the API's JSON, narrowed by `fields`. Returns revisionId.

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `fields` (string): format raw only: a field mask over the Presentation, e.g. "slides(objectId,pageElements(objectId,image))".
- `format` (string)
- `from_slide` (integer): The first slide to show, by position (0-based).
- `max_chars` (integer)
- `presentation_id` (string, required): The presentation's id, or its URL.
- `slide_ids` (array): Only these slides, by object id. Empty means every slide.

### `get_spreadsheet` (~284 tokens)

Read a spreadsheet's structure

A spreadsheet's structure with the ids batch_update_spreadsheet needs: per sheet its sheetId, size, frozen rows and columns, merges, conditional format rules (with their index), basic filter, filter views, protected and banded ranges, charts, slicers, tables and row/column groups, plus named ranges; ranges as A1. No cell data (get_values, read_rows). format raw returns the API's JSON for `ranges`, with grid data if asked, narrowed by `fields`: the way to read cell formats, notes, validation and pivots.

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `data_filters` (array): format raw, instead of ranges: DataFilters choosing the grid data (developer metadata lookups included).
- `fields` (string): format raw: a field mask over the Spreadsheet, e.g. "sheets(properties,data(rowData(values(userEnteredFormat,note))))".
- `format` (string)
- `include_grid_data` (boolean): format raw: include each cell's data and formats for `ranges`.
- `max_chars` (integer)
- `ranges` (array): format raw: A1 ranges to limit the grid data to.
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.

### `get_thread` (~178 tokens)

Get thread

Fetch one thread with all its messages, their attachment metadata and their bodies. Thread ids are per-mailbox: pass the account that search_threads reported alongside the id.

Input parameters:

- `account` (string): Which registered mailbox: a label from list_accounts; empty means the default account.
- `detail` (string): How much comes back. 'metadata' = headers and snippet only, the cheapest fetch; it carries NO attachment list, because Gmail's metadata format does not return the MIME tree at all. 'text' (default) a…
- `thread_id` (string, required)

### `get_thumbnails` (~95 tokens)

Slide thumbnails

A PNG thumbnail URL per slide, rendered by Google. The URLs expire after about half an hour.

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `presentation_id` (string, required): The presentation's id, or its URL.
- `size` (string): SMALL is 200px wide, MEDIUM 800px, LARGE 1600px.

### `get_transcript` (~145 tokens)

Get transcript

A Meet transcript as text, one line per thing said: "[0:12:05] Ana Popescu: ...", the time counted from when the transcript began. Long ones come in parts of about max_chars; pass nextPageToken back to read on. The transcript is the name list_meetings gives (conferenceRecords/.../transcripts/...).

Input parameters:

- `account` (string): Which account: a label from list_accounts; empty means the first one with a grant.
- `max_chars` (integer)
- `page_token` (string): nextPageToken from the previous part.
- `transcript` (string, required): The transcript's name, from list_meetings.

### `get_values` (~281 tokens)

Read cell values

Values of several A1 ranges in one read, as Sheets renders them (FORMATTED_VALUE), as numbers (UNFORMATTED_VALUE: a double, so read_rows is the tool for money) or as formulas (FORMULA), chosen by A1 ranges or by data_filters. A large answer is cut at a row (`totalRows` says how many there were).

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `data_filters` (array): Instead of A1 ranges: Sheets DataFilters, each one of {"a1Range": "Sheet1!A1:B2"}, {"gridRange": {...}} or {"developerMetadataLookup": {"metadataKey": "...", ...}}: cells found by the developer metad…
- `date_time_render` (string): For dates when value_render is not FORMATTED_VALUE.
- `major_dimension` (string)
- `max_chars` (integer)
- `ranges` (array): A1 ranges, each naming its sheet: "Sheet1!A1:D20", "Sheet1!B:B", "'Q3 plan'!2:2", or a sheet name alone.
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.
- `value_render` (string)

### `harvest_attachments` (~340 tokens)

Harvest attachments for a query

Every attachment matching a Gmail query, downloaded to a folder in ONE call, with a manifest. Use it for a month of invoices ('has:attachment after:2026/08/01 before:2026/09/01') instead of walking threads and calling get_attachment per file. Do NOT use Gmail's own 'download all attachments' zip as an alternative: it is keyed by thread id and silently omits parts keyed by message id. Returns counts (threadsMatched, attachmentsFound, savedCount) so the harvest can be reconciled against Gmail's own has:attachment count, each saved file with its size and sha256, plus a per-file failure list. Files are named <message id>-<filename>; two of one name both survive. A client that asks for progress hears each attachment saved, out of how many; cancelling stops the harvest, which then ends as cancelled.

Input parameters:

- `account` (string): Which registered mailbox: a label from list_accounts; empty means the default account.
- `extensions` (array): Filter by extension, e.g. ['.pdf','.xml']. Empty means every attachment.
- `max_attachments` (integer): Hard cap on files written. `truncated: true` in the result means you hit it.
- `max_threads` (integer): Threads scanned. Page with the query.
- `query` (string): Gmail query syntax. Narrow it with a date window.
- `save_dir` (string): A folder INSIDE the account's attachments folder (e.g. "2026-09/invoices"); empty for the folder itself. Copy files from the result's path to where they belong.

### `health` (~318 tokens)

Health

Check that every grant is alive, per product and per account. Under `gmail`, per grant: the mailbox each credential resolves to, the scope Google actually GRANTED (not the one credentials.json declares), whether it can write, and the OAuth client the live token belongs to; an account may hold its own gmail.modify grant and a gmail.readonly one, and `activeGrant` is the one the tools use. Under `calendar`: that the grant refreshes, which account it resolves to (the consent screen offers every account signed in), and whether it carries the scopes the tools need. Under `drive`: which account the grant acts as and the storage used. Under `editors` (Docs, Sheets, Slides), per account: the address, the granted scopes, and whether each of the three APIs is enabled in the grant's Cloud project. Under `tasks`, `contacts` and `meet`, per account with a grant: the address, the granted scopes, and whether the API answers (`disabled` when its Cloud project has not enabled it); contacts also says which sources it reads. Under `config`: where the state directory, the OAuth client, the mailboxes and each account's address come from (a variable, or what setup or a consent saved; a variable that is set wins). Checks EVERY account by default. Run this before concluding the server is broken, and before retrying a write that refused.

Input parameters:

- `account` (string): Which account: a label from list_accounts, or "*" for every account (the default).

### `link_sheet_cells` (~120 tokens)

Link existing text cells

Add or replace hyperlinks on existing plain text cells in one atomic batch. Keeps their values, validation and all other formatting. Row and column are 1-based. Every cell must still contain expected_text; formulas, rich text and chips are refused before any write.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `links` (array, required)
- `sheet` (string, required)
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.

### `list_accounts` (~225 tokens)

List accounts

The Google accounts this server reaches, per product. Start here: every other tool takes one of these labels. Under `gmail`: the mailboxes, which one a tool uses when no account is given, and which grant each runs on: `modify` (its own; the write tools work) or `readonly` (a read-only grant; the write tools refuse). Under `calendar`: the accounts whose calendars it reaches, the address each grant must resolve to, and the time zone. Under `drive`: which grant Drive runs on (its own once begin_consent made one, else an rclone remote's when one is configured). Under `editors`: the accounts Docs, Sheets and Slides can act as (one may run on the Drive grant; any other needs a grant made with begin_consent). Under `tasks`, `contacts` and `meet`: the accounts with a grant for Google Tasks, for contacts and the directory, and for Meet's records. Says nothing about whether a grant is alive; that is health, which costs a round trip to Google.

### `list_calendars` (~72 tokens)

List calendars

Every calendar each account can see (its own, shared ones, subscriptions, holidays), with its id, the account's access to it, and whether agenda reads it by default.

Input parameters:

- `account` (string): Which account: a label from list_accounts, or "*" for every account at once (the default).

### `list_comments` (~148 tokens)

List a file's comments

The comment threads on a Doc, Sheet, deck or any Drive file, each with its author, time, text, the text it quotes, whether it is resolved, and its replies (with any resolve or reopen action). Counts the open and resolved threads.

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `file_id` (string, required): The file's id, or its URL.
- `include_deleted` (boolean)
- `include_resolved` (boolean)
- `max_results` (integer)
- `modified_after` (string): RFC 3339; only threads changed since (a new reply counts).

### `list_drafts` (~85 tokens)

List drafts

The account's drafts, newest first, with recipients, subject, snippet and each draft's webUrl.

Input parameters:

- `account` (string): Which registered mailbox: a label from list_accounts; empty means the default account.
- `max_results` (integer)
- `page_token` (string)
- `query` (string): Gmail query syntax to filter drafts. Empty means all.

### `list_filters` (~71 tokens)

List filters

The mailbox's Gmail filters: criteria, the labels each adds and removes (by name), and `query`, the search its criteria amount to, for applying it to mail already there with modify_labels_matching.

Input parameters:

- `account` (string): Which registered mailbox: a label from list_accounts; empty means the default account.

### `list_folder` (~108 tokens)

List a folder

The direct children of one folder. Give file_id or path; empty path means the root of My Drive. Google Docs come back with isNative true and bytes null, because a Doc has no stored bytes.

Input parameters:

- `file_id` (string): Drive file id. Takes precedence over path.
- `include_trashed` (boolean)
- `limit` (integer)
- `path` (string): Path under My Drive, e.g. "Reports/2026". Empty means the root.

### `list_history` (~131 tokens)

List history

Messages added since a historyId: incremental sync instead of re-searching the same window. Take the anchor from health's historyId, store it, pass it back next run. Anchors are per-mailbox, so keep one per account. Gmail expires old anchors: a 404 here means the anchor is too old and a full search_threads is needed to re-baseline.

Input parameters:

- `account` (string): Which registered mailbox: a label from list_accounts; empty means the default account.
- `max_results` (integer)
- `page_token` (string)
- `start_history_id` (string, required)

### `list_labels` (~43 tokens)

List labels

List Gmail labels (system and user-defined) for one account.

Input parameters:

- `account` (string): Which registered mailbox: a label from list_accounts; empty means the default account.

### `list_layouts` (~78 tokens)

List a deck's layouts

A deck's (or template's) layouts, by name, and the placeholders each has (TITLE, SUBTITLE, BODY, ...).

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `presentation_id` (string, required): The presentation's id, or its URL.

### `list_meetings` (~256 tokens)

List meetings

The Google Meet meetings one account took part in, each with its transcripts, smart notes (Gemini's notes) and recordings, as Meet records them: for a calendar event (event_id from agenda: its Meet call, and for one occurrence the sitting that started within an hour of it), for a Meet link or code (meeting), or else for the last days (at most 30: Meet keeps records 30 days). A transcript or notes is a Google Doc: its document id opens with read_document; get_transcript reads a transcript as lines with who said what. Reading an event needs the account's calendar grant too.

Input parameters:

- `account` (string): Which account: a label from list_accounts; empty means the first one with a grant.
- `calendar_id` (string): The event's calendarId, with event_id.
- `days` (integer): Without event_id or meeting: how many days back.
- `event_id` (string): An event's eventId from agenda, in this account's calendar.
- `limit` (integer): The newest meetings, at most.
- `meeting` (string): A Meet link (https://meet.google.com/abc-defg-hij) or its code.

### `list_permissions` (~100 tokens)

Who has access to a file

Every permission on a Drive file: each person, group, domain or link with its role, expiry and id, whether it comes from a shared drive, and the link sharing in one line. Read it before share_file, update_permission or remove_permission.

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `file_id` (string, required): The file's id, or its URL.

### `list_revisions` (~104 tokens)

List a file's revisions

A Drive file's revisions, oldest first: id, time, who made it, size and checksum for files with bytes, whether it is kept forever or published, and for a Doc, Sheet or deck the formats an old revision can be saved as with download_revision.

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `file_id` (string, required): The file's id, or its URL.

### `list_sheets` (~71 tokens)

List a spreadsheet's sheets

A spreadsheet's title, locale, time zone and sheets (id, name, size, frozen rows).

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.

### `list_task_lists` (~65 tokens)

List task lists

Every Google Tasks list of each account (every account by default), with the taskListId the other task tools take; the default list is marked.

Input parameters:

- `account` (string): Which account: a label from list_accounts, or "*" for every account at once (the default).

### `list_tasks` (~204 tokens)

List tasks

Tasks across every account by default, from every list unless task_list names one, in each list's own order. Open tasks only unless include_completed. due_from and due_to keep the tasks due between those dates (inclusive), which leaves out tasks with no due date. Each task carries account, taskListId and taskId for the write tools, parent when it is a subtask, and its link in Google Tasks. agenda with include_tasks shows the tasks due beside the events.

Input parameters:

- `account` (string): Which account: a label from list_accounts, or "*" for every account at once (the default).
- `due_from` (string): YYYY-MM-DD, inclusive.
- `due_to` (string): YYYY-MM-DD, inclusive.
- `include_completed` (boolean)
- `limit` (integer): Per account.
- `task_list` (string): A taskListId, "@default" for the default list, or empty (the default) for every list.

### `mark_spam` (~106 tokens)

Mark as spam

Report threads or messages as spam: into SPAM and out of the inbox, as Gmail's own button does.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `ids` (array, required): Thread or message ids, per `target`, from this account.
- `target` (string): Act on whole threads (default, what Gmail's own buttons do) or on single messages.

### `modify_labels` (~175 tokens)

Modify labels

Add and remove labels on threads or messages. In Gmail these ARE the everyday actions: archive = remove INBOX; mark read = remove UNREAD (unread = add it); star = add STARRED; important = IMPORTANT. Labels may be given by id or by name, case-insensitively. For threads, ids that failed are listed under `failed` beside the ones changed.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `add` (array)
- `ids` (array, required): Thread or message ids, per `target`, from this account.
- `remove` (array)
- `target` (string): Act on whole threads (default, what Gmail's own buttons do) or on single messages.

### `modify_labels_matching` (~275 tokens)

Modify labels on every match

Add and remove labels on EVERY thread a Gmail search matches, as Gmail's "select all conversations that match this search" does: file a sender under a label and archive it (add the label, remove INBOX), mark old mail read (remove UNREAD). A dry run by default: it returns how many threads match and a sample, and changes nothing. With dry_run false it refuses when the search matches more than `limit`, and otherwise changes every match, listing any thread that failed. To be able to find and reverse a large change later, also add a marker label created for it (e.g. 'cleanup/2026-10').

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `add` (array): Labels to add, by name or id.
- `dry_run` (boolean): True (default): count and sample only. False: apply.
- `limit` (integer): The most threads one call may change.
- `query` (string, required): Gmail query syntax, e.g. 'in:inbox from:news.example.com' or 'in:inbox older_than:90d'.
- `remove` (array): Labels to remove: INBOX archives, UNREAD marks read.

### `move_file` (~160 tokens)

Move files

Move one or many files into a folder, optionally renaming a single one. Sources are paths, or "id:<fileId>" to name one directly. Works the same for a Google Doc as for a PDF, which is the reason to use this instead of moving files in a synced local copy: moving a Doc's placeholder there deletes the Doc and uploads a small shortcut file in its place. Call with dry_run first; the plan names every collision.

Input parameters:

- `destination_id` (string)
- `destination_path` (string)
- `dry_run` (boolean): Compute the plan and write nothing.
- `rename_to` (string): Only legal with a single source.
- `sources` (array, required): Paths, or "id:<fileId>".

### `move_task` (~186 tokens)

Move task

Move a task: under parent (a subtask; top level when omitted), after previous (first among its siblings when omitted), and into another list of the same account with to_list. A repeating task cannot change lists.

Input parameters:

- `account` (string, required): Which account this writes to: a label from list_accounts. Required; writes have no default.
- `parent` (string): The taskId it goes under; omit for the top level.
- `previous` (string): The sibling taskId it goes after; omit for the first position.
- `task_id` (string, required): The taskId from list_tasks.
- `task_list` (string): The list's taskListId from list_task_lists or list_tasks; "@default" (the default) is the account's default list.
- `to_list` (string): Another taskListId of the same account to move it into.

### `path_of` (~42 tokens)

Path of an id

The reverse of resolve_path: where a file sits, as a path, by walking its parents to the root.

Input parameters:

- `file_id` (string, required): Drive file id.

### `read_deck` (~71 tokens)

Read a deck

Every slide's layout, title, other text (tables as rows) and speaker notes, in order.

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `presentation_id` (string, required): The presentation's id, or its URL.

### `read_doc` (~90 tokens)

Read a Google Doc

Export a Google Doc, Sheet or Slide deck as text and return it. Sheets come back as CSV. A Doc has no bytes to download, and this fetches its words in one request. Binary files are refused on purpose; download_file saves their bytes.

Input parameters:

- `file_id` (string)
- `format` (string)
- `max_chars` (integer)
- `path` (string)

### `read_document` (~211 tokens)

Read a Google Doc

A Google Doc as markdown (headings, lists, tables, bold/italic/links), with an index of its sections: each heading's stable id and path ("Plan > Risks"). Pass `section` (an id, a path or a unique title) to read one section. Returns revisionId for a later edit's expected_revision. Reads the first tab unless `tab` names another; images, footnotes and the like are left out, and `notShown` counts them.

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `document_id` (string, required): The document's id, or its URL.
- `max_chars` (integer)
- `section` (string): A section id, path or unique heading title. Empty reads the whole document.
- `tab` (string): A tab other than the first: its id (t.abc) or title, from get_document. Empty means the first tab.

### `read_rows` (~166 tokens)

Read rows

Rows as objects keyed by the header row. Values are typed: numbers stay numbers, dates are ISO strings (sheet-local time), and a currency cell is {value, amount, formatted} with `amount` its exact decimal string. Each row has `_row`, its row number. Empty rows are skipped.

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `header_row` (integer): The row that names the columns (1-based).
- `limit` (integer)
- `offset` (integer)
- `sheet` (string): The sheet (tab) name, from list_sheets. Empty means the first sheet.
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.

### `read_sheet_cells` (~148 tokens)

Read formulas and linked cells

Sparse cells with entered formulas, shown values, hyperlinks, rich-text links, smart chips and notes. Row and column numbers are 1-based. offset counts physical rows; follow nextOffset until null. Use this before changing a link or formula, rather than inferring it from read_rows display text.

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `limit` (integer)
- `offset` (integer)
- `sheet` (string): The sheet (tab) name, from list_sheets. Empty means the first sheet.
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.

### `remove_permission` (~120 tokens)

Remove someone's access

Remove one permission from a file: that person, group, domain or link loses access. Inherited shared-drive access cannot be removed here. It changes who can open the file: do it only on the user's explicit go for this file and these people.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `file_id` (string, required): The file's id, or its URL.
- `permission_id` (string, required): The permission's id, from list_permissions.

### `rename_file` (~65 tokens)

Rename a file

Change a file's name in place. Names cannot contain a slash; Drive has no path, only parents.

Input parameters:

- `file_id` (string): Drive file id. Takes precedence over path.
- `new_name` (string, required)
- `path` (string): Path under My Drive.

### `reply` (~594 tokens)

Reply

Answer a message, threaded properly for every party: the original's threadId, In-Reply-To and References, a 'Re:' subject, and the recipients Gmail's own Reply picks (Reply-To when the original has one, else From; a reply to a message you SENT goes to its recipients). Goes out from the alias the original reached. Creates a DRAFT unless send is true. `to` replaces the computed recipients; `cc` and `bcc` add to them.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `attachments` (array): Files to attach. Never type or paste a file's base64 yourself: a retyped file can arrive changed. Give the path of a file in your workspace folders (the roots this client shares; without them /workdi…
- `bcc` (array): Bcc recipients (kept on a draft, stripped from what recipients see). Each entry is an address, "Name <address>", or a comma-separated list of those.
- `body_html` (string): HTML body. Given alone, a plain-text alternative is generated from it; give both to control each.
- `body_text` (string): Plain-text body: write paragraphs as single lines, never hard-wrapped. Given alone, an HTML alternative with the same lines is generated, so Gmail renders it as ordinary prose.
- `cc` (array): Cc recipients. Each entry is an address, "Name <address>", or a comma-separated list of those.
- `from` (string): Send as this address. It must be one of the account's verified send-as aliases; defaults to the account's default.
- `message_id` (string, required): The message being answered (from get_thread).
- `quote` (boolean): Quote the original below the reply as Gmail's own Reply does, with the images the quote shows. In Gmail's conversation view the quote folds behind "•••"; with conversation view off Gmail shows every…
- `reply_all` (boolean): Also address the original's To and Cc, minus this account's own addresses.
- `send` (boolean): false (default) saves a DRAFT and returns its webUrl. true SENDS immediately. SENDING IS OUTWARD-FACING AND CANNOT BE UNDONE: only send when the user has explicitly approved THIS message (its recipie…
- `subject` (string)
- `to` (array): Recipients. Each entry is an address, "Name <address>", or a comma-separated list of those.

### `reply_to_comment` (~116 tokens)

Reply to, resolve or reopen a comment

A reply on a comment thread, and/or its resolve or reopen (`action`), as the account; what the comment box's Reply and Resolve buttons do.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `action` (string)
- `comment_id` (string, required): The comment's id, from list_comments.
- `content` (string)
- `file_id` (string, required): The file's id, or its URL.

### `resolve_path` (~113 tokens)

Resolve a path to an id

Walk a path and return the id it names. A path is a QUERY on Drive, not an address: two objects may hold the same name in one folder, and this reports that as an error listing both ids rather than picking one. Use it before any tool that writes. A Drive name may itself contain a slash (Google Meet writes folders like "Weekly sync - 2030/01/15"); those are reachable only by id, which find_files returns.

Input parameters:

- `path` (string, required): Path under My Drive.

### `respond_to_invite` (~160 tokens)

Respond to invite

Answer an invitation: accepted, declined or tentative. Changes only your own answer. The organiser sees it on the event either way; send_updates=all also emails them.

Input parameters:

- `account` (string, required): Which account this writes to: a label from list_accounts. Required; writes have no default.
- `calendar_id` (string): Calendar id, from list_calendars or from an agenda event's calendarId. "primary" is the account's own.
- `event_id` (string, required): The eventId from agenda.
- `response` (string, required)
- `send_updates` (string): Who is emailed about this change: "none" (the default), "all" guests, or "externalOnly" (guests outside the domain).

### `search_developer_metadata` (~204 tokens)

Find developer metadata

Developer metadata: key/value tags a program attaches to the spreadsheet, a sheet, or rows and columns (createDeveloperMetadata in batch_update_spreadsheet), which then name those cells in data_filters. One tag by metadata_id, or every tag data_filters match (e.g. [{"developerMetadataLookup": {"metadataKey": "invoice-table"}}]), each with where it is attached as A1.

Input parameters:

- `account` (string): Which account: a label from list_accounts (its editors section). Empty means the default account.
- `data_filters` (array): Instead of A1 ranges: Sheets DataFilters, each one of {"a1Range": "Sheet1!A1:B2"}, {"gridRange": {...}} or {"developerMetadataLookup": {"metadataKey": "...", ...}}: cells found by the developer metad…
- `metadata_id` (integer)
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.

### `search_threads` (~181 tokens)

Search threads

Search threads with Gmail query syntax, e.g. 'from:billing.example.com after:2026/01/01 has:attachment', 'subject:invoice newer_than:30d', 'label:INBOX is:unread'. Searches EVERY registered mailbox by default and tags each result with the account it came from, so a question that does not know which mailbox holds the answer needs one call. Returns summaries; use get_thread for bodies.

Input parameters:

- `account` (string): Which registered mailbox. A label from list_accounts, or "*" for every mailbox at once (the default here).
- `max_results` (integer): Per account, not in total.
- `page_token` (string): From a previous call's per-account nextPageToken. Page ONE account at a time.
- `query` (string): Gmail query syntax. Empty means the whole mailbox.

### `send_draft` (~111 tokens)

Send draft

Send an existing draft as it stands. SENDING IS OUTWARD-FACING AND CANNOT BE UNDONE: only send when the user has explicitly approved THIS message (its recipients and its text) in the current conversation. Otherwise create a draft and give the user its webUrl. Returns the sent message's webUrl.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `draft_id` (string, required)

### `send_message` (~411 tokens)

Send message

Send a NEW message immediately. SENDING IS OUTWARD-FACING AND CANNOT BE UNDONE: only send when the user has explicitly approved THIS message (its recipients and its text) in the current conversation. Otherwise create a draft and give the user its webUrl. Prefer create_draft. To answer an existing message use reply, which threads it. Returns the sent message's webUrl.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `attachments` (array): Files to attach. Never type or paste a file's base64 yourself: a retyped file can arrive changed. Give the path of a file in your workspace folders (the roots this client shares; without them /workdi…
- `bcc` (array): Bcc recipients (kept on a draft, stripped from what recipients see). Each entry is an address, "Name <address>", or a comma-separated list of those.
- `body_html` (string): HTML body. Given alone, a plain-text alternative is generated from it; give both to control each.
- `body_text` (string): Plain-text body: write paragraphs as single lines, never hard-wrapped. Given alone, an HTML alternative with the same lines is generated, so Gmail renders it as ordinary prose.
- `cc` (array): Cc recipients. Each entry is an address, "Name <address>", or a comma-separated list of those.
- `from` (string): Send as this address. It must be one of the account's verified send-as aliases; defaults to the account's default.
- `subject` (string)
- `to` (array): Recipients. Each entry is an address, "Name <address>", or a comma-separated list of those.

### `sender_breakdown` (~181 tokens)

Sender breakdown

Who the threads matching a query come from: per sending domain, busiest first, the thread and unread counts, the newest and oldest date, and its busiest addresses. The starting point for sorting a mailbox into labels and filters. A thread's sender is the author of its latest message you did not send. One mailbox per call. A few hundred threads take seconds; past that Gmail's quota allows about 25 threads a second, so a 3000-thread inbox takes about two minutes.

Input parameters:

- `account` (string): Which registered mailbox: a label from list_accounts; empty means the default account.
- `limit` (integer): Threads scanned at most; `more` says whether the query matched more.
- `query` (string): Gmail query syntax; default the inbox.
- `top` (integer): Domains listed; the rest are summed in otherDomains.

### `share_file` (~269 tokens)

Share a file

Give a person or group (email_address), a domain, or anyone with the link access to a Drive file, as reader, commenter, writer (organizer roles on shared drives), with an optional expiry. Drive's email goes out only with notify: true, with `message` in it. Making someone the owner needs transfer_ownership: true. It changes who can open the file: do it only on the user's explicit go for this file and these people.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `discoverable` (boolean): For domain and anyone: whether it shows up in search, rather than by link only.
- `domain` (string): For domain, e.g. example.com.
- `email_address` (string): For user and group.
- `expiration_time` (string): RFC 3339: when the access ends.
- `file_id` (string, required): The file's id, or its URL.
- `message` (string): The email's text, with notify.
- `notify` (boolean): Send Drive's email to the person or group.
- `role` (string, required)
- `transfer_ownership` (boolean)
- `type` (string, required)

### `start_drive_cursor` (~47 tokens)

Mint a change cursor

A token meaning "from now on". Keep it, and hand it to drive_changes later. This is the cheap half of keeping anything in step with Drive: one call, no listing.

### `trash` (~117 tokens)

Trash

Move threads or messages to Trash, where Gmail keeps them 30 days. There is no permanent delete here. Returns the ids trashed, and any that failed under `failed`.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `ids` (array, required): Thread or message ids, per `target`, from this account.
- `target` (string): Act on whole threads (default, what Gmail's own buttons do) or on single messages.

### `trash_file` (~78 tokens)

Move to trash

Send a file to Drive's trash, where it stays recoverable for 30 days. There is deliberately no permanent-delete tool here: the one operation that cannot be undone is the one this server does not expose.

Input parameters:

- `file_id` (string): Drive file id. Takes precedence over path.
- `path` (string): Path under My Drive.

### `unmark_spam` (~98 tokens)

Not spam

Take threads or messages out of SPAM and back into the inbox.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `ids` (array, required): Thread or message ids, per `target`, from this account.
- `target` (string): Act on whole threads (default, what Gmail's own buttons do) or on single messages.

### `untrash` (~104 tokens)

Untrash

Bring threads or messages back out of Trash. Returns the ids restored, and any that failed under `failed`.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `ids` (array, required): Thread or message ids, per `target`, from this account.
- `target` (string): Act on whole threads (default, what Gmail's own buttons do) or on single messages.

### `untrash_file` (~61 tokens)

Restore from trash

Take a file back out of the trash. Find it by id; a trashed file no longer answers to its path.

Input parameters:

- `file_id` (string): Drive file id. Takes precedence over path.
- `path` (string): Path under My Drive.

### `update_draft` (~390 tokens)

Update draft

Change an existing draft. Only the fields you pass change; everything else, including a reply draft's threading, is kept. Passing either body replaces both bodies. Attachments are kept unless you pass a list, and an empty list removes them.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `attachments` (array): Files to attach. Never type or paste a file's base64 yourself: a retyped file can arrive changed. Give the path of a file in your workspace folders (the roots this client shares; without them /workdi…
- `bcc` (array): Bcc recipients (kept on a draft, stripped from what recipients see). Each entry is an address, "Name <address>", or a comma-separated list of those.
- `body_html` (string): HTML body. Given alone, a plain-text alternative is generated from it; give both to control each.
- `body_text` (string): Plain-text body: write paragraphs as single lines, never hard-wrapped. Given alone, an HTML alternative with the same lines is generated, so Gmail renders it as ordinary prose.
- `cc` (array): Cc recipients. Each entry is an address, "Name <address>", or a comma-separated list of those.
- `draft_id` (string, required)
- `from` (string): Send as this address. It must be one of the account's verified send-as aliases; defaults to the account's default.
- `subject` (string)
- `to` (array): Recipients. Each entry is an address, "Name <address>", or a comma-separated list of those.

### `update_event` (~719 tokens)

Update event

Change an event. Only the fields given change; a new start with no end keeps the event's length. On a recurring event, the eventId decides the reach: an instance id from agenda changes that one occurrence, and a series id (every occurrence) is refused unless scope is "series". attendees replaces the guest list, and the result says who that added and removed. Guests are NOT emailed unless send_updates says so. meet: true adds a Meet link to an event without one. An event's type cannot change; a focus time or out of office event takes new auto_decline, decline_message and chat_status.

Input parameters:

- `account` (string, required): Which account this writes to: a label from list_accounts. Required; writes have no default.
- `attendees` (array): Guest email addresses. On an update this REPLACES the guest list; the result lists who that added and removed.
- `auto_decline` (string): Focus time and out of office only: which invitations Calendar declines while it lasts. "none" (the default), "new" (only those that arrive meanwhile) or "all" (every conflicting one, accepted ones to…
- `busy` (boolean): Whether it blocks time ("show as busy"). Default true.
- `calendar_id` (string): Calendar id, from list_calendars or from an agenda event's calendarId. "primary" is the account's own.
- `chat_status` (string): Focus time only: the status Google Chat shows meanwhile.
- `decline_message` (string): Focus time and out of office only: the note a decline carries.
- `description` (string)
- `duration_minutes` (integer): Length when end is omitted. Default: an hour for a new event; on an update, the length it already had.
- `end` (string): Same forms as start, and the same kind. All-day ends are EXCLUSIVE: a one-day event on the 29th ends on the 30th. Omit to use duration_minutes.
- `event_id` (string, required): The eventId from agenda.
- `location` (string)
- `meet` (boolean): true: Google makes a new Meet call for the event, and the result carries its link (meetingUrl). On an update, adds one to an event that has none.
- `recurrence` (array): Repeat rule as RFC 5545 lines: ["RRULE:FREQ=YEARLY"] for a birthday, ["RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR"] for a gym slot. On an update of a series this REPLACES the rule; [] stops it repeating.
- `reminders_minutes` (array): Popup reminders, in minutes before the start, replacing the calendar's defaults. [] means none.
- `scope` (string): "one" (the default): the event or the one occurrence the eventId names. "series": required when the eventId is a whole recurring series, which this then changes in every occurrence.
- `send_updates` (string): Who is emailed about this change: "none" (the default), "all" guests, or "externalOnly" (guests outside the domain).
- `start` (string): YYYY-MM-DD for an all-day event, YYYY-MM-DDTHH:MM for a time in the server's zone (UTC), or RFC 3339 with an offset.
- `title` (string): The event's title.

### `update_label` (~162 tokens)

Update label

Rename or restyle a user label, given by id or name. System labels (INBOX, SENT, ...) are refused.

Input parameters:

- `account` (string, required): Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes.
- `background_color` (string): Hex from Gmail's fixed palette, e.g. #16a766. Needs text_color too.
- `label` (string, required): Id or current name.
- `label_list_visibility` (string): In the label list on the left.
- `message_list_visibility` (string): On messages in the message list.
- `name` (string): New name.
- `text_color` (string): Hex from Gmail's fixed palette, e.g. #ffffff.

### `update_permission` (~148 tokens)

Change someone's access

A permission's role or expiry (or its expiry removed). Making someone the owner needs transfer_ownership: true. It changes who can open the file: do it only on the user's explicit go for this file and these people.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `expiration_time` (string)
- `file_id` (string, required): The file's id, or its URL.
- `permission_id` (string, required): The permission's id, from list_permissions.
- `remove_expiration` (boolean)
- `role` (string)
- `transfer_ownership` (boolean)

### `update_revision` (~162 tokens)

Keep or publish a revision

keep_forever stops Drive pruning a revision of a file with bytes. published puts a Doc, Sheet or deck revision on the web for anyone at its publishedLink (publish_auto follows later revisions, published_outside_domain lets people outside the domain read it): only on the user's explicit go.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `file_id` (string, required): The file's id, or its URL.
- `keep_forever` (boolean)
- `publish_auto` (boolean)
- `published` (boolean)
- `published_outside_domain` (boolean)
- `revision_id` (string, required): The revision's id, from list_revisions.

### `update_task` (~166 tokens)

Update task

Change a task's title, notes or due date. Only the fields given change, and only when they differ: an empty notes or due clears it. To mark it done use complete_task; to move it, move_task.

Input parameters:

- `account` (string, required): Which account this writes to: a label from list_accounts. Required; writes have no default.
- `due` (string): YYYY-MM-DD, or "" to clear the due date.
- `notes` (string): New notes; "" clears them.
- `task_id` (string, required): The taskId from list_tasks.
- `task_list` (string): The list's taskListId from list_task_lists or list_tasks; "@default" (the default) is the account's default list.
- `title` (string)

### `update_values` (~181 tokens)

Write cell values

Values or formulas into several A1 ranges in one call (values.batchUpdate). Returns the cells updated per range (and what they now show, with include_values). write_range is the one-range variant that returns what the cells held before.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `data` (array, required)
- `include_values` (boolean): Return the written cells as Sheets now renders them.
- `major_dimension` (string)
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.
- `value_input` (string): USER_ENTERED parses as if typed ("=SUM(A:A)" a formula, "2026-10-08" a date, "12.5" a number); RAW stores strings as text.

### `upload_file` (~575 tokens)

Upload a file to Drive

Put a file on Drive byte for byte and prove it: after the upload, Drive's own checksum of what it stored (or the bytes read back, when Drive reports none) is compared with the source's. A difference fails the call and is taken back: a replacement's damaged revision is deleted, a new file is trashed. The file goes as path (a file in your workspace folders (the roots this client shares with the server; without them /workdir); a bare name is taken from the first) or as content_base64 with its sha256. Never type or paste a file's base64 yourself: a retyped file can arrive changed. Give the path of a file in your workspace folders (the roots this client shares; without them /workdir), or have a script that read the file send content_base64 with its sha256. The folder is folder_id, else folder_path, else the root. A name already in the folder is refused unless if_exists says what to do. A file given by path keeps its modification time on Drive. Returns the stored file with its link, size, sha256, md5 and verifiedBy. With convert, an Office file (.docx, .doc, .xlsx, .xls, .csv, .pptx, .ppt) becomes a Google Doc, Sheet or deck instead, named without its extension; a converted file keeps no bytes to compare, so the check is that Drive made the Google type, and the result gives the source's size and sha256 under `source`.

Input parameters:

- `content_base64` (string): The bytes as standard base64, only as a script that read the file wrote them, never typed. Needs sha256 and filename.
- `convert` (boolean): Upload an Office file as the Google Doc, Sheet or deck it converts to. Default false: the bytes as they are.
- `filename` (string): The name on Drive; defaults to the file's (the same key a Gmail attachment takes).
- `folder_id` (string): The destination folder's id. Takes precedence over folder_path.
- `folder_path` (string): The destination folder's path under My Drive; empty is the root.
- `if_exists` (string): A file of that name already there: "fail" (default), "replace" (new revision, same id), or "keep_both".
- `mime_type` (string): Defaults to a guess from the name.
- `path` (string): The file as a file in your workspace folders (the roots this client shares with the server; without them /workdir); a bare name is taken from the first. Links are refused.
- `sha256` (string): Hex SHA-256 of the source file. Required with content_base64, checked with path.

### `upsert_rows` (~212 tokens)

Upsert rows by key

Update the rows whose `key` column matches (only the columns given change), append the rest. One atomic batch. A key on two sheet rows is refused.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `header_row` (integer): The row that names the columns (1-based).
- `key` (string, required): The header name of the column that identifies a row.
- `rows` (array, required): Objects keyed by the header row's names. Numbers and booleans are stored as such, "=..." as a formula, "YYYY-MM-DD" (or with THH:MM) as a date, any other string as text; null clears a cell. A key the…
- `sheet` (string): The sheet (tab) name, from list_sheets. Empty means the first sheet.
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.

### `write_range` (~173 tokens)

Write a range

Values or formulas into an A1 range that names its sheet ("Invoices!A2:C4"), parsed as if typed (USER_ENTERED): "=SUM(B2:B9)" is a formula, "28/09/2026" a date in the sheet's locale. Returns `before`, what those cells held (formulas as formulas), so the write can be undone by writing it back.

Input parameters:

- `account` (string, required): Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default.
- `range` (string, required): A1 range with its sheet, e.g. "Sheet1!A1:D20".
- `spreadsheet_id` (string, required): The spreadsheet's id, or its URL.
- `values` (array, required): Rows of cells, top to bottom.

## Diagnostics

Captured diagnostic sections: Provenance, Dependencies. The full working is on the page: https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#diagnostics

## Score history

- 2026-11-05: 81
- 2026-11-04: 80
- 2026-11-03: 80
- 2026-11-02: 53
- 2026-11-01: 79
- 2026-10-31: 63
- 2026-10-30: 78
- 2026-10-29: 77
- 2026-10-28: 77
- 2026-10-27: 76
- 2026-10-26: 76

## Common questions

### What is the Google Workspace MCP server?

Google Workspace is an MCP server listed in the public MCP registry as com.foreverworks/google. Search and work in Gmail, Calendar, Drive, Docs, Sheets and Slides across several Google accounts. This page covers its npm package (@foreverworks/mcp-google).

### Is the Google Workspace MCP server safe to use?

Google Workspace scores 81 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 5 November 2026. It declares no install or post-install scripts. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the Google Workspace MCP server expose?

Google Workspace exposes 115 tools: add_chart, add_comment, agenda, append_rows, append_to_document, and 110 more. Their descriptions and schemas cost roughly 23,242 tokens of context every time the server is loaded.

### Is the Google Workspace MCP server still maintained?

Google Workspace is still listed as active in the MCP registry. We last reached this channel on 5 November 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the Google Workspace MCP server under?

Google Workspace declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- npm package: https://www.npmjs.com/package/@foreverworks/mcp-google
- Socket report: https://socket.dev/npm/package/@foreverworks/mcp-google
- Repository: https://github.com/foreverworks/mcps
- Website: https://foreverworks.github.io/mcps/servers/google/
- Changelog RSS feed: https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google.xml
- Changelog JSON feed: https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google.json
- HTML version of this page: https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google
