Google Sheets Managed MCP Server
Connect agents to Google Sheets so they can read and update spreadsheet rows with each user’s own Google permissions. Every row comes back keyed by its column header, so a data policy can mask, drop, or filter one column without hiding the rest of the sheet.
After reading this page, you will be able to:
-
Enroll a Google Cloud project in the Google Workspace Developer Preview Program and create the OAuth client the server needs
-
Configure an OAuth provider and create the Google Sheets managed MCP server
-
Mask or filter columns with a data policy
|
The Google Sheets managed MCP server is in Preview. If you don’t see Google Sheets in the marketplace picker, contact Redpanda support. This server calls Google’s own Sheets MCP server, and Google offers that server only through the Google Workspace Developer Preview Program. That program is separate from this server’s own Preview status. Your Google Cloud project must be enrolled in it before any tool works. See Join the Google Workspace Developer Preview Program. |
What this MCP server does
The Google Sheets managed type calls Google’s hosted Sheets MCP server and authenticates per user through the token vault in AI Gateway. Every call runs with the calling user’s own Google token, so a user can reach only the spreadsheets they can open in Google Sheets. Per-user OAuth is the only authentication mode this type supports.
Each row comes back as a record whose keys come from the sheet’s column headers. See How rows become records.
The Google Sheets managed type exposes these tools:
| Tool | What it does |
|---|---|
|
Return a spreadsheet’s title, locale, time zone, and its grid sheets (tabs), with each sheet’s title, row count, column count, and frozen rows. The result never includes cell values. |
|
Return the rows below the header row of one sheet, as |
|
Overwrite cells in existing rows. Each record names its row with |
|
Add rows after the last row that holds data. Records are keyed by column key and can’t include |
Keep these behaviors in mind:
-
Blank rows are skipped.
read_rowsskips a row when every cell it returns is empty, including when you passcolumns. Use a record’s_rowto find its row, not its position inrecords. -
Pages can be empty. A page that falls in a run of blank rows can come back empty, so keep paging until
next_start_rowis0, which means the read reached the end of the sheet. -
Write values are typed. Values must be strings, numbers, booleans, or
null. A string is always stored as literal text and never evaluated as a formula, so a value such as=B2can’t copy another cell’s contents into the sheet. -
Some features aren’t available. Cell formatting, charts, formulas, filters, and sheet management aren’t supported.
How rows become records
The server reads the column headers from one row of each sheet, set by Header Row in the server configuration (row 1 by default), and turns each header into a record key:
-
Letters A to Z become lowercase, and digits stay. Every run of other characters, including spaces, punctuation, and letters outside A to Z, becomes an underscore (
_). Leading and trailing underscores are removed. -
A key that starts with a digit gets a leading underscore.
-
A header with no letters A to Z or digits, such as
名前, becomesu_plus the hexadecimal encoding of its lowercased UTF-8 bytes. -
A column whose header is empty becomes
col_plus its column letter.
These headers produce these record keys:
| Header | Record key |
|---|---|
|
|
|
|
|
|
|
|
|
|
(empty, column D) |
|
Each record also carries _row, its 1-based row number in the sheet, and holds every column the read returns. An empty cell reads as "".
The header row defines the table. The server returns and writes only the columns within the header row’s width. It never returns or writes the header row or any row above it. If two headers produce the same key, such as Email and EMAIL, the read fails with an error that names both columns. Rename one of the headers.
Prerequisites
-
A Google Workspace account, which the Google Workspace Developer Preview Program requires. Google Workspace for Education accounts also qualify.
-
An email account that accepts being added to Google Groups. Google adds program members to a Google Group.
-
A Google Cloud project where you can enable APIs and create OAuth clients. The project runs no compute, because Google hosts the Sheets MCP server and AI Gateway calls it over HTTPS. Your Agentic Data Plane environment doesn’t need to run on Google Cloud.
-
The
gcloudCLI, signed in to that project. See Install the gcloud CLI. -
Permission in Agentic Data Plane to create secrets, OAuth providers, and MCP servers.
-
Familiarity with user-delegated OAuth.
Join the Google Workspace Developer Preview Program
Google serves its Sheets MCP server only to Google Cloud projects enrolled in the Google Workspace Developer Preview Program. Enroll the project that owns the OAuth client you create in Create the OAuth client.
| This configuration refers to a third-party product. Specific UI elements may not be up to date. |
-
Read the program terms on the Developer Preview Program page.
-
Submit the application form linked from that page, with your Google Workspace account and the Google Cloud project’s details.
-
Wait for Google to verify your account, add you to a Google Group, and email you that it registered your Google Cloud project. Google says this usually takes a couple of days.
To enroll a different project later, use the request form for adding or removing projects on the same page.
Enrollment applies to the project, so only the person who applies has to join the program. Other users in your Google Workspace domain connect their own Google accounts through the same OAuth client.
The program’s terms limit who can use features in preview. Read them before you give the server to users outside your own Google Workspace domain.
Until the project is enrolled, every tool call fails with this error:
google sheets MCP get_spreadsheet: Google refused the call because the OAuth client's Google Cloud project is not enrolled in the Google Workspace Developer Preview Program (https://developers.google.com/workspace/preview)
Enable the Sheets APIs
Enable both the Google Sheets API and the Google Sheets MCP API on the enrolled project. Replace <project-id> with your project ID:
gcloud services enable sheets.googleapis.com --project=<project-id>
gcloud services enable sheetsmcp.googleapis.com --project=<project-id>
Configure the OAuth consent screen
| This configuration refers to a third-party product. Specific UI elements may not be up to date. |
In the Google Auth Platform for the enrolled project:
-
On the Branding page, give the app a name your users recognize on the consent screen, for example
Redpanda Agentic Data Plane. -
On the Audience page, choose Internal so only users in your Google Workspace organization can connect. If Internal isn’t available, choose External and add each user as a test user.
-
On the Data Access page, add the
https://www.googleapis.com/auth/spreadsheetsscope.
For the full procedure, see Google’s Configure the Google Sheets MCP server guide.
Create the OAuth client
| This configuration refers to a third-party product. Specific UI elements may not be up to date. |
-
In the Google Auth Platform, open Clients and create a client.
-
Set Application type to Web application.
-
Select Use this client for an AI-powered agent. Google rejects requests to its Sheets MCP server from a client without this option. If you already have a client that also serves features other than agents, create a separate one for this server.
-
Under Authorized redirect URIs, add the AI Gateway OAuth callback for your environment:
https://aigw.<cluster-id>.clusters.rdpa.co/oauth/v1/callbackReplace
<cluster-id>with your cluster’s identifier. If you’re not sure of the exact URL, add it later. The OAuth provider you create in Configure the OAuth provider shows it on its detail page. -
Create the client, then copy its Client ID and Client secret.
Store the client secret
Store the client secret in the Agentic Data Plane secret store under an UPPER_SNAKE_CASE ID, for example GOOGLE_SHEETS_CLIENT_SECRET, with the Single line value format. See Create a secret. The OAuth provider references the secret by its ID, so the secret value never appears in the MCP server’s configuration.
Configure the OAuth provider
Register a OAuth provider for Google in Agentic Data Plane. See Configure an OAuth Provider for the full form.
-
Open Integrations setup in the sidebar and click the Outbound providers tab.
-
Click Add provider, then choose the Google preset.
The preset fills in Google’s authorization, token, and revocation endpoints, the
openid,email, andprofilescopes, PKCE, and the Client Secret (POST) authentication method. It also requests offline access, so each user’s connection gets a refresh token and doesn’t expire after an hour. -
Set
Name, for examplegoogle-sheets. You reference the provider by this name when you create the server. -
Enter the
Client IDfrom the Google client, and setClient secret referenceto the secret, for exampleGOOGLE_SHEETS_CLIENT_SECRET. -
Under
Scopes, click Add scope and addhttps://www.googleapis.com/auth/spreadsheets. -
Click Create provider.
If you haven’t added the callback URL to the Google client yet, copy the Authorization callback URL from the provider’s detail page and add it under the client’s Authorized redirect URIs.
Create the server
Before you start, create the OAuth provider. See Configure the OAuth provider.
-
Open MCP servers in the sidebar.
-
Click Add MCP server.
-
Click the Google Sheets card in the marketplace picker.
-
Replace the suggested
Name, and optionally add aDescription. -
Optional: Set
Header Rowto the row that holds the column headers on every sheet, from 1 to 1,000. Leave it empty for row 1. -
Optional: Turn on
Allow Writesto add theupdate_rowsandappend_rowstools. Leave it off for a read-only server. -
Optional: Under
Allowed Spreadsheet Ids, add the IDs of the only spreadsheets this server may open, up to 100. Leave the list empty to allow any spreadsheet each user can open.A spreadsheet’s ID is the part of its URL after
/d/. -
Under
Auth, setProvider Nameto the provider you created, for examplegoogle-sheets. -
Under
Required Scopes, addhttps://www.googleapis.com/auth/spreadsheets. -
Click Create server.
Each user’s connection must hold every required scope. A connection with fewer scopes returns scope_upgrade_required, and the user reconnects to grant the missing one. Every required scope must also be in the OAuth provider’s Scopes.
Configure from the CLI
You need rpk ai 0.2.100 or later to create this server from the CLI. Run rpk ai version to check your version. To update, run rpk ai upgrade.
To create the same server with rpk ai, pass the configuration as JSON in --managed.config:
rpk ai mcp-server create google-sheets \
--enabled \
--description "Google Sheets with per-user OAuth" \
--managed.config '{
"@type": "type.googleapis.com/redpanda.mcps.google_sheets.v1.GoogleSheetsMCPConfig",
"headerRow": 1,
"allowWrites": false,
"allowedSpreadsheetIds": ["1AbCdEfGhIjKlMnOpQrStUvWxYz0123456789_-abcd"],
"userOauth": {
"providerName": "google-sheets",
"requiredScopes": ["https://www.googleapis.com/auth/spreadsheets"]
}
}'
Connect your Google account and test
Each user connects their own Google account before the tools work for them:
-
Open the server in Agentic Data Plane. If a connection banner appears, click Connect, sign in to Google, and approve the consent screen.
If you call a tool before connecting, the call fails with a connection-required result that carries an authorization URL. That URL starts the same Google consent flow.
-
Open the server’s Inspector tab and run
get_spreadsheetwith the ID of a spreadsheet you can open:{"spreadsheet_id": "1AbCdEfGhIjKlMnOpQrStUvWxYz0123456789_-abcd"}The result lists the spreadsheet’s sheets. See Test an MCP Server’s Tools with the Inspector.
-
Run
read_rowswith one of the sheet titles:{"spreadsheet_id": "1AbCdEfGhIjKlMnOpQrStUvWxYz0123456789_-abcd", "sheet": "Customers", "max_rows": 2}
For a sheet whose headers are Customer ID, Annual Income, and Region, the result looks like this:
{
"columns": [
{"key": "customer_id", "header": "Customer ID", "letter": "A"},
{"key": "annual_income", "header": "Annual Income", "letter": "B"},
{"key": "region", "header": "Region", "letter": "C"}
],
"records": [
{"_row": 2, "customer_id": "288", "annual_income": "86764.66", "region": "EMEA"},
{"_row": 3, "customer_id": "301", "annual_income": "41200", "region": "AMER"}
],
"next_start_row": 4
}
Reads return every value as a string, formatted as Google shows it in the sheet, such as "86764.66" or "1/2/2026". Writes accept strings, numbers, booleans, and null.
To run the same call from the CLI, use rpk ai mcp-server tools call. See Test from the CLI.
rpk ai mcp-server tools call google-sheets read_rows \
--args '{"spreadsheet_id":"1AbCdEfGhIjKlMnOpQrStUvWxYz0123456789_-abcd","sheet":"Customers","max_rows":2}'
Govern columns with data policies
Because every column has a predictable key, a data policy on this server can target one column by name. Add policies on the server’s Data Policies tab. See Shape Tool Data with Data Policies.
For example, this policy masks the annual_income key (the Annual Income column) and keeps only the EMEA rows in read_rows results for one user:
rpk ai mcp-server update google-sheets --data-policies '{
"displayName": "emea-rows-masked-income",
"tools": ["read_rows"],
"principals": ["User:analyst@example.com"],
"shaping": {
"response": {
"fieldActions": {
"rules": [
{"selector": {"jsonPath": "$.records.annual_income"}, "mask": {"redact": {}}}
]
},
"rowFilters": [
{"path": "$.records", "expression": "@.region == \"EMEA\""}
]
}
}
}'
The --data-policies flag replaces the server’s whole list of data policies. Pass every policy the server should keep.
|
For analyst@example.com, read_rows then returns only the rows whose region is EMEA, with annual_income as [REDACTED]. Every other user still sees every row.
Keep these points in mind when you write policies for this server:
-
Keep mask and drop rules strict. A strict rule denies the call when a returned record is missing its field, and an absence-safe rule lets the result through instead. A policy follows the header, not the data. If someone renames
Annual Income, its key changes, and a strict rule on$.records.annual_incomedenies the call instead of returning the column under its new key. -
Write exact paths, and create rules in the YAML editor or the CLI. Rules you create in the form are absence-safe by default, so make the rule strict in the YAML editor, or set it with
--data-policiesas in the example. Rules passed that way are strict unless you mark them absence-safe. Use exact paths such as$.records.annual_income, not descendant selectors such as$..annual_income, which can’t be strict. See Select fields. -
Put row filters on
$.records. Compare a record key, such as@.region == "EMEA". -
Use allowlist mode to return only the columns you name. Turn on Drop fields without policy and add Keep rules. A column someone adds to the sheet later stays hidden too. Allowlist mode also drops
_rowand the result’scolumnsandnext_start_rowfields unless you keep them. Keep$.records._rowif agents need row numbers forupdate_rows, and$.next_start_rowif they need to page through the sheet. -
Govern writes with request rules. Add them to
update_rowsandappend_rows. A drop rule on$.records.annual_incomestops agents from writing that column. -
Pin the server to known spreadsheets. List them in
Allowed Spreadsheet Ids. A mask on one header doesn’t cover the same figures under a different header or on another sheet.
When a data policy applies to a caller and a tool, the agent never sees that tool’s own errors. Every tool error reaches the agent as [policy] response withheld: required data-access transforms could not be applied. That includes errors that explain the problem, such as an unknown sheet name or a refused write. To see the full error, call the tool as a user that no policy targets.
Troubleshooting
| Symptom | What to check |
|---|---|
|
The project that owns the OAuth client isn’t enrolled yet. See Join the Google Workspace Developer Preview Program. |
Every call fails, although the project is enrolled and the Sheets APIs are enabled |
Select Use this client for an AI-powered agent on the OAuth client. See Create the OAuth client. |
|
The user hasn’t connected their Google account yet. Have the user open the server in Agentic Data Plane, click Connect, and approve Google’s consent screen. Later calls reuse the stored token. See user-delegated OAuth. |
|
The user’s connection lacks a scope the server requires. If the scope is missing from the OAuth provider’s |
Users have to reconnect about every hour |
The OAuth provider doesn’t request offline access, so connections get no refresh token. Create the provider from the Google preset, which requests it. |
|
Add the ID to |
|
The |
|
|
|
Two headers produce the same record key. Rename one of them. See How rows become records. |
|
|
|
A data policy applies to the caller, so the tool’s error is withheld, or a strict rule matched nothing, for example after a header was renamed. Call the tool as a user that no policy targets to see the error, and check the policy’s selectors against the current headers. |
An error shows no Google error text |
The server doesn’t pass Google’s own error text on. Errors name the tool and, for HTTP failures, the status code. |
|
Your |