mcp-google-multi 5.2.0 → 5.2.1-alpha.1
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/executor.js +11 -0
- package/dist/tools/drive.d.ts +7 -0
- package/dist/tools/drive.js +21 -3
- package/dist/tools/google-api.js +2 -1
- package/package.json +1 -1
package/dist/executor.js
CHANGED
|
@@ -30,6 +30,17 @@ export async function executeApiMethod(method, args, deps = {}) {
|
|
|
30
30
|
account: args.account,
|
|
31
31
|
}, true);
|
|
32
32
|
}
|
|
33
|
+
// drive.files.export streams binary and, without alt=media, Google returns a confusing
|
|
34
|
+
// "Export requires alt=media" — either way the escape hatch can't surface the content.
|
|
35
|
+
if (method.id === 'drive.files.export') {
|
|
36
|
+
return jsonResult({
|
|
37
|
+
error: 'binary_unsupported',
|
|
38
|
+
message: 'drive.files.export returns binary content, not JSON, through this tool.',
|
|
39
|
+
hint: 'Use drive_export to export a Google Workspace file to disk.',
|
|
40
|
+
retriable: false,
|
|
41
|
+
account: args.account,
|
|
42
|
+
}, true);
|
|
43
|
+
}
|
|
33
44
|
let url;
|
|
34
45
|
try {
|
|
35
46
|
url = method.baseUrl + expandPath(method.path, args.pathParams ?? {});
|
package/dist/tools/drive.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import type { ToolRegistry } from '../registry.js';
|
|
2
|
+
export declare function prepareLocalDest(savePath: string, filename: string): string;
|
|
3
|
+
export declare function resolveShareNotification(opts: {
|
|
4
|
+
type: string;
|
|
5
|
+
role: string;
|
|
6
|
+
transferOwnership?: boolean;
|
|
7
|
+
sendNotification?: boolean;
|
|
8
|
+
}): boolean | undefined;
|
|
2
9
|
export declare function registerDriveTools(server: ToolRegistry): void;
|
|
3
10
|
export type TransferPlan = {
|
|
4
11
|
kind: 'binary';
|
package/dist/tools/drive.js
CHANGED
|
@@ -28,6 +28,23 @@ const COMMENT_FIELDS = `${COMMENT_BASE_FIELDS},replies(${REPLY_SUBFIELDS})`;
|
|
|
28
28
|
const COMMENT_LIST_FIELDS = `nextPageToken,comments(${COMMENT_BASE_FIELDS},replies(${REPLY_SUBFIELDS}))`;
|
|
29
29
|
const REPLY_FIELDS = `kind,htmlContent,${REPLY_SUBFIELDS}`;
|
|
30
30
|
const REPLY_LIST_FIELDS = `nextPageToken,replies(${REPLY_FIELDS})`;
|
|
31
|
+
// basename-sanitize the caller filename (never escapes savePath), create the destination
|
|
32
|
+
// directory if missing (callers otherwise hit ENOENT on a non-existent savePath), and
|
|
33
|
+
// return the absolute path to write.
|
|
34
|
+
export function prepareLocalDest(savePath, filename) {
|
|
35
|
+
const dest = path.join(savePath, path.basename(filename));
|
|
36
|
+
fs.mkdirSync(savePath, { recursive: true });
|
|
37
|
+
return dest;
|
|
38
|
+
}
|
|
39
|
+
// sendNotificationEmail is only valid for user/group permissions, and Google forbids
|
|
40
|
+
// disabling it on an ownership transfer. Returns undefined to omit the param entirely.
|
|
41
|
+
export function resolveShareNotification(opts) {
|
|
42
|
+
if (opts.type !== 'user' && opts.type !== 'group')
|
|
43
|
+
return undefined;
|
|
44
|
+
if (opts.transferOwnership || opts.role === 'owner')
|
|
45
|
+
return true;
|
|
46
|
+
return opts.sendNotification ?? true;
|
|
47
|
+
}
|
|
31
48
|
export function registerDriveTools(server) {
|
|
32
49
|
// ─── Read / search / list ──────────────────────────────────────────────
|
|
33
50
|
server.registerTool('drive_search', {
|
|
@@ -243,7 +260,7 @@ export function registerDriveTools(server) {
|
|
|
243
260
|
try {
|
|
244
261
|
const auth = await getClient(account);
|
|
245
262
|
const drive = google.drive({ version: 'v3', auth });
|
|
246
|
-
const dest =
|
|
263
|
+
const dest = prepareLocalDest(savePath, filename);
|
|
247
264
|
const res = await drive.files.get({ fileId, alt: 'media', supportsAllDrives: true }, { responseType: 'stream' });
|
|
248
265
|
// pipeline destroys both streams on source/sink error; raw .pipe leaks the partial file.
|
|
249
266
|
await pipeline(res.data, fs.createWriteStream(dest, { mode: 0o600 }));
|
|
@@ -269,7 +286,7 @@ export function registerDriveTools(server) {
|
|
|
269
286
|
try {
|
|
270
287
|
const auth = await getClient(account);
|
|
271
288
|
const drive = google.drive({ version: 'v3', auth });
|
|
272
|
-
const dest =
|
|
289
|
+
const dest = prepareLocalDest(savePath, filename);
|
|
273
290
|
const res = await drive.files.export({ fileId, mimeType: exportMime }, { responseType: 'stream' });
|
|
274
291
|
// pipeline destroys both streams on source/sink error; raw .pipe leaks the partial file.
|
|
275
292
|
await pipeline(res.data, fs.createWriteStream(dest, { mode: 0o600 }));
|
|
@@ -525,9 +542,10 @@ export function registerDriveTools(server) {
|
|
|
525
542
|
const requestBody = { type, role, emailAddress, domain };
|
|
526
543
|
if (expirationTime)
|
|
527
544
|
requestBody.expirationTime = expirationTime;
|
|
545
|
+
const notify = resolveShareNotification({ type, role, transferOwnership, sendNotification });
|
|
528
546
|
const res = await drive.permissions.create({
|
|
529
547
|
fileId,
|
|
530
|
-
sendNotificationEmail:
|
|
548
|
+
...(notify === undefined ? {} : { sendNotificationEmail: notify }),
|
|
531
549
|
emailMessage,
|
|
532
550
|
transferOwnership: transferOwnership ?? false,
|
|
533
551
|
supportsAllDrives: true,
|
package/dist/tools/google-api.js
CHANGED
|
@@ -92,7 +92,8 @@ export function registerEscapeTools(registry, policy, deps = {}) {
|
|
|
92
92
|
registry.registerMeta('google_api_call', {
|
|
93
93
|
description: 'Invoke any Google Workspace REST method by Discovery id (escape hatch for operations without a ' +
|
|
94
94
|
'dedicated tool). Find methods with google_api_search first. Subject to the same write-control ' +
|
|
95
|
-
'policy as named tools.'
|
|
95
|
+
'policy as named tools. Returns JSON only — for binary/file content (media downloads, ' +
|
|
96
|
+
'drive.files.export) use drive_download / drive_export instead.',
|
|
96
97
|
inputSchema: {
|
|
97
98
|
account: accountEnum.describe('Google account alias'),
|
|
98
99
|
api: z.string().describe(`API alias: ${apiList}`),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-google-multi",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.1-alpha.1",
|
|
4
4
|
"description": "Local MCP server for Google Workspace (Gmail, Drive, Calendar, Sheets, Docs, Contacts, Tasks, Meet, Search Console, +Forms/Chat/Admin) across multiple accounts — OAuth-only, encrypted token storage, deny-by-default writes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|