gogcli-mcp-sheets 2.2.0 → 2.4.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 +265 -4
- package/manifest.json +73 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/tools/sheets-extra.ts +281 -3
- package/tests/tools/sheets-extra.test.ts +371 -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.4.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.4.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.4.0" : "0.0.0";
|
|
31403
31403
|
function createServer(options) {
|
|
31404
31404
|
return new McpServer({
|
|
31405
31405
|
name: options?.name ?? "gogcli",
|
|
@@ -31548,13 +31548,32 @@ function registerExtraSheetsTools(server2) {
|
|
|
31548
31548
|
start: external_exports.number().describe("Start index (0-based)"),
|
|
31549
31549
|
count: external_exports.number().optional().describe("Number of rows/columns to insert (default: 1)"),
|
|
31550
31550
|
after: external_exports.boolean().optional().describe("Insert after the start index instead of before. With after:true the new dimension lands at start+1, leaving the existing dimension at start untouched."),
|
|
31551
|
+
inheritFromBefore: external_exports.boolean().optional().describe("Inherit number format / styling from the row/column before the insertion. Defaults to true with after:true, false otherwise; pass false to inherit from the row/column after the insertion instead. Cannot inherit from before when inserting at the first row/column."),
|
|
31551
31552
|
account: accountParam
|
|
31552
31553
|
}
|
|
31553
|
-
}, async ({ spreadsheetId, sheet, dimension, start, count, after, account }) => {
|
|
31554
|
+
}, async ({ spreadsheetId, sheet, dimension, start, count, after, inheritFromBefore, account }) => {
|
|
31554
31555
|
const effectiveStart = after ? start + 1 : start;
|
|
31555
31556
|
const args = ["sheets", "insert", spreadsheetId, sheet, dimension, String(effectiveStart)];
|
|
31556
31557
|
if (count !== void 0) args.push(`--count=${count}`);
|
|
31557
31558
|
if (after) args.push("--after");
|
|
31559
|
+
if (inheritFromBefore !== void 0) args.push(`--inherit-from-before=${inheritFromBefore}`);
|
|
31560
|
+
return runOrDiagnose(args, { account });
|
|
31561
|
+
});
|
|
31562
|
+
server2.registerTool("gog_sheets_copy_paste", {
|
|
31563
|
+
description: "Copy a source range's values/formulas/format to a destination range via the Sheets CopyPasteRequest. A destination larger than the source tiles the source to fill it \u2014 the canonical way to fill formulas down or across with relative references adjusted (aliases: fill, copy-range). Use type to control what is pasted (NORMAL pastes everything; FORMULA fills formulas with adjusted references; VALUES/FORMAT/etc. paste only that aspect).",
|
|
31564
|
+
annotations: { destructiveHint: true },
|
|
31565
|
+
inputSchema: {
|
|
31566
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31567
|
+
source: external_exports.string().describe("Source range (e.g. Sheet1!A2:H71)"),
|
|
31568
|
+
dest: external_exports.string().describe("Destination range (e.g. Sheet1!A2:H120). Larger than the source tiles/fills it."),
|
|
31569
|
+
type: external_exports.enum(["NORMAL", "VALUES", "FORMAT", "FORMULA", "NO_BORDERS", "DATA_VALIDATION", "CONDITIONAL_FORMATTING"]).optional().describe("Paste type (default: NORMAL pastes everything)"),
|
|
31570
|
+
transpose: external_exports.boolean().optional().describe("Paste transposed (swap rows and columns)"),
|
|
31571
|
+
account: accountParam
|
|
31572
|
+
}
|
|
31573
|
+
}, async ({ spreadsheetId, source, dest, type, transpose, account }) => {
|
|
31574
|
+
const args = ["sheets", "copy-paste", spreadsheetId, source, dest];
|
|
31575
|
+
if (type) args.push(`--type=${type}`);
|
|
31576
|
+
if (transpose) args.push("--transpose");
|
|
31558
31577
|
return runOrDiagnose(args, { account });
|
|
31559
31578
|
});
|
|
31560
31579
|
server2.registerTool("gog_sheets_merge", {
|
|
@@ -31835,11 +31854,11 @@ ${result.content[0].text}` }] };
|
|
|
31835
31854
|
return runOrDiagnose(["sheets", "named-ranges", "delete", spreadsheetId, nameOrId], { account });
|
|
31836
31855
|
});
|
|
31837
31856
|
server2.registerTool("gog_sheets_batch_update", {
|
|
31838
|
-
description:
|
|
31857
|
+
description: `Update values in multiple ranges with one Sheets API request. dataJson is a JSON array of {range, values} objects \u2014 pass it inline as a literal JSON string. Atomic \u2014 either all ranges update or none do. (The CLI also accepts "@/path/to/file.json", but that file is read on the gog server's filesystem, not the caller's, so inline JSON is the right form for remote MCP callers.)`,
|
|
31839
31858
|
annotations: { destructiveHint: true },
|
|
31840
31859
|
inputSchema: {
|
|
31841
31860
|
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31842
|
-
dataJson: external_exports.string().describe('Value ranges as JSON array,
|
|
31861
|
+
dataJson: external_exports.string().describe('Value ranges as an inline JSON array, e.g. [{"range":"Sheet1!A1:B2","values":[["a","b"]]}]. (An "@/path" form is read on the gog server filesystem, not yours \u2014 inline the JSON instead.)'),
|
|
31843
31862
|
input: external_exports.enum(["RAW", "USER_ENTERED"]).optional().describe("Value input option (default: USER_ENTERED)"),
|
|
31844
31863
|
includeValuesInResponse: external_exports.boolean().optional().describe("Include updated values in the response"),
|
|
31845
31864
|
responseRender: external_exports.enum(["FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA"]).optional().describe("Response value render option"),
|
|
@@ -31869,6 +31888,248 @@ ${result.content[0].text}` }] };
|
|
|
31869
31888
|
{ account }
|
|
31870
31889
|
);
|
|
31871
31890
|
});
|
|
31891
|
+
server2.registerTool("gog_sheets_chart_list", {
|
|
31892
|
+
description: "List embedded charts in a spreadsheet (chartId, type, position).",
|
|
31893
|
+
annotations: { readOnlyHint: true },
|
|
31894
|
+
inputSchema: {
|
|
31895
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31896
|
+
account: accountParam
|
|
31897
|
+
}
|
|
31898
|
+
}, async ({ spreadsheetId, account }) => {
|
|
31899
|
+
return runOrDiagnose(["sheets", "chart", "list", spreadsheetId], { account });
|
|
31900
|
+
});
|
|
31901
|
+
server2.registerTool("gog_sheets_chart_get", {
|
|
31902
|
+
description: "Get the full definition (spec + position) of a single chart by its numeric chart ID.",
|
|
31903
|
+
annotations: { readOnlyHint: true },
|
|
31904
|
+
inputSchema: {
|
|
31905
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31906
|
+
chartId: external_exports.string().describe("Numeric chart ID (from gog_sheets_chart_list)"),
|
|
31907
|
+
account: accountParam
|
|
31908
|
+
}
|
|
31909
|
+
}, async ({ spreadsheetId, chartId, account }) => {
|
|
31910
|
+
return runOrDiagnose(["sheets", "chart", "get", spreadsheetId, chartId], { account });
|
|
31911
|
+
});
|
|
31912
|
+
server2.registerTool("gog_sheets_chart_create", {
|
|
31913
|
+
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.",
|
|
31914
|
+
inputSchema: {
|
|
31915
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31916
|
+
specJson: external_exports.string().describe("ChartSpec or EmbeddedChart JSON (inline or @file)"),
|
|
31917
|
+
sheet: external_exports.string().optional().describe("Sheet name for the anchor (resolved to sheetId)"),
|
|
31918
|
+
anchor: external_exports.string().optional().describe("Anchor cell in A1 notation (e.g. A1, E10)"),
|
|
31919
|
+
width: external_exports.number().optional().describe("Chart width in pixels (default: 600)"),
|
|
31920
|
+
height: external_exports.number().optional().describe("Chart height in pixels (default: 371)"),
|
|
31921
|
+
account: accountParam
|
|
31922
|
+
}
|
|
31923
|
+
}, async ({ spreadsheetId, specJson, sheet, anchor, width, height, account }) => {
|
|
31924
|
+
const args = ["sheets", "chart", "create", spreadsheetId, `--spec-json=${specJson}`];
|
|
31925
|
+
if (sheet) args.push(`--sheet=${sheet}`);
|
|
31926
|
+
if (anchor) args.push(`--anchor=${anchor}`);
|
|
31927
|
+
if (width !== void 0) args.push(`--width=${width}`);
|
|
31928
|
+
if (height !== void 0) args.push(`--height=${height}`);
|
|
31929
|
+
return runOrDiagnose(args, { account });
|
|
31930
|
+
});
|
|
31931
|
+
server2.registerTool("gog_sheets_chart_update", {
|
|
31932
|
+
description: "Replace a chart spec by chart ID. specJson is a Sheets API ChartSpec (or full EmbeddedChart) \u2014 inline or @/path/to/file.json.",
|
|
31933
|
+
annotations: { destructiveHint: true },
|
|
31934
|
+
inputSchema: {
|
|
31935
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31936
|
+
chartId: external_exports.string().describe("Numeric chart ID to update"),
|
|
31937
|
+
specJson: external_exports.string().describe("ChartSpec or EmbeddedChart JSON (inline or @file)"),
|
|
31938
|
+
account: accountParam
|
|
31939
|
+
}
|
|
31940
|
+
}, async ({ spreadsheetId, chartId, specJson, account }) => {
|
|
31941
|
+
return runOrDiagnose(
|
|
31942
|
+
["sheets", "chart", "update", spreadsheetId, chartId, `--spec-json=${specJson}`],
|
|
31943
|
+
{ account }
|
|
31944
|
+
);
|
|
31945
|
+
});
|
|
31946
|
+
server2.registerTool("gog_sheets_chart_delete", {
|
|
31947
|
+
description: "Delete a chart by its numeric chart ID.",
|
|
31948
|
+
annotations: { destructiveHint: true },
|
|
31949
|
+
inputSchema: {
|
|
31950
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31951
|
+
chartId: external_exports.string().describe("Numeric chart ID to delete"),
|
|
31952
|
+
account: accountParam
|
|
31953
|
+
}
|
|
31954
|
+
}, async ({ spreadsheetId, chartId, account }) => {
|
|
31955
|
+
return runOrDiagnose(["sheets", "chart", "delete", spreadsheetId, chartId], { account });
|
|
31956
|
+
});
|
|
31957
|
+
server2.registerTool("gog_sheets_table_list", {
|
|
31958
|
+
description: "List Google Sheets tables in a spreadsheet (tableId, name, range).",
|
|
31959
|
+
annotations: { readOnlyHint: true },
|
|
31960
|
+
inputSchema: {
|
|
31961
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31962
|
+
account: accountParam
|
|
31963
|
+
}
|
|
31964
|
+
}, async ({ spreadsheetId, account }) => {
|
|
31965
|
+
return runOrDiagnose(["sheets", "table", "list", spreadsheetId], { account });
|
|
31966
|
+
});
|
|
31967
|
+
server2.registerTool("gog_sheets_table_get", {
|
|
31968
|
+
description: "Get a single Google Sheets table (definition + columns) by its table ID.",
|
|
31969
|
+
annotations: { readOnlyHint: true },
|
|
31970
|
+
inputSchema: {
|
|
31971
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31972
|
+
tableId: external_exports.string().describe("Table ID (from gog_sheets_table_list)"),
|
|
31973
|
+
account: accountParam
|
|
31974
|
+
}
|
|
31975
|
+
}, async ({ spreadsheetId, tableId, account }) => {
|
|
31976
|
+
return runOrDiagnose(["sheets", "table", "get", spreadsheetId, tableId], { account });
|
|
31977
|
+
});
|
|
31978
|
+
server2.registerTool("gog_sheets_table_create", {
|
|
31979
|
+
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.",
|
|
31980
|
+
inputSchema: {
|
|
31981
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31982
|
+
range: external_exports.string().describe("Range the table covers (e.g. Sheet1!A1:D20)"),
|
|
31983
|
+
name: external_exports.string().describe("Table name"),
|
|
31984
|
+
columnsJson: external_exports.string().describe("Column definitions as JSON array or @file (columnName + optional columnType)"),
|
|
31985
|
+
account: accountParam
|
|
31986
|
+
}
|
|
31987
|
+
}, async ({ spreadsheetId, range, name, columnsJson, account }) => {
|
|
31988
|
+
return runOrDiagnose(
|
|
31989
|
+
["sheets", "table", "create", spreadsheetId, range, `--name=${name}`, `--columns-json=${columnsJson}`],
|
|
31990
|
+
{ account }
|
|
31991
|
+
);
|
|
31992
|
+
});
|
|
31993
|
+
server2.registerTool("gog_sheets_table_append", {
|
|
31994
|
+
description: "Append data rows to a table. valuesJson is a JSON 2D array of row values.",
|
|
31995
|
+
inputSchema: {
|
|
31996
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31997
|
+
tableId: external_exports.string().describe("Table ID to append to"),
|
|
31998
|
+
valuesJson: external_exports.string().describe('Values as JSON 2D array (e.g. [["a",1],["b",2]])'),
|
|
31999
|
+
input: external_exports.enum(["RAW", "USER_ENTERED"]).optional().describe("Value input option (default: USER_ENTERED)"),
|
|
32000
|
+
account: accountParam
|
|
32001
|
+
}
|
|
32002
|
+
}, async ({ spreadsheetId, tableId, valuesJson, input, account }) => {
|
|
32003
|
+
const args = ["sheets", "table", "append", spreadsheetId, tableId, `--values-json=${valuesJson}`];
|
|
32004
|
+
if (input) args.push(`--input=${input}`);
|
|
32005
|
+
return runOrDiagnose(args, { account });
|
|
32006
|
+
});
|
|
32007
|
+
server2.registerTool("gog_sheets_table_clear", {
|
|
32008
|
+
description: "Clear all data rows from a table (keeps the table and its columns).",
|
|
32009
|
+
annotations: { destructiveHint: true },
|
|
32010
|
+
inputSchema: {
|
|
32011
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
32012
|
+
tableId: external_exports.string().describe("Table ID to clear"),
|
|
32013
|
+
account: accountParam
|
|
32014
|
+
}
|
|
32015
|
+
}, async ({ spreadsheetId, tableId, account }) => {
|
|
32016
|
+
return runOrDiagnose(["sheets", "table", "clear", spreadsheetId, tableId], { account });
|
|
32017
|
+
});
|
|
32018
|
+
server2.registerTool("gog_sheets_table_delete", {
|
|
32019
|
+
description: "Delete a table by its table ID.",
|
|
32020
|
+
annotations: { destructiveHint: true },
|
|
32021
|
+
inputSchema: {
|
|
32022
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
32023
|
+
tableId: external_exports.string().describe("Table ID to delete"),
|
|
32024
|
+
account: accountParam
|
|
32025
|
+
}
|
|
32026
|
+
}, async ({ spreadsheetId, tableId, account }) => {
|
|
32027
|
+
return runOrDiagnose(["sheets", "table", "delete", spreadsheetId, tableId], { account });
|
|
32028
|
+
});
|
|
32029
|
+
server2.registerTool("gog_sheets_banding_list", {
|
|
32030
|
+
description: "List alternating-color banded ranges. Optionally scope to a single sheet.",
|
|
32031
|
+
annotations: { readOnlyHint: true },
|
|
32032
|
+
inputSchema: {
|
|
32033
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
32034
|
+
sheet: external_exports.string().optional().describe("Only list banding from this sheet"),
|
|
32035
|
+
account: accountParam
|
|
32036
|
+
}
|
|
32037
|
+
}, async ({ spreadsheetId, sheet, account }) => {
|
|
32038
|
+
const args = ["sheets", "banding", "list", spreadsheetId];
|
|
32039
|
+
if (sheet) args.push(`--sheet=${sheet}`);
|
|
32040
|
+
return runOrDiagnose(args, { account });
|
|
32041
|
+
});
|
|
32042
|
+
server2.registerTool("gog_sheets_banding_set", {
|
|
32043
|
+
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.",
|
|
32044
|
+
inputSchema: {
|
|
32045
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
32046
|
+
range: external_exports.string().describe("Range to band (e.g. Sheet1!A1:D20)"),
|
|
32047
|
+
rowPropertiesJson: external_exports.string().optional().describe("BandingProperties JSON for row colors"),
|
|
32048
|
+
columnPropertiesJson: external_exports.string().optional().describe("BandingProperties JSON for column colors"),
|
|
32049
|
+
account: accountParam
|
|
32050
|
+
}
|
|
32051
|
+
}, async ({ spreadsheetId, range, rowPropertiesJson, columnPropertiesJson, account }) => {
|
|
32052
|
+
const args = ["sheets", "banding", "set", spreadsheetId, range];
|
|
32053
|
+
if (rowPropertiesJson) args.push(`--row-properties-json=${rowPropertiesJson}`);
|
|
32054
|
+
if (columnPropertiesJson) args.push(`--column-properties-json=${columnPropertiesJson}`);
|
|
32055
|
+
return runOrDiagnose(args, { account });
|
|
32056
|
+
});
|
|
32057
|
+
server2.registerTool("gog_sheets_banding_clear", {
|
|
32058
|
+
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.",
|
|
32059
|
+
annotations: { destructiveHint: true },
|
|
32060
|
+
inputSchema: {
|
|
32061
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
32062
|
+
id: external_exports.number().optional().describe("Banded range ID to remove"),
|
|
32063
|
+
all: external_exports.boolean().optional().describe("Remove all banding from the sheet (requires sheet)"),
|
|
32064
|
+
sheet: external_exports.string().optional().describe("Sheet name (used with all:true)"),
|
|
32065
|
+
account: accountParam
|
|
32066
|
+
}
|
|
32067
|
+
}, async ({ spreadsheetId, id, all, sheet, account }) => {
|
|
32068
|
+
const args = ["sheets", "banding", "clear", spreadsheetId];
|
|
32069
|
+
if (id !== void 0) args.push(`--id=${id}`);
|
|
32070
|
+
if (all) args.push("--all");
|
|
32071
|
+
if (sheet) args.push(`--sheet=${sheet}`);
|
|
32072
|
+
return runOrDiagnose(args, { account });
|
|
32073
|
+
});
|
|
32074
|
+
server2.registerTool("gog_sheets_conditional_format_list", {
|
|
32075
|
+
description: "List conditional formatting rules. Optionally scope to a single sheet.",
|
|
32076
|
+
annotations: { readOnlyHint: true },
|
|
32077
|
+
inputSchema: {
|
|
32078
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
32079
|
+
sheet: external_exports.string().optional().describe("Only list rules from this sheet"),
|
|
32080
|
+
account: accountParam
|
|
32081
|
+
}
|
|
32082
|
+
}, async ({ spreadsheetId, sheet, account }) => {
|
|
32083
|
+
const args = ["sheets", "conditional-format", "list", spreadsheetId];
|
|
32084
|
+
if (sheet) args.push(`--sheet=${sheet}`);
|
|
32085
|
+
return runOrDiagnose(args, { account });
|
|
32086
|
+
});
|
|
32087
|
+
server2.registerTool("gog_sheets_conditional_format_add", {
|
|
32088
|
+
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).",
|
|
32089
|
+
inputSchema: {
|
|
32090
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
32091
|
+
range: external_exports.string().describe("Range the rule applies to (e.g. Sheet1!A1:A100)"),
|
|
32092
|
+
type: external_exports.enum([
|
|
32093
|
+
"text-eq",
|
|
32094
|
+
"text-contains",
|
|
32095
|
+
"text-starts-with",
|
|
32096
|
+
"text-ends-with",
|
|
32097
|
+
"number-eq",
|
|
32098
|
+
"number-gt",
|
|
32099
|
+
"number-gte",
|
|
32100
|
+
"number-lt",
|
|
32101
|
+
"number-lte",
|
|
32102
|
+
"blank",
|
|
32103
|
+
"not-blank",
|
|
32104
|
+
"custom-formula"
|
|
32105
|
+
]).describe("Rule type"),
|
|
32106
|
+
formatJson: external_exports.string().describe("CellFormat JSON to apply when the condition matches (inline or @file)"),
|
|
32107
|
+
expr: external_exports.string().optional().describe("Expression value or custom formula (omit for blank/not-blank)"),
|
|
32108
|
+
formatFields: external_exports.string().optional().describe("Format field mask for force-sending zero/false fields (e.g. backgroundColor,textFormat.bold)"),
|
|
32109
|
+
account: accountParam
|
|
32110
|
+
}
|
|
32111
|
+
}, async ({ spreadsheetId, range, type, formatJson, expr, formatFields, account }) => {
|
|
32112
|
+
const args = ["sheets", "conditional-format", "add", spreadsheetId, range, `--type=${type}`, `--format-json=${formatJson}`];
|
|
32113
|
+
if (expr !== void 0) args.push(`--expr=${expr}`);
|
|
32114
|
+
if (formatFields) args.push(`--format-fields=${formatFields}`);
|
|
32115
|
+
return runOrDiagnose(args, { account });
|
|
32116
|
+
});
|
|
32117
|
+
server2.registerTool("gog_sheets_conditional_format_clear", {
|
|
32118
|
+
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.",
|
|
32119
|
+
annotations: { destructiveHint: true },
|
|
32120
|
+
inputSchema: {
|
|
32121
|
+
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
32122
|
+
sheet: external_exports.string().describe("Sheet name to clear rules from"),
|
|
32123
|
+
index: external_exports.number().optional().describe("0-based rule index to remove"),
|
|
32124
|
+
all: external_exports.boolean().optional().describe("Remove all conditional formatting rules from the sheet"),
|
|
32125
|
+
account: accountParam
|
|
32126
|
+
}
|
|
32127
|
+
}, async ({ spreadsheetId, sheet, index, all, account }) => {
|
|
32128
|
+
const args = ["sheets", "conditional-format", "clear", spreadsheetId, `--sheet=${sheet}`];
|
|
32129
|
+
if (index !== void 0) args.push(`--index=${index}`);
|
|
32130
|
+
if (all) args.push("--all");
|
|
32131
|
+
return runOrDiagnose(args, { account });
|
|
32132
|
+
});
|
|
31872
32133
|
}
|
|
31873
32134
|
|
|
31874
32135
|
// 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.4.0",
|
|
7
7
|
"description": "Extended Google Sheets for Claude via gogcli — auth + full Sheets support",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Chris Hall",
|
|
@@ -136,6 +136,10 @@
|
|
|
136
136
|
"name": "gog_sheets_insert",
|
|
137
137
|
"description": "Insert rows or columns into a sheet."
|
|
138
138
|
},
|
|
139
|
+
{
|
|
140
|
+
"name": "gog_sheets_copy_paste",
|
|
141
|
+
"description": "Copy a range's values/formulas/format to another range (tiles to fill down/across)."
|
|
142
|
+
},
|
|
139
143
|
{
|
|
140
144
|
"name": "gog_sheets_merge",
|
|
141
145
|
"description": "Merge cells in a range."
|
|
@@ -207,6 +211,74 @@
|
|
|
207
211
|
{
|
|
208
212
|
"name": "gog_sheets_reorder_tab",
|
|
209
213
|
"description": "Move a tab to a specific 0-based position."
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"name": "gog_sheets_chart_list",
|
|
217
|
+
"description": "List embedded charts in a spreadsheet."
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"name": "gog_sheets_chart_get",
|
|
221
|
+
"description": "Get a chart's full definition (spec + position) by chart ID."
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"name": "gog_sheets_chart_create",
|
|
225
|
+
"description": "Create an embedded chart from a JSON ChartSpec."
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"name": "gog_sheets_chart_update",
|
|
229
|
+
"description": "Replace a chart's spec by chart ID."
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"name": "gog_sheets_chart_delete",
|
|
233
|
+
"description": "Delete a chart by chart ID."
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"name": "gog_sheets_table_list",
|
|
237
|
+
"description": "List Google Sheets tables in a spreadsheet."
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"name": "gog_sheets_table_get",
|
|
241
|
+
"description": "Get a Google Sheets table by table ID."
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"name": "gog_sheets_table_create",
|
|
245
|
+
"description": "Create a Google Sheets table over a range with typed columns."
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"name": "gog_sheets_table_append",
|
|
249
|
+
"description": "Append data rows to a table."
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
"name": "gog_sheets_table_clear",
|
|
253
|
+
"description": "Clear all data rows from a table."
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"name": "gog_sheets_table_delete",
|
|
257
|
+
"description": "Delete a table by table ID."
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
"name": "gog_sheets_banding_list",
|
|
261
|
+
"description": "List alternating-color banded ranges."
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"name": "gog_sheets_banding_set",
|
|
265
|
+
"description": "Apply alternating colors to a range."
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
"name": "gog_sheets_banding_clear",
|
|
269
|
+
"description": "Remove alternating-color banding by ID or for a whole sheet."
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"name": "gog_sheets_conditional_format_list",
|
|
273
|
+
"description": "List conditional formatting rules."
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
"name": "gog_sheets_conditional_format_add",
|
|
277
|
+
"description": "Add a conditional formatting rule to a range."
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
"name": "gog_sheets_conditional_format_clear",
|
|
281
|
+
"description": "Remove conditional formatting rules from a sheet."
|
|
210
282
|
}
|
|
211
283
|
],
|
|
212
284
|
"compatibility": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gogcli-mcp-sheets",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.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.4.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "gogcli-mcp-sheets",
|
|
15
|
-
"version": "2.
|
|
15
|
+
"version": "2.4.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
|
@@ -178,9 +178,10 @@ export function registerExtraSheetsTools(server: McpServer): void {
|
|
|
178
178
|
start: z.number().describe('Start index (0-based)'),
|
|
179
179
|
count: z.number().optional().describe('Number of rows/columns to insert (default: 1)'),
|
|
180
180
|
after: z.boolean().optional().describe('Insert after the start index instead of before. With after:true the new dimension lands at start+1, leaving the existing dimension at start untouched.'),
|
|
181
|
+
inheritFromBefore: z.boolean().optional().describe('Inherit number format / styling from the row/column before the insertion. Defaults to true with after:true, false otherwise; pass false to inherit from the row/column after the insertion instead. Cannot inherit from before when inserting at the first row/column.'),
|
|
181
182
|
account: accountParam,
|
|
182
183
|
},
|
|
183
|
-
}, async ({ spreadsheetId, sheet, dimension, start, count, after, account }) => {
|
|
184
|
+
}, async ({ spreadsheetId, sheet, dimension, start, count, after, inheritFromBefore, account }) => {
|
|
184
185
|
// The underlying `gog sheets insert` treats `start` as 1-based with `--after` shifting +1.
|
|
185
186
|
// To honor our 0-based `start` and make `after:true` actually shift the insertion to start+1
|
|
186
187
|
// (issue #42), we send `start+1` when `after:true`. With after:false the CLI's `start-1`
|
|
@@ -189,6 +190,25 @@ export function registerExtraSheetsTools(server: McpServer): void {
|
|
|
189
190
|
const args = ['sheets', 'insert', spreadsheetId, sheet, dimension, String(effectiveStart)];
|
|
190
191
|
if (count !== undefined) args.push(`--count=${count}`);
|
|
191
192
|
if (after) args.push('--after');
|
|
193
|
+
if (inheritFromBefore !== undefined) args.push(`--inherit-from-before=${inheritFromBefore}`);
|
|
194
|
+
return runOrDiagnose(args, { account });
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
server.registerTool('gog_sheets_copy_paste', {
|
|
198
|
+
description: 'Copy a source range\'s values/formulas/format to a destination range via the Sheets CopyPasteRequest. A destination larger than the source tiles the source to fill it — the canonical way to fill formulas down or across with relative references adjusted (aliases: fill, copy-range). Use type to control what is pasted (NORMAL pastes everything; FORMULA fills formulas with adjusted references; VALUES/FORMAT/etc. paste only that aspect).',
|
|
199
|
+
annotations: { destructiveHint: true },
|
|
200
|
+
inputSchema: {
|
|
201
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
202
|
+
source: z.string().describe('Source range (e.g. Sheet1!A2:H71)'),
|
|
203
|
+
dest: z.string().describe('Destination range (e.g. Sheet1!A2:H120). Larger than the source tiles/fills it.'),
|
|
204
|
+
type: z.enum(['NORMAL', 'VALUES', 'FORMAT', 'FORMULA', 'NO_BORDERS', 'DATA_VALIDATION', 'CONDITIONAL_FORMATTING']).optional().describe('Paste type (default: NORMAL pastes everything)'),
|
|
205
|
+
transpose: z.boolean().optional().describe('Paste transposed (swap rows and columns)'),
|
|
206
|
+
account: accountParam,
|
|
207
|
+
},
|
|
208
|
+
}, async ({ spreadsheetId, source, dest, type, transpose, account }) => {
|
|
209
|
+
const args = ['sheets', 'copy-paste', spreadsheetId, source, dest];
|
|
210
|
+
if (type) args.push(`--type=${type}`);
|
|
211
|
+
if (transpose) args.push('--transpose');
|
|
192
212
|
return runOrDiagnose(args, { account });
|
|
193
213
|
});
|
|
194
214
|
|
|
@@ -485,11 +505,11 @@ export function registerExtraSheetsTools(server: McpServer): void {
|
|
|
485
505
|
});
|
|
486
506
|
|
|
487
507
|
server.registerTool('gog_sheets_batch_update', {
|
|
488
|
-
description: 'Update values in multiple ranges with one Sheets API request. dataJson is a JSON array of {range, values} objects
|
|
508
|
+
description: 'Update values in multiple ranges with one Sheets API request. dataJson is a JSON array of {range, values} objects — pass it inline as a literal JSON string. Atomic — either all ranges update or none do. (The CLI also accepts "@/path/to/file.json", but that file is read on the gog server\'s filesystem, not the caller\'s, so inline JSON is the right form for remote MCP callers.)',
|
|
489
509
|
annotations: { destructiveHint: true },
|
|
490
510
|
inputSchema: {
|
|
491
511
|
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
492
|
-
dataJson: z.string().describe('Value ranges as JSON array,
|
|
512
|
+
dataJson: z.string().describe('Value ranges as an inline JSON array, e.g. [{"range":"Sheet1!A1:B2","values":[["a","b"]]}]. (An "@/path" form is read on the gog server filesystem, not yours — inline the JSON instead.)'),
|
|
493
513
|
input: z.enum(['RAW', 'USER_ENTERED']).optional().describe('Value input option (default: USER_ENTERED)'),
|
|
494
514
|
includeValuesInResponse: z.boolean().optional().describe('Include updated values in the response'),
|
|
495
515
|
responseRender: z.enum(['FORMATTED_VALUE', 'UNFORMATTED_VALUE', 'FORMULA']).optional().describe('Response value render option'),
|
|
@@ -520,4 +540,262 @@ export function registerExtraSheetsTools(server: McpServer): void {
|
|
|
520
540
|
{ account },
|
|
521
541
|
);
|
|
522
542
|
});
|
|
543
|
+
|
|
544
|
+
// ---- Charts (gog 0.19.0) ----
|
|
545
|
+
|
|
546
|
+
server.registerTool('gog_sheets_chart_list', {
|
|
547
|
+
description: 'List embedded charts in a spreadsheet (chartId, type, position).',
|
|
548
|
+
annotations: { readOnlyHint: true },
|
|
549
|
+
inputSchema: {
|
|
550
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
551
|
+
account: accountParam,
|
|
552
|
+
},
|
|
553
|
+
}, async ({ spreadsheetId, account }) => {
|
|
554
|
+
return runOrDiagnose(['sheets', 'chart', 'list', spreadsheetId], { account });
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
server.registerTool('gog_sheets_chart_get', {
|
|
558
|
+
description: 'Get the full definition (spec + position) of a single chart by its numeric chart ID.',
|
|
559
|
+
annotations: { readOnlyHint: true },
|
|
560
|
+
inputSchema: {
|
|
561
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
562
|
+
chartId: z.string().describe('Numeric chart ID (from gog_sheets_chart_list)'),
|
|
563
|
+
account: accountParam,
|
|
564
|
+
},
|
|
565
|
+
}, async ({ spreadsheetId, chartId, account }) => {
|
|
566
|
+
return runOrDiagnose(['sheets', 'chart', 'get', spreadsheetId, chartId], { account });
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
server.registerTool('gog_sheets_chart_create', {
|
|
570
|
+
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.',
|
|
571
|
+
inputSchema: {
|
|
572
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
573
|
+
specJson: z.string().describe('ChartSpec or EmbeddedChart JSON (inline or @file)'),
|
|
574
|
+
sheet: z.string().optional().describe('Sheet name for the anchor (resolved to sheetId)'),
|
|
575
|
+
anchor: z.string().optional().describe('Anchor cell in A1 notation (e.g. A1, E10)'),
|
|
576
|
+
width: z.number().optional().describe('Chart width in pixels (default: 600)'),
|
|
577
|
+
height: z.number().optional().describe('Chart height in pixels (default: 371)'),
|
|
578
|
+
account: accountParam,
|
|
579
|
+
},
|
|
580
|
+
}, async ({ spreadsheetId, specJson, sheet, anchor, width, height, account }) => {
|
|
581
|
+
const args = ['sheets', 'chart', 'create', spreadsheetId, `--spec-json=${specJson}`];
|
|
582
|
+
if (sheet) args.push(`--sheet=${sheet}`);
|
|
583
|
+
if (anchor) args.push(`--anchor=${anchor}`);
|
|
584
|
+
if (width !== undefined) args.push(`--width=${width}`);
|
|
585
|
+
if (height !== undefined) args.push(`--height=${height}`);
|
|
586
|
+
return runOrDiagnose(args, { account });
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
server.registerTool('gog_sheets_chart_update', {
|
|
590
|
+
description: 'Replace a chart spec by chart ID. specJson is a Sheets API ChartSpec (or full EmbeddedChart) — inline or @/path/to/file.json.',
|
|
591
|
+
annotations: { destructiveHint: true },
|
|
592
|
+
inputSchema: {
|
|
593
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
594
|
+
chartId: z.string().describe('Numeric chart ID to update'),
|
|
595
|
+
specJson: z.string().describe('ChartSpec or EmbeddedChart JSON (inline or @file)'),
|
|
596
|
+
account: accountParam,
|
|
597
|
+
},
|
|
598
|
+
}, async ({ spreadsheetId, chartId, specJson, account }) => {
|
|
599
|
+
return runOrDiagnose(
|
|
600
|
+
['sheets', 'chart', 'update', spreadsheetId, chartId, `--spec-json=${specJson}`],
|
|
601
|
+
{ account },
|
|
602
|
+
);
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
server.registerTool('gog_sheets_chart_delete', {
|
|
606
|
+
description: 'Delete a chart by its numeric chart ID.',
|
|
607
|
+
annotations: { destructiveHint: true },
|
|
608
|
+
inputSchema: {
|
|
609
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
610
|
+
chartId: z.string().describe('Numeric chart ID to delete'),
|
|
611
|
+
account: accountParam,
|
|
612
|
+
},
|
|
613
|
+
}, async ({ spreadsheetId, chartId, account }) => {
|
|
614
|
+
return runOrDiagnose(['sheets', 'chart', 'delete', spreadsheetId, chartId], { account });
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
// ---- Tables (gog 0.19.0) ----
|
|
618
|
+
|
|
619
|
+
server.registerTool('gog_sheets_table_list', {
|
|
620
|
+
description: 'List Google Sheets tables in a spreadsheet (tableId, name, range).',
|
|
621
|
+
annotations: { readOnlyHint: true },
|
|
622
|
+
inputSchema: {
|
|
623
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
624
|
+
account: accountParam,
|
|
625
|
+
},
|
|
626
|
+
}, async ({ spreadsheetId, account }) => {
|
|
627
|
+
return runOrDiagnose(['sheets', 'table', 'list', spreadsheetId], { account });
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
server.registerTool('gog_sheets_table_get', {
|
|
631
|
+
description: 'Get a single Google Sheets table (definition + columns) by its table ID.',
|
|
632
|
+
annotations: { readOnlyHint: true },
|
|
633
|
+
inputSchema: {
|
|
634
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
635
|
+
tableId: z.string().describe('Table ID (from gog_sheets_table_list)'),
|
|
636
|
+
account: accountParam,
|
|
637
|
+
},
|
|
638
|
+
}, async ({ spreadsheetId, tableId, account }) => {
|
|
639
|
+
return runOrDiagnose(['sheets', 'table', 'get', spreadsheetId, tableId], { account });
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
server.registerTool('gog_sheets_table_create', {
|
|
643
|
+
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.',
|
|
644
|
+
inputSchema: {
|
|
645
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
646
|
+
range: z.string().describe('Range the table covers (e.g. Sheet1!A1:D20)'),
|
|
647
|
+
name: z.string().describe('Table name'),
|
|
648
|
+
columnsJson: z.string().describe('Column definitions as JSON array or @file (columnName + optional columnType)'),
|
|
649
|
+
account: accountParam,
|
|
650
|
+
},
|
|
651
|
+
}, async ({ spreadsheetId, range, name, columnsJson, account }) => {
|
|
652
|
+
return runOrDiagnose(
|
|
653
|
+
['sheets', 'table', 'create', spreadsheetId, range, `--name=${name}`, `--columns-json=${columnsJson}`],
|
|
654
|
+
{ account },
|
|
655
|
+
);
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
server.registerTool('gog_sheets_table_append', {
|
|
659
|
+
description: 'Append data rows to a table. valuesJson is a JSON 2D array of row values.',
|
|
660
|
+
inputSchema: {
|
|
661
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
662
|
+
tableId: z.string().describe('Table ID to append to'),
|
|
663
|
+
valuesJson: z.string().describe('Values as JSON 2D array (e.g. [["a",1],["b",2]])'),
|
|
664
|
+
input: z.enum(['RAW', 'USER_ENTERED']).optional().describe('Value input option (default: USER_ENTERED)'),
|
|
665
|
+
account: accountParam,
|
|
666
|
+
},
|
|
667
|
+
}, async ({ spreadsheetId, tableId, valuesJson, input, account }) => {
|
|
668
|
+
const args = ['sheets', 'table', 'append', spreadsheetId, tableId, `--values-json=${valuesJson}`];
|
|
669
|
+
if (input) args.push(`--input=${input}`);
|
|
670
|
+
return runOrDiagnose(args, { account });
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
server.registerTool('gog_sheets_table_clear', {
|
|
674
|
+
description: 'Clear all data rows from a table (keeps the table and its columns).',
|
|
675
|
+
annotations: { destructiveHint: true },
|
|
676
|
+
inputSchema: {
|
|
677
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
678
|
+
tableId: z.string().describe('Table ID to clear'),
|
|
679
|
+
account: accountParam,
|
|
680
|
+
},
|
|
681
|
+
}, async ({ spreadsheetId, tableId, account }) => {
|
|
682
|
+
return runOrDiagnose(['sheets', 'table', 'clear', spreadsheetId, tableId], { account });
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
server.registerTool('gog_sheets_table_delete', {
|
|
686
|
+
description: 'Delete a table by its table ID.',
|
|
687
|
+
annotations: { destructiveHint: true },
|
|
688
|
+
inputSchema: {
|
|
689
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
690
|
+
tableId: z.string().describe('Table ID to delete'),
|
|
691
|
+
account: accountParam,
|
|
692
|
+
},
|
|
693
|
+
}, async ({ spreadsheetId, tableId, account }) => {
|
|
694
|
+
return runOrDiagnose(['sheets', 'table', 'delete', spreadsheetId, tableId], { account });
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
// ---- Banding / alternating colors (gog 0.19.0) ----
|
|
698
|
+
|
|
699
|
+
server.registerTool('gog_sheets_banding_list', {
|
|
700
|
+
description: 'List alternating-color banded ranges. Optionally scope to a single sheet.',
|
|
701
|
+
annotations: { readOnlyHint: true },
|
|
702
|
+
inputSchema: {
|
|
703
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
704
|
+
sheet: z.string().optional().describe('Only list banding from this sheet'),
|
|
705
|
+
account: accountParam,
|
|
706
|
+
},
|
|
707
|
+
}, async ({ spreadsheetId, sheet, account }) => {
|
|
708
|
+
const args = ['sheets', 'banding', 'list', spreadsheetId];
|
|
709
|
+
if (sheet) args.push(`--sheet=${sheet}`);
|
|
710
|
+
return runOrDiagnose(args, { account });
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
server.registerTool('gog_sheets_banding_set', {
|
|
714
|
+
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.',
|
|
715
|
+
inputSchema: {
|
|
716
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
717
|
+
range: z.string().describe('Range to band (e.g. Sheet1!A1:D20)'),
|
|
718
|
+
rowPropertiesJson: z.string().optional().describe('BandingProperties JSON for row colors'),
|
|
719
|
+
columnPropertiesJson: z.string().optional().describe('BandingProperties JSON for column colors'),
|
|
720
|
+
account: accountParam,
|
|
721
|
+
},
|
|
722
|
+
}, async ({ spreadsheetId, range, rowPropertiesJson, columnPropertiesJson, account }) => {
|
|
723
|
+
const args = ['sheets', 'banding', 'set', spreadsheetId, range];
|
|
724
|
+
if (rowPropertiesJson) args.push(`--row-properties-json=${rowPropertiesJson}`);
|
|
725
|
+
if (columnPropertiesJson) args.push(`--column-properties-json=${columnPropertiesJson}`);
|
|
726
|
+
return runOrDiagnose(args, { account });
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
server.registerTool('gog_sheets_banding_clear', {
|
|
730
|
+
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.',
|
|
731
|
+
annotations: { destructiveHint: true },
|
|
732
|
+
inputSchema: {
|
|
733
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
734
|
+
id: z.number().optional().describe('Banded range ID to remove'),
|
|
735
|
+
all: z.boolean().optional().describe('Remove all banding from the sheet (requires sheet)'),
|
|
736
|
+
sheet: z.string().optional().describe('Sheet name (used with all:true)'),
|
|
737
|
+
account: accountParam,
|
|
738
|
+
},
|
|
739
|
+
}, async ({ spreadsheetId, id, all, sheet, account }) => {
|
|
740
|
+
const args = ['sheets', 'banding', 'clear', spreadsheetId];
|
|
741
|
+
if (id !== undefined) args.push(`--id=${id}`);
|
|
742
|
+
if (all) args.push('--all');
|
|
743
|
+
if (sheet) args.push(`--sheet=${sheet}`);
|
|
744
|
+
return runOrDiagnose(args, { account });
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
// ---- Conditional formatting (gog 0.19.0) ----
|
|
748
|
+
|
|
749
|
+
server.registerTool('gog_sheets_conditional_format_list', {
|
|
750
|
+
description: 'List conditional formatting rules. Optionally scope to a single sheet.',
|
|
751
|
+
annotations: { readOnlyHint: true },
|
|
752
|
+
inputSchema: {
|
|
753
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
754
|
+
sheet: z.string().optional().describe('Only list rules from this sheet'),
|
|
755
|
+
account: accountParam,
|
|
756
|
+
},
|
|
757
|
+
}, async ({ spreadsheetId, sheet, account }) => {
|
|
758
|
+
const args = ['sheets', 'conditional-format', 'list', spreadsheetId];
|
|
759
|
+
if (sheet) args.push(`--sheet=${sheet}`);
|
|
760
|
+
return runOrDiagnose(args, { account });
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
server.registerTool('gog_sheets_conditional_format_add', {
|
|
764
|
+
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).',
|
|
765
|
+
inputSchema: {
|
|
766
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
767
|
+
range: z.string().describe('Range the rule applies to (e.g. Sheet1!A1:A100)'),
|
|
768
|
+
type: z.enum([
|
|
769
|
+
'text-eq', 'text-contains', 'text-starts-with', 'text-ends-with',
|
|
770
|
+
'number-eq', 'number-gt', 'number-gte', 'number-lt', 'number-lte',
|
|
771
|
+
'blank', 'not-blank', 'custom-formula',
|
|
772
|
+
]).describe('Rule type'),
|
|
773
|
+
formatJson: z.string().describe('CellFormat JSON to apply when the condition matches (inline or @file)'),
|
|
774
|
+
expr: z.string().optional().describe('Expression value or custom formula (omit for blank/not-blank)'),
|
|
775
|
+
formatFields: z.string().optional().describe('Format field mask for force-sending zero/false fields (e.g. backgroundColor,textFormat.bold)'),
|
|
776
|
+
account: accountParam,
|
|
777
|
+
},
|
|
778
|
+
}, async ({ spreadsheetId, range, type, formatJson, expr, formatFields, account }) => {
|
|
779
|
+
const args = ['sheets', 'conditional-format', 'add', spreadsheetId, range, `--type=${type}`, `--format-json=${formatJson}`];
|
|
780
|
+
if (expr !== undefined) args.push(`--expr=${expr}`);
|
|
781
|
+
if (formatFields) args.push(`--format-fields=${formatFields}`);
|
|
782
|
+
return runOrDiagnose(args, { account });
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
server.registerTool('gog_sheets_conditional_format_clear', {
|
|
786
|
+
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.',
|
|
787
|
+
annotations: { destructiveHint: true },
|
|
788
|
+
inputSchema: {
|
|
789
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
790
|
+
sheet: z.string().describe('Sheet name to clear rules from'),
|
|
791
|
+
index: z.number().optional().describe('0-based rule index to remove'),
|
|
792
|
+
all: z.boolean().optional().describe('Remove all conditional formatting rules from the sheet'),
|
|
793
|
+
account: accountParam,
|
|
794
|
+
},
|
|
795
|
+
}, async ({ spreadsheetId, sheet, index, all, account }) => {
|
|
796
|
+
const args = ['sheets', 'conditional-format', 'clear', spreadsheetId, `--sheet=${sheet}`];
|
|
797
|
+
if (index !== undefined) args.push(`--index=${index}`);
|
|
798
|
+
if (all) args.push('--all');
|
|
799
|
+
return runOrDiagnose(args, { account });
|
|
800
|
+
});
|
|
523
801
|
}
|
|
@@ -149,6 +149,43 @@ describe('gog_sheets_insert', () => {
|
|
|
149
149
|
await handlers.get('gog_sheets_insert')!({ spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 0, after: false });
|
|
150
150
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '0'], { account: undefined });
|
|
151
151
|
});
|
|
152
|
+
|
|
153
|
+
it('passes --inherit-from-before=true when set', async () => {
|
|
154
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
155
|
+
const handlers = setupHandlers();
|
|
156
|
+
await handlers.get('gog_sheets_insert')!({ spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 5, after: true, inheritFromBefore: true });
|
|
157
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '6', '--after', '--inherit-from-before=true'], { account: undefined });
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('passes --inherit-from-before=false to inherit from the following neighbor', async () => {
|
|
161
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
162
|
+
const handlers = setupHandlers();
|
|
163
|
+
await handlers.get('gog_sheets_insert')!({ spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 5, after: true, inheritFromBefore: false });
|
|
164
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '6', '--after', '--inherit-from-before=false'], { account: undefined });
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
describe('gog_sheets_copy_paste', () => {
|
|
169
|
+
it('calls runOrDiagnose with required source/dest', async () => {
|
|
170
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
171
|
+
const handlers = setupHandlers();
|
|
172
|
+
await handlers.get('gog_sheets_copy_paste')!({ spreadsheetId: 'sid', source: 'Sheet1!A2:H71', dest: 'Sheet1!A2:H120' });
|
|
173
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy-paste', 'sid', 'Sheet1!A2:H71', 'Sheet1!A2:H120'], { account: undefined });
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('appends --type and --transpose when provided', async () => {
|
|
177
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
178
|
+
const handlers = setupHandlers();
|
|
179
|
+
await handlers.get('gog_sheets_copy_paste')!({ spreadsheetId: 'sid', source: 'A1:B2', dest: 'D1:E2', type: 'FORMULA', transpose: true });
|
|
180
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy-paste', 'sid', 'A1:B2', 'D1:E2', '--type=FORMULA', '--transpose'], { account: undefined });
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('forwards account', async () => {
|
|
184
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
185
|
+
const handlers = setupHandlers();
|
|
186
|
+
await handlers.get('gog_sheets_copy_paste')!({ spreadsheetId: 'sid', source: 'A1', dest: 'B1', account: 'a@b.com' });
|
|
187
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy-paste', 'sid', 'A1', 'B1'], { account: 'a@b.com' });
|
|
188
|
+
});
|
|
152
189
|
});
|
|
153
190
|
|
|
154
191
|
// 8. merge
|
|
@@ -487,6 +524,18 @@ describe('gog_sheets_number_format', () => {
|
|
|
487
524
|
expect(result.content[0].text).toMatch(/warning/i);
|
|
488
525
|
});
|
|
489
526
|
|
|
527
|
+
it('does not warn when every cell is null or empty (no small integers seen)', async () => {
|
|
528
|
+
const handlers = setupHandlers();
|
|
529
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
530
|
+
if (args[1] === 'get') {
|
|
531
|
+
return toText(JSON.stringify({ range: 'A1:A3', values: [[null], [''], [null]] }));
|
|
532
|
+
}
|
|
533
|
+
return toText('{"ok":true}');
|
|
534
|
+
});
|
|
535
|
+
const result = await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE' });
|
|
536
|
+
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
537
|
+
});
|
|
538
|
+
|
|
490
539
|
it('proceeds with format when peek fails (does not block)', async () => {
|
|
491
540
|
const handlers = setupHandlers();
|
|
492
541
|
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
@@ -775,3 +824,325 @@ describe('gog_sheets_reorder_tab', () => {
|
|
|
775
824
|
);
|
|
776
825
|
});
|
|
777
826
|
});
|
|
827
|
+
|
|
828
|
+
// 25. chart list (gog 0.19.0)
|
|
829
|
+
describe('gog_sheets_chart_list', () => {
|
|
830
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
831
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
832
|
+
const handlers = setupHandlers();
|
|
833
|
+
await handlers.get('gog_sheets_chart_list')!({ spreadsheetId: 'sid' });
|
|
834
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'list', 'sid'], { account: undefined });
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
it('forwards account', async () => {
|
|
838
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
839
|
+
const handlers = setupHandlers();
|
|
840
|
+
await handlers.get('gog_sheets_chart_list')!({ spreadsheetId: 'sid', account: 'a@b.com' });
|
|
841
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'list', 'sid'], { account: 'a@b.com' });
|
|
842
|
+
});
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
// 26. chart get
|
|
846
|
+
describe('gog_sheets_chart_get', () => {
|
|
847
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
848
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
849
|
+
const handlers = setupHandlers();
|
|
850
|
+
await handlers.get('gog_sheets_chart_get')!({ spreadsheetId: 'sid', chartId: '12345' });
|
|
851
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'get', 'sid', '12345'], { account: undefined });
|
|
852
|
+
});
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
// 27. chart create
|
|
856
|
+
describe('gog_sheets_chart_create', () => {
|
|
857
|
+
it('calls runOrDiagnose with only --spec-json', async () => {
|
|
858
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
859
|
+
const handlers = setupHandlers();
|
|
860
|
+
const spec = '{"basicChart":{"chartType":"COLUMN"}}';
|
|
861
|
+
await handlers.get('gog_sheets_chart_create')!({ spreadsheetId: 'sid', specJson: spec });
|
|
862
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
863
|
+
['sheets', 'chart', 'create', 'sid', `--spec-json=${spec}`],
|
|
864
|
+
{ account: undefined },
|
|
865
|
+
);
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
it('includes --sheet, --anchor, --width, --height when provided', async () => {
|
|
869
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
870
|
+
const handlers = setupHandlers();
|
|
871
|
+
await handlers.get('gog_sheets_chart_create')!({
|
|
872
|
+
spreadsheetId: 'sid', specJson: '{}', sheet: 'Data', anchor: 'E10', width: 800, height: 400,
|
|
873
|
+
});
|
|
874
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
875
|
+
['sheets', 'chart', 'create', 'sid', '--spec-json={}', '--sheet=Data', '--anchor=E10', '--width=800', '--height=400'],
|
|
876
|
+
{ account: undefined },
|
|
877
|
+
);
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
it('includes --width=0 and --height=0 when zero', async () => {
|
|
881
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
882
|
+
const handlers = setupHandlers();
|
|
883
|
+
await handlers.get('gog_sheets_chart_create')!({ spreadsheetId: 'sid', specJson: '{}', width: 0, height: 0 });
|
|
884
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
885
|
+
['sheets', 'chart', 'create', 'sid', '--spec-json={}', '--width=0', '--height=0'],
|
|
886
|
+
{ account: undefined },
|
|
887
|
+
);
|
|
888
|
+
});
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
// 28. chart update
|
|
892
|
+
describe('gog_sheets_chart_update', () => {
|
|
893
|
+
it('calls runOrDiagnose with chartId and --spec-json', async () => {
|
|
894
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
895
|
+
const handlers = setupHandlers();
|
|
896
|
+
await handlers.get('gog_sheets_chart_update')!({ spreadsheetId: 'sid', chartId: '99', specJson: '{}' });
|
|
897
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
898
|
+
['sheets', 'chart', 'update', 'sid', '99', '--spec-json={}'],
|
|
899
|
+
{ account: undefined },
|
|
900
|
+
);
|
|
901
|
+
});
|
|
902
|
+
});
|
|
903
|
+
|
|
904
|
+
// 29. chart delete
|
|
905
|
+
describe('gog_sheets_chart_delete', () => {
|
|
906
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
907
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
908
|
+
const handlers = setupHandlers();
|
|
909
|
+
await handlers.get('gog_sheets_chart_delete')!({ spreadsheetId: 'sid', chartId: '7' });
|
|
910
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'delete', 'sid', '7'], { account: undefined });
|
|
911
|
+
});
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
// 30. table list
|
|
915
|
+
describe('gog_sheets_table_list', () => {
|
|
916
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
917
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
918
|
+
const handlers = setupHandlers();
|
|
919
|
+
await handlers.get('gog_sheets_table_list')!({ spreadsheetId: 'sid' });
|
|
920
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'list', 'sid'], { account: undefined });
|
|
921
|
+
});
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
// 31. table get
|
|
925
|
+
describe('gog_sheets_table_get', () => {
|
|
926
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
927
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
928
|
+
const handlers = setupHandlers();
|
|
929
|
+
await handlers.get('gog_sheets_table_get')!({ spreadsheetId: 'sid', tableId: 't1' });
|
|
930
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'get', 'sid', 't1'], { account: undefined });
|
|
931
|
+
});
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
// 32. table create
|
|
935
|
+
describe('gog_sheets_table_create', () => {
|
|
936
|
+
it('calls runOrDiagnose with range, --name, --columns-json', async () => {
|
|
937
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
938
|
+
const handlers = setupHandlers();
|
|
939
|
+
const cols = '[{"columnName":"Name","columnType":"TEXT"}]';
|
|
940
|
+
await handlers.get('gog_sheets_table_create')!({ spreadsheetId: 'sid', range: 'Sheet1!A1:B10', name: 'People', columnsJson: cols });
|
|
941
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
942
|
+
['sheets', 'table', 'create', 'sid', 'Sheet1!A1:B10', '--name=People', `--columns-json=${cols}`],
|
|
943
|
+
{ account: undefined },
|
|
944
|
+
);
|
|
945
|
+
});
|
|
946
|
+
});
|
|
947
|
+
|
|
948
|
+
// 33. table append
|
|
949
|
+
describe('gog_sheets_table_append', () => {
|
|
950
|
+
it('calls runOrDiagnose with --values-json (no --input)', async () => {
|
|
951
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
952
|
+
const handlers = setupHandlers();
|
|
953
|
+
await handlers.get('gog_sheets_table_append')!({ spreadsheetId: 'sid', tableId: 't1', valuesJson: '[["a",1]]' });
|
|
954
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
955
|
+
['sheets', 'table', 'append', 'sid', 't1', '--values-json=[["a",1]]'],
|
|
956
|
+
{ account: undefined },
|
|
957
|
+
);
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
it('includes --input when provided', async () => {
|
|
961
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
962
|
+
const handlers = setupHandlers();
|
|
963
|
+
await handlers.get('gog_sheets_table_append')!({ spreadsheetId: 'sid', tableId: 't1', valuesJson: '[]', input: 'RAW' });
|
|
964
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
965
|
+
['sheets', 'table', 'append', 'sid', 't1', '--values-json=[]', '--input=RAW'],
|
|
966
|
+
{ account: undefined },
|
|
967
|
+
);
|
|
968
|
+
});
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
// 34. table clear
|
|
972
|
+
describe('gog_sheets_table_clear', () => {
|
|
973
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
974
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
975
|
+
const handlers = setupHandlers();
|
|
976
|
+
await handlers.get('gog_sheets_table_clear')!({ spreadsheetId: 'sid', tableId: 't1' });
|
|
977
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'clear', 'sid', 't1'], { account: undefined });
|
|
978
|
+
});
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
// 35. table delete
|
|
982
|
+
describe('gog_sheets_table_delete', () => {
|
|
983
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
984
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
985
|
+
const handlers = setupHandlers();
|
|
986
|
+
await handlers.get('gog_sheets_table_delete')!({ spreadsheetId: 'sid', tableId: 't1' });
|
|
987
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'delete', 'sid', 't1'], { account: undefined });
|
|
988
|
+
});
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
// 36. banding list
|
|
992
|
+
describe('gog_sheets_banding_list', () => {
|
|
993
|
+
it('calls runOrDiagnose with no --sheet', async () => {
|
|
994
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
995
|
+
const handlers = setupHandlers();
|
|
996
|
+
await handlers.get('gog_sheets_banding_list')!({ spreadsheetId: 'sid' });
|
|
997
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'list', 'sid'], { account: undefined });
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
it('includes --sheet when provided', async () => {
|
|
1001
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1002
|
+
const handlers = setupHandlers();
|
|
1003
|
+
await handlers.get('gog_sheets_banding_list')!({ spreadsheetId: 'sid', sheet: 'Data' });
|
|
1004
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'list', 'sid', '--sheet=Data'], { account: undefined });
|
|
1005
|
+
});
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
// 37. banding set
|
|
1009
|
+
describe('gog_sheets_banding_set', () => {
|
|
1010
|
+
it('calls runOrDiagnose with range only', async () => {
|
|
1011
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1012
|
+
const handlers = setupHandlers();
|
|
1013
|
+
await handlers.get('gog_sheets_banding_set')!({ spreadsheetId: 'sid', range: 'Sheet1!A1:D20' });
|
|
1014
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'set', 'sid', 'Sheet1!A1:D20'], { account: undefined });
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
it('includes --row-properties-json and --column-properties-json when provided', async () => {
|
|
1018
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1019
|
+
const handlers = setupHandlers();
|
|
1020
|
+
await handlers.get('gog_sheets_banding_set')!({
|
|
1021
|
+
spreadsheetId: 'sid', range: 'A1:D20',
|
|
1022
|
+
rowPropertiesJson: '{"firstBandColor":{}}',
|
|
1023
|
+
columnPropertiesJson: '{"secondBandColor":{}}',
|
|
1024
|
+
});
|
|
1025
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1026
|
+
['sheets', 'banding', 'set', 'sid', 'A1:D20', '--row-properties-json={"firstBandColor":{}}', '--column-properties-json={"secondBandColor":{}}'],
|
|
1027
|
+
{ account: undefined },
|
|
1028
|
+
);
|
|
1029
|
+
});
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
// 38. banding clear
|
|
1033
|
+
describe('gog_sheets_banding_clear', () => {
|
|
1034
|
+
it('calls runOrDiagnose with no optional flags', async () => {
|
|
1035
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1036
|
+
const handlers = setupHandlers();
|
|
1037
|
+
await handlers.get('gog_sheets_banding_clear')!({ spreadsheetId: 'sid' });
|
|
1038
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid'], { account: undefined });
|
|
1039
|
+
});
|
|
1040
|
+
|
|
1041
|
+
it('includes --id when provided (including 0)', async () => {
|
|
1042
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1043
|
+
const handlers = setupHandlers();
|
|
1044
|
+
await handlers.get('gog_sheets_banding_clear')!({ spreadsheetId: 'sid', id: 0 });
|
|
1045
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--id=0'], { account: undefined });
|
|
1046
|
+
});
|
|
1047
|
+
|
|
1048
|
+
it('includes --all and --sheet when clearing a whole sheet', async () => {
|
|
1049
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1050
|
+
const handlers = setupHandlers();
|
|
1051
|
+
await handlers.get('gog_sheets_banding_clear')!({ spreadsheetId: 'sid', all: true, sheet: 'Data' });
|
|
1052
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--all', '--sheet=Data'], { account: undefined });
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
it('omits --all when false', async () => {
|
|
1056
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1057
|
+
const handlers = setupHandlers();
|
|
1058
|
+
await handlers.get('gog_sheets_banding_clear')!({ spreadsheetId: 'sid', all: false, id: 5 });
|
|
1059
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--id=5'], { account: undefined });
|
|
1060
|
+
});
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
// 39. conditional-format list
|
|
1064
|
+
describe('gog_sheets_conditional_format_list', () => {
|
|
1065
|
+
it('calls runOrDiagnose with no --sheet', async () => {
|
|
1066
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1067
|
+
const handlers = setupHandlers();
|
|
1068
|
+
await handlers.get('gog_sheets_conditional_format_list')!({ spreadsheetId: 'sid' });
|
|
1069
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'list', 'sid'], { account: undefined });
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
it('includes --sheet when provided', async () => {
|
|
1073
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1074
|
+
const handlers = setupHandlers();
|
|
1075
|
+
await handlers.get('gog_sheets_conditional_format_list')!({ spreadsheetId: 'sid', sheet: 'Data' });
|
|
1076
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'list', 'sid', '--sheet=Data'], { account: undefined });
|
|
1077
|
+
});
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
// 40. conditional-format add
|
|
1081
|
+
describe('gog_sheets_conditional_format_add', () => {
|
|
1082
|
+
it('calls runOrDiagnose with type and --format-json (no expr/fields)', async () => {
|
|
1083
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1084
|
+
const handlers = setupHandlers();
|
|
1085
|
+
await handlers.get('gog_sheets_conditional_format_add')!({
|
|
1086
|
+
spreadsheetId: 'sid', range: 'A1:A100', type: 'not-blank', formatJson: '{"backgroundColor":{}}',
|
|
1087
|
+
});
|
|
1088
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1089
|
+
['sheets', 'conditional-format', 'add', 'sid', 'A1:A100', '--type=not-blank', '--format-json={"backgroundColor":{}}'],
|
|
1090
|
+
{ account: undefined },
|
|
1091
|
+
);
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
it('includes --expr and --format-fields when provided', async () => {
|
|
1095
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1096
|
+
const handlers = setupHandlers();
|
|
1097
|
+
await handlers.get('gog_sheets_conditional_format_add')!({
|
|
1098
|
+
spreadsheetId: 'sid', range: 'B1:B50', type: 'number-gt', expr: '100', formatJson: '{}', formatFields: 'backgroundColor,textFormat.bold',
|
|
1099
|
+
});
|
|
1100
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1101
|
+
['sheets', 'conditional-format', 'add', 'sid', 'B1:B50', '--type=number-gt', '--format-json={}', '--expr=100', '--format-fields=backgroundColor,textFormat.bold'],
|
|
1102
|
+
{ account: undefined },
|
|
1103
|
+
);
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
it('includes --expr when it is an empty string', async () => {
|
|
1107
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1108
|
+
const handlers = setupHandlers();
|
|
1109
|
+
await handlers.get('gog_sheets_conditional_format_add')!({
|
|
1110
|
+
spreadsheetId: 'sid', range: 'A1', type: 'text-eq', expr: '', formatJson: '{}',
|
|
1111
|
+
});
|
|
1112
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1113
|
+
['sheets', 'conditional-format', 'add', 'sid', 'A1', '--type=text-eq', '--format-json={}', '--expr='],
|
|
1114
|
+
{ account: undefined },
|
|
1115
|
+
);
|
|
1116
|
+
});
|
|
1117
|
+
});
|
|
1118
|
+
|
|
1119
|
+
// 41. conditional-format clear
|
|
1120
|
+
describe('gog_sheets_conditional_format_clear', () => {
|
|
1121
|
+
it('calls runOrDiagnose with --sheet only', async () => {
|
|
1122
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1123
|
+
const handlers = setupHandlers();
|
|
1124
|
+
await handlers.get('gog_sheets_conditional_format_clear')!({ spreadsheetId: 'sid', sheet: 'Data' });
|
|
1125
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data'], { account: undefined });
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1128
|
+
it('includes --index when provided (including 0)', async () => {
|
|
1129
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1130
|
+
const handlers = setupHandlers();
|
|
1131
|
+
await handlers.get('gog_sheets_conditional_format_clear')!({ spreadsheetId: 'sid', sheet: 'Data', index: 0 });
|
|
1132
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--index=0'], { account: undefined });
|
|
1133
|
+
});
|
|
1134
|
+
|
|
1135
|
+
it('includes --all when true', async () => {
|
|
1136
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1137
|
+
const handlers = setupHandlers();
|
|
1138
|
+
await handlers.get('gog_sheets_conditional_format_clear')!({ spreadsheetId: 'sid', sheet: 'Data', all: true });
|
|
1139
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--all'], { account: undefined });
|
|
1140
|
+
});
|
|
1141
|
+
|
|
1142
|
+
it('omits --all when false', async () => {
|
|
1143
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1144
|
+
const handlers = setupHandlers();
|
|
1145
|
+
await handlers.get('gog_sheets_conditional_format_clear')!({ spreadsheetId: 'sid', sheet: 'Data', all: false, index: 2 });
|
|
1146
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--index=2'], { account: undefined });
|
|
1147
|
+
});
|
|
1148
|
+
});
|