Skip to content

mcp · claude-code · ai-governance · revops

Gmail MCP Search Returns IDs, Not Senders

search_gmail_messages returns message IDs and web links, with no sender, subject or snippet. Four of our agents filed it in seven days. The cost, and the server-side fix.

On this page
  1. What the tool actually returns
  2. Why this breaks inbox triage specifically
  3. The batch fetch does not rescue it
  4. Four filings in seven days
  5. The fix: enrich server-side with the metadata format
  6. The other two Gmail gaps
  7. Outbound mail arrives mangled
  8. The content tools cannot be governed
  9. The scorecard
  10. What to take from this
  11. Related

search_gmail_messages, the search tool in the Google Workspace MCP server, returns message IDs and web links. It does not return the sender, the subject, or a snippet. An agent triaging an inbox therefore has to fetch the full body of every message just to learn who wrote to it — and those bodies overflow the context window or spill to a file the agent cannot read back.

The tool is not broken. It returns correct data. It returns it at the wrong shape, and the difference between those two things is the most underrated failure mode in the MCP layer.

Between 22 and 28 July 2026, four of our own agents filed this same defect independently, seven days apart end to end. That is the story worth telling.

What the tool actually returns

The documented behaviour and the observed behaviour diverge. The tool description promises search results. What arrives is a list of identifiers:

{
  "messages": [
    {"id": "1a2b3c...", "webLink": "https://mail.google.com/..."},
    {"id": "4d5e6f...", "webLink": "https://mail.google.com/..."}
  ]
}

No from. No subject. No snippet. No date.

For a human clicking a web link, this is fine. For an agent, it is close to useless: the entire point of a search result is to let the caller decide which results are worth opening. Stripped of sender and subject, every result is indistinguishable from every other result, so the agent must open all of them.

Why this breaks inbox triage specifically

The canonical agent workflow over email is two-pass. First pass: scan the inbox, classify what matters. Second pass: open only the messages that survived the first pass.

The metadata gap collapses the two passes into one. To do the first pass at all, the agent must perform the second pass on every message. A 40-message inbox becomes 40 full-body fetches, each of which can run to thousands of tokens of quoted threads, signatures and disclaimers.

Three consequences, in order of how much they hurt:

  1. Context exhaustion. The agent burns its window on message bodies before it has decided anything. This is the reflection tax in its purest form: tokens spent before the first task-advancing action.
  2. Cost. Every triage run pays full-body prices for metadata-shaped work.
  3. Failure that looks like success. The agent gets correct results and still fails the task, because it ran out of room to act on them. Nothing in the logs says "error".

The batch fetch does not rescue it

The obvious workaround is get_gmail_messages_content_batch — fetch many messages in one call, cheaper than N individual calls.

It does not help. An issue filed 9 July 2026 and still open: the batch tool spills its output to a file and remains unreadable even at batch size 1. The response the agent receives is a pointer to storage it has no tool to read. Batch size 1 is the diagnostic detail — it means the spill is not a volume problem that a smaller batch would avoid.

So the workaround for the metadata gap is itself gapped, and the agent is left with per-message full fetches as the only path that returns usable data.

Four filings in seven days

We run a fleet of agents against a governed gateway. When an agent hits friction it cannot route around, it files a structured issue against the deployment repository. Those issues are the honest record of where tooling fails, because no human decided they were worth writing.

Four separate agents, on four separate days, filed this same defect:

Date Filed as
22 July 2026 "returns no sender/subject/snippet, forcing full-body fetch of every inbox message"
23 July 2026 "returns only IDs/links, not sender/subject/snippet as documented"
24 July 2026 "returns no sender/subject/snippet, forcing full-body fetch for every message"
28 July 2026 "omits sender/subject/snippet, breaking the documented first-pass triage workflow"

Four independent discoveries of one defect inside a week is a measurement, not an anecdote. It says the gap sits directly on the path of the most common thing an agent does with email, and that no amount of agent cleverness routes around it. Each of those agents burned a session hitting the same wall and wrote the same report.

It is also a decent argument for making agents file issues at all. Without that channel, this would have registered as four separate slow days.

The fix: enrich server-side with the metadata format

The Gmail API has always supported this. users.messages.get accepts a format parameter, and format=metadata returns headers — From, Subject, Datewithout the body. It is cheap, and it is exactly what a search result needs.

The upstream MCP server does not use it. So we wrapped it.

Our gateway registers a native search_gmail tool that calls the upstream search, then enriches every hit server-side using the batch fetch in format="metadata" mode. It never pulls a body. The agent receives what it should have received in the first place:

{
  "messages": [
    {"id": "1a2b3c...", "from": "sarah@example.com",
     "subject": "Phase one review moved", "date": "2026-07-28T09:14:00Z"}
  ]
}

One round trip, no bodies, no file spill, no context burn. The two-pass workflow works again.

The general principle: response shaping belongs in the wrapper, not the prompt. You can tell an agent to be economical with context. You cannot prompt your way out of a tool that only speaks in identifiers.

The other two Gmail gaps

While we were in there, two more.

Outbound mail arrives mangled

An issue filed 2 July 2026, rated P1. send_gmail_message builds a text/plain message by default. The SMTP layer line-folds plain text at roughly 78 characters, so multi-sentence paragraphs arrive hard-wrapped mid-sentence, with blank lines stripped. Email written by an agent looked like email written by a bot — which also hurts deliverability, because that shape correlates with spam placement.

The tool does render clean HTML, but only when called with body_format="html". Pass HTML without that flag and the raw tags arrive as visible text in the recipient's inbox. A separate issue from 27 May 2026 is the same wound a month earlier: the parameter name was undocumented in the description the agent reads.

Our fix is a send_email tool that takes a plain-text draft, converts paragraphs to <p> and single newlines to <br> server-side, HTML-escapes the content so user text cannot inject markup, and always calls the upstream tool with body_format="html". The agent writes natural text and cannot get the formatting wrong, because the formatting decision was removed from the agent.

The content tools cannot be governed

An issue filed 29 July 2026, open, P1. get_gmail_message_content and get_gmail_messages_content_batch accept no task_id parameter.

Our gateway gates tool calls on declared intent: an agent states what it is doing, receives a task ID, and passes it on every call. Calls without a live task ID are blocked. That is how tool use stays attributable to a purpose.

A tool that has nowhere to put the task ID cannot participate. Those two tools can read mail outside any declared task, and no policy at the gateway layer can stop them without blocking the tool outright. This is the Ungated failure mode, and it is the one category that wrappers cannot fully bridge — you can wrap the call, but you cannot retrofit governance onto a signature that has no slot for it.

The scorecard

Gmail surface only, scored 0–4, where 0 means not addressable at that layer and 4 means first-call success from the tool description alone.

Layer Read Write Relationship Workflow Audit / History
UI 4 4 3 3 3
API 4 4 3 3 2
MCP 1 2 2 1 0

UI and API rows are assessed against Google's published product and API surface. The MCP row is scored from production use, before our overrides.

MCP read scores 1 — partial, high friction, agent has to guess — precisely because search returns data that cannot be acted on without a second expensive call. Tool count says otherwise; the tool count is not the coverage.

What to take from this

If you are evaluating an MCP server, count tools last. Ask instead what a tool returns, at what size, and whether the agent can act on it without a follow-up call. A server with thirty well-shaped tools beats a server with three hundred that answer in identifiers.

And if you run agents at any scale, give them a way to file structured friction reports. Ours told us about this defect four times in a week. A human team would have absorbed it as four bad afternoons and never written it down.

Frequently asked questions

Why does search_gmail_messages return only IDs and links?

The Google Workspace MCP server's search tool returns message IDs and Gmail web links without calling the Gmail API's metadata format, so no sender, subject, snippet or date is included. The underlying Gmail API does support this: users.messages.get accepts format=metadata, which returns From, Subject and Date headers without the message body. The MCP server simply does not use it.

How do I get sender and subject from Gmail MCP search results?

Wrap the tool. Call the upstream search, then enrich each hit server-side with a batch fetch in format="metadata" mode, which returns headers without bodies. Return sender, subject and date alongside the ID. This keeps the two-pass triage workflow intact and avoids fetching any message body during the scan pass.

Why does get_gmail_messages_content_batch spill to a file?

The batch content tool writes its output to file storage and returns a pointer rather than inline content. It does this even at batch size 1, so reducing the batch size does not avoid it. Agents typically have no tool to read that storage back, which makes the batch path unusable as a workaround for the search metadata gap.

Why do emails sent through Gmail MCP arrive hard-wrapped?

send_gmail_message builds a text/plain message by default, and the SMTP layer line-folds plain text at roughly 78 characters, breaking paragraphs mid-sentence and stripping blank lines. The tool renders clean HTML only when called with body_format="html". Passing HTML without that flag delivers raw tags as visible text.

What is the Unshaped failure mode in MCP servers?

Unshaped is when a tool works and returns correct data, but at the wrong granularity or size for an agent to act on: oversized JSON, raw API payloads, no summarization, or identifiers instead of content. The symptom is an agent that gets correct results and still fails the task, because it exhausted its context window before it could act. The fix is response shaping in a wrapper.