google-sheet-mcp 1.0.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/LICENSE +21 -0
- package/README.md +210 -0
- package/docs/setup-google.md +122 -0
- package/docs/setup-oauth2.md +177 -0
- package/examples/claude-mcp.json +12 -0
- package/examples/codex-mcp.json +12 -0
- package/examples/cursor-mcp.json +12 -0
- package/examples/vscode-mcp.json +13 -0
- package/package.json +53 -0
- package/src/cli/cli.mjs +87 -0
- package/src/cli/commands/append.mjs +55 -0
- package/src/cli/commands/config.mjs +93 -0
- package/src/cli/commands/create.mjs +45 -0
- package/src/cli/commands/helpers/show-mcp-config.mjs +71 -0
- package/src/cli/commands/init.mjs +415 -0
- package/src/cli/commands/list.mjs +50 -0
- package/src/cli/commands/read.mjs +64 -0
- package/src/cli/commands/test.mjs +99 -0
- package/src/cli/commands/token-status.mjs +114 -0
- package/src/config/config.mjs +166 -0
- package/src/server/oauth2-client.mjs +155 -0
- package/src/server/server.mjs +524 -0
- package/src/server/sheets-client.mjs +106 -0
package/src/cli/cli.mjs
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* google-sheet-mcp CLI
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx google-sheet-mcp init — Setup (service account, default)
|
|
7
|
+
* npx google-sheet-mcp init --auth oauth — Setup (OAuth2, personal account)
|
|
8
|
+
* npx google-sheet-mcp test — Test connection
|
|
9
|
+
* npx google-sheet-mcp token-status — Check OAuth2 token health
|
|
10
|
+
* npx google-sheet-mcp list — List sheets
|
|
11
|
+
* npx google-sheet-mcp read — Read sheet data
|
|
12
|
+
* npx google-sheet-mcp config — Show current config
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { program } from "commander";
|
|
16
|
+
import { initCommand } from "./commands/init.mjs";
|
|
17
|
+
import { testCommand } from "./commands/test.mjs";
|
|
18
|
+
import { listCommand } from "./commands/list.mjs";
|
|
19
|
+
import { readCommand } from "./commands/read.mjs";
|
|
20
|
+
import { configCommand } from "./commands/config.mjs";
|
|
21
|
+
import { createCommand } from "./commands/create.mjs";
|
|
22
|
+
import { appendCommand } from "./commands/append.mjs";
|
|
23
|
+
import { tokenStatusCommand } from "./commands/token-status.mjs";
|
|
24
|
+
|
|
25
|
+
program
|
|
26
|
+
.name("google-sheet-mcp")
|
|
27
|
+
.description(
|
|
28
|
+
"Connect Google Sheets to Cursor, VS Code, Claude Code and AI agents in 3 minutes."
|
|
29
|
+
)
|
|
30
|
+
.version("1.0.0");
|
|
31
|
+
|
|
32
|
+
program
|
|
33
|
+
.command("init")
|
|
34
|
+
.description("Interactive setup: connect a Google Sheet")
|
|
35
|
+
.option(
|
|
36
|
+
"--auth <type>",
|
|
37
|
+
"Authentication type: service-account (default) or oauth",
|
|
38
|
+
"service-account"
|
|
39
|
+
)
|
|
40
|
+
.action(initCommand);
|
|
41
|
+
|
|
42
|
+
program
|
|
43
|
+
.command("test")
|
|
44
|
+
.description("Test connection to the configured Google Sheet")
|
|
45
|
+
.option("-s, --sheet <name>", "Test reading a specific sheet")
|
|
46
|
+
.action(testCommand);
|
|
47
|
+
|
|
48
|
+
program
|
|
49
|
+
.command("list")
|
|
50
|
+
.description("List all sheets in the connected spreadsheet")
|
|
51
|
+
.action(listCommand);
|
|
52
|
+
|
|
53
|
+
program
|
|
54
|
+
.command("read")
|
|
55
|
+
.description("Read data from a sheet")
|
|
56
|
+
.requiredOption("-s, --sheet <name>", "Sheet name to read")
|
|
57
|
+
.option("-r, --range <range>", "Cell range in A1 notation (e.g., A1:Z100)")
|
|
58
|
+
.option("--raw", "Return raw 2D array instead of objects")
|
|
59
|
+
.action(readCommand);
|
|
60
|
+
|
|
61
|
+
program
|
|
62
|
+
.command("config")
|
|
63
|
+
.description("Show current configuration")
|
|
64
|
+
.action(configCommand);
|
|
65
|
+
|
|
66
|
+
program
|
|
67
|
+
.command("create")
|
|
68
|
+
.description("Create a new sheet")
|
|
69
|
+
.requiredOption("-s, --sheet <name>", "New sheet name")
|
|
70
|
+
.action(createCommand);
|
|
71
|
+
|
|
72
|
+
program
|
|
73
|
+
.command("append")
|
|
74
|
+
.description("Append a row to a sheet")
|
|
75
|
+
.requiredOption("-s, --sheet <name>", "Sheet name")
|
|
76
|
+
.requiredOption(
|
|
77
|
+
"-d, --data <json>",
|
|
78
|
+
"Row data as JSON (e.g., '{\"name\":\"Anton\"}')"
|
|
79
|
+
)
|
|
80
|
+
.action(appendCommand);
|
|
81
|
+
|
|
82
|
+
program
|
|
83
|
+
.command("token-status")
|
|
84
|
+
.description("Check OAuth2 token health (validates refresh token)")
|
|
85
|
+
.action(tokenStatusCommand);
|
|
86
|
+
|
|
87
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* append — Add a row to a sheet.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import ora from "ora";
|
|
7
|
+
import { loadConfig } from "../../config/config.mjs";
|
|
8
|
+
import { createSheetsClient } from "../../server/sheets-client.mjs";
|
|
9
|
+
|
|
10
|
+
export async function appendCommand(options) {
|
|
11
|
+
const config = loadConfig();
|
|
12
|
+
if (!config) {
|
|
13
|
+
console.error(chalk.red("❌ No configuration found. Run `npx google-sheet-mcp init` first."));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let data;
|
|
18
|
+
try {
|
|
19
|
+
data = JSON.parse(options.data);
|
|
20
|
+
} catch {
|
|
21
|
+
console.error(chalk.red("❌ Invalid JSON for --data. Example: '{\"name\":\"Anton\"}'"));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const spinner = ora(`Appending to "${options.sheet}"...`).start();
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
// First, get headers to align columns
|
|
29
|
+
const sheets = createSheetsClient(config.credentialsPath);
|
|
30
|
+
const headerRes = await sheets.spreadsheets.values.get({
|
|
31
|
+
spreadsheetId: config.spreadsheetId,
|
|
32
|
+
range: `${options.sheet}!1:1`,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const headers = headerRes.data.values?.[0] || Object.keys(data);
|
|
36
|
+
const row = headers.map((h) => data[h] ?? "");
|
|
37
|
+
|
|
38
|
+
await sheets.spreadsheets.values.append({
|
|
39
|
+
spreadsheetId: config.spreadsheetId,
|
|
40
|
+
range: `${options.sheet}!A1`,
|
|
41
|
+
valueInputOption: "USER_ENTERED",
|
|
42
|
+
requestBody: {
|
|
43
|
+
values: [row],
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
spinner.succeed(`Row appended to "${options.sheet}"`);
|
|
48
|
+
console.log(chalk.gray(` ${headers.slice(0, 6).join(" | ")}` + (headers.length > 6 ? "..." : "")));
|
|
49
|
+
console.log(chalk.white(` ${row.slice(0, 6).join(" | ")}` + (row.length > 6 ? "..." : "")));
|
|
50
|
+
|
|
51
|
+
} catch (err) {
|
|
52
|
+
spinner.fail(`Failed: ${err.message}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config — Show current configuration.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { loadConfig } from "../../config/config.mjs";
|
|
7
|
+
|
|
8
|
+
export async function configCommand() {
|
|
9
|
+
const config = loadConfig();
|
|
10
|
+
if (!config) {
|
|
11
|
+
console.log(chalk.yellow("⚠️ No configuration found."));
|
|
12
|
+
console.log();
|
|
13
|
+
console.log("To set up, run:");
|
|
14
|
+
console.log(chalk.cyan(" npx google-sheet-mcp init"));
|
|
15
|
+
console.log();
|
|
16
|
+
console.log("Or set environment variables:");
|
|
17
|
+
console.log(chalk.cyan(" Service Account: GOOGLE_SPREADSHEET_ID + GOOGLE_APPLICATION_CREDENTIALS"));
|
|
18
|
+
console.log(chalk.cyan(" OAuth2: GOOGLE_SPREADSHEET_ID + GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET + GOOGLE_REFRESH_TOKEN"));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const authType = config.authType || "service-account";
|
|
23
|
+
|
|
24
|
+
console.log(chalk.bold.cyan("\n⚙️ Current Configuration\n"));
|
|
25
|
+
console.log(chalk.gray(` Source: ${config.source}`));
|
|
26
|
+
if (config._path) {
|
|
27
|
+
console.log(chalk.gray(` Config file: ${config._path}`));
|
|
28
|
+
}
|
|
29
|
+
console.log(chalk.white(` Spreadsheet ID: ${config.spreadsheetId}`));
|
|
30
|
+
console.log(chalk.white(` Auth type: ${authType}`));
|
|
31
|
+
|
|
32
|
+
if (authType === "oauth2" && config.oauth2) {
|
|
33
|
+
console.log(
|
|
34
|
+
chalk.white(
|
|
35
|
+
` Client ID: ${config.oauth2.client_id?.substring(0, 16)}...`
|
|
36
|
+
)
|
|
37
|
+
);
|
|
38
|
+
console.log(
|
|
39
|
+
chalk.gray(` Refresh token: ${config.oauth2.refresh_token?.substring(0, 12)}...`)
|
|
40
|
+
);
|
|
41
|
+
console.log();
|
|
42
|
+
console.log(chalk.gray(" Token health:"));
|
|
43
|
+
console.log(chalk.cyan(" npx google-sheet-mcp token-status"));
|
|
44
|
+
} else {
|
|
45
|
+
console.log(chalk.white(` Key file: ${config.credentialsPath}`));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (config.sheets && config.sheets.length > 0) {
|
|
49
|
+
console.log(chalk.white(` Known sheets: ${config.sheets.join(", ")}`));
|
|
50
|
+
}
|
|
51
|
+
console.log();
|
|
52
|
+
|
|
53
|
+
// MCP config hint
|
|
54
|
+
console.log(chalk.bold("MCP Configuration for AI IDEs:"));
|
|
55
|
+
console.log();
|
|
56
|
+
console.log(chalk.gray(" Add this to your MCP config:"));
|
|
57
|
+
console.log();
|
|
58
|
+
|
|
59
|
+
if (authType === "oauth2") {
|
|
60
|
+
console.log(
|
|
61
|
+
chalk.white(` {
|
|
62
|
+
"mcpServers": {
|
|
63
|
+
"google-sheets": {
|
|
64
|
+
"command": "npx",
|
|
65
|
+
"args": ["google-sheet-mcp"],
|
|
66
|
+
"env": {
|
|
67
|
+
"GOOGLE_SPREADSHEET_ID": "${config.spreadsheetId}",
|
|
68
|
+
"GOOGLE_CLIENT_ID": "${config.oauth2?.client_id}",
|
|
69
|
+
"GOOGLE_CLIENT_SECRET": "${config.oauth2?.client_secret}",
|
|
70
|
+
"GOOGLE_REFRESH_TOKEN": "${config.oauth2?.refresh_token}"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}`)
|
|
75
|
+
);
|
|
76
|
+
} else {
|
|
77
|
+
console.log(
|
|
78
|
+
chalk.white(` {
|
|
79
|
+
"mcpServers": {
|
|
80
|
+
"google-sheets": {
|
|
81
|
+
"command": "npx",
|
|
82
|
+
"args": ["google-sheet-mcp"],
|
|
83
|
+
"env": {
|
|
84
|
+
"GOOGLE_SPREADSHEET_ID": "${config.spreadsheetId}",
|
|
85
|
+
"GOOGLE_APPLICATION_CREDENTIALS": "${config.credentialsPath}"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}`)
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
console.log();
|
|
93
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* create — Create a new sheet tab in the spreadsheet.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import ora from "ora";
|
|
7
|
+
import { loadConfig } from "../../config/config.mjs";
|
|
8
|
+
import { createSheetsClient } from "../../server/sheets-client.mjs";
|
|
9
|
+
|
|
10
|
+
export async function createCommand(options) {
|
|
11
|
+
const config = loadConfig();
|
|
12
|
+
if (!config) {
|
|
13
|
+
console.error(chalk.red("❌ No configuration found. Run `npx google-sheet-mcp init` first."));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const spinner = ora(`Creating sheet "${options.sheet}"...`).start();
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const sheets = createSheetsClient(config.credentialsPath);
|
|
21
|
+
|
|
22
|
+
await sheets.spreadsheets.batchUpdate({
|
|
23
|
+
spreadsheetId: config.spreadsheetId,
|
|
24
|
+
requestBody: {
|
|
25
|
+
requests: [
|
|
26
|
+
{
|
|
27
|
+
addSheet: {
|
|
28
|
+
properties: {
|
|
29
|
+
title: options.sheet,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
spinner.succeed(`Sheet "${options.sheet}" created`);
|
|
38
|
+
console.log();
|
|
39
|
+
console.log(` Read it: ${chalk.cyan(`npx google-sheet-mcp read -s "${options.sheet}"`)}`);
|
|
40
|
+
|
|
41
|
+
} catch (err) {
|
|
42
|
+
spinner.fail(`Failed: ${err.message}`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Show MCP configuration snippets for all supported IDEs.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
|
|
7
|
+
export function showMCPConfig() {
|
|
8
|
+
console.log(chalk.bold("━━━ MCP Configuration ━━━"));
|
|
9
|
+
console.log();
|
|
10
|
+
console.log(chalk.bold("Cursor (.cursor/mcp.json):"));
|
|
11
|
+
console.log(
|
|
12
|
+
chalk.white(`{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"google-sheets": {
|
|
15
|
+
"command": "npx",
|
|
16
|
+
"args": ["google-sheet-mcp"]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}`)
|
|
20
|
+
);
|
|
21
|
+
console.log();
|
|
22
|
+
console.log(chalk.bold("VS Code (.vscode/mcp.json):"));
|
|
23
|
+
console.log(
|
|
24
|
+
chalk.white(`{
|
|
25
|
+
"servers": {
|
|
26
|
+
"google-sheets": {
|
|
27
|
+
"type": "stdio",
|
|
28
|
+
"command": "npx",
|
|
29
|
+
"args": ["google-sheet-mcp"]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}`)
|
|
33
|
+
);
|
|
34
|
+
console.log();
|
|
35
|
+
console.log(chalk.bold("Claude Code (.claude/mcp.json):"));
|
|
36
|
+
console.log(
|
|
37
|
+
chalk.white(`{
|
|
38
|
+
"mcpServers": {
|
|
39
|
+
"google-sheets": {
|
|
40
|
+
"command": "npx",
|
|
41
|
+
"args": ["google-sheet-mcp"]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}`)
|
|
45
|
+
);
|
|
46
|
+
console.log();
|
|
47
|
+
console.log(chalk.bold("Codex CLI (codex.json):"));
|
|
48
|
+
console.log(
|
|
49
|
+
chalk.white(`{
|
|
50
|
+
"mcpServers": {
|
|
51
|
+
"google-sheets": {
|
|
52
|
+
"command": "npx",
|
|
53
|
+
"args": ["-y", "google-sheet-mcp"]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}`)
|
|
57
|
+
);
|
|
58
|
+
console.log();
|
|
59
|
+
console.log(chalk.gray("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
|
60
|
+
console.log();
|
|
61
|
+
console.log(
|
|
62
|
+
"After adding the config, restart your IDE. The AI will see these tools:"
|
|
63
|
+
);
|
|
64
|
+
console.log(chalk.green(" • sheets_list_tabs — list all sheets"));
|
|
65
|
+
console.log(chalk.green(" • sheets_read_range — read data"));
|
|
66
|
+
console.log(chalk.green(" • sheets_get_sheet — sheet metadata"));
|
|
67
|
+
console.log(chalk.green(" • sheets_write_range — write data"));
|
|
68
|
+
console.log(chalk.green(" • sheets_create_tab — create new tab"));
|
|
69
|
+
console.log(chalk.green(" • sheets_append_row — append a row"));
|
|
70
|
+
console.log();
|
|
71
|
+
}
|