mcp-gsheets 1.4.3 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +188 -51
  2. package/dist/index.js +158 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -12,68 +12,79 @@
12
12
  ![Node](https://img.shields.io/badge/Node.js-18%2B-339933?logo=node.js&logoColor=white)
13
13
  ![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?logo=prettier&logoColor=white)
14
14
 
15
- A Model Context Protocol (MCP) server for Google Sheets API integration. Enables reading, writing, and managing Google Sheets documents directly from your MCP client (e.g., Claude Desktop).
15
+ A Model Context Protocol (MCP) server for Google Sheets API integration. Enables reading, writing, and managing Google Sheets documents directly from your MCP client (e.g., Claude Code, Claude Desktop, Cursor, etc.).
16
16
 
17
- ## 🚀 Quick Start
17
+ ## Key Features
18
18
 
19
- ### 1. Prerequisites
19
+ - **Complete Google Sheets Integration**: Read, write, and manage spreadsheets
20
+ - **Advanced Operations**: Batch operations, formatting, charts, and conditional formatting
21
+ - **Flexible Authentication**: Support for both file-based and JSON string credentials
22
+ - **Production Ready**: Built with TypeScript, comprehensive error handling, and full test coverage
20
23
 
21
- - Node.js v18 or higher
22
- - Google Cloud Project with Sheets API enabled
24
+ ## Requirements
25
+
26
+ - [Node.js](https://nodejs.org/) v18 or higher
27
+ - [Google Cloud Project](https://console.cloud.google.com) with Sheets API enabled
23
28
  - Service Account with JSON key file
29
+ - [npm](https://www.npmjs.com/)
24
30
 
25
- ### 2. Installation
31
+ ## Getting Started
26
32
 
27
- ```bash
28
- # Clone the repository
29
- git clone https://github.com/freema/mcp-gsheets.git
30
- # Or using SSH
31
- # git clone git@github.com:freema/mcp-gsheets.git
32
- cd mcp-gsheets
33
+ ### Quick Install (Recommended)
33
34
 
34
- # Install dependencies
35
- npm install
35
+ Add the following config to your MCP client:
36
36
 
37
- # Build the project
38
- npm run build
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "mcp-gsheets": {
41
+ "command": "npx",
42
+ "args": ["-y", "mcp-gsheets@latest"],
43
+ "env": {
44
+ "GOOGLE_PROJECT_ID": "your-project-id",
45
+ "GOOGLE_APPLICATION_CREDENTIALS": "/absolute/path/to/service-account-key.json"
46
+ }
47
+ }
48
+ }
49
+ }
39
50
  ```
40
51
 
41
- ### 3. Google Cloud Setup
52
+ > [!NOTE]
53
+ > Using `mcp-gsheets@latest` ensures that your MCP client will always use the latest version of the MCP Google Sheets server.
42
54
 
43
- 1. Go to [Google Cloud Console](https://console.cloud.google.com)
44
- 2. Create a new project or select existing
45
- 3. Enable Google Sheets API:
46
- - Navigate to "APIs & Services" → "Library"
47
- - Search for "Google Sheets API" and click "Enable"
48
- 4. Create Service Account:
49
- - Go to "APIs & Services" → "Credentials"
50
- - Click "Create Credentials" → "Service Account"
51
- - Download the JSON key file
52
- 5. Share your spreadsheets:
53
- - Open your Google Sheet
54
- - Click Share and add the service account email (from JSON file)
55
- - Grant "Editor" permissions
55
+ ### MCP Client Configuration
56
56
 
57
- ### 4. Configure MCP Client
57
+ <details>
58
+ <summary>Claude Code</summary>
59
+ Use the Claude Code CLI to add the MCP Google Sheets server (<a href="https://docs.anthropic.com/en/docs/claude-code/mcp">guide</a>):
58
60
 
59
- #### Easy Setup (Recommended)
61
+ ```bash
62
+ claude mcp add mcp-gsheets npx mcp-gsheets@latest
63
+ ```
60
64
 
61
- Run the interactive setup script:
65
+ After adding, edit your Claude Code config to add the required environment variables:
62
66
 
63
- ```bash
64
- npm run setup
67
+ ```json
68
+ {
69
+ "mcpServers": {
70
+ "mcp-gsheets": {
71
+ "command": "npx",
72
+ "args": ["mcp-gsheets@latest"],
73
+ "env": {
74
+ "GOOGLE_PROJECT_ID": "your-project-id",
75
+ "GOOGLE_APPLICATION_CREDENTIALS": "/absolute/path/to/service-account-key.json"
76
+ }
77
+ }
78
+ }
79
+ }
65
80
  ```
66
81
 
67
- This will:
68
- - Guide you through the configuration
69
- - Automatically detect your Node.js installation (including nvm)
70
- - Find your Claude Desktop config
71
- - Create the proper JSON configuration
72
- - Optionally create a .env file for development
82
+ </details>
73
83
 
74
- #### Manual Setup
84
+ <details>
85
+ <summary>Claude Desktop</summary>
75
86
 
76
- If you prefer manual configuration, add to your Claude Desktop config:
87
+ Add to your Claude Desktop config:
77
88
  - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
78
89
  - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
79
90
  - Linux: `~/.config/claude/claude_desktop_config.json`
@@ -82,8 +93,8 @@ If you prefer manual configuration, add to your Claude Desktop config:
82
93
  {
83
94
  "mcpServers": {
84
95
  "mcp-gsheets": {
85
- "command": "node",
86
- "args": ["/absolute/path/to/mcp-gsheets/dist/index.js"],
96
+ "command": "npx",
97
+ "args": ["-y", "mcp-gsheets@latest"],
87
98
  "env": {
88
99
  "GOOGLE_PROJECT_ID": "your-project-id",
89
100
  "GOOGLE_APPLICATION_CREDENTIALS": "/absolute/path/to/service-account-key.json"
@@ -93,16 +104,55 @@ If you prefer manual configuration, add to your Claude Desktop config:
93
104
  }
94
105
  ```
95
106
 
96
- #### Alternative: JSON String Authentication
107
+ </details>
108
+
109
+ <details>
110
+ <summary>Cursor</summary>
111
+
112
+ Go to `Cursor Settings` → `MCP` → `New MCP Server`. Use the config provided above.
113
+
114
+ </details>
115
+
116
+ <details>
117
+ <summary>Cline</summary>
118
+
119
+ Follow https://docs.cline.bot/mcp/configuring-mcp-servers and use the config provided above.
120
+
121
+ </details>
122
+
123
+ <details>
124
+ <summary>Other MCP Clients</summary>
125
+
126
+ For other MCP clients, use the standard configuration format shown above. Ensure the `command` is set to `npx` and include the environment variables for Google Cloud authentication.
127
+
128
+ </details>
129
+
130
+ ### Google Cloud Setup
131
+
132
+ 1. Go to [Google Cloud Console](https://console.cloud.google.com)
133
+ 2. Create a new project or select existing
134
+ 3. Enable Google Sheets API:
135
+ - Navigate to "APIs & Services" → "Library"
136
+ - Search for "Google Sheets API" and click "Enable"
137
+ 4. Create Service Account:
138
+ - Go to "APIs & Services" → "Credentials"
139
+ - Click "Create Credentials" → "Service Account"
140
+ - Download the JSON key file
141
+ 5. Share your spreadsheets:
142
+ - Open your Google Sheet
143
+ - Click Share and add the service account email (from JSON file)
144
+ - Grant "Editor" permissions
145
+
146
+ ### Alternative: JSON String Authentication
97
147
 
98
- Instead of using a file path, you can provide the service account credentials directly as a JSON string. This is useful for containerized environments, CI/CD pipelines, or when you want to avoid managing credential files.
148
+ Instead of using a file path for credentials, you can provide the service account credentials directly as a JSON string. This is useful for containerized environments, CI/CD pipelines, or when you want to avoid managing credential files.
99
149
 
100
150
  ```json
101
151
  {
102
152
  "mcpServers": {
103
153
  "mcp-gsheets": {
104
- "command": "node",
105
- "args": ["/absolute/path/to/mcp-gsheets/dist/index.js"],
154
+ "command": "npx",
155
+ "args": ["-y", "mcp-gsheets@latest"],
106
156
  "env": {
107
157
  "GOOGLE_PROJECT_ID": "your-project-id",
108
158
  "GOOGLE_SERVICE_ACCOUNT_KEY": "{\"type\":\"service_account\",\"project_id\":\"your-project\",\"private_key_id\":\"...\",\"private_key\":\"-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n\",\"client_email\":\"...@....iam.gserviceaccount.com\",\"client_id\":\"...\",\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\",\"token_uri\":\"https://oauth2.googleapis.com/token\",\"auth_provider_x509_cert_url\":\"https://www.googleapis.com/oauth2/v1/certs\",\"client_x509_cert_url\":\"...\"}"
@@ -118,7 +168,55 @@ Instead of using a file path, you can provide the service account credentials di
118
168
  - Newlines in the private key must be represented as `\\n`
119
169
  - If the JSON includes a `project_id`, you can omit `GOOGLE_PROJECT_ID`
120
170
 
121
- Restart Claude Desktop after adding the configuration.
171
+ ## Local Development Setup
172
+
173
+ If you want to develop or contribute to this project, you can clone and build it locally:
174
+
175
+ ```bash
176
+ # Clone the repository
177
+ git clone https://github.com/freema/mcp-gsheets.git
178
+ cd mcp-gsheets
179
+
180
+ # Install dependencies
181
+ npm install
182
+
183
+ # Build the project
184
+ npm run build
185
+ ```
186
+
187
+ ### Interactive Setup Script
188
+
189
+ Run the interactive setup script to configure your local MCP client:
190
+
191
+ ```bash
192
+ npm run setup
193
+ ```
194
+
195
+ This will:
196
+ - Guide you through the configuration
197
+ - Automatically detect your Node.js installation (including nvm)
198
+ - Find your Claude Desktop config
199
+ - Create the proper JSON configuration
200
+ - Optionally create a .env file for development
201
+
202
+ ### Manual Local Configuration
203
+
204
+ If you prefer manual configuration with a local build, add to your MCP client config:
205
+
206
+ ```json
207
+ {
208
+ "mcpServers": {
209
+ "mcp-gsheets": {
210
+ "command": "node",
211
+ "args": ["/absolute/path/to/mcp-gsheets/dist/index.js"],
212
+ "env": {
213
+ "GOOGLE_PROJECT_ID": "your-project-id",
214
+ "GOOGLE_APPLICATION_CREDENTIALS": "/absolute/path/to/service-account-key.json"
215
+ }
216
+ }
217
+ }
218
+ }
219
+ ```
122
220
 
123
221
  ## 📦 Build & Development
124
222
 
@@ -195,8 +293,9 @@ npm run dev # Watch mode with auto-reload
195
293
  ### Writing Data
196
294
  - `sheets_update_values` - Write to a range
197
295
  - `sheets_batch_update_values` - Write to multiple ranges
198
- - `sheets_append_values` - Append rows to a table
296
+ - `sheets_append_values` - Append rows to a table (**Note:** Default `insertDataOption` is `OVERWRITE`. To insert new rows, set `insertDataOption: 'INSERT_ROWS'`)
199
297
  - `sheets_clear_values` - Clear cell contents
298
+ - `sheets_insert_rows` - Insert new rows at specific position with optional data
200
299
 
201
300
  ### Sheet Management
202
301
  - `sheets_insert_sheet` - Add new sheet
@@ -295,6 +394,44 @@ Use `sheets_get_metadata` to list all sheets with their IDs.
295
394
  4. Check rate limits for large operations
296
395
  5. Use `sheets_check_access` to verify permissions before operations
297
396
 
397
+ ## 📘 Tool Details
398
+
399
+ ### sheets_insert_rows
400
+
401
+ Insert new rows at a specific position in a spreadsheet with optional data.
402
+
403
+ **Parameters:**
404
+ - `spreadsheetId` (required): The ID of the spreadsheet
405
+ - `range` (required): A1 notation anchor point where rows will be inserted (e.g., "Sheet1!A5")
406
+ - `rows` (optional): Number of rows to insert (default: 1)
407
+ - `position` (optional): 'BEFORE' or 'AFTER' the anchor row (default: 'BEFORE')
408
+ - `inheritFromBefore` (optional): Whether to inherit formatting from the row before (default: false)
409
+ - `values` (optional): 2D array of values to fill the newly inserted rows
410
+ - `valueInputOption` (optional): 'RAW' or 'USER_ENTERED' (default: 'USER_ENTERED')
411
+
412
+ **Examples:**
413
+
414
+ ```javascript
415
+ // Insert 1 empty row before row 5
416
+ {
417
+ "spreadsheetId": "your-spreadsheet-id",
418
+ "range": "Sheet1!A5"
419
+ }
420
+
421
+ // Insert 3 rows after row 10 with data
422
+ {
423
+ "spreadsheetId": "your-spreadsheet-id",
424
+ "range": "Sheet1!A10",
425
+ "rows": 3,
426
+ "position": "AFTER",
427
+ "values": [
428
+ ["John", "Doe", "john@example.com"],
429
+ ["Jane", "Smith", "jane@example.com"],
430
+ ["Bob", "Johnson", "bob@example.com"]
431
+ ]
432
+ }
433
+ ```
434
+
298
435
  ## 📋 Changelog
299
436
 
300
437
  See [CHANGELOG.md](CHANGELOG.md) for a list of changes in each version.
package/dist/index.js CHANGED
@@ -12624,6 +12624,14 @@ function validateSpreadsheetIdField(id) {
12624
12624
  throw new Error("Invalid spreadsheet ID format");
12625
12625
  }
12626
12626
  }
12627
+ function validateRangeField(range) {
12628
+ if (!range || typeof range !== "string") {
12629
+ throw new Error(ERROR_MESSAGES.RANGE_REQUIRED);
12630
+ }
12631
+ if (!validateRange(range)) {
12632
+ throw new Error(ERROR_MESSAGES.INVALID_RANGE);
12633
+ }
12634
+ }
12627
12635
  function validateSheetIdField(sheetId) {
12628
12636
  if (sheetId === void 0 || typeof sheetId !== "number") {
12629
12637
  throw new Error(ERROR_MESSAGES.SHEET_ID_REQUIRED);
@@ -12940,6 +12948,34 @@ function validateDeleteChartInput(input) {
12940
12948
  chartId: input.chartId
12941
12949
  };
12942
12950
  }
12951
+ function validateInsertRowsInput(input) {
12952
+ validateSpreadsheetIdField(input.spreadsheetId);
12953
+ validateRangeField(input.range);
12954
+ const rows = input.rows ?? 1;
12955
+ if (typeof rows !== "number" || rows <= 0) {
12956
+ throw new Error("Rows must be a positive number");
12957
+ }
12958
+ const position = input.position ?? "BEFORE";
12959
+ if (!["BEFORE", "AFTER"].includes(position)) {
12960
+ throw new Error("Position must be either BEFORE or AFTER");
12961
+ }
12962
+ const inheritFromBefore = input.inheritFromBefore ?? false;
12963
+ const valueInputOption = input.valueInputOption ?? "USER_ENTERED";
12964
+ if (input.values) {
12965
+ if (!Array.isArray(input.values) || !input.values.every((row) => Array.isArray(row))) {
12966
+ throw new Error("Values must be a 2D array");
12967
+ }
12968
+ }
12969
+ return {
12970
+ spreadsheetId: input.spreadsheetId,
12971
+ range: input.range,
12972
+ rows,
12973
+ position,
12974
+ inheritFromBefore,
12975
+ values: input.values,
12976
+ valueInputOption
12977
+ };
12978
+ }
12943
12979
 
12944
12980
  // src/utils/response-helpers.ts
12945
12981
  function createTextResponse(text) {
@@ -13162,7 +13198,7 @@ To fix this, either:
13162
13198
  // src/tools/append-values.ts
13163
13199
  var appendValuesTool = {
13164
13200
  name: "sheets_append_values",
13165
- description: "Append values to the end of a table in a Google Sheets spreadsheet",
13201
+ description: 'Append values to the end of a table in a Google Sheets spreadsheet. IMPORTANT: By default, this will OVERWRITE existing empty cells. To INSERT new rows instead, set insertDataOption to "INSERT_ROWS".',
13166
13202
  inputSchema: {
13167
13203
  type: "object",
13168
13204
  properties: {
@@ -15566,6 +15602,122 @@ async function handleInsertDate(input) {
15566
15602
  }
15567
15603
  }
15568
15604
 
15605
+ // src/tools/insert-rows.ts
15606
+ var insertRowsTool = {
15607
+ name: "sheets_insert_rows",
15608
+ description: "Insert new rows at a specific position with optional data",
15609
+ inputSchema: {
15610
+ type: "object",
15611
+ properties: {
15612
+ spreadsheetId: {
15613
+ type: "string",
15614
+ description: "The ID of the spreadsheet (found in the URL after /d/)"
15615
+ },
15616
+ range: {
15617
+ type: "string",
15618
+ description: 'The A1 notation anchor point where rows will be inserted (e.g., "Sheet1!A5")'
15619
+ },
15620
+ rows: {
15621
+ type: "number",
15622
+ description: "Number of rows to insert (default: 1)"
15623
+ },
15624
+ position: {
15625
+ type: "string",
15626
+ enum: ["BEFORE", "AFTER"],
15627
+ description: "Position relative to the anchor row (default: BEFORE)"
15628
+ },
15629
+ inheritFromBefore: {
15630
+ type: "boolean",
15631
+ description: "Whether to inherit formatting from the row before (default: false)"
15632
+ },
15633
+ values: {
15634
+ type: "array",
15635
+ items: {
15636
+ type: "array"
15637
+ },
15638
+ description: "Optional 2D array of values to fill the newly inserted rows"
15639
+ },
15640
+ valueInputOption: {
15641
+ type: "string",
15642
+ enum: ["RAW", "USER_ENTERED"],
15643
+ description: "How the input data should be interpreted (default: USER_ENTERED)"
15644
+ }
15645
+ },
15646
+ required: ["spreadsheetId", "range"]
15647
+ }
15648
+ };
15649
+ function indexToColumn(index) {
15650
+ let column = "";
15651
+ let num = index + 1;
15652
+ while (num > 0) {
15653
+ num--;
15654
+ column = String.fromCharCode(num % 26 + "A".charCodeAt(0)) + column;
15655
+ num = Math.floor(num / 26);
15656
+ }
15657
+ return column;
15658
+ }
15659
+ async function handleInsertRows(input) {
15660
+ try {
15661
+ const validatedInput = validateInsertRowsInput(input);
15662
+ const sheets = await getAuthenticatedClient();
15663
+ const { sheetName, range: cellRange } = extractSheetName(validatedInput.range);
15664
+ const sheetId = await getSheetId(sheets, validatedInput.spreadsheetId, sheetName);
15665
+ const parsedRange = parseRange(cellRange, sheetId);
15666
+ const anchorRowIndex = parsedRange.startRowIndex ?? 0;
15667
+ const anchorColumnIndex = parsedRange.startColumnIndex ?? 0;
15668
+ const startIndex = validatedInput.position === "AFTER" ? anchorRowIndex + 1 : anchorRowIndex;
15669
+ const endIndex = startIndex + validatedInput.rows;
15670
+ const insertRequest = {
15671
+ spreadsheetId: validatedInput.spreadsheetId,
15672
+ requestBody: {
15673
+ requests: [
15674
+ {
15675
+ insertDimension: {
15676
+ range: {
15677
+ sheetId,
15678
+ dimension: "ROWS",
15679
+ startIndex,
15680
+ endIndex
15681
+ },
15682
+ inheritFromBefore: validatedInput.inheritFromBefore
15683
+ }
15684
+ }
15685
+ ]
15686
+ }
15687
+ };
15688
+ await sheets.spreadsheets.batchUpdate(insertRequest);
15689
+ if (validatedInput.values && validatedInput.values.length > 0) {
15690
+ const updateStartRow = startIndex + 1;
15691
+ const updateEndRow = updateStartRow + validatedInput.values.length - 1;
15692
+ const startColumn = indexToColumn(anchorColumnIndex);
15693
+ const endColumn = indexToColumn(
15694
+ anchorColumnIndex + Math.max(...validatedInput.values.map((row) => row.length)) - 1
15695
+ );
15696
+ const updateRange = sheetName ? `'${sheetName}'!${startColumn}${updateStartRow}:${endColumn}${updateEndRow}` : `${startColumn}${updateStartRow}:${endColumn}${updateEndRow}`;
15697
+ await sheets.spreadsheets.values.update({
15698
+ spreadsheetId: validatedInput.spreadsheetId,
15699
+ range: updateRange,
15700
+ valueInputOption: validatedInput.valueInputOption,
15701
+ requestBody: {
15702
+ values: validatedInput.values
15703
+ }
15704
+ });
15705
+ const cellCount = validatedInput.values.reduce(
15706
+ (sum, row) => sum + row.length,
15707
+ 0
15708
+ );
15709
+ return formatToolResponse(
15710
+ `Inserted ${validatedInput.rows} rows ${validatedInput.position} row ${anchorRowIndex + 1} on "${sheetName || "Sheet"}" and updated ${cellCount} cells in range: ${updateRange}`
15711
+ );
15712
+ }
15713
+ return formatToolResponse(
15714
+ `Inserted ${validatedInput.rows} rows ${validatedInput.position} row ${anchorRowIndex + 1} on "${sheetName || "Sheet"}"`
15715
+ );
15716
+ } catch (error) {
15717
+ return handleError(error);
15718
+ }
15719
+ }
15720
+
15569
15721
  // src/index.ts
15570
15722
  if (process.env.NODE_ENV !== "production") {
15571
15723
  try {
@@ -15607,7 +15759,9 @@ var toolHandlers = /* @__PURE__ */ new Map([
15607
15759
  ["sheets_delete_chart", handleDeleteChart],
15608
15760
  // Link and date operations
15609
15761
  ["sheets_insert_link", handleInsertLink],
15610
- ["sheets_insert_date", handleInsertDate]
15762
+ ["sheets_insert_date", handleInsertDate],
15763
+ // Row operations
15764
+ ["sheets_insert_rows", handleInsertRows]
15611
15765
  ]);
15612
15766
  var allTools = [
15613
15767
  checkAccessTool,
@@ -15635,7 +15789,8 @@ var allTools = [
15635
15789
  updateChartTool,
15636
15790
  deleteChartTool,
15637
15791
  insertLinkTool,
15638
- insertDateTool
15792
+ insertDateTool,
15793
+ insertRowsTool
15639
15794
  ];
15640
15795
  async function main() {
15641
15796
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-gsheets",
3
- "version": "1.4.3",
3
+ "version": "1.5.2",
4
4
  "description": "Model Context Protocol (MCP) server for Google Sheets API integration",
5
5
  "author": "freema",
6
6
  "license": "MIT",