mcp-gsheets 1.5.3 → 1.6.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 +5 -0
  2. package/dist/index.js +410 -5
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -137,6 +137,7 @@ For other MCP clients, use the standard configuration format shown above. Ensure
137
137
  4. Create Service Account:
138
138
  - Go to "APIs & Services" → "Credentials"
139
139
  - Click "Create Credentials" → "Service Account"
140
+ - In the service accounts list, click the three dots in the `Actions` column → `Manage keys` → `Add key` → `Create new key` → select JSON format
140
141
  - Download the JSON key file
141
142
  5. Share your spreadsheets:
142
143
  - Open your Google Sheet
@@ -474,6 +475,10 @@ See [CHANGELOG.md](CHANGELOG.md) for a list of changes in each version.
474
475
  5. Push to the branch (`git push origin feature/amazing-feature`)
475
476
  6. Open a Pull Request
476
477
 
478
+ ## 👤 Author
479
+
480
+ **Tomáš Grásl** - [tomasgrasl.cz](https://www.tomasgrasl.cz/)
481
+
477
482
  ## 📄 License
478
483
 
479
484
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
package/dist/index.js CHANGED
@@ -15748,6 +15748,399 @@ async function handleInsertRows(input) {
15748
15748
  }
15749
15749
  }
15750
15750
 
15751
+ // src/resources/index.ts
15752
+ function parseResourceUri(uri) {
15753
+ let path;
15754
+ if (uri.startsWith("spreadsheet://")) {
15755
+ path = uri.slice("spreadsheet://".length);
15756
+ } else {
15757
+ return null;
15758
+ }
15759
+ const parts = path.split("/");
15760
+ if (parts.length < 2) {
15761
+ return null;
15762
+ }
15763
+ const spreadsheetId = parts[0];
15764
+ if (parts[1] === "info" && spreadsheetId) {
15765
+ return { type: "info", spreadsheetId };
15766
+ }
15767
+ if (parts[1] === "sheets" && spreadsheetId) {
15768
+ return { type: "sheets", spreadsheetId };
15769
+ }
15770
+ if (parts[1] === "sheet" && parts.length >= 3 && spreadsheetId) {
15771
+ const sheetName = decodeURIComponent(parts.slice(2).join("/"));
15772
+ if (sheetName) {
15773
+ return { type: "sheet-data", spreadsheetId, sheetName };
15774
+ }
15775
+ }
15776
+ return null;
15777
+ }
15778
+ async function readSpreadsheetInfo(spreadsheetId, uri) {
15779
+ try {
15780
+ const sheets = await getAuthenticatedClient();
15781
+ const response = await sheets.spreadsheets.get({
15782
+ spreadsheetId,
15783
+ fields: "spreadsheetId,properties,spreadsheetUrl"
15784
+ });
15785
+ const data = {
15786
+ spreadsheetId: response.data.spreadsheetId,
15787
+ title: response.data.properties?.title,
15788
+ locale: response.data.properties?.locale,
15789
+ timeZone: response.data.properties?.timeZone,
15790
+ url: response.data.spreadsheetUrl
15791
+ };
15792
+ return {
15793
+ contents: [
15794
+ {
15795
+ uri,
15796
+ mimeType: "application/json",
15797
+ text: JSON.stringify(data, null, 2)
15798
+ }
15799
+ ]
15800
+ };
15801
+ } catch (error) {
15802
+ const errorResponse = handleError(error);
15803
+ const errorText = errorResponse.content[0]?.text ?? "Unknown error";
15804
+ throw new Error(errorText);
15805
+ }
15806
+ }
15807
+ async function readSheetList(spreadsheetId, uri) {
15808
+ try {
15809
+ const sheets = await getAuthenticatedClient();
15810
+ const response = await sheets.spreadsheets.get({
15811
+ spreadsheetId,
15812
+ fields: "sheets.properties"
15813
+ });
15814
+ const sheetList = response.data.sheets?.map((sheet) => ({
15815
+ sheetId: sheet.properties?.sheetId,
15816
+ title: sheet.properties?.title,
15817
+ index: sheet.properties?.index,
15818
+ sheetType: sheet.properties?.sheetType,
15819
+ rowCount: sheet.properties?.gridProperties?.rowCount,
15820
+ columnCount: sheet.properties?.gridProperties?.columnCount,
15821
+ frozenRowCount: sheet.properties?.gridProperties?.frozenRowCount,
15822
+ frozenColumnCount: sheet.properties?.gridProperties?.frozenColumnCount
15823
+ })) || [];
15824
+ return {
15825
+ contents: [
15826
+ {
15827
+ uri,
15828
+ mimeType: "application/json",
15829
+ text: JSON.stringify({ sheets: sheetList }, null, 2)
15830
+ }
15831
+ ]
15832
+ };
15833
+ } catch (error) {
15834
+ const errorResponse = handleError(error);
15835
+ const errorText = errorResponse.content[0]?.text ?? "Unknown error";
15836
+ throw new Error(errorText);
15837
+ }
15838
+ }
15839
+ async function readSheetData(spreadsheetId, sheetName, uri) {
15840
+ try {
15841
+ const sheets = await getAuthenticatedClient();
15842
+ const response = await sheets.spreadsheets.values.get({
15843
+ spreadsheetId,
15844
+ range: sheetName,
15845
+ valueRenderOption: "FORMATTED_VALUE"
15846
+ });
15847
+ const values = response.data.values || [];
15848
+ const data = {
15849
+ range: response.data.range,
15850
+ majorDimension: response.data.majorDimension,
15851
+ values,
15852
+ rowCount: values.length,
15853
+ columnCount: values[0]?.length || 0
15854
+ };
15855
+ return {
15856
+ contents: [
15857
+ {
15858
+ uri,
15859
+ mimeType: "application/json",
15860
+ text: JSON.stringify(data, null, 2)
15861
+ }
15862
+ ]
15863
+ };
15864
+ } catch (error) {
15865
+ const errorResponse = handleError(error);
15866
+ const errorText = errorResponse.content[0]?.text ?? "Unknown error";
15867
+ throw new Error(errorText);
15868
+ }
15869
+ }
15870
+ var resourceTemplates = [
15871
+ {
15872
+ uriTemplate: "spreadsheet://{spreadsheetId}/info",
15873
+ name: "Spreadsheet Info",
15874
+ description: "Get metadata about a Google Spreadsheet including title, locale, timezone, and URL",
15875
+ mimeType: "application/json"
15876
+ },
15877
+ {
15878
+ uriTemplate: "spreadsheet://{spreadsheetId}/sheets",
15879
+ name: "Sheet List",
15880
+ description: "List all sheets in a spreadsheet with their properties (id, title, row/column counts, frozen rows/columns)",
15881
+ mimeType: "application/json"
15882
+ },
15883
+ {
15884
+ uriTemplate: "spreadsheet://{spreadsheetId}/sheet/{sheetName}",
15885
+ name: "Sheet Data",
15886
+ description: "Get all data from a specific sheet by name",
15887
+ mimeType: "application/json"
15888
+ }
15889
+ ];
15890
+ async function handleResourceRead(uri) {
15891
+ const parsed = parseResourceUri(uri);
15892
+ if (!parsed) {
15893
+ throw new Error(
15894
+ `Invalid resource URI: ${uri}. Expected format: spreadsheet://{spreadsheetId}/info, spreadsheet://{spreadsheetId}/sheets, or spreadsheet://{spreadsheetId}/sheet/{sheetName}`
15895
+ );
15896
+ }
15897
+ switch (parsed.type) {
15898
+ case "info":
15899
+ return readSpreadsheetInfo(parsed.spreadsheetId, uri);
15900
+ case "sheets":
15901
+ return readSheetList(parsed.spreadsheetId, uri);
15902
+ case "sheet-data":
15903
+ if (!parsed.sheetName) {
15904
+ throw new Error("Sheet name is required for sheet-data resource");
15905
+ }
15906
+ return readSheetData(parsed.spreadsheetId, parsed.sheetName, uri);
15907
+ }
15908
+ }
15909
+
15910
+ // src/prompts/index.ts
15911
+ var createTablePrompt = {
15912
+ name: "create-table",
15913
+ description: "Guide for creating a new table with headers and data in a Google Spreadsheet",
15914
+ arguments: [
15915
+ {
15916
+ name: "spreadsheetId",
15917
+ description: "The ID of the target spreadsheet (from the URL)",
15918
+ required: true
15919
+ },
15920
+ {
15921
+ name: "sheetName",
15922
+ description: "Name of the sheet to create the table in (default: Sheet1)",
15923
+ required: false
15924
+ },
15925
+ {
15926
+ name: "dataDescription",
15927
+ description: "Description of the data/table you want to create",
15928
+ required: true
15929
+ }
15930
+ ]
15931
+ };
15932
+ function handleCreateTable(args) {
15933
+ const spreadsheetId = args.spreadsheetId || "{spreadsheetId}";
15934
+ const sheetName = args.sheetName || "Sheet1";
15935
+ const dataDescription = args.dataDescription || "your data";
15936
+ return {
15937
+ description: `Create a table for: ${dataDescription}`,
15938
+ messages: [
15939
+ {
15940
+ role: "user",
15941
+ content: {
15942
+ type: "text",
15943
+ text: `I want to create a table in Google Sheets.
15944
+
15945
+ Spreadsheet ID: ${spreadsheetId}
15946
+ Sheet: ${sheetName}
15947
+ Data description: ${dataDescription}
15948
+
15949
+ Please help me:
15950
+ 1. First, check if I have access to the spreadsheet using sheets_check_access
15951
+ 2. Define appropriate column headers based on the data description
15952
+ 3. Create the table structure using sheets_update_values
15953
+ 4. Optionally format the header row (bold, background color) using sheets_format_cells
15954
+
15955
+ Please proceed step by step.`
15956
+ }
15957
+ }
15958
+ ]
15959
+ };
15960
+ }
15961
+ var formatReportPrompt = {
15962
+ name: "format-report",
15963
+ description: "Format existing data as a professional report with headers, borders, and styling",
15964
+ arguments: [
15965
+ {
15966
+ name: "spreadsheetId",
15967
+ description: "The ID of the spreadsheet containing the data",
15968
+ required: true
15969
+ },
15970
+ {
15971
+ name: "range",
15972
+ description: 'The range containing data to format (e.g., "Sheet1!A1:E20")',
15973
+ required: true
15974
+ },
15975
+ {
15976
+ name: "reportStyle",
15977
+ description: 'Style preference: "professional", "minimal", or "colorful"',
15978
+ required: false
15979
+ }
15980
+ ]
15981
+ };
15982
+ function handleFormatReport(args) {
15983
+ const spreadsheetId = args.spreadsheetId || "{spreadsheetId}";
15984
+ const range = args.range || "Sheet1!A1:E20";
15985
+ const reportStyle = args.reportStyle || "professional";
15986
+ const styleGuide = {
15987
+ professional: "navy blue headers with white text, light gray alternating rows, thin borders",
15988
+ minimal: "bold headers, subtle bottom border on header row only, clean white background",
15989
+ colorful: "vibrant colored headers, light pastel alternating row colors, medium borders"
15990
+ };
15991
+ return {
15992
+ description: `Format report in ${reportStyle} style`,
15993
+ messages: [
15994
+ {
15995
+ role: "user",
15996
+ content: {
15997
+ type: "text",
15998
+ text: `I want to format my data as a ${reportStyle} report.
15999
+
16000
+ Spreadsheet ID: ${spreadsheetId}
16001
+ Data range: ${range}
16002
+ Style: ${reportStyle} - ${styleGuide[reportStyle] || styleGuide["professional"]}
16003
+
16004
+ Please help me:
16005
+ 1. First, read the data to understand its structure using sheets_get_values
16006
+ 2. Format the header row (first row) with appropriate styling using sheets_format_cells
16007
+ 3. Add borders to the data range using sheets_update_borders
16008
+ 4. Optionally auto-resize columns for better readability
16009
+
16010
+ Use the ${reportStyle} style guidelines to make the report look great.`
16011
+ }
16012
+ }
16013
+ ]
16014
+ };
16015
+ }
16016
+ var summarizeDataPrompt = {
16017
+ name: "summarize-data",
16018
+ description: "Analyze spreadsheet data and provide insights or create a summary",
16019
+ arguments: [
16020
+ {
16021
+ name: "spreadsheetId",
16022
+ description: "The ID of the spreadsheet to analyze",
16023
+ required: true
16024
+ },
16025
+ {
16026
+ name: "range",
16027
+ description: 'The range containing data to analyze (e.g., "Sheet1!A1:F100")',
16028
+ required: true
16029
+ },
16030
+ {
16031
+ name: "analysisType",
16032
+ description: 'Type of analysis: "overview", "statistics", or "trends"',
16033
+ required: false
16034
+ }
16035
+ ]
16036
+ };
16037
+ function handleSummarizeData(args) {
16038
+ const spreadsheetId = args.spreadsheetId || "{spreadsheetId}";
16039
+ const range = args.range || "Sheet1!A:Z";
16040
+ const analysisType = args.analysisType || "overview";
16041
+ return {
16042
+ description: `Analyze data: ${analysisType}`,
16043
+ messages: [
16044
+ {
16045
+ role: "user",
16046
+ content: {
16047
+ type: "text",
16048
+ text: `I want to analyze and summarize my spreadsheet data.
16049
+
16050
+ Spreadsheet ID: ${spreadsheetId}
16051
+ Data range: ${range}
16052
+ Analysis type: ${analysisType}
16053
+
16054
+ Please help me:
16055
+ 1. First, get the spreadsheet metadata using sheets_get_metadata to understand the structure
16056
+ 2. Read the data from the specified range using sheets_get_values
16057
+ 3. Analyze the data and provide:
16058
+ - Overview of the data structure (columns, rows, data types)
16059
+ - Key statistics (counts, sums, averages where applicable)
16060
+ - Notable patterns or insights
16061
+ 4. Suggest any potential improvements to the data organization
16062
+
16063
+ Focus on a ${analysisType} analysis.`
16064
+ }
16065
+ }
16066
+ ]
16067
+ };
16068
+ }
16069
+ var createChartGuidePrompt = {
16070
+ name: "create-chart-guide",
16071
+ description: "Step-by-step guide to create a chart from spreadsheet data",
16072
+ arguments: [
16073
+ {
16074
+ name: "spreadsheetId",
16075
+ description: "The ID of the spreadsheet containing the data",
16076
+ required: true
16077
+ },
16078
+ {
16079
+ name: "dataRange",
16080
+ description: 'The range containing data for the chart (e.g., "Sheet1!A1:B10")',
16081
+ required: true
16082
+ },
16083
+ {
16084
+ name: "chartType",
16085
+ description: 'Preferred chart type: "COLUMN", "BAR", "LINE", "PIE", "AREA", or "SCATTER"',
16086
+ required: false
16087
+ }
16088
+ ]
16089
+ };
16090
+ function handleCreateChartGuide(args) {
16091
+ const spreadsheetId = args.spreadsheetId || "{spreadsheetId}";
16092
+ const dataRange = args.dataRange || "Sheet1!A1:B10";
16093
+ const chartType = args.chartType || "COLUMN";
16094
+ return {
16095
+ description: `Create ${chartType} chart`,
16096
+ messages: [
16097
+ {
16098
+ role: "user",
16099
+ content: {
16100
+ type: "text",
16101
+ text: `I want to create a ${chartType} chart from my spreadsheet data.
16102
+
16103
+ Spreadsheet ID: ${spreadsheetId}
16104
+ Data range: ${dataRange}
16105
+ Chart type: ${chartType}
16106
+
16107
+ Please help me:
16108
+ 1. First, read the data to understand what we're visualizing using sheets_get_values
16109
+ 2. Get the sheet ID needed for chart creation using sheets_get_metadata
16110
+ 3. Create the chart using sheets_create_chart with appropriate configuration:
16111
+ - Title based on the data
16112
+ - Proper axis labels
16113
+ - Legend if needed
16114
+ 4. Position the chart appropriately on the sheet
16115
+
16116
+ Provide the chart configuration and create it step by step.`
16117
+ }
16118
+ }
16119
+ ]
16120
+ };
16121
+ }
16122
+ var allPrompts = [
16123
+ createTablePrompt,
16124
+ formatReportPrompt,
16125
+ summarizeDataPrompt,
16126
+ createChartGuidePrompt
16127
+ ];
16128
+ var promptHandlers = /* @__PURE__ */ new Map([
16129
+ ["create-table", handleCreateTable],
16130
+ ["format-report", handleFormatReport],
16131
+ ["summarize-data", handleSummarizeData],
16132
+ ["create-chart-guide", handleCreateChartGuide]
16133
+ ]);
16134
+ function handleGetPrompt(name, args) {
16135
+ const handler = promptHandlers.get(name);
16136
+ if (!handler) {
16137
+ throw new Error(
16138
+ `Unknown prompt: ${name}. Available prompts: ${allPrompts.map((p) => p.name).join(", ")}`
16139
+ );
16140
+ }
16141
+ return handler(args || {});
16142
+ }
16143
+
15751
16144
  // src/index.ts
15752
16145
  if (process.env.NODE_ENV !== "production") {
15753
16146
  try {
@@ -15832,12 +16225,13 @@ async function main() {
15832
16225
  const server = new Server(
15833
16226
  {
15834
16227
  name: "spreadsheet",
15835
- version: "1.0.0"
16228
+ version: "1.6.0"
15836
16229
  },
15837
16230
  {
15838
16231
  capabilities: {
15839
- resources: {},
15840
- tools: {}
16232
+ resources: { listChanged: false },
16233
+ tools: { listChanged: false },
16234
+ prompts: { listChanged: false }
15841
16235
  }
15842
16236
  }
15843
16237
  );
@@ -15859,11 +16253,22 @@ async function main() {
15859
16253
  throw error;
15860
16254
  }
15861
16255
  });
16256
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
16257
+ return { resourceTemplates };
16258
+ });
15862
16259
  server.setRequestHandler(ListResourcesRequestSchema, async () => {
15863
16260
  return { resources: [] };
15864
16261
  });
15865
- server.setRequestHandler(ReadResourceRequestSchema, async () => {
15866
- throw new Error("Resource reading not implemented");
16262
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
16263
+ const uri = request.params.uri;
16264
+ return handleResourceRead(uri);
16265
+ });
16266
+ server.setRequestHandler(ListPromptsRequestSchema, async () => {
16267
+ return { prompts: allPrompts };
16268
+ });
16269
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
16270
+ const { name, arguments: args } = request.params;
16271
+ return handleGetPrompt(name, args);
15867
16272
  });
15868
16273
  const transport = new StdioServerTransport();
15869
16274
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-gsheets",
3
- "version": "1.5.3",
3
+ "version": "1.6.0",
4
4
  "description": "Model Context Protocol (MCP) server for Google Sheets API integration",
5
5
  "author": "freema",
6
6
  "license": "MIT",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "@modelcontextprotocol/sdk": "^1.17.1",
50
- "googleapis": "^140.0.0",
50
+ "googleapis": "^164.1.0",
51
51
  "zod": "^3.22.4"
52
52
  },
53
53
  "devDependencies": {