@starfysh/gdrive-mcp 1.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.
- package/LICENSE +7 -0
- package/README.md +521 -0
- package/dist/auth.js +148 -0
- package/dist/googleDocsApiHelpers.js +618 -0
- package/dist/googleSheetsApiHelpers.js +356 -0
- package/dist/server.js +2451 -0
- package/dist/types.js +107 -0
- package/package.json +53 -0
package/dist/auth.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
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 { fileURLToPath } from 'url';
|
|
8
|
+
// --- Calculate paths relative to this script file (ESM way) ---
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
const projectRootDir = path.resolve(__dirname, '..');
|
|
12
|
+
const TOKEN_PATH = path.join(projectRootDir, 'token.json');
|
|
13
|
+
const CREDENTIALS_PATH = path.join(projectRootDir, 'credentials.json');
|
|
14
|
+
// --- End of path calculation ---
|
|
15
|
+
const SCOPES = [
|
|
16
|
+
'https://www.googleapis.com/auth/documents',
|
|
17
|
+
'https://www.googleapis.com/auth/drive', // Full Drive access for listing, searching, and document discovery
|
|
18
|
+
'https://www.googleapis.com/auth/spreadsheets' // Google Sheets API access
|
|
19
|
+
];
|
|
20
|
+
// --- NEW FUNCTION: Handles Service Account Authentication ---
|
|
21
|
+
// This entire function is new. It is called only when the
|
|
22
|
+
// SERVICE_ACCOUNT_PATH environment variable is set.
|
|
23
|
+
// Supports domain-wide delegation via GOOGLE_IMPERSONATE_USER env var.
|
|
24
|
+
async function authorizeWithServiceAccount() {
|
|
25
|
+
const serviceAccountPath = process.env.SERVICE_ACCOUNT_PATH; // We know this is set if we are in this function
|
|
26
|
+
const impersonateUser = process.env.GOOGLE_IMPERSONATE_USER; // Optional: email of user to impersonate
|
|
27
|
+
try {
|
|
28
|
+
const keyFileContent = await fs.readFile(serviceAccountPath, 'utf8');
|
|
29
|
+
const serviceAccountKey = JSON.parse(keyFileContent);
|
|
30
|
+
const auth = new JWT({
|
|
31
|
+
email: serviceAccountKey.client_email,
|
|
32
|
+
key: serviceAccountKey.private_key,
|
|
33
|
+
scopes: SCOPES,
|
|
34
|
+
subject: impersonateUser, // Enables domain-wide delegation when set
|
|
35
|
+
});
|
|
36
|
+
await auth.authorize();
|
|
37
|
+
if (impersonateUser) {
|
|
38
|
+
console.error(`Service Account authentication successful, impersonating: ${impersonateUser}`);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
console.error('Service Account authentication successful!');
|
|
42
|
+
}
|
|
43
|
+
return auth;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (error.code === 'ENOENT') {
|
|
47
|
+
console.error(`FATAL: Service account key file not found at path: ${serviceAccountPath}`);
|
|
48
|
+
throw new Error(`Service account key file not found. Please check the path in SERVICE_ACCOUNT_PATH.`);
|
|
49
|
+
}
|
|
50
|
+
console.error('FATAL: Error loading or authorizing the service account key:', error.message);
|
|
51
|
+
throw new Error('Failed to authorize using the service account. Ensure the key file is valid and the path is correct.');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// --- END OF NEW FUNCTION---
|
|
55
|
+
async function loadSavedCredentialsIfExist() {
|
|
56
|
+
try {
|
|
57
|
+
const content = await fs.readFile(TOKEN_PATH);
|
|
58
|
+
const credentials = JSON.parse(content.toString());
|
|
59
|
+
const { client_secret, client_id, redirect_uris } = await loadClientSecrets();
|
|
60
|
+
const client = new google.auth.OAuth2(client_id, client_secret, redirect_uris?.[0]);
|
|
61
|
+
client.setCredentials(credentials);
|
|
62
|
+
return client;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function loadClientSecrets() {
|
|
69
|
+
const content = await fs.readFile(CREDENTIALS_PATH);
|
|
70
|
+
const keys = JSON.parse(content.toString());
|
|
71
|
+
const key = keys.installed || keys.web;
|
|
72
|
+
if (!key)
|
|
73
|
+
throw new Error("Could not find client secrets in credentials.json.");
|
|
74
|
+
return {
|
|
75
|
+
client_id: key.client_id,
|
|
76
|
+
client_secret: key.client_secret,
|
|
77
|
+
redirect_uris: key.redirect_uris || ['http://localhost:3000/'], // Default for web clients
|
|
78
|
+
client_type: keys.web ? 'web' : 'installed'
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async function saveCredentials(client) {
|
|
82
|
+
const { client_secret, client_id } = await loadClientSecrets();
|
|
83
|
+
const payload = JSON.stringify({
|
|
84
|
+
type: 'authorized_user',
|
|
85
|
+
client_id: client_id,
|
|
86
|
+
client_secret: client_secret,
|
|
87
|
+
refresh_token: client.credentials.refresh_token,
|
|
88
|
+
});
|
|
89
|
+
await fs.writeFile(TOKEN_PATH, payload);
|
|
90
|
+
console.error('Token stored to', TOKEN_PATH);
|
|
91
|
+
}
|
|
92
|
+
async function authenticate() {
|
|
93
|
+
const { client_secret, client_id, redirect_uris, client_type } = await loadClientSecrets();
|
|
94
|
+
// For web clients, use the configured redirect URI; for desktop clients, use 'urn:ietf:wg:oauth:2.0:oob'
|
|
95
|
+
const redirectUri = client_type === 'web' ? redirect_uris[0] : 'urn:ietf:wg:oauth:2.0:oob';
|
|
96
|
+
console.error(`DEBUG: Using redirect URI: ${redirectUri}`);
|
|
97
|
+
console.error(`DEBUG: Client type: ${client_type}`);
|
|
98
|
+
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirectUri);
|
|
99
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
100
|
+
const authorizeUrl = oAuth2Client.generateAuthUrl({
|
|
101
|
+
access_type: 'offline',
|
|
102
|
+
scope: SCOPES.join(' '),
|
|
103
|
+
});
|
|
104
|
+
console.error('DEBUG: Generated auth URL:', authorizeUrl);
|
|
105
|
+
console.error('Authorize this app by visiting this url:', authorizeUrl);
|
|
106
|
+
const code = await rl.question('Enter the code from that page here: ');
|
|
107
|
+
rl.close();
|
|
108
|
+
try {
|
|
109
|
+
const { tokens } = await oAuth2Client.getToken(code);
|
|
110
|
+
oAuth2Client.setCredentials(tokens);
|
|
111
|
+
if (tokens.refresh_token) { // Save only if we got a refresh token
|
|
112
|
+
await saveCredentials(oAuth2Client);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
console.error("Did not receive refresh token. Token might expire.");
|
|
116
|
+
}
|
|
117
|
+
console.error('Authentication successful!');
|
|
118
|
+
return oAuth2Client;
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
console.error('Error retrieving access token', err);
|
|
122
|
+
throw new Error('Authentication failed');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// --- MODIFIED: The Main Exported Function ---
|
|
126
|
+
// This function now acts as a router. It checks for the environment
|
|
127
|
+
// variable and decides which authentication method to use.
|
|
128
|
+
export async function authorize() {
|
|
129
|
+
// Check if the Service Account environment variable is set.
|
|
130
|
+
if (process.env.SERVICE_ACCOUNT_PATH) {
|
|
131
|
+
console.error('Service account path detected. Attempting service account authentication...');
|
|
132
|
+
return authorizeWithServiceAccount();
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// If not, execute the original OAuth 2.0 flow exactly as it was.
|
|
136
|
+
console.error('No service account path detected. Falling back to standard OAuth 2.0 flow...');
|
|
137
|
+
let client = await loadSavedCredentialsIfExist();
|
|
138
|
+
if (client) {
|
|
139
|
+
// Optional: Add token refresh logic here if needed, though library often handles it.
|
|
140
|
+
console.error('Using saved credentials.');
|
|
141
|
+
return client;
|
|
142
|
+
}
|
|
143
|
+
console.error('Starting authentication flow...');
|
|
144
|
+
client = await authenticate();
|
|
145
|
+
return client;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// --- END OF MODIFIED: The Main Exported Function ---
|