Announcing CIMD support for MCP Client registration
Learn more

Do You Actually Need Google Docs MCP, or Will the API Do?

Nishant Choudhary
Tech Evangelist

TL;DR

  • The official Google Docs MCP server exposes exactly two tools, read_doc and update_doc, mapping to documents.get and documents.batchUpdate. documents.create is not exposed, and neither is document search.
  • update_doc accepts a raw documents.batchUpdate payload. Raw write capability is close to the REST API's, but your model has to author the full request JSON, including zero-based UTF-16 index arithmetic, with no per-operation schema and no way to allow inserts while denying deletes.
  • The Docs MCP server ships under the Google Workspace Developer Preview Program. The program terms state that preview features may not be included in public applications before general availability, and that you may not grant access to end users outside your domain or company. For a multi-tenant B2B agent, that is a licensing gate, not a capability gap.
  • Anything a document agent needs beyond read and update, listing documents, searching, exporting to PDF, duplicating, commenting, lives in the Drive API or in a second MCP server. A working Docs agent on the official MCP path is a multi-server orchestration problem on day one.
  • Both paths hand you one Google credential per user and neither manages the vault, the refresh, or the revocation. Scalekit's Google Docs connector covers 47 tools spanning both the Docs and Drive surfaces, resolves the per-user token server-side, and can serve the same tools over a virtual MCP server, so the MCP versus API choice stops determining your auth architecture.

Your agent needs to read and write Google Docs. Google now ships two paths: a remote MCP server at docsmcp.googleapis.com and the Docs REST API that has existed for years. They are not two views of the same surface. They differ on what your agent can call, what your model has to know, what credentials you have to hold, and, most consequentially in 2026, whether you are allowed to ship the result to a customer. Here is the decision framework.

What Google Docs MCP and the Google Docs API Actually Are

Both paths terminate at the same three REST methods. The difference is what sits in front of them, and how much of the Google Workspace surface each one can reach.

The Google Docs MCP Server

Google runs a remote Model Context Protocol (MCP) server for Docs at https://docsmcp.googleapis.com/mcp/v1, transport HTTP, auth OAuth 2.0. It is not a community fork; it is a first-party Google service, and it ships as part of the Google Workspace Developer Preview Program.

Standing it up means enabling two services in a Google Cloud project, docs.googleapis.com and docsmcp.googleapis.com, then creating your own OAuth 2.0 web application client. The documented setup registers a redirect URI per MCP host: one value for Antigravity, another for Claude. Four scopes are documented: documents, documents.readonly, drive.file, and drive.readonly.

Tool calls inherit the authorizing user's Google permissions. Google's documentation flags indirect prompt injection as a first-class risk here and points to Model Armor or an equivalent screening layer.

Official documentation: Configure the Docs MCP server.

The Google Docs REST API

The Docs API v1 has a small method surface. The documents resource exposes three methods: documents.create, documents.get, and documents.batchUpdate. Judging this API by method count badly understates it.

The real surface lives inside batchUpdate. Its Request union accepts more than 45 distinct request types, from insertText and updateTextStyle through insertTable, createHeader, pinTableHeaderRows, addDocumentTab, and insertRichLink. Each call takes an array of these, applied atomically.

Auth is standard Google OAuth 2.0 with the same Docs scopes. Nothing about the API is LLM-shaped: schema handling, pagination on the Drive side, index bookkeeping, error mapping, and retry behaviour are entirely yours.

Official documentation: Google Docs API reference.

Where Google Drive Enters the Picture

This is the part that surprises teams. The Docs API cannot list or search documents; Drive owns file discovery, export, duplication, permissions, and the GA comments resource.

On the official MCP path that means a second server. Each Workspace product has its own dedicated MCP server, so a Docs agent that needs to find a document before editing it also needs drivemcp.googleapis.com, with its own service enablement, its own scopes, and its own entry in your client config.

Comparing Them Where It Matters for Agents

The interesting comparison here is not a feature checklist. Because update_doc forwards a whole batchUpdate payload, the two paths are far closer on raw capability than they look, and far further apart on everything that determines production behaviour.

What Your Agent Can Actually Do

The table below covers the operations that show up in real document agents. The third column is included because most teams reading this are choosing between three options, not two.

Capability
Google Docs MCP server
Google Docs API (direct)
Scalekit Google Docs connector
Read document structure and text
Yes, read_doc
Yes, documents.get
Yes, googledocs_read_document
Create a new document
No
Yes, documents.create
Yes, googledocs_create_document
Insert or replace text
Yes, via update_doc
Yes, via batchUpdate
Yes, discrete tools
Apply text and paragraph styles
Yes, via update_doc
Yes, via batchUpdate
Yes, discrete tools
Tables, headers, footers, footnotes
Yes, via update_doc
Yes, via batchUpdate
Yes, discrete tools
List or search documents
No
No, Drive API owns this
Yes, googledocs_list_documents
Create or reply to comments
Developer Preview requests
Developer Preview in Docs API
Yes, via the Drive API
Accept or reject suggestions
Developer Preview requests
Developer Preview
Yes, googledocs_accept_suggestion
Export to PDF, DOCX, or HTML
No
No, Drive API owns this
Yes, googledocs_export_document
Duplicate a document
No
No, Drive API owns this
Yes, googledocs_copy_document
Restrict the agent to a tool subset
No documented mechanism
Not applicable
Yes, via a scoped tool filter
Ship to users outside your org today
No, preview terms
Yes
Yes

Two caveats on that table. Comment and suggestion request types inside batchUpdate carry Developer Preview badges in Google's own Requests reference, so anything depending on them inherits preview status. Scalekit's comment tools sidestep this by routing through the Drive API comments resource instead.

Where the Gap Actually Bites

Missing documents.create sounds minor until you trace a normal workflow. "Draft a project brief from this meeting transcript" starts with creating a document, and the Docs MCP server cannot do it. You either pre-create the file elsewhere or bring in the Drive MCP server's create_file.

Discovery is the same story, one level worse. "Update the Q3 pricing doc" requires resolving a title to a document ID, which is a Drive search. The Docs MCP server holds Drive scopes but exposes no Drive tools.

So the minimum viable official-MCP document agent is two servers, two enablement steps, and a coordination layer you write. That is the operational cost the two-tool surface hides.

Why a Thin Tool Surface Cuts Both Ways

Give Google's design its due. Two tools means almost no tool-schema overhead in the context window, and no schema drift when Google ships new batchUpdate request types. A 40-tool connector, by Scalekit's own estimate in its virtual MCP documentation, can consume roughly 8,000 tokens before the agent does any work.

The cost lands somewhere less visible. Because update_doc takes a raw batchUpdate request, the model has to know that schema itself, either from pretraining or from prompt tokens you spend on it.

That schema is unforgiving. Location.index is a zero-based offset in UTF-16 code units. Inserting a table puts the table start index at the requested index plus one. Creating paragraph bullets strips leading tabs and can shift surrounding indices. Invalid deleteContentRange boundaries return HTTP 400.

The Blast Radius of a Single Write Tool

There is a security consequence to collapsing 45-plus operations into one tool. The documented update_doc surface offers no per-request-type restriction.

An agent authorized to call update_doc can send any request in the union. There is no documented way to permit insertText while denying deleteContentRange. Least privilege, at the level a security reviewer will ask about, is not expressible on this path. This parallels the broader challenge of access control for multi-tenant AI agents, where per-operation scope enforcement is foundational.

The Auth Path Each One Puts You On

The MCP server uses OAuth 2.0 with an OAuth client you create and own. The documented setup is interactive: an Authenticate button in Antigravity, or a custom connector in Claude, which also requires a Claude Enterprise, Pro, Max, or Team plan. A human completes consent, and in the Antigravity flow pastes an authorization code back.

The REST API accepts the same user-delegated OAuth, and the Workspace platform supports service accounts with domain-wide delegation for org-level automation. Worth knowing: the Developer Preview Program cannot register service accounts, so that pattern and the preview MCP server do not combine.

Neither path escapes Google's consent screen review. Most agent connectors need an External audience, and until Google verifies your app, users see an unverified-app screen. An org-managed OAuth client does not bypass this.

The Preview Term That Decides This for B2B

This is the fact that reorders the whole comparison, and most write-ups on Google Workspace MCP skip it.

The Developer Preview Program terms state that program features may not appear in public applications before the general availability announcement, and that members may not grant end users outside their own domain or company access to applications built on pre-GA APIs. The FAQ answers it directly, and the answer is no. Pre-GA APIs ship as-is, and features typically sit in preview for three to six months.

Internal tooling for your own Workspace domain is workable. A B2B product where your customers' employees connect their own Google accounts is not, regardless of how good the tools are.

What You Own in Production

On the MCP path Google runs the server, the tool schemas, and the transport. You still own the OAuth client and its verification status, per-user token storage and refresh, revocation handling, tenant isolation, and the second server needed for Drive operations.

On the REST path you own all of that plus the request construction, index arithmetic, pagination across Docs and Drive, error mapping, and retries. In exchange you get a stable versioned contract and the full method surface, including documents.create.

Governance on both paths runs through the same objects: the Google Cloud project, the OAuth client and its scope grants, and Workspace admin allowlisting of that client. Connection troubleshooting goes through OAuth log events in the Workspace security investigation tool.

Quotas Are Metered the Same Way

Do not assume MCP buys you a separate lane. Google's Docs API usage limits page documents the same read and write request model for both surfaces: 3,000 reads and 600 writes per minute per project, and 300 reads and 60 writes per minute per user per project. Each read_doc costs one read request; each update_doc costs one write request.

That 60 writes per minute per user is the number to design against. An agent issuing one write per paragraph hits the ceiling at 60 paragraphs a minute; the same edits batched into a single update_doc call cost one write request. Google also notes that standard use is free today, with charges for exceeding quota planned later in 2026.

When to Use the Google Docs MCP Server

  • You are building an internal document agent for users inside your own Workspace domain, where preview terms are satisfied and Workspace admins control the OAuth client directly.
  • Your agent is a human-in-the-loop assistant inside an MCP host such as Antigravity or Claude, where interactive OAuth consent is natural and a person reviews edits.
  • Read-and-annotate workloads dominate: summarizing a brief, checking a runbook, appending a section to a document whose ID the user already supplied.
  • You want to keep tool-schema tokens near zero and are comfortable spending prompt budget on batchUpdate semantics instead.

When to Use the Google Docs API

  • You are shipping to customers outside your own organization, where the Developer Preview terms rule the MCP path out entirely.
  • Your agent creates documents, which documents.create supports and the MCP server does not expose.
  • The workflow spans Docs and Drive: find the document, edit it, export it to PDF, drop the copy in a folder, notify a reviewer.
  • The agent runs unattended on a schedule, where you need deterministic request construction, explicit retry behaviour, and a versioned contract you control.
  • You need least privilege expressed per operation, because a security review will ask whether the agent can delete document content.

Recommended reading: Access control for multi-tenant AI agents, since most Docs agents end up spanning both surfaces.

The Credential Problem That Exists on Both Paths

Whichever path you pick, Google hands you one OAuth credential per authorizing user and stops there. There is no vault, no rotation logic, no revocation flow, and no tenant boundary in the box.

One Google Credential Per User, Times N

In a multi-tenant B2B document agent, which is the default rather than the exception, every user connects their own Google account. Forty users across eight customer organizations is 40 tokens to encrypt at rest, refresh proactively, isolate per tenant, and revoke on offboarding.

The token type differs between paths. The infrastructure required does not. Understanding secure token management for AI agents at scale is the prerequisite for any production credential strategy here.

Revocation Arrives as a 401, Not a Webhook

Google does not notify your application when a user disconnects it from their account settings. You discover it on the next tool call, as an authorization failure with no useful context, which the agent may well interpret as an empty result rather than a failure.

This is the failure mode worth designing for explicitly: a document agent that silently returns nothing looks identical to a document agent that correctly found nothing.

Scope Changes Are a One-Shot Decision

A token issued against a scope set does not silently widen. Deciding six months in that your agent also needs drive.file means a fresh consent from every already-connected user, coordinated across every tenant.

Get the scope decision right before your first customer connects, or budget for a re-authorization campaign.

How Scalekit Handles Google Docs for Both Paths

Scalekit's approach is to make the transport decision reversible. One connector, one credential model, one audit surface, consumable either as native framework tools or as an MCP endpoint.

One Connector Spanning Docs and Drive

The Scalekit Google Docs connector is OAuth 2.0 and ships 47 tools. Critically, it does not stop at the Docs API boundary.

Comment operations route through the Drive API comments resource. googledocs_export_document uses the Drive export endpoint. googledocs_copy_document and googledocs_list_documents use Drive as well. The agent sees one coherent document toolset instead of two servers stitched together.

Discrete tools also restore what update_doc collapses. googledocs_insert_text, googledocs_apply_text_style, and googledocs_delete_content_range are separate, individually grantable operations rather than one write endpoint that can do anything.

Set Up the Connection

Register your Google OAuth credentials once per environment in the Scalekit dashboard under AgentKit, Connections. Enable the Google Docs API in your Google Cloud project and add the Scalekit redirect URI to your OAuth client, following the connector setup steps.

pip install scalekit-sdk-python langchain-openai # .env SCALEKIT_ENVIRONMENT_URL=<your-environment-url> SCALEKIT_CLIENT_ID=<your-client-id> SCALEKIT_CLIENT_SECRET=<your-client-secret>

The connection_name you pass in code must match the connection name configured in the dashboard exactly. This is the single most common integration error on a first run.

Authorize a User and Execute a Tool

Before an agent can act, the user needs an active connected account. Check for one, and if it is missing or inactive, send them through authorization.

import os from scalekit.client import ScalekitClient from dotenv import load_dotenv load_dotenv() scalekit_client = ScalekitClient( env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"), client_id=os.getenv("SCALEKIT_CLIENT_ID"), client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), ) actions = scalekit_client.actions CONNECTION_NAME = "googledocs" # must match the dashboard connection name IDENTIFIER = "user_123" # your app's stable id for this user account = actions.get_or_create_connected_account( connection_name=CONNECTION_NAME, identifier=IDENTIFIER, ) if account.connected_account.status != "ACTIVE": link = actions.get_authorization_link( connection_name=CONNECTION_NAME, identifier=IDENTIFIER, ) print("Authorize Google Docs:", link.link) input("Press Enter after authorizing...") result = actions.execute_tool( tool_name="googledocs_list_documents", tool_input={"query": "mimeType = 'application/vnd.google-apps.document' and trashed = false", "page_size": 25}, connection_name=CONNECTION_NAME, identifier=IDENTIFIER, ) print(result)

For production authorization handling, including redirect and verification, see Authorize a user.

Retrieve the Tools This User Is Authorized to Call

This is the step that separates a per-user agent from a shared-credential one. list_scoped_tools does not return a flat catalogue of everything the connector supports; it returns the tools the current user's connected account is authorized to call, filtered to the subset you allow.

from scalekit.v1.tools.tools_pb2 import ScopedToolFilter response = scalekit_client.tools.list_scoped_tools( IDENTIFIER, filter=ScopedToolFilter( connection_names=["googledocs"], tool_names=[ "googledocs_list_documents", "googledocs_read_document", "googledocs_create_document", "googledocs_insert_text", "googledocs_apply_text_style", ], ), page_size=100, ) for scoped in response.tools: print(scoped.tool.definition.name)

Note what is absent from that filter: googledocs_delete_content_range. A drafting agent has no business deleting content ranges, and here that is a configuration decision rather than a prompt instruction. This is the per-operation least privilege the update_doc surface cannot express.

Run the Agent Loop in LangChain

Scalekit's LangChain adapter returns native StructuredTool objects, so there is no schema reshaping between the connector and the framework. For a broader look at how LangChain tool calling works and where it stops, the pattern here maps cleanly onto that architecture.

from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage, ToolMessage tools = actions.langchain.get_tools( identifier=IDENTIFIER, connection_names=["googledocs"], page_size=100, ) tool_map = {t.name: t for t in tools} llm = ChatOpenAI(model="gpt-4o").bind_tools(tools) messages = [HumanMessage( "Find the Q3 pricing proposal in my Docs, then append a 'Renewal terms' " "heading with a one-paragraph summary of the discount schedule." )] while True: response = llm.invoke(messages) messages.append(response) if not response.tool_calls: print(response.content) break for tc in response.tool_calls: result = tool_map[tc["name"]].invoke(tc["args"]) messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"]))

Every call in that loop resolves the authorizing user's Google credential server-side. The token never enters the model's context, and the write lands as that user in the document's revision history.

When You Need an Endpoint the Connector Does Not Cover

The Docs API moves quickly, and preview request types land before connector tools do. The proxy keeps you unblocked without abandoning the credential model.

result = actions.request( connection_name="googledocs", identifier=IDENTIFIER, path="/v1/documents/<DOCUMENT_ID>", method="GET", ) print(result)

The same per-user token resolution and logging apply. See proxying API calls for the full pattern.

Serving the Same Tools Over MCP With a Virtual MCP Server

If you want MCP as your transport, you do not have to accept Google's tool boundaries to get it. A virtual MCP server is a scoped endpoint that declares which connections and which tools an agent can see, with per-user credentials resolved behind it.

Why Not Just Point Your Agent at Google's Server

Three reasons, in descending order of how hard they are to work around.

Preview terms block customer-facing deployment outright. The Docs-only boundary forces a second MCP server for search and export, each with its own config and consent. And a single update_doc tool cannot be narrowed to the operations your agent should actually perform.

A virtual MCP server addresses all three at once. Docs tools and Drive tools land on one endpoint, the tool list is whatever subset you declare, and the underlying connectors are generally available rather than pre-GA.

Define the Server Once Per Agent Role

Create it once per agent role, not once per user. The response includes a static mcp_server_url you reuse across every user and session.

import os from scalekit import ScalekitClient from scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping scalekit_client = ScalekitClient( env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"], client_id=os.environ["SCALEKIT_CLIENT_ID"], client_secret=os.environ["SCALEKIT_CLIENT_SECRET"], ) vmcp_response = scalekit_client.actions.mcp.create_config( name="document-drafting-agent", connection_tool_mappings=[ McpConfigConnectionToolMapping( connection_name="googledocs", tools=[ "googledocs_list_documents", "googledocs_read_document", "googledocs_create_document", "googledocs_insert_text", "googledocs_apply_text_style", "googledocs_export_document", ], ), McpConfigConnectionToolMapping( connection_name="googledrive", tools=["gdrive_files_list", "gdrive_file_get"], ), ], ) config_id = vmcp_response.config.id mcp_server_url = vmcp_response.config.mcp_server_url

Confirm the exact Google Drive tool names against the Google Drive connector reference before you ship; connector tool lists are the authoritative source.

Mint a Per-User Session Token Before Each Run

OAuth credentials expire and get revoked between runs, so verify the connections are still active, then mint a short-lived token bound to that specific user. Getting token refresh right for AI agents is critical here — a stale token at run time means a silent failure.

from datetime import timedelta accounts_response = scalekit_client.actions.mcp.list_mcp_connected_accounts( config_id=config_id, identifier="user_123", include_auth_link=True, ) for account in accounts_response.connected_accounts: if account.connected_account_status != "ACTIVE": print(f"{account.connection_name} needs auth: {account.authentication_link}") token_response = scalekit_client.actions.mcp.create_session_token( mcp_config_id=config_id, identifier="user_123", expiry=timedelta(minutes=30), ) token = token_response.token

Never reuse a token across runs, and set the expiry longer than the expected run duration. The setup and lifecycle details are in set up and connect a virtual MCP server.

Consume It From a Mastra Agent in TypeScript

Mastra has native MCP support, so it discovers the tool list and Zod-compatible schemas from the endpoint without any manual conversion. Generate the per-user URL on your backend and hand it to the agent for that request only.

npm install @mastra/core @mastra/mcp @ai-sdk/openai
import { Agent } from '@mastra/core/agent'; import { MCPClient } from '@mastra/mcp'; import { openai } from '@ai-sdk/openai'; // Resolved on your backend for the authenticated user, never a shared constant const mcpUrl = await getMcpUrlForUser(currentUserId); const mcp = new MCPClient({ servers: { scalekit: { url: new URL(mcpUrl) }, }, }); const tools = await mcp.getTools(); const agent = new Agent({ name: 'document_agent', instructions: 'You draft and revise Google Docs on behalf of the current user. ' + 'Always read a document before editing it.', model: openai('gpt-4o'), tools, }); const result = await agent.generate( 'Create a doc titled "Acme renewal brief" and add an executive summary section.', ); console.log(result.text); await mcp.disconnect();

One MCP URL per user, resolved server-side per request. A process-wide URL is safe only in a single-user demo; sharing it runs every request as that one user. The full pattern is in the Mastra example.

Observability: Auth Logs for Downstream Tool Calls

Capability comparisons rarely mention this, and it is usually the first thing an enterprise security reviewer asks about. When your agent edited a customer's document, who authorized it, which tool ran, and what came back?

What Attribution Looks Like Per Call

Because Scalekit resolves the credential at call time rather than handing your runtime a token, every downstream tool call has an identity attached to it. Logs carry the authorizing user, the agent, the tool, the scope, and the response, exportable to your SIEM with failures separated by source.

The Node SDK surfaces a per-call handle directly. scalekit.actions.executeTool returns both data and executionId, so you can correlate a Google Docs write with the agent run and the user consent that authorized it.

npm install @scalekit-sdk/node
import { ScalekitClient } from '@scalekit-sdk/node'; const scalekit = new ScalekitClient( process.env.SCALEKIT_ENVIRONMENT_URL!, process.env.SCALEKIT_CLIENT_ID!, process.env.SCALEKIT_CLIENT_SECRET!, ); const response = await scalekit.actions.executeTool({ toolName: 'googledocs_insert_text', toolInput: { document_id: docId, text: '\n\nRenewal terms\n' }, identifier: 'user_123', connector: 'googledocs', }); // Correlate this write with the agent run and the user who authorized it logger.info({ executionId: response.executionId, userId: 'user_123', docId });

Why This Gap Is Structural, Not a Missing Feature

Google's MCP server logs on Google's side: OAuth log events in the Workspace security investigation tool, visible to the Workspace admin. That is the right place for a Workspace admin to look. It is the wrong place for you.

You cannot answer a customer's question about their tenant from another customer's Workspace audit log. Cross-tenant attribution for your own agent has to live in your infrastructure, and it has to exist before the security questionnaire arrives, not after. A proper approach to audit trails for agent auth in B2B SaaS means logging is built into the execution layer, not retrofitted.

Which One to Build Against

For Google Docs specifically, this decision is less balanced than the equivalent one for Slack or Notion, and the deciding factor is not capability.

If your agent serves users inside your own Workspace domain with a human present, the official Docs MCP server is a reasonable start, provided you accept a second server for Drive operations and one unrestricted write tool. If it serves customers outside your organization, creates documents, or runs unattended, build against the Docs and Drive APIs directly. Preview terms make that a compliance decision, not a preference.

Either way, the per-user credential problem is identical, and it does not get smaller as you add Google Sheets, Gmail, and Slack behind the same agent. Understanding who holds the token across agent tool-calling patterns is the layer worth making infrastructure.

Building a Google Docs Agent?

Browse the connector references and starter agents:

Building on Google Docs and want to compare notes on scopes, consent screen verification, or virtual MCP design? Join the Scalekit Slack community, or talk to an engineer if you need an answer today.

No items found.
Agent
Auth Quickstart
On this page
Share this article
Agent
Auth Quickstart

Acquire enterprise customers with
zero upfront cost.

Every feature unlocked. No hidden fees.