mcp-google-multi 6.0.0-beta.2 → 6.0.0-beta.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 +5 -5
- package/dist/accounts.d.ts +20 -4
- package/dist/accounts.js +68 -44
- package/dist/api-probe.d.ts +5 -0
- package/dist/api-probe.js +20 -15
- package/dist/arg-normalize.js +19 -38
- package/dist/arg-strict.d.ts +7 -0
- package/dist/arg-strict.js +38 -28
- package/dist/auth.d.ts +5 -4
- package/dist/auth.js +16 -23
- package/dist/boot-gates.d.ts +26 -0
- package/dist/boot-gates.js +36 -0
- package/dist/cimd-cache.d.ts +38 -0
- package/dist/cimd-cache.js +111 -0
- package/dist/client-config.d.ts +2 -2
- package/dist/client-config.js +8 -14
- package/dist/client.d.ts +32 -2
- package/dist/client.js +0 -0
- package/dist/compose.d.ts +18 -0
- package/dist/compose.js +109 -0
- package/dist/config-file.d.ts +13 -0
- package/dist/config-file.js +41 -10
- package/dist/discover.js +7 -10
- package/dist/discovery-client.d.ts +3 -0
- package/dist/discovery-client.js +36 -32
- package/dist/doctor.d.ts +9 -5
- package/dist/doctor.js +119 -29
- package/dist/env-load.js +2 -5
- package/dist/executor.d.ts +3 -0
- package/dist/executor.js +6 -6
- package/dist/fanout.d.ts +9 -1
- package/dist/fanout.js +47 -22
- package/dist/fs-atomic.d.ts +8 -0
- package/dist/fs-atomic.js +216 -29
- package/dist/hint-provider.d.ts +129 -0
- package/dist/hint-provider.js +58 -0
- package/dist/http-config.d.ts +2 -2
- package/dist/http-config.js +3 -7
- package/dist/http-transport.d.ts +67 -9
- package/dist/http-transport.js +318 -52
- package/dist/identity.d.ts +6 -3
- package/dist/identity.js +24 -8
- package/dist/index.js +76 -160
- package/dist/master-key.d.ts +3 -3
- package/dist/master-key.js +16 -19
- package/dist/mcp-token.d.ts +126 -19
- package/dist/mcp-token.js +557 -76
- package/dist/metrics-cli.d.ts +1 -1
- package/dist/metrics-cli.js +1 -1
- package/dist/metrics-tap.js +2 -5
- package/dist/migrate-config.d.ts +1 -2
- package/dist/migrate-config.js +5 -7
- package/dist/net-tuning.js +2 -4
- package/dist/oauth-as.d.ts +104 -4
- package/dist/oauth-as.js +368 -114
- package/dist/oauth-consent.d.ts +10 -0
- package/dist/oauth-consent.js +20 -10
- package/dist/outbound-allowlist.js +9 -11
- package/dist/reauth-hint.d.ts +1 -1
- package/dist/reauth-hint.js +11 -10
- package/dist/registry-transfer.d.ts +5 -0
- package/dist/registry-transfer.js +63 -11
- package/dist/registry.d.ts +13 -4
- package/dist/registry.js +100 -72
- package/dist/scope-catalog.d.ts +16 -0
- package/dist/scope-catalog.js +40 -13
- package/dist/scope-observability.d.ts +10 -9
- package/dist/scope-observability.js +34 -24
- package/dist/services.d.ts +18 -10
- package/dist/services.js +61 -22
- package/dist/ssrf-guard.d.ts +23 -9
- package/dist/ssrf-guard.js +106 -78
- package/dist/tenant-purge.d.ts +26 -0
- package/dist/tenant-purge.js +65 -0
- package/dist/token-store.d.ts +20 -2
- package/dist/token-store.js +0 -0
- package/dist/tools/_coerce.d.ts +4 -4
- package/dist/tools/_errors.d.ts +3 -0
- package/dist/tools/_errors.js +72 -60
- package/dist/tools/_local-files.d.ts +9 -0
- package/dist/tools/_local-files.js +22 -8
- package/dist/tools/account-wizard.d.ts +13 -3
- package/dist/tools/account-wizard.js +107 -56
- package/dist/tools/accounts-tool.d.ts +8 -1
- package/dist/tools/accounts-tool.js +35 -19
- package/dist/tools/admin.d.ts +10 -1
- package/dist/tools/admin.js +14 -11
- package/dist/tools/analytics.d.ts +10 -1
- package/dist/tools/analytics.js +14 -10
- package/dist/tools/calendar.d.ts +2 -1
- package/dist/tools/calendar.js +19 -18
- package/dist/tools/chat.d.ts +10 -1
- package/dist/tools/chat.js +12 -8
- package/dist/tools/contacts.d.ts +2 -1
- package/dist/tools/contacts.js +22 -19
- package/dist/tools/docs.d.ts +2 -1
- package/dist/tools/docs.js +35 -36
- package/dist/tools/drive.d.ts +2 -1
- package/dist/tools/drive.js +80 -74
- package/dist/tools/forms.d.ts +10 -1
- package/dist/tools/forms.js +18 -12
- package/dist/tools/generated/_shared.d.ts +2 -1
- package/dist/tools/generated/_shared.js +7 -3
- package/dist/tools/generated/admin.d.ts +2 -1
- package/dist/tools/generated/admin.js +265 -265
- package/dist/tools/generated/analytics.d.ts +2 -1
- package/dist/tools/generated/analytics.js +125 -125
- package/dist/tools/generated/appsmarket.d.ts +2 -1
- package/dist/tools/generated/appsmarket.js +5 -5
- package/dist/tools/generated/calendar.d.ts +2 -1
- package/dist/tools/generated/calendar.js +55 -55
- package/dist/tools/generated/chat.d.ts +2 -1
- package/dist/tools/generated/chat.js +95 -95
- package/dist/tools/generated/classroom.d.ts +2 -1
- package/dist/tools/generated/classroom.js +209 -209
- package/dist/tools/generated/cloudidentity.d.ts +2 -1
- package/dist/tools/generated/cloudidentity.js +133 -133
- package/dist/tools/generated/cloudsearch.d.ts +2 -1
- package/dist/tools/generated/cloudsearch.js +99 -99
- package/dist/tools/generated/contacts.d.ts +2 -1
- package/dist/tools/generated/contacts.js +29 -29
- package/dist/tools/generated/drive.d.ts +2 -1
- package/dist/tools/generated/drive.js +71 -71
- package/dist/tools/generated/driveactivity.d.ts +2 -1
- package/dist/tools/generated/driveactivity.js +3 -3
- package/dist/tools/generated/drivelabels.d.ts +2 -1
- package/dist/tools/generated/drivelabels.js +53 -53
- package/dist/tools/generated/forms.d.ts +2 -1
- package/dist/tools/generated/forms.js +7 -7
- package/dist/tools/generated/gmail.d.ts +2 -1
- package/dist/tools/generated/gmail.js +117 -117
- package/dist/tools/generated/groupsmigration.d.ts +2 -1
- package/dist/tools/generated/groupsmigration.js +3 -3
- package/dist/tools/generated/groupssettings.d.ts +2 -1
- package/dist/tools/generated/groupssettings.js +7 -7
- package/dist/tools/generated/index.d.ts +2 -1
- package/dist/tools/generated/keep.d.ts +2 -1
- package/dist/tools/generated/keep.js +15 -15
- package/dist/tools/generated/licensing.d.ts +2 -1
- package/dist/tools/generated/licensing.js +15 -15
- package/dist/tools/generated/meet.d.ts +2 -1
- package/dist/tools/generated/meet.js +27 -27
- package/dist/tools/generated/postmaster.d.ts +2 -1
- package/dist/tools/generated/postmaster.js +9 -9
- package/dist/tools/generated/reseller.d.ts +2 -1
- package/dist/tools/generated/reseller.js +35 -35
- package/dist/tools/generated/script.d.ts +2 -1
- package/dist/tools/generated/script.js +33 -33
- package/dist/tools/generated/searchconsole.d.ts +2 -1
- package/dist/tools/generated/searchconsole.js +3 -3
- package/dist/tools/generated/sheets.d.ts +2 -1
- package/dist/tools/generated/sheets.js +15 -15
- package/dist/tools/generated/tasks.d.ts +2 -1
- package/dist/tools/generated/tasks.js +5 -5
- package/dist/tools/generated/vault.d.ts +2 -1
- package/dist/tools/generated/vault.js +67 -67
- package/dist/tools/generated/workspaceevents.d.ts +2 -1
- package/dist/tools/generated/workspaceevents.js +31 -31
- package/dist/tools/gmail-mime.d.ts +1 -1
- package/dist/tools/gmail-mime.js +7 -12
- package/dist/tools/gmail.d.ts +4 -3
- package/dist/tools/gmail.js +67 -69
- package/dist/tools/google-api.d.ts +2 -3
- package/dist/tools/google-api.js +34 -22
- package/dist/tools/meet.d.ts +10 -1
- package/dist/tools/meet.js +12 -9
- package/dist/tools/searchconsole.d.ts +2 -1
- package/dist/tools/searchconsole.js +16 -13
- package/dist/tools/sheets.d.ts +2 -1
- package/dist/tools/sheets.js +35 -32
- package/dist/tools/slides.d.ts +10 -1
- package/dist/tools/slides.js +16 -10
- package/dist/tools/tasks.d.ts +2 -1
- package/dist/tools/tasks.js +18 -15
- package/dist/trim.d.ts +5 -0
- package/dist/trim.js +19 -0
- package/dist/usage-metrics.d.ts +23 -0
- package/dist/usage-metrics.js +67 -56
- package/dist/write-control.d.ts +4 -8
- package/dist/write-control.js +13 -6
- package/package.json +47 -21
package/dist/tools/gmail.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { coerceArray, coerceBoolean, coerceJson, coerceNumber } from './_coerce.js';
|
|
3
3
|
import { gmail as gmailClient } from '@googleapis/gmail';
|
|
4
|
-
import {
|
|
4
|
+
import { accountArgLive } from '../accounts.js';
|
|
5
5
|
import { getClient } from '../client.js';
|
|
6
6
|
import { handleGoogleApiError, invalidParams, mapGoogleError } from './_errors.js';
|
|
7
7
|
import { buildReplyHeaders, composeRaw, renderMarkdown, htmlToMarkdown, HeaderInjectionError } from './gmail-mime.js';
|
|
8
|
-
import { prepareLocalDest } from './_local-files.js';
|
|
8
|
+
import { hostFilesRefused, prepareLocalDest } from './_local-files.js';
|
|
9
9
|
import { checkOutbound } from '../outbound-allowlist.js';
|
|
10
10
|
import addressparser from 'nodemailer/lib/addressparser/index.js';
|
|
11
11
|
import { lookup as lookupMime } from 'mime-types';
|
|
@@ -14,7 +14,6 @@ import { getTokenDir } from '../accounts.js';
|
|
|
14
14
|
import { sliceClean } from '../trim.js';
|
|
15
15
|
import * as path from 'path';
|
|
16
16
|
import * as fs from 'fs';
|
|
17
|
-
const accountEnum = accountAliasSchema.optional();
|
|
18
17
|
function getHeader(headers, name) {
|
|
19
18
|
return headers?.find((h) => h.name?.toLowerCase() === name.toLowerCase())?.value ?? '';
|
|
20
19
|
}
|
|
@@ -39,16 +38,15 @@ function decodeBody(payload, rawHtml = false) {
|
|
|
39
38
|
const plain = [];
|
|
40
39
|
const html = [];
|
|
41
40
|
collectTextParts(payload, plain, html, true);
|
|
42
|
-
// Concatenating every plain leaf keeps forwarded/mixed messages whole;
|
|
43
|
-
//
|
|
44
|
-
// A plain leaf is returned verbatim (byte-identical to v5).
|
|
41
|
+
// Concatenating every plain leaf keeps forwarded/mixed messages whole; plain
|
|
42
|
+
// always wins over HTML since alternatives duplicate the same body.
|
|
45
43
|
if (plain.length > 0)
|
|
46
44
|
return { body: plain.join('\n\n'), bodyFormat: 'plain' };
|
|
47
45
|
if (html.length > 0) {
|
|
48
46
|
const joined = html.join('\n\n');
|
|
49
47
|
if (rawHtml)
|
|
50
48
|
return { body: joined, bodyFormat: 'html' };
|
|
51
|
-
// HTML-only -> Markdown
|
|
49
|
+
// HTML-only -> Markdown. turndown failure degrades to plain text.
|
|
52
50
|
const { text, ok } = htmlToMarkdown(joined);
|
|
53
51
|
return { body: text, bodyFormat: ok ? 'markdown' : 'plain' };
|
|
54
52
|
}
|
|
@@ -92,9 +90,8 @@ function parseAddresses(headerValue) {
|
|
|
92
90
|
return out;
|
|
93
91
|
}
|
|
94
92
|
const normAddr = (a) => a.trim().toLowerCase();
|
|
95
|
-
/**
|
|
96
|
-
* names
|
|
97
|
-
* the same header-injection guard as caller-supplied ones. */
|
|
93
|
+
/** Rebuilds a comma-separated address header; composeRaw/MailComposer re-encodes
|
|
94
|
+
* names and apply the same header-injection guard as caller-supplied recipients. */
|
|
98
95
|
function formatAddresses(list) {
|
|
99
96
|
return list.map((a) => (a.name ? `${a.name} <${a.address}>` : a.address)).join(', ');
|
|
100
97
|
}
|
|
@@ -104,12 +101,12 @@ export function deriveReplySubject(sourceSubject) {
|
|
|
104
101
|
const s = sourceSubject.trim();
|
|
105
102
|
return /^re:/i.test(s) ? s : `Re: ${s}`;
|
|
106
103
|
}
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
// process; a stale miss only costs one extra self-copy, never a wrong send.
|
|
104
|
+
// Own-address set (primary + send-as aliases) for excluding the caller from
|
|
105
|
+
// reply-all; memoized per-process (a stale miss just costs one self-copy), keyed by alias+mailbox.
|
|
110
106
|
const ownAddressCache = new Map();
|
|
111
107
|
function getOwnAddresses(gmail, account, primaryEmail) {
|
|
112
|
-
|
|
108
|
+
const key = `${account} ${normAddr(primaryEmail)}`;
|
|
109
|
+
let cached = ownAddressCache.get(key);
|
|
113
110
|
if (!cached) {
|
|
114
111
|
cached = (async () => {
|
|
115
112
|
const set = new Set([normAddr(primaryEmail)]);
|
|
@@ -121,17 +118,17 @@ function getOwnAddresses(gmail, account, primaryEmail) {
|
|
|
121
118
|
}
|
|
122
119
|
}
|
|
123
120
|
catch {
|
|
124
|
-
// insufficient_scope
|
|
121
|
+
// insufficient_scope/5xx: degrade silently
|
|
125
122
|
}
|
|
126
123
|
return set;
|
|
127
124
|
})();
|
|
128
|
-
ownAddressCache.set(
|
|
125
|
+
ownAddressCache.set(key, cached);
|
|
129
126
|
}
|
|
130
127
|
return cached;
|
|
131
128
|
}
|
|
132
129
|
/** Fetch the reply source once and derive threading headers plus (auto-fill)
|
|
133
130
|
* to/cc/subject. On any fetch failure, degrade to the API id for threading and
|
|
134
|
-
* report sourceFound:false so the caller can decide
|
|
131
|
+
* report sourceFound:false so the caller can decide. */
|
|
135
132
|
export async function resolveReply(gmail, account, primaryEmail, replyToMessageId, replyAll) {
|
|
136
133
|
let headers;
|
|
137
134
|
try {
|
|
@@ -144,7 +141,7 @@ export async function resolveReply(gmail, account, primaryEmail, replyToMessageI
|
|
|
144
141
|
headers = meta.data.payload?.headers;
|
|
145
142
|
}
|
|
146
143
|
catch {
|
|
147
|
-
// Degrade to the API id
|
|
144
|
+
// Degrade to the API id rather than blocking the send.
|
|
148
145
|
return { inReplyTo: replyToMessageId, references: replyToMessageId, sourceFound: false };
|
|
149
146
|
}
|
|
150
147
|
const threading = buildReplyHeaders(replyToMessageId, getHeader(headers, 'Message-ID'), getHeader(headers, 'References'));
|
|
@@ -180,15 +177,11 @@ export async function resolveReply(gmail, account, primaryEmail, replyToMessageI
|
|
|
180
177
|
};
|
|
181
178
|
}
|
|
182
179
|
const BODY_CAP_CHARS = 50_000;
|
|
183
|
-
// gmail_read_batch aggregate ceiling:
|
|
184
|
-
// blow the context window
|
|
185
|
-
// (matches the tool's anthropic/maxResultSizeChars hint). Per-message bodies are
|
|
186
|
-
// still individually capped at BODY_CAP_CHARS first.
|
|
180
|
+
// gmail_read_batch aggregate ceiling: bounds the summed serialized output so a
|
|
181
|
+
// 100-message batch can't blow the context window (per-message bodies are capped at BODY_CAP_CHARS first).
|
|
187
182
|
const BATCH_MAX_RESULT_CHARS = 100_000;
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
// ENCODED total there rather than the spec's nominal 35 MB raw (spike/live
|
|
191
|
-
// correction — a 35 MB raw payload is ~47 MB on the wire and Gmail 400s it).
|
|
183
|
+
// messages.send rejects near ~25MB on the wire (base64 adds ~33%), so cap the
|
|
184
|
+
// estimated ENCODED total here rather than the nominal 35MB raw limit, which 400s at ~47MB wire.
|
|
192
185
|
const GMAIL_MAX_MESSAGE_BYTES = 25 * 1024 * 1024;
|
|
193
186
|
const estimateEncoded = (rawBytes) => Math.ceil((rawBytes * 4) / 3);
|
|
194
187
|
const attachmentSchema = z
|
|
@@ -202,10 +195,8 @@ const attachmentSchema = z
|
|
|
202
195
|
/** Reads attachment files into buffers (THIS server reads them, never
|
|
203
196
|
* MailComposer), enforces absolute-path + total-size guards, and derives the
|
|
204
197
|
* MIME filename via basename so a caller name can't inject path separators. */
|
|
205
|
-
// Deny reading
|
|
206
|
-
//
|
|
207
|
-
// exfil channel (this same server also reads untrusted mail/drive content, so
|
|
208
|
-
// a prompt-injected attach path is a real confused-deputy vector).
|
|
198
|
+
// Deny reading inside the server's own secret dirs; mailing out master.key /
|
|
199
|
+
// <alias>.enc / mcp-jwt.key would be account takeover (also guards against a prompt-injected attach path).
|
|
209
200
|
function isInside(dir, target) {
|
|
210
201
|
let d = path.resolve(dir);
|
|
211
202
|
let t = path.resolve(target);
|
|
@@ -279,9 +270,8 @@ export async function readAttachments(raw, bodyBytes) {
|
|
|
279
270
|
}
|
|
280
271
|
return out;
|
|
281
272
|
}
|
|
282
|
-
/** Tagged compose-time failure
|
|
283
|
-
*
|
|
284
|
-
* auto-fill raises it as not_found when a source message can't be fetched. */
|
|
273
|
+
/** Tagged compose-time failure mapped to an isError envelope; `category`
|
|
274
|
+
* defaults to validation_error (reply auto-fill raises not_found instead). */
|
|
285
275
|
class GmailComposeError extends Error {
|
|
286
276
|
slug;
|
|
287
277
|
category;
|
|
@@ -335,7 +325,7 @@ export function parseMessage(msg, bodyCap, opts) {
|
|
|
335
325
|
};
|
|
336
326
|
}
|
|
337
327
|
/**
|
|
338
|
-
*
|
|
328
|
+
* gmail_read_batch core: fetch N ids with bounded parallelism, parse each,
|
|
339
329
|
* and bound the aggregate output. Order is preserved by input index. A per-id
|
|
340
330
|
* failure becomes a `{ id, error }` entry; an account-wide auth/scope failure
|
|
341
331
|
* is rethrown so the caller maps it to a whole-call isError. Returns the
|
|
@@ -382,9 +372,8 @@ export async function readBatch(gmail, account, ids, full, rawHtml) {
|
|
|
382
372
|
}
|
|
383
373
|
}
|
|
384
374
|
}
|
|
385
|
-
// Aggregate guard: walk in order
|
|
386
|
-
//
|
|
387
|
-
// later success body so a big batch never blows the context window.
|
|
375
|
+
// Aggregate guard: walk in order accumulating serialized size; once the running
|
|
376
|
+
// total would exceed the budget, cap this and every later success body so the batch never blows the context window.
|
|
388
377
|
let running = 0;
|
|
389
378
|
let truncatedAny = false;
|
|
390
379
|
let exhausted = false;
|
|
@@ -436,13 +425,19 @@ export function compactMessageRow(m) {
|
|
|
436
425
|
snippet: snippet.length > 120 ? `${snippet.slice(0, 119)}…` : snippet,
|
|
437
426
|
};
|
|
438
427
|
}
|
|
439
|
-
export function registerGmailTools(server) {
|
|
428
|
+
export function registerGmailTools(server, deps = {}) {
|
|
429
|
+
// Per-registry, live account enum + injectable client: the schema follows
|
|
430
|
+
// the registry's account view at parse time, not a process-global default.
|
|
431
|
+
const accountEnum = accountArgLive(() => server.accountAliases()).optional();
|
|
432
|
+
const getClientFn = deps.getClientFn ?? getClient;
|
|
433
|
+
const localFiles = deps.localFiles ?? true;
|
|
434
|
+
const registerHostFileTool = localFiles ? server.registerTool : (() => undefined);
|
|
440
435
|
server.registerTool('gmail_search', {
|
|
441
436
|
description: 'Search messages in a Gmail account. Returns compact rows (id, from, subject, date, snippet); ' +
|
|
442
437
|
'pass full=true for threadId, to, labelIds and the untruncated snippet.',
|
|
443
438
|
inputSchema: {
|
|
444
439
|
account: accountEnum.describe('Google account alias'),
|
|
445
|
-
query: z.string().describe('Gmail search syntax, e.g. "from:
|
|
440
|
+
query: z.string().describe('Gmail search syntax, e.g. "from:alice is:unread"'),
|
|
446
441
|
maxResults: coerceNumber(z.number().min(1).max(100)).optional()
|
|
447
442
|
.describe('Max results to return (default: 20, max: 100)'),
|
|
448
443
|
full: coerceBoolean.optional().describe('Return the full row shape instead of the compact default'),
|
|
@@ -454,7 +449,7 @@ export function registerGmailTools(server) {
|
|
|
454
449
|
if (query.trim() === '') {
|
|
455
450
|
return invalidParams(account, '`query` is empty, and Gmail reads an empty query as "match everything".', 'Pass a real Gmail search term such as "is:unread" or "from:someone@example.com". Returning the whole mailbox for a blank query would look like a successful search.');
|
|
456
451
|
}
|
|
457
|
-
const auth = await
|
|
452
|
+
const auth = await getClientFn(account);
|
|
458
453
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
459
454
|
const listRes = await gmail.users.messages.list({
|
|
460
455
|
userId: 'me',
|
|
@@ -486,9 +481,8 @@ export function registerGmailTools(server) {
|
|
|
486
481
|
});
|
|
487
482
|
}
|
|
488
483
|
}
|
|
489
|
-
// Search is a pick-one-message step
|
|
490
|
-
// the compact row is the selection signal
|
|
491
|
-
// (p90 14.4k chars per call). full=true restores the pre-6.0 shape.
|
|
484
|
+
// Search is a pick-one-message step almost always followed by gmail_read;
|
|
485
|
+
// the compact row is the selection signal (full=true returns the previous, heavier row).
|
|
492
486
|
const rows = full === true ? results : results.map(compactMessageRow);
|
|
493
487
|
return {
|
|
494
488
|
content: [{ type: 'text', text: JSON.stringify(rows, null, 2) }],
|
|
@@ -511,7 +505,7 @@ export function registerGmailTools(server) {
|
|
|
511
505
|
},
|
|
512
506
|
}, async ({ account, messageId, full, rawHtml }) => {
|
|
513
507
|
try {
|
|
514
|
-
const auth = await
|
|
508
|
+
const auth = await getClientFn(account);
|
|
515
509
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
516
510
|
const res = await gmail.users.messages.get({
|
|
517
511
|
userId: 'me',
|
|
@@ -542,7 +536,7 @@ export function registerGmailTools(server) {
|
|
|
542
536
|
},
|
|
543
537
|
}, async ({ account, threadId, full, rawHtml, mode }) => {
|
|
544
538
|
try {
|
|
545
|
-
const auth = await
|
|
539
|
+
const auth = await getClientFn(account);
|
|
546
540
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
547
541
|
if (mode === 'summary') {
|
|
548
542
|
const res = await gmail.users.threads.get({
|
|
@@ -592,7 +586,7 @@ export function registerGmailTools(server) {
|
|
|
592
586
|
},
|
|
593
587
|
}, async ({ account, ids, full, rawHtml }) => {
|
|
594
588
|
try {
|
|
595
|
-
const auth = await
|
|
589
|
+
const auth = await getClientFn(account);
|
|
596
590
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
597
591
|
const result = await readBatch(gmail, account, ids ?? [], full === true, rawHtml === true);
|
|
598
592
|
return {
|
|
@@ -622,13 +616,15 @@ export function registerGmailTools(server) {
|
|
|
622
616
|
.describe('With replyToMessageId: include the source To+Cc (minus your own addresses) in cc. Default false (reply to sender only).'),
|
|
623
617
|
replyToThreadId: z.string().optional()
|
|
624
618
|
.describe('Thread ID to send the message in'),
|
|
625
|
-
attachments: coerceJson(attachmentSchema),
|
|
619
|
+
...(localFiles ? { attachments: coerceJson(attachmentSchema) } : {}),
|
|
626
620
|
},
|
|
627
621
|
}, async ({ account, to, subject, body, htmlBody, allowRawHtml, cc, replyToMessageId, replyAll, replyToThreadId, attachments }) => {
|
|
622
|
+
if (!localFiles && attachments?.length)
|
|
623
|
+
return hostFilesRefused(account, 'attachments');
|
|
628
624
|
try {
|
|
629
|
-
const auth = await
|
|
625
|
+
const auth = await getClientFn(account);
|
|
630
626
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
631
|
-
const config =
|
|
627
|
+
const config = server.accountSet().configs[account];
|
|
632
628
|
if (htmlBody !== undefined) {
|
|
633
629
|
throw new GmailComposeError('E_HTMLBODY_REMOVED', 'htmlBody was removed in v6: author Markdown in `body`; for literal HTML pass `allowRawHtml: true`.');
|
|
634
630
|
}
|
|
@@ -686,7 +682,7 @@ export function registerGmailTools(server) {
|
|
|
686
682
|
return mapped ?? handleGmailError(error, account);
|
|
687
683
|
}
|
|
688
684
|
});
|
|
689
|
-
|
|
685
|
+
registerHostFileTool('gmail_download_attachment', {
|
|
690
686
|
description: 'Download an email attachment to local disk. Use gmail_read first to get the attachmentId.',
|
|
691
687
|
inputSchema: {
|
|
692
688
|
account: accountEnum.describe('Google account alias'),
|
|
@@ -697,7 +693,7 @@ export function registerGmailTools(server) {
|
|
|
697
693
|
},
|
|
698
694
|
}, async ({ account, messageId, attachmentId, filename, savePath }) => {
|
|
699
695
|
try {
|
|
700
|
-
const auth = await
|
|
696
|
+
const auth = await getClientFn(account);
|
|
701
697
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
702
698
|
const res = await gmail.users.messages.attachments.get({
|
|
703
699
|
userId: 'me',
|
|
@@ -737,13 +733,15 @@ export function registerGmailTools(server) {
|
|
|
737
733
|
.describe('With replyToMessageId: include the source To+Cc (minus your own addresses) in cc. Default false (reply to sender only).'),
|
|
738
734
|
replyToThreadId: z.string().optional()
|
|
739
735
|
.describe('Thread ID to associate the draft with'),
|
|
740
|
-
attachments: coerceJson(attachmentSchema),
|
|
736
|
+
...(localFiles ? { attachments: coerceJson(attachmentSchema) } : {}),
|
|
741
737
|
},
|
|
742
738
|
}, async ({ account, to, subject, body, htmlBody, allowRawHtml, cc, replyToMessageId, replyAll, replyToThreadId, attachments }) => {
|
|
739
|
+
if (!localFiles && attachments?.length)
|
|
740
|
+
return hostFilesRefused(account, 'attachments');
|
|
743
741
|
try {
|
|
744
|
-
const auth = await
|
|
742
|
+
const auth = await getClientFn(account);
|
|
745
743
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
746
|
-
const config =
|
|
744
|
+
const config = server.accountSet().configs[account];
|
|
747
745
|
if (htmlBody !== undefined) {
|
|
748
746
|
throw new GmailComposeError('E_HTMLBODY_REMOVED', 'htmlBody was removed in v6: author Markdown in `body`; for literal HTML pass `allowRawHtml: true`.');
|
|
749
747
|
}
|
|
@@ -811,7 +809,7 @@ export function registerGmailTools(server) {
|
|
|
811
809
|
},
|
|
812
810
|
}, async ({ account, messageId, addLabelIds, removeLabelIds }) => {
|
|
813
811
|
try {
|
|
814
|
-
const auth = await
|
|
812
|
+
const auth = await getClientFn(account);
|
|
815
813
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
816
814
|
const res = await gmail.users.messages.modify({
|
|
817
815
|
userId: 'me',
|
|
@@ -838,7 +836,7 @@ export function registerGmailTools(server) {
|
|
|
838
836
|
},
|
|
839
837
|
}, async ({ account, messageId }) => {
|
|
840
838
|
try {
|
|
841
|
-
const auth = await
|
|
839
|
+
const auth = await getClientFn(account);
|
|
842
840
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
843
841
|
const res = await gmail.users.messages.trash({ userId: 'me', id: messageId });
|
|
844
842
|
return {
|
|
@@ -858,7 +856,7 @@ export function registerGmailTools(server) {
|
|
|
858
856
|
},
|
|
859
857
|
}, async ({ account, messageId }) => {
|
|
860
858
|
try {
|
|
861
|
-
const auth = await
|
|
859
|
+
const auth = await getClientFn(account);
|
|
862
860
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
863
861
|
await gmail.users.messages.delete({ userId: 'me', id: messageId });
|
|
864
862
|
return {
|
|
@@ -880,7 +878,7 @@ export function registerGmailTools(server) {
|
|
|
880
878
|
},
|
|
881
879
|
}, async ({ account, messageIds, addLabelIds, removeLabelIds }) => {
|
|
882
880
|
try {
|
|
883
|
-
const auth = await
|
|
881
|
+
const auth = await getClientFn(account);
|
|
884
882
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
885
883
|
await gmail.users.messages.batchModify({
|
|
886
884
|
userId: 'me',
|
|
@@ -907,7 +905,7 @@ export function registerGmailTools(server) {
|
|
|
907
905
|
},
|
|
908
906
|
}, async ({ account, messageIds }) => {
|
|
909
907
|
try {
|
|
910
|
-
const auth = await
|
|
908
|
+
const auth = await getClientFn(account);
|
|
911
909
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
912
910
|
await gmail.users.messages.batchDelete({
|
|
913
911
|
userId: 'me',
|
|
@@ -932,7 +930,7 @@ export function registerGmailTools(server) {
|
|
|
932
930
|
},
|
|
933
931
|
}, async ({ account, maxResults, query }) => {
|
|
934
932
|
try {
|
|
935
|
-
const auth = await
|
|
933
|
+
const auth = await getClientFn(account);
|
|
936
934
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
937
935
|
const res = await gmail.users.drafts.list({
|
|
938
936
|
userId: 'me',
|
|
@@ -956,7 +954,7 @@ export function registerGmailTools(server) {
|
|
|
956
954
|
},
|
|
957
955
|
}, async ({ account, draftId }) => {
|
|
958
956
|
try {
|
|
959
|
-
const auth = await
|
|
957
|
+
const auth = await getClientFn(account);
|
|
960
958
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
961
959
|
const res = await gmail.users.drafts.get({
|
|
962
960
|
userId: 'me',
|
|
@@ -980,7 +978,7 @@ export function registerGmailTools(server) {
|
|
|
980
978
|
},
|
|
981
979
|
}, async ({ account, draftId }) => {
|
|
982
980
|
try {
|
|
983
|
-
const auth = await
|
|
981
|
+
const auth = await getClientFn(account);
|
|
984
982
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
985
983
|
const res = await gmail.users.drafts.send({
|
|
986
984
|
userId: 'me',
|
|
@@ -1002,7 +1000,7 @@ export function registerGmailTools(server) {
|
|
|
1002
1000
|
},
|
|
1003
1001
|
}, async ({ account }) => {
|
|
1004
1002
|
try {
|
|
1005
|
-
const auth = await
|
|
1003
|
+
const auth = await getClientFn(account);
|
|
1006
1004
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
1007
1005
|
const res = await gmail.users.labels.list({ userId: 'me' });
|
|
1008
1006
|
return {
|
|
@@ -1026,7 +1024,7 @@ export function registerGmailTools(server) {
|
|
|
1026
1024
|
},
|
|
1027
1025
|
}, async ({ account, name, messageListVisibility, labelListVisibility }) => {
|
|
1028
1026
|
try {
|
|
1029
|
-
const auth = await
|
|
1027
|
+
const auth = await getClientFn(account);
|
|
1030
1028
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
1031
1029
|
const res = await gmail.users.labels.create({
|
|
1032
1030
|
userId: 'me',
|
|
@@ -1053,7 +1051,7 @@ export function registerGmailTools(server) {
|
|
|
1053
1051
|
},
|
|
1054
1052
|
}, async ({ account, labelId }) => {
|
|
1055
1053
|
try {
|
|
1056
|
-
const auth = await
|
|
1054
|
+
const auth = await getClientFn(account);
|
|
1057
1055
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
1058
1056
|
await gmail.users.labels.delete({ userId: 'me', id: labelId });
|
|
1059
1057
|
return {
|
|
@@ -1072,7 +1070,7 @@ export function registerGmailTools(server) {
|
|
|
1072
1070
|
},
|
|
1073
1071
|
}, async ({ account }) => {
|
|
1074
1072
|
try {
|
|
1075
|
-
const auth = await
|
|
1073
|
+
const auth = await getClientFn(account);
|
|
1076
1074
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
1077
1075
|
const res = await gmail.users.getProfile({ userId: 'me' });
|
|
1078
1076
|
return {
|
|
@@ -1096,7 +1094,7 @@ export function registerGmailTools(server) {
|
|
|
1096
1094
|
},
|
|
1097
1095
|
}, async ({ account, startHistoryId, maxResults, historyTypes }) => {
|
|
1098
1096
|
try {
|
|
1099
|
-
const auth = await
|
|
1097
|
+
const auth = await getClientFn(account);
|
|
1100
1098
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
1101
1099
|
const res = await gmail.users.history.list({
|
|
1102
1100
|
userId: 'me',
|
|
@@ -1120,7 +1118,7 @@ export function registerGmailTools(server) {
|
|
|
1120
1118
|
},
|
|
1121
1119
|
}, async ({ account }) => {
|
|
1122
1120
|
try {
|
|
1123
|
-
const auth = await
|
|
1121
|
+
const auth = await getClientFn(account);
|
|
1124
1122
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
1125
1123
|
const res = await gmail.users.settings.getVacation({ userId: 'me' });
|
|
1126
1124
|
return {
|
|
@@ -1146,7 +1144,7 @@ export function registerGmailTools(server) {
|
|
|
1146
1144
|
},
|
|
1147
1145
|
}, async ({ account, enableAutoReply, responseSubject, responseBodyPlainText, startTime, endTime, restrictToContacts, restrictToDomain }) => {
|
|
1148
1146
|
try {
|
|
1149
|
-
const auth = await
|
|
1147
|
+
const auth = await getClientFn(account);
|
|
1150
1148
|
const gmail = gmailClient({ version: 'v1', auth });
|
|
1151
1149
|
const res = await gmail.users.settings.updateVacation({
|
|
1152
1150
|
userId: 'me',
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { ToolRegistry } from '../registry.js';
|
|
2
2
|
import { type Policy } from '../write-control.js';
|
|
3
|
-
import { getClient } from '../client.js';
|
|
4
3
|
import { type Toolsets } from '../toolsets.js';
|
|
4
|
+
import { type ExecuteDeps } from '../executor.js';
|
|
5
5
|
import { type DiscoveryDeps, type DiscoveryMethod } from '../discovery-client.js';
|
|
6
|
-
export interface EscapeDeps extends DiscoveryDeps {
|
|
7
|
-
getClientFn?: typeof getClient;
|
|
6
|
+
export interface EscapeDeps extends DiscoveryDeps, ExecuteDeps {
|
|
8
7
|
toolsets?: Toolsets;
|
|
9
8
|
}
|
|
10
9
|
/** Up to three closest known ids for the unknown_method did-you-mean hint;
|
package/dist/tools/google-api.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { isAllowed, writeDisabledResult } from '../write-control.js';
|
|
3
|
-
import {
|
|
3
|
+
import { accountArgLive } from '../accounts.js';
|
|
4
4
|
import { getClient } from '../client.js';
|
|
5
5
|
import { coerceJson } from './_coerce.js';
|
|
6
6
|
import { getToolsets, toolsetEnabled } from '../toolsets.js';
|
|
7
|
-
import { editDistance } from '../scope-catalog.js';
|
|
7
|
+
import { editDistance, SUGGEST_MAX_INPUT } from '../scope-catalog.js';
|
|
8
8
|
import { executeApiMethod, jsonResult } from '../executor.js';
|
|
9
|
-
import { SUPPORTED_APIS, resolveApiAliases, cudFromMethod, loadMethodIndex, searchMethods, } from '../discovery-client.js';
|
|
10
|
-
|
|
9
|
+
import { SUPPORTED_APIS, resolveApiAliases, cudFromMethod, loadMethodIndex, searchMethods, MAX_SEARCH_QUERY, } from '../discovery-client.js';
|
|
10
|
+
import { hints } from '../hint-provider.js';
|
|
11
11
|
// Policy/toolset namespace for each API alias must match the NAMED tools' service
|
|
12
12
|
// names, or user deny globs and GOOGLE_TOOLSETS silently miss escape-hatch calls.
|
|
13
13
|
const SERVICE_FOR_ALIAS = {
|
|
@@ -32,12 +32,21 @@ const SERVICE_FOR_ALIAS = {
|
|
|
32
32
|
analyticsadmin: 'analytics',
|
|
33
33
|
analyticsdata: 'analytics',
|
|
34
34
|
};
|
|
35
|
+
/** Discovery ids are short dotted identifiers; the bound keeps a caller from
|
|
36
|
+
* sending megabytes through lookup, suggestion and the echoed message. */
|
|
37
|
+
const MAX_METHOD_ID = 256;
|
|
38
|
+
const MAX_API_ALIAS = 64;
|
|
35
39
|
/** Up to three closest known ids for the unknown_method did-you-mean hint;
|
|
36
40
|
* bounded distance so unrelated ids never masquerade as suggestions. */
|
|
37
41
|
export function nearestMethodIds(methodId, index) {
|
|
42
|
+
if (methodId.length > SUGGEST_MAX_INPUT)
|
|
43
|
+
return [];
|
|
38
44
|
const maxDist = Math.max(3, Math.floor(methodId.length / 3));
|
|
45
|
+
const wanted = methodId.toLowerCase();
|
|
39
46
|
return index
|
|
40
|
-
|
|
47
|
+
// the distance is at least the length gap, so most ids skip the DP
|
|
48
|
+
.filter((m) => Math.abs(m.id.length - wanted.length) <= maxDist)
|
|
49
|
+
.map((m) => ({ id: m.id, d: editDistance(wanted, m.id.toLowerCase()) }))
|
|
41
50
|
.filter((x) => x.d <= maxDist)
|
|
42
51
|
.sort((a, b) => a.d - b.d)
|
|
43
52
|
.slice(0, 3)
|
|
@@ -55,6 +64,8 @@ function describeMethod(m) {
|
|
|
55
64
|
};
|
|
56
65
|
}
|
|
57
66
|
export function registerEscapeTools(registry, policy, deps = {}) {
|
|
67
|
+
// Live per-registry account validation, same as the curated files.
|
|
68
|
+
const accountEnum = accountArgLive(() => registry.accountAliases()).optional();
|
|
58
69
|
const getClientFn = deps.getClientFn ?? getClient;
|
|
59
70
|
const toolsets = deps.toolsets ?? getToolsets();
|
|
60
71
|
const serviceForAlias = (alias) => SERVICE_FOR_ALIAS[alias] ?? alias;
|
|
@@ -63,19 +74,22 @@ export function registerEscapeTools(registry, policy, deps = {}) {
|
|
|
63
74
|
};
|
|
64
75
|
const enabledApis = Object.keys(SUPPORTED_APIS).filter(apiEnabled);
|
|
65
76
|
const apiList = enabledApis.join(', ');
|
|
66
|
-
const toolsetDisabled = (alias) =>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
const toolsetDisabled = (alias) => {
|
|
78
|
+
const rendered = hints()?.render({ kind: 'toolset_disabled', api: alias, service: serviceForAlias(alias) });
|
|
79
|
+
return jsonResult({
|
|
80
|
+
error: 'toolset_disabled',
|
|
81
|
+
message: rendered?.message ?? `API "${alias}" maps to service "${serviceForAlias(alias)}", which is excluded by GOOGLE_TOOLSETS.`,
|
|
82
|
+
hint: rendered?.hint ?? 'Add the service to GOOGLE_TOOLSETS (or unset it) to use this API.',
|
|
83
|
+
retriable: false,
|
|
84
|
+
}, true);
|
|
85
|
+
};
|
|
72
86
|
registry.registerMeta('google_api_search', {
|
|
73
87
|
description: 'Search the Google API Discovery index for any Workspace REST method, including ones with no ' +
|
|
74
88
|
'dedicated tool here. Returns method ids + parameters to invoke via google_api_call. ' +
|
|
75
89
|
`APIs: ${apiList}.`,
|
|
76
90
|
inputSchema: {
|
|
77
|
-
query: z.string().describe('Keywords, e.g. "slides create presentation" or "drive revisions"'),
|
|
78
|
-
api: z.string().optional().describe('Restrict the search to one API alias'),
|
|
91
|
+
query: z.string().max(MAX_SEARCH_QUERY).describe('Keywords, e.g. "slides create presentation" or "drive revisions"'),
|
|
92
|
+
api: z.string().max(MAX_API_ALIAS).optional().describe('Restrict the search to one API alias'),
|
|
79
93
|
maxResults: z.number().min(1).max(25).default(10).optional(),
|
|
80
94
|
},
|
|
81
95
|
annotations: { openWorldHint: true },
|
|
@@ -125,8 +139,8 @@ export function registerEscapeTools(registry, policy, deps = {}) {
|
|
|
125
139
|
'drive.files.export) use drive_download / drive_export instead.',
|
|
126
140
|
inputSchema: {
|
|
127
141
|
account: accountEnum.describe('Google account alias (omit for the default account)'),
|
|
128
|
-
api: z.string().describe(`API alias: ${apiList}`),
|
|
129
|
-
methodId: z.string().min(1).describe('Discovery method id, e.g. "drive.revisions.list"'),
|
|
142
|
+
api: z.string().max(MAX_API_ALIAS).describe(`API alias: ${apiList}`),
|
|
143
|
+
methodId: z.string().min(1).max(MAX_METHOD_ID).describe('Discovery method id, e.g. "drive.revisions.list"'),
|
|
130
144
|
pathParams: coerceJson(z.record(z.string(), z.union([z.string(), z.number()])).optional())
|
|
131
145
|
.describe('Values for {placeholders} in the method path'),
|
|
132
146
|
queryParams: coerceJson(z
|
|
@@ -164,9 +178,8 @@ export function registerEscapeTools(registry, policy, deps = {}) {
|
|
|
164
178
|
}
|
|
165
179
|
let method = index.find((m) => m.id === methodId);
|
|
166
180
|
if (!method) {
|
|
167
|
-
// Some discovery docs keep a legacy id prefix (
|
|
168
|
-
//
|
|
169
|
-
// alias, retry under the doc's own prefix before failing.
|
|
181
|
+
// Some discovery docs keep a legacy id prefix (searchconsole's methods are
|
|
182
|
+
// webmasters.*): retry under the doc's own prefix if the caller used our api alias.
|
|
170
183
|
const docPrefix = index[0]?.id.split('.')[0];
|
|
171
184
|
const [head, ...rest] = String(methodId).split('.');
|
|
172
185
|
if (docPrefix && (head === apiKey || head === api) && head !== docPrefix && rest.length > 0) {
|
|
@@ -191,9 +204,8 @@ export function registerEscapeTools(registry, policy, deps = {}) {
|
|
|
191
204
|
const cud = cudFromMethod(method);
|
|
192
205
|
const policyService = serviceForAlias(apiKey);
|
|
193
206
|
const lastSegment = method.id.split('.').pop() ?? method.id;
|
|
194
|
-
// The escape hatch self-gates, so it needs the same scope signal the
|
|
195
|
-
//
|
|
196
|
-
// safe-writes here while its generated twin is refused.
|
|
207
|
+
// The escape hatch self-gates, so it needs the same scope signal as the registry
|
|
208
|
+
// wrapper, or a privileged method would slip past safe-writes here while its generated twin is refused.
|
|
197
209
|
const toolRef = { name: `${policyService}_${lastSegment}`, service: policyService, cud, scopes: method.scopes };
|
|
198
210
|
if (cud !== 'read' && !isAllowed(toolRef, policy)) {
|
|
199
211
|
return writeDisabledResult(toolRef, policy, account);
|
|
@@ -203,6 +215,6 @@ export function registerEscapeTools(registry, policy, deps = {}) {
|
|
|
203
215
|
pathParams: pathParams,
|
|
204
216
|
queryParams: queryParams,
|
|
205
217
|
body,
|
|
206
|
-
}, { getClientFn });
|
|
218
|
+
}, { getClientFn, scopeDeps: deps.scopeDeps });
|
|
207
219
|
});
|
|
208
220
|
}
|
package/dist/tools/meet.d.ts
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
1
|
import type { ToolRegistry } from '../registry.js';
|
|
2
|
-
|
|
2
|
+
import type { Account } from '../accounts.js';
|
|
3
|
+
import { type CuratedToolDeps } from '../client.js';
|
|
4
|
+
export declare function registerMeetTools(server: ToolRegistry, deps?: CuratedToolDeps): void;
|
|
5
|
+
export declare function handleMeetError(error: any, account: Account): {
|
|
6
|
+
content: {
|
|
7
|
+
type: "text";
|
|
8
|
+
text: string;
|
|
9
|
+
}[];
|
|
10
|
+
isError: true;
|
|
11
|
+
};
|
package/dist/tools/meet.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { meet as meetClient } from '@googleapis/meet';
|
|
3
|
-
import {
|
|
3
|
+
import { accountArgLive } from '../accounts.js';
|
|
4
4
|
import { getClient } from '../client.js';
|
|
5
5
|
import { handleGoogleApiError } from './_errors.js';
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
export function registerMeetTools(server, deps = {}) {
|
|
7
|
+
// Per-registry, live account enum + injectable client: the schema follows
|
|
8
|
+
// the registry's account view at parse time, not a process-global default.
|
|
9
|
+
const accountEnum = accountArgLive(() => server.accountAliases()).optional();
|
|
10
|
+
const getClientFn = deps.getClientFn ?? getClient;
|
|
8
11
|
// ─── Conference records (past meetings) ────────────────────────────────
|
|
9
12
|
server.registerTool('meet_conference_records_list', {
|
|
10
13
|
description: 'List past conference records (completed Meet sessions) the user has access to',
|
|
@@ -16,7 +19,7 @@ export function registerMeetTools(server) {
|
|
|
16
19
|
},
|
|
17
20
|
}, async ({ account, pageSize, pageToken, filter }) => {
|
|
18
21
|
try {
|
|
19
|
-
const auth = await
|
|
22
|
+
const auth = await getClientFn(account);
|
|
20
23
|
const meet = meetClient({ version: 'v2', auth });
|
|
21
24
|
const res = await meet.conferenceRecords.list({
|
|
22
25
|
pageSize: pageSize ?? 20,
|
|
@@ -39,7 +42,7 @@ export function registerMeetTools(server) {
|
|
|
39
42
|
},
|
|
40
43
|
}, async ({ account, name }) => {
|
|
41
44
|
try {
|
|
42
|
-
const auth = await
|
|
45
|
+
const auth = await getClientFn(account);
|
|
43
46
|
const meet = meetClient({ version: 'v2', auth });
|
|
44
47
|
const res = await meet.conferenceRecords.get({ name });
|
|
45
48
|
return {
|
|
@@ -61,7 +64,7 @@ export function registerMeetTools(server) {
|
|
|
61
64
|
},
|
|
62
65
|
}, async ({ account, parent, pageSize, pageToken }) => {
|
|
63
66
|
try {
|
|
64
|
-
const auth = await
|
|
67
|
+
const auth = await getClientFn(account);
|
|
65
68
|
const meet = meetClient({ version: 'v2', auth });
|
|
66
69
|
const res = await meet.conferenceRecords.recordings.list({
|
|
67
70
|
parent,
|
|
@@ -87,7 +90,7 @@ export function registerMeetTools(server) {
|
|
|
87
90
|
},
|
|
88
91
|
}, async ({ account, parent, pageSize, pageToken }) => {
|
|
89
92
|
try {
|
|
90
|
-
const auth = await
|
|
93
|
+
const auth = await getClientFn(account);
|
|
91
94
|
const meet = meetClient({ version: 'v2', auth });
|
|
92
95
|
const res = await meet.conferenceRecords.transcripts.list({
|
|
93
96
|
parent,
|
|
@@ -112,7 +115,7 @@ export function registerMeetTools(server) {
|
|
|
112
115
|
},
|
|
113
116
|
}, async ({ account, parent, pageSize, pageToken }) => {
|
|
114
117
|
try {
|
|
115
|
-
const auth = await
|
|
118
|
+
const auth = await getClientFn(account);
|
|
116
119
|
const meet = meetClient({ version: 'v2', auth });
|
|
117
120
|
const res = await meet.conferenceRecords.transcripts.entries.list({
|
|
118
121
|
parent,
|
|
@@ -128,7 +131,7 @@ export function registerMeetTools(server) {
|
|
|
128
131
|
}
|
|
129
132
|
});
|
|
130
133
|
}
|
|
131
|
-
function handleMeetError(error, account) {
|
|
134
|
+
export function handleMeetError(error, account) {
|
|
132
135
|
return handleGoogleApiError(error, account, {
|
|
133
136
|
scope: 'Meet needs the meetings.space.readonly scope and the Google Meet API enabled in Cloud Console. Confirm both for this account.',
|
|
134
137
|
resource: `Meet denied this conference record to "${account}". Meet exposes records only to the organizer or a participant, so check which account hosted the meeting.`,
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import type { ToolRegistry } from '../registry.js';
|
|
2
|
-
|
|
2
|
+
import { type CuratedToolDeps } from '../client.js';
|
|
3
|
+
export declare function registerSearchConsoleTools(server: ToolRegistry, deps?: CuratedToolDeps): void;
|