gogcli-mcp-sheets 2.3.0 → 2.4.1

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.
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "Extended Google Sheets for Claude via gogcli — auth + full Sheets support",
10
- "version": "2.3.0"
10
+ "version": "2.4.1"
11
11
  },
12
12
  "plugins": [
13
13
  {
@@ -15,7 +15,7 @@
15
15
  "displayName": "gogcli (Sheets)",
16
16
  "source": "./",
17
17
  "description": "Extended Google Sheets for Claude via gogcli — auth + full Sheets support",
18
- "version": "2.3.0",
18
+ "version": "2.4.1",
19
19
  "author": {
20
20
  "name": "Chris Hall"
21
21
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gogcli-mcp-sheets",
3
3
  "displayName": "gogcli (Sheets)",
4
- "version": "2.3.0",
4
+ "version": "2.4.1",
5
5
  "description": "Extended Google Sheets for Claude via gogcli — auth + full Sheets support",
6
6
  "author": {
7
7
  "name": "Chris Hall",
package/dist/index.js CHANGED
@@ -31399,7 +31399,7 @@ function registerSheetsTools(server2) {
31399
31399
  }
31400
31400
 
31401
31401
  // ../gogcli-mcp/src/server.ts
31402
- var VERSION = true ? "2.3.0" : "0.0.0";
31402
+ var VERSION = true ? "2.4.1" : "0.0.0";
31403
31403
  function createServer(options) {
31404
31404
  return new McpServer({
31405
31405
  name: options?.name ?? "gogcli",
@@ -31548,13 +31548,32 @@ function registerExtraSheetsTools(server2) {
31548
31548
  start: external_exports.number().describe("Start index (0-based)"),
31549
31549
  count: external_exports.number().optional().describe("Number of rows/columns to insert (default: 1)"),
31550
31550
  after: external_exports.boolean().optional().describe("Insert after the start index instead of before. With after:true the new dimension lands at start+1, leaving the existing dimension at start untouched."),
31551
+ inheritFromBefore: external_exports.boolean().optional().describe("Inherit number format / styling from the row/column before the insertion. Defaults to true with after:true, false otherwise; pass false to inherit from the row/column after the insertion instead. Cannot inherit from before when inserting at the first row/column."),
31551
31552
  account: accountParam
31552
31553
  }
31553
- }, async ({ spreadsheetId, sheet, dimension, start, count, after, account }) => {
31554
+ }, async ({ spreadsheetId, sheet, dimension, start, count, after, inheritFromBefore, account }) => {
31554
31555
  const effectiveStart = after ? start + 1 : start;
31555
31556
  const args = ["sheets", "insert", spreadsheetId, sheet, dimension, String(effectiveStart)];
31556
31557
  if (count !== void 0) args.push(`--count=${count}`);
31557
31558
  if (after) args.push("--after");
31559
+ if (inheritFromBefore !== void 0) args.push(`--inherit-from-before=${inheritFromBefore}`);
31560
+ return runOrDiagnose(args, { account });
31561
+ });
31562
+ server2.registerTool("gog_sheets_copy_paste", {
31563
+ description: "Copy a source range's values/formulas/format to a destination range via the Sheets CopyPasteRequest. A destination larger than the source tiles the source to fill it \u2014 the canonical way to fill formulas down or across with relative references adjusted (aliases: fill, copy-range). Use type to control what is pasted (NORMAL pastes everything; FORMULA fills formulas with adjusted references; VALUES/FORMAT/etc. paste only that aspect).",
31564
+ annotations: { destructiveHint: true },
31565
+ inputSchema: {
31566
+ spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
31567
+ source: external_exports.string().describe("Source range (e.g. Sheet1!A2:H71)"),
31568
+ dest: external_exports.string().describe("Destination range (e.g. Sheet1!A2:H120). Larger than the source tiles/fills it."),
31569
+ type: external_exports.enum(["NORMAL", "VALUES", "FORMAT", "FORMULA", "NO_BORDERS", "DATA_VALIDATION", "CONDITIONAL_FORMATTING"]).optional().describe("Paste type (default: NORMAL pastes everything)"),
31570
+ transpose: external_exports.boolean().optional().describe("Paste transposed (swap rows and columns)"),
31571
+ account: accountParam
31572
+ }
31573
+ }, async ({ spreadsheetId, source, dest, type, transpose, account }) => {
31574
+ const args = ["sheets", "copy-paste", spreadsheetId, source, dest];
31575
+ if (type) args.push(`--type=${type}`);
31576
+ if (transpose) args.push("--transpose");
31558
31577
  return runOrDiagnose(args, { account });
31559
31578
  });
31560
31579
  server2.registerTool("gog_sheets_merge", {
@@ -31835,11 +31854,11 @@ ${result.content[0].text}` }] };
31835
31854
  return runOrDiagnose(["sheets", "named-ranges", "delete", spreadsheetId, nameOrId], { account });
31836
31855
  });
31837
31856
  server2.registerTool("gog_sheets_batch_update", {
31838
- description: 'Update values in multiple ranges with one Sheets API request. dataJson is a JSON array of {range, values} objects (or "@/path/to/file.json"). Atomic \u2014 either all ranges update or none do.',
31857
+ description: `Update values in multiple ranges with one Sheets API request. dataJson is a JSON array of {range, values} objects \u2014 pass it inline as a literal JSON string. Atomic \u2014 either all ranges update or none do. (The CLI also accepts "@/path/to/file.json", but that file is read on the gog server's filesystem, not the caller's, so inline JSON is the right form for remote MCP callers.)`,
31839
31858
  annotations: { destructiveHint: true },
31840
31859
  inputSchema: {
31841
31860
  spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
31842
- dataJson: external_exports.string().describe('Value ranges as JSON array, or @file (e.g. [{"range":"Sheet1!A1:B2","values":[["a","b"]]}])'),
31861
+ dataJson: external_exports.string().describe('Value ranges as an inline JSON array, e.g. [{"range":"Sheet1!A1:B2","values":[["a","b"]]}]. (An "@/path" form is read on the gog server filesystem, not yours \u2014 inline the JSON instead.)'),
31843
31862
  input: external_exports.enum(["RAW", "USER_ENTERED"]).optional().describe("Value input option (default: USER_ENTERED)"),
31844
31863
  includeValuesInResponse: external_exports.boolean().optional().describe("Include updated values in the response"),
31845
31864
  responseRender: external_exports.enum(["FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA"]).optional().describe("Response value render option"),
package/manifest.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "manifest_version": "0.3",
4
4
  "name": "gogcli-mcp-sheets",
5
5
  "display_name": "gogcli (Sheets)",
6
- "version": "2.3.0",
6
+ "version": "2.4.1",
7
7
  "description": "Extended Google Sheets for Claude via gogcli — auth + full Sheets support",
8
8
  "author": {
9
9
  "name": "Chris Hall",
@@ -136,6 +136,10 @@
136
136
  "name": "gog_sheets_insert",
137
137
  "description": "Insert rows or columns into a sheet."
138
138
  },
139
+ {
140
+ "name": "gog_sheets_copy_paste",
141
+ "description": "Copy a range's values/formulas/format to another range (tiles to fill down/across)."
142
+ },
139
143
  {
140
144
  "name": "gog_sheets_merge",
141
145
  "description": "Merge cells in a range."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gogcli-mcp-sheets",
3
- "version": "2.3.0",
3
+ "version": "2.4.1",
4
4
  "mcpName": "io.github.chrischall/gogcli-mcp-sheets",
5
5
  "description": "Extended Google Sheets MCP server via gogcli — all base tools plus full Sheets support",
6
6
  "author": "Claude Code (AI) <https://www.anthropic.com/claude>",
package/server.json CHANGED
@@ -7,12 +7,12 @@
7
7
  "source": "github",
8
8
  "subfolder": "packages/gogcli-mcp-sheets"
9
9
  },
10
- "version": "2.3.0",
10
+ "version": "2.4.1",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "gogcli-mcp-sheets",
15
- "version": "2.3.0",
15
+ "version": "2.4.1",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  },
@@ -178,9 +178,10 @@ export function registerExtraSheetsTools(server: McpServer): void {
178
178
  start: z.number().describe('Start index (0-based)'),
179
179
  count: z.number().optional().describe('Number of rows/columns to insert (default: 1)'),
180
180
  after: z.boolean().optional().describe('Insert after the start index instead of before. With after:true the new dimension lands at start+1, leaving the existing dimension at start untouched.'),
181
+ inheritFromBefore: z.boolean().optional().describe('Inherit number format / styling from the row/column before the insertion. Defaults to true with after:true, false otherwise; pass false to inherit from the row/column after the insertion instead. Cannot inherit from before when inserting at the first row/column.'),
181
182
  account: accountParam,
182
183
  },
183
- }, async ({ spreadsheetId, sheet, dimension, start, count, after, account }) => {
184
+ }, async ({ spreadsheetId, sheet, dimension, start, count, after, inheritFromBefore, account }) => {
184
185
  // The underlying `gog sheets insert` treats `start` as 1-based with `--after` shifting +1.
185
186
  // To honor our 0-based `start` and make `after:true` actually shift the insertion to start+1
186
187
  // (issue #42), we send `start+1` when `after:true`. With after:false the CLI's `start-1`
@@ -189,6 +190,25 @@ export function registerExtraSheetsTools(server: McpServer): void {
189
190
  const args = ['sheets', 'insert', spreadsheetId, sheet, dimension, String(effectiveStart)];
190
191
  if (count !== undefined) args.push(`--count=${count}`);
191
192
  if (after) args.push('--after');
193
+ if (inheritFromBefore !== undefined) args.push(`--inherit-from-before=${inheritFromBefore}`);
194
+ return runOrDiagnose(args, { account });
195
+ });
196
+
197
+ server.registerTool('gog_sheets_copy_paste', {
198
+ description: 'Copy a source range\'s values/formulas/format to a destination range via the Sheets CopyPasteRequest. A destination larger than the source tiles the source to fill it — the canonical way to fill formulas down or across with relative references adjusted (aliases: fill, copy-range). Use type to control what is pasted (NORMAL pastes everything; FORMULA fills formulas with adjusted references; VALUES/FORMAT/etc. paste only that aspect).',
199
+ annotations: { destructiveHint: true },
200
+ inputSchema: {
201
+ spreadsheetId: z.string().describe('Spreadsheet ID'),
202
+ source: z.string().describe('Source range (e.g. Sheet1!A2:H71)'),
203
+ dest: z.string().describe('Destination range (e.g. Sheet1!A2:H120). Larger than the source tiles/fills it.'),
204
+ type: z.enum(['NORMAL', 'VALUES', 'FORMAT', 'FORMULA', 'NO_BORDERS', 'DATA_VALIDATION', 'CONDITIONAL_FORMATTING']).optional().describe('Paste type (default: NORMAL pastes everything)'),
205
+ transpose: z.boolean().optional().describe('Paste transposed (swap rows and columns)'),
206
+ account: accountParam,
207
+ },
208
+ }, async ({ spreadsheetId, source, dest, type, transpose, account }) => {
209
+ const args = ['sheets', 'copy-paste', spreadsheetId, source, dest];
210
+ if (type) args.push(`--type=${type}`);
211
+ if (transpose) args.push('--transpose');
192
212
  return runOrDiagnose(args, { account });
193
213
  });
194
214
 
@@ -485,11 +505,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
485
505
  });
486
506
 
487
507
  server.registerTool('gog_sheets_batch_update', {
488
- description: 'Update values in multiple ranges with one Sheets API request. dataJson is a JSON array of {range, values} objects (or "@/path/to/file.json"). Atomic either all ranges update or none do.',
508
+ description: 'Update values in multiple ranges with one Sheets API request. dataJson is a JSON array of {range, values} objects — pass it inline as a literal JSON string. Atomic — either all ranges update or none do. (The CLI also accepts "@/path/to/file.json", but that file is read on the gog server\'s filesystem, not the caller\'s, so inline JSON is the right form for remote MCP callers.)',
489
509
  annotations: { destructiveHint: true },
490
510
  inputSchema: {
491
511
  spreadsheetId: z.string().describe('Spreadsheet ID'),
492
- dataJson: z.string().describe('Value ranges as JSON array, or @file (e.g. [{"range":"Sheet1!A1:B2","values":[["a","b"]]}])'),
512
+ dataJson: z.string().describe('Value ranges as an inline JSON array, e.g. [{"range":"Sheet1!A1:B2","values":[["a","b"]]}]. (An "@/path" form is read on the gog server filesystem, not yours — inline the JSON instead.)'),
493
513
  input: z.enum(['RAW', 'USER_ENTERED']).optional().describe('Value input option (default: USER_ENTERED)'),
494
514
  includeValuesInResponse: z.boolean().optional().describe('Include updated values in the response'),
495
515
  responseRender: z.enum(['FORMATTED_VALUE', 'UNFORMATTED_VALUE', 'FORMULA']).optional().describe('Response value render option'),
@@ -149,6 +149,43 @@ describe('gog_sheets_insert', () => {
149
149
  await handlers.get('gog_sheets_insert')!({ spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 0, after: false });
150
150
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '0'], { account: undefined });
151
151
  });
152
+
153
+ it('passes --inherit-from-before=true when set', async () => {
154
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
155
+ const handlers = setupHandlers();
156
+ await handlers.get('gog_sheets_insert')!({ spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 5, after: true, inheritFromBefore: true });
157
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '6', '--after', '--inherit-from-before=true'], { account: undefined });
158
+ });
159
+
160
+ it('passes --inherit-from-before=false to inherit from the following neighbor', async () => {
161
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
162
+ const handlers = setupHandlers();
163
+ await handlers.get('gog_sheets_insert')!({ spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 5, after: true, inheritFromBefore: false });
164
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '6', '--after', '--inherit-from-before=false'], { account: undefined });
165
+ });
166
+ });
167
+
168
+ describe('gog_sheets_copy_paste', () => {
169
+ it('calls runOrDiagnose with required source/dest', async () => {
170
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
171
+ const handlers = setupHandlers();
172
+ await handlers.get('gog_sheets_copy_paste')!({ spreadsheetId: 'sid', source: 'Sheet1!A2:H71', dest: 'Sheet1!A2:H120' });
173
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy-paste', 'sid', 'Sheet1!A2:H71', 'Sheet1!A2:H120'], { account: undefined });
174
+ });
175
+
176
+ it('appends --type and --transpose when provided', async () => {
177
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
178
+ const handlers = setupHandlers();
179
+ await handlers.get('gog_sheets_copy_paste')!({ spreadsheetId: 'sid', source: 'A1:B2', dest: 'D1:E2', type: 'FORMULA', transpose: true });
180
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy-paste', 'sid', 'A1:B2', 'D1:E2', '--type=FORMULA', '--transpose'], { account: undefined });
181
+ });
182
+
183
+ it('forwards account', async () => {
184
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
185
+ const handlers = setupHandlers();
186
+ await handlers.get('gog_sheets_copy_paste')!({ spreadsheetId: 'sid', source: 'A1', dest: 'B1', account: 'a@b.com' });
187
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy-paste', 'sid', 'A1', 'B1'], { account: 'a@b.com' });
188
+ });
152
189
  });
153
190
 
154
191
  // 8. merge