mcp-gsheets 1.9.0 → 1.10.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 +56 -0
  2. package/dist/index.js +158 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -312,6 +312,62 @@ cp .env.example .env
312
312
  npm run dev # Watch mode with auto-reload
313
313
  ```
314
314
 
315
+ ## 🎚️ Reducing context cost with toolsets
316
+
317
+ All 44 tools together cost about **9,900 tokens of context in every session**,
318
+ before the model does anything. Most workflows need a fraction of that.
319
+ `GSHEETS_TOOLSETS` limits which tools the server exposes:
320
+
321
+ ```json
322
+ {
323
+ "mcpServers": {
324
+ "gsheets": {
325
+ "command": "npx",
326
+ "args": ["mcp-gsheets"],
327
+ "env": {
328
+ "GOOGLE_PROJECT_ID": "your-project-id",
329
+ "GOOGLE_APPLICATION_CREDENTIALS": "/path/to/key.json",
330
+ "GSHEETS_TOOLSETS": "core,charts"
331
+ }
332
+ }
333
+ }
334
+ }
335
+ ```
336
+
337
+ | Toolset | Tools | What it covers |
338
+ |---|---|---|
339
+ | `core` | 11 | Read/write values, metadata, sheet structure, create spreadsheet |
340
+ | `sheets` | 9 | Sheet lifecycle, rows and columns |
341
+ | `formatting` | 15 | Colours, borders, merges, conditional rules, links, dates |
342
+ | `charts` | 3 | Create, update, delete charts |
343
+ | `tables` | 4 | Native tables |
344
+ | `analysis` | 2 | Full-sheet snapshot, range comparison |
345
+
346
+ Measured `tools/list` cost:
347
+
348
+ | Configuration | Tools | ≈ Tokens |
349
+ |---|---|---|
350
+ | _unset_ (default, all toolsets) | 44 | 9,875 |
351
+ | `GSHEETS_TOOLSETS=core` | 11 | 1,986 |
352
+ | `GSHEETS_TOOLSETS=core,sheets` | 20 | 3,548 |
353
+ | `GSHEETS_READ_ONLY=true` | 16 | 3,599 |
354
+ | `GSHEETS_TOOLSETS=core` + read-only | 6 | 912 |
355
+
356
+ Notes:
357
+
358
+ - **The default is unchanged** — leave `GSHEETS_TOOLSETS` unset and you get
359
+ every tool, exactly as before.
360
+ - **`core` is always included.** `GSHEETS_TOOLSETS=charts` means "charts as
361
+ well as core", not "charts only" — without core the server cannot read a cell.
362
+ - **A typo is a startup error**, not a silently smaller tool list.
363
+ - `GSHEETS_READ_ONLY=true` drops every writing tool and can be combined with
364
+ `GSHEETS_TOOLSETS`. It is enforced when a tool is called, not just when the
365
+ list is built, so a client cannot write by naming a hidden tool.
366
+
367
+ All tools also carry MCP annotations (`readOnlyHint`, `destructiveHint`,
368
+ `idempotentHint`), so clients can skip confirmation prompts on reads and warn
369
+ before destructive operations.
370
+
315
371
  ## 📋 Available Tools
316
372
 
317
373
  ### Reading Data
package/dist/index.js CHANGED
@@ -18036,6 +18036,141 @@ function validateAuth() {
18036
18036
  }
18037
18037
  }
18038
18038
 
18039
+ // src/config/toolsets.ts
18040
+ var TOOLSET_NAMES = [
18041
+ "core",
18042
+ "sheets",
18043
+ "formatting",
18044
+ "charts",
18045
+ "tables",
18046
+ "analysis"
18047
+ ];
18048
+ var TOOLSETS = {
18049
+ /** Reading and writing cells, plus the metadata needed to do it safely. */
18050
+ core: [
18051
+ "sheets_check_access",
18052
+ "sheets_get_metadata",
18053
+ "sheets_get_sheet_structure",
18054
+ "sheets_get_sheet_dimensions",
18055
+ "sheets_get_values",
18056
+ "sheets_batch_get_values",
18057
+ "sheets_update_values",
18058
+ "sheets_batch_update_values",
18059
+ "sheets_append_values",
18060
+ "sheets_clear_values",
18061
+ "sheets_create_spreadsheet"
18062
+ ],
18063
+ /** Sheet lifecycle and row/column structure. */
18064
+ sheets: [
18065
+ "sheets_insert_sheet",
18066
+ "sheets_delete_sheet",
18067
+ "sheets_duplicate_sheet",
18068
+ "sheets_copy_to",
18069
+ "sheets_update_sheet_properties",
18070
+ "sheets_batch_delete_sheets",
18071
+ "sheets_insert_rows",
18072
+ "sheets_delete_rows",
18073
+ "sheets_delete_columns"
18074
+ ],
18075
+ /** Cell appearance: colours, borders, merges, conditional rules, links. */
18076
+ formatting: [
18077
+ "sheets_format_cells",
18078
+ "sheets_batch_format_cells",
18079
+ "sheets_update_borders",
18080
+ "sheets_get_border_map",
18081
+ "sheets_merge_cells",
18082
+ "sheets_unmerge_cells",
18083
+ "sheets_get_merged_cells",
18084
+ "sheets_get_sheet_formatting",
18085
+ "sheets_get_formatting_compact",
18086
+ "sheets_add_conditional_formatting",
18087
+ "sheets_get_conditional_formatting",
18088
+ "sheets_get_data_validation",
18089
+ "sheets_get_basic_filter",
18090
+ "sheets_insert_link",
18091
+ "sheets_insert_date"
18092
+ ],
18093
+ charts: ["sheets_create_chart", "sheets_update_chart", "sheets_delete_chart"],
18094
+ tables: ["sheets_add_table", "sheets_update_table", "sheets_delete_table", "sheets_get_tables"],
18095
+ /** Whole-sheet reads and diffing — powerful, but the heaviest schemas. */
18096
+ analysis: ["sheets_get_full_sheet_snapshot", "sheets_compare_ranges"]
18097
+ };
18098
+ var READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
18099
+ "sheets_check_access",
18100
+ "sheets_get_metadata",
18101
+ "sheets_get_sheet_structure",
18102
+ "sheets_get_sheet_dimensions",
18103
+ "sheets_get_values",
18104
+ "sheets_batch_get_values",
18105
+ "sheets_get_border_map",
18106
+ "sheets_get_merged_cells",
18107
+ "sheets_get_sheet_formatting",
18108
+ "sheets_get_formatting_compact",
18109
+ "sheets_get_conditional_formatting",
18110
+ "sheets_get_data_validation",
18111
+ "sheets_get_basic_filter",
18112
+ "sheets_get_tables",
18113
+ "sheets_get_full_sheet_snapshot",
18114
+ "sheets_compare_ranges"
18115
+ ]);
18116
+ var DESTRUCTIVE_TOOLS = /* @__PURE__ */ new Set([
18117
+ "sheets_clear_values",
18118
+ "sheets_delete_sheet",
18119
+ "sheets_batch_delete_sheets",
18120
+ "sheets_delete_rows",
18121
+ "sheets_delete_columns",
18122
+ "sheets_delete_chart",
18123
+ "sheets_delete_table",
18124
+ "sheets_unmerge_cells"
18125
+ ]);
18126
+ var ToolsetConfigError = class extends Error {
18127
+ };
18128
+ function parseList(raw) {
18129
+ return raw.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
18130
+ }
18131
+ function resolveToolsets(env = process.env) {
18132
+ const raw = env.GSHEETS_TOOLSETS?.trim();
18133
+ const readOnly = env.GSHEETS_READ_ONLY?.trim().toLowerCase() === "true";
18134
+ let enabled;
18135
+ if (!raw || raw.toLowerCase() === "all") {
18136
+ enabled = [...TOOLSET_NAMES];
18137
+ } else {
18138
+ const requested = parseList(raw);
18139
+ const unknown2 = requested.filter((n) => !TOOLSET_NAMES.includes(n));
18140
+ if (unknown2.length > 0) {
18141
+ throw new ToolsetConfigError(
18142
+ `Unknown toolset${unknown2.length > 1 ? "s" : ""}: ${unknown2.join(", ")}. Valid toolsets: ${TOOLSET_NAMES.join(", ")}, all.`
18143
+ );
18144
+ }
18145
+ const set = new Set(requested);
18146
+ set.add("core");
18147
+ enabled = TOOLSET_NAMES.filter((n) => set.has(n));
18148
+ }
18149
+ const allowed = /* @__PURE__ */ new Set();
18150
+ for (const name of enabled) {
18151
+ for (const tool of TOOLSETS[name]) {
18152
+ if (readOnly && !READ_ONLY_TOOLS.has(tool)) {
18153
+ continue;
18154
+ }
18155
+ allowed.add(tool);
18156
+ }
18157
+ }
18158
+ return { enabled, readOnly, allowed };
18159
+ }
18160
+ function annotationsFor(toolName) {
18161
+ if (READ_ONLY_TOOLS.has(toolName)) {
18162
+ return { readOnlyHint: true, idempotentHint: true };
18163
+ }
18164
+ const annotations = {};
18165
+ if (!DESTRUCTIVE_TOOLS.has(toolName)) {
18166
+ annotations.destructiveHint = false;
18167
+ }
18168
+ if (toolName !== "sheets_append_values") {
18169
+ annotations.idempotentHint = true;
18170
+ }
18171
+ return annotations;
18172
+ }
18173
+
18039
18174
  // src/utils/error-handler.ts
18040
18175
  function handleError(error2) {
18041
18176
  console.error("Error in Google Sheets operation:", error2);
@@ -23925,6 +24060,23 @@ async function main() {
23925
24060
  console.error("Authentication Error:", error2.message);
23926
24061
  process.exit(1);
23927
24062
  }
24063
+ let toolsetConfig;
24064
+ try {
24065
+ toolsetConfig = resolveToolsets();
24066
+ } catch (error2) {
24067
+ if (error2 instanceof ToolsetConfigError) {
24068
+ console.error(`Configuration Error: ${error2.message}`);
24069
+ process.exit(1);
24070
+ }
24071
+ throw error2;
24072
+ }
24073
+ const { enabled, readOnly, allowed } = toolsetConfig;
24074
+ const exposedTools = allTools.filter((tool) => allowed.has(tool.name)).map((tool) => ({ ...tool, annotations: annotationsFor(tool.name) }));
24075
+ if (enabled.length < Object.keys(TOOLSETS).length || readOnly) {
24076
+ console.error(
24077
+ `Toolsets: ${enabled.join(", ")}${readOnly ? " (read-only)" : ""} \u2014 ${exposedTools.length}/${allTools.length} tools`
24078
+ );
24079
+ }
23928
24080
  const server = new Server(
23929
24081
  {
23930
24082
  name: "spreadsheet",
@@ -23940,11 +24092,16 @@ async function main() {
23940
24092
  );
23941
24093
  server.setRequestHandler(ListToolsRequestSchema, async () => {
23942
24094
  return {
23943
- tools: allTools
24095
+ tools: exposedTools
23944
24096
  };
23945
24097
  });
23946
24098
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
23947
24099
  const { name, arguments: args } = request.params;
24100
+ if (!allowed.has(name)) {
24101
+ throw new Error(
24102
+ toolHandlers.has(name) ? `Tool ${name} is disabled by the current GSHEETS_TOOLSETS/GSHEETS_READ_ONLY configuration` : `Unknown tool: ${name}`
24103
+ );
24104
+ }
23948
24105
  const handler = toolHandlers.get(name);
23949
24106
  if (!handler) {
23950
24107
  throw new Error(`Unknown tool: ${name}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-gsheets",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "mcpName": "io.github.freema/mcp-gsheets",
5
5
  "description": "Model Context Protocol (MCP) server for Google Sheets API integration",
6
6
  "author": "freema",