google-workspace-mcp 2.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.
Files changed (75) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +765 -0
  3. package/dist/accounts.d.ts +85 -0
  4. package/dist/accounts.d.ts.map +1 -0
  5. package/dist/accounts.js +520 -0
  6. package/dist/accounts.js.map +1 -0
  7. package/dist/auth.d.ts +4 -0
  8. package/dist/auth.d.ts.map +1 -0
  9. package/dist/auth.js +206 -0
  10. package/dist/auth.js.map +1 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +426 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/errorHelpers.d.ts +40 -0
  16. package/dist/errorHelpers.d.ts.map +1 -0
  17. package/dist/errorHelpers.js +52 -0
  18. package/dist/errorHelpers.js.map +1 -0
  19. package/dist/googleDocsApiHelpers.d.ts +118 -0
  20. package/dist/googleDocsApiHelpers.d.ts.map +1 -0
  21. package/dist/googleDocsApiHelpers.js +850 -0
  22. package/dist/googleDocsApiHelpers.js.map +1 -0
  23. package/dist/googleSheetsApiHelpers.d.ts +75 -0
  24. package/dist/googleSheetsApiHelpers.d.ts.map +1 -0
  25. package/dist/googleSheetsApiHelpers.js +376 -0
  26. package/dist/googleSheetsApiHelpers.js.map +1 -0
  27. package/dist/server.d.ts +2 -0
  28. package/dist/server.d.ts.map +1 -0
  29. package/dist/server.js +119 -0
  30. package/dist/server.js.map +1 -0
  31. package/dist/serverWrapper.d.ts +21 -0
  32. package/dist/serverWrapper.d.ts.map +1 -0
  33. package/dist/serverWrapper.js +74 -0
  34. package/dist/serverWrapper.js.map +1 -0
  35. package/dist/tools/accounts.tools.d.ts +3 -0
  36. package/dist/tools/accounts.tools.d.ts.map +1 -0
  37. package/dist/tools/accounts.tools.js +154 -0
  38. package/dist/tools/accounts.tools.js.map +1 -0
  39. package/dist/tools/calendar.tools.d.ts +3 -0
  40. package/dist/tools/calendar.tools.d.ts.map +1 -0
  41. package/dist/tools/calendar.tools.js +487 -0
  42. package/dist/tools/calendar.tools.js.map +1 -0
  43. package/dist/tools/docs.tools.d.ts +3 -0
  44. package/dist/tools/docs.tools.d.ts.map +1 -0
  45. package/dist/tools/docs.tools.js +1766 -0
  46. package/dist/tools/docs.tools.js.map +1 -0
  47. package/dist/tools/drive.tools.d.ts +3 -0
  48. package/dist/tools/drive.tools.d.ts.map +1 -0
  49. package/dist/tools/drive.tools.js +1001 -0
  50. package/dist/tools/drive.tools.js.map +1 -0
  51. package/dist/tools/forms.tools.d.ts +3 -0
  52. package/dist/tools/forms.tools.d.ts.map +1 -0
  53. package/dist/tools/forms.tools.js +370 -0
  54. package/dist/tools/forms.tools.js.map +1 -0
  55. package/dist/tools/gmail.tools.d.ts +3 -0
  56. package/dist/tools/gmail.tools.d.ts.map +1 -0
  57. package/dist/tools/gmail.tools.js +520 -0
  58. package/dist/tools/gmail.tools.js.map +1 -0
  59. package/dist/tools/sheets.tools.d.ts +3 -0
  60. package/dist/tools/sheets.tools.d.ts.map +1 -0
  61. package/dist/tools/sheets.tools.js +521 -0
  62. package/dist/tools/sheets.tools.js.map +1 -0
  63. package/dist/tools/slides.tools.d.ts +3 -0
  64. package/dist/tools/slides.tools.d.ts.map +1 -0
  65. package/dist/tools/slides.tools.js +323 -0
  66. package/dist/tools/slides.tools.js.map +1 -0
  67. package/dist/types.d.ts +507 -0
  68. package/dist/types.d.ts.map +1 -0
  69. package/dist/types.js +185 -0
  70. package/dist/types.js.map +1 -0
  71. package/dist/urlHelpers.d.ts +60 -0
  72. package/dist/urlHelpers.d.ts.map +1 -0
  73. package/dist/urlHelpers.js +101 -0
  74. package/dist/urlHelpers.js.map +1 -0
  75. package/package.json +77 -0
package/dist/auth.js ADDED
@@ -0,0 +1,206 @@
1
+ // src/auth.ts
2
+ import { google } from 'googleapis';
3
+ import { JWT } from 'google-auth-library'; // ADDED: Import for Service Account client
4
+ import * as fs from 'fs/promises';
5
+ import * as path from 'path';
6
+ import * as readline from 'readline/promises';
7
+ import * as http from 'http';
8
+ import { fileURLToPath } from 'url';
9
+ // --- Calculate paths relative to this script file (ESM way) ---
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+ const projectRootDir = path.resolve(__dirname, '..');
13
+ const TOKEN_PATH = path.join(projectRootDir, 'token.json');
14
+ const CREDENTIALS_PATH = path.join(projectRootDir, 'credentials.json');
15
+ // --- End of path calculation ---
16
+ const SCOPES = [
17
+ 'https://www.googleapis.com/auth/documents',
18
+ 'https://www.googleapis.com/auth/drive', // Full Drive access for listing, searching, and document discovery
19
+ 'https://www.googleapis.com/auth/spreadsheets', // Google Sheets API access
20
+ ];
21
+ // --- NEW FUNCTION: Handles Service Account Authentication ---
22
+ // This entire function is new. It is called only when the
23
+ // SERVICE_ACCOUNT_PATH environment variable is set.
24
+ // Supports domain-wide delegation via GOOGLE_IMPERSONATE_USER env var.
25
+ async function authorizeWithServiceAccount() {
26
+ const serviceAccountPath = process.env.SERVICE_ACCOUNT_PATH;
27
+ if (!serviceAccountPath) {
28
+ throw new Error('SERVICE_ACCOUNT_PATH environment variable is required');
29
+ }
30
+ const impersonateUser = process.env.GOOGLE_IMPERSONATE_USER; // Optional: email of user to impersonate
31
+ try {
32
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- serviceAccountPath from environment variable, admin-controlled
33
+ const keyFileContent = await fs.readFile(serviceAccountPath, 'utf8');
34
+ const serviceAccountKey = JSON.parse(keyFileContent);
35
+ const auth = new JWT({
36
+ email: serviceAccountKey.client_email,
37
+ key: serviceAccountKey.private_key,
38
+ scopes: SCOPES,
39
+ subject: impersonateUser, // Enables domain-wide delegation when set
40
+ });
41
+ await auth.authorize();
42
+ if (impersonateUser) {
43
+ console.error(`Service Account authentication successful, impersonating: ${impersonateUser}`);
44
+ }
45
+ else {
46
+ console.error('Service Account authentication successful!');
47
+ }
48
+ return auth;
49
+ }
50
+ catch (error) {
51
+ const isNodeError = (e) => e instanceof Error && 'code' in e;
52
+ if (isNodeError(error) && error.code === 'ENOENT') {
53
+ console.error(`FATAL: Service account key file not found at path: ${serviceAccountPath}`);
54
+ throw new Error('Service account key file not found. Please check the path in SERVICE_ACCOUNT_PATH.');
55
+ }
56
+ const message = error instanceof Error ? error.message : String(error);
57
+ console.error('FATAL: Error loading or authorizing the service account key:', message);
58
+ throw new Error('Failed to authorize using the service account. Ensure the key file is valid and the path is correct.');
59
+ }
60
+ }
61
+ // --- END OF NEW FUNCTION---
62
+ async function loadSavedCredentialsIfExist() {
63
+ try {
64
+ const content = await fs.readFile(TOKEN_PATH);
65
+ const credentials = JSON.parse(content.toString());
66
+ const { client_secret, client_id, redirect_uris } = await loadClientSecrets();
67
+ const client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
68
+ client.setCredentials(credentials);
69
+ return client;
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ }
75
+ async function loadClientSecrets() {
76
+ const content = await fs.readFile(CREDENTIALS_PATH);
77
+ const keys = JSON.parse(content.toString());
78
+ const key = keys.installed ?? keys.web;
79
+ if (!key)
80
+ throw new Error('Could not find client secrets in credentials.json.');
81
+ return {
82
+ client_id: key.client_id,
83
+ client_secret: key.client_secret,
84
+ redirect_uris: key.redirect_uris ?? ['http://localhost:3000/'], // Default for web clients
85
+ client_type: keys.web ? 'web' : 'installed',
86
+ };
87
+ }
88
+ async function saveCredentials(client) {
89
+ const { client_secret, client_id } = await loadClientSecrets();
90
+ const payload = JSON.stringify({
91
+ type: 'authorized_user',
92
+ client_id: client_id,
93
+ client_secret: client_secret,
94
+ refresh_token: client.credentials.refresh_token,
95
+ });
96
+ await fs.writeFile(TOKEN_PATH, payload);
97
+ console.error('Token stored to', TOKEN_PATH);
98
+ }
99
+ async function authenticate() {
100
+ const { client_secret, client_id, redirect_uris, client_type } = await loadClientSecrets();
101
+ // Use localhost redirect for desktop apps (OOB flow is deprecated)
102
+ const redirectUri = client_type === 'web' ? redirect_uris[0] : 'http://localhost:3000';
103
+ console.error(`DEBUG: Using redirect URI: ${redirectUri}`);
104
+ console.error(`DEBUG: Client type: ${client_type}`);
105
+ const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirectUri);
106
+ const authorizeUrl = oAuth2Client.generateAuthUrl({
107
+ access_type: 'offline',
108
+ scope: SCOPES.join(' '),
109
+ });
110
+ console.error('DEBUG: Generated auth URL:', authorizeUrl);
111
+ console.error('Authorize this app by visiting this url:', authorizeUrl);
112
+ // For desktop apps, start a local server to receive the OAuth callback
113
+ if (client_type === 'installed') {
114
+ return new Promise((resolve, reject) => {
115
+ const server = http.createServer((req, res) => {
116
+ void (async () => {
117
+ try {
118
+ const url = new URL(req.url || '', 'http://localhost:3000');
119
+ const code = url.searchParams.get('code');
120
+ if (code) {
121
+ res.writeHead(200, { 'Content-Type': 'text/html' });
122
+ res.end('<html><body><h1>Authentication successful!</h1><p>You can close this window.</p></body></html>');
123
+ server.close();
124
+ const { tokens } = await oAuth2Client.getToken(code);
125
+ oAuth2Client.setCredentials(tokens);
126
+ if (tokens.refresh_token) {
127
+ await saveCredentials(oAuth2Client);
128
+ }
129
+ else {
130
+ console.error('Did not receive refresh token. Token might expire.');
131
+ }
132
+ console.error('Authentication successful!');
133
+ resolve(oAuth2Client);
134
+ }
135
+ else {
136
+ res.writeHead(400, { 'Content-Type': 'text/html' });
137
+ res.end('<html><body><h1>Error: No code received</h1></body></html>');
138
+ }
139
+ }
140
+ catch (err) {
141
+ console.error('Error retrieving access token', err);
142
+ res.writeHead(500, { 'Content-Type': 'text/html' });
143
+ res.end('<html><body><h1>Authentication failed</h1></body></html>');
144
+ server.close();
145
+ reject(new Error('Authentication failed'));
146
+ }
147
+ })();
148
+ });
149
+ server.listen(3000, () => {
150
+ console.error('Local server started on http://localhost:3000');
151
+ console.error('Waiting for OAuth callback...');
152
+ });
153
+ server.on('error', (err) => {
154
+ console.error('Server error:', err);
155
+ reject(err);
156
+ });
157
+ });
158
+ }
159
+ else {
160
+ // For web clients, use readline to get the code manually
161
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
162
+ const code = await rl.question('Enter the code from that page here: ');
163
+ rl.close();
164
+ try {
165
+ const { tokens } = await oAuth2Client.getToken(code);
166
+ oAuth2Client.setCredentials(tokens);
167
+ if (tokens.refresh_token) {
168
+ await saveCredentials(oAuth2Client);
169
+ }
170
+ else {
171
+ console.error('Did not receive refresh token. Token might expire.');
172
+ }
173
+ console.error('Authentication successful!');
174
+ return oAuth2Client;
175
+ }
176
+ catch (err) {
177
+ console.error('Error retrieving access token', err);
178
+ throw new Error('Authentication failed');
179
+ }
180
+ }
181
+ }
182
+ // --- MODIFIED: The Main Exported Function ---
183
+ // This function now acts as a router. It checks for the environment
184
+ // variable and decides which authentication method to use.
185
+ export async function authorize() {
186
+ // Check if the Service Account environment variable is set.
187
+ if (process.env.SERVICE_ACCOUNT_PATH) {
188
+ console.error('Service account path detected. Attempting service account authentication...');
189
+ return authorizeWithServiceAccount();
190
+ }
191
+ else {
192
+ // If not, execute the original OAuth 2.0 flow exactly as it was.
193
+ console.error('No service account path detected. Falling back to standard OAuth 2.0 flow...');
194
+ let client = await loadSavedCredentialsIfExist();
195
+ if (client) {
196
+ // Optional: Add token refresh logic here if needed, though library often handles it.
197
+ console.error('Using saved credentials.');
198
+ return client;
199
+ }
200
+ console.error('Starting authentication flow...');
201
+ client = await authenticate();
202
+ return client;
203
+ }
204
+ }
205
+ // --- END OF MODIFIED: The Main Exported Function ---
206
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,426 @@
1
+ #!/usr/bin/env node
2
+ // src/cli.ts - CLI entrypoint for Google Workspace MCP Server
3
+ import { Command } from 'commander';
4
+ import * as fs from 'fs/promises';
5
+ import { exec } from 'child_process';
6
+ import { initializeAccounts, listAccounts, completeAddAccount, removeAccount, getConfigDir, getCredentialsPath, getAccountClients, } from './accounts.js';
7
+ /** Type guard for errors with a message property */
8
+ function isErrorWithMessage(error) {
9
+ return (typeof error === 'object' &&
10
+ error !== null &&
11
+ 'message' in error &&
12
+ typeof error.message === 'string');
13
+ }
14
+ /** Type guard for Google API errors with a code property */
15
+ function isGoogleApiError(error) {
16
+ return (isErrorWithMessage(error) &&
17
+ 'code' in error &&
18
+ typeof error.code === 'number');
19
+ }
20
+ /** Get error message from unknown error */
21
+ function getErrorMessage(error) {
22
+ if (isErrorWithMessage(error)) {
23
+ return error.message;
24
+ }
25
+ return String(error);
26
+ }
27
+ // Helper to open URL in browser (cross-platform)
28
+ function openBrowser(url) {
29
+ const platform = process.platform;
30
+ let command;
31
+ if (platform === 'darwin') {
32
+ command = `open "${url}"`;
33
+ }
34
+ else if (platform === 'win32') {
35
+ command = `start "" "${url}"`;
36
+ }
37
+ else {
38
+ command = `xdg-open "${url}"`;
39
+ }
40
+ // eslint-disable-next-line security/detect-child-process -- command is constructed from known safe platform-specific openers with URL-encoded input
41
+ exec(command, (error) => {
42
+ if (error) {
43
+ console.error('Could not open browser automatically. Please open the URL manually.');
44
+ }
45
+ });
46
+ }
47
+ const program = new Command();
48
+ // Version from package.json
49
+ const packageJsonPath = new URL('../package.json', import.meta.url);
50
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- packageJsonPath is a known safe path relative to this file
51
+ const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
52
+ program
53
+ .name('google-workspace-mcp')
54
+ .description('Google Workspace MCP Server - Manage Google Docs, Sheets, Drive, Gmail, Calendar, Slides, and Forms')
55
+ .version(packageJson.version);
56
+ // === MCP Server Command ===
57
+ program
58
+ .command('serve')
59
+ .alias('mcp')
60
+ .description('Start the MCP server (for use with Claude Desktop, VS Code, etc.)')
61
+ .option('--read-only', 'Run in read-only mode (block all write operations)')
62
+ .action(async (options) => {
63
+ // Set env var for server.ts to pick up
64
+ if (options.readOnly) {
65
+ process.env.GOOGLE_MCP_READ_ONLY = 'true';
66
+ }
67
+ // Dynamically import and start the server
68
+ await import('./server.js');
69
+ });
70
+ // === Setup Command ===
71
+ program
72
+ .command('setup')
73
+ .description('Interactive setup wizard for configuring the MCP server')
74
+ .action(async () => {
75
+ console.log('\nšŸš€ Google Workspace MCP Server Setup\n');
76
+ console.log('This wizard will help you set up the MCP server.\n');
77
+ const configDir = getConfigDir();
78
+ const credPath = getCredentialsPath();
79
+ console.log('šŸ“ Configuration directory:', configDir);
80
+ console.log('šŸ”‘ Credentials file path:', credPath);
81
+ console.log('');
82
+ // Check if credentials exist
83
+ try {
84
+ await fs.access(credPath);
85
+ console.log('āœ… Credentials file found at', credPath);
86
+ }
87
+ catch {
88
+ console.log('āŒ Credentials file NOT found at', credPath);
89
+ console.log('');
90
+ console.log('To set up credentials:');
91
+ console.log('1. Go to https://console.cloud.google.com/');
92
+ console.log('2. Create a new project (or select an existing one)');
93
+ console.log('3. Enable the following APIs:');
94
+ console.log(' - Google Docs API');
95
+ console.log(' - Google Drive API');
96
+ console.log(' - Google Sheets API');
97
+ console.log(' - Gmail API');
98
+ console.log(' - Google Calendar API');
99
+ console.log(' - Google Slides API');
100
+ console.log(' - Google Forms API');
101
+ console.log('4. Create OAuth 2.0 credentials (Desktop application)');
102
+ console.log('5. Download the credentials JSON file');
103
+ console.log(`6. Copy it to: ${credPath}`);
104
+ console.log('');
105
+ console.log('After setting up credentials, run this command again or use:');
106
+ console.log(' google-workspace-mcp accounts add <account-name>');
107
+ return;
108
+ }
109
+ // Initialize and check accounts
110
+ await initializeAccounts();
111
+ const accounts = await listAccounts();
112
+ if (accounts.length === 0) {
113
+ console.log('');
114
+ console.log('No accounts configured yet.');
115
+ console.log('');
116
+ console.log('To add an account, run:');
117
+ console.log(' google-workspace-mcp accounts add <account-name>');
118
+ console.log('');
119
+ console.log('Example:');
120
+ console.log(' google-workspace-mcp accounts add personal');
121
+ console.log(' google-workspace-mcp accounts add work');
122
+ }
123
+ else {
124
+ console.log('');
125
+ console.log(`āœ… Found ${accounts.length} configured account(s):`);
126
+ accounts.forEach((acc, i) => {
127
+ console.log(` ${i + 1}. ${acc.name}${acc.email ? ` (${acc.email})` : ''}`);
128
+ });
129
+ console.log('');
130
+ console.log('Your MCP server is ready to use!');
131
+ console.log('');
132
+ console.log('To start the server:');
133
+ console.log(' google-workspace-mcp serve');
134
+ }
135
+ console.log('');
136
+ console.log('šŸ“š For more information, see the README.md');
137
+ });
138
+ // === Accounts Commands ===
139
+ const accountsCmd = program.command('accounts').description('Manage Google account authentication');
140
+ accountsCmd
141
+ .command('list')
142
+ .description('List all configured Google accounts')
143
+ .action(async () => {
144
+ await initializeAccounts();
145
+ const accounts = await listAccounts();
146
+ if (accounts.length === 0) {
147
+ console.log('No accounts configured.');
148
+ console.log('');
149
+ console.log('To add an account, run:');
150
+ console.log(' google-workspace-mcp accounts add <account-name>');
151
+ return;
152
+ }
153
+ console.log(`\nConfigured accounts (${accounts.length}):\n`);
154
+ accounts.forEach((account, index) => {
155
+ console.log(`${index + 1}. ${account.name}`);
156
+ if (account.email) {
157
+ console.log(` Email: ${account.email}`);
158
+ }
159
+ console.log(` Added: ${account.addedAt}`);
160
+ console.log('');
161
+ });
162
+ });
163
+ accountsCmd
164
+ .command('add <name>')
165
+ .description('Add a new Google account')
166
+ .option('-c, --credentials <path>', 'Path to custom credentials.json file for this account')
167
+ .option('--open', 'Automatically open the authorization URL in browser (default)')
168
+ .option('--no-open', 'Do not automatically open browser, just print the URL')
169
+ .action(async (name, options) => {
170
+ console.log(`\nAdding account: ${name}\n`);
171
+ // Validate name
172
+ if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
173
+ console.error('Error: Account name must contain only letters, numbers, underscores, and hyphens.');
174
+ process.exit(1);
175
+ }
176
+ await initializeAccounts();
177
+ // Check if account already exists
178
+ const accounts = await listAccounts();
179
+ if (accounts.some((a) => a.name === name)) {
180
+ console.error(`Error: Account "${name}" already exists.`);
181
+ console.error('Use "google-workspace-mcp auth remove <name>" first if you want to re-add it.');
182
+ process.exit(1);
183
+ }
184
+ const port = 3000;
185
+ console.log(`Starting OAuth flow on port ${port}...`);
186
+ console.log('');
187
+ // Default to opening browser unless --no-open is specified
188
+ const shouldOpenBrowser = options.open !== false;
189
+ try {
190
+ // This will block until OAuth is complete
191
+ await completeAddAccount(name, port, options.credentials, (authUrl) => {
192
+ console.log('╔════════════════════════════════════════════════════════════════════╗');
193
+ console.log('ā•‘ AUTHORIZATION REQUIRED ā•‘');
194
+ console.log('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•');
195
+ console.log('');
196
+ if (shouldOpenBrowser) {
197
+ console.log('Opening browser for authorization...');
198
+ console.log('');
199
+ console.log('If the browser does not open, visit this URL manually:');
200
+ }
201
+ else {
202
+ console.log('Open this URL in your browser to authorize:');
203
+ }
204
+ console.log(authUrl);
205
+ console.log('');
206
+ console.log('Waiting for authorization...');
207
+ // Open the URL in the default browser (unless --no-open)
208
+ if (shouldOpenBrowser) {
209
+ openBrowser(authUrl);
210
+ }
211
+ });
212
+ console.log('');
213
+ console.log(`āœ… Account "${name}" added successfully!`);
214
+ console.log('');
215
+ console.log('You can now use this account with the MCP server.');
216
+ }
217
+ catch (error) {
218
+ console.error('');
219
+ console.error(`āŒ Failed to add account: ${getErrorMessage(error)}`);
220
+ process.exit(1);
221
+ }
222
+ });
223
+ accountsCmd
224
+ .command('remove <name>')
225
+ .description('Remove a Google account')
226
+ .action(async (name) => {
227
+ await initializeAccounts();
228
+ try {
229
+ await removeAccount(name);
230
+ console.log(`āœ… Account "${name}" removed successfully.`);
231
+ }
232
+ catch (error) {
233
+ console.error(`āŒ Failed to remove account: ${getErrorMessage(error)}`);
234
+ process.exit(1);
235
+ }
236
+ });
237
+ accountsCmd
238
+ .command('test-permissions [name]')
239
+ .description('Test API permissions for account(s). Tests all accounts if no name specified.')
240
+ .action(async (name) => {
241
+ await initializeAccounts();
242
+ const accounts = await listAccounts();
243
+ if (accounts.length === 0) {
244
+ console.log('No accounts configured.');
245
+ console.log('');
246
+ console.log('To add an account, run:');
247
+ console.log(' google-workspace-mcp accounts add <account-name>');
248
+ return;
249
+ }
250
+ // Filter to specific account if name provided
251
+ const accountsToTest = name ? accounts.filter((a) => a.name === name) : accounts;
252
+ if (name && accountsToTest.length === 0) {
253
+ console.error(`Account "${name}" not found.`);
254
+ process.exit(1);
255
+ }
256
+ console.log(`\nšŸ” Testing API permissions for ${accountsToTest.length} account(s)...\n`);
257
+ /** Helper to handle 404 errors as success (resource doesn't exist but we have access) */
258
+ const handle404 = (promise) => promise.catch((e) => {
259
+ if (isGoogleApiError(e) && e.code === 404)
260
+ return { status: 200 };
261
+ throw e;
262
+ });
263
+ const services = [
264
+ { name: 'Drive', test: async (clients) => await clients.drive.files.list({ pageSize: 1 }) },
265
+ {
266
+ name: 'Docs',
267
+ test: async (clients) => await handle404(clients.docs.documents.get({ documentId: 'test' })),
268
+ },
269
+ {
270
+ name: 'Sheets',
271
+ test: async (clients) => await handle404(clients.sheets.spreadsheets.get({ spreadsheetId: 'test' })),
272
+ },
273
+ {
274
+ name: 'Gmail',
275
+ test: async (clients) => await clients.gmail.users.labels.list({ userId: 'me' }),
276
+ },
277
+ {
278
+ name: 'Calendar',
279
+ test: async (clients) => await clients.calendar.calendarList.list({ maxResults: 1 }),
280
+ },
281
+ {
282
+ name: 'Slides',
283
+ test: async (clients) => await handle404(clients.slides.presentations.get({ presentationId: 'test' })),
284
+ },
285
+ {
286
+ name: 'Forms',
287
+ test: async (clients) => await handle404(clients.forms.forms.get({ formId: 'test' })),
288
+ },
289
+ ];
290
+ let totalIssues = 0;
291
+ for (const account of accountsToTest) {
292
+ console.log(`šŸ“§ ${account.name}${account.email ? ` (${account.email})` : ''}`);
293
+ try {
294
+ const clients = await getAccountClients(account.name);
295
+ let accountIssues = 0;
296
+ for (const service of services) {
297
+ try {
298
+ await service.test(clients);
299
+ console.log(` āœ… ${service.name}`);
300
+ }
301
+ catch (error) {
302
+ accountIssues++;
303
+ const message = getErrorMessage(error);
304
+ if (message.includes('insufficient') ||
305
+ message.includes('permission') ||
306
+ message.includes('403')) {
307
+ console.log(` āŒ ${service.name}: Permission denied`);
308
+ }
309
+ else if (message.includes('401') || message.includes('invalid_grant')) {
310
+ console.log(` āŒ ${service.name}: Token expired or revoked`);
311
+ }
312
+ else {
313
+ console.log(` āŒ ${service.name}: ${message.substring(0, 60)}`);
314
+ }
315
+ }
316
+ }
317
+ if (accountIssues > 0) {
318
+ totalIssues += accountIssues;
319
+ console.log(` āš ļø ${accountIssues} service(s) need attention`);
320
+ }
321
+ }
322
+ catch (error) {
323
+ console.log(` āŒ Failed to load account: ${getErrorMessage(error)}`);
324
+ totalIssues++;
325
+ }
326
+ console.log('');
327
+ }
328
+ if (totalIssues === 0) {
329
+ console.log('āœ… All API permissions verified successfully!');
330
+ }
331
+ else {
332
+ console.log(`āš ļø ${totalIssues} issue(s) found.`);
333
+ console.log('');
334
+ console.log('To fix permission issues, remove and re-add the account:');
335
+ console.log(' google-workspace-mcp accounts remove <name>');
336
+ console.log(' google-workspace-mcp accounts add <name>');
337
+ }
338
+ });
339
+ // === Config Commands ===
340
+ const configCmd = program.command('config').description('View configuration information');
341
+ configCmd
342
+ .command('path')
343
+ .description('Show the configuration directory path')
344
+ .action(() => {
345
+ console.log(getConfigDir());
346
+ });
347
+ configCmd
348
+ .command('show')
349
+ .description('Show current configuration')
350
+ .action(async () => {
351
+ const configDir = getConfigDir();
352
+ const credPath = getCredentialsPath();
353
+ console.log('\nGoogle Workspace MCP Server Configuration\n');
354
+ console.log('Configuration directory:', configDir);
355
+ console.log('Credentials file:', credPath);
356
+ console.log('');
357
+ // Check credentials
358
+ try {
359
+ await fs.access(credPath);
360
+ console.log('Credentials status: āœ… Found');
361
+ }
362
+ catch {
363
+ console.log('Credentials status: āŒ Not found');
364
+ }
365
+ // List accounts
366
+ await initializeAccounts();
367
+ const accounts = await listAccounts();
368
+ console.log('');
369
+ console.log(`Accounts configured: ${accounts.length}`);
370
+ if (accounts.length > 0) {
371
+ accounts.forEach((acc, i) => {
372
+ console.log(` ${i + 1}. ${acc.name}${acc.email ? ` (${acc.email})` : ''}`);
373
+ });
374
+ }
375
+ });
376
+ // === Status Command ===
377
+ program
378
+ .command('status')
379
+ .description('Check if the server is properly configured and ready')
380
+ .action(async () => {
381
+ console.log('\nšŸ” Checking Google Workspace MCP Server status...\n');
382
+ let issues = 0;
383
+ // Check credentials
384
+ const credPath = getCredentialsPath();
385
+ try {
386
+ await fs.access(credPath);
387
+ console.log('āœ… Credentials file found');
388
+ }
389
+ catch {
390
+ console.log('āŒ Credentials file NOT found');
391
+ console.log(` Expected at: ${credPath}`);
392
+ issues++;
393
+ }
394
+ // Check accounts
395
+ await initializeAccounts();
396
+ const accounts = await listAccounts();
397
+ if (accounts.length > 0) {
398
+ console.log(`āœ… ${accounts.length} account(s) configured`);
399
+ accounts.forEach((acc) => {
400
+ console.log(` - ${acc.name}${acc.email ? ` (${acc.email})` : ''}`);
401
+ });
402
+ }
403
+ else {
404
+ console.log('āš ļø No accounts configured');
405
+ console.log(' Run: google-workspace-mcp accounts add <account-name>');
406
+ issues++;
407
+ }
408
+ console.log('');
409
+ if (issues === 0) {
410
+ console.log('āœ… Server is ready to use!');
411
+ console.log('');
412
+ console.log('Start the server with:');
413
+ console.log(' google-workspace-mcp serve');
414
+ }
415
+ else {
416
+ console.log(`āš ļø ${issues} issue(s) found. Please resolve them before using the server.`);
417
+ }
418
+ });
419
+ // Default command is 'serve' if no command is specified
420
+ program.action(() => {
421
+ // If no command provided, show help
422
+ program.help();
423
+ });
424
+ // Parse arguments
425
+ program.parse();
426
+ //# sourceMappingURL=cli.js.map