{"$schema":"https://verifymcp.io/schemas/trust-report.json","schemaVersion":1,"license":{"name":"CC BY-NC 4.0","url":"https://creativecommons.org/licenses/by-nc/4.0/"},"generatedAt":"2026-11-05T15:52:20.317Z","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google","markdownUrl":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google.md","server":{"slug":"com-foreverworks-google","name":"Google Workspace","registryName":"com.foreverworks/google","description":"Search and work in Gmail, Calendar, Drive, Docs, Sheets and Slides across several Google accounts.","registryStatus":"active","deletedAt":null,"statusMessage":null,"liveness":"live","publisher":{"class":"unknown","claimed":false}},"component":{"slug":"foreverworks-mcp-google","type":"npm","identifier":"@foreverworks/mcp-google","withdrawn":false,"url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google","version":"0.2.0","transport":"stdio","scoredAt":"2026-11-05T03:13:23.043Z"},"score":{"total":81,"tier":"high trust","status":"scored","weekDelta":4,"history":[{"date":"2026-11-05","total":81},{"date":"2026-11-04","total":80},{"date":"2026-11-03","total":80},{"date":"2026-11-02","total":53},{"date":"2026-11-01","total":79},{"date":"2026-10-31","total":63},{"date":"2026-10-30","total":78},{"date":"2026-10-29","total":77},{"date":"2026-10-28","total":77},{"date":"2026-10-27","total":76},{"date":"2026-10-26","total":76}]},"categories":[{"name":"Supply Chain Security","score":99,"verdict":"partial","reasons":["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."],"codes":[{"code":"supplychain.malware_clean","args":{}},{"code":"cve.none","args":{}},{"code":"installscript.none","args":{}},{"code":"dephealth.assessed","args":{"deps":"37","unhealthy":"7"}}]},{"name":"Provenance & Transparency","score":100,"verdict":"pass","reasons":["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)."],"codes":[{"code":"provenance.repo_verified","args":{}},{"code":"provenance.verified","args":{"repo":"foreverworks/mcps"}},{"code":"license.osi","args":{"spdx":"MIT"}},{"code":"maintenance.recent","args":{"age_days":"2"}},{"code":"disclosure.present","args":{}}]},{"name":"Schema Quality & AI Usability","score":57,"verdict":"fail","reasons":["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."],"codes":[{"code":"schema.ai_quality","args":{"band":"good"}},{"code":"schema.footprint_heavy","args":{"avg":"204","items":"115","tools":"115","tokens":"23539","resources":"0"}},{"code":"schema.examples_absent","args":{}}]},{"name":"Stability & Change Management","score":33,"verdict":"partial","reasons":["Stability observed for 10 of 30 days with no destabilising changes; credit accrues until the full window elapses."],"codes":[{"code":"stability.building_history","args":{"days":"10"}}]},{"name":"Tool Coverage","score":93,"verdict":"partial","reasons":["100% of tools have a non-trivial description (not blank, and not just the tool's name).","80% of tool parameters carry a description."],"codes":[{"code":"toolquality.descriptions","args":{"pct":"100"}},{"code":"toolquality.param_docs","args":{"pct":"80"}}]},{"name":"Tool Safety","score":75,"verdict":"fail","reasons":["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."],"codes":[{"code":"toolsafety.injection_clean","args":{}},{"code":"toolsafety.destructive_undeclared","args":{"pct":"0","tool":"delete_comment","verb":"delete","declared":"none","annotated":"0","destructive":"9"}},{"code":"toolsafety.manipulation_clean","args":{"units":"116","judged":"116"}}]},{"name":"Capabilities","score":100,"verdict":"pass","reasons":["Implements a current MCP spec version (2026-07-28)."],"codes":[{"code":"protocol.current","args":{"version":"2026-07-28"}}]}],"inconclusive":[],"malware":[],"components":[{"slug":"foreverworks-mcp-google","type":"npm","identifier":"@foreverworks/mcp-google","withdrawn":false,"score":81,"current":true,"url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google","markdownUrl":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google.md"}],"facts":[{"label":"Registry","value":"npm"},{"label":"Package","value":"@foreverworks/mcp-google"},{"label":"Version","value":"0.2.0"},{"label":"Transport","value":"stdio"}],"install":{"available":true,"bundleSha256":null},"tools":[{"name":"add_chart","title":"Add a chart","description":"A chart over a data range: the first column is the domain (x axis, pie labels), the other columns (or series_columns) the series.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"anchor_cell","type":"string","required":true,"description":"Where the chart's top-left corner sits, e.g. \"Sheet1!H2\"."},{"name":"data_range","type":"string","required":true,"description":"A1 range, e.g. \"Sheet1!A1:D20\", \"B:B\" or \"2:2\". No sheet name means the first sheet."},{"name":"headers","type":"boolean","required":false,"description":"Whether the data's first row names the series."},{"name":"series_columns","type":"array","required":false,"description":"Column letters to plot, e.g. [\"C\",\"E\"]. Empty means every column after the first."},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."},{"name":"title","type":"string","required":false,"description":null},{"name":"type","type":"string","required":true,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":209},{"name":"add_comment","title":"Comment on a file","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"anchor","type":"string","required":false,"description":"Drive's anchor JSON, for non-Google files."},{"name":"content","type":"string","required":true,"description":null},{"name":"file_id","type":"string","required":true,"description":"The file's id, or its URL."},{"name":"quoted","type":"string","required":false,"description":"The passage the comment is about."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":158},{"name":"agenda","title":"Agenda","description":"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).","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts, or \"*\" for every account at once (the default)."},{"name":"calendars","type":null,"required":false,"description":"\"selected\" (what the Calendar app shows, the default), \"primary\" (each account's own calendar only), \"all\", or a list of calendar ids or names."},{"name":"days","type":"integer","required":false,"description":"Window length when `to` is empty; at most 366."},{"name":"from","type":"string","required":false,"description":"YYYY-MM-DD or a time. Empty means today (UTC)."},{"name":"include_declined","type":"boolean","required":false,"description":null},{"name":"include_tasks","type":"boolean","required":false,"description":"Also the open tasks due on these days, from the same accounts' Google Tasks."},{"name":"limit","type":"integer","required":false,"description":null},{"name":"query","type":"string","required":false,"description":"Free-text search over title, description, location and guests."},{"name":"to","type":"string","required":false,"description":"Exclusive end. Empty means `days` after from."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":361},{"name":"append_rows","title":"Append rows","description":"Rows after the last one holding data, keyed by the header, formatted like the row above. One atomic batch.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"header_row","type":"integer","required":false,"description":"The row that names the columns (1-based)."},{"name":"rows","type":"array","required":true,"description":"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…"},{"name":"sheet","type":"string","required":false,"description":"The sheet (tab) name, from list_sheets. Empty means the first sheet."},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":184},{"name":"append_to_document","title":"Append to a Google Doc","description":"Markdown added after the document's last paragraph, in one batch.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"document_id","type":"string","required":true,"description":"The document's id, or its URL."},{"name":"expected_revision","type":"string","required":false,"description":"The revisionId read_document returned. When given, the edit is refused if the document has changed since."},{"name":"markdown","type":"string","required":true,"description":"Markdown: # headings, paragraphs, - and 1. lists (nested by indent), **bold**, *italic*, ~~strike~~, `code`, ``` fences, [links](url), | tables |."},{"name":"tab","type":"string","required":false,"description":"A tab other than the first: its id (t.abc) or title, from get_document. Empty means the first tab."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":177},{"name":"append_values","title":"Append rows after a table","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"include_values","type":"boolean","required":false,"description":null},{"name":"insert_data","type":"string","required":false,"description":null},{"name":"major_dimension","type":"string","required":false,"description":null},{"name":"range","type":"string","required":true,"description":"Where the table is, e.g. \"Sheet1!A1\" or \"Sheet1!A:E\"."},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."},{"name":"value_input","type":"string","required":false,"description":"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."},{"name":"values","type":"array","required":true,"description":"Rows of cells (columns with major_dimension COLUMNS). null leaves a cell as it is; \"\" empties it."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":246},{"name":"batch_update_document","title":"Apply Docs API requests","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"document_id","type":"string","required":true,"description":"The document's id, or its URL."},{"name":"expected_revision","type":"string","required":false,"description":"The revisionId these requests were computed from; the batch is refused if the document is no longer at it."},{"name":"images","type":"object","required":false,"description":"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…"},{"name":"requests","type":"array","required":true,"description":"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,…"},{"name":"target_revision","type":"string","required":false,"description":"Instead: apply as of this revisionId, with Docs moving the positions over edits made since."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":641},{"name":"batch_update_presentation","title":"Apply Slides API requests","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"expected_revision","type":"string","required":false,"description":"The revisionId these requests were computed from; the batch is refused if the deck is no longer at it."},{"name":"images","type":"object","required":false,"description":"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…"},{"name":"presentation_id","type":"string","required":true,"description":"The presentation's id, or its URL."},{"name":"requests","type":"array","required":true,"description":"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_…"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":587},{"name":"batch_update_spreadsheet","title":"Apply Sheets API requests","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"requests","type":"array","required":true,"description":"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…"},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":639},{"name":"begin_consent","title":"Begin a Google consent","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"The account label, as list_accounts names it."},{"name":"grant","type":"string","required":false,"description":"Gmail only: the grant to consent. modify is the only one this server holds."},{"name":"product","type":"string","required":true,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":165},{"name":"clear_values","title":"Clear cell values","description":"Empty the values of several A1 ranges (or the cells data_filters choose), keeping their formatting, validation and notes. Returns the ranges cleared.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"data_filters","type":"array","required":false,"description":"Instead of A1 ranges: Sheets DataFilters, each one of {\"a1Range\": \"Sheet1!A1:B2\"}, {\"gridRange\": {...}} or {\"developerMetadataLookup\": {\"metadataKey\": \"...\", ...}}: cells found by the developer metad…"},{"name":"ranges","type":"array","required":false,"description":"A1 ranges, each naming its sheet: \"Sheet1!A1:D20\", \"Sheet1!B:B\", \"'Q3 plan'!2:2\", or a sheet name alone."},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":194},{"name":"complete_consent","title":"Complete a Google consent","description":"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.","inputs":[{"name":"redirect_url","type":"string","required":true,"description":"The whole URL from the browser's address bar."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":94},{"name":"complete_task","title":"Complete task","description":"Mark a task done, or open again with completed: false. A task already in that state is left as it is.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes to: a label from list_accounts. Required; writes have no default."},{"name":"completed","type":"boolean","required":false,"description":null},{"name":"task_id","type":"string","required":true,"description":"The taskId from list_tasks."},{"name":"task_list","type":"string","required":false,"description":"The list's taskListId from list_task_lists or list_tasks; \"@default\" (the default) is the account's default list."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":114},{"name":"copy_file","title":"Copy a file","description":"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.","inputs":[{"name":"destination_id","type":"string","required":true,"description":null},{"name":"expected_modified","type":"string","required":false,"description":"Refuse if source modifiedTime differs from this observed value."},{"name":"file_id","type":"string","required":true,"description":null},{"name":"name","type":"string","required":false,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":94},{"name":"copy_sheet_to","title":"Copy a sheet to another spreadsheet","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"destination_spreadsheet_id","type":"string","required":false,"description":"The spreadsheet to copy into, by id or URL. Empty means the same one."},{"name":"sheet","type":"string","required":false,"description":"The sheet's title or sheetId. Empty means the first sheet."},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":144},{"name":"create_deck_from_outline","title":"Create a deck from an outline","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"folder_id","type":"string","required":false,"description":"Drive folder id to create it in. Takes precedence over folder_path."},{"name":"folder_path","type":"string","required":false,"description":"Folder path under My Drive, e.g. \"Clients/Acme\". Both empty means the root."},{"name":"keep_template_slides","type":"boolean","required":false,"description":null},{"name":"outline","type":"array","required":true,"description":null},{"name":"template_id","type":"string","required":true,"description":"The template deck's id, or its URL."},{"name":"title","type":"string","required":true,"description":"The new deck's name."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":187},{"name":"create_document","title":"Create a Google Doc","description":"A new Google Doc from markdown, in a Drive folder or My Drive's root. Returns its id and link.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"folder_id","type":"string","required":false,"description":"Drive folder id to create it in. Takes precedence over folder_path."},{"name":"folder_path","type":"string","required":false,"description":"Folder path under My Drive, e.g. \"Clients/Acme\". Both empty means the root."},{"name":"markdown","type":"string","required":false,"description":"Markdown: # headings, paragraphs, - and 1. lists (nested by indent), **bold**, *italic*, ~~strike~~, `code`, ``` fences, [links](url), | tables |."},{"name":"title","type":"string","required":true,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":165},{"name":"create_draft","title":"Create draft","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"attachments","type":"array","required":false,"description":"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…"},{"name":"bcc","type":"array","required":false,"description":"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."},{"name":"body_html","type":"string","required":false,"description":"HTML body. Given alone, a plain-text alternative is generated from it; give both to control each."},{"name":"body_text","type":"string","required":false,"description":"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."},{"name":"cc","type":"array","required":false,"description":"Cc recipients. Each entry is an address, \"Name <address>\", or a comma-separated list of those."},{"name":"from","type":"string","required":false,"description":"Send as this address. It must be one of the account's verified send-as aliases; defaults to the account's default."},{"name":"subject","type":"string","required":false,"description":null},{"name":"to","type":"array","required":false,"description":"Recipients. Each entry is an address, \"Name <address>\", or a comma-separated list of those."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":393},{"name":"create_event","title":"Create event","description":"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).","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes to: a label from list_accounts. Required; writes have no default."},{"name":"attendees","type":"array","required":false,"description":"Guest email addresses. On an update this REPLACES the guest list; the result lists who that added and removed."},{"name":"auto_decline","type":"string","required":false,"description":"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…"},{"name":"busy","type":"boolean","required":false,"description":"Whether it blocks time (\"show as busy\"). Default true."},{"name":"calendar_id","type":"string","required":false,"description":"Calendar id, from list_calendars or from an agenda event's calendarId. \"primary\" is the account's own."},{"name":"chat_status","type":"string","required":false,"description":"Focus time only: the status Google Chat shows meanwhile."},{"name":"decline_message","type":"string","required":false,"description":"Focus time and out of office only: the note a decline carries."},{"name":"description","type":"string","required":false,"description":null},{"name":"duration_minutes","type":"integer","required":false,"description":"Length when end is omitted. Default: an hour for a new event; on an update, the length it already had."},{"name":"end","type":"string","required":false,"description":"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."},{"name":"event_type","type":"string","required":false,"description":"\"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…"},{"name":"location","type":"string","required":false,"description":null},{"name":"meet","type":"boolean","required":false,"description":"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."},{"name":"recurrence","type":"array","required":false,"description":"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."},{"name":"reminders_minutes","type":"array","required":false,"description":"Popup reminders, in minutes before the start, replacing the calendar's defaults. [] means none."},{"name":"send_updates","type":"string","required":false,"description":"Who is emailed about this change: \"none\" (the default), \"all\" guests, or \"externalOnly\" (guests outside the domain)."},{"name":"start","type":"string","required":false,"description":"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."},{"name":"title","type":"string","required":false,"description":"The event's title."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":678},{"name":"create_filter","title":"Create filter","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"add","type":"array","required":false,"description":"Labels to add (names or ids). Create a missing one first with create_label."},{"name":"discard","type":"boolean","required":false,"description":"Allow add to hold TRASH or SPAM: arriving mail then goes there unseen."},{"name":"from","type":"string","required":false,"description":"Sender: an address, a domain (e.g. news.example.com), or Gmail's from: syntax."},{"name":"has_attachment","type":"boolean","required":false,"description":null},{"name":"negated_query","type":"string","required":false,"description":"\"Doesn't have\": none of these words."},{"name":"query","type":"string","required":false,"description":"Any Gmail search (\"Has the words\"), e.g. 'list:github.com OR from:notifications@github.com'."},{"name":"remove","type":"array","required":false,"description":"Labels to remove: INBOX skips the inbox, UNREAD marks read."},{"name":"subject","type":"string","required":false,"description":"Words in the subject."},{"name":"to","type":"string","required":false,"description":"Recipient, as in Gmail's to:."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":348},{"name":"create_folder","title":"Create a folder","description":"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.","inputs":[{"name":"path","type":"string","required":true,"description":"Full path under My Drive."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":58},{"name":"create_label","title":"Create label","description":"Create a user label. Nest it under another with a slash in the name, e.g. 'Clients/Acme'.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"background_color","type":"string","required":false,"description":"Hex from Gmail's fixed palette, e.g. #16a766. Needs text_color too."},{"name":"label_list_visibility","type":"string","required":false,"description":"In the label list on the left."},{"name":"message_list_visibility","type":"string","required":false,"description":"On messages in the message list."},{"name":"name","type":"string","required":true,"description":null},{"name":"text_color","type":"string","required":false,"description":"Hex from Gmail's fixed palette, e.g. #ffffff."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":148},{"name":"create_presentation","title":"Create a blank deck","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"folder_id","type":"string","required":false,"description":"Drive folder id to create it in. Takes precedence over folder_path."},{"name":"folder_path","type":"string","required":false,"description":"Folder path under My Drive, e.g. \"Clients/Acme\". Both empty means the root."},{"name":"title","type":"string","required":true,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":141},{"name":"create_spreadsheet","title":"Create a spreadsheet","description":"A new Google Sheet, optionally with named sheets and bold, frozen header rows, in a Drive folder or My Drive's root.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"folder_id","type":"string","required":false,"description":"Drive folder id to create it in. Takes precedence over folder_path."},{"name":"folder_path","type":"string","required":false,"description":"Folder path under My Drive, e.g. \"Clients/Acme\". Both empty means the root."},{"name":"sheets","type":"array","required":false,"description":"Sheets to create, in order. Empty keeps the one default sheet."},{"name":"title","type":"string","required":true,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":141},{"name":"create_task","title":"Create task","description":"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).","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes to: a label from list_accounts. Required; writes have no default."},{"name":"due","type":"string","required":false,"description":"The day it is due: YYYY-MM-DD. Google Tasks keeps a date only, never a time of day."},{"name":"notes","type":"string","required":false,"description":null},{"name":"parent","type":"string","required":false,"description":"A taskId in the same list: the new task becomes its subtask."},{"name":"previous","type":"string","required":false,"description":"A sibling's taskId: the new task goes right after it."},{"name":"task_list","type":"string","required":false,"description":"The list's taskListId from list_task_lists or list_tasks; \"@default\" (the default) is the account's default list."},{"name":"title","type":"string","required":true,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":198},{"name":"create_task_list","title":"Create task list","description":"A new Google Tasks list in one account; returns its taskListId.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes to: a label from list_accounts. Required; writes have no default."},{"name":"title","type":"string","required":true,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":56},{"name":"delete_comment","title":"Delete a comment or reply","description":"Delete a comment thread with its replies, or one reply with reply_id. Drive lets an account delete only what it wrote (owners excepted).","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"comment_id","type":"string","required":true,"description":"The comment's id, from list_comments."},{"name":"file_id","type":"string","required":true,"description":"The file's id, or its URL."},{"name":"reply_id","type":"string","required":false,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":106},{"name":"delete_draft","title":"Delete draft","description":"Delete a draft permanently (drafts do not go to Trash). The draft's message is gone with it.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"draft_id","type":"string","required":true,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":69},{"name":"delete_event","title":"Delete event","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes to: a label from list_accounts. Required; writes have no default."},{"name":"calendar_id","type":"string","required":false,"description":"Calendar id, from list_calendars or from an agenda event's calendarId. \"primary\" is the account's own."},{"name":"event_id","type":"string","required":true,"description":"The eventId from agenda."},{"name":"scope","type":"string","required":false,"description":"\"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."},{"name":"send_updates","type":"string","required":false,"description":"Who is emailed about this change: \"none\" (the default), \"all\" guests, or \"externalOnly\" (guests outside the domain)."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":218},{"name":"delete_filter","title":"Delete filter","description":"Delete a Gmail filter by id (list_filters). Mail it already filed keeps its labels.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"filter_id","type":"string","required":true,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":64},{"name":"delete_label","title":"Delete label","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"label","type":"string","required":true,"description":"Id or name."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":80},{"name":"describe_file","title":"Describe one file","description":"Everything Drive reports about a single file or folder, by id or path.","inputs":[{"name":"file_id","type":"string","required":false,"description":"Drive file id. Takes precedence over path."},{"name":"path","type":"string","required":false,"description":"Path under My Drive, e.g. \"Reports/2026\". Empty means the root."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":64},{"name":"download_file","title":"Download a file from Drive","description":"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).","inputs":[{"name":"file_id","type":"string","required":false,"description":"The file's id. Takes precedence over path."},{"name":"filename","type":"string","required":false,"description":"The saved name; defaults to Drive's, with the export's extension."},{"name":"format","type":"string","required":false,"description":"For a Google file only: what to save it as. Empty is its default."},{"name":"path","type":"string","required":false,"description":"The file's path under My Drive, e.g. \"Reports/summary.pdf\"."},{"name":"save_dir","type":"string","required":false,"description":"A folder inside /tmp/.local/state/mcps/google/drive/downloads to save into; empty is /tmp/.local/state/mcps/google/drive/downloads itself."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":288},{"name":"download_revision","title":"Save an old revision","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"file_id","type":"string","required":true,"description":"The file's id, or its URL."},{"name":"filename","type":"string","required":false,"description":"Defaults to the file's name with the revision id."},{"name":"format","type":"string","required":false,"description":"For a Google file: what to save it as."},{"name":"revision_id","type":"string","required":true,"description":"The revision's id, from list_revisions."},{"name":"save_dir","type":"string","required":false,"description":"A folder inside the downloads folder."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":176},{"name":"drive_changes","title":"What changed since the cursor","description":"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.","inputs":[{"name":"cursor","type":"string","required":true,"description":"A cursor from start_drive_cursor or from a previous drive_changes call."},{"name":"limit","type":"integer","required":false,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":96},{"name":"edit_comment","title":"Edit a comment or reply","description":"New text for a comment, or for one of its replies with reply_id. Drive lets an account edit only what it wrote.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"comment_id","type":"string","required":true,"description":"The comment's id, from list_comments."},{"name":"content","type":"string","required":true,"description":null},{"name":"file_id","type":"string","required":true,"description":"The file's id, or its URL."},{"name":"reply_id","type":"string","required":false,"description":"A reply's id, to edit the reply rather than the comment."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":123},{"name":"edit_document_section","title":"Replace a section of a Google Doc","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"clear","type":"boolean","required":false,"description":"With an empty markdown: delete everything under the heading."},{"name":"document_id","type":"string","required":true,"description":"The document's id, or its URL."},{"name":"drop_hidden","type":"boolean","required":false,"description":"Delete the images, footnotes and the like the section holds along with its text."},{"name":"expected_revision","type":"string","required":true,"description":"The revisionId read_document returned for the text this replaces. The edit is refused if the document has changed since."},{"name":"markdown","type":"string","required":true,"description":"Markdown: # headings, paragraphs, - and 1. lists (nested by indent), **bold**, *italic*, ~~strike~~, `code`, ``` fences, [links](url), | tables |."},{"name":"section","type":"string","required":true,"description":"The section's id, path or unique heading title, from read_document."},{"name":"tab","type":"string","required":false,"description":"A tab other than the first: its id (t.abc) or title, from get_document. Empty means the first tab."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":344},{"name":"editors_api_reference","title":"Docs, Sheets and Slides API reference","description":"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.","inputs":[{"name":"api","type":"string","required":true,"description":null},{"name":"depth","type":"integer","required":false,"description":"How many levels of referred types to expand."},{"name":"name","type":"string","required":false,"description":"A request kind or a type name. Empty lists the request kinds."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":177},{"name":"fill_placeholders","title":"Fill {{tags}} in a deck","description":"Replace every {{tag}} with its value (exact case) across the deck, or only the slides named. Reports how many of each were found.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"presentation_id","type":"string","required":true,"description":"The presentation's id, or its URL."},{"name":"slide_ids","type":"array","required":false,"description":"Slide object ids (read_deck) to limit it to. Empty means every slide."},{"name":"values","type":"object","required":true,"description":"Tag to text, e.g. {\"client\": \"Acme\"} fills {{client}}."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":134},{"name":"find_files","title":"Search Drive","description":"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.","inputs":[{"name":"full_text","type":"string","required":false,"description":"Search inside document contents, Google's own index."},{"name":"include_trashed","type":"boolean","required":false,"description":null},{"name":"limit","type":"integer","required":false,"description":null},{"name":"mime_type","type":"string","required":false,"description":"e.g. \"application/vnd.google-apps.folder\" or \"application/pdf\"."},{"name":"modified_after","type":"string","required":false,"description":"RFC 3339; a bare date means midnight UTC."},{"name":"name_contains","type":"string","required":false,"description":null},{"name":"parent_id","type":"string","required":false,"description":"Restrict to one folder's direct children."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":169},{"name":"find_in_document","title":"Find text in a Google Doc","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"document_id","type":"string","required":true,"description":"The document's id, or its URL."},{"name":"match_case","type":"boolean","required":false,"description":null},{"name":"max_results","type":"integer","required":false,"description":null},{"name":"regex","type":"boolean","required":false,"description":"text is a regular expression, e.g. \"\\{\\{[a-z_]+\\}\\}\" for placeholders."},{"name":"segments","type":"string","required":false,"description":null},{"name":"tab","type":"string","required":false,"description":"A tab's id (t.0) or its title. Empty means the first tab."},{"name":"text","type":"string","required":true,"description":null},{"name":"whole_word","type":"boolean","required":false,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":240},{"name":"find_people","title":"Find people","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts, or \"*\" for every account at once (the default)."},{"name":"limit","type":"integer","required":false,"description":"Per account and source, before merging."},{"name":"query","type":"string","required":true,"description":"A name or its start (\"Ana Pop\"), or the start of an address or a phone number."},{"name":"sources","type":"array","required":false,"description":"Where to look: \"contacts\", \"other\" (saved from mail) and \"directory\" (Workspace). Default all three."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":213},{"name":"find_time","title":"Find time","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts, or \"*\" for every account at once (the default)."},{"name":"attendees","type":"array","required":false,"description":"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…"},{"name":"buffer_after_minutes","type":"integer","required":false,"description":"Free time kept after each existing event."},{"name":"buffer_before_minutes","type":"integer","required":false,"description":"Free time kept before each existing event."},{"name":"calendars","type":null,"required":false,"description":"\"selected\" (what the Calendar app shows, the default), \"primary\" (each account's own calendar only), \"all\", or a list of calendar ids or names."},{"name":"days","type":"integer","required":false,"description":"Window length when `to` is empty; at most 62."},{"name":"duration_minutes","type":"integer","required":true,"description":"Meeting length."},{"name":"from","type":"string","required":false,"description":"YYYY-MM-DD or a time. Empty means today (UTC)."},{"name":"hours_end","type":"string","required":false,"description":"Working hours end, HH:MM local, exclusive; 24:00 is midnight. After hours_start."},{"name":"hours_start","type":"string","required":false,"description":"Working hours start, HH:MM local."},{"name":"max_results","type":"integer","required":false,"description":null},{"name":"min_notice_minutes","type":"integer","required":false,"description":"No slot starts sooner than this from now."},{"name":"prefer","type":"string","required":false,"description":"Rank: earliest first, or that half of the day (before/after 12:00 local) first."},{"name":"step_minutes","type":"integer","required":false,"description":"Slots start on this grid from hours_start: 30 gives :00 and :30."},{"name":"tentative_is_free","type":"boolean","required":false,"description":"Offer slots over tentative events and \"maybe\" answers."},{"name":"time_zone","type":"string","required":false,"description":"IANA zone for the working hours, the window and the answer. Empty means the server's zone (UTC)."},{"name":"to","type":"string","required":false,"description":"Exclusive end. Empty means `days` after from."},{"name":"working_days","type":"array","required":false,"description":"Days a slot may fall on, in the working-hours zone."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":557},{"name":"format_range","title":"Format a range","description":"Number format, bold/italic, colours, alignment, wrapping, borders and frozen rows/columns for a range. Only what is given changes.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"background","type":"string","required":false,"description":"\"#RRGGBB\"."},{"name":"bold","type":"boolean","required":false,"description":null},{"name":"borders","type":"object","required":false,"description":null},{"name":"foreground","type":"string","required":false,"description":"\"#RRGGBB\"."},{"name":"freeze_columns","type":"integer","required":false,"description":null},{"name":"freeze_rows","type":"integer","required":false,"description":"Rows frozen at the top of the range's sheet."},{"name":"horizontal_alignment","type":"string","required":false,"description":null},{"name":"italic","type":"boolean","required":false,"description":null},{"name":"number_format","type":"object","required":false,"description":null},{"name":"range","type":"string","required":true,"description":"A1 range, e.g. \"Sheet1!A1:D20\", \"B:B\" or \"2:2\". No sheet name means the first sheet."},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."},{"name":"vertical_alignment","type":"string","required":false,"description":null},{"name":"wrap","type":"string","required":false,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":224},{"name":"forward","title":"Forward","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"attachments","type":"array","required":false,"description":"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…"},{"name":"bcc","type":"array","required":false,"description":"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."},{"name":"body_html","type":"string","required":false,"description":"HTML body. Given alone, a plain-text alternative is generated from it; give both to control each."},{"name":"body_text","type":"string","required":false,"description":"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."},{"name":"cc","type":"array","required":false,"description":"Cc recipients. Each entry is an address, \"Name <address>\", or a comma-separated list of those."},{"name":"from","type":"string","required":false,"description":"Send as this address. It must be one of the account's verified send-as aliases; defaults to the account's default."},{"name":"include_attachments","type":"boolean","required":false,"description":"Carry the original's attachments. Default true. The images its body shows travel either way."},{"name":"message_id","type":"string","required":true,"description":"The message to forward (from get_thread)."},{"name":"send","type":"boolean","required":false,"description":"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…"},{"name":"subject","type":"string","required":false,"description":null},{"name":"to","type":"array","required":false,"description":"Recipients. Each entry is an address, \"Name <address>\", or a comma-separated list of those."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":491},{"name":"free_busy","title":"Free/busy","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts, or \"*\" for every account at once (the default)."},{"name":"attendees","type":"array","required":false,"description":"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…"},{"name":"calendars","type":null,"required":false,"description":"\"selected\" (what the Calendar app shows, the default), \"primary\" (each account's own calendar only), \"all\", or a list of calendar ids or names."},{"name":"days","type":"integer","required":false,"description":"Window length when `to` is empty; at most 62."},{"name":"from","type":"string","required":false,"description":"YYYY-MM-DD or a time. Empty means today (UTC)."},{"name":"to","type":"string","required":false,"description":"Exclusive end. Empty means `days` after from."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":286},{"name":"get_attachment","title":"Download attachment","description":"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\".","inputs":[{"name":"account","type":"string","required":false,"description":"Which registered mailbox: a label from list_accounts; empty means the default account."},{"name":"attachment_id","type":"string","required":true,"description":null},{"name":"filename","type":"string","required":false,"description":"Defaults to <attachment_id>.bin. Path separators are stripped."},{"name":"message_id","type":"string","required":true,"description":null},{"name":"save_dir","type":"string","required":false,"description":"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."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":213},{"name":"get_document","title":"Read a Google Doc's structure","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"document_id","type":"string","required":true,"description":"The document's id, or its URL."},{"name":"end_index","type":"integer","required":false,"description":"Only body elements that start before this index."},{"name":"fields","type":"string","required":false,"description":"format raw only: a field mask over the Document, e.g. \"revisionId,tabs(tabProperties)\"."},{"name":"format","type":"string","required":false,"description":null},{"name":"max_chars","type":"integer","required":false,"description":null},{"name":"parts","type":"array","required":false,"description":"What to include for the tab."},{"name":"start_index","type":"integer","required":false,"description":"Only body elements that end after this index."},{"name":"suggestions","type":"string","required":false,"description":"How suggested edits show: inline with their ids, as if accepted, or as if rejected. Default: Docs' choice for the account's access."},{"name":"tab","type":"string","required":false,"description":"A tab's id (t.0) or its title. Empty means the first tab."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":455},{"name":"get_draft","title":"Get draft","description":"One draft: its message (headers, Bcc included, bodies and attachments) and its webUrl.","inputs":[{"name":"account","type":"string","required":false,"description":"Which registered mailbox: a label from list_accounts; empty means the default account."},{"name":"detail","type":"string","required":false,"description":"As on get_message."},{"name":"draft_id","type":"string","required":true,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":71},{"name":"get_event","title":"Get event","description":"One event in full: description, guest list with each answer, reminders, meeting link, recurrence.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts; empty means the default account."},{"name":"calendar_id","type":"string","required":false,"description":"Calendar id, from list_calendars or from an agenda event's calendarId. \"primary\" is the account's own."},{"name":"event_id","type":"string","required":true,"description":"The eventId from agenda."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":95},{"name":"get_message","title":"Get message","description":"Fetch a single message by id. See get_thread for the detail levels and the account rule.","inputs":[{"name":"account","type":"string","required":false,"description":"Which registered mailbox: a label from list_accounts; empty means the default account."},{"name":"detail","type":"string","required":false,"description":"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…"},{"name":"message_id","type":"string","required":true,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":164},{"name":"get_messages","title":"Get messages","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which registered mailbox: a label from list_accounts; empty means the default account."},{"name":"detail","type":"string","required":false,"description":"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…"},{"name":"message_ids","type":"array","required":true,"description":"Message ids from one mailbox, as search_threads, get_thread or list_history report them."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":255},{"name":"get_presentation","title":"Read a deck's structure","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"fields","type":"string","required":false,"description":"format raw only: a field mask over the Presentation, e.g. \"slides(objectId,pageElements(objectId,image))\"."},{"name":"format","type":"string","required":false,"description":null},{"name":"from_slide","type":"integer","required":false,"description":"The first slide to show, by position (0-based)."},{"name":"max_chars","type":"integer","required":false,"description":null},{"name":"presentation_id","type":"string","required":true,"description":"The presentation's id, or its URL."},{"name":"slide_ids","type":"array","required":false,"description":"Only these slides, by object id. Empty means every slide."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":280},{"name":"get_spreadsheet","title":"Read a spreadsheet's structure","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"data_filters","type":"array","required":false,"description":"format raw, instead of ranges: DataFilters choosing the grid data (developer metadata lookups included)."},{"name":"fields","type":"string","required":false,"description":"format raw: a field mask over the Spreadsheet, e.g. \"sheets(properties,data(rowData(values(userEnteredFormat,note))))\"."},{"name":"format","type":"string","required":false,"description":null},{"name":"include_grid_data","type":"boolean","required":false,"description":"format raw: include each cell's data and formats for `ranges`."},{"name":"max_chars","type":"integer","required":false,"description":null},{"name":"ranges","type":"array","required":false,"description":"format raw: A1 ranges to limit the grid data to."},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":284},{"name":"get_thread","title":"Get thread","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which registered mailbox: a label from list_accounts; empty means the default account."},{"name":"detail","type":"string","required":false,"description":"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…"},{"name":"thread_id","type":"string","required":true,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":178},{"name":"get_thumbnails","title":"Slide thumbnails","description":"A PNG thumbnail URL per slide, rendered by Google. The URLs expire after about half an hour.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"presentation_id","type":"string","required":true,"description":"The presentation's id, or its URL."},{"name":"size","type":"string","required":false,"description":"SMALL is 200px wide, MEDIUM 800px, LARGE 1600px."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":95},{"name":"get_transcript","title":"Get transcript","description":"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/...).","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts; empty means the first one with a grant."},{"name":"max_chars","type":"integer","required":false,"description":null},{"name":"page_token","type":"string","required":false,"description":"nextPageToken from the previous part."},{"name":"transcript","type":"string","required":true,"description":"The transcript's name, from list_meetings."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":145},{"name":"get_values","title":"Read cell values","description":"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).","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"data_filters","type":"array","required":false,"description":"Instead of A1 ranges: Sheets DataFilters, each one of {\"a1Range\": \"Sheet1!A1:B2\"}, {\"gridRange\": {...}} or {\"developerMetadataLookup\": {\"metadataKey\": \"...\", ...}}: cells found by the developer metad…"},{"name":"date_time_render","type":"string","required":false,"description":"For dates when value_render is not FORMATTED_VALUE."},{"name":"major_dimension","type":"string","required":false,"description":null},{"name":"max_chars","type":"integer","required":false,"description":null},{"name":"ranges","type":"array","required":false,"description":"A1 ranges, each naming its sheet: \"Sheet1!A1:D20\", \"Sheet1!B:B\", \"'Q3 plan'!2:2\", or a sheet name alone."},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."},{"name":"value_render","type":"string","required":false,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":281},{"name":"harvest_attachments","title":"Harvest attachments for a query","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which registered mailbox: a label from list_accounts; empty means the default account."},{"name":"extensions","type":"array","required":false,"description":"Filter by extension, e.g. ['.pdf','.xml']. Empty means every attachment."},{"name":"max_attachments","type":"integer","required":false,"description":"Hard cap on files written. `truncated: true` in the result means you hit it."},{"name":"max_threads","type":"integer","required":false,"description":"Threads scanned. Page with the query."},{"name":"query","type":"string","required":false,"description":"Gmail query syntax. Narrow it with a date window."},{"name":"save_dir","type":"string","required":false,"description":"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."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":340},{"name":"health","title":"Health","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts, or \"*\" for every account (the default)."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":318},{"name":"link_sheet_cells","title":"Link existing text cells","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"links","type":"array","required":true,"description":null},{"name":"sheet","type":"string","required":true,"description":null},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":120},{"name":"list_accounts","title":"List accounts","description":"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.","inputs":[],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":225},{"name":"list_calendars","title":"List calendars","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts, or \"*\" for every account at once (the default)."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":72},{"name":"list_comments","title":"List a file's comments","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"file_id","type":"string","required":true,"description":"The file's id, or its URL."},{"name":"include_deleted","type":"boolean","required":false,"description":null},{"name":"include_resolved","type":"boolean","required":false,"description":null},{"name":"max_results","type":"integer","required":false,"description":null},{"name":"modified_after","type":"string","required":false,"description":"RFC 3339; only threads changed since (a new reply counts)."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":148},{"name":"list_drafts","title":"List drafts","description":"The account's drafts, newest first, with recipients, subject, snippet and each draft's webUrl.","inputs":[{"name":"account","type":"string","required":false,"description":"Which registered mailbox: a label from list_accounts; empty means the default account."},{"name":"max_results","type":"integer","required":false,"description":null},{"name":"page_token","type":"string","required":false,"description":null},{"name":"query","type":"string","required":false,"description":"Gmail query syntax to filter drafts. Empty means all."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":85},{"name":"list_filters","title":"List filters","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which registered mailbox: a label from list_accounts; empty means the default account."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":71},{"name":"list_folder","title":"List a folder","description":"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.","inputs":[{"name":"file_id","type":"string","required":false,"description":"Drive file id. Takes precedence over path."},{"name":"include_trashed","type":"boolean","required":false,"description":null},{"name":"limit","type":"integer","required":false,"description":null},{"name":"path","type":"string","required":false,"description":"Path under My Drive, e.g. \"Reports/2026\". Empty means the root."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":108},{"name":"list_history","title":"List history","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which registered mailbox: a label from list_accounts; empty means the default account."},{"name":"max_results","type":"integer","required":false,"description":null},{"name":"page_token","type":"string","required":false,"description":null},{"name":"start_history_id","type":"string","required":true,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":131},{"name":"list_labels","title":"List labels","description":"List Gmail labels (system and user-defined) for one account.","inputs":[{"name":"account","type":"string","required":false,"description":"Which registered mailbox: a label from list_accounts; empty means the default account."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":43},{"name":"list_layouts","title":"List a deck's layouts","description":"A deck's (or template's) layouts, by name, and the placeholders each has (TITLE, SUBTITLE, BODY, ...).","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"presentation_id","type":"string","required":true,"description":"The presentation's id, or its URL."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":78},{"name":"list_meetings","title":"List meetings","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts; empty means the first one with a grant."},{"name":"calendar_id","type":"string","required":false,"description":"The event's calendarId, with event_id."},{"name":"days","type":"integer","required":false,"description":"Without event_id or meeting: how many days back."},{"name":"event_id","type":"string","required":false,"description":"An event's eventId from agenda, in this account's calendar."},{"name":"limit","type":"integer","required":false,"description":"The newest meetings, at most."},{"name":"meeting","type":"string","required":false,"description":"A Meet link (https://meet.google.com/abc-defg-hij) or its code."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":256},{"name":"list_permissions","title":"Who has access to a file","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"file_id","type":"string","required":true,"description":"The file's id, or its URL."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":100},{"name":"list_revisions","title":"List a file's revisions","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"file_id","type":"string","required":true,"description":"The file's id, or its URL."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":104},{"name":"list_sheets","title":"List a spreadsheet's sheets","description":"A spreadsheet's title, locale, time zone and sheets (id, name, size, frozen rows).","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":71},{"name":"list_task_lists","title":"List task lists","description":"Every Google Tasks list of each account (every account by default), with the taskListId the other task tools take; the default list is marked.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts, or \"*\" for every account at once (the default)."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":65},{"name":"list_tasks","title":"List tasks","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts, or \"*\" for every account at once (the default)."},{"name":"due_from","type":"string","required":false,"description":"YYYY-MM-DD, inclusive."},{"name":"due_to","type":"string","required":false,"description":"YYYY-MM-DD, inclusive."},{"name":"include_completed","type":"boolean","required":false,"description":null},{"name":"limit","type":"integer","required":false,"description":"Per account."},{"name":"task_list","type":"string","required":false,"description":"A taskListId, \"@default\" for the default list, or empty (the default) for every list."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":204},{"name":"mark_spam","title":"Mark as spam","description":"Report threads or messages as spam: into SPAM and out of the inbox, as Gmail's own button does.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"ids","type":"array","required":true,"description":"Thread or message ids, per `target`, from this account."},{"name":"target","type":"string","required":false,"description":"Act on whole threads (default, what Gmail's own buttons do) or on single messages."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":106},{"name":"modify_labels","title":"Modify labels","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"add","type":"array","required":false,"description":null},{"name":"ids","type":"array","required":true,"description":"Thread or message ids, per `target`, from this account."},{"name":"remove","type":"array","required":false,"description":null},{"name":"target","type":"string","required":false,"description":"Act on whole threads (default, what Gmail's own buttons do) or on single messages."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":175},{"name":"modify_labels_matching","title":"Modify labels on every match","description":"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').","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"add","type":"array","required":false,"description":"Labels to add, by name or id."},{"name":"dry_run","type":"boolean","required":false,"description":"True (default): count and sample only. False: apply."},{"name":"limit","type":"integer","required":false,"description":"The most threads one call may change."},{"name":"query","type":"string","required":true,"description":"Gmail query syntax, e.g. 'in:inbox from:news.example.com' or 'in:inbox older_than:90d'."},{"name":"remove","type":"array","required":false,"description":"Labels to remove: INBOX archives, UNREAD marks read."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":275},{"name":"move_file","title":"Move files","description":"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.","inputs":[{"name":"destination_id","type":"string","required":false,"description":null},{"name":"destination_path","type":"string","required":false,"description":null},{"name":"dry_run","type":"boolean","required":false,"description":"Compute the plan and write nothing."},{"name":"rename_to","type":"string","required":false,"description":"Only legal with a single source."},{"name":"sources","type":"array","required":true,"description":"Paths, or \"id:<fileId>\"."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":160},{"name":"move_task","title":"Move task","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes to: a label from list_accounts. Required; writes have no default."},{"name":"parent","type":"string","required":false,"description":"The taskId it goes under; omit for the top level."},{"name":"previous","type":"string","required":false,"description":"The sibling taskId it goes after; omit for the first position."},{"name":"task_id","type":"string","required":true,"description":"The taskId from list_tasks."},{"name":"task_list","type":"string","required":false,"description":"The list's taskListId from list_task_lists or list_tasks; \"@default\" (the default) is the account's default list."},{"name":"to_list","type":"string","required":false,"description":"Another taskListId of the same account to move it into."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":186},{"name":"path_of","title":"Path of an id","description":"The reverse of resolve_path: where a file sits, as a path, by walking its parents to the root.","inputs":[{"name":"file_id","type":"string","required":true,"description":"Drive file id."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":42},{"name":"read_deck","title":"Read a deck","description":"Every slide's layout, title, other text (tables as rows) and speaker notes, in order.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"presentation_id","type":"string","required":true,"description":"The presentation's id, or its URL."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":71},{"name":"read_doc","title":"Read a Google Doc","description":"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.","inputs":[{"name":"file_id","type":"string","required":false,"description":null},{"name":"format","type":"string","required":false,"description":null},{"name":"max_chars","type":"integer","required":false,"description":null},{"name":"path","type":"string","required":false,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":90},{"name":"read_document","title":"Read a Google Doc","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"document_id","type":"string","required":true,"description":"The document's id, or its URL."},{"name":"max_chars","type":"integer","required":false,"description":null},{"name":"section","type":"string","required":false,"description":"A section id, path or unique heading title. Empty reads the whole document."},{"name":"tab","type":"string","required":false,"description":"A tab other than the first: its id (t.abc) or title, from get_document. Empty means the first tab."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":211},{"name":"read_rows","title":"Read rows","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"header_row","type":"integer","required":false,"description":"The row that names the columns (1-based)."},{"name":"limit","type":"integer","required":false,"description":null},{"name":"offset","type":"integer","required":false,"description":null},{"name":"sheet","type":"string","required":false,"description":"The sheet (tab) name, from list_sheets. Empty means the first sheet."},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":166},{"name":"read_sheet_cells","title":"Read formulas and linked cells","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"limit","type":"integer","required":false,"description":null},{"name":"offset","type":"integer","required":false,"description":null},{"name":"sheet","type":"string","required":false,"description":"The sheet (tab) name, from list_sheets. Empty means the first sheet."},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":148},{"name":"remove_permission","title":"Remove someone's access","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"file_id","type":"string","required":true,"description":"The file's id, or its URL."},{"name":"permission_id","type":"string","required":true,"description":"The permission's id, from list_permissions."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":120},{"name":"rename_file","title":"Rename a file","description":"Change a file's name in place. Names cannot contain a slash; Drive has no path, only parents.","inputs":[{"name":"file_id","type":"string","required":false,"description":"Drive file id. Takes precedence over path."},{"name":"new_name","type":"string","required":true,"description":null},{"name":"path","type":"string","required":false,"description":"Path under My Drive."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":65},{"name":"reply","title":"Reply","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"attachments","type":"array","required":false,"description":"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…"},{"name":"bcc","type":"array","required":false,"description":"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."},{"name":"body_html","type":"string","required":false,"description":"HTML body. Given alone, a plain-text alternative is generated from it; give both to control each."},{"name":"body_text","type":"string","required":false,"description":"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."},{"name":"cc","type":"array","required":false,"description":"Cc recipients. Each entry is an address, \"Name <address>\", or a comma-separated list of those."},{"name":"from","type":"string","required":false,"description":"Send as this address. It must be one of the account's verified send-as aliases; defaults to the account's default."},{"name":"message_id","type":"string","required":true,"description":"The message being answered (from get_thread)."},{"name":"quote","type":"boolean","required":false,"description":"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…"},{"name":"reply_all","type":"boolean","required":false,"description":"Also address the original's To and Cc, minus this account's own addresses."},{"name":"send","type":"boolean","required":false,"description":"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…"},{"name":"subject","type":"string","required":false,"description":null},{"name":"to","type":"array","required":false,"description":"Recipients. Each entry is an address, \"Name <address>\", or a comma-separated list of those."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":594},{"name":"reply_to_comment","title":"Reply to, resolve or reopen a comment","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"action","type":"string","required":false,"description":null},{"name":"comment_id","type":"string","required":true,"description":"The comment's id, from list_comments."},{"name":"content","type":"string","required":false,"description":null},{"name":"file_id","type":"string","required":true,"description":"The file's id, or its URL."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":116},{"name":"resolve_path","title":"Resolve a path to an id","description":"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.","inputs":[{"name":"path","type":"string","required":true,"description":"Path under My Drive."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":113},{"name":"respond_to_invite","title":"Respond to invite","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes to: a label from list_accounts. Required; writes have no default."},{"name":"calendar_id","type":"string","required":false,"description":"Calendar id, from list_calendars or from an agenda event's calendarId. \"primary\" is the account's own."},{"name":"event_id","type":"string","required":true,"description":"The eventId from agenda."},{"name":"response","type":"string","required":true,"description":null},{"name":"send_updates","type":"string","required":false,"description":"Who is emailed about this change: \"none\" (the default), \"all\" guests, or \"externalOnly\" (guests outside the domain)."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":160},{"name":"search_developer_metadata","title":"Find developer metadata","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which account: a label from list_accounts (its editors section). Empty means the default account."},{"name":"data_filters","type":"array","required":false,"description":"Instead of A1 ranges: Sheets DataFilters, each one of {\"a1Range\": \"Sheet1!A1:B2\"}, {\"gridRange\": {...}} or {\"developerMetadataLookup\": {\"metadataKey\": \"...\", ...}}: cells found by the developer metad…"},{"name":"metadata_id","type":"integer","required":false,"description":null},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":204},{"name":"search_threads","title":"Search threads","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which registered mailbox. A label from list_accounts, or \"*\" for every mailbox at once (the default here)."},{"name":"max_results","type":"integer","required":false,"description":"Per account, not in total."},{"name":"page_token","type":"string","required":false,"description":"From a previous call's per-account nextPageToken. Page ONE account at a time."},{"name":"query","type":"string","required":false,"description":"Gmail query syntax. Empty means the whole mailbox."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":181},{"name":"send_draft","title":"Send draft","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"draft_id","type":"string","required":true,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":111},{"name":"send_message","title":"Send message","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"attachments","type":"array","required":false,"description":"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…"},{"name":"bcc","type":"array","required":false,"description":"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."},{"name":"body_html","type":"string","required":false,"description":"HTML body. Given alone, a plain-text alternative is generated from it; give both to control each."},{"name":"body_text","type":"string","required":false,"description":"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."},{"name":"cc","type":"array","required":false,"description":"Cc recipients. Each entry is an address, \"Name <address>\", or a comma-separated list of those."},{"name":"from","type":"string","required":false,"description":"Send as this address. It must be one of the account's verified send-as aliases; defaults to the account's default."},{"name":"subject","type":"string","required":false,"description":null},{"name":"to","type":"array","required":false,"description":"Recipients. Each entry is an address, \"Name <address>\", or a comma-separated list of those."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":411},{"name":"sender_breakdown","title":"Sender breakdown","description":"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.","inputs":[{"name":"account","type":"string","required":false,"description":"Which registered mailbox: a label from list_accounts; empty means the default account."},{"name":"limit","type":"integer","required":false,"description":"Threads scanned at most; `more` says whether the query matched more."},{"name":"query","type":"string","required":false,"description":"Gmail query syntax; default the inbox."},{"name":"top","type":"integer","required":false,"description":"Domains listed; the rest are summed in otherDomains."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":181},{"name":"share_file","title":"Share a file","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"discoverable","type":"boolean","required":false,"description":"For domain and anyone: whether it shows up in search, rather than by link only."},{"name":"domain","type":"string","required":false,"description":"For domain, e.g. example.com."},{"name":"email_address","type":"string","required":false,"description":"For user and group."},{"name":"expiration_time","type":"string","required":false,"description":"RFC 3339: when the access ends."},{"name":"file_id","type":"string","required":true,"description":"The file's id, or its URL."},{"name":"message","type":"string","required":false,"description":"The email's text, with notify."},{"name":"notify","type":"boolean","required":false,"description":"Send Drive's email to the person or group."},{"name":"role","type":"string","required":true,"description":null},{"name":"transfer_ownership","type":"boolean","required":false,"description":null},{"name":"type","type":"string","required":true,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":269},{"name":"start_drive_cursor","title":"Mint a change cursor","description":"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.","inputs":[],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":47},{"name":"trash","title":"Trash","description":"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`.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"ids","type":"array","required":true,"description":"Thread or message ids, per `target`, from this account."},{"name":"target","type":"string","required":false,"description":"Act on whole threads (default, what Gmail's own buttons do) or on single messages."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":117},{"name":"trash_file","title":"Move to trash","description":"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.","inputs":[{"name":"file_id","type":"string","required":false,"description":"Drive file id. Takes precedence over path."},{"name":"path","type":"string","required":false,"description":"Path under My Drive."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":78},{"name":"unmark_spam","title":"Not spam","description":"Take threads or messages out of SPAM and back into the inbox.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"ids","type":"array","required":true,"description":"Thread or message ids, per `target`, from this account."},{"name":"target","type":"string","required":false,"description":"Act on whole threads (default, what Gmail's own buttons do) or on single messages."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":98},{"name":"untrash","title":"Untrash","description":"Bring threads or messages back out of Trash. Returns the ids restored, and any that failed under `failed`.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"ids","type":"array","required":true,"description":"Thread or message ids, per `target`, from this account."},{"name":"target","type":"string","required":false,"description":"Act on whole threads (default, what Gmail's own buttons do) or on single messages."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":104},{"name":"untrash_file","title":"Restore from trash","description":"Take a file back out of the trash. Find it by id; a trashed file no longer answers to its path.","inputs":[{"name":"file_id","type":"string","required":false,"description":"Drive file id. Takes precedence over path."},{"name":"path","type":"string","required":false,"description":"Path under My Drive."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":61},{"name":"update_draft","title":"Update draft","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"attachments","type":"array","required":false,"description":"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…"},{"name":"bcc","type":"array","required":false,"description":"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."},{"name":"body_html","type":"string","required":false,"description":"HTML body. Given alone, a plain-text alternative is generated from it; give both to control each."},{"name":"body_text","type":"string","required":false,"description":"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."},{"name":"cc","type":"array","required":false,"description":"Cc recipients. Each entry is an address, \"Name <address>\", or a comma-separated list of those."},{"name":"draft_id","type":"string","required":true,"description":null},{"name":"from","type":"string","required":false,"description":"Send as this address. It must be one of the account's verified send-as aliases; defaults to the account's default."},{"name":"subject","type":"string","required":false,"description":null},{"name":"to","type":"array","required":false,"description":"Recipients. Each entry is an address, \"Name <address>\", or a comma-separated list of those."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":390},{"name":"update_event","title":"Update event","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes to: a label from list_accounts. Required; writes have no default."},{"name":"attendees","type":"array","required":false,"description":"Guest email addresses. On an update this REPLACES the guest list; the result lists who that added and removed."},{"name":"auto_decline","type":"string","required":false,"description":"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…"},{"name":"busy","type":"boolean","required":false,"description":"Whether it blocks time (\"show as busy\"). Default true."},{"name":"calendar_id","type":"string","required":false,"description":"Calendar id, from list_calendars or from an agenda event's calendarId. \"primary\" is the account's own."},{"name":"chat_status","type":"string","required":false,"description":"Focus time only: the status Google Chat shows meanwhile."},{"name":"decline_message","type":"string","required":false,"description":"Focus time and out of office only: the note a decline carries."},{"name":"description","type":"string","required":false,"description":null},{"name":"duration_minutes","type":"integer","required":false,"description":"Length when end is omitted. Default: an hour for a new event; on an update, the length it already had."},{"name":"end","type":"string","required":false,"description":"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."},{"name":"event_id","type":"string","required":true,"description":"The eventId from agenda."},{"name":"location","type":"string","required":false,"description":null},{"name":"meet","type":"boolean","required":false,"description":"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."},{"name":"recurrence","type":"array","required":false,"description":"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."},{"name":"reminders_minutes","type":"array","required":false,"description":"Popup reminders, in minutes before the start, replacing the calendar's defaults. [] means none."},{"name":"scope","type":"string","required":false,"description":"\"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."},{"name":"send_updates","type":"string","required":false,"description":"Who is emailed about this change: \"none\" (the default), \"all\" guests, or \"externalOnly\" (guests outside the domain)."},{"name":"start","type":"string","required":false,"description":"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."},{"name":"title","type":"string","required":false,"description":"The event's title."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":719},{"name":"update_label","title":"Update label","description":"Rename or restyle a user label, given by id or name. System labels (INBOX, SENT, ...) are refused.","inputs":[{"name":"account","type":"string","required":true,"description":"Which mailbox this writes to or sends from: a label from list_accounts. Required; there is no default for writes."},{"name":"background_color","type":"string","required":false,"description":"Hex from Gmail's fixed palette, e.g. #16a766. Needs text_color too."},{"name":"label","type":"string","required":true,"description":"Id or current name."},{"name":"label_list_visibility","type":"string","required":false,"description":"In the label list on the left."},{"name":"message_list_visibility","type":"string","required":false,"description":"On messages in the message list."},{"name":"name","type":"string","required":false,"description":"New name."},{"name":"text_color","type":"string","required":false,"description":"Hex from Gmail's fixed palette, e.g. #ffffff."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":162},{"name":"update_permission","title":"Change someone's access","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"expiration_time","type":"string","required":false,"description":null},{"name":"file_id","type":"string","required":true,"description":"The file's id, or its URL."},{"name":"permission_id","type":"string","required":true,"description":"The permission's id, from list_permissions."},{"name":"remove_expiration","type":"boolean","required":false,"description":null},{"name":"role","type":"string","required":false,"description":null},{"name":"transfer_ownership","type":"boolean","required":false,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":148},{"name":"update_revision","title":"Keep or publish a revision","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"file_id","type":"string","required":true,"description":"The file's id, or its URL."},{"name":"keep_forever","type":"boolean","required":false,"description":null},{"name":"publish_auto","type":"boolean","required":false,"description":null},{"name":"published","type":"boolean","required":false,"description":null},{"name":"published_outside_domain","type":"boolean","required":false,"description":null},{"name":"revision_id","type":"string","required":true,"description":"The revision's id, from list_revisions."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":162},{"name":"update_task","title":"Update task","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes to: a label from list_accounts. Required; writes have no default."},{"name":"due","type":"string","required":false,"description":"YYYY-MM-DD, or \"\" to clear the due date."},{"name":"notes","type":"string","required":false,"description":"New notes; \"\" clears them."},{"name":"task_id","type":"string","required":true,"description":"The taskId from list_tasks."},{"name":"task_list","type":"string","required":false,"description":"The list's taskListId from list_task_lists or list_tasks; \"@default\" (the default) is the account's default list."},{"name":"title","type":"string","required":false,"description":null}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":166},{"name":"update_values","title":"Write cell values","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"data","type":"array","required":true,"description":null},{"name":"include_values","type":"boolean","required":false,"description":"Return the written cells as Sheets now renders them."},{"name":"major_dimension","type":"string","required":false,"description":null},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."},{"name":"value_input","type":"string","required":false,"description":"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."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":181},{"name":"upload_file","title":"Upload a file to Drive","description":"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`.","inputs":[{"name":"content_base64","type":"string","required":false,"description":"The bytes as standard base64, only as a script that read the file wrote them, never typed. Needs sha256 and filename."},{"name":"convert","type":"boolean","required":false,"description":"Upload an Office file as the Google Doc, Sheet or deck it converts to. Default false: the bytes as they are."},{"name":"filename","type":"string","required":false,"description":"The name on Drive; defaults to the file's (the same key a Gmail attachment takes)."},{"name":"folder_id","type":"string","required":false,"description":"The destination folder's id. Takes precedence over folder_path."},{"name":"folder_path","type":"string","required":false,"description":"The destination folder's path under My Drive; empty is the root."},{"name":"if_exists","type":"string","required":false,"description":"A file of that name already there: \"fail\" (default), \"replace\" (new revision, same id), or \"keep_both\"."},{"name":"mime_type","type":"string","required":false,"description":"Defaults to a guess from the name."},{"name":"path","type":"string","required":false,"description":"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."},{"name":"sha256","type":"string","required":false,"description":"Hex SHA-256 of the source file. Required with content_base64, checked with path."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":575},{"name":"upsert_rows","title":"Upsert rows by key","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"header_row","type":"integer","required":false,"description":"The row that names the columns (1-based)."},{"name":"key","type":"string","required":true,"description":"The header name of the column that identifies a row."},{"name":"rows","type":"array","required":true,"description":"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…"},{"name":"sheet","type":"string","required":false,"description":"The sheet (tab) name, from list_sheets. Empty means the first sheet."},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":212},{"name":"write_range","title":"Write a range","description":"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.","inputs":[{"name":"account","type":"string","required":true,"description":"Which account this writes as: a label from list_accounts (its editors section). Required; writes have no default."},{"name":"range","type":"string","required":true,"description":"A1 range with its sheet, e.g. \"Sheet1!A1:D20\"."},{"name":"spreadsheet_id","type":"string","required":true,"description":"The spreadsheet's id, or its URL."},{"name":"values","type":"array","required":true,"description":"Rows of cells, top to bottom."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":173}],"toolsTruncatedAtCapture":false,"diagnostics":{"capturedAt":"2026-11-05T03:13:23.000Z","track":"package","endpoint":null,"declaredUrl":null,"truncated":false,"tls":null,"dnssec":null,"auth":null,"httpsRedirect":null,"transports":[],"provenance":{"outcome":"verified","label":"Verified","reason":"verified","reasonLabel":"Verified","discovery":"attestation_endpoint","discoveryLabel":"Registry attestation endpoint","sourceRepo":null,"attestationSkipped":null,"attestationSkippedLabel":null},"installScripts":[],"installScriptsTotal":null,"vulnerabilities":[],"vulnerabilitiesTotal":null,"dependencies":{"nodes":37,"partial":false,"deprecated":null,"stale":7,"noRepo":null}},"mismatches":[],"changelog":{"trackingSince":"2026-10-26","recent":[{"id":"chg_01a1a9b2-6474-7132-930a-b28c136b6dfa","kind":"score.total_changed","summary":"Trust score (80 → 81)","materiality":"functional","direction":"improvement","scoreDelta":1,"occurredOn":"2026-11-05","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a1a9b2-6474-7132-930a-b28c136b6dfa"},{"id":"chg_01a19f61-d640-77bb-986a-46d861a9a87b","kind":"check.reverified","summary":"Malware scan (unverified → pass)","materiality":"security","direction":"improvement","scoreDelta":null,"occurredOn":"2026-11-03","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a19f61-d640-77bb-986a-46d861a9a87b"},{"id":"chg_01a19f61-d642-7449-983e-9faeea7ef8bc","kind":"check.reverified","summary":"Known CVEs (unverified → pass)","materiality":"security","direction":"improvement","scoreDelta":null,"occurredOn":"2026-11-03","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a19f61-d642-7449-983e-9faeea7ef8bc"},{"id":"chg_01a19f61-d642-7be2-ae89-f83e97fb5663","kind":"check.reverified","summary":"Dependency health (unverified → 0.90)","materiality":"functional","direction":"improvement","scoreDelta":null,"occurredOn":"2026-11-03","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a19f61-d642-7be2-ae89-f83e97fb5663"},{"id":"chg_01a19f61-d643-722c-8455-4694bdf74965","kind":"score.total_changed","summary":"Trust score (53 → 80)","materiality":"functional","direction":"improvement","scoreDelta":27,"occurredOn":"2026-11-03","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a19f61-d643-722c-8455-4694bdf74965"},{"id":"chg_01a19b3c-441a-7ac4-aabc-cf9a688167ca","kind":"check.unverifiable","summary":"Malware scan (pass → unverified)","materiality":"security","direction":"regression","scoreDelta":null,"occurredOn":"2026-11-02","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a19b3c-441a-7ac4-aabc-cf9a688167ca"},{"id":"chg_01a19b3c-441b-7efd-b9f8-0e8f5d1d8e81","kind":"check.unverifiable","summary":"Known CVEs (pass → unverified)","materiality":"security","direction":"regression","scoreDelta":null,"occurredOn":"2026-11-02","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a19b3c-441b-7efd-b9f8-0e8f5d1d8e81"},{"id":"chg_01a19b3c-441c-7567-a504-fe42894e6727","kind":"check.unverifiable","summary":"Dependency health (0.90 → unverified)","materiality":"functional","direction":"regression","scoreDelta":null,"occurredOn":"2026-11-02","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a19b3c-441c-7567-a504-fe42894e6727"},{"id":"chg_01a19b3c-441c-7def-aa3c-6d248ddf4386","kind":"package.version_changed","summary":"Package version (0.1.1 → 0.2.0)","materiality":"functional","direction":"neutral","scoreDelta":null,"occurredOn":"2026-11-02","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a19b3c-441c-7def-aa3c-6d248ddf4386"},{"id":"chg_01a19b3c-441d-73aa-ae62-6f5f42a4abcc","kind":"score.total_changed","summary":"Trust score (79 → 53)","materiality":"functional","direction":"regression","scoreDelta":-26,"occurredOn":"2026-11-02","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a19b3c-441d-73aa-ae62-6f5f42a4abcc"},{"id":"chg_01a19515-71e1-7777-bcec-8c841e300a90","kind":"check.reverified","summary":"Malware scan (unverified → pass)","materiality":"security","direction":"improvement","scoreDelta":null,"occurredOn":"2026-11-01","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a19515-71e1-7777-bcec-8c841e300a90"},{"id":"chg_01a19515-71e2-7ef7-932c-104c226ec2cc","kind":"score.total_changed","summary":"Trust score (63 → 79)","materiality":"functional","direction":"improvement","scoreDelta":16,"occurredOn":"2026-11-01","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a19515-71e2-7ef7-932c-104c226ec2cc"},{"id":"chg_01a19232-980e-72b7-8d77-c93d8b4aace4","kind":"check.unverifiable","summary":"Malware scan (pass → unverified)","materiality":"security","direction":"regression","scoreDelta":null,"occurredOn":"2026-10-31","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a19232-980e-72b7-8d77-c93d8b4aace4"},{"id":"chg_01a19232-980e-7a1f-b346-15c95cf91f62","kind":"package.version_changed","summary":"Package version (0.1.0 → 0.1.1)","materiality":"functional","direction":"neutral","scoreDelta":null,"occurredOn":"2026-10-31","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a19232-980e-7a1f-b346-15c95cf91f62"},{"id":"chg_01a19232-980f-72c1-b22b-26453aaa6db4","kind":"score.total_changed","summary":"Trust score (78 → 63)","materiality":"functional","direction":"regression","scoreDelta":-15,"occurredOn":"2026-10-31","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a19232-980f-72c1-b22b-26453aaa6db4"},{"id":"chg_01a18ac8-d74e-7d0f-91d0-8055dfa66b5a","kind":"score.total_changed","summary":"Trust score (77 → 78)","materiality":"functional","direction":"improvement","scoreDelta":1,"occurredOn":"2026-10-30","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a18ac8-d74e-7d0f-91d0-8055dfa66b5a"},{"id":"chg_01a1807d-c7e3-7204-9c9c-2ba08b7b7685","kind":"score.total_changed","summary":"Trust score (76 → 77)","materiality":"functional","direction":"improvement","scoreDelta":1,"occurredOn":"2026-10-28","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a1807d-c7e3-7204-9c9c-2ba08b7b7685"},{"id":"chg_01a176ff-4403-75ae-a9f7-78ed77cbdafb","kind":"score.first_scored","summary":"First indexed and scored","materiality":"functional","direction":"neutral","scoreDelta":null,"occurredOn":"2026-10-26","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a176ff-4403-75ae-a9f7-78ed77cbdafb"},{"id":"chg_01a176d1-80f3-739e-8c1c-f113b71ab322","kind":"score.first_scored","summary":"First indexed and scored","materiality":"functional","direction":"neutral","scoreDelta":null,"occurredOn":"2026-10-26","url":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google#chg-chg_01a176d1-80f3-739e-8c1c-f113b71ab322"}],"truncated":false,"feedUrl":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google/changelog.json"},"links":{"html":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google","markdown":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google.md","changelogJson":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google/changelog.json","changelogRss":"https://verifymcp.io/servers/com-foreverworks-google/foreverworks-mcp-google.xml","external":[{"label":"npm package","href":"https://www.npmjs.com/package/@foreverworks/mcp-google"},{"label":"Socket report","href":"https://socket.dev/npm/package/@foreverworks/mcp-google"},{"label":"Repository","href":"https://github.com/foreverworks/mcps"},{"label":"Website","href":"https://foreverworks.github.io/mcps/servers/google/"}]}}