google-tools-mcp 1.2.1 → 1.2.3

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 (2) hide show
  1. package/dist/setup.js +128 -72
  2. package/package.json +3 -1
package/dist/setup.js CHANGED
@@ -1,6 +1,7 @@
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';
@@ -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');
@@ -74,106 +71,165 @@ function runCommand(cmd) {
74
71
  });
75
72
  }
76
73
 
74
+ function cancelled() {
75
+ p.cancel('Setup cancelled.');
76
+ process.exit(0);
77
+ }
78
+
77
79
  // ---------------------------------------------------------------------------
78
80
  // Setup flow
79
81
  // ---------------------------------------------------------------------------
80
82
  export async function runSetup() {
81
- const rl = readline.createInterface({
82
- input: process.stdin,
83
- 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',
84
100
  });
85
-
86
- console.log('\n🔧 google-tools-mcp setup\n');
87
-
88
- // Step 1: Enable APIs
89
- console.log('Step 1: Enable Google APIs');
90
- console.log('─────────────────────────');
91
- console.log('Opening Google Cloud Console to enable all required APIs.');
92
- console.log('If you don\'t have a project yet, it will ask you to create one.\n');
101
+ if (p.isCancel(ready1)) cancelled();
93
102
  openBrowser(ENABLE_APIS_URL);
94
- await prompt(rl, 'Press Enter when done...');
95
-
96
- // Step 2: OAuth consent screen
97
- console.log('\nStep 2: Configure OAuth consent screen');
98
- console.log('──────────────────────────────────────');
99
- console.log('Opening the OAuth consent screen configuration.');
100
- console.log(' • Choose "External" for the user type');
101
- console.log(' • Fill in the app name (anything is fine, e.g. "MCP")');
102
- console.log(' • Add your email as a test user\n');
103
+ p.log.message(chalk.dim(ENABLE_APIS_URL));
104
+
105
+ const step1 = await p.confirm({
106
+ message: 'Done enabling APIs?',
107
+ active: 'yes, continue',
108
+ inactive: 'not yet',
109
+ });
110
+ if (p.isCancel(step1)) cancelled();
111
+
112
+ // ── Step 2: OAuth consent screen ─────────────────────────────────────
113
+ p.log.step(chalk.cyan.bold('Step 2') + chalk.dim(' · ') + 'Configure OAuth consent screen');
114
+ p.log.message([
115
+ `${chalk.white('›')} Choose ${chalk.bold('"External"')} for the user type`,
116
+ `${chalk.white('›')} Fill in the app name ${chalk.dim('(anything works, e.g. "MCP")')}`,
117
+ `${chalk.white('›')} Add your email as a ${chalk.bold('test user')}`,
118
+ ].join('\n'));
119
+
120
+ const ready2 = await p.confirm({
121
+ message: 'Ready? This will open your browser.',
122
+ active: 'open browser',
123
+ inactive: 'not yet',
124
+ });
125
+ if (p.isCancel(ready2)) cancelled();
103
126
  openBrowser(CONSENT_SCREEN_URL);
104
- await prompt(rl, 'Press Enter when done...');
105
-
106
- // Step 3: Create OAuth credentials
107
- console.log('\nStep 3: Create OAuth Client ID');
108
- console.log('──────────────────────────────');
109
- console.log('Opening the credentials page.');
110
- console.log(' • Select "Desktop application" as the type');
111
- console.log(' • Click Create, then copy the Client ID and Client Secret\n');
127
+ p.log.message(chalk.dim(CONSENT_SCREEN_URL));
128
+
129
+ const step2 = await p.confirm({
130
+ message: 'Done configuring consent screen?',
131
+ active: 'yes, continue',
132
+ inactive: 'not yet',
133
+ });
134
+ if (p.isCancel(step2)) cancelled();
135
+
136
+ // ── Step 3: Create OAuth credentials ─────────────────────────────────
137
+ p.log.step(chalk.cyan.bold('Step 3') + chalk.dim(' · ') + 'Create OAuth Client ID');
138
+ p.log.message([
139
+ `${chalk.white('›')} Select ${chalk.bold('"Desktop application"')} as the type`,
140
+ `${chalk.white('›')} Click ${chalk.bold('Create')}`,
141
+ `${chalk.white('›')} Copy the ${chalk.bold('Client ID')} and ${chalk.bold('Client Secret')}`,
142
+ ].join('\n'));
143
+
144
+ const ready3 = await p.confirm({
145
+ message: 'Ready? This will open your browser.',
146
+ active: 'open browser',
147
+ inactive: 'not yet',
148
+ });
149
+ if (p.isCancel(ready3)) cancelled();
112
150
  openBrowser(CREATE_CREDENTIALS_URL);
113
- await prompt(rl, 'Press Enter when you have your Client ID and Secret...');
151
+ p.log.message(chalk.dim(CREATE_CREDENTIALS_URL));
152
+
153
+ const credentials = await p.group({
154
+ clientId: () => p.text({
155
+ message: 'Client ID',
156
+ placeholder: 'xxxx.apps.googleusercontent.com',
157
+ validate: (v) => {
158
+ if (!v?.trim()) return 'Client ID is required';
159
+ },
160
+ }),
161
+ clientSecret: () => p.text({
162
+ message: 'Client Secret',
163
+ placeholder: 'GOCSPX-xxxx',
164
+ validate: (v) => {
165
+ if (!v?.trim()) return 'Client Secret is required';
166
+ },
167
+ }),
168
+ });
169
+ if (p.isCancel(credentials)) cancelled();
114
170
 
115
- // Step 4: Collect credentials
116
- console.log('');
117
- const clientId = (await prompt(rl, 'Client ID: ')).trim();
118
- const clientSecret = (await prompt(rl, 'Client Secret: ')).trim();
171
+ const clientId = credentials.clientId.trim();
172
+ const clientSecret = credentials.clientSecret.trim();
119
173
 
120
- if (!clientId || !clientSecret) {
121
- rl.close();
122
- throw new Error('Client ID and Client Secret are required.');
123
- }
124
-
125
- // Save to config dir
174
+ // ── Save credentials ─────────────────────────────────────────────────
126
175
  const configDir = getConfigDir();
127
176
  await fs.mkdir(configDir, { recursive: true });
128
177
  const envPath = path.join(configDir, '.env');
129
178
  const envContent = `GOOGLE_CLIENT_ID=${clientId}\nGOOGLE_CLIENT_SECRET=${clientSecret}\n`;
130
179
  await fs.writeFile(envPath, envContent);
131
180
  const displayPath = envPath.replace(os.homedir(), '~');
132
- console.log(`\nCredentials saved to ${displayPath}`);
181
+ p.log.success(`Credentials saved to ${chalk.dim(displayPath)}`);
133
182
 
134
- // Step 5: Run OAuth flow
135
- console.log('\nStep 4: Authenticate with Google');
136
- console.log('────────────────────────────────');
137
- console.log('Opening browser for OAuth consent...\n');
183
+ // ── Step 4: Authenticate ─────────────────────────────────────────────
184
+ p.log.step(chalk.cyan.bold('Step 4') + chalk.dim(' · ') + 'Authenticate with Google');
185
+ p.log.message('Opening browser for OAuth consent...');
138
186
 
139
- // Set env vars so auth picks them up immediately
140
187
  process.env.GOOGLE_CLIENT_ID = clientId;
141
188
  process.env.GOOGLE_CLIENT_SECRET = clientSecret;
142
189
 
143
190
  const { runAuthFlow } = await import('./auth.js');
144
191
  await runAuthFlow();
145
192
 
146
- // Step 6: Install into MCP client
147
- console.log('\nStep 5: Install');
148
- console.log('───────────────');
193
+ p.log.success('Authenticated with Google!');
194
+
195
+ // ── Step 5: Install ──────────────────────────────────────────────────
196
+ p.log.step(chalk.cyan.bold('Step 5') + chalk.dim(' · ') + 'Install MCP server');
149
197
 
150
198
  const hasClaude = hasCli('claude');
151
199
  if (hasClaude) {
152
- const answer = (await prompt(rl, 'Add to Claude Code as a user-scope MCP server? (Y/n) ')).trim().toLowerCase();
153
- if (answer === '' || answer === 'y' || answer === 'yes') {
154
- console.log('Running: claude mcp add -s user google -- npx -y google-tools-mcp');
200
+ const install = await p.confirm({
201
+ message: 'Add to Claude Code as a user-scope MCP server?',
202
+ active: 'yes',
203
+ inactive: 'no',
204
+ initialValue: true,
205
+ });
206
+ if (p.isCancel(install)) cancelled();
207
+
208
+ if (install) {
209
+ const s = p.spinner();
210
+ s.start('Adding to Claude Code...');
155
211
  try {
156
212
  await runCommand('claude mcp add -s user google -- npx -y google-tools-mcp');
157
- console.log('Added to Claude Code!');
213
+ s.stop('Added to Claude Code!');
158
214
  } catch (err) {
159
- console.error('Failed to add:', err.message);
160
- console.log('You can add it manually:');
161
- console.log(' claude mcp add -s user google -- npx -y google-tools-mcp');
215
+ s.stop('Failed to add automatically');
216
+ p.log.warn(`Error: ${err.message}`);
217
+ p.log.message(`Run manually:\n${chalk.cyan('claude mcp add -s user google -- npx -y google-tools-mcp')}`);
162
218
  }
163
219
  } else {
164
- console.log('\nTo add it later:');
165
- console.log(' claude mcp add -s user google -- npx -y google-tools-mcp');
220
+ p.log.message(`To add later:\n${chalk.cyan('claude mcp add -s user google -- npx -y google-tools-mcp')}`);
166
221
  }
167
222
  } else {
168
- console.log('Add google-tools-mcp to your MCP client config:');
169
- console.log('');
170
- console.log(' Claude Code:');
171
- console.log(' claude mcp add -s user google -- npx -y google-tools-mcp');
172
- console.log('');
173
- console.log(' Other clients (.mcp.json / claude_desktop_config.json):');
174
- console.log(' { "mcpServers": { "google": { "command": "npx", "args": ["-y", "google-tools-mcp"] } } }');
223
+ p.log.message([
224
+ 'Add to your MCP client:',
225
+ '',
226
+ chalk.dim('Claude Code:'),
227
+ chalk.cyan(' claude mcp add -s user google -- npx -y google-tools-mcp'),
228
+ '',
229
+ chalk.dim('Other clients') + chalk.dim(' (.mcp.json):'),
230
+ chalk.cyan(' { "mcpServers": { "google": { "command": "npx", "args": ["-y", "google-tools-mcp"] } } }'),
231
+ ].join('\n'));
175
232
  }
176
233
 
177
- rl.close();
178
- console.log('\n✅ Setup complete! You\'re ready to use google-tools-mcp.\n');
234
+ p.outro(chalk.green.bold('Setup complete!') + chalk.dim(' You\'re ready to use google-tools-mcp.'));
179
235
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "google-tools-mcp",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
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",