Skip to main content
Glama
README.md
# google-sheets-mcp

A local [MCP](https://modelcontextprotocol.io) server that lets an AI assistant read and write your Google Sheets, with per-user OAuth and no cloud hosting.

## Why

Most ways of giving an AI assistant access to a spreadsheet are either read-only or route your data through a third-party automation platform. Neither is good enough for a working document you update every day.

This server runs on your own machine and talks directly to the Google Sheets API as you. It exposes targeted operations rather than a raw API surface, so updating a status field changes that one cell instead of rewriting a block of rows — which matters when the spreadsheet is a register you rely on rather than a scratch file.

## Features

Twelve tools, split between reading and writing. Write tools can be disabled entirely with `READ_ONLY=true`.

**Read**

| Tool | Description |
| --- | --- |
| `list_tabs` | Lists a spreadsheet's tabs with dimensions and sheet IDs |
| `read_range` | Reads a range in A1 notation, or a whole tab |
| `find_rows` | Returns only rows matching column filters or free text, with their absolute row numbers |

**Write**

| Tool | Description |
| --- | --- |
| `update_cell` | Sets a single cell; cannot affect neighbouring cells |
| `update_row` | Updates only the named columns of an existing row, found by number or by a matching value |
| `write_range` | Overwrites a range with a value grid |
| `append_rows` | Appends rows after the last occupied row |
| `clear_range` | Clears values while preserving formatting |
| `create_spreadsheet` | Creates a new spreadsheet in your Drive |
| `add_tab` | Adds a tab to an existing spreadsheet |
| `rename_tab` | Renames a tab |
| `delete_tab` | Deletes a tab; requires an explicit `confirm: true` |

Two design decisions worth calling out. `find_rows` returns row numbers that feed straight into `update_row`, so the model can locate a record and change one field without pulling the whole table into context. And `update_row` refuses to act when a match is ambiguous, listing the candidate rows instead of updating several at once.

## Requirements

- Node.js 20 or later
- A Google account and a Google Cloud project
- An MCP-capable client that supports local stdio servers (Claude Desktop, Claude Code, or any other MCP client)

This is a stdio server. It does not listen on a public port and cannot be used by browser-based clients that only reach remote HTTPS endpoints.

## Setup

Starting from a clean machine.

**1. Enable the Google Sheets API**

Go to the [Google Cloud Console](https://console.cloud.google.com), create or select a project, then open **APIs & Services > Library**, search for **Google Sheets API** and click **Enable**.

**2. Configure the OAuth consent screen**

Open **Google Auth Platform > Branding** and fill in the app name, user support email and developer contact email. Under **Audience**, choose **Internal** if your account belongs to a Google Workspace organisation, otherwise **External**.

If you chose External and stay in **Testing** publishing status, add your own address under **Test users** — without it, sign-in fails with `access_denied` — and be aware that Google revokes refresh tokens seven days after consent. Publishing the app to production removes that expiry; the app stays private either way, since reaching the consent screen requires your client ID.

**3. Create OAuth credentials**

Open **Google Auth Platform > Clients > Create client** and choose application type **Desktop app**. Desktop clients accept a loopback redirect on any port, so there is no redirect URI to configure. Copy the client ID and client secret.

**4. Install**

```bash
git clone https://github.com/your-username/google-sheets-mcp.git
cd google-sheets-mcp
npm install
npm run build
```

**5. Configure credentials**

```bash
cp .env.example .env
```

Open `.env` and fill in `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`. On first run, consider also setting `ALLOWED_SPREADSHEET_IDS` to a single test spreadsheet.

**6. Link your Google account**

```bash
npm run login
```

The command prints a URL. Open it, sign in with the account you added as a test user, and grant access. If the app is unverified you will see a "Google hasn't verified this app" screen — click **Advanced**, then continue. Google redirects to a temporary local server, the code is exchanged automatically, and the refresh token is written to `~/.gsheets-mcp/tokens.json` with mode `0600`.

To check how much time is left on the authorization:

```bash
npm run status
```

## Configuration

Add the server to your MCP client's configuration file. For Claude Desktop:

- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
- Linux: `~/.config/Claude/claude_desktop_config.json`

```json
{
  "mcpServers": {
    "gsheets": {
      "command": "node",
      "args": ["/absolute/path/to/google-sheets-mcp/dist/index.js"],
      "env": {
        "GOOGLE_CLIENT_ID": "your-client-id-here.apps.googleusercontent.com",
        "GOOGLE_CLIENT_SECRET": "your-client-secret-here",
        "ALLOWED_SPREADSHEET_IDS": ""
      }
    }
  }
}
```

The path in `args` must be absolute. On Windows, escape backslashes: `"C:\\path\\to\\google-sheets-mcp\\dist\\index.js"`. Credentials can be supplied either here or through `.env` — the `env` block takes precedence.

Restart the client after editing the file.

## Usage

Given a spreadsheet with a `Log` tab whose columns are `Date`, `Task`, `Client`, `Hours`, `Status`:

> Find the rows in Log where Status is "Pending"

```
3 of 214 rows in "Log"

row  Date        Task                  Client    Hours  Status
47   2026-03-02  Draft API spec        Acme      3      Pending
88   2026-03-14  Review contract       Northwind 1.5    Pending
131  2026-04-01  Migrate CI pipeline   Acme      6      Pending
```

> Mark row 47 as Done

```
Row 47 of "Log" updated.
  Status: "Pending" -> "Done"
```

Only column `E` of row 47 is written. Every other cell in that row is left untouched, and the response reports the previous value so the change can be verified without opening the spreadsheet.

## Known limitations

- **stdio only.** No HTTP transport, so browser and mobile MCP clients cannot reach it. Adding one would require hosting and an OAuth authorization-server layer.
- **Values, not formatting.** Cell values and formulas are supported; colours, borders, conditional formatting, charts, filters and data validation are not. Those need `spreadsheets.batchUpdate`, which is deliberately not exposed — a general-purpose passthrough would be hard to reason about and easy to misuse.
- **No server-side search.** The Sheets API has no query endpoint, so `find_rows` reads the tab and filters in process. The saving is in what reaches the model's context, not in API traffic. On very large sheets the read itself is the cost.
- **Single account.** One linked Google identity per installation.
- **Scope granularity.** The `spreadsheets` scope covers every spreadsheet the account can reach; Google offers no per-file scope. `ALLOWED_SPREADSHEET_IDS` is the only real restriction available, and it is enforced by this server rather than by Google.
- **Rate limits.** Google allows 300 read and 300 write requests per minute per project, and 60 per minute per user. Quota is shared across everything using the same Cloud project. Exceeding it returns HTTP 429; the server surfaces it but does not retry or back off.
- **Token expiry in Testing.** With External audience and Testing publishing status, Google revokes refresh tokens after seven days. The server detects `invalid_grant` and returns an actionable message rather than a raw API error.
- **Untrusted cell content.** Anything the model reads from a spreadsheet enters its context as untrusted text. A sheet shared by someone else could contain instructions aimed at the model. Consider `READ_ONLY=true` or an allowlist when working with files you did not create.

## License

MIT — see [LICENSE](LICENSE).

TDQS

A3.8/5.0

Scored across 12 tools

Disambiguation5/5

Each tool targets a distinct operation: tab management, range reading/writing, row searching, and cell/row updates. The descriptions explicitly differentiate overlapping tools like read_range vs find_rows and update_cell vs update_row vs write_range, preventing misselection.

Naming Consistency5/5

All tools follow a consistent snake_case verb_noun pattern (e.g., list_tabs, read_range, update_row, create_spreadsheet). No deviations or mixed conventions.

Tool Count5/5

12 tools is well-scoped for a Google Sheets server, covering read/write, search, and tab lifecycle without excessive bloat.

Completeness4/5

Core CRUD for cells, rows, and tabs is present, but there is no operation to delete rows or insert rows in the middle of a sheet, and no batch update tool. These are minor gaps that agents can work around with clear_range or multiple updates.

Maintenance

ActivityMaintained
ResponsivenessNo issues