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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/setup.js +155 -55
  3. 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
- claude mcp add -s user google -- npx -y google-tools-mcp
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
- // Opens the right Google Cloud Console URLs and saves credentials.
3
- import * as readline from 'readline';
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
- const rl = readline.createInterface({
64
- input: process.stdin,
65
- output: process.stdout,
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
- await prompt(rl, 'Press Enter when done...');
77
-
78
- // Step 2: OAuth consent screen
79
- console.log('\nStep 2: Configure OAuth consent screen');
80
- console.log('──────────────────────────────────────');
81
- console.log('Opening the OAuth consent screen configuration.');
82
- console.log(' • Choose "External" for the user type');
83
- console.log(' • Fill in the app name (anything is fine, e.g. "MCP")');
84
- console.log(' • Add your email as a test user\n');
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
- await prompt(rl, 'Press Enter when done...');
87
-
88
- // Step 3: Create OAuth credentials
89
- console.log('\nStep 3: Create OAuth Client ID');
90
- console.log('──────────────────────────────');
91
- console.log('Opening the credentials page.');
92
- console.log(' • Select "Desktop application" as the type');
93
- console.log(' • Click Create, then copy the Client ID and Client Secret\n');
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
- // Step 4: Collect credentials
98
- console.log('');
99
- const clientId = (await prompt(rl, 'Client ID: ')).trim();
100
- const clientSecret = (await prompt(rl, 'Client Secret: ')).trim();
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
- if (!clientId || !clientSecret) {
103
- rl.close();
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 to config dir
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
- console.log(`\nCredentials saved to ${displayPath}`);
178
+ p.log.success(`Credentials saved to ${chalk.dim(displayPath)}`);
115
179
 
116
- // Step 5: Run OAuth flow
117
- console.log('\nStep 4: Authenticate with Google');
118
- console.log('────────────────────────────────');
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
- console.log('\n✅ Setup complete! You\'re ready to use google-tools-mcp.');
130
- console.log('\nAdd it to Claude Code:');
131
- console.log(' claude mcp add -s user google -- npx -y google-tools-mcp\n');
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.0",
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",