gogcli-mcp-sheets 2.0.12 → 2.3.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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +81 -0
- package/dist/index.js +405 -36
- package/manifest.json +70 -2
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/tools/sheets-extra.ts +328 -7
- package/tests/tools/sheets-extra.test.ts +547 -3
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"source": "github",
|
|
8
8
|
"subfolder": "packages/gogcli-mcp-sheets"
|
|
9
9
|
},
|
|
10
|
-
"version": "2.0
|
|
10
|
+
"version": "2.3.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "gogcli-mcp-sheets",
|
|
15
|
-
"version": "2.0
|
|
15
|
+
"version": "2.3.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
|
@@ -18,6 +18,55 @@ export function hexToRgb(hex: string): { red: number; green: number; blue: numbe
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// Issue #43: peek the target range before applying DATE / DATE_TIME number
|
|
22
|
+
// formatting. If every numeric cell is a small integer (< 10000), Sheets will
|
|
23
|
+
// render them as day-serials near 1899-12-30 — almost certainly not what the
|
|
24
|
+
// caller intended. Return a warning string in that case, otherwise null.
|
|
25
|
+
//
|
|
26
|
+
// Returns null (no warning) when:
|
|
27
|
+
// - the peek fails / response isn't parseable JSON
|
|
28
|
+
// - the range is empty
|
|
29
|
+
// - any value is a non-integer number or a number >= 10000
|
|
30
|
+
// - any value is a string (other than empty), boolean, etc.
|
|
31
|
+
// Nulls and empty strings are ignored.
|
|
32
|
+
async function checkDateFormatTarget(
|
|
33
|
+
spreadsheetId: string,
|
|
34
|
+
range: string,
|
|
35
|
+
account: string | undefined,
|
|
36
|
+
): Promise<string | null> {
|
|
37
|
+
const peek = await runOrDiagnose(
|
|
38
|
+
['sheets', 'get', spreadsheetId, range, '--render=UNFORMATTED_VALUE'],
|
|
39
|
+
{ account },
|
|
40
|
+
);
|
|
41
|
+
let parsed: { values?: unknown[][] };
|
|
42
|
+
try {
|
|
43
|
+
parsed = JSON.parse(peek.content[0].text) as { values?: unknown[][] };
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const rows = parsed.values;
|
|
48
|
+
if (!Array.isArray(rows) || rows.length === 0) return null;
|
|
49
|
+
let sawSmallInt = false;
|
|
50
|
+
for (const row of rows) {
|
|
51
|
+
for (const cell of row) {
|
|
52
|
+
if (cell === null || cell === undefined || cell === '') continue;
|
|
53
|
+
if (typeof cell !== 'number') return null;
|
|
54
|
+
if (!Number.isInteger(cell)) return null;
|
|
55
|
+
if (cell < 0) return null;
|
|
56
|
+
if (cell >= 10000) return null;
|
|
57
|
+
sawSmallInt = true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (!sawSmallInt) return null;
|
|
61
|
+
return (
|
|
62
|
+
'Warning: applying DATE/DATE_TIME format to cells holding small integers (< 10000) ' +
|
|
63
|
+
'will render them as dates near 1899-12-30 because Sheets interprets numeric values as ' +
|
|
64
|
+
'day-serials from that epoch. If those integers are ordinals (1, 2, 3, ...) and not ' +
|
|
65
|
+
'day offsets, this is almost certainly not what you want. Pass force:true to suppress ' +
|
|
66
|
+
'this warning, or convert the cells to real dates / strings first.'
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
21
70
|
export function registerExtraSheetsTools(server: McpServer): void {
|
|
22
71
|
|
|
23
72
|
server.registerTool('gog_sheets_list_tabs', {
|
|
@@ -120,7 +169,7 @@ export function registerExtraSheetsTools(server: McpServer): void {
|
|
|
120
169
|
});
|
|
121
170
|
|
|
122
171
|
server.registerTool('gog_sheets_insert', {
|
|
123
|
-
description: 'Insert rows or columns into a sheet.',
|
|
172
|
+
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).',
|
|
124
173
|
annotations: { destructiveHint: true },
|
|
125
174
|
inputSchema: {
|
|
126
175
|
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
@@ -128,11 +177,16 @@ export function registerExtraSheetsTools(server: McpServer): void {
|
|
|
128
177
|
dimension: z.string().describe('Dimension to insert: ROWS or COLUMNS'),
|
|
129
178
|
start: z.number().describe('Start index (0-based)'),
|
|
130
179
|
count: z.number().optional().describe('Number of rows/columns to insert (default: 1)'),
|
|
131
|
-
after: z.boolean().optional().describe('Insert after the start index instead of before'),
|
|
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.'),
|
|
132
181
|
account: accountParam,
|
|
133
182
|
},
|
|
134
183
|
}, async ({ spreadsheetId, sheet, dimension, start, count, after, account }) => {
|
|
135
|
-
|
|
184
|
+
// The underlying `gog sheets insert` treats `start` as 1-based with `--after` shifting +1.
|
|
185
|
+
// To honor our 0-based `start` and make `after:true` actually shift the insertion to start+1
|
|
186
|
+
// (issue #42), we send `start+1` when `after:true`. With after:false the CLI's `start-1`
|
|
187
|
+
// conversion lands the insertion at our 0-based start.
|
|
188
|
+
const effectiveStart = after ? start + 1 : start;
|
|
189
|
+
const args = ['sheets', 'insert', spreadsheetId, sheet, dimension, String(effectiveStart)];
|
|
136
190
|
if (count !== undefined) args.push(`--count=${count}`);
|
|
137
191
|
if (after) args.push('--after');
|
|
138
192
|
return runOrDiagnose(args, { account });
|
|
@@ -255,20 +309,29 @@ export function registerExtraSheetsTools(server: McpServer): void {
|
|
|
255
309
|
});
|
|
256
310
|
|
|
257
311
|
server.registerTool('gog_sheets_number_format', {
|
|
258
|
-
description: 'Set number format on a range (currency, percentage, date, etc.).',
|
|
312
|
+
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.',
|
|
259
313
|
annotations: { destructiveHint: true },
|
|
260
314
|
inputSchema: {
|
|
261
315
|
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
262
316
|
range: z.string().describe('Range to format (e.g. Sheet1!A1:A10)'),
|
|
263
|
-
type: z.string().optional().describe('Format type: NUMBER, CURRENCY, PERCENT, DATE, TIME, SCIENTIFIC, etc.'),
|
|
317
|
+
type: z.string().optional().describe('Format type: NUMBER, CURRENCY, PERCENT, DATE, DATE_TIME, TIME, SCIENTIFIC, etc.'),
|
|
264
318
|
pattern: z.string().optional().describe('Custom format pattern (e.g. "#,##0.00", "yyyy-mm-dd")'),
|
|
319
|
+
force: z.boolean().optional().describe('Skip the DATE/DATE_TIME small-integer warning check'),
|
|
265
320
|
account: accountParam,
|
|
266
321
|
},
|
|
267
|
-
}, async ({ spreadsheetId, range, type, pattern, account }) => {
|
|
322
|
+
}, async ({ spreadsheetId, range, type, pattern, force, account }) => {
|
|
323
|
+
const isDateType = type === 'DATE' || type === 'DATE_TIME';
|
|
324
|
+
const warning = isDateType && !force
|
|
325
|
+
? await checkDateFormatTarget(spreadsheetId, range, account)
|
|
326
|
+
: null;
|
|
268
327
|
const args = ['sheets', 'number-format', spreadsheetId, range];
|
|
269
328
|
if (type) args.push(`--type=${type}`);
|
|
270
329
|
if (pattern) args.push(`--pattern=${pattern}`);
|
|
271
|
-
|
|
330
|
+
const result = await runOrDiagnose(args, { account });
|
|
331
|
+
if (warning) {
|
|
332
|
+
return { content: [{ type: 'text' as const, text: `${warning}\n\n${result.content[0].text}` }] };
|
|
333
|
+
}
|
|
334
|
+
return result;
|
|
272
335
|
});
|
|
273
336
|
|
|
274
337
|
server.registerTool('gog_sheets_read_format', {
|
|
@@ -457,4 +520,262 @@ export function registerExtraSheetsTools(server: McpServer): void {
|
|
|
457
520
|
{ account },
|
|
458
521
|
);
|
|
459
522
|
});
|
|
523
|
+
|
|
524
|
+
// ---- Charts (gog 0.19.0) ----
|
|
525
|
+
|
|
526
|
+
server.registerTool('gog_sheets_chart_list', {
|
|
527
|
+
description: 'List embedded charts in a spreadsheet (chartId, type, position).',
|
|
528
|
+
annotations: { readOnlyHint: true },
|
|
529
|
+
inputSchema: {
|
|
530
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
531
|
+
account: accountParam,
|
|
532
|
+
},
|
|
533
|
+
}, async ({ spreadsheetId, account }) => {
|
|
534
|
+
return runOrDiagnose(['sheets', 'chart', 'list', spreadsheetId], { account });
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
server.registerTool('gog_sheets_chart_get', {
|
|
538
|
+
description: 'Get the full definition (spec + position) of a single chart by its numeric chart ID.',
|
|
539
|
+
annotations: { readOnlyHint: true },
|
|
540
|
+
inputSchema: {
|
|
541
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
542
|
+
chartId: z.string().describe('Numeric chart ID (from gog_sheets_chart_list)'),
|
|
543
|
+
account: accountParam,
|
|
544
|
+
},
|
|
545
|
+
}, async ({ spreadsheetId, chartId, account }) => {
|
|
546
|
+
return runOrDiagnose(['sheets', 'chart', 'get', spreadsheetId, chartId], { account });
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
server.registerTool('gog_sheets_chart_create', {
|
|
550
|
+
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.',
|
|
551
|
+
inputSchema: {
|
|
552
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
553
|
+
specJson: z.string().describe('ChartSpec or EmbeddedChart JSON (inline or @file)'),
|
|
554
|
+
sheet: z.string().optional().describe('Sheet name for the anchor (resolved to sheetId)'),
|
|
555
|
+
anchor: z.string().optional().describe('Anchor cell in A1 notation (e.g. A1, E10)'),
|
|
556
|
+
width: z.number().optional().describe('Chart width in pixels (default: 600)'),
|
|
557
|
+
height: z.number().optional().describe('Chart height in pixels (default: 371)'),
|
|
558
|
+
account: accountParam,
|
|
559
|
+
},
|
|
560
|
+
}, async ({ spreadsheetId, specJson, sheet, anchor, width, height, account }) => {
|
|
561
|
+
const args = ['sheets', 'chart', 'create', spreadsheetId, `--spec-json=${specJson}`];
|
|
562
|
+
if (sheet) args.push(`--sheet=${sheet}`);
|
|
563
|
+
if (anchor) args.push(`--anchor=${anchor}`);
|
|
564
|
+
if (width !== undefined) args.push(`--width=${width}`);
|
|
565
|
+
if (height !== undefined) args.push(`--height=${height}`);
|
|
566
|
+
return runOrDiagnose(args, { account });
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
server.registerTool('gog_sheets_chart_update', {
|
|
570
|
+
description: 'Replace a chart spec by chart ID. specJson is a Sheets API ChartSpec (or full EmbeddedChart) — inline or @/path/to/file.json.',
|
|
571
|
+
annotations: { destructiveHint: true },
|
|
572
|
+
inputSchema: {
|
|
573
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
574
|
+
chartId: z.string().describe('Numeric chart ID to update'),
|
|
575
|
+
specJson: z.string().describe('ChartSpec or EmbeddedChart JSON (inline or @file)'),
|
|
576
|
+
account: accountParam,
|
|
577
|
+
},
|
|
578
|
+
}, async ({ spreadsheetId, chartId, specJson, account }) => {
|
|
579
|
+
return runOrDiagnose(
|
|
580
|
+
['sheets', 'chart', 'update', spreadsheetId, chartId, `--spec-json=${specJson}`],
|
|
581
|
+
{ account },
|
|
582
|
+
);
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
server.registerTool('gog_sheets_chart_delete', {
|
|
586
|
+
description: 'Delete a chart by its numeric chart ID.',
|
|
587
|
+
annotations: { destructiveHint: true },
|
|
588
|
+
inputSchema: {
|
|
589
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
590
|
+
chartId: z.string().describe('Numeric chart ID to delete'),
|
|
591
|
+
account: accountParam,
|
|
592
|
+
},
|
|
593
|
+
}, async ({ spreadsheetId, chartId, account }) => {
|
|
594
|
+
return runOrDiagnose(['sheets', 'chart', 'delete', spreadsheetId, chartId], { account });
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
// ---- Tables (gog 0.19.0) ----
|
|
598
|
+
|
|
599
|
+
server.registerTool('gog_sheets_table_list', {
|
|
600
|
+
description: 'List Google Sheets tables in a spreadsheet (tableId, name, range).',
|
|
601
|
+
annotations: { readOnlyHint: true },
|
|
602
|
+
inputSchema: {
|
|
603
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
604
|
+
account: accountParam,
|
|
605
|
+
},
|
|
606
|
+
}, async ({ spreadsheetId, account }) => {
|
|
607
|
+
return runOrDiagnose(['sheets', 'table', 'list', spreadsheetId], { account });
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
server.registerTool('gog_sheets_table_get', {
|
|
611
|
+
description: 'Get a single Google Sheets table (definition + columns) by its table ID.',
|
|
612
|
+
annotations: { readOnlyHint: true },
|
|
613
|
+
inputSchema: {
|
|
614
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
615
|
+
tableId: z.string().describe('Table ID (from gog_sheets_table_list)'),
|
|
616
|
+
account: accountParam,
|
|
617
|
+
},
|
|
618
|
+
}, async ({ spreadsheetId, tableId, account }) => {
|
|
619
|
+
return runOrDiagnose(['sheets', 'table', 'get', spreadsheetId, tableId], { account });
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
server.registerTool('gog_sheets_table_create', {
|
|
623
|
+
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.',
|
|
624
|
+
inputSchema: {
|
|
625
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
626
|
+
range: z.string().describe('Range the table covers (e.g. Sheet1!A1:D20)'),
|
|
627
|
+
name: z.string().describe('Table name'),
|
|
628
|
+
columnsJson: z.string().describe('Column definitions as JSON array or @file (columnName + optional columnType)'),
|
|
629
|
+
account: accountParam,
|
|
630
|
+
},
|
|
631
|
+
}, async ({ spreadsheetId, range, name, columnsJson, account }) => {
|
|
632
|
+
return runOrDiagnose(
|
|
633
|
+
['sheets', 'table', 'create', spreadsheetId, range, `--name=${name}`, `--columns-json=${columnsJson}`],
|
|
634
|
+
{ account },
|
|
635
|
+
);
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
server.registerTool('gog_sheets_table_append', {
|
|
639
|
+
description: 'Append data rows to a table. valuesJson is a JSON 2D array of row values.',
|
|
640
|
+
inputSchema: {
|
|
641
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
642
|
+
tableId: z.string().describe('Table ID to append to'),
|
|
643
|
+
valuesJson: z.string().describe('Values as JSON 2D array (e.g. [["a",1],["b",2]])'),
|
|
644
|
+
input: z.enum(['RAW', 'USER_ENTERED']).optional().describe('Value input option (default: USER_ENTERED)'),
|
|
645
|
+
account: accountParam,
|
|
646
|
+
},
|
|
647
|
+
}, async ({ spreadsheetId, tableId, valuesJson, input, account }) => {
|
|
648
|
+
const args = ['sheets', 'table', 'append', spreadsheetId, tableId, `--values-json=${valuesJson}`];
|
|
649
|
+
if (input) args.push(`--input=${input}`);
|
|
650
|
+
return runOrDiagnose(args, { account });
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
server.registerTool('gog_sheets_table_clear', {
|
|
654
|
+
description: 'Clear all data rows from a table (keeps the table and its columns).',
|
|
655
|
+
annotations: { destructiveHint: true },
|
|
656
|
+
inputSchema: {
|
|
657
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
658
|
+
tableId: z.string().describe('Table ID to clear'),
|
|
659
|
+
account: accountParam,
|
|
660
|
+
},
|
|
661
|
+
}, async ({ spreadsheetId, tableId, account }) => {
|
|
662
|
+
return runOrDiagnose(['sheets', 'table', 'clear', spreadsheetId, tableId], { account });
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
server.registerTool('gog_sheets_table_delete', {
|
|
666
|
+
description: 'Delete a table by its table ID.',
|
|
667
|
+
annotations: { destructiveHint: true },
|
|
668
|
+
inputSchema: {
|
|
669
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
670
|
+
tableId: z.string().describe('Table ID to delete'),
|
|
671
|
+
account: accountParam,
|
|
672
|
+
},
|
|
673
|
+
}, async ({ spreadsheetId, tableId, account }) => {
|
|
674
|
+
return runOrDiagnose(['sheets', 'table', 'delete', spreadsheetId, tableId], { account });
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
// ---- Banding / alternating colors (gog 0.19.0) ----
|
|
678
|
+
|
|
679
|
+
server.registerTool('gog_sheets_banding_list', {
|
|
680
|
+
description: 'List alternating-color banded ranges. Optionally scope to a single sheet.',
|
|
681
|
+
annotations: { readOnlyHint: true },
|
|
682
|
+
inputSchema: {
|
|
683
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
684
|
+
sheet: z.string().optional().describe('Only list banding from this sheet'),
|
|
685
|
+
account: accountParam,
|
|
686
|
+
},
|
|
687
|
+
}, async ({ spreadsheetId, sheet, account }) => {
|
|
688
|
+
const args = ['sheets', 'banding', 'list', spreadsheetId];
|
|
689
|
+
if (sheet) args.push(`--sheet=${sheet}`);
|
|
690
|
+
return runOrDiagnose(args, { account });
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
server.registerTool('gog_sheets_banding_set', {
|
|
694
|
+
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.',
|
|
695
|
+
inputSchema: {
|
|
696
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
697
|
+
range: z.string().describe('Range to band (e.g. Sheet1!A1:D20)'),
|
|
698
|
+
rowPropertiesJson: z.string().optional().describe('BandingProperties JSON for row colors'),
|
|
699
|
+
columnPropertiesJson: z.string().optional().describe('BandingProperties JSON for column colors'),
|
|
700
|
+
account: accountParam,
|
|
701
|
+
},
|
|
702
|
+
}, async ({ spreadsheetId, range, rowPropertiesJson, columnPropertiesJson, account }) => {
|
|
703
|
+
const args = ['sheets', 'banding', 'set', spreadsheetId, range];
|
|
704
|
+
if (rowPropertiesJson) args.push(`--row-properties-json=${rowPropertiesJson}`);
|
|
705
|
+
if (columnPropertiesJson) args.push(`--column-properties-json=${columnPropertiesJson}`);
|
|
706
|
+
return runOrDiagnose(args, { account });
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
server.registerTool('gog_sheets_banding_clear', {
|
|
710
|
+
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.',
|
|
711
|
+
annotations: { destructiveHint: true },
|
|
712
|
+
inputSchema: {
|
|
713
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
714
|
+
id: z.number().optional().describe('Banded range ID to remove'),
|
|
715
|
+
all: z.boolean().optional().describe('Remove all banding from the sheet (requires sheet)'),
|
|
716
|
+
sheet: z.string().optional().describe('Sheet name (used with all:true)'),
|
|
717
|
+
account: accountParam,
|
|
718
|
+
},
|
|
719
|
+
}, async ({ spreadsheetId, id, all, sheet, account }) => {
|
|
720
|
+
const args = ['sheets', 'banding', 'clear', spreadsheetId];
|
|
721
|
+
if (id !== undefined) args.push(`--id=${id}`);
|
|
722
|
+
if (all) args.push('--all');
|
|
723
|
+
if (sheet) args.push(`--sheet=${sheet}`);
|
|
724
|
+
return runOrDiagnose(args, { account });
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
// ---- Conditional formatting (gog 0.19.0) ----
|
|
728
|
+
|
|
729
|
+
server.registerTool('gog_sheets_conditional_format_list', {
|
|
730
|
+
description: 'List conditional formatting rules. Optionally scope to a single sheet.',
|
|
731
|
+
annotations: { readOnlyHint: true },
|
|
732
|
+
inputSchema: {
|
|
733
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
734
|
+
sheet: z.string().optional().describe('Only list rules from this sheet'),
|
|
735
|
+
account: accountParam,
|
|
736
|
+
},
|
|
737
|
+
}, async ({ spreadsheetId, sheet, account }) => {
|
|
738
|
+
const args = ['sheets', 'conditional-format', 'list', spreadsheetId];
|
|
739
|
+
if (sheet) args.push(`--sheet=${sheet}`);
|
|
740
|
+
return runOrDiagnose(args, { account });
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
server.registerTool('gog_sheets_conditional_format_add', {
|
|
744
|
+
description: 'Add a conditional formatting rule to a range. 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).',
|
|
745
|
+
inputSchema: {
|
|
746
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
747
|
+
range: z.string().describe('Range the rule applies to (e.g. Sheet1!A1:A100)'),
|
|
748
|
+
type: z.enum([
|
|
749
|
+
'text-eq', 'text-contains', 'text-starts-with', 'text-ends-with',
|
|
750
|
+
'number-eq', 'number-gt', 'number-gte', 'number-lt', 'number-lte',
|
|
751
|
+
'blank', 'not-blank', 'custom-formula',
|
|
752
|
+
]).describe('Rule type'),
|
|
753
|
+
formatJson: z.string().describe('CellFormat JSON to apply when the condition matches (inline or @file)'),
|
|
754
|
+
expr: z.string().optional().describe('Expression value or custom formula (omit for blank/not-blank)'),
|
|
755
|
+
formatFields: z.string().optional().describe('Format field mask for force-sending zero/false fields (e.g. backgroundColor,textFormat.bold)'),
|
|
756
|
+
account: accountParam,
|
|
757
|
+
},
|
|
758
|
+
}, async ({ spreadsheetId, range, type, formatJson, expr, formatFields, account }) => {
|
|
759
|
+
const args = ['sheets', 'conditional-format', 'add', spreadsheetId, range, `--type=${type}`, `--format-json=${formatJson}`];
|
|
760
|
+
if (expr !== undefined) args.push(`--expr=${expr}`);
|
|
761
|
+
if (formatFields) args.push(`--format-fields=${formatFields}`);
|
|
762
|
+
return runOrDiagnose(args, { account });
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
server.registerTool('gog_sheets_conditional_format_clear', {
|
|
766
|
+
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.',
|
|
767
|
+
annotations: { destructiveHint: true },
|
|
768
|
+
inputSchema: {
|
|
769
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
770
|
+
sheet: z.string().describe('Sheet name to clear rules from'),
|
|
771
|
+
index: z.number().optional().describe('0-based rule index to remove'),
|
|
772
|
+
all: z.boolean().optional().describe('Remove all conditional formatting rules from the sheet'),
|
|
773
|
+
account: accountParam,
|
|
774
|
+
},
|
|
775
|
+
}, async ({ spreadsheetId, sheet, index, all, account }) => {
|
|
776
|
+
const args = ['sheets', 'conditional-format', 'clear', spreadsheetId, `--sheet=${sheet}`];
|
|
777
|
+
if (index !== undefined) args.push(`--index=${index}`);
|
|
778
|
+
if (all) args.push('--all');
|
|
779
|
+
return runOrDiagnose(args, { account });
|
|
780
|
+
});
|
|
460
781
|
}
|