gogcli-mcp-sheets 2.2.0 → 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/dist/index.js +243 -1
- package/manifest.json +69 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/tools/sheets-extra.ts +258 -0
- package/tests/tools/sheets-extra.test.ts +334 -0
|
@@ -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.
|
|
10
|
+
"version": "2.3.0"
|
|
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.
|
|
18
|
+
"version": "2.3.0",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Hall"
|
|
21
21
|
},
|
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.
|
|
31402
|
+
var VERSION = true ? "2.3.0" : "0.0.0";
|
|
31403
31403
|
function createServer(options) {
|
|
31404
31404
|
return new McpServer({
|
|
31405
31405
|
name: options?.name ?? "gogcli",
|
|
@@ -31869,6 +31869,248 @@ ${result.content[0].text}` }] };
|
|
|
31869
31869
|
{ account }
|
|
31870
31870
|
);
|
|
31871
31871
|
});
|
|
31872
|
+
server2.registerTool("gog_sheets_chart_list", {
|
|
31873
|
+
description: "List embedded charts in a spreadsheet (chartId, type, position).",
|
|
31874
|
+
annotations: { readOnlyHint: true },
|
|
31875
|
+
inputSchema: {
|
|
31876
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31877
|
+
account: accountParam
|
|
31878
|
+
}
|
|
31879
|
+
}, async ({ spreadsheetId, account }) => {
|
|
31880
|
+
return runOrDiagnose(["sheets", "chart", "list", spreadsheetId], { account });
|
|
31881
|
+
});
|
|
31882
|
+
server2.registerTool("gog_sheets_chart_get", {
|
|
31883
|
+
description: "Get the full definition (spec + position) of a single chart by its numeric chart ID.",
|
|
31884
|
+
annotations: { readOnlyHint: true },
|
|
31885
|
+
inputSchema: {
|
|
31886
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31887
|
+
chartId: external_exports.string().describe("Numeric chart ID (from gog_sheets_chart_list)"),
|
|
31888
|
+
account: accountParam
|
|
31889
|
+
}
|
|
31890
|
+
}, async ({ spreadsheetId, chartId, account }) => {
|
|
31891
|
+
return runOrDiagnose(["sheets", "chart", "get", spreadsheetId, chartId], { account });
|
|
31892
|
+
});
|
|
31893
|
+
server2.registerTool("gog_sheets_chart_create", {
|
|
31894
|
+
description: "Create an embedded chart from a JSON spec. specJson is a Sheets API ChartSpec (or full EmbeddedChart) \u2014 inline or @/path/to/file.json. Anchor the chart with sheet + anchor (A1 cell), and optionally size it with width/height pixels.",
|
|
31895
|
+
inputSchema: {
|
|
31896
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31897
|
+
specJson: external_exports.string().describe("ChartSpec or EmbeddedChart JSON (inline or @file)"),
|
|
31898
|
+
sheet: external_exports.string().optional().describe("Sheet name for the anchor (resolved to sheetId)"),
|
|
31899
|
+
anchor: external_exports.string().optional().describe("Anchor cell in A1 notation (e.g. A1, E10)"),
|
|
31900
|
+
width: external_exports.number().optional().describe("Chart width in pixels (default: 600)"),
|
|
31901
|
+
height: external_exports.number().optional().describe("Chart height in pixels (default: 371)"),
|
|
31902
|
+
account: accountParam
|
|
31903
|
+
}
|
|
31904
|
+
}, async ({ spreadsheetId, specJson, sheet, anchor, width, height, account }) => {
|
|
31905
|
+
const args = ["sheets", "chart", "create", spreadsheetId, `--spec-json=${specJson}`];
|
|
31906
|
+
if (sheet) args.push(`--sheet=${sheet}`);
|
|
31907
|
+
if (anchor) args.push(`--anchor=${anchor}`);
|
|
31908
|
+
if (width !== void 0) args.push(`--width=${width}`);
|
|
31909
|
+
if (height !== void 0) args.push(`--height=${height}`);
|
|
31910
|
+
return runOrDiagnose(args, { account });
|
|
31911
|
+
});
|
|
31912
|
+
server2.registerTool("gog_sheets_chart_update", {
|
|
31913
|
+
description: "Replace a chart spec by chart ID. specJson is a Sheets API ChartSpec (or full EmbeddedChart) \u2014 inline or @/path/to/file.json.",
|
|
31914
|
+
annotations: { destructiveHint: true },
|
|
31915
|
+
inputSchema: {
|
|
31916
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31917
|
+
chartId: external_exports.string().describe("Numeric chart ID to update"),
|
|
31918
|
+
specJson: external_exports.string().describe("ChartSpec or EmbeddedChart JSON (inline or @file)"),
|
|
31919
|
+
account: accountParam
|
|
31920
|
+
}
|
|
31921
|
+
}, async ({ spreadsheetId, chartId, specJson, account }) => {
|
|
31922
|
+
return runOrDiagnose(
|
|
31923
|
+
["sheets", "chart", "update", spreadsheetId, chartId, `--spec-json=${specJson}`],
|
|
31924
|
+
{ account }
|
|
31925
|
+
);
|
|
31926
|
+
});
|
|
31927
|
+
server2.registerTool("gog_sheets_chart_delete", {
|
|
31928
|
+
description: "Delete a chart by its numeric chart ID.",
|
|
31929
|
+
annotations: { destructiveHint: true },
|
|
31930
|
+
inputSchema: {
|
|
31931
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31932
|
+
chartId: external_exports.string().describe("Numeric chart ID to delete"),
|
|
31933
|
+
account: accountParam
|
|
31934
|
+
}
|
|
31935
|
+
}, async ({ spreadsheetId, chartId, account }) => {
|
|
31936
|
+
return runOrDiagnose(["sheets", "chart", "delete", spreadsheetId, chartId], { account });
|
|
31937
|
+
});
|
|
31938
|
+
server2.registerTool("gog_sheets_table_list", {
|
|
31939
|
+
description: "List Google Sheets tables in a spreadsheet (tableId, name, range).",
|
|
31940
|
+
annotations: { readOnlyHint: true },
|
|
31941
|
+
inputSchema: {
|
|
31942
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31943
|
+
account: accountParam
|
|
31944
|
+
}
|
|
31945
|
+
}, async ({ spreadsheetId, account }) => {
|
|
31946
|
+
return runOrDiagnose(["sheets", "table", "list", spreadsheetId], { account });
|
|
31947
|
+
});
|
|
31948
|
+
server2.registerTool("gog_sheets_table_get", {
|
|
31949
|
+
description: "Get a single Google Sheets table (definition + columns) by its table ID.",
|
|
31950
|
+
annotations: { readOnlyHint: true },
|
|
31951
|
+
inputSchema: {
|
|
31952
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31953
|
+
tableId: external_exports.string().describe("Table ID (from gog_sheets_table_list)"),
|
|
31954
|
+
account: accountParam
|
|
31955
|
+
}
|
|
31956
|
+
}, async ({ spreadsheetId, tableId, account }) => {
|
|
31957
|
+
return runOrDiagnose(["sheets", "table", "get", spreadsheetId, tableId], { account });
|
|
31958
|
+
});
|
|
31959
|
+
server2.registerTool("gog_sheets_table_create", {
|
|
31960
|
+
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.",
|
|
31961
|
+
inputSchema: {
|
|
31962
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31963
|
+
range: external_exports.string().describe("Range the table covers (e.g. Sheet1!A1:D20)"),
|
|
31964
|
+
name: external_exports.string().describe("Table name"),
|
|
31965
|
+
columnsJson: external_exports.string().describe("Column definitions as JSON array or @file (columnName + optional columnType)"),
|
|
31966
|
+
account: accountParam
|
|
31967
|
+
}
|
|
31968
|
+
}, async ({ spreadsheetId, range, name, columnsJson, account }) => {
|
|
31969
|
+
return runOrDiagnose(
|
|
31970
|
+
["sheets", "table", "create", spreadsheetId, range, `--name=${name}`, `--columns-json=${columnsJson}`],
|
|
31971
|
+
{ account }
|
|
31972
|
+
);
|
|
31973
|
+
});
|
|
31974
|
+
server2.registerTool("gog_sheets_table_append", {
|
|
31975
|
+
description: "Append data rows to a table. valuesJson is a JSON 2D array of row values.",
|
|
31976
|
+
inputSchema: {
|
|
31977
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31978
|
+
tableId: external_exports.string().describe("Table ID to append to"),
|
|
31979
|
+
valuesJson: external_exports.string().describe('Values as JSON 2D array (e.g. [["a",1],["b",2]])'),
|
|
31980
|
+
input: external_exports.enum(["RAW", "USER_ENTERED"]).optional().describe("Value input option (default: USER_ENTERED)"),
|
|
31981
|
+
account: accountParam
|
|
31982
|
+
}
|
|
31983
|
+
}, async ({ spreadsheetId, tableId, valuesJson, input, account }) => {
|
|
31984
|
+
const args = ["sheets", "table", "append", spreadsheetId, tableId, `--values-json=${valuesJson}`];
|
|
31985
|
+
if (input) args.push(`--input=${input}`);
|
|
31986
|
+
return runOrDiagnose(args, { account });
|
|
31987
|
+
});
|
|
31988
|
+
server2.registerTool("gog_sheets_table_clear", {
|
|
31989
|
+
description: "Clear all data rows from a table (keeps the table and its columns).",
|
|
31990
|
+
annotations: { destructiveHint: true },
|
|
31991
|
+
inputSchema: {
|
|
31992
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31993
|
+
tableId: external_exports.string().describe("Table ID to clear"),
|
|
31994
|
+
account: accountParam
|
|
31995
|
+
}
|
|
31996
|
+
}, async ({ spreadsheetId, tableId, account }) => {
|
|
31997
|
+
return runOrDiagnose(["sheets", "table", "clear", spreadsheetId, tableId], { account });
|
|
31998
|
+
});
|
|
31999
|
+
server2.registerTool("gog_sheets_table_delete", {
|
|
32000
|
+
description: "Delete a table by its table ID.",
|
|
32001
|
+
annotations: { destructiveHint: true },
|
|
32002
|
+
inputSchema: {
|
|
32003
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
32004
|
+
tableId: external_exports.string().describe("Table ID to delete"),
|
|
32005
|
+
account: accountParam
|
|
32006
|
+
}
|
|
32007
|
+
}, async ({ spreadsheetId, tableId, account }) => {
|
|
32008
|
+
return runOrDiagnose(["sheets", "table", "delete", spreadsheetId, tableId], { account });
|
|
32009
|
+
});
|
|
32010
|
+
server2.registerTool("gog_sheets_banding_list", {
|
|
32011
|
+
description: "List alternating-color banded ranges. Optionally scope to a single sheet.",
|
|
32012
|
+
annotations: { readOnlyHint: true },
|
|
32013
|
+
inputSchema: {
|
|
32014
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
32015
|
+
sheet: external_exports.string().optional().describe("Only list banding from this sheet"),
|
|
32016
|
+
account: accountParam
|
|
32017
|
+
}
|
|
32018
|
+
}, async ({ spreadsheetId, sheet, account }) => {
|
|
32019
|
+
const args = ["sheets", "banding", "list", spreadsheetId];
|
|
32020
|
+
if (sheet) args.push(`--sheet=${sheet}`);
|
|
32021
|
+
return runOrDiagnose(args, { account });
|
|
32022
|
+
});
|
|
32023
|
+
server2.registerTool("gog_sheets_banding_set", {
|
|
32024
|
+
description: "Apply alternating colors to a range. Provide rowPropertiesJson and/or columnPropertiesJson \u2014 each a Sheets API BandingProperties JSON object ({headerColor, firstBandColor, secondBandColor, footerColor}). At least one is required.",
|
|
32025
|
+
inputSchema: {
|
|
32026
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
32027
|
+
range: external_exports.string().describe("Range to band (e.g. Sheet1!A1:D20)"),
|
|
32028
|
+
rowPropertiesJson: external_exports.string().optional().describe("BandingProperties JSON for row colors"),
|
|
32029
|
+
columnPropertiesJson: external_exports.string().optional().describe("BandingProperties JSON for column colors"),
|
|
32030
|
+
account: accountParam
|
|
32031
|
+
}
|
|
32032
|
+
}, async ({ spreadsheetId, range, rowPropertiesJson, columnPropertiesJson, account }) => {
|
|
32033
|
+
const args = ["sheets", "banding", "set", spreadsheetId, range];
|
|
32034
|
+
if (rowPropertiesJson) args.push(`--row-properties-json=${rowPropertiesJson}`);
|
|
32035
|
+
if (columnPropertiesJson) args.push(`--column-properties-json=${columnPropertiesJson}`);
|
|
32036
|
+
return runOrDiagnose(args, { account });
|
|
32037
|
+
});
|
|
32038
|
+
server2.registerTool("gog_sheets_banding_clear", {
|
|
32039
|
+
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.",
|
|
32040
|
+
annotations: { destructiveHint: true },
|
|
32041
|
+
inputSchema: {
|
|
32042
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
32043
|
+
id: external_exports.number().optional().describe("Banded range ID to remove"),
|
|
32044
|
+
all: external_exports.boolean().optional().describe("Remove all banding from the sheet (requires sheet)"),
|
|
32045
|
+
sheet: external_exports.string().optional().describe("Sheet name (used with all:true)"),
|
|
32046
|
+
account: accountParam
|
|
32047
|
+
}
|
|
32048
|
+
}, async ({ spreadsheetId, id, all, sheet, account }) => {
|
|
32049
|
+
const args = ["sheets", "banding", "clear", spreadsheetId];
|
|
32050
|
+
if (id !== void 0) args.push(`--id=${id}`);
|
|
32051
|
+
if (all) args.push("--all");
|
|
32052
|
+
if (sheet) args.push(`--sheet=${sheet}`);
|
|
32053
|
+
return runOrDiagnose(args, { account });
|
|
32054
|
+
});
|
|
32055
|
+
server2.registerTool("gog_sheets_conditional_format_list", {
|
|
32056
|
+
description: "List conditional formatting rules. Optionally scope to a single sheet.",
|
|
32057
|
+
annotations: { readOnlyHint: true },
|
|
32058
|
+
inputSchema: {
|
|
32059
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
32060
|
+
sheet: external_exports.string().optional().describe("Only list rules from this sheet"),
|
|
32061
|
+
account: accountParam
|
|
32062
|
+
}
|
|
32063
|
+
}, async ({ spreadsheetId, sheet, account }) => {
|
|
32064
|
+
const args = ["sheets", "conditional-format", "list", spreadsheetId];
|
|
32065
|
+
if (sheet) args.push(`--sheet=${sheet}`);
|
|
32066
|
+
return runOrDiagnose(args, { account });
|
|
32067
|
+
});
|
|
32068
|
+
server2.registerTool("gog_sheets_conditional_format_add", {
|
|
32069
|
+
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).",
|
|
32070
|
+
inputSchema: {
|
|
32071
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
32072
|
+
range: external_exports.string().describe("Range the rule applies to (e.g. Sheet1!A1:A100)"),
|
|
32073
|
+
type: external_exports.enum([
|
|
32074
|
+
"text-eq",
|
|
32075
|
+
"text-contains",
|
|
32076
|
+
"text-starts-with",
|
|
32077
|
+
"text-ends-with",
|
|
32078
|
+
"number-eq",
|
|
32079
|
+
"number-gt",
|
|
32080
|
+
"number-gte",
|
|
32081
|
+
"number-lt",
|
|
32082
|
+
"number-lte",
|
|
32083
|
+
"blank",
|
|
32084
|
+
"not-blank",
|
|
32085
|
+
"custom-formula"
|
|
32086
|
+
]).describe("Rule type"),
|
|
32087
|
+
formatJson: external_exports.string().describe("CellFormat JSON to apply when the condition matches (inline or @file)"),
|
|
32088
|
+
expr: external_exports.string().optional().describe("Expression value or custom formula (omit for blank/not-blank)"),
|
|
32089
|
+
formatFields: external_exports.string().optional().describe("Format field mask for force-sending zero/false fields (e.g. backgroundColor,textFormat.bold)"),
|
|
32090
|
+
account: accountParam
|
|
32091
|
+
}
|
|
32092
|
+
}, async ({ spreadsheetId, range, type, formatJson, expr, formatFields, account }) => {
|
|
32093
|
+
const args = ["sheets", "conditional-format", "add", spreadsheetId, range, `--type=${type}`, `--format-json=${formatJson}`];
|
|
32094
|
+
if (expr !== void 0) args.push(`--expr=${expr}`);
|
|
32095
|
+
if (formatFields) args.push(`--format-fields=${formatFields}`);
|
|
32096
|
+
return runOrDiagnose(args, { account });
|
|
32097
|
+
});
|
|
32098
|
+
server2.registerTool("gog_sheets_conditional_format_clear", {
|
|
32099
|
+
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.",
|
|
32100
|
+
annotations: { destructiveHint: true },
|
|
32101
|
+
inputSchema: {
|
|
32102
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
32103
|
+
sheet: external_exports.string().describe("Sheet name to clear rules from"),
|
|
32104
|
+
index: external_exports.number().optional().describe("0-based rule index to remove"),
|
|
32105
|
+
all: external_exports.boolean().optional().describe("Remove all conditional formatting rules from the sheet"),
|
|
32106
|
+
account: accountParam
|
|
32107
|
+
}
|
|
32108
|
+
}, async ({ spreadsheetId, sheet, index, all, account }) => {
|
|
32109
|
+
const args = ["sheets", "conditional-format", "clear", spreadsheetId, `--sheet=${sheet}`];
|
|
32110
|
+
if (index !== void 0) args.push(`--index=${index}`);
|
|
32111
|
+
if (all) args.push("--all");
|
|
32112
|
+
return runOrDiagnose(args, { account });
|
|
32113
|
+
});
|
|
31872
32114
|
}
|
|
31873
32115
|
|
|
31874
32116
|
// src/index.ts
|
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.
|
|
6
|
+
"version": "2.3.0",
|
|
7
7
|
"description": "Extended Google Sheets for Claude via gogcli — auth + full Sheets support",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Chris Hall",
|
|
@@ -207,6 +207,74 @@
|
|
|
207
207
|
{
|
|
208
208
|
"name": "gog_sheets_reorder_tab",
|
|
209
209
|
"description": "Move a tab to a specific 0-based position."
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"name": "gog_sheets_chart_list",
|
|
213
|
+
"description": "List embedded charts in a spreadsheet."
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"name": "gog_sheets_chart_get",
|
|
217
|
+
"description": "Get a chart's full definition (spec + position) by chart ID."
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"name": "gog_sheets_chart_create",
|
|
221
|
+
"description": "Create an embedded chart from a JSON ChartSpec."
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"name": "gog_sheets_chart_update",
|
|
225
|
+
"description": "Replace a chart's spec by chart ID."
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"name": "gog_sheets_chart_delete",
|
|
229
|
+
"description": "Delete a chart by chart ID."
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"name": "gog_sheets_table_list",
|
|
233
|
+
"description": "List Google Sheets tables in a spreadsheet."
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"name": "gog_sheets_table_get",
|
|
237
|
+
"description": "Get a Google Sheets table by table ID."
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"name": "gog_sheets_table_create",
|
|
241
|
+
"description": "Create a Google Sheets table over a range with typed columns."
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"name": "gog_sheets_table_append",
|
|
245
|
+
"description": "Append data rows to a table."
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"name": "gog_sheets_table_clear",
|
|
249
|
+
"description": "Clear all data rows from a table."
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
"name": "gog_sheets_table_delete",
|
|
253
|
+
"description": "Delete a table by table ID."
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"name": "gog_sheets_banding_list",
|
|
257
|
+
"description": "List alternating-color banded ranges."
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
"name": "gog_sheets_banding_set",
|
|
261
|
+
"description": "Apply alternating colors to a range."
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"name": "gog_sheets_banding_clear",
|
|
265
|
+
"description": "Remove alternating-color banding by ID or for a whole sheet."
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
"name": "gog_sheets_conditional_format_list",
|
|
269
|
+
"description": "List conditional formatting rules."
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"name": "gog_sheets_conditional_format_add",
|
|
273
|
+
"description": "Add a conditional formatting rule to a range."
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
"name": "gog_sheets_conditional_format_clear",
|
|
277
|
+
"description": "Remove conditional formatting rules from a sheet."
|
|
210
278
|
}
|
|
211
279
|
],
|
|
212
280
|
"compatibility": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gogcli-mcp-sheets",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
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.
|
|
10
|
+
"version": "2.3.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "gogcli-mcp-sheets",
|
|
15
|
-
"version": "2.
|
|
15
|
+
"version": "2.3.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
|
@@ -520,4 +520,262 @@ export function registerExtraSheetsTools(server: McpServer): void {
|
|
|
520
520
|
{ account },
|
|
521
521
|
);
|
|
522
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
|
+
});
|
|
523
781
|
}
|
|
@@ -487,6 +487,18 @@ describe('gog_sheets_number_format', () => {
|
|
|
487
487
|
expect(result.content[0].text).toMatch(/warning/i);
|
|
488
488
|
});
|
|
489
489
|
|
|
490
|
+
it('does not warn when every cell is null or empty (no small integers seen)', async () => {
|
|
491
|
+
const handlers = setupHandlers();
|
|
492
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
493
|
+
if (args[1] === 'get') {
|
|
494
|
+
return toText(JSON.stringify({ range: 'A1:A3', values: [[null], [''], [null]] }));
|
|
495
|
+
}
|
|
496
|
+
return toText('{"ok":true}');
|
|
497
|
+
});
|
|
498
|
+
const result = await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE' });
|
|
499
|
+
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
500
|
+
});
|
|
501
|
+
|
|
490
502
|
it('proceeds with format when peek fails (does not block)', async () => {
|
|
491
503
|
const handlers = setupHandlers();
|
|
492
504
|
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
@@ -775,3 +787,325 @@ describe('gog_sheets_reorder_tab', () => {
|
|
|
775
787
|
);
|
|
776
788
|
});
|
|
777
789
|
});
|
|
790
|
+
|
|
791
|
+
// 25. chart list (gog 0.19.0)
|
|
792
|
+
describe('gog_sheets_chart_list', () => {
|
|
793
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
794
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
795
|
+
const handlers = setupHandlers();
|
|
796
|
+
await handlers.get('gog_sheets_chart_list')!({ spreadsheetId: 'sid' });
|
|
797
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'list', 'sid'], { account: undefined });
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
it('forwards account', async () => {
|
|
801
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
802
|
+
const handlers = setupHandlers();
|
|
803
|
+
await handlers.get('gog_sheets_chart_list')!({ spreadsheetId: 'sid', account: 'a@b.com' });
|
|
804
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'list', 'sid'], { account: 'a@b.com' });
|
|
805
|
+
});
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
// 26. chart get
|
|
809
|
+
describe('gog_sheets_chart_get', () => {
|
|
810
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
811
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
812
|
+
const handlers = setupHandlers();
|
|
813
|
+
await handlers.get('gog_sheets_chart_get')!({ spreadsheetId: 'sid', chartId: '12345' });
|
|
814
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'get', 'sid', '12345'], { account: undefined });
|
|
815
|
+
});
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
// 27. chart create
|
|
819
|
+
describe('gog_sheets_chart_create', () => {
|
|
820
|
+
it('calls runOrDiagnose with only --spec-json', async () => {
|
|
821
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
822
|
+
const handlers = setupHandlers();
|
|
823
|
+
const spec = '{"basicChart":{"chartType":"COLUMN"}}';
|
|
824
|
+
await handlers.get('gog_sheets_chart_create')!({ spreadsheetId: 'sid', specJson: spec });
|
|
825
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
826
|
+
['sheets', 'chart', 'create', 'sid', `--spec-json=${spec}`],
|
|
827
|
+
{ account: undefined },
|
|
828
|
+
);
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
it('includes --sheet, --anchor, --width, --height when provided', async () => {
|
|
832
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
833
|
+
const handlers = setupHandlers();
|
|
834
|
+
await handlers.get('gog_sheets_chart_create')!({
|
|
835
|
+
spreadsheetId: 'sid', specJson: '{}', sheet: 'Data', anchor: 'E10', width: 800, height: 400,
|
|
836
|
+
});
|
|
837
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
838
|
+
['sheets', 'chart', 'create', 'sid', '--spec-json={}', '--sheet=Data', '--anchor=E10', '--width=800', '--height=400'],
|
|
839
|
+
{ account: undefined },
|
|
840
|
+
);
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
it('includes --width=0 and --height=0 when zero', async () => {
|
|
844
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
845
|
+
const handlers = setupHandlers();
|
|
846
|
+
await handlers.get('gog_sheets_chart_create')!({ spreadsheetId: 'sid', specJson: '{}', width: 0, height: 0 });
|
|
847
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
848
|
+
['sheets', 'chart', 'create', 'sid', '--spec-json={}', '--width=0', '--height=0'],
|
|
849
|
+
{ account: undefined },
|
|
850
|
+
);
|
|
851
|
+
});
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
// 28. chart update
|
|
855
|
+
describe('gog_sheets_chart_update', () => {
|
|
856
|
+
it('calls runOrDiagnose with chartId and --spec-json', async () => {
|
|
857
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
858
|
+
const handlers = setupHandlers();
|
|
859
|
+
await handlers.get('gog_sheets_chart_update')!({ spreadsheetId: 'sid', chartId: '99', specJson: '{}' });
|
|
860
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
861
|
+
['sheets', 'chart', 'update', 'sid', '99', '--spec-json={}'],
|
|
862
|
+
{ account: undefined },
|
|
863
|
+
);
|
|
864
|
+
});
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
// 29. chart delete
|
|
868
|
+
describe('gog_sheets_chart_delete', () => {
|
|
869
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
870
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
871
|
+
const handlers = setupHandlers();
|
|
872
|
+
await handlers.get('gog_sheets_chart_delete')!({ spreadsheetId: 'sid', chartId: '7' });
|
|
873
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'delete', 'sid', '7'], { account: undefined });
|
|
874
|
+
});
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
// 30. table list
|
|
878
|
+
describe('gog_sheets_table_list', () => {
|
|
879
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
880
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
881
|
+
const handlers = setupHandlers();
|
|
882
|
+
await handlers.get('gog_sheets_table_list')!({ spreadsheetId: 'sid' });
|
|
883
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'list', 'sid'], { account: undefined });
|
|
884
|
+
});
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
// 31. table get
|
|
888
|
+
describe('gog_sheets_table_get', () => {
|
|
889
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
890
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
891
|
+
const handlers = setupHandlers();
|
|
892
|
+
await handlers.get('gog_sheets_table_get')!({ spreadsheetId: 'sid', tableId: 't1' });
|
|
893
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'get', 'sid', 't1'], { account: undefined });
|
|
894
|
+
});
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
// 32. table create
|
|
898
|
+
describe('gog_sheets_table_create', () => {
|
|
899
|
+
it('calls runOrDiagnose with range, --name, --columns-json', async () => {
|
|
900
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
901
|
+
const handlers = setupHandlers();
|
|
902
|
+
const cols = '[{"columnName":"Name","columnType":"TEXT"}]';
|
|
903
|
+
await handlers.get('gog_sheets_table_create')!({ spreadsheetId: 'sid', range: 'Sheet1!A1:B10', name: 'People', columnsJson: cols });
|
|
904
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
905
|
+
['sheets', 'table', 'create', 'sid', 'Sheet1!A1:B10', '--name=People', `--columns-json=${cols}`],
|
|
906
|
+
{ account: undefined },
|
|
907
|
+
);
|
|
908
|
+
});
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
// 33. table append
|
|
912
|
+
describe('gog_sheets_table_append', () => {
|
|
913
|
+
it('calls runOrDiagnose with --values-json (no --input)', async () => {
|
|
914
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
915
|
+
const handlers = setupHandlers();
|
|
916
|
+
await handlers.get('gog_sheets_table_append')!({ spreadsheetId: 'sid', tableId: 't1', valuesJson: '[["a",1]]' });
|
|
917
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
918
|
+
['sheets', 'table', 'append', 'sid', 't1', '--values-json=[["a",1]]'],
|
|
919
|
+
{ account: undefined },
|
|
920
|
+
);
|
|
921
|
+
});
|
|
922
|
+
|
|
923
|
+
it('includes --input when provided', async () => {
|
|
924
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
925
|
+
const handlers = setupHandlers();
|
|
926
|
+
await handlers.get('gog_sheets_table_append')!({ spreadsheetId: 'sid', tableId: 't1', valuesJson: '[]', input: 'RAW' });
|
|
927
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
928
|
+
['sheets', 'table', 'append', 'sid', 't1', '--values-json=[]', '--input=RAW'],
|
|
929
|
+
{ account: undefined },
|
|
930
|
+
);
|
|
931
|
+
});
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
// 34. table clear
|
|
935
|
+
describe('gog_sheets_table_clear', () => {
|
|
936
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
937
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
938
|
+
const handlers = setupHandlers();
|
|
939
|
+
await handlers.get('gog_sheets_table_clear')!({ spreadsheetId: 'sid', tableId: 't1' });
|
|
940
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'clear', 'sid', 't1'], { account: undefined });
|
|
941
|
+
});
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
// 35. table delete
|
|
945
|
+
describe('gog_sheets_table_delete', () => {
|
|
946
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
947
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
948
|
+
const handlers = setupHandlers();
|
|
949
|
+
await handlers.get('gog_sheets_table_delete')!({ spreadsheetId: 'sid', tableId: 't1' });
|
|
950
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'delete', 'sid', 't1'], { account: undefined });
|
|
951
|
+
});
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
// 36. banding list
|
|
955
|
+
describe('gog_sheets_banding_list', () => {
|
|
956
|
+
it('calls runOrDiagnose with no --sheet', async () => {
|
|
957
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
958
|
+
const handlers = setupHandlers();
|
|
959
|
+
await handlers.get('gog_sheets_banding_list')!({ spreadsheetId: 'sid' });
|
|
960
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'list', 'sid'], { account: undefined });
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
it('includes --sheet when provided', async () => {
|
|
964
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
965
|
+
const handlers = setupHandlers();
|
|
966
|
+
await handlers.get('gog_sheets_banding_list')!({ spreadsheetId: 'sid', sheet: 'Data' });
|
|
967
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'list', 'sid', '--sheet=Data'], { account: undefined });
|
|
968
|
+
});
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
// 37. banding set
|
|
972
|
+
describe('gog_sheets_banding_set', () => {
|
|
973
|
+
it('calls runOrDiagnose with range only', async () => {
|
|
974
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
975
|
+
const handlers = setupHandlers();
|
|
976
|
+
await handlers.get('gog_sheets_banding_set')!({ spreadsheetId: 'sid', range: 'Sheet1!A1:D20' });
|
|
977
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'set', 'sid', 'Sheet1!A1:D20'], { account: undefined });
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
it('includes --row-properties-json and --column-properties-json when provided', async () => {
|
|
981
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
982
|
+
const handlers = setupHandlers();
|
|
983
|
+
await handlers.get('gog_sheets_banding_set')!({
|
|
984
|
+
spreadsheetId: 'sid', range: 'A1:D20',
|
|
985
|
+
rowPropertiesJson: '{"firstBandColor":{}}',
|
|
986
|
+
columnPropertiesJson: '{"secondBandColor":{}}',
|
|
987
|
+
});
|
|
988
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
989
|
+
['sheets', 'banding', 'set', 'sid', 'A1:D20', '--row-properties-json={"firstBandColor":{}}', '--column-properties-json={"secondBandColor":{}}'],
|
|
990
|
+
{ account: undefined },
|
|
991
|
+
);
|
|
992
|
+
});
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
// 38. banding clear
|
|
996
|
+
describe('gog_sheets_banding_clear', () => {
|
|
997
|
+
it('calls runOrDiagnose with no optional flags', async () => {
|
|
998
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
999
|
+
const handlers = setupHandlers();
|
|
1000
|
+
await handlers.get('gog_sheets_banding_clear')!({ spreadsheetId: 'sid' });
|
|
1001
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid'], { account: undefined });
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
it('includes --id when provided (including 0)', async () => {
|
|
1005
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1006
|
+
const handlers = setupHandlers();
|
|
1007
|
+
await handlers.get('gog_sheets_banding_clear')!({ spreadsheetId: 'sid', id: 0 });
|
|
1008
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--id=0'], { account: undefined });
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
it('includes --all and --sheet when clearing a whole sheet', async () => {
|
|
1012
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1013
|
+
const handlers = setupHandlers();
|
|
1014
|
+
await handlers.get('gog_sheets_banding_clear')!({ spreadsheetId: 'sid', all: true, sheet: 'Data' });
|
|
1015
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--all', '--sheet=Data'], { account: undefined });
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
it('omits --all when false', async () => {
|
|
1019
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1020
|
+
const handlers = setupHandlers();
|
|
1021
|
+
await handlers.get('gog_sheets_banding_clear')!({ spreadsheetId: 'sid', all: false, id: 5 });
|
|
1022
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--id=5'], { account: undefined });
|
|
1023
|
+
});
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
// 39. conditional-format list
|
|
1027
|
+
describe('gog_sheets_conditional_format_list', () => {
|
|
1028
|
+
it('calls runOrDiagnose with no --sheet', async () => {
|
|
1029
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1030
|
+
const handlers = setupHandlers();
|
|
1031
|
+
await handlers.get('gog_sheets_conditional_format_list')!({ spreadsheetId: 'sid' });
|
|
1032
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'list', 'sid'], { account: undefined });
|
|
1033
|
+
});
|
|
1034
|
+
|
|
1035
|
+
it('includes --sheet when provided', async () => {
|
|
1036
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1037
|
+
const handlers = setupHandlers();
|
|
1038
|
+
await handlers.get('gog_sheets_conditional_format_list')!({ spreadsheetId: 'sid', sheet: 'Data' });
|
|
1039
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'list', 'sid', '--sheet=Data'], { account: undefined });
|
|
1040
|
+
});
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1043
|
+
// 40. conditional-format add
|
|
1044
|
+
describe('gog_sheets_conditional_format_add', () => {
|
|
1045
|
+
it('calls runOrDiagnose with type and --format-json (no expr/fields)', async () => {
|
|
1046
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1047
|
+
const handlers = setupHandlers();
|
|
1048
|
+
await handlers.get('gog_sheets_conditional_format_add')!({
|
|
1049
|
+
spreadsheetId: 'sid', range: 'A1:A100', type: 'not-blank', formatJson: '{"backgroundColor":{}}',
|
|
1050
|
+
});
|
|
1051
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1052
|
+
['sheets', 'conditional-format', 'add', 'sid', 'A1:A100', '--type=not-blank', '--format-json={"backgroundColor":{}}'],
|
|
1053
|
+
{ account: undefined },
|
|
1054
|
+
);
|
|
1055
|
+
});
|
|
1056
|
+
|
|
1057
|
+
it('includes --expr and --format-fields when provided', async () => {
|
|
1058
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1059
|
+
const handlers = setupHandlers();
|
|
1060
|
+
await handlers.get('gog_sheets_conditional_format_add')!({
|
|
1061
|
+
spreadsheetId: 'sid', range: 'B1:B50', type: 'number-gt', expr: '100', formatJson: '{}', formatFields: 'backgroundColor,textFormat.bold',
|
|
1062
|
+
});
|
|
1063
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1064
|
+
['sheets', 'conditional-format', 'add', 'sid', 'B1:B50', '--type=number-gt', '--format-json={}', '--expr=100', '--format-fields=backgroundColor,textFormat.bold'],
|
|
1065
|
+
{ account: undefined },
|
|
1066
|
+
);
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
it('includes --expr when it is an empty string', async () => {
|
|
1070
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1071
|
+
const handlers = setupHandlers();
|
|
1072
|
+
await handlers.get('gog_sheets_conditional_format_add')!({
|
|
1073
|
+
spreadsheetId: 'sid', range: 'A1', type: 'text-eq', expr: '', formatJson: '{}',
|
|
1074
|
+
});
|
|
1075
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1076
|
+
['sheets', 'conditional-format', 'add', 'sid', 'A1', '--type=text-eq', '--format-json={}', '--expr='],
|
|
1077
|
+
{ account: undefined },
|
|
1078
|
+
);
|
|
1079
|
+
});
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
// 41. conditional-format clear
|
|
1083
|
+
describe('gog_sheets_conditional_format_clear', () => {
|
|
1084
|
+
it('calls runOrDiagnose with --sheet only', async () => {
|
|
1085
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1086
|
+
const handlers = setupHandlers();
|
|
1087
|
+
await handlers.get('gog_sheets_conditional_format_clear')!({ spreadsheetId: 'sid', sheet: 'Data' });
|
|
1088
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data'], { account: undefined });
|
|
1089
|
+
});
|
|
1090
|
+
|
|
1091
|
+
it('includes --index when provided (including 0)', async () => {
|
|
1092
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1093
|
+
const handlers = setupHandlers();
|
|
1094
|
+
await handlers.get('gog_sheets_conditional_format_clear')!({ spreadsheetId: 'sid', sheet: 'Data', index: 0 });
|
|
1095
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--index=0'], { account: undefined });
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
it('includes --all when true', async () => {
|
|
1099
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1100
|
+
const handlers = setupHandlers();
|
|
1101
|
+
await handlers.get('gog_sheets_conditional_format_clear')!({ spreadsheetId: 'sid', sheet: 'Data', all: true });
|
|
1102
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--all'], { account: undefined });
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
it('omits --all when false', async () => {
|
|
1106
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1107
|
+
const handlers = setupHandlers();
|
|
1108
|
+
await handlers.get('gog_sheets_conditional_format_clear')!({ spreadsheetId: 'sid', sheet: 'Data', all: false, index: 2 });
|
|
1109
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--index=2'], { account: undefined });
|
|
1110
|
+
});
|
|
1111
|
+
});
|