mcp-gsheets 1.8.0 → 1.9.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.
Files changed (3) hide show
  1. package/README.md +11 -0
  2. package/dist/index.js +483 -1
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ <!-- mcp-name: io.github.freema/mcp-gsheets -->
2
+
1
3
  # MCP Google Sheets Server
2
4
 
3
5
  <a href="https://glama.ai/mcp/servers/@freema/mcp-gsheets">
@@ -358,6 +360,15 @@ npm run dev # Watch mode with auto-reload
358
360
  | `sheets_unmerge_cells` | Unmerge previously merged cells in a range | `spreadsheetId`, `range` |
359
361
  | `sheets_add_conditional_formatting` | Add a conditional formatting rule (gradient or boolean) to a range | `spreadsheetId`, `range`, `rule` |
360
362
 
363
+ ### Native Tables
364
+
365
+ | Tool | Description | Key Parameters |
366
+ |------|-------------|----------------|
367
+ | `sheets_add_table` | Create a native Google Sheets table with typed columns and optional dropdown values | `spreadsheetId`, `sheetName`, `range`, `name`, `columns` |
368
+ | `sheets_update_table` | Update an existing native Google Sheets table by table ID using an explicit field mask | `spreadsheetId`, `tableId`, `fields`, `name`, `range`, `columns` |
369
+ | `sheets_delete_table` | Delete a native Google Sheets table by table ID | `spreadsheetId`, `tableId` |
370
+ | `sheets_get_tables` | Read native tables for a spreadsheet or a specific sheet | `spreadsheetId`, `sheetName` |
371
+
361
372
  ### Charts
362
373
 
363
374
  | Tool | Description | Key Parameters |
package/dist/index.js CHANGED
@@ -18319,7 +18319,7 @@ async function getSheetId(sheets, spreadsheetId, sheetName) {
18319
18319
  const sheetsData = response.data.sheets || [];
18320
18320
  if (sheetName) {
18321
18321
  const sheet = sheetsData.find((s) => s.properties?.title === sheetName);
18322
- if (!sheet?.properties?.sheetId) {
18322
+ if (sheet?.properties?.sheetId === void 0 || sheet.properties.sheetId === null) {
18323
18323
  const availableSheets = sheetsData.map((s) => s.properties?.title).filter((title) => title).join(", ");
18324
18324
  throw new Error(`Sheet "${sheetName}" not found. Available sheets: ${availableSheets}`);
18325
18325
  }
@@ -20130,6 +20130,480 @@ async function addConditionalFormattingHandler(input) {
20130
20130
  }
20131
20131
  }
20132
20132
 
20133
+ // src/utils/table-helpers.ts
20134
+ var TABLE_COLUMN_TYPES = [
20135
+ "COLUMN_TYPE_UNSPECIFIED",
20136
+ "DOUBLE",
20137
+ "CURRENCY",
20138
+ "PERCENT",
20139
+ "DATE",
20140
+ "TIME",
20141
+ "DATE_TIME",
20142
+ "TEXT",
20143
+ "BOOLEAN",
20144
+ "DROPDOWN",
20145
+ "FILES_CHIP",
20146
+ "PEOPLE_CHIP",
20147
+ "FINANCE_CHIP",
20148
+ "PLACE_CHIP",
20149
+ "RATINGS_CHIP"
20150
+ ];
20151
+ function resolveTableRangeInput(sheetName, range) {
20152
+ const extracted = extractSheetName(range);
20153
+ if (extracted.sheetName && extracted.sheetName !== sheetName) {
20154
+ throw new Error(`Range sheet "${extracted.sheetName}" does not match sheetName "${sheetName}"`);
20155
+ }
20156
+ return {
20157
+ sheetName: extracted.sheetName ?? sheetName,
20158
+ range: extracted.range
20159
+ };
20160
+ }
20161
+ function buildTableColumnProperties(columns) {
20162
+ return columns.map((column, index) => {
20163
+ const columnProperties = {
20164
+ columnIndex: index,
20165
+ columnName: column.name,
20166
+ columnType: column.columnType
20167
+ };
20168
+ if (column.dropdownValues && column.dropdownValues.length > 0) {
20169
+ columnProperties.dataValidationRule = {
20170
+ condition: {
20171
+ type: "ONE_OF_LIST",
20172
+ values: column.dropdownValues.map((value) => ({ userEnteredValue: value }))
20173
+ }
20174
+ };
20175
+ }
20176
+ return columnProperties;
20177
+ });
20178
+ }
20179
+ function getRangeColumnCount(range) {
20180
+ if (range.startColumnIndex === void 0 || range.startColumnIndex === null || range.endColumnIndex === void 0 || range.endColumnIndex === null) {
20181
+ return void 0;
20182
+ }
20183
+ return range.endColumnIndex - range.startColumnIndex;
20184
+ }
20185
+ function validateTableGridRange(range, rangeText) {
20186
+ const startRow = range.startRowIndex;
20187
+ const endRow = range.endRowIndex;
20188
+ const startColumn = range.startColumnIndex;
20189
+ const endColumn = range.endColumnIndex;
20190
+ if (startRow === void 0 || startRow === null || endRow === void 0 || endRow === null || startColumn === void 0 || startColumn === null || endColumn === void 0 || endColumn === null || startRow < 0 || startColumn < 0 || endRow <= startRow || endColumn <= startColumn) {
20191
+ throw new Error(`Invalid table range: ${rangeText}. Use a rectangular A1 range like "A1:D20".`);
20192
+ }
20193
+ }
20194
+ function validateColumnCount(range, columns, rangeText) {
20195
+ const columnCount = getRangeColumnCount(range);
20196
+ if (columnCount !== void 0 && columnCount !== columns.length) {
20197
+ throw new Error(
20198
+ `Column count mismatch: range ${rangeText} spans ${columnCount} column(s), but ${columns.length} column definition(s) were provided`
20199
+ );
20200
+ }
20201
+ }
20202
+ function rangesOverlap(first, second) {
20203
+ if (first.sheetId !== void 0 && first.sheetId !== null && second.sheetId !== void 0 && second.sheetId !== null && first.sheetId !== second.sheetId) {
20204
+ return false;
20205
+ }
20206
+ const firstStartRow = first.startRowIndex ?? 0;
20207
+ const firstEndRow = first.endRowIndex ?? Number.POSITIVE_INFINITY;
20208
+ const firstStartColumn = first.startColumnIndex ?? 0;
20209
+ const firstEndColumn = first.endColumnIndex ?? Number.POSITIVE_INFINITY;
20210
+ const secondStartRow = second.startRowIndex ?? 0;
20211
+ const secondEndRow = second.endRowIndex ?? Number.POSITIVE_INFINITY;
20212
+ const secondStartColumn = second.startColumnIndex ?? 0;
20213
+ const secondEndColumn = second.endColumnIndex ?? Number.POSITIVE_INFINITY;
20214
+ return firstStartRow < secondEndRow && firstEndRow > secondStartRow && firstStartColumn < secondEndColumn && firstEndColumn > secondStartColumn;
20215
+ }
20216
+ function ensureNoOverlappingTable(tables, range, sheetName, excludeTableId) {
20217
+ const overlappingTable = tables.find(
20218
+ (table) => table.tableId !== excludeTableId && table.range && rangesOverlap(table.range, range)
20219
+ );
20220
+ if (!overlappingTable) {
20221
+ return;
20222
+ }
20223
+ const existingRange = overlappingTable.range ? gridRangeToA1(overlappingTable.range) : "unknown";
20224
+ const requestedRange = gridRangeToA1(range);
20225
+ const tableLabel = overlappingTable.name || overlappingTable.tableId || "unnamed table";
20226
+ throw new Error(
20227
+ `Table range ${sheetName}!${requestedRange} overlaps existing table "${tableLabel}" (${sheetName}!${existingRange}). Choose a non-overlapping range or update/delete the existing table.`
20228
+ );
20229
+ }
20230
+ function formatTableForResponse(table, sheetName) {
20231
+ const range = table.range ? gridRangeToA1(table.range) : null;
20232
+ return {
20233
+ tableId: table.tableId ?? null,
20234
+ name: table.name ?? null,
20235
+ range: range && sheetName ? `${sheetName}!${range}` : range,
20236
+ gridRange: table.range ?? null,
20237
+ columnProperties: table.columnProperties ?? []
20238
+ };
20239
+ }
20240
+
20241
+ // src/tools/add-table.ts
20242
+ var columnSchema = external_exports.object({
20243
+ name: external_exports.string().min(1),
20244
+ columnType: external_exports.enum(TABLE_COLUMN_TYPES),
20245
+ dropdownValues: external_exports.array(external_exports.string()).optional()
20246
+ });
20247
+ var addTableInputSchema = external_exports.object({
20248
+ spreadsheetId: external_exports.string().min(1),
20249
+ sheetName: external_exports.string().min(1),
20250
+ range: external_exports.string().min(1),
20251
+ name: external_exports.string().min(1),
20252
+ columns: external_exports.array(columnSchema).min(1)
20253
+ });
20254
+ var addTableTool = {
20255
+ name: "sheets_add_table",
20256
+ description: "Create a native Google Sheets table with typed columns and optional dropdown values",
20257
+ inputSchema: {
20258
+ type: "object",
20259
+ properties: {
20260
+ spreadsheetId: {
20261
+ type: "string",
20262
+ description: "The ID of the spreadsheet (found in the URL after /d/)"
20263
+ },
20264
+ sheetName: {
20265
+ type: "string",
20266
+ description: "Name of the sheet (tab) where the table will be created"
20267
+ },
20268
+ range: {
20269
+ type: "string",
20270
+ description: 'A1 notation range for the table, e.g. "A1:D20" or "Sheet1!A1:D20"'
20271
+ },
20272
+ name: {
20273
+ type: "string",
20274
+ description: "Unique table name within the spreadsheet"
20275
+ },
20276
+ columns: {
20277
+ type: "array",
20278
+ description: "Column definitions for the table",
20279
+ items: {
20280
+ type: "object",
20281
+ properties: {
20282
+ name: { type: "string", description: "Column name" },
20283
+ columnType: {
20284
+ type: "string",
20285
+ enum: [...TABLE_COLUMN_TYPES],
20286
+ description: "Google Sheets table column type"
20287
+ },
20288
+ dropdownValues: {
20289
+ type: "array",
20290
+ items: { type: "string" },
20291
+ description: "Allowed values for DROPDOWN columns"
20292
+ }
20293
+ },
20294
+ required: ["name", "columnType"]
20295
+ }
20296
+ }
20297
+ },
20298
+ required: ["spreadsheetId", "sheetName", "range", "name", "columns"]
20299
+ }
20300
+ };
20301
+ async function addTableHandler(input) {
20302
+ try {
20303
+ const validatedInput = addTableInputSchema.parse(input);
20304
+ const sheets = await getAuthenticatedClient();
20305
+ const { sheetName, range } = resolveTableRangeInput(
20306
+ validatedInput.sheetName,
20307
+ validatedInput.range
20308
+ );
20309
+ const sheetId = await getSheetId(sheets, validatedInput.spreadsheetId, sheetName);
20310
+ const gridRange = parseRange(range, sheetId);
20311
+ validateTableGridRange(gridRange, `${sheetName}!${range}`);
20312
+ validateColumnCount(gridRange, validatedInput.columns, `${sheetName}!${range}`);
20313
+ const metadataResponse = await sheets.spreadsheets.get({
20314
+ spreadsheetId: validatedInput.spreadsheetId,
20315
+ fields: "sheets.properties.title,sheets.properties.sheetId,sheets.tables"
20316
+ });
20317
+ const sheetData = findSheetOrThrow(metadataResponse.data.sheets ?? [], sheetName);
20318
+ ensureNoOverlappingTable(sheetData.tables ?? [], gridRange, sheetName);
20319
+ const table = {
20320
+ name: validatedInput.name,
20321
+ range: gridRange,
20322
+ columnProperties: buildTableColumnProperties(validatedInput.columns)
20323
+ };
20324
+ const response = await sheets.spreadsheets.batchUpdate({
20325
+ spreadsheetId: validatedInput.spreadsheetId,
20326
+ requestBody: {
20327
+ requests: [
20328
+ {
20329
+ addTable: {
20330
+ table
20331
+ }
20332
+ }
20333
+ ]
20334
+ }
20335
+ });
20336
+ const addedTable = response.data.replies?.[0]?.addTable?.table ?? table;
20337
+ return formatToolResponse(`Successfully created table "${validatedInput.name}"`, {
20338
+ spreadsheetId: response.data.spreadsheetId,
20339
+ table: formatTableForResponse(addedTable, sheetName)
20340
+ });
20341
+ } catch (error2) {
20342
+ return handleError(error2);
20343
+ }
20344
+ }
20345
+
20346
+ // src/tools/update-table.ts
20347
+ var columnSchema2 = external_exports.object({
20348
+ name: external_exports.string().min(1),
20349
+ columnType: external_exports.enum(TABLE_COLUMN_TYPES),
20350
+ dropdownValues: external_exports.array(external_exports.string()).optional()
20351
+ });
20352
+ var updateTableInputSchema = external_exports.object({
20353
+ spreadsheetId: external_exports.string().min(1),
20354
+ tableId: external_exports.string().min(1),
20355
+ fields: external_exports.string().min(1),
20356
+ sheetName: external_exports.string().min(1).optional(),
20357
+ range: external_exports.string().min(1).optional(),
20358
+ name: external_exports.string().min(1).optional(),
20359
+ columns: external_exports.array(columnSchema2).min(1).optional()
20360
+ }).refine((value) => value.name || value.range || value.columns, {
20361
+ message: "At least one of name, range, or columns must be provided"
20362
+ });
20363
+ var updateTableTool = {
20364
+ name: "sheets_update_table",
20365
+ description: "Update an existing native Google Sheets table by tableId",
20366
+ inputSchema: {
20367
+ type: "object",
20368
+ properties: {
20369
+ spreadsheetId: {
20370
+ type: "string",
20371
+ description: "The ID of the spreadsheet (found in the URL after /d/)"
20372
+ },
20373
+ tableId: {
20374
+ type: "string",
20375
+ description: "The ID of the table to update"
20376
+ },
20377
+ fields: {
20378
+ type: "string",
20379
+ description: 'Required field mask for the table update, e.g. "name", "range", "columnProperties", or "name,range"'
20380
+ },
20381
+ sheetName: {
20382
+ type: "string",
20383
+ description: "Name of the target sheet when updating the table range"
20384
+ },
20385
+ range: {
20386
+ type: "string",
20387
+ description: 'Optional new A1 notation range for the table, e.g. "A1:D20"'
20388
+ },
20389
+ name: {
20390
+ type: "string",
20391
+ description: "Optional new table name"
20392
+ },
20393
+ columns: {
20394
+ type: "array",
20395
+ description: "Optional replacement column definitions for the table",
20396
+ items: {
20397
+ type: "object",
20398
+ properties: {
20399
+ name: { type: "string", description: "Column name" },
20400
+ columnType: {
20401
+ type: "string",
20402
+ enum: [...TABLE_COLUMN_TYPES],
20403
+ description: "Google Sheets table column type"
20404
+ },
20405
+ dropdownValues: {
20406
+ type: "array",
20407
+ items: { type: "string" },
20408
+ description: "Allowed values for DROPDOWN columns"
20409
+ }
20410
+ },
20411
+ required: ["name", "columnType"]
20412
+ }
20413
+ }
20414
+ },
20415
+ required: ["spreadsheetId", "tableId", "fields"]
20416
+ }
20417
+ };
20418
+ async function updateTableHandler(input) {
20419
+ try {
20420
+ const validatedInput = updateTableInputSchema.parse(input);
20421
+ const sheets = await getAuthenticatedClient();
20422
+ const metadataResponse = await sheets.spreadsheets.get({
20423
+ spreadsheetId: validatedInput.spreadsheetId,
20424
+ fields: "sheets.properties.title,sheets.properties.sheetId,sheets.tables"
20425
+ });
20426
+ const allSheets = metadataResponse.data.sheets ?? [];
20427
+ const existingTable = allSheets.flatMap((sheet) => sheet.tables ?? []).find((table2) => table2.tableId === validatedInput.tableId);
20428
+ if (!existingTable) {
20429
+ throw new Error(`Table "${validatedInput.tableId}" not found`);
20430
+ }
20431
+ const table = {
20432
+ tableId: validatedInput.tableId
20433
+ };
20434
+ if (validatedInput.name) {
20435
+ table.name = validatedInput.name;
20436
+ }
20437
+ if (validatedInput.columns) {
20438
+ table.columnProperties = buildTableColumnProperties(validatedInput.columns);
20439
+ }
20440
+ let responseSheetName;
20441
+ if (validatedInput.range) {
20442
+ const extracted = extractSheetName(validatedInput.range);
20443
+ const sheetName = validatedInput.sheetName ?? extracted.sheetName;
20444
+ if (!sheetName) {
20445
+ throw new Error("sheetName is required when updating a table range");
20446
+ }
20447
+ const resolvedRange = resolveTableRangeInput(sheetName, validatedInput.range);
20448
+ responseSheetName = resolvedRange.sheetName;
20449
+ const sheetId = await getSheetId(
20450
+ sheets,
20451
+ validatedInput.spreadsheetId,
20452
+ resolvedRange.sheetName
20453
+ );
20454
+ const gridRange = parseRange(resolvedRange.range, sheetId);
20455
+ validateTableGridRange(gridRange, `${resolvedRange.sheetName}!${resolvedRange.range}`);
20456
+ if (validatedInput.columns) {
20457
+ validateColumnCount(
20458
+ gridRange,
20459
+ validatedInput.columns,
20460
+ `${resolvedRange.sheetName}!${resolvedRange.range}`
20461
+ );
20462
+ }
20463
+ const sheetData = findSheetOrThrow(allSheets, resolvedRange.sheetName);
20464
+ ensureNoOverlappingTable(
20465
+ sheetData.tables ?? [],
20466
+ gridRange,
20467
+ resolvedRange.sheetName,
20468
+ validatedInput.tableId
20469
+ );
20470
+ table.range = gridRange;
20471
+ }
20472
+ const response = await sheets.spreadsheets.batchUpdate({
20473
+ spreadsheetId: validatedInput.spreadsheetId,
20474
+ requestBody: {
20475
+ requests: [
20476
+ {
20477
+ updateTable: {
20478
+ table,
20479
+ fields: validatedInput.fields
20480
+ }
20481
+ }
20482
+ ]
20483
+ }
20484
+ });
20485
+ return formatToolResponse(`Successfully updated table "${validatedInput.tableId}"`, {
20486
+ spreadsheetId: response.data.spreadsheetId,
20487
+ table: formatTableForResponse({ ...existingTable, ...table }, responseSheetName),
20488
+ fields: validatedInput.fields
20489
+ });
20490
+ } catch (error2) {
20491
+ return handleError(error2);
20492
+ }
20493
+ }
20494
+
20495
+ // src/tools/delete-table.ts
20496
+ var deleteTableInputSchema = external_exports.object({
20497
+ spreadsheetId: external_exports.string().min(1),
20498
+ tableId: external_exports.string().min(1)
20499
+ });
20500
+ var deleteTableTool = {
20501
+ name: "sheets_delete_table",
20502
+ description: "Delete a native Google Sheets table by tableId",
20503
+ inputSchema: {
20504
+ type: "object",
20505
+ properties: {
20506
+ spreadsheetId: {
20507
+ type: "string",
20508
+ description: "The ID of the spreadsheet (found in the URL after /d/)"
20509
+ },
20510
+ tableId: {
20511
+ type: "string",
20512
+ description: "The ID of the table to delete"
20513
+ }
20514
+ },
20515
+ required: ["spreadsheetId", "tableId"]
20516
+ }
20517
+ };
20518
+ async function deleteTableHandler(input) {
20519
+ try {
20520
+ const validatedInput = deleteTableInputSchema.parse(input);
20521
+ const sheets = await getAuthenticatedClient();
20522
+ const metadataResponse = await sheets.spreadsheets.get({
20523
+ spreadsheetId: validatedInput.spreadsheetId,
20524
+ fields: "sheets.tables.tableId"
20525
+ });
20526
+ const tableExists = (metadataResponse.data.sheets ?? []).flatMap((sheet) => sheet.tables ?? []).some((table) => table.tableId === validatedInput.tableId);
20527
+ if (!tableExists) {
20528
+ throw new Error(`Table "${validatedInput.tableId}" not found`);
20529
+ }
20530
+ const response = await sheets.spreadsheets.batchUpdate({
20531
+ spreadsheetId: validatedInput.spreadsheetId,
20532
+ requestBody: {
20533
+ requests: [
20534
+ {
20535
+ deleteTable: {
20536
+ tableId: validatedInput.tableId
20537
+ }
20538
+ }
20539
+ ]
20540
+ }
20541
+ });
20542
+ return formatToolResponse(`Successfully deleted table "${validatedInput.tableId}"`, {
20543
+ spreadsheetId: response.data.spreadsheetId,
20544
+ tableId: validatedInput.tableId
20545
+ });
20546
+ } catch (error2) {
20547
+ return handleError(error2);
20548
+ }
20549
+ }
20550
+
20551
+ // src/tools/get-tables.ts
20552
+ var getTablesInputSchema = external_exports.object({
20553
+ spreadsheetId: external_exports.string().min(1),
20554
+ sheetName: external_exports.string().min(1).optional()
20555
+ });
20556
+ var getTablesTool = {
20557
+ name: "sheets_get_tables",
20558
+ description: "Read native Google Sheets tables for a spreadsheet or a specific sheet",
20559
+ inputSchema: {
20560
+ type: "object",
20561
+ properties: {
20562
+ spreadsheetId: {
20563
+ type: "string",
20564
+ description: "The ID of the spreadsheet (found in the URL after /d/)"
20565
+ },
20566
+ sheetName: {
20567
+ type: "string",
20568
+ description: "Optional name of the sheet (tab) to inspect"
20569
+ }
20570
+ },
20571
+ required: ["spreadsheetId"]
20572
+ }
20573
+ };
20574
+ async function getTablesHandler(input) {
20575
+ try {
20576
+ const validatedInput = getTablesInputSchema.parse(input);
20577
+ const sheets = await getAuthenticatedClient();
20578
+ const response = await sheets.spreadsheets.get({
20579
+ spreadsheetId: validatedInput.spreadsheetId,
20580
+ fields: "sheets.properties.title,sheets.properties.sheetId,sheets.tables"
20581
+ });
20582
+ const spreadsheetSheets = response.data.sheets ?? [];
20583
+ const targetSheets = validatedInput.sheetName ? [findSheetOrThrow(spreadsheetSheets, validatedInput.sheetName)] : spreadsheetSheets;
20584
+ const tables = targetSheets.flatMap((sheet) => {
20585
+ const sheetName = sheet.properties?.title ?? void 0;
20586
+ return (sheet.tables ?? []).map((table) => ({
20587
+ sheetName: sheetName ?? null,
20588
+ sheetId: sheet.properties?.sheetId ?? null,
20589
+ ...formatTableForResponse(table, sheetName)
20590
+ }));
20591
+ });
20592
+ const context = validatedInput.sheetName ? ` in sheet "${validatedInput.sheetName}"` : " in spreadsheet";
20593
+ return formatSuccessResponse(
20594
+ {
20595
+ spreadsheetId: validatedInput.spreadsheetId,
20596
+ sheetName: validatedInput.sheetName,
20597
+ tableCount: tables.length,
20598
+ tables
20599
+ },
20600
+ `Found ${tables.length} table(s)${context}`
20601
+ );
20602
+ } catch (error2) {
20603
+ return handleError(error2);
20604
+ }
20605
+ }
20606
+
20133
20607
  // src/tools/check-access.ts
20134
20608
  var CheckAccessSchema = external_exports.object({
20135
20609
  spreadsheetId: external_exports.string().describe("The ID of the spreadsheet to check access for")
@@ -23366,6 +23840,10 @@ var toolHandlers = /* @__PURE__ */ new Map([
23366
23840
  ["sheets_merge_cells", mergeCellsHandler],
23367
23841
  ["sheets_unmerge_cells", unmergeCellsHandler],
23368
23842
  ["sheets_add_conditional_formatting", addConditionalFormattingHandler],
23843
+ ["sheets_add_table", addTableHandler],
23844
+ ["sheets_update_table", updateTableHandler],
23845
+ ["sheets_delete_table", deleteTableHandler],
23846
+ ["sheets_get_tables", getTablesHandler],
23369
23847
  // Batch operations
23370
23848
  ["sheets_batch_delete_sheets", handleBatchDeleteSheets],
23371
23849
  ["sheets_batch_format_cells", handleBatchFormatCells],
@@ -23414,6 +23892,10 @@ var allTools = [
23414
23892
  mergeCellsTool,
23415
23893
  unmergeCellsTool,
23416
23894
  addConditionalFormattingTool,
23895
+ addTableTool,
23896
+ updateTableTool,
23897
+ deleteTableTool,
23898
+ getTablesTool,
23417
23899
  batchDeleteSheetsTool,
23418
23900
  batchFormatCellsTool,
23419
23901
  createChartTool,
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "mcp-gsheets",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
+ "mcpName": "io.github.freema/mcp-gsheets",
4
5
  "description": "Model Context Protocol (MCP) server for Google Sheets API integration",
5
6
  "author": "freema",
6
7
  "license": "MIT",