google-tools-mcp 1.2.0 → 1.2.2
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/README.md +1 -1
- package/dist/setup.js +155 -55
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ The **easiest way** to connect your AI agent to Google Workspace.
|
|
|
5
5
|
**153 tools** for Drive, Docs, Sheets, Gmail, Calendar, and Forms — all in one package. One install, one auth, and you're done.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
npx -y google-tools-mcp setup
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Why google-tools-mcp?
|
package/dist/setup.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// Guided setup wizard for google-tools-mcp.
|
|
2
|
-
//
|
|
3
|
-
import * as
|
|
2
|
+
// Rich terminal UI using @clack/prompts.
|
|
3
|
+
import * as p from '@clack/prompts';
|
|
4
|
+
import chalk from 'chalk';
|
|
4
5
|
import * as fs from 'fs/promises';
|
|
5
6
|
import * as path from 'path';
|
|
6
7
|
import * as os from 'os';
|
|
7
|
-
import { exec } from 'child_process';
|
|
8
|
+
import { exec, execSync } from 'child_process';
|
|
8
9
|
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
10
11
|
// Config
|
|
@@ -44,10 +45,6 @@ function openBrowser(url) {
|
|
|
44
45
|
exec(cmd, () => {});
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
function prompt(rl, question) {
|
|
48
|
-
return new Promise((resolve) => rl.question(question, resolve));
|
|
49
|
-
}
|
|
50
|
-
|
|
51
48
|
function getConfigDir() {
|
|
52
49
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
53
50
|
const base = xdg || path.join(os.homedir(), '.config');
|
|
@@ -56,77 +53,180 @@ function getConfigDir() {
|
|
|
56
53
|
return profile ? path.join(baseDir, profile) : baseDir;
|
|
57
54
|
}
|
|
58
55
|
|
|
56
|
+
function hasCli(name) {
|
|
57
|
+
try {
|
|
58
|
+
execSync(`${name} --version`, { stdio: 'ignore' });
|
|
59
|
+
return true;
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function runCommand(cmd) {
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
exec(cmd, (err, stdout, stderr) => {
|
|
68
|
+
if (err) reject(new Error(stderr || err.message));
|
|
69
|
+
else resolve(stdout);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function cancelled() {
|
|
75
|
+
p.cancel('Setup cancelled.');
|
|
76
|
+
process.exit(0);
|
|
77
|
+
}
|
|
78
|
+
|
|
59
79
|
// ---------------------------------------------------------------------------
|
|
60
80
|
// Setup flow
|
|
61
81
|
// ---------------------------------------------------------------------------
|
|
62
82
|
export async function runSetup() {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
83
|
+
console.clear();
|
|
84
|
+
|
|
85
|
+
p.intro(chalk.bgCyan.bold.white(' google-tools-mcp setup '));
|
|
86
|
+
|
|
87
|
+
// ── Step 1: Enable APIs ──────────────────────────────────────────────
|
|
88
|
+
p.log.step(chalk.cyan.bold('Step 1') + chalk.dim(' · ') + 'Enable Google APIs');
|
|
89
|
+
p.log.message([
|
|
90
|
+
'This will open Google Cloud Console to enable all required APIs.',
|
|
91
|
+
chalk.dim('If you don\'t have a project yet, it will ask you to create one.'),
|
|
92
|
+
'',
|
|
93
|
+
chalk.dim('APIs: ') + APIS.map(a => chalk.yellow(a.replace('.googleapis.com', ''))).join(chalk.dim(', ')),
|
|
94
|
+
].join('\n'));
|
|
95
|
+
|
|
96
|
+
const ready1 = await p.confirm({
|
|
97
|
+
message: 'Ready? This will open your browser.',
|
|
98
|
+
active: 'open browser',
|
|
99
|
+
inactive: 'not yet',
|
|
66
100
|
});
|
|
67
|
-
|
|
68
|
-
console.log('\n🔧 google-tools-mcp setup\n');
|
|
69
|
-
|
|
70
|
-
// Step 1: Enable APIs
|
|
71
|
-
console.log('Step 1: Enable Google APIs');
|
|
72
|
-
console.log('─────────────────────────');
|
|
73
|
-
console.log('Opening Google Cloud Console to enable all required APIs.');
|
|
74
|
-
console.log('If you don\'t have a project yet, it will ask you to create one.\n');
|
|
101
|
+
if (p.isCancel(ready1)) cancelled();
|
|
75
102
|
openBrowser(ENABLE_APIS_URL);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
103
|
+
|
|
104
|
+
const step1 = await p.confirm({
|
|
105
|
+
message: 'Done enabling APIs?',
|
|
106
|
+
active: 'yes, continue',
|
|
107
|
+
inactive: 'not yet',
|
|
108
|
+
});
|
|
109
|
+
if (p.isCancel(step1)) cancelled();
|
|
110
|
+
|
|
111
|
+
// ── Step 2: OAuth consent screen ─────────────────────────────────────
|
|
112
|
+
p.log.step(chalk.cyan.bold('Step 2') + chalk.dim(' · ') + 'Configure OAuth consent screen');
|
|
113
|
+
p.log.message([
|
|
114
|
+
`${chalk.white('›')} Choose ${chalk.bold('"External"')} for the user type`,
|
|
115
|
+
`${chalk.white('›')} Fill in the app name ${chalk.dim('(anything works, e.g. "MCP")')}`,
|
|
116
|
+
`${chalk.white('›')} Add your email as a ${chalk.bold('test user')}`,
|
|
117
|
+
].join('\n'));
|
|
118
|
+
|
|
119
|
+
const ready2 = await p.confirm({
|
|
120
|
+
message: 'Ready? This will open your browser.',
|
|
121
|
+
active: 'open browser',
|
|
122
|
+
inactive: 'not yet',
|
|
123
|
+
});
|
|
124
|
+
if (p.isCancel(ready2)) cancelled();
|
|
85
125
|
openBrowser(CONSENT_SCREEN_URL);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
126
|
+
|
|
127
|
+
const step2 = await p.confirm({
|
|
128
|
+
message: 'Done configuring consent screen?',
|
|
129
|
+
active: 'yes, continue',
|
|
130
|
+
inactive: 'not yet',
|
|
131
|
+
});
|
|
132
|
+
if (p.isCancel(step2)) cancelled();
|
|
133
|
+
|
|
134
|
+
// ── Step 3: Create OAuth credentials ─────────────────────────────────
|
|
135
|
+
p.log.step(chalk.cyan.bold('Step 3') + chalk.dim(' · ') + 'Create OAuth Client ID');
|
|
136
|
+
p.log.message([
|
|
137
|
+
`${chalk.white('›')} Select ${chalk.bold('"Desktop application"')} as the type`,
|
|
138
|
+
`${chalk.white('›')} Click ${chalk.bold('Create')}`,
|
|
139
|
+
`${chalk.white('›')} Copy the ${chalk.bold('Client ID')} and ${chalk.bold('Client Secret')}`,
|
|
140
|
+
].join('\n'));
|
|
141
|
+
|
|
142
|
+
const ready3 = await p.confirm({
|
|
143
|
+
message: 'Ready? This will open your browser.',
|
|
144
|
+
active: 'open browser',
|
|
145
|
+
inactive: 'not yet',
|
|
146
|
+
});
|
|
147
|
+
if (p.isCancel(ready3)) cancelled();
|
|
94
148
|
openBrowser(CREATE_CREDENTIALS_URL);
|
|
95
|
-
await prompt(rl, 'Press Enter when you have your Client ID and Secret...');
|
|
96
149
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
150
|
+
const credentials = await p.group({
|
|
151
|
+
clientId: () => p.text({
|
|
152
|
+
message: 'Client ID',
|
|
153
|
+
placeholder: 'xxxx.apps.googleusercontent.com',
|
|
154
|
+
validate: (v) => {
|
|
155
|
+
if (!v?.trim()) return 'Client ID is required';
|
|
156
|
+
},
|
|
157
|
+
}),
|
|
158
|
+
clientSecret: () => p.text({
|
|
159
|
+
message: 'Client Secret',
|
|
160
|
+
placeholder: 'GOCSPX-xxxx',
|
|
161
|
+
validate: (v) => {
|
|
162
|
+
if (!v?.trim()) return 'Client Secret is required';
|
|
163
|
+
},
|
|
164
|
+
}),
|
|
165
|
+
});
|
|
166
|
+
if (p.isCancel(credentials)) cancelled();
|
|
101
167
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
throw new Error('Client ID and Client Secret are required.');
|
|
105
|
-
}
|
|
168
|
+
const clientId = credentials.clientId.trim();
|
|
169
|
+
const clientSecret = credentials.clientSecret.trim();
|
|
106
170
|
|
|
107
|
-
// Save
|
|
171
|
+
// ── Save credentials ─────────────────────────────────────────────────
|
|
108
172
|
const configDir = getConfigDir();
|
|
109
173
|
await fs.mkdir(configDir, { recursive: true });
|
|
110
174
|
const envPath = path.join(configDir, '.env');
|
|
111
175
|
const envContent = `GOOGLE_CLIENT_ID=${clientId}\nGOOGLE_CLIENT_SECRET=${clientSecret}\n`;
|
|
112
176
|
await fs.writeFile(envPath, envContent);
|
|
113
177
|
const displayPath = envPath.replace(os.homedir(), '~');
|
|
114
|
-
|
|
178
|
+
p.log.success(`Credentials saved to ${chalk.dim(displayPath)}`);
|
|
115
179
|
|
|
116
|
-
// Step
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
console.log('Opening browser for OAuth consent...\n');
|
|
120
|
-
rl.close();
|
|
180
|
+
// ── Step 4: Authenticate ─────────────────────────────────────────────
|
|
181
|
+
p.log.step(chalk.cyan.bold('Step 4') + chalk.dim(' · ') + 'Authenticate with Google');
|
|
182
|
+
p.log.message('Opening browser for OAuth consent...');
|
|
121
183
|
|
|
122
|
-
// Set env vars so auth picks them up immediately
|
|
123
184
|
process.env.GOOGLE_CLIENT_ID = clientId;
|
|
124
185
|
process.env.GOOGLE_CLIENT_SECRET = clientSecret;
|
|
125
186
|
|
|
126
187
|
const { runAuthFlow } = await import('./auth.js');
|
|
127
188
|
await runAuthFlow();
|
|
128
189
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
190
|
+
p.log.success('Authenticated with Google!');
|
|
191
|
+
|
|
192
|
+
// ── Step 5: Install ──────────────────────────────────────────────────
|
|
193
|
+
p.log.step(chalk.cyan.bold('Step 5') + chalk.dim(' · ') + 'Install MCP server');
|
|
194
|
+
|
|
195
|
+
const hasClaude = hasCli('claude');
|
|
196
|
+
if (hasClaude) {
|
|
197
|
+
const install = await p.confirm({
|
|
198
|
+
message: 'Add to Claude Code as a user-scope MCP server?',
|
|
199
|
+
active: 'yes',
|
|
200
|
+
inactive: 'no',
|
|
201
|
+
initialValue: true,
|
|
202
|
+
});
|
|
203
|
+
if (p.isCancel(install)) cancelled();
|
|
204
|
+
|
|
205
|
+
if (install) {
|
|
206
|
+
const s = p.spinner();
|
|
207
|
+
s.start('Adding to Claude Code...');
|
|
208
|
+
try {
|
|
209
|
+
await runCommand('claude mcp add -s user google -- npx -y google-tools-mcp');
|
|
210
|
+
s.stop('Added to Claude Code!');
|
|
211
|
+
} catch (err) {
|
|
212
|
+
s.stop('Failed to add automatically');
|
|
213
|
+
p.log.warn(`Error: ${err.message}`);
|
|
214
|
+
p.log.message(`Run manually:\n${chalk.cyan('claude mcp add -s user google -- npx -y google-tools-mcp')}`);
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
p.log.message(`To add later:\n${chalk.cyan('claude mcp add -s user google -- npx -y google-tools-mcp')}`);
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
p.log.message([
|
|
221
|
+
'Add to your MCP client:',
|
|
222
|
+
'',
|
|
223
|
+
chalk.dim('Claude Code:'),
|
|
224
|
+
chalk.cyan(' claude mcp add -s user google -- npx -y google-tools-mcp'),
|
|
225
|
+
'',
|
|
226
|
+
chalk.dim('Other clients') + chalk.dim(' (.mcp.json):'),
|
|
227
|
+
chalk.cyan(' { "mcpServers": { "google": { "command": "npx", "args": ["-y", "google-tools-mcp"] } } }'),
|
|
228
|
+
].join('\n'));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
p.outro(chalk.green.bold('Setup complete!') + chalk.dim(' You\'re ready to use google-tools-mcp.'));
|
|
132
232
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "google-tools-mcp",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "The easiest MCP server for Google Workspace — Drive, Docs, Sheets, Gmail, Calendar, and Forms. 153 tools with one-click browser auth. Read Word docs, PDFs, and spreadsheets straight from Drive.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -62,6 +62,8 @@
|
|
|
62
62
|
},
|
|
63
63
|
"license": "MIT",
|
|
64
64
|
"dependencies": {
|
|
65
|
+
"@clack/prompts": "^1.2.0",
|
|
66
|
+
"chalk": "^5.6.2",
|
|
65
67
|
"fastmcp": "^3.24.0",
|
|
66
68
|
"google-auth-library": "^10.5.0",
|
|
67
69
|
"googleapis": "^171.4.0",
|