Changelog

New tools, improvements, and fixes across the gateway and its connectors.

agentgateway

The agent now uses the same MCP you do

The agent in your dashboard used to connect straight to the connector servers behind the gateway. It had every connector tool that way, and none of the gateway's own, which is why it could run a Gmail search for you but could not answer "which accounts am I connected to". The one question it was most often asked about itself was the one question it had no tool for.

It is now a client of our MCP, the same surface your desktop client talks to. Concretely:

It can tell you what you have connected. Ask it, and it checks, the same way any other client would.

It can hand you your MCP config. Ask for it in the thread instead of going to find the setup page.

What it can do and what your own client can do no longer drift. There is one tool surface and both of you are looking at it. A tool that appears for one appears for the other, on the same day, because it is the same list.

The tool cards in the thread say what happened rather than which function ran: "Checking your connected accounts" instead of a snake_case id. Connector calls keep their literal tool names, deliberately, because someone auditing what the agent touched in their mailbox wants the exact name.

Nothing changes about how you connect, what the agent is allowed to do, or which calls ask you for approval first.

usagesheetsgateway

Google Workspace errors survive redaction, and one lookup stopped asking permission

A failed Google Workspace call now shows you the real error. Open a tool in your dashboard's usage view and each failed call carries a message. For a long time almost every one of those read [redacted-content], which told you a call failed and nothing else.

The cause was our redaction rule, not the errors. It treated any quoted string over 40 characters as your content, and a JSON error envelope quotes everything. So a Google message saying replacement text parameter is required but not specified was destroyed for being long. Every diagnostic worth reading was over the limit by construction.

Redaction is now by field rather than by shape. A recognised error envelope is parsed and rebuilt from an allowlist of diagnostic fields, so the code, the message and the reason survive. Everything kept is still scrubbed by the old rules, so an email address, a long file id, or a run of your document text quoted inside a diagnostic still gets redacted. Fields we do not recognise are dropped rather than passed through, and anything that does not parse takes the old blanket path unchanged. Ambiguity still resolves to "this is content".

The honest limit: only Google's envelope shape is recognised. Jira and Confluence return errors in a different shape, and some of our own messages are plain prose rather than an envelope at all. Both still take the blanket path, so a failed Atlassian call can still read [redacted-content] in your usage log.

We are not going to fix that by teaching the parser one more shape, and then the next one. The redaction rule exists to keep your content out of a third-party analytics tool, which is a real constraint that is not going anywhere. It was never a reason to censor the copy we store for you and show back to you, in a table only you can see, quoting an error you already received in full at the time of the call. So the stored copy is going to stop being redacted at all. That fixes Atlassian, our own messages, and every connector we add later, in one change.

sheets_find_rows no longer asks for approval in the agent. It searches a range and returns matching row numbers, and it changes nothing, so the prompt was never a judgement anyone made: the classifier fails closed on any tool it has not been told is a read, and this one had not been reviewed yet. sheets_update, sheets_format_range, sheets_format_table and sheets_batch_update still ask, and a tool the classifier does not recognise still asks.

docssheetsgws-mcp

Read part of a long document, and clearer errors in Sheets and Docs

Three changes, all of them prompted by real calls that failed.

docs_get can now read part of a document. Pass start_index and end_index and you get that slice back. They are the same character indices mode: "index" reports and docs_batch_update consumes, so a read and the edit that follows it speak one coordinate system rather than two.

A ranged response also tells you where you are: totalEndIndex for the document's full length, clipped for whether there is more, and nextStartIndex to continue from. That matters more than the range itself. Without it, a few thousand characters coming back looks identical whether the document ended or the answer was cut off, and those two need opposite next steps. With no range given, nothing changes and you get the whole document as before.

docs_batch_update now shows what a request looks like. The description carries worked examples for the common shapes, replaceAllText among them, including the detail that matchCase belongs inside containsText and not at the top level. That nesting is easy to get wrong and the API only tells you afterwards.

sheets_find_rows answers a bad tab name with the real ones. Ask for a range naming a tab that does not exist and instead of a raw parse error you get told there is no tab of that name, followed by the tabs the spreadsheet actually has. One round trip instead of a guess. The examples in the range parameter no longer name a tab that most spreadsheets do not have, which is where some of those wrong guesses were coming from in the first place.

drivegws-mcp

Rename and copy files in Drive

Drive could search, read and create folders. It could not rename anything, and it could not copy anything. Two tools close that.

drive_rename_file renames a file or folder. The name changes and nothing else does: content, location and sharing are untouched. It works on any Drive item the account can edit, Docs and Sheets and Slides and folders alike.

drive_copy_file copies a file and names the copy in the same call, optionally placing it in a different folder with parent_id.

The copy is the more useful of the two, and the reason is templates. Ask Claude to rebuild a template and you get something that looks close and drifts a little every time. Ask it to copy one and you get the original's tabs, formatting and formulas exactly, because it is the same file. Copy first, then fill in the copy.

One limit worth stating plainly: folders cannot be copied. Drive answers a folder copy with a 403 saying the file cannot be copied by the user, which reads like a permissions problem you should retry. It is not. It is how the API works, and no amount of access changes it.

gmailemailtools

Claude can send properly formatted email now

Until today, every email sent through DataToRAG went out as plain text. If you asked Claude for a formatted summary with links, the recipient got the HTML as visible tags.

gmail_send, gmail_create_draft and gmail_update_draft now take an html_body. The message goes out as multipart/alternative with a plain-text fallback, so clients that render HTML show your formatting and clients that do not still get something readable. Pass body as well and that becomes the fallback part; leave it out and we derive one from the HTML.

gmail_reply and gmail_forward take html_body too, and behave differently in one way worth knowing. They send single-part text/html with no plain-text alternative, because those paths hand quoting and threading to a composer that builds the whole message. Same parameter, same result in a normal mail client, but no fallback part. We would rather tell you that than have you find it.

One smaller fix came with it: gmail_forward now accepts a body. It never did. You could forward a message but not say anything above it.

Nothing changes if you were already sending plain text. html_body is optional everywhere and no existing call behaves differently.

sheetsformattingtools

Sheets Claude writes now look like someone made them

Writing values into a spreadsheet gives you no formatting at all. The default is columns 100 pixels wide with every long cell clipped to a slit, so a sheet with entirely correct data is routinely unreadable, and the reader never learns there was more text.

Four tools fix that, and they are live now.

sheets_format_table does the whole readable pass in one call: column widths, wrapped and top-aligned cells, a styled and frozen header row, hairline borders. This is the one to run on any sheet a person is going to open.

sheets_format_range is for specific styling on top: fonts, colours, alignment, padding, number formats, merges. It takes a list of instructions and applies them in one atomic call, so a whole formatting pass is one call rather than one per range.

sheets_batch_update is the escape hatch beneath both, for anything they do not cover: borders, banding, copy and paste, inserting or deleting columns, duplicating sheets.

sheets_find_rows is the one that saves the most work and is easiest to miss. It finds rows by value and returns their row numbers, so you can update them directly instead of reading a whole sheet and filtering it yourself. It searches many values in one call, so looking up twenty customers is one call and not twenty, and values that matched nothing come back listed separately, so an empty result cannot be mistaken for a broken one.

Nothing changes for existing calls. All four are additions.

connectionsoauthonboardingbugfix

Connecting Google now actually connects

If you connected Google and then found that nothing worked, this is why, and it was our fault rather than yours.

Google's consent screen brings the service checkboxes up unticked. You had just signed in, where clicking Continue was correct and sufficient, so clicking Continue again was the natural thing to do. It granted us nothing. And we recorded that as a successful connection, so your dashboard said Google was connected while every tool call failed.

Two changes.

We now tell you what is coming before we hand you to Google. A short page explains that the boxes come unticked and that "Select all" is the one control that matters. It sits on the connect route itself, so every path to connecting gets it: the dashboard, the connections page, the agent's inline prompt, links from the docs.

A consent that grants no services is now refused rather than recorded. If you continue without ticking anything, we do not write a connection at all. You land back with a clear message and a retry, instead of a dashboard that claims you are connected and a set of tools that quietly refuse.

A connection that granted only some services is unaffected and still works for what you did grant. The change is specific to grants that cover nothing, which we can now tell apart from a grant we simply could not read.

connectionsmulti-accountoauth

Choose which account your tool calls run as

If you have connected more than one Google account, one of them is the default: the account a tool call runs as when you do not name one. Until now you could see which one it was and nothing more.

You can now change it. Each connected account carries a control to make it the default, on both the connections overview and the per-service page. It asks you to confirm first, because the default decides the identity every unqualified call acts as, writes included, and a silent switch is not something you should discover from a file appearing in the wrong Drive.

When the current default cannot serve anything and another account can, we say so. One line names the account that would work, with the switch beside it. It never suggests moving away from a default that grants at least one service, since that may be exactly what you intended.

Two smaller honesty fixes came with it. A permission refusal now links straight to the service page that offers the fix, instead of a page that redirects somewhere else. And an account connected before we started recording permissions now says its grant predates recording, rather than claiming every service was granted.

connectionsmulti-accountoauthbugfix

Reconnecting with the right permissions now works

A bug worth describing plainly, because if you hit it, nothing you did would have fixed it.

When you connect more than one Google account, one of them is the default: the account a tool call runs as when you do not name one. The first account you ever connected became the default and nothing ever moved it. So if that first account granted no services, every call failed, and connecting a second account with full permissions did not help. The calls still ran as the first one.

The failure was silent and permanent, and it was worse than it sounds: our own error message told you to grant the missing permissions, and doing exactly that changed nothing.

Fixed. A default account whose grant covers no services now yields to one that works. A default that grants at least one service is never touched, because that may be a deliberate choice on a multi-account setup, and moving it would be its own bug.

Scope errors also now name the account that can serve the call, so a refusal points somewhere instead of repeating itself.

connectionsoauthscopes

See what each connected account actually granted

Connecting a Google account and granting it access to your Google services are two different things, and until now the product treated them as one. If you clicked through the consent screen without ticking the service boxes, we said "Connected" and then failed every call.

Two changes, and the second is the one that matters.

The connections page now shows what each account actually granted, service by service, rather than a single Connected badge. A connection is shown as complete, partial, or identity-only, so "Connected" now means connected.

A failed call tells you which service was missing. Instead of a bare 403, a tool that needs a scope you did not grant now says so by name, for example Gmail not granted, and the connection state above tells you where to fix it.

If you connected an account weeks ago and have been wondering why a tool kept refusing, open your connections page. The answer is probably on it now.

site

FAQ page

Frequently asked questions now have a home: the FAQ. It covers getting set up (including why connecting an account comes before copying your config), what the gateway can and can't do, safety topics like the approval gate on writes and Google's verification of the app, and account basics. Answers link into the docs and blog wherever there's a longer story.

jiraatlassian-mcp

jira_search returns full issue rows

jira_search results now carry the fields a search is usually after: each row includes the issue key, summary, status, priority, and assignee, plus a browsable /browse/ URL that opens the issue in Jira. Enough to report results, and link them, straight from the search response.

gmailgws-mcp

Gmail labels

Your assistant can now manage Gmail labels, not just search by them. gmail_create_label creates a label (nest with / in the name, like Alerts/Invoices) and returns it, including its ID. gmail_list_labels lists every label on the account, system and user-created alike, with ID, name, and type; those IDs are what gmail_label_message, gmail_update_label, and gmail_delete_label take. gmail_update_label renames a label or changes its visibility, by ID rather than name, and a rename keeps the label on messages that already carry it. gmail_delete_label deletes a label by ID and removes it from every message carrying it; the messages themselves are untouched, and system labels (INBOX, UNREAD, SENT) cannot be deleted.

The workhorse is gmail_label_message: add or remove labels on one message or several in a single call. Since Gmail treats inbox state as labels, this is also how you archive (remove INBOX) and mark read (remove UNREAD).

sheetsgws-mcp

Sheets values starting with = are stored as text

When your assistant writes to a spreadsheet, a value beginning with = is now stored as text, never run as a formula. A cell written as =SUM(A1:A10) holds that string; nothing evaluates. Other values keep Sheets' usual interpretation, so a numeric-looking string like 007 is still stored as the number 7.

If you want a live formula in a cell, enter it in the Sheets editor, where it evaluates as always.

sheetsgws-mcp

Sheets tab lifecycle

Tabs are now first-class. sheets_add_tab adds a tab to an existing spreadsheet, with a title and an optional header row. sheets_rename_tab renames a tab by its current title and leaves the data untouched; saved ranges that name the old title stop resolving. sheets_clear clears the values in a range while the tab and its formatting stay; pass a bare tab name to clear the whole tab. sheets_delete_tab deletes a tab and every row in it, and cannot be undone through the API, so when "empty, but keep the tab" is what you mean, sheets_clear is the non-destructive alternative.

siteskills

Skills page

There's a new Skills page. Each entry is a working agent skill you can copy into Claude: triage your inbox, see the week ahead across every calendar, keep a knowledge base in a Google Sheet, turn a retro page into Jira tickets. They're the actual files we run, not descriptions of what's possible. Paste one into Claude, connect your account, and it runs against your own data through the gateway. Browse the full grid, or start from a persona that matches how you work.

calendargws-mcp

Compact calendar_list_events

calendar_list_events now returns a compact view by default: per event you get the id, title, times, location, a plain-text description (HTML stripped and truncated, tune with max_description_chars), the organizer, an attendee count plus your own response status, and the video join link (Meet, or Zoom and friends from conference data). Meetings with 10 or fewer people keep their full roster, so a 1:1 still tells you who it's with; bigger meetings collapse to the count. Recurring events are flagged and attachments come through. What's gone from the default payload: large-meeting rosters, reminders, and raw conference metadata.

Why: on a busy calendar, a 7-day pull was returning ~55 KB for 25 events (mostly HTML meeting boilerplate and 100+ attendee objects) and overflowing the tool response limit before your assistant saw a single event. The compact view is roughly 85% smaller on the same calendar.

Need the raw Calendar API payload? Pass full: true. calendar_get_event also converts descriptions to plain text now, with the same full opt-out for the original HTML.

dashboardgateway

Playground runs and a rebuilt setup wizard

The dashboard now has a Playground. Example prompts don't just sit there anymore: hit Run and a Claude-powered agent executes them against your connected Google Workspace and Atlassian accounts, choosing tools and streaming results live. It's a demo surface for trying the connectors before you wire up your own client, with about 20 free runs per account. Every response gets a thumbs up/down so you can tell us what worked and what didn't.

The client setup wizard is rebuilt too, with step-by-step flows for Claude web, Claude Desktop, Claude Code, Cursor, and ChatGPT. A live status check flips to green when your first tool call comes through, so you know the connection works before you close the tab.

gmailgws-mcp

Batch mark-read

gmail_mark_read now accepts message_ids (up to 1,000) to change labels on a whole batch in one call, using the Gmail batchModify API. The single-message path is unchanged. Small quality-of-life fix in the same release: gws_run documentation now spells out dotted API resource paths (users.drafts, not drafts).

gmailgws-mcp

Leaner Gmail reads and search results

gmail_read gains a text_only mode: flattened from/to/cc/subject/date headers, the decoded plain-text body, and attachment metadata instead of the raw MIME payload. On a typical marketing email that is about 2% of the full response size. max_body_chars truncates long bodies with a marker. Default behavior is unchanged.

gmail_search and gmail_list results are now flattened to {id, threadId, from, to, subject, date, snippet, labelIds}. This also fixes a bug where search and list results were missing From/Subject headers entirely.

gmailgws-mcp

Gmail drafts: full lifecycle

Two new tools, gmail_send_draft and gmail_delete_draft, complete the draft lifecycle. Together with gmail_create_draft and gmail_update_draft (shipped earlier), your assistant can now create a draft, revise it across turns, and send or discard it when you give the word. The human-in-the-loop flow this enables: AI writes, you review in Gmail, AI sends.

gmailgws-mcpbreaking

Removed: gmail_triage

The experimental gmail_triage tool is removed. Its job is better done by composing gmail_search, gmail_read (now with text_only), and gmail_mark_read directly.

gmailgws-mcp

gmail_mark_read

New gmail_mark_read tool for label modification: mark messages read, and add or remove labels in the same call. Marking read always removes the UNREAD label regardless of other labels you add.

docsgws-mcp

docs_get returns inline image metadata

docs_get responses now include metadata for inline images embedded in a document, so assistants can see where images sit in the content flow instead of silently skipping them.