google_docs_create returns the whole Docs resource for a document that is, by definition, empty.
packages/plugins/google-docs/tools/docs.ts (createDocument) raw-passes the API response:
const res = await ctx.http("https://docs.googleapis.com/v1/documents", { ... });
return res.json();
For a freshly created doc that payload carries documentStyle, revisionId, suggestionsViewMode, and the complete namedStyles ramp (NORMAL_TEXT through SUBTITLE) — and then repeats body, documentStyle and the whole namedStyles ramp again under tabs[0].documentTab. The only fields a caller can act on are documentId and title; everything else describes default styling the caller did not ask for and cannot use until it edits the doc.
The cost lands in the agent's context window. An agent that creates a doc and then edits it pays for that boilerplate on the create call, and the follow-up google_docs_batch_update needs nothing from it beyond documentId.
Suggested fix
Return a slim row, the way neighbouring tools already do:
const doc = await res.json();
return { documentId: doc.documentId, title: doc.title };
Prior art in the same codebase:
google_docs_get_plaintext (same file) already projects down to { documentId, title, text }.
google_drive_list / google_drive_search return slim { id, name, mimeType, modifiedTime, size? } rows and say so in their descriptions.
google_docs_get should keep returning the full structure — its description advertises that, and callers use it to compute edit indices.
Related
This is the same raw-res.json() passthrough pattern recorded in docs/findings/2026-06-12-builtin-tools-round-review.md; this is one more instance of it rather than a new class of problem.
How it surfaced
Creating a sample doc end-to-end over MCP (google_docs_create -> google_docs_batch_update -> google_docs_get_plaintext). The create step dominated the transcript; the two useful fields were buried in it.
google_docs_createreturns the whole Docs resource for a document that is, by definition, empty.packages/plugins/google-docs/tools/docs.ts(createDocument) raw-passes the API response:For a freshly created doc that payload carries
documentStyle,revisionId,suggestionsViewMode, and the completenamedStylesramp (NORMAL_TEXT through SUBTITLE) — and then repeats body, documentStyle and the whole namedStyles ramp again undertabs[0].documentTab. The only fields a caller can act on aredocumentIdandtitle; everything else describes default styling the caller did not ask for and cannot use until it edits the doc.The cost lands in the agent's context window. An agent that creates a doc and then edits it pays for that boilerplate on the create call, and the follow-up
google_docs_batch_updateneeds nothing from it beyonddocumentId.Suggested fix
Return a slim row, the way neighbouring tools already do:
Prior art in the same codebase:
google_docs_get_plaintext(same file) already projects down to{ documentId, title, text }.google_drive_list/google_drive_searchreturn slim{ id, name, mimeType, modifiedTime, size? }rows and say so in their descriptions.google_docs_getshould keep returning the full structure — its description advertises that, and callers use it to compute edit indices.Related
This is the same raw-
res.json()passthrough pattern recorded indocs/findings/2026-06-12-builtin-tools-round-review.md; this is one more instance of it rather than a new class of problem.How it surfaced
Creating a sample doc end-to-end over MCP (
google_docs_create->google_docs_batch_update->google_docs_get_plaintext). The create step dominated the transcript; the two useful fields were buried in it.