mcp-google-extras 1.0.1 → 1.0.4
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 +99 -13
- package/dist/auth.js +91 -25
- package/dist/clients.js +6 -2
- package/dist/index.js +2 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,19 +11,51 @@ All 44 tools from `@a-bonus/google-docs-mcp`, plus:
|
|
|
11
11
|
- **readFile** — Read the full text content of a `.docx` or `.pdf` file from Google Drive by file ID
|
|
12
12
|
- **searchFileContents** — Search Google Drive and extract matching text snippets from inside `.docx` and `.pdf` files
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Getting Started
|
|
15
15
|
|
|
16
|
-
### 1
|
|
16
|
+
### Step 1: Create Google OAuth Credentials
|
|
17
17
|
|
|
18
18
|
1. Go to the [Google Cloud Console](https://console.cloud.google.com/)
|
|
19
19
|
2. Create a project (or use an existing one)
|
|
20
|
-
3. Enable the Google Docs API
|
|
21
|
-
4. Create
|
|
22
|
-
5.
|
|
20
|
+
3. Enable the **Google Docs API**, **Google Sheets API**, and **Google Drive API**
|
|
21
|
+
4. Go to **Credentials** → **Create Credentials** → **OAuth Client ID**
|
|
22
|
+
5. Select **Desktop application** as the application type
|
|
23
|
+
6. Download the credentials or note your **Client ID** and **Client Secret**
|
|
23
24
|
|
|
24
|
-
### 2
|
|
25
|
+
### Step 2: Provide Your Credentials
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
Choose **one** of the following methods (whichever you prefer):
|
|
28
|
+
|
|
29
|
+
#### Option A: Use `credentials.json`
|
|
30
|
+
|
|
31
|
+
Download the JSON file from Google Cloud Console and place it in either location:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
~/.config/google-docs-mcp/credentials.json (recommended — shared across projects)
|
|
35
|
+
./credentials.json (local to your project)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
That's it — no env vars needed. The server will find it automatically.
|
|
39
|
+
|
|
40
|
+
#### Option B: Create a `.env` file
|
|
41
|
+
|
|
42
|
+
Create a `.env` file in either location:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
~/.config/google-docs-mcp/.env (recommended — shared across projects)
|
|
46
|
+
./.env (local to your project)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
With the following contents:
|
|
50
|
+
|
|
51
|
+
```env
|
|
52
|
+
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
|
|
53
|
+
GOOGLE_CLIENT_SECRET=your-client-secret
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### Option C: Set env vars in your MCP config
|
|
57
|
+
|
|
58
|
+
Add the credentials directly to your MCP configuration:
|
|
27
59
|
|
|
28
60
|
```json
|
|
29
61
|
{
|
|
@@ -40,16 +72,68 @@ Add this to your MCP configuration (e.g., `.mcp.json`):
|
|
|
40
72
|
}
|
|
41
73
|
```
|
|
42
74
|
|
|
43
|
-
|
|
75
|
+
> **Credential lookup order:** env vars → `~/.config/google-docs-mcp/.env` → project root `.env` → `~/.config/google-docs-mcp/credentials.json` → project root `credentials.json`
|
|
76
|
+
|
|
77
|
+
### Step 3: Add to Your MCP Client
|
|
78
|
+
|
|
79
|
+
#### Claude Code (recommended)
|
|
80
|
+
|
|
81
|
+
If you used Option A or B above:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
claude mcp add google-docs -- npx -y mcp-google-extras
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Or with env vars (Option C):
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
claude mcp add google-docs \
|
|
91
|
+
-e GOOGLE_CLIENT_ID=your-client-id \
|
|
92
|
+
-e GOOGLE_CLIENT_SECRET=your-client-secret \
|
|
93
|
+
-- npx -y mcp-google-extras
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### Other MCP clients
|
|
97
|
+
|
|
98
|
+
Add this to your MCP configuration (e.g., `.mcp.json`, `claude_desktop_config.json`):
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"mcpServers": {
|
|
103
|
+
"google-docs": {
|
|
104
|
+
"command": "npx",
|
|
105
|
+
"args": ["-y", "mcp-google-extras"]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
If using Option C, add an `"env"` block with your `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`.
|
|
112
|
+
|
|
113
|
+
### Step 4: Authenticate
|
|
44
114
|
|
|
45
|
-
On first
|
|
115
|
+
On your first tool call, the server will automatically open your browser for Google OAuth consent. Sign in and grant access — the token is saved to `~/.config/google-docs-mcp/token.json` for future use.
|
|
46
116
|
|
|
47
|
-
You can also run the auth flow manually:
|
|
117
|
+
You can also run the auth flow manually anytime:
|
|
48
118
|
|
|
49
119
|
```bash
|
|
50
120
|
npx mcp-google-extras auth
|
|
51
121
|
```
|
|
52
122
|
|
|
123
|
+
### Multi-Account Support
|
|
124
|
+
|
|
125
|
+
Set the `GOOGLE_MCP_PROFILE` env var to use separate tokens per profile:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"env": {
|
|
130
|
+
"GOOGLE_MCP_PROFILE": "work"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
This stores tokens in `~/.config/google-docs-mcp/work/` instead of the default directory.
|
|
136
|
+
|
|
53
137
|
## Tools
|
|
54
138
|
|
|
55
139
|
### Google Docs
|
|
@@ -68,13 +152,15 @@ npx mcp-google-extras auth
|
|
|
68
152
|
|
|
69
153
|
| Variable | Required | Description |
|
|
70
154
|
|---|---|---|
|
|
71
|
-
| `GOOGLE_CLIENT_ID` |
|
|
72
|
-
| `GOOGLE_CLIENT_SECRET` |
|
|
73
|
-
| `GOOGLE_MCP_PROFILE` | No | Profile name for multi-account support |
|
|
155
|
+
| `GOOGLE_CLIENT_ID` | No* | OAuth 2.0 Client ID |
|
|
156
|
+
| `GOOGLE_CLIENT_SECRET` | No* | OAuth 2.0 Client Secret |
|
|
157
|
+
| `GOOGLE_MCP_PROFILE` | No | Profile name for multi-account support (see above) |
|
|
74
158
|
| `LOG_LEVEL` | No | `debug`, `info`, `warn`, `error`, or `silent` |
|
|
75
159
|
| `SERVICE_ACCOUNT_PATH` | No | Path to service account JSON key (alternative to OAuth) |
|
|
76
160
|
| `GOOGLE_IMPERSONATE_USER` | No | Email to impersonate with service account |
|
|
77
161
|
|
|
162
|
+
\* Not required as env vars if you provide credentials via `.env` file or `credentials.json` (see [Step 2](#step-2-provide-your-credentials)).
|
|
163
|
+
|
|
78
164
|
## License
|
|
79
165
|
|
|
80
166
|
ISC (based on [@a-bonus/google-docs-mcp](https://www.npmjs.com/package/@a-bonus/google-docs-mcp))
|
package/dist/auth.js
CHANGED
|
@@ -5,6 +5,7 @@ import * as fs from 'fs/promises';
|
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import * as os from 'os';
|
|
7
7
|
import * as http from 'http';
|
|
8
|
+
import { exec } from 'child_process';
|
|
8
9
|
import { fileURLToPath } from 'url';
|
|
9
10
|
import { logger } from './logger.js';
|
|
10
11
|
// ---------------------------------------------------------------------------
|
|
@@ -42,42 +43,86 @@ const SCOPES = [
|
|
|
42
43
|
'https://www.googleapis.com/auth/script.external_request',
|
|
43
44
|
];
|
|
44
45
|
// ---------------------------------------------------------------------------
|
|
46
|
+
// .env file loader
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
async function loadEnvFile(filePath) {
|
|
49
|
+
try {
|
|
50
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
51
|
+
for (const line of content.split('\n')) {
|
|
52
|
+
const trimmed = line.trim();
|
|
53
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
54
|
+
const eqIdx = trimmed.indexOf('=');
|
|
55
|
+
if (eqIdx === -1) continue;
|
|
56
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
57
|
+
let value = trimmed.slice(eqIdx + 1).trim();
|
|
58
|
+
// Strip surrounding quotes
|
|
59
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
60
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
61
|
+
value = value.slice(1, -1);
|
|
62
|
+
}
|
|
63
|
+
if (!process.env[key]) {
|
|
64
|
+
process.env[key] = value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
} catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
45
73
|
// Client secrets resolution
|
|
46
74
|
// ---------------------------------------------------------------------------
|
|
47
75
|
/**
|
|
48
76
|
* Resolves OAuth client ID and secret.
|
|
49
77
|
*
|
|
50
78
|
* Priority:
|
|
51
|
-
* 1. GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET env vars (
|
|
52
|
-
* 2.
|
|
79
|
+
* 1. GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET env vars (including from MCP config)
|
|
80
|
+
* 2. .env file in config dir (~/.config/google-docs-mcp/.env)
|
|
81
|
+
* 3. .env file in project root
|
|
82
|
+
* 4. credentials.json in config dir (~/.config/google-docs-mcp/credentials.json)
|
|
83
|
+
* 5. credentials.json in project root (legacy dev fallback)
|
|
53
84
|
*/
|
|
54
85
|
async function loadClientSecrets() {
|
|
55
|
-
// 1.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (envId && envSecret) {
|
|
59
|
-
return { client_id: envId, client_secret: envSecret };
|
|
86
|
+
// 1. Check env vars first (may already be set via MCP config)
|
|
87
|
+
if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) {
|
|
88
|
+
return { client_id: process.env.GOOGLE_CLIENT_ID, client_secret: process.env.GOOGLE_CLIENT_SECRET };
|
|
60
89
|
}
|
|
61
|
-
// 2.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return {
|
|
70
|
-
client_id: key.client_id,
|
|
71
|
-
client_secret: key.client_secret,
|
|
72
|
-
};
|
|
90
|
+
// 2–4. Try loading .env files (config dir, cwd, then package root)
|
|
91
|
+
const configDir = getConfigDir();
|
|
92
|
+
const cwd = process.cwd();
|
|
93
|
+
await loadEnvFile(path.join(configDir, '.env'));
|
|
94
|
+
await loadEnvFile(path.join(cwd, '.env'));
|
|
95
|
+
await loadEnvFile(path.join(projectRootDir, '.env'));
|
|
96
|
+
if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) {
|
|
97
|
+
logger.info('Loaded client credentials from .env file.');
|
|
98
|
+
return { client_id: process.env.GOOGLE_CLIENT_ID, client_secret: process.env.GOOGLE_CLIENT_SECRET };
|
|
73
99
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
100
|
+
// 5–7. Try credentials.json (config dir, cwd, then package root)
|
|
101
|
+
const credentialsPaths = [
|
|
102
|
+
path.join(configDir, 'credentials.json'),
|
|
103
|
+
path.join(cwd, 'credentials.json'),
|
|
104
|
+
CREDENTIALS_PATH,
|
|
105
|
+
];
|
|
106
|
+
for (const credPath of credentialsPaths) {
|
|
107
|
+
try {
|
|
108
|
+
const content = await fs.readFile(credPath, 'utf8');
|
|
109
|
+
const keys = JSON.parse(content);
|
|
110
|
+
const key = keys.installed || keys.web;
|
|
111
|
+
if (key) {
|
|
112
|
+
logger.info('Loaded client credentials from', credPath);
|
|
113
|
+
return { client_id: key.client_id, client_secret: key.client_secret };
|
|
114
|
+
}
|
|
115
|
+
} catch (err) {
|
|
116
|
+
if (err.code !== 'ENOENT') throw err;
|
|
78
117
|
}
|
|
79
|
-
throw err;
|
|
80
118
|
}
|
|
119
|
+
const configDirDisplay = configDir.replace(os.homedir(), '~');
|
|
120
|
+
throw new Error(
|
|
121
|
+
'No OAuth credentials found. Provide them in any of these ways:\n' +
|
|
122
|
+
` 1. Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET env vars in your MCP config\n` +
|
|
123
|
+
` 2. Create a .env file with GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in ${configDirDisplay}/ or your project directory\n` +
|
|
124
|
+
` 3. Place your credentials.json (from Google Cloud Console) in ${configDirDisplay}/ or your project directory`
|
|
125
|
+
);
|
|
81
126
|
}
|
|
82
127
|
// ---------------------------------------------------------------------------
|
|
83
128
|
// Service account auth (unchanged)
|
|
@@ -144,6 +189,25 @@ async function saveCredentials(client) {
|
|
|
144
189
|
logger.info('Token stored to', tokenPath);
|
|
145
190
|
}
|
|
146
191
|
// ---------------------------------------------------------------------------
|
|
192
|
+
// Auto-open browser (cross-platform)
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
function openBrowser(url) {
|
|
195
|
+
const platform = process.platform;
|
|
196
|
+
let cmd;
|
|
197
|
+
if (platform === 'win32') {
|
|
198
|
+
cmd = `start "" "${url}"`;
|
|
199
|
+
} else if (platform === 'darwin') {
|
|
200
|
+
cmd = `open "${url}"`;
|
|
201
|
+
} else {
|
|
202
|
+
cmd = `xdg-open "${url}"`;
|
|
203
|
+
}
|
|
204
|
+
exec(cmd, (err) => {
|
|
205
|
+
if (err) {
|
|
206
|
+
logger.warn('Could not auto-open browser. Please open this URL manually.');
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
147
211
|
// Interactive OAuth browser flow
|
|
148
212
|
// ---------------------------------------------------------------------------
|
|
149
213
|
async function authenticate() {
|
|
@@ -158,7 +222,9 @@ async function authenticate() {
|
|
|
158
222
|
access_type: 'offline',
|
|
159
223
|
scope: SCOPES.join(' '),
|
|
160
224
|
});
|
|
161
|
-
logger.info('
|
|
225
|
+
logger.info('Opening browser for authorization...');
|
|
226
|
+
logger.info('If the browser does not open, visit this URL:', authorizeUrl);
|
|
227
|
+
openBrowser(authorizeUrl);
|
|
162
228
|
// Wait for the OAuth callback
|
|
163
229
|
const code = await new Promise((resolve, reject) => {
|
|
164
230
|
server.on('request', (req, res) => {
|
package/dist/clients.js
CHANGED
|
@@ -24,13 +24,17 @@ export async function initializeGoogleClient() {
|
|
|
24
24
|
logger.info('Google API client authorized successfully.');
|
|
25
25
|
}
|
|
26
26
|
catch (error) {
|
|
27
|
-
logger.error('
|
|
27
|
+
logger.error('Failed to initialize Google API client:', error);
|
|
28
28
|
authClient = null;
|
|
29
29
|
googleDocs = null;
|
|
30
30
|
googleDrive = null;
|
|
31
31
|
googleSheets = null;
|
|
32
32
|
googleScript = null;
|
|
33
|
-
throw new
|
|
33
|
+
throw new UserError(
|
|
34
|
+
'Google authentication required. A browser window should have opened automatically. ' +
|
|
35
|
+
'If not, run: npx mcp-google-extras auth\n\n' +
|
|
36
|
+
'Details: ' + (error.message || error)
|
|
37
|
+
);
|
|
34
38
|
}
|
|
35
39
|
}
|
|
36
40
|
if (authClient && !googleDocs) {
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
// mcp-google-extras auth Run the interactive OAuth flow
|
|
9
9
|
import { FastMCP } from 'fastmcp';
|
|
10
10
|
import { buildCachedToolsListPayload, collectToolsWhileRegistering, installCachedToolsListHandler, } from './cachedToolsList.js';
|
|
11
|
-
import { initializeGoogleClient } from './clients.js';
|
|
12
11
|
import { registerAllTools } from './tools/index.js';
|
|
13
12
|
import { logger } from './logger.js';
|
|
14
13
|
// --- Auth subcommand ---
|
|
@@ -40,13 +39,12 @@ const registeredTools = [];
|
|
|
40
39
|
collectToolsWhileRegistering(server, registeredTools);
|
|
41
40
|
registerAllTools(server);
|
|
42
41
|
try {
|
|
43
|
-
|
|
44
|
-
logger.info('Starting Ultimate Google Docs & Sheets MCP server...');
|
|
42
|
+
logger.info('Starting mcp-google-extras server...');
|
|
45
43
|
const cachedToolsList = await buildCachedToolsListPayload(registeredTools);
|
|
46
44
|
await server.start({ transportType: 'stdio' });
|
|
47
45
|
installCachedToolsListHandler(server, cachedToolsList);
|
|
48
46
|
logger.info('MCP Server running using stdio. Awaiting client connection...');
|
|
49
|
-
logger.info('
|
|
47
|
+
logger.info('Google auth will run automatically on first tool call.');
|
|
50
48
|
}
|
|
51
49
|
catch (startError) {
|
|
52
50
|
logger.error('FATAL: Server failed to start:', startError.message || startError);
|