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.
- package/dist/setup.js +128 -72
- package/package.json +3 -1
package/dist/setup.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
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';
|
|
@@ -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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
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
|
-
|
|
116
|
-
|
|
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
|
-
|
|
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
|
-
|
|
181
|
+
p.log.success(`Credentials saved to ${chalk.dim(displayPath)}`);
|
|
133
182
|
|
|
134
|
-
// Step
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
213
|
+
s.stop('Added to Claude Code!');
|
|
158
214
|
} catch (err) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
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.
|
|
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",
|