gogcli-mcp-sheets 2.30.0 → 4.0.0

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.
@@ -1,6 +1,6 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import { McpServer } from '@modelcontextprotocol/server';
2
+ import type { CallToolResult } from '@modelcontextprotocol/server';
2
3
  import { z } from 'zod';
3
- import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
4
4
  import { rawTextResult } from '@chrischall/mcp-utils';
5
5
  import { accountParam, runOrDiagnose, run, diagnose, errorText, payloadArg } from '../../../gogcli-mcp/src/lib.js';
6
6
 
@@ -81,10 +81,10 @@ export function registerExtraSheetsTools(server: McpServer): void {
81
81
  server.registerTool('gog_sheets_list_tabs', {
82
82
  description: 'List tabs (sheets) in a spreadsheet with their titles, sheetIds, and indices. A friendlier view than gog_sheets_metadata when you only need the tab list — useful for restructuring a workbook over a long agent session without losing track of names.',
83
83
  annotations: { readOnlyHint: true },
84
- inputSchema: {
84
+ inputSchema: z.object({
85
85
  spreadsheetId: z.string().describe('Spreadsheet ID (from the URL)'),
86
86
  account: accountParam,
87
- },
87
+ }),
88
88
  }, async ({ spreadsheetId, account }) => {
89
89
  // jq projection keeps the response compact: sheetId, title, index, gridProperties only.
90
90
  return runOrDiagnose(
@@ -95,11 +95,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
95
95
 
96
96
  server.registerTool('gog_sheets_add_tab', {
97
97
  description: 'Add a new sheet tab to a spreadsheet.',
98
- inputSchema: {
98
+ inputSchema: z.object({
99
99
  spreadsheetId: z.string().describe('Spreadsheet ID (from the URL)'),
100
100
  tabName: z.string().describe('Name for the new tab'),
101
101
  account: accountParam,
102
- },
102
+ }),
103
103
  }, async ({ spreadsheetId, tabName, account }) => {
104
104
  return runOrDiagnose(['sheets', 'add-tab', spreadsheetId, tabName], { account });
105
105
  });
@@ -107,11 +107,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
107
107
  server.registerTool('gog_sheets_delete_tab', {
108
108
  description: 'Delete a sheet tab from a spreadsheet.',
109
109
  annotations: { destructiveHint: true },
110
- inputSchema: {
110
+ inputSchema: z.object({
111
111
  spreadsheetId: z.string().describe('Spreadsheet ID'),
112
112
  tabName: z.string().describe('Name of the tab to delete'),
113
113
  account: accountParam,
114
- },
114
+ }),
115
115
  }, async ({ spreadsheetId, tabName, account }) => {
116
116
  return runOrDiagnose(['sheets', 'delete-tab', spreadsheetId, tabName, '--force'], { account }); // gog gates this op; without --force the runner's --no-input makes it refuse
117
117
  });
@@ -119,24 +119,24 @@ export function registerExtraSheetsTools(server: McpServer): void {
119
119
  server.registerTool('gog_sheets_rename_tab', {
120
120
  description: 'Rename a sheet tab.',
121
121
  annotations: { destructiveHint: true },
122
- inputSchema: {
122
+ inputSchema: z.object({
123
123
  spreadsheetId: z.string().describe('Spreadsheet ID'),
124
124
  oldName: z.string().describe('Current tab name'),
125
125
  newName: z.string().describe('New tab name'),
126
126
  account: accountParam,
127
- },
127
+ }),
128
128
  }, async ({ spreadsheetId, oldName, newName, account }) => {
129
129
  return runOrDiagnose(['sheets', 'rename-tab', spreadsheetId, oldName, newName], { account });
130
130
  });
131
131
 
132
132
  server.registerTool('gog_sheets_copy', {
133
133
  description: 'Copy a spreadsheet to a new spreadsheet with the given title.',
134
- inputSchema: {
134
+ inputSchema: z.object({
135
135
  spreadsheetId: z.string().describe('Spreadsheet ID to copy'),
136
136
  title: z.string().describe('Title for the new copy'),
137
137
  parent: z.string().optional().describe('Parent folder ID to place the copy in'),
138
138
  account: accountParam,
139
- },
139
+ }),
140
140
  }, async ({ spreadsheetId, title, parent, account }) => {
141
141
  const args = ['sheets', 'copy', spreadsheetId, title];
142
142
  if (parent) args.push(`--parent=${parent}`);
@@ -146,13 +146,13 @@ export function registerExtraSheetsTools(server: McpServer): void {
146
146
  server.registerTool('gog_sheets_export', {
147
147
  description: 'Export a spreadsheet as CSV, TSV, or PDF.',
148
148
  annotations: { readOnlyHint: true },
149
- inputSchema: {
149
+ inputSchema: z.object({
150
150
  spreadsheetId: z.string().describe('Spreadsheet ID'),
151
151
  format: z.string().optional().describe('Export format: csv, tsv, pdf (default: csv)'),
152
152
  out: z.string().optional().describe('Output file path'),
153
153
  overwrite: z.boolean().optional().describe('Overwrite the output file if it already exists (gog refuses otherwise)'),
154
154
  account: accountParam,
155
- },
155
+ }),
156
156
  }, async ({ spreadsheetId, format, out, overwrite, account }) => {
157
157
  const args = ['sheets', 'export', spreadsheetId];
158
158
  if (format) args.push(`--format=${format}`);
@@ -164,13 +164,13 @@ export function registerExtraSheetsTools(server: McpServer): void {
164
164
  server.registerTool('gog_sheets_freeze', {
165
165
  description: 'Freeze rows and/or columns in a sheet.',
166
166
  annotations: { destructiveHint: true },
167
- inputSchema: {
167
+ inputSchema: z.object({
168
168
  spreadsheetId: z.string().describe('Spreadsheet ID'),
169
169
  rows: z.number().optional().describe('Number of rows to freeze'),
170
170
  cols: z.number().optional().describe('Number of columns to freeze'),
171
171
  sheet: z.string().optional().describe('Sheet tab name (default: first sheet)'),
172
172
  account: accountParam,
173
- },
173
+ }),
174
174
  }, async ({ spreadsheetId, rows, cols, sheet, account }) => {
175
175
  const args = ['sheets', 'freeze', spreadsheetId];
176
176
  if (rows !== undefined) args.push(`--rows=${rows}`);
@@ -182,7 +182,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
182
182
  server.registerTool('gog_sheets_insert', {
183
183
  description: 'Insert rows or columns into a sheet. With after:false (default), the new dimension lands at start. With after:true, the new dimension lands at start+1 (the existing dimension at start is preserved).',
184
184
  annotations: { destructiveHint: true },
185
- inputSchema: {
185
+ inputSchema: z.object({
186
186
  spreadsheetId: z.string().describe('Spreadsheet ID'),
187
187
  sheet: z.string().describe('Sheet tab name'),
188
188
  dimension: z.string().describe('Dimension to insert: ROWS or COLUMNS'),
@@ -191,7 +191,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
191
191
  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.'),
192
192
  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.'),
193
193
  account: accountParam,
194
- },
194
+ }),
195
195
  }, async ({ spreadsheetId, sheet, dimension, start, count, after, inheritFromBefore, account }) => {
196
196
  // gog's positional `<start>` is 1-based, and it rejects 0 ("start must be >= 1").
197
197
  // Without --after it inserts *before* the position, so API start_index = positional - 1;
@@ -209,14 +209,14 @@ export function registerExtraSheetsTools(server: McpServer): void {
209
209
  server.registerTool('gog_sheets_copy_paste', {
210
210
  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).',
211
211
  annotations: { destructiveHint: true },
212
- inputSchema: {
212
+ inputSchema: z.object({
213
213
  spreadsheetId: z.string().describe('Spreadsheet ID'),
214
214
  source: z.string().describe('Source range (e.g. Sheet1!A2:H71)'),
215
215
  dest: z.string().describe('Destination range (e.g. Sheet1!A2:H120). Larger than the source tiles/fills it.'),
216
216
  type: z.enum(['NORMAL', 'VALUES', 'FORMAT', 'FORMULA', 'NO_BORDERS', 'DATA_VALIDATION', 'CONDITIONAL_FORMATTING']).optional().describe('Paste type (default: NORMAL pastes everything)'),
217
217
  transpose: z.boolean().optional().describe('Paste transposed (swap rows and columns)'),
218
218
  account: accountParam,
219
- },
219
+ }),
220
220
  }, async ({ spreadsheetId, source, dest, type, transpose, account }) => {
221
221
  const args = ['sheets', 'copy-paste', spreadsheetId, source, dest];
222
222
  if (type) args.push(`--type=${type}`);
@@ -227,12 +227,12 @@ export function registerExtraSheetsTools(server: McpServer): void {
227
227
  server.registerTool('gog_sheets_merge', {
228
228
  description: 'Merge cells in a range.',
229
229
  annotations: { destructiveHint: true },
230
- inputSchema: {
230
+ inputSchema: z.object({
231
231
  spreadsheetId: z.string().describe('Spreadsheet ID'),
232
232
  range: z.string().describe('Range to merge (e.g. Sheet1!A1:C3)'),
233
233
  type: z.string().optional().describe('Merge type: MERGE_ALL, MERGE_COLUMNS, MERGE_ROWS'),
234
234
  account: accountParam,
235
- },
235
+ }),
236
236
  }, async ({ spreadsheetId, range, type, account }) => {
237
237
  const args = ['sheets', 'merge', spreadsheetId, range];
238
238
  if (type) args.push(`--type=${type}`);
@@ -242,11 +242,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
242
242
  server.registerTool('gog_sheets_unmerge', {
243
243
  description: 'Unmerge previously merged cells in a range.',
244
244
  annotations: { destructiveHint: true },
245
- inputSchema: {
245
+ inputSchema: z.object({
246
246
  spreadsheetId: z.string().describe('Spreadsheet ID'),
247
247
  range: z.string().describe('Range to unmerge (e.g. Sheet1!A1:C3)'),
248
248
  account: accountParam,
249
- },
249
+ }),
250
250
  }, async ({ spreadsheetId, range, account }) => {
251
251
  return runOrDiagnose(['sheets', 'unmerge', spreadsheetId, range], { account });
252
252
  });
@@ -254,7 +254,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
254
254
  server.registerTool('gog_sheets_format', {
255
255
  description: 'Apply cell formatting to a range. The named flags (bold, italic, backgroundColor, etc.) compose into a Sheets API CellFormat — use them for the 90% case. For full API control, pass formatJson + formatFields (Sheets CellFormat + field mask) directly.',
256
256
  annotations: { destructiveHint: true },
257
- inputSchema: {
257
+ inputSchema: z.object({
258
258
  spreadsheetId: z.string().describe('Spreadsheet ID'),
259
259
  range: z.string().describe('Range to format (e.g. Sheet1!A1:C3)'),
260
260
  bold: z.boolean().optional().describe('Set bold'),
@@ -271,7 +271,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
271
271
  formatJson: z.string().optional().describe('Escape hatch: raw CellFormat JSON. When provided, named flags above are ignored and formatJson is sent as-is.'),
272
272
  formatFields: z.string().optional().describe('Comma-separated field mask (e.g. textFormat.bold,backgroundColor). Required when using formatJson; auto-computed when using named flags.'),
273
273
  account: accountParam,
274
- },
274
+ }),
275
275
  }, async (rawArgs) => {
276
276
  const a = rawArgs as {
277
277
  spreadsheetId: string;
@@ -343,14 +343,14 @@ export function registerExtraSheetsTools(server: McpServer): void {
343
343
  server.registerTool('gog_sheets_number_format', {
344
344
  description: 'Set number format on a range (currency, percentage, date, etc.). When type is DATE or DATE_TIME, the target range is peeked first; if every numeric cell is a small integer (< 10000), a warning is prepended to the response because Sheets will render those as 1899/1900 day-serials. Pass force:true to skip the check.',
345
345
  annotations: { destructiveHint: true },
346
- inputSchema: {
346
+ inputSchema: z.object({
347
347
  spreadsheetId: z.string().describe('Spreadsheet ID'),
348
348
  range: z.string().describe('Range to format (e.g. Sheet1!A1:A10)'),
349
349
  type: z.string().optional().describe('Format type: NUMBER, CURRENCY, PERCENT, DATE, DATE_TIME, TIME, SCIENTIFIC, etc.'),
350
350
  pattern: z.string().optional().describe('Custom format pattern (e.g. "#,##0.00", "yyyy-mm-dd")'),
351
351
  force: z.boolean().optional().describe('Skip the DATE/DATE_TIME small-integer warning check'),
352
352
  account: accountParam,
353
- },
353
+ }),
354
354
  }, async ({ spreadsheetId, range, type, pattern, force, account }) => {
355
355
  const isDateType = type === 'DATE' || type === 'DATE_TIME';
356
356
  const warning = isDateType && !force
@@ -369,12 +369,12 @@ export function registerExtraSheetsTools(server: McpServer): void {
369
369
  server.registerTool('gog_sheets_read_format', {
370
370
  description: 'Read cell formatting for a range.',
371
371
  annotations: { readOnlyHint: true },
372
- inputSchema: {
372
+ inputSchema: z.object({
373
373
  spreadsheetId: z.string().describe('Spreadsheet ID'),
374
374
  range: z.string().describe('Range to read formatting from'),
375
375
  effective: z.boolean().optional().describe('Return effective (computed) format including inherited styles'),
376
376
  account: accountParam,
377
- },
377
+ }),
378
378
  }, async ({ spreadsheetId, range, effective, account }) => {
379
379
  const args = ['sheets', 'read-format', spreadsheetId, range];
380
380
  if (effective) args.push('--effective');
@@ -384,13 +384,13 @@ export function registerExtraSheetsTools(server: McpServer): void {
384
384
  server.registerTool('gog_sheets_resize_columns', {
385
385
  description: 'Resize column widths. Use --auto for auto-fit or --width for a specific pixel width.',
386
386
  annotations: { destructiveHint: true },
387
- inputSchema: {
387
+ inputSchema: z.object({
388
388
  spreadsheetId: z.string().describe('Spreadsheet ID'),
389
389
  columns: z.string().describe('Column range (e.g. A:C, or Sheet1!A:C)'),
390
390
  width: z.number().optional().describe('Width in pixels'),
391
391
  auto: z.boolean().optional().describe('Auto-fit column width to content'),
392
392
  account: accountParam,
393
- },
393
+ }),
394
394
  }, async ({ spreadsheetId, columns, width, auto, account }) => {
395
395
  const args = ['sheets', 'resize-columns', spreadsheetId, columns];
396
396
  if (width !== undefined) args.push(`--width=${width}`);
@@ -401,13 +401,13 @@ export function registerExtraSheetsTools(server: McpServer): void {
401
401
  server.registerTool('gog_sheets_resize_rows', {
402
402
  description: 'Resize row heights. Use --auto for auto-fit or --height for a specific pixel height.',
403
403
  annotations: { destructiveHint: true },
404
- inputSchema: {
404
+ inputSchema: z.object({
405
405
  spreadsheetId: z.string().describe('Spreadsheet ID'),
406
406
  rows: z.string().describe('Row range (e.g. 1:10, or Sheet1!1:10)'),
407
407
  height: z.number().optional().describe('Height in pixels'),
408
408
  auto: z.boolean().optional().describe('Auto-fit row height to content'),
409
409
  account: accountParam,
410
- },
410
+ }),
411
411
  }, async ({ spreadsheetId, rows, height, auto, account }) => {
412
412
  const args = ['sheets', 'resize-rows', spreadsheetId, rows];
413
413
  if (height !== undefined) args.push(`--height=${height}`);
@@ -418,11 +418,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
418
418
  server.registerTool('gog_sheets_notes', {
419
419
  description: 'Read cell notes in a range.',
420
420
  annotations: { readOnlyHint: true },
421
- inputSchema: {
421
+ inputSchema: z.object({
422
422
  spreadsheetId: z.string().describe('Spreadsheet ID'),
423
423
  range: z.string().describe('Range to read notes from (e.g. Sheet1!A1:B5)'),
424
424
  account: accountParam,
425
- },
425
+ }),
426
426
  }, async ({ spreadsheetId, range, account }) => {
427
427
  return runOrDiagnose(['sheets', 'notes', spreadsheetId, range], { account });
428
428
  });
@@ -430,12 +430,12 @@ export function registerExtraSheetsTools(server: McpServer): void {
430
430
  server.registerTool('gog_sheets_update_note', {
431
431
  description: 'Add, update, or clear a cell note. Pass an empty string to clear the note.',
432
432
  annotations: { destructiveHint: true },
433
- inputSchema: {
433
+ inputSchema: z.object({
434
434
  spreadsheetId: z.string().describe('Spreadsheet ID'),
435
435
  range: z.string().describe('Cell or range to set the note on (e.g. Sheet1!A1)'),
436
436
  note: z.string().describe('Note text (empty string clears the note). Large notes are written to a temp file and passed to gog as --note-file automatically.'),
437
437
  account: accountParam,
438
- },
438
+ }),
439
439
  }, async ({ spreadsheetId, range, note, account }) => {
440
440
  // payloadArg keeps normal notes on `--note=` and spills a large one to a
441
441
  // temp file passed as `--note-file`, so it can't blow the argv size cap.
@@ -450,11 +450,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
450
450
  server.registerTool('gog_sheets_links', {
451
451
  description: 'List hyperlinks in a range.',
452
452
  annotations: { readOnlyHint: true },
453
- inputSchema: {
453
+ inputSchema: z.object({
454
454
  spreadsheetId: z.string().describe('Spreadsheet ID'),
455
455
  range: z.string().describe('Range to scan for hyperlinks (e.g. Sheet1!A1:Z100)'),
456
456
  account: accountParam,
457
- },
457
+ }),
458
458
  }, async ({ spreadsheetId, range, account }) => {
459
459
  return runOrDiagnose(['sheets', 'links', spreadsheetId, range], { account });
460
460
  });
@@ -465,7 +465,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
465
465
  '(2) multi-link cell — pass cell + runsJson, a JSON array of rich-text runs (a run with an empty uri is plain text); ' +
466
466
  '(3) batch — pass cellsJson, a JSON array of {cell,url,text} or {cell,runs:[...]} objects written in one request.',
467
467
  annotations: { destructiveHint: true },
468
- inputSchema: {
468
+ inputSchema: z.object({
469
469
  spreadsheetId: z.string().describe('Spreadsheet ID'),
470
470
  cell: z.string().optional().describe('Target cell in A1 notation (e.g. Sheet1!B2). Used by single-link and runsJson modes; omit for batch (cellsJson).'),
471
471
  url: z.string().optional().describe('Hyperlink URL for single-link mode'),
@@ -473,7 +473,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
473
473
  runsJson: z.string().optional().describe('Multi-link cell: JSON array of runs, e.g. [{"text":"Act A","uri":"https://a"},{"text":" / "},{"text":"Act B","uri":"https://b"}]. A run with an empty uri is plain text.'),
474
474
  cellsJson: z.string().optional().describe('Batch: JSON array of {cell,url,text} or {cell,runs:[{text,uri}]} objects, written in one request. Inline JSON or @file (a path gog reads itself — cheaper than inlining a large batch). gog also accepts @- for stdin, but this server never writes to gog\'s stdin, so @- would hang until the call times out.'),
475
475
  account: accountParam,
476
- },
476
+ }),
477
477
  }, async ({ spreadsheetId, cell, url, text, runsJson, cellsJson, account }) => {
478
478
  const args = ['sheets', 'links', 'set', spreadsheetId];
479
479
  if (cell) args.push(cell);
@@ -487,11 +487,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
487
487
  server.registerTool('gog_sheets_validation_get', {
488
488
  description: 'Read the data-validation rules (dropdowns, checkboxes, number/date conditions, custom formulas) applied to a range.',
489
489
  annotations: { readOnlyHint: true },
490
- inputSchema: {
490
+ inputSchema: z.object({
491
491
  spreadsheetId: z.string().describe('Spreadsheet ID'),
492
492
  range: z.string().describe('Range to inspect (e.g. Sheet1!A1:A10)'),
493
493
  account: accountParam,
494
- },
494
+ }),
495
495
  }, async ({ spreadsheetId, range, account }) => {
496
496
  return runOrDiagnose(['sheets', 'validation', 'get', spreadsheetId, range], { account });
497
497
  });
@@ -499,7 +499,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
499
499
  server.registerTool('gog_sheets_validation_set', {
500
500
  description: 'Set a data-validation rule on a range — dropdowns (ONE_OF_LIST / ONE_OF_RANGE), checkboxes (BOOLEAN), number/date conditions, or custom formulas. Overwrites any existing rule on the range. Repeat values for list entries or between-bounds.',
501
501
  annotations: { destructiveHint: true },
502
- inputSchema: {
502
+ inputSchema: z.object({
503
503
  spreadsheetId: z.string().describe('Spreadsheet ID'),
504
504
  range: z.string().describe('Range to apply the rule to (e.g. Sheet1!A1:A10)'),
505
505
  type: z.string().describe('Condition type, e.g. ONE_OF_LIST, ONE_OF_RANGE, NUMBER_BETWEEN, NUMBER_GREATER, DATE_AFTER, BOOLEAN, CUSTOM_FORMULA'),
@@ -509,7 +509,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
509
509
  showCustomUi: z.boolean().optional().describe('Show dropdown or checkbox UI where supported'),
510
510
  filteredRowsIncluded: z.boolean().optional().describe('Apply the rule to filtered rows too; required for table-managed dropdown columns'),
511
511
  account: accountParam,
512
- },
512
+ }),
513
513
  }, async ({ spreadsheetId, range, type, values, strict, inputMessage, showCustomUi, filteredRowsIncluded, account }) => {
514
514
  const args = ['sheets', 'validation', 'set', spreadsheetId, range, `--type=${type}`];
515
515
  if (values) for (const v of values) args.push(`--value=${v}`);
@@ -523,12 +523,12 @@ export function registerExtraSheetsTools(server: McpServer): void {
523
523
  server.registerTool('gog_sheets_validation_clear', {
524
524
  description: 'Remove all data-validation rules from a range.',
525
525
  annotations: { destructiveHint: true },
526
- inputSchema: {
526
+ inputSchema: z.object({
527
527
  spreadsheetId: z.string().describe('Spreadsheet ID'),
528
528
  range: z.string().describe('Range to clear (e.g. Sheet1!A1:A10)'),
529
529
  filteredRowsIncluded: z.boolean().optional().describe('Clear rules from filtered rows too; required for table-managed dropdown columns'),
530
530
  account: accountParam,
531
- },
531
+ }),
532
532
  }, async ({ spreadsheetId, range, filteredRowsIncluded, account }) => {
533
533
  const args = ['sheets', 'validation', 'clear', spreadsheetId, range];
534
534
  if (filteredRowsIncluded) args.push('--filtered-rows-included');
@@ -538,14 +538,14 @@ export function registerExtraSheetsTools(server: McpServer): void {
538
538
  server.registerTool('gog_sheets_delete_dimension', {
539
539
  description: 'Delete a span of rows or columns, table-aware: intersecting table objects are preserved (shrunk) along with their remaining data, instead of being corrupted as with a raw DeleteDimension batch update. Target by A1 range (e.g. Sheet1!5:7 or Sheet1!C:D) or by sheet name plus start/end.',
540
540
  annotations: { destructiveHint: true },
541
- inputSchema: {
541
+ inputSchema: z.object({
542
542
  spreadsheetId: z.string().describe('Spreadsheet ID'),
543
543
  rangeOrSheet: z.string().describe('A1 range covering the rows/columns to delete, or a sheet name (then start/end are required)'),
544
544
  dimension: z.enum(['ROWS', 'COLUMNS']).describe('Dimension to delete'),
545
545
  start: z.number().int().optional().describe('First row/column to delete (1-based, inclusive; required with a sheet-name target)'),
546
546
  end: z.number().int().optional().describe('Last row/column to delete (1-based, inclusive; required with a sheet-name target)'),
547
547
  account: accountParam,
548
- },
548
+ }),
549
549
  }, async ({ spreadsheetId, rangeOrSheet, dimension, start, end, account }) => {
550
550
  const args = ['sheets', 'delete-dimension', spreadsheetId, rangeOrSheet, `--dimension=${dimension}`];
551
551
  if (start !== undefined) args.push(`--start=${start}`);
@@ -557,10 +557,10 @@ export function registerExtraSheetsTools(server: McpServer): void {
557
557
  server.registerTool('gog_sheets_named_ranges_list', {
558
558
  description: 'List all named ranges in a spreadsheet.',
559
559
  annotations: { readOnlyHint: true },
560
- inputSchema: {
560
+ inputSchema: z.object({
561
561
  spreadsheetId: z.string().describe('Spreadsheet ID'),
562
562
  account: accountParam,
563
- },
563
+ }),
564
564
  }, async ({ spreadsheetId, account }) => {
565
565
  return runOrDiagnose(['sheets', 'named-ranges', 'list', spreadsheetId], { account });
566
566
  });
@@ -568,23 +568,23 @@ export function registerExtraSheetsTools(server: McpServer): void {
568
568
  server.registerTool('gog_sheets_named_ranges_get', {
569
569
  description: 'Get a named range by name or ID.',
570
570
  annotations: { readOnlyHint: true },
571
- inputSchema: {
571
+ inputSchema: z.object({
572
572
  spreadsheetId: z.string().describe('Spreadsheet ID'),
573
573
  nameOrId: z.string().describe('Named range name or ID'),
574
574
  account: accountParam,
575
- },
575
+ }),
576
576
  }, async ({ spreadsheetId, nameOrId, account }) => {
577
577
  return runOrDiagnose(['sheets', 'named-ranges', 'get', spreadsheetId, nameOrId], { account });
578
578
  });
579
579
 
580
580
  server.registerTool('gog_sheets_named_ranges_add', {
581
581
  description: 'Create a new named range.',
582
- inputSchema: {
582
+ inputSchema: z.object({
583
583
  spreadsheetId: z.string().describe('Spreadsheet ID'),
584
584
  name: z.string().describe('Name for the range'),
585
585
  range: z.string().describe('Cell range (e.g. Sheet1!A1:B10)'),
586
586
  account: accountParam,
587
- },
587
+ }),
588
588
  }, async ({ spreadsheetId, name, range, account }) => {
589
589
  return runOrDiagnose(['sheets', 'named-ranges', 'add', spreadsheetId, name, range], { account });
590
590
  });
@@ -592,13 +592,13 @@ export function registerExtraSheetsTools(server: McpServer): void {
592
592
  server.registerTool('gog_sheets_named_ranges_update', {
593
593
  description: 'Update a named range (change its name, range, or both).',
594
594
  annotations: { destructiveHint: true },
595
- inputSchema: {
595
+ inputSchema: z.object({
596
596
  spreadsheetId: z.string().describe('Spreadsheet ID'),
597
597
  nameOrId: z.string().describe('Current named range name or ID'),
598
598
  name: z.string().optional().describe('New name'),
599
599
  range: z.string().optional().describe('New cell range'),
600
600
  account: accountParam,
601
- },
601
+ }),
602
602
  }, async ({ spreadsheetId, nameOrId, name, range, account }) => {
603
603
  const args = ['sheets', 'named-ranges', 'update', spreadsheetId, nameOrId];
604
604
  if (name) args.push(`--name=${name}`);
@@ -609,11 +609,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
609
609
  server.registerTool('gog_sheets_named_ranges_delete', {
610
610
  description: 'Delete a named range.',
611
611
  annotations: { destructiveHint: true },
612
- inputSchema: {
612
+ inputSchema: z.object({
613
613
  spreadsheetId: z.string().describe('Spreadsheet ID'),
614
614
  nameOrId: z.string().describe('Named range name or ID to delete'),
615
615
  account: accountParam,
616
- },
616
+ }),
617
617
  }, async ({ spreadsheetId, nameOrId, account }) => {
618
618
  return runOrDiagnose(['sheets', 'named-ranges', 'delete', spreadsheetId, nameOrId], { account });
619
619
  });
@@ -621,7 +621,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
621
621
  server.registerTool('gog_sheets_batch_update', {
622
622
  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.)',
623
623
  annotations: { destructiveHint: true },
624
- inputSchema: {
624
+ inputSchema: z.object({
625
625
  spreadsheetId: z.string().describe('Spreadsheet ID'),
626
626
  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.)'),
627
627
  input: z.enum(['RAW', 'USER_ENTERED']).optional().describe('Value input option (default: USER_ENTERED)'),
@@ -629,7 +629,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
629
629
  responseRender: z.enum(['FORMATTED_VALUE', 'UNFORMATTED_VALUE', 'FORMULA']).optional().describe('Response value render option'),
630
630
  responseDateTimeRender: z.enum(['SERIAL_NUMBER', 'FORMATTED_STRING']).optional().describe('Response date/time render option'),
631
631
  account: accountParam,
632
- },
632
+ }),
633
633
  }, async ({ spreadsheetId, dataJson, input, includeValuesInResponse, responseRender, responseDateTimeRender, account }) => {
634
634
  const args = ['sheets', 'batch-update', spreadsheetId, `--data-json=${dataJson}`];
635
635
  if (input) args.push(`--input=${input}`);
@@ -642,12 +642,12 @@ export function registerExtraSheetsTools(server: McpServer): void {
642
642
  server.registerTool('gog_sheets_reorder_tab', {
643
643
  description: 'Move a tab to a specific 0-based position. `tab` is the tab name or numeric sheetId; `to=0` is the leftmost slot.',
644
644
  annotations: { destructiveHint: true },
645
- inputSchema: {
645
+ inputSchema: z.object({
646
646
  spreadsheetId: z.string().describe('Spreadsheet ID'),
647
647
  tab: z.string().describe('Target tab by name or numeric sheet ID'),
648
648
  to: z.number().int().min(0).describe('Destination final 0-based tab index'),
649
649
  account: accountParam,
650
- },
650
+ }),
651
651
  }, async ({ spreadsheetId, tab, to, account }) => {
652
652
  return runOrDiagnose(
653
653
  ['sheets', 'reorder-tab', spreadsheetId, `--tab=${tab}`, `--to=${to}`],
@@ -660,10 +660,10 @@ export function registerExtraSheetsTools(server: McpServer): void {
660
660
  server.registerTool('gog_sheets_chart_list', {
661
661
  description: 'List embedded charts in a spreadsheet (chartId, type, position).',
662
662
  annotations: { readOnlyHint: true },
663
- inputSchema: {
663
+ inputSchema: z.object({
664
664
  spreadsheetId: z.string().describe('Spreadsheet ID'),
665
665
  account: accountParam,
666
- },
666
+ }),
667
667
  }, async ({ spreadsheetId, account }) => {
668
668
  return runOrDiagnose(['sheets', 'chart', 'list', spreadsheetId], { account });
669
669
  });
@@ -671,18 +671,18 @@ export function registerExtraSheetsTools(server: McpServer): void {
671
671
  server.registerTool('gog_sheets_chart_get', {
672
672
  description: 'Get the full definition (spec + position) of a single chart by its numeric chart ID.',
673
673
  annotations: { readOnlyHint: true },
674
- inputSchema: {
674
+ inputSchema: z.object({
675
675
  spreadsheetId: z.string().describe('Spreadsheet ID'),
676
676
  chartId: z.string().describe('Numeric chart ID (from gog_sheets_chart_list)'),
677
677
  account: accountParam,
678
- },
678
+ }),
679
679
  }, async ({ spreadsheetId, chartId, account }) => {
680
680
  return runOrDiagnose(['sheets', 'chart', 'get', spreadsheetId, chartId], { account });
681
681
  });
682
682
 
683
683
  server.registerTool('gog_sheets_chart_create', {
684
684
  description: 'Create an embedded chart from a JSON spec. specJson is a Sheets API ChartSpec (or full EmbeddedChart) — inline or @/path/to/file.json. Anchor the chart with sheet + anchor (A1 cell), and optionally size it with width/height pixels.',
685
- inputSchema: {
685
+ inputSchema: z.object({
686
686
  spreadsheetId: z.string().describe('Spreadsheet ID'),
687
687
  specJson: z.string().describe('ChartSpec or EmbeddedChart JSON (inline or @file)'),
688
688
  sheet: z.string().optional().describe('Sheet name for the anchor (resolved to sheetId)'),
@@ -690,7 +690,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
690
690
  width: z.number().optional().describe('Chart width in pixels (default: 600)'),
691
691
  height: z.number().optional().describe('Chart height in pixels (default: 371)'),
692
692
  account: accountParam,
693
- },
693
+ }),
694
694
  }, async ({ spreadsheetId, specJson, sheet, anchor, width, height, account }) => {
695
695
  const args = ['sheets', 'chart', 'create', spreadsheetId, `--spec-json=${specJson}`];
696
696
  if (sheet) args.push(`--sheet=${sheet}`);
@@ -703,12 +703,12 @@ export function registerExtraSheetsTools(server: McpServer): void {
703
703
  server.registerTool('gog_sheets_chart_update', {
704
704
  description: 'Replace a chart spec by chart ID. specJson is a Sheets API ChartSpec (or full EmbeddedChart) — inline or @/path/to/file.json.',
705
705
  annotations: { destructiveHint: true },
706
- inputSchema: {
706
+ inputSchema: z.object({
707
707
  spreadsheetId: z.string().describe('Spreadsheet ID'),
708
708
  chartId: z.string().describe('Numeric chart ID to update'),
709
709
  specJson: z.string().describe('ChartSpec or EmbeddedChart JSON (inline or @file)'),
710
710
  account: accountParam,
711
- },
711
+ }),
712
712
  }, async ({ spreadsheetId, chartId, specJson, account }) => {
713
713
  return runOrDiagnose(
714
714
  ['sheets', 'chart', 'update', spreadsheetId, chartId, `--spec-json=${specJson}`],
@@ -719,11 +719,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
719
719
  server.registerTool('gog_sheets_chart_delete', {
720
720
  description: 'Delete a chart by its numeric chart ID.',
721
721
  annotations: { destructiveHint: true },
722
- inputSchema: {
722
+ inputSchema: z.object({
723
723
  spreadsheetId: z.string().describe('Spreadsheet ID'),
724
724
  chartId: z.string().describe('Numeric chart ID to delete'),
725
725
  account: accountParam,
726
- },
726
+ }),
727
727
  }, async ({ spreadsheetId, chartId, account }) => {
728
728
  return runOrDiagnose(['sheets', 'chart', 'delete', spreadsheetId, chartId, '--force'], { account }); // gog gates this op; without --force the runner's --no-input makes it refuse
729
729
  });
@@ -733,10 +733,10 @@ export function registerExtraSheetsTools(server: McpServer): void {
733
733
  server.registerTool('gog_sheets_table_list', {
734
734
  description: 'List Google Sheets tables in a spreadsheet (tableId, name, range).',
735
735
  annotations: { readOnlyHint: true },
736
- inputSchema: {
736
+ inputSchema: z.object({
737
737
  spreadsheetId: z.string().describe('Spreadsheet ID'),
738
738
  account: accountParam,
739
- },
739
+ }),
740
740
  }, async ({ spreadsheetId, account }) => {
741
741
  return runOrDiagnose(['sheets', 'table', 'list', spreadsheetId], { account });
742
742
  });
@@ -744,24 +744,24 @@ export function registerExtraSheetsTools(server: McpServer): void {
744
744
  server.registerTool('gog_sheets_table_get', {
745
745
  description: 'Get a single Google Sheets table (definition + columns) by its table ID.',
746
746
  annotations: { readOnlyHint: true },
747
- inputSchema: {
747
+ inputSchema: z.object({
748
748
  spreadsheetId: z.string().describe('Spreadsheet ID'),
749
749
  tableId: z.string().describe('Table ID (from gog_sheets_table_list)'),
750
750
  account: accountParam,
751
- },
751
+ }),
752
752
  }, async ({ spreadsheetId, tableId, account }) => {
753
753
  return runOrDiagnose(['sheets', 'table', 'get', spreadsheetId, tableId], { account });
754
754
  });
755
755
 
756
756
  server.registerTool('gog_sheets_table_create', {
757
757
  description: 'Create a Google Sheets table over a range. columnsJson is a JSON array of column definitions (each {columnName, columnType?}); valid columnType values: TEXT, DOUBLE, BOOLEAN, DATE, DROPDOWN. Inline JSON or @/path/to/file.json.',
758
- inputSchema: {
758
+ inputSchema: z.object({
759
759
  spreadsheetId: z.string().describe('Spreadsheet ID'),
760
760
  range: z.string().describe('Range the table covers (e.g. Sheet1!A1:D20)'),
761
761
  name: z.string().describe('Table name'),
762
762
  columnsJson: z.string().describe('Column definitions as JSON array or @file (columnName + optional columnType)'),
763
763
  account: accountParam,
764
- },
764
+ }),
765
765
  }, async ({ spreadsheetId, range, name, columnsJson, account }) => {
766
766
  return runOrDiagnose(
767
767
  ['sheets', 'table', 'create', spreadsheetId, range, `--name=${name}`, `--columns-json=${columnsJson}`],
@@ -771,13 +771,13 @@ export function registerExtraSheetsTools(server: McpServer): void {
771
771
 
772
772
  server.registerTool('gog_sheets_table_append', {
773
773
  description: 'Append data rows to a table. valuesJson is a JSON 2D array of row values.',
774
- inputSchema: {
774
+ inputSchema: z.object({
775
775
  spreadsheetId: z.string().describe('Spreadsheet ID'),
776
776
  tableId: z.string().describe('Table ID to append to'),
777
777
  valuesJson: z.string().describe('Values as JSON 2D array (e.g. [["a",1],["b",2]])'),
778
778
  input: z.enum(['RAW', 'USER_ENTERED']).optional().describe('Value input option (default: USER_ENTERED)'),
779
779
  account: accountParam,
780
- },
780
+ }),
781
781
  }, async ({ spreadsheetId, tableId, valuesJson, input, account }) => {
782
782
  const args = ['sheets', 'table', 'append', spreadsheetId, tableId, `--values-json=${valuesJson}`];
783
783
  if (input) args.push(`--input=${input}`);
@@ -787,11 +787,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
787
787
  server.registerTool('gog_sheets_table_clear', {
788
788
  description: 'Clear all data rows from a table (keeps the table and its columns).',
789
789
  annotations: { destructiveHint: true },
790
- inputSchema: {
790
+ inputSchema: z.object({
791
791
  spreadsheetId: z.string().describe('Spreadsheet ID'),
792
792
  tableId: z.string().describe('Table ID to clear'),
793
793
  account: accountParam,
794
- },
794
+ }),
795
795
  }, async ({ spreadsheetId, tableId, account }) => {
796
796
  return runOrDiagnose(['sheets', 'table', 'clear', spreadsheetId, tableId, '--force'], { account }); // gog gates this op; without --force the runner's --no-input makes it refuse
797
797
  });
@@ -803,14 +803,14 @@ export function registerExtraSheetsTools(server: McpServer): void {
803
803
  'Formatting and banding are still lost; data-validation dropdowns are also not restored automatically, but you can snapshot them first with gog_sheets_validation_get and re-apply with gog_sheets_validation_set. ' +
804
804
  'Set keep_data=false to delete the table AND wipe its cell data (the raw destructive behaviour). To preserve everything, snapshot the whole spreadsheet first with gog_sheets_snapshot.',
805
805
  annotations: { destructiveHint: true },
806
- inputSchema: {
806
+ inputSchema: z.object({
807
807
  spreadsheetId: z.string().describe('Spreadsheet ID'),
808
808
  tableId: z.string().describe('Table ID to delete'),
809
809
  keep_data: z.boolean().optional().describe(
810
810
  'Default true: preserve the table\'s cell values and formulas (read → delete → restore), emulating "Convert to range". Set false to delete the table AND its data.',
811
811
  ),
812
812
  account: accountParam,
813
- },
813
+ }),
814
814
  }, async ({ spreadsheetId, tableId, keep_data = true, account }) => {
815
815
  if (!keep_data) {
816
816
  // Raw destructive delete: removes the table and its cell data. gog ≥ 0.23
@@ -868,11 +868,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
868
868
  server.registerTool('gog_sheets_banding_list', {
869
869
  description: 'List alternating-color banded ranges. Optionally scope to a single sheet.',
870
870
  annotations: { readOnlyHint: true },
871
- inputSchema: {
871
+ inputSchema: z.object({
872
872
  spreadsheetId: z.string().describe('Spreadsheet ID'),
873
873
  sheet: z.string().optional().describe('Only list banding from this sheet'),
874
874
  account: accountParam,
875
- },
875
+ }),
876
876
  }, async ({ spreadsheetId, sheet, account }) => {
877
877
  const args = ['sheets', 'banding', 'list', spreadsheetId];
878
878
  if (sheet) args.push(`--sheet=${sheet}`);
@@ -881,13 +881,13 @@ export function registerExtraSheetsTools(server: McpServer): void {
881
881
 
882
882
  server.registerTool('gog_sheets_banding_set', {
883
883
  description: 'Apply alternating colors to a range. Provide rowPropertiesJson and/or columnPropertiesJson — each a Sheets API BandingProperties JSON object ({headerColor, firstBandColor, secondBandColor, footerColor}). At least one is required.',
884
- inputSchema: {
884
+ inputSchema: z.object({
885
885
  spreadsheetId: z.string().describe('Spreadsheet ID'),
886
886
  range: z.string().describe('Range to band (e.g. Sheet1!A1:D20)'),
887
887
  rowPropertiesJson: z.string().optional().describe('BandingProperties JSON for row colors'),
888
888
  columnPropertiesJson: z.string().optional().describe('BandingProperties JSON for column colors'),
889
889
  account: accountParam,
890
- },
890
+ }),
891
891
  }, async ({ spreadsheetId, range, rowPropertiesJson, columnPropertiesJson, account }) => {
892
892
  const args = ['sheets', 'banding', 'set', spreadsheetId, range];
893
893
  if (rowPropertiesJson) args.push(`--row-properties-json=${rowPropertiesJson}`);
@@ -898,13 +898,13 @@ export function registerExtraSheetsTools(server: McpServer): void {
898
898
  server.registerTool('gog_sheets_banding_clear', {
899
899
  description: 'Remove alternating-color banding. Pass id to remove a single banded range, or all:true with sheet to remove every banding on that sheet.',
900
900
  annotations: { destructiveHint: true },
901
- inputSchema: {
901
+ inputSchema: z.object({
902
902
  spreadsheetId: z.string().describe('Spreadsheet ID'),
903
903
  id: z.number().optional().describe('Banded range ID to remove'),
904
904
  all: z.boolean().optional().describe('Remove all banding from the sheet (requires sheet)'),
905
905
  sheet: z.string().optional().describe('Sheet name (used with all:true)'),
906
906
  account: accountParam,
907
- },
907
+ }),
908
908
  }, async ({ spreadsheetId, id, all, sheet, account }) => {
909
909
  const args = ['sheets', 'banding', 'clear', spreadsheetId];
910
910
  if (id !== undefined) args.push(`--id=${id}`);
@@ -918,12 +918,12 @@ export function registerExtraSheetsTools(server: McpServer): void {
918
918
 
919
919
  server.registerTool('gog_sheets_filter_set', {
920
920
  description: 'Set a basic filter on a range (the filter/sort header Sheets shows on a data range). A sheet can hold one basic filter; replacing an existing one requires replace=true — without it, gog refuses rather than silently overwriting.',
921
- inputSchema: {
921
+ inputSchema: z.object({
922
922
  spreadsheetId: z.string().describe('Spreadsheet ID'),
923
923
  range: z.string().describe('Range to filter (A1 notation with sheet name, e.g. Sheet1!A1:C100, or a named range name)'),
924
924
  replace: z.boolean().optional().describe('Replace the sheet\'s existing basic filter if one is set (appends --force)'),
925
925
  account: accountParam,
926
- },
926
+ }),
927
927
  }, async ({ spreadsheetId, range, replace, account }) => {
928
928
  const args = ['sheets', 'filter', 'set', spreadsheetId, range];
929
929
  if (replace) args.push('--force');
@@ -935,11 +935,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
935
935
  server.registerTool('gog_sheets_conditional_format_list', {
936
936
  description: 'List conditional formatting rules. Optionally scope to a single sheet.',
937
937
  annotations: { readOnlyHint: true },
938
- inputSchema: {
938
+ inputSchema: z.object({
939
939
  spreadsheetId: z.string().describe('Spreadsheet ID'),
940
940
  sheet: z.string().optional().describe('Only list rules from this sheet'),
941
941
  account: accountParam,
942
- },
942
+ }),
943
943
  }, async ({ spreadsheetId, sheet, account }) => {
944
944
  const args = ['sheets', 'conditional-format', 'list', spreadsheetId];
945
945
  if (sheet) args.push(`--sheet=${sheet}`);
@@ -948,7 +948,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
948
948
 
949
949
  server.registerTool('gog_sheets_conditional_format_add', {
950
950
  description: 'Add a conditional formatting rule to a range. Boolean rules: type picks the condition, expr is its value/formula (omit for blank/not-blank), formatJson is the CellFormat to apply when the condition matches (inline or @file); use formatFields to force-send zero/false fields (e.g. backgroundColor,textFormat.bold). Gradient rules (color scales): pass gradientRuleJson instead — a GradientRule JSON with minpoint/maxpoint (and optional midpoint), each {"color":{...},"type":"MIN|MAX|NUMBER|PERCENT|PERCENTILE","value":"..."}. The two modes are mutually exclusive.',
951
- inputSchema: {
951
+ inputSchema: z.object({
952
952
  spreadsheetId: z.string().describe('Spreadsheet ID'),
953
953
  range: z.string().describe('Range the rule applies to (e.g. Sheet1!A1:A100)'),
954
954
  type: z.enum([
@@ -961,7 +961,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
961
961
  formatFields: z.string().optional().describe('Format field mask for force-sending zero/false fields (e.g. backgroundColor,textFormat.bold)'),
962
962
  gradientRuleJson: z.string().optional().describe('GradientRule JSON for gradient conditional formats (inline or @file; must include minpoint and maxpoint)'),
963
963
  account: accountParam,
964
- },
964
+ }),
965
965
  }, async ({ spreadsheetId, range, type, formatJson, expr, formatFields, gradientRuleJson, account }) => {
966
966
  const args = ['sheets', 'conditional-format', 'add', spreadsheetId, range];
967
967
  if (type) args.push(`--type=${type}`);
@@ -975,13 +975,13 @@ export function registerExtraSheetsTools(server: McpServer): void {
975
975
  server.registerTool('gog_sheets_conditional_format_clear', {
976
976
  description: 'Remove conditional formatting rules from a sheet. Pass index to remove a single rule by its 0-based index, or all:true to remove every rule on the sheet.',
977
977
  annotations: { destructiveHint: true },
978
- inputSchema: {
978
+ inputSchema: z.object({
979
979
  spreadsheetId: z.string().describe('Spreadsheet ID'),
980
980
  sheet: z.string().describe('Sheet name to clear rules from'),
981
981
  index: z.number().optional().describe('0-based rule index to remove'),
982
982
  all: z.boolean().optional().describe('Remove all conditional formatting rules from the sheet'),
983
983
  account: accountParam,
984
- },
984
+ }),
985
985
  }, async ({ spreadsheetId, sheet, index, all, account }) => {
986
986
  const args = ['sheets', 'conditional-format', 'clear', spreadsheetId, `--sheet=${sheet}`];
987
987
  if (index !== undefined) args.push(`--index=${index}`);
@@ -994,12 +994,12 @@ export function registerExtraSheetsTools(server: McpServer): void {
994
994
  description:
995
995
  'Make a backup copy of an entire spreadsheet — a one-call safety snapshot to take BEFORE a risky or destructive edit (table delete, bulk clear, large rewrite). ' +
996
996
  'Returns the new copy\'s file ID and URL; if the edit goes wrong, restore by copying the backup back or sharing it. The copy is independent — later edits to the original do not affect it.',
997
- inputSchema: {
997
+ inputSchema: z.object({
998
998
  spreadsheetId: z.string().describe('Spreadsheet ID to back up'),
999
999
  name: z.string().describe('Name for the backup copy, e.g. "Budget — backup before table delete"'),
1000
1000
  parent: z.string().optional().describe('Destination folder ID for the copy (default: same location as the original)'),
1001
1001
  account: accountParam,
1002
- },
1002
+ }),
1003
1003
  }, async ({ spreadsheetId, name, parent, account }) => {
1004
1004
  const args = ['drive', 'copy', spreadsheetId, name];
1005
1005
  if (parent) args.push(`--parent=${parent}`);
@@ -1032,10 +1032,10 @@ export function registerExtraSheetsTools(server: McpServer): void {
1032
1032
  'and current execution status. This is the discovery call — start here to get the dataSourceId the describe tool wants. ' +
1033
1033
  'Deliberately does not print custom SQL; use gog_sheets_datasource_describe for that.' + bigQueryScopeNote,
1034
1034
  annotations: { readOnlyHint: true },
1035
- inputSchema: {
1035
+ inputSchema: z.object({
1036
1036
  spreadsheetId: z.string().describe('Spreadsheet ID'),
1037
1037
  account: accountParam,
1038
- },
1038
+ }),
1039
1039
  }, async ({ spreadsheetId, account }) => {
1040
1040
  return runOrDiagnose(['sheets', 'datasource', 'list', spreadsheetId], { account });
1041
1041
  });
@@ -1047,11 +1047,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
1047
1047
  'Refreshes are asynchronous and this tool cannot start one — poll it until state is SUCCEEDED or FAILED, and read the ' +
1048
1048
  'status error text when it is FAILED.' + bigQueryScopeNote,
1049
1049
  annotations: { readOnlyHint: true },
1050
- inputSchema: {
1050
+ inputSchema: z.object({
1051
1051
  spreadsheetId: z.string().describe('Spreadsheet ID'),
1052
1052
  dataSourceId: z.string().describe('Data source ID, as reported by gog_sheets_datasource_list'),
1053
1053
  account: accountParam,
1054
- },
1054
+ }),
1055
1055
  }, async ({ spreadsheetId, dataSourceId, account }) => {
1056
1056
  return runOrDiagnose(['sheets', 'datasource', 'describe', spreadsheetId, dataSourceId], { account });
1057
1057
  });
@@ -1063,11 +1063,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
1063
1063
  'sheet-qualified A1 anchor (e.g. "Extracts!B3"). That anchor is what the describe and read tools take. Filter with ' +
1064
1064
  'dataSourceId to see only one source\'s extracts.' + bigQueryScopeNote,
1065
1065
  annotations: { readOnlyHint: true },
1066
- inputSchema: {
1066
+ inputSchema: z.object({
1067
1067
  spreadsheetId: z.string().describe('Spreadsheet ID'),
1068
1068
  dataSourceId: z.string().optional().describe('Only list tables belonging to this data source ID'),
1069
1069
  account: accountParam,
1070
- },
1070
+ }),
1071
1071
  }, async ({ spreadsheetId, dataSourceId, account }) => {
1072
1072
  const args = ['sheets', 'datasource', 'table', 'list', spreadsheetId];
1073
1073
  if (dataSourceId) args.push(`--data-source-id=${dataSourceId}`);
@@ -1080,11 +1080,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
1080
1080
  'row limit. Read this before gog_sheets_datasource_table_read when you need to know what the columns MEAN — the read ' +
1081
1081
  'returns values, not the extract\'s definition.' + bigQueryScopeNote,
1082
1082
  annotations: { readOnlyHint: true },
1083
- inputSchema: {
1083
+ inputSchema: z.object({
1084
1084
  spreadsheetId: z.string().describe('Spreadsheet ID'),
1085
1085
  anchor: z.string().describe('Sheet-qualified A1 anchor of the extract\'s top-left cell, e.g. "Extracts!B3". Get it from gog_sheets_datasource_table_list — an extract has no other identifier.'),
1086
1086
  account: accountParam,
1087
- },
1087
+ }),
1088
1088
  }, async ({ spreadsheetId, anchor, account }) => {
1089
1089
  return runOrDiagnose(['sheets', 'datasource', 'table', 'describe', spreadsheetId, anchor], { account });
1090
1090
  });
@@ -1097,13 +1097,13 @@ export function registerExtraSheetsTools(server: McpServer): void {
1097
1097
  'conclude a value is absent from it; raise maxRows or narrow the extract instead. Use render=UNFORMATTED_VALUE for ' +
1098
1098
  'arithmetic (FORMATTED_VALUE returns display strings) or render=FORMULA to see the cell formulas.' + bigQueryScopeNote,
1099
1099
  annotations: { readOnlyHint: true },
1100
- inputSchema: {
1100
+ inputSchema: z.object({
1101
1101
  spreadsheetId: z.string().describe('Spreadsheet ID'),
1102
1102
  anchor: z.string().describe('Sheet-qualified A1 anchor of the extract\'s top-left cell, e.g. "Extracts!B3" (from gog_sheets_datasource_table_list)'),
1103
1103
  maxRows: z.number().int().positive().optional().describe('Maximum data rows to read (gog default: 1000). The header row is returned separately and does not count against this.'),
1104
1104
  render: z.enum(['FORMATTED_VALUE', 'UNFORMATTED_VALUE', 'FORMULA']).optional().describe('How cell values are rendered (gog default: FORMATTED_VALUE)'),
1105
1105
  account: accountParam,
1106
- },
1106
+ }),
1107
1107
  }, async ({ spreadsheetId, anchor, maxRows, render, account }) => {
1108
1108
  const args = ['sheets', 'datasource', 'table', 'read', spreadsheetId, anchor];
1109
1109
  if (maxRows !== undefined) args.push(`--max-rows=${maxRows}`);
@@ -1138,7 +1138,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
1138
1138
  'BigQuery project charged for the query; tableProject only names which project OWNS the table, and defaults to the ' +
1139
1139
  'billing project. Returns the new dataSourceId, which every other datasource tool takes.' +
1140
1140
  bigQueryChargeNote + bigQueryScopeNote,
1141
- inputSchema: {
1141
+ inputSchema: z.object({
1142
1142
  spreadsheetId: z.string().describe('Spreadsheet ID'),
1143
1143
  billingProject: z.string().describe('Billing-enabled BigQuery project charged for this source\'s queries'),
1144
1144
  query: z.string().optional().describe('BigQuery SQL to run. Mutually exclusive with dataset/table/tableProject.'),
@@ -1146,7 +1146,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
1146
1146
  table: z.string().optional().describe('BigQuery table ID. Requires dataset; mutually exclusive with query.'),
1147
1147
  tableProject: z.string().optional().describe('BigQuery project that owns the table (default: the billing project). Only valid with dataset/table.'),
1148
1148
  account: accountParam,
1149
- },
1149
+ }),
1150
1150
  }, async ({ spreadsheetId, billingProject, query, dataset, table, tableProject, account }) => {
1151
1151
  const tableFlags = tableProject !== undefined || dataset !== undefined || table !== undefined;
1152
1152
  if (query !== undefined && tableFlags) {
@@ -1171,7 +1171,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
1171
1171
  'gog_sheets_datasource_describe first if you are unsure which kind it is. Replacing the SQL DISCARDS the source\'s ' +
1172
1172
  'current results.' + bigQueryChargeNote + bigQueryScopeNote,
1173
1173
  annotations: { destructiveHint: true },
1174
- inputSchema: {
1174
+ inputSchema: z.object({
1175
1175
  spreadsheetId: z.string().describe('Spreadsheet ID'),
1176
1176
  dataSourceId: z.string().describe('Data source ID, as reported by gog_sheets_datasource_list'),
1177
1177
  billingProject: z.string().optional().describe('New billing-enabled BigQuery project to charge executions to'),
@@ -1180,7 +1180,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
1180
1180
  table: z.string().optional().describe('Replacement table ID. Only valid on a table-backed source.'),
1181
1181
  tableProject: z.string().optional().describe('Replacement project owning the table. Only valid on a table-backed source.'),
1182
1182
  account: accountParam,
1183
- },
1183
+ }),
1184
1184
  }, async ({ spreadsheetId, dataSourceId, billingProject, query, dataset, table, tableProject, account }) => {
1185
1185
  const tableFlags = tableProject !== undefined || dataset !== undefined || table !== undefined;
1186
1186
  if (query !== undefined && tableFlags) {
@@ -1218,12 +1218,12 @@ export function registerExtraSheetsTools(server: McpServer): void {
1218
1218
  'Re-run a Connected Sheets data source so its sheet and extracts pick up current BigQuery data. Google refuses to ' +
1219
1219
  'refresh a source whose previous execution FAILED; forceRefresh overrides that, which is what you want after fixing ' +
1220
1220
  'the underlying query or permissions.' + bigQueryChargeNote + bigQueryScopeNote,
1221
- inputSchema: {
1221
+ inputSchema: z.object({
1222
1222
  spreadsheetId: z.string().describe('Spreadsheet ID'),
1223
1223
  dataSourceId: z.string().describe('Data source ID, as reported by gog_sheets_datasource_list'),
1224
1224
  forceRefresh: z.boolean().optional().describe('Refresh even when the previous execution failed'),
1225
1225
  account: accountParam,
1226
- },
1226
+ }),
1227
1227
  }, async ({ spreadsheetId, dataSourceId, forceRefresh, account }) => {
1228
1228
  const args = ['sheets', 'datasource', 'refresh', spreadsheetId, dataSourceId];
1229
1229
  if (forceRefresh) args.push('--force-refresh');
@@ -1237,11 +1237,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
1237
1237
  'objects on other tabs that the caller never named. Take gog_sheets_snapshot first if any of that matters. The ' +
1238
1238
  'BigQuery data itself is untouched; only the spreadsheet\'s connection to it goes away.' + bigQueryScopeNote,
1239
1239
  annotations: { destructiveHint: true },
1240
- inputSchema: {
1240
+ inputSchema: z.object({
1241
1241
  spreadsheetId: z.string().describe('Spreadsheet ID'),
1242
1242
  dataSourceId: z.string().describe('Data source ID, as reported by gog_sheets_datasource_list'),
1243
1243
  account: accountParam,
1244
- },
1244
+ }),
1245
1245
  }, async ({ spreadsheetId, dataSourceId, account }) => {
1246
1246
  // gog gates this delete behind a confirmation; the runner injects
1247
1247
  // --no-input, so without --force it refuses at runtime. Verified live