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.
Files changed (181) hide show
  1. package/README.md +5 -5
  2. package/dist/accounts.d.ts +20 -4
  3. package/dist/accounts.js +68 -44
  4. package/dist/api-probe.d.ts +5 -0
  5. package/dist/api-probe.js +20 -15
  6. package/dist/arg-normalize.js +19 -38
  7. package/dist/arg-strict.d.ts +7 -0
  8. package/dist/arg-strict.js +38 -28
  9. package/dist/auth.d.ts +5 -4
  10. package/dist/auth.js +16 -23
  11. package/dist/boot-gates.d.ts +26 -0
  12. package/dist/boot-gates.js +36 -0
  13. package/dist/cimd-cache.d.ts +38 -0
  14. package/dist/cimd-cache.js +111 -0
  15. package/dist/client-config.d.ts +2 -2
  16. package/dist/client-config.js +8 -14
  17. package/dist/client.d.ts +32 -2
  18. package/dist/client.js +0 -0
  19. package/dist/compose.d.ts +18 -0
  20. package/dist/compose.js +109 -0
  21. package/dist/config-file.d.ts +13 -0
  22. package/dist/config-file.js +41 -10
  23. package/dist/discover.js +7 -10
  24. package/dist/discovery-client.d.ts +3 -0
  25. package/dist/discovery-client.js +36 -32
  26. package/dist/doctor.d.ts +9 -5
  27. package/dist/doctor.js +119 -29
  28. package/dist/env-load.js +2 -5
  29. package/dist/executor.d.ts +3 -0
  30. package/dist/executor.js +6 -6
  31. package/dist/fanout.d.ts +9 -1
  32. package/dist/fanout.js +47 -22
  33. package/dist/fs-atomic.d.ts +8 -0
  34. package/dist/fs-atomic.js +216 -29
  35. package/dist/hint-provider.d.ts +129 -0
  36. package/dist/hint-provider.js +58 -0
  37. package/dist/http-config.d.ts +2 -2
  38. package/dist/http-config.js +3 -7
  39. package/dist/http-transport.d.ts +67 -9
  40. package/dist/http-transport.js +318 -52
  41. package/dist/identity.d.ts +6 -3
  42. package/dist/identity.js +24 -8
  43. package/dist/index.js +76 -160
  44. package/dist/master-key.d.ts +3 -3
  45. package/dist/master-key.js +16 -19
  46. package/dist/mcp-token.d.ts +126 -19
  47. package/dist/mcp-token.js +557 -76
  48. package/dist/metrics-cli.d.ts +1 -1
  49. package/dist/metrics-cli.js +1 -1
  50. package/dist/metrics-tap.js +2 -5
  51. package/dist/migrate-config.d.ts +1 -2
  52. package/dist/migrate-config.js +5 -7
  53. package/dist/net-tuning.js +2 -4
  54. package/dist/oauth-as.d.ts +104 -4
  55. package/dist/oauth-as.js +368 -114
  56. package/dist/oauth-consent.d.ts +10 -0
  57. package/dist/oauth-consent.js +20 -10
  58. package/dist/outbound-allowlist.js +9 -11
  59. package/dist/reauth-hint.d.ts +1 -1
  60. package/dist/reauth-hint.js +11 -10
  61. package/dist/registry-transfer.d.ts +5 -0
  62. package/dist/registry-transfer.js +63 -11
  63. package/dist/registry.d.ts +13 -4
  64. package/dist/registry.js +100 -72
  65. package/dist/scope-catalog.d.ts +16 -0
  66. package/dist/scope-catalog.js +40 -13
  67. package/dist/scope-observability.d.ts +10 -9
  68. package/dist/scope-observability.js +34 -24
  69. package/dist/services.d.ts +18 -10
  70. package/dist/services.js +61 -22
  71. package/dist/ssrf-guard.d.ts +23 -9
  72. package/dist/ssrf-guard.js +106 -78
  73. package/dist/tenant-purge.d.ts +26 -0
  74. package/dist/tenant-purge.js +65 -0
  75. package/dist/token-store.d.ts +20 -2
  76. package/dist/token-store.js +0 -0
  77. package/dist/tools/_coerce.d.ts +4 -4
  78. package/dist/tools/_errors.d.ts +3 -0
  79. package/dist/tools/_errors.js +72 -60
  80. package/dist/tools/_local-files.d.ts +9 -0
  81. package/dist/tools/_local-files.js +22 -8
  82. package/dist/tools/account-wizard.d.ts +13 -3
  83. package/dist/tools/account-wizard.js +107 -56
  84. package/dist/tools/accounts-tool.d.ts +8 -1
  85. package/dist/tools/accounts-tool.js +35 -19
  86. package/dist/tools/admin.d.ts +10 -1
  87. package/dist/tools/admin.js +14 -11
  88. package/dist/tools/analytics.d.ts +10 -1
  89. package/dist/tools/analytics.js +14 -10
  90. package/dist/tools/calendar.d.ts +2 -1
  91. package/dist/tools/calendar.js +19 -18
  92. package/dist/tools/chat.d.ts +10 -1
  93. package/dist/tools/chat.js +12 -8
  94. package/dist/tools/contacts.d.ts +2 -1
  95. package/dist/tools/contacts.js +22 -19
  96. package/dist/tools/docs.d.ts +2 -1
  97. package/dist/tools/docs.js +35 -36
  98. package/dist/tools/drive.d.ts +2 -1
  99. package/dist/tools/drive.js +80 -74
  100. package/dist/tools/forms.d.ts +10 -1
  101. package/dist/tools/forms.js +18 -12
  102. package/dist/tools/generated/_shared.d.ts +2 -1
  103. package/dist/tools/generated/_shared.js +7 -3
  104. package/dist/tools/generated/admin.d.ts +2 -1
  105. package/dist/tools/generated/admin.js +265 -265
  106. package/dist/tools/generated/analytics.d.ts +2 -1
  107. package/dist/tools/generated/analytics.js +125 -125
  108. package/dist/tools/generated/appsmarket.d.ts +2 -1
  109. package/dist/tools/generated/appsmarket.js +5 -5
  110. package/dist/tools/generated/calendar.d.ts +2 -1
  111. package/dist/tools/generated/calendar.js +55 -55
  112. package/dist/tools/generated/chat.d.ts +2 -1
  113. package/dist/tools/generated/chat.js +95 -95
  114. package/dist/tools/generated/classroom.d.ts +2 -1
  115. package/dist/tools/generated/classroom.js +209 -209
  116. package/dist/tools/generated/cloudidentity.d.ts +2 -1
  117. package/dist/tools/generated/cloudidentity.js +133 -133
  118. package/dist/tools/generated/cloudsearch.d.ts +2 -1
  119. package/dist/tools/generated/cloudsearch.js +99 -99
  120. package/dist/tools/generated/contacts.d.ts +2 -1
  121. package/dist/tools/generated/contacts.js +29 -29
  122. package/dist/tools/generated/drive.d.ts +2 -1
  123. package/dist/tools/generated/drive.js +71 -71
  124. package/dist/tools/generated/driveactivity.d.ts +2 -1
  125. package/dist/tools/generated/driveactivity.js +3 -3
  126. package/dist/tools/generated/drivelabels.d.ts +2 -1
  127. package/dist/tools/generated/drivelabels.js +53 -53
  128. package/dist/tools/generated/forms.d.ts +2 -1
  129. package/dist/tools/generated/forms.js +7 -7
  130. package/dist/tools/generated/gmail.d.ts +2 -1
  131. package/dist/tools/generated/gmail.js +117 -117
  132. package/dist/tools/generated/groupsmigration.d.ts +2 -1
  133. package/dist/tools/generated/groupsmigration.js +3 -3
  134. package/dist/tools/generated/groupssettings.d.ts +2 -1
  135. package/dist/tools/generated/groupssettings.js +7 -7
  136. package/dist/tools/generated/index.d.ts +2 -1
  137. package/dist/tools/generated/keep.d.ts +2 -1
  138. package/dist/tools/generated/keep.js +15 -15
  139. package/dist/tools/generated/licensing.d.ts +2 -1
  140. package/dist/tools/generated/licensing.js +15 -15
  141. package/dist/tools/generated/meet.d.ts +2 -1
  142. package/dist/tools/generated/meet.js +27 -27
  143. package/dist/tools/generated/postmaster.d.ts +2 -1
  144. package/dist/tools/generated/postmaster.js +9 -9
  145. package/dist/tools/generated/reseller.d.ts +2 -1
  146. package/dist/tools/generated/reseller.js +35 -35
  147. package/dist/tools/generated/script.d.ts +2 -1
  148. package/dist/tools/generated/script.js +33 -33
  149. package/dist/tools/generated/searchconsole.d.ts +2 -1
  150. package/dist/tools/generated/searchconsole.js +3 -3
  151. package/dist/tools/generated/sheets.d.ts +2 -1
  152. package/dist/tools/generated/sheets.js +15 -15
  153. package/dist/tools/generated/tasks.d.ts +2 -1
  154. package/dist/tools/generated/tasks.js +5 -5
  155. package/dist/tools/generated/vault.d.ts +2 -1
  156. package/dist/tools/generated/vault.js +67 -67
  157. package/dist/tools/generated/workspaceevents.d.ts +2 -1
  158. package/dist/tools/generated/workspaceevents.js +31 -31
  159. package/dist/tools/gmail-mime.d.ts +1 -1
  160. package/dist/tools/gmail-mime.js +7 -12
  161. package/dist/tools/gmail.d.ts +4 -3
  162. package/dist/tools/gmail.js +67 -69
  163. package/dist/tools/google-api.d.ts +2 -3
  164. package/dist/tools/google-api.js +34 -22
  165. package/dist/tools/meet.d.ts +10 -1
  166. package/dist/tools/meet.js +12 -9
  167. package/dist/tools/searchconsole.d.ts +2 -1
  168. package/dist/tools/searchconsole.js +16 -13
  169. package/dist/tools/sheets.d.ts +2 -1
  170. package/dist/tools/sheets.js +35 -32
  171. package/dist/tools/slides.d.ts +10 -1
  172. package/dist/tools/slides.js +16 -10
  173. package/dist/tools/tasks.d.ts +2 -1
  174. package/dist/tools/tasks.js +18 -15
  175. package/dist/trim.d.ts +5 -0
  176. package/dist/trim.js +19 -0
  177. package/dist/usage-metrics.d.ts +23 -0
  178. package/dist/usage-metrics.js +67 -56
  179. package/dist/write-control.d.ts +4 -8
  180. package/dist/write-control.js +13 -6
  181. package/package.json +47 -21
@@ -1,13 +1,16 @@
1
1
  import { z } from 'zod';
2
2
  import { coerceArray, coerceBoolean } from './_coerce.js';
3
3
  import { calendar as calendarClient } from '@googleapis/calendar';
4
- import { accountAliasSchema } from '../accounts.js';
4
+ import { accountArgLive } from '../accounts.js';
5
5
  import { getClient } from '../client.js';
6
6
  import { checkOutbound } from '../outbound-allowlist.js';
7
7
  import { handleGoogleApiError, invalidParams } from './_errors.js';
8
8
  import { listResult, sliceClean } from '../trim.js';
9
- const accountEnum = accountAliasSchema.optional();
10
- export function registerCalendarTools(server) {
9
+ export function registerCalendarTools(server, deps = {}) {
10
+ // Per-registry, live account enum + injectable client: the schema follows
11
+ // the registry's account view at parse time, not a process-global default.
12
+ const accountEnum = accountArgLive(() => server.accountAliases()).optional();
13
+ const getClientFn = deps.getClientFn ?? getClient;
11
14
  server.registerTool('calendar_list_calendars', {
12
15
  description: 'List all calendars for a Google account',
13
16
  inputSchema: {
@@ -15,7 +18,7 @@ export function registerCalendarTools(server) {
15
18
  },
16
19
  }, async ({ account }) => {
17
20
  try {
18
- const auth = await getClient(account);
21
+ const auth = await getClientFn(account);
19
22
  const cal = calendarClient({ version: 'v3', auth });
20
23
  const res = await cal.calendarList.list();
21
24
  const calendars = (res.data.items ?? []).map((c) => ({
@@ -55,7 +58,7 @@ export function registerCalendarTools(server) {
55
58
  if (rangeError)
56
59
  return invalidParams(account, rangeError, TIME_RANGE_HINT);
57
60
  try {
58
- const auth = await getClient(account);
61
+ const auth = await getClientFn(account);
59
62
  const cal = calendarClient({ version: 'v3', auth });
60
63
  const params = {
61
64
  calendarId: calendarId ?? 'primary',
@@ -98,7 +101,7 @@ export function registerCalendarTools(server) {
98
101
  },
99
102
  }, async ({ account, eventId, calendarId }) => {
100
103
  try {
101
- const auth = await getClient(account);
104
+ const auth = await getClientFn(account);
102
105
  const cal = calendarClient({ version: 'v3', auth });
103
106
  const res = await cal.events.get({
104
107
  calendarId: calendarId ?? 'primary',
@@ -135,7 +138,7 @@ export function registerCalendarTools(server) {
135
138
  if (outbound)
136
139
  return outbound;
137
140
  }
138
- const auth = await getClient(account);
141
+ const auth = await getClientFn(account);
139
142
  const cal = calendarClient({ version: 'v3', auth });
140
143
  const event = { summary };
141
144
  if (allDay) {
@@ -190,7 +193,7 @@ export function registerCalendarTools(server) {
190
193
  if (outbound)
191
194
  return outbound;
192
195
  }
193
- const auth = await getClient(account);
196
+ const auth = await getClientFn(account);
194
197
  const cal = calendarClient({ version: 'v3', auth });
195
198
  // Fetch the event first so a 404 surfaces before we attempt the patch.
196
199
  await cal.events.get({
@@ -243,7 +246,7 @@ export function registerCalendarTools(server) {
243
246
  },
244
247
  }, async ({ account, eventId, calendarId }) => {
245
248
  try {
246
- const auth = await getClient(account);
249
+ const auth = await getClientFn(account);
247
250
  const cal = calendarClient({ version: 'v3', auth });
248
251
  await cal.events.delete({
249
252
  calendarId: calendarId ?? 'primary',
@@ -268,7 +271,7 @@ export function registerCalendarTools(server) {
268
271
  },
269
272
  }, async ({ account, calendarId, text, sendNotifications }) => {
270
273
  try {
271
- const auth = await getClient(account);
274
+ const auth = await getClientFn(account);
272
275
  const cal = calendarClient({ version: 'v3', auth });
273
276
  const res = await cal.events.quickAdd({
274
277
  calendarId: calendarId ?? 'primary',
@@ -294,7 +297,7 @@ export function registerCalendarTools(server) {
294
297
  },
295
298
  }, async ({ account, calendarId, eventId, destinationCalendarId, sendNotifications }) => {
296
299
  try {
297
- const auth = await getClient(account);
300
+ const auth = await getClientFn(account);
298
301
  const cal = calendarClient({ version: 'v3', auth });
299
302
  const res = await cal.events.move({
300
303
  calendarId,
@@ -329,7 +332,7 @@ export function registerCalendarTools(server) {
329
332
  if (rangeError)
330
333
  return invalidParams(account, rangeError, TIME_RANGE_HINT);
331
334
  try {
332
- const auth = await getClient(account);
335
+ const auth = await getClientFn(account);
333
336
  const cal = calendarClient({ version: 'v3', auth });
334
337
  const res = await cal.events.instances({
335
338
  calendarId: calendarId ?? 'primary',
@@ -367,7 +370,7 @@ export function registerCalendarTools(server) {
367
370
  const rangeError = timeRangeError(timeMin, timeMax);
368
371
  if (rangeError)
369
372
  return invalidParams(account, rangeError, TIME_RANGE_HINT);
370
- const auth = await getClient(account);
373
+ const auth = await getClientFn(account);
371
374
  const cal = calendarClient({ version: 'v3', auth });
372
375
  const res = await cal.freebusy.query({
373
376
  requestBody: {
@@ -378,10 +381,8 @@ export function registerCalendarTools(server) {
378
381
  },
379
382
  });
380
383
  return {
381
- // freebusy answers 200 with NO `calendars` key on an empty items
382
- // list, and JSON.stringify(undefined) returns undefined, not a
383
- // string: the SDK then rejected our own result with -32602. The
384
- // value is a map keyed by calendar id, so the fallback is {}.
384
+ // Empty items returns 200 with no `calendars` key; JSON.stringify(undefined)
385
+ // would make the SDK reject our own result, so fall back to {}.
385
386
  content: [{ type: 'text', text: JSON.stringify(res.data.calendars ?? {}, null, 2) }],
386
387
  };
387
388
  }
@@ -399,7 +400,7 @@ export function registerCalendarTools(server) {
399
400
  },
400
401
  }, async ({ account, summary, description, timeZone }) => {
401
402
  try {
402
- const auth = await getClient(account);
403
+ const auth = await getClientFn(account);
403
404
  const cal = calendarClient({ version: 'v3', auth });
404
405
  const res = await cal.calendars.insert({
405
406
  requestBody: {
@@ -1,2 +1,11 @@
1
1
  import type { ToolRegistry } from '../registry.js';
2
- export declare function registerChatTools(server: ToolRegistry): void;
2
+ import type { Account } from '../accounts.js';
3
+ import { type CuratedToolDeps } from '../client.js';
4
+ export declare function registerChatTools(server: ToolRegistry, deps?: CuratedToolDeps): void;
5
+ export declare function handleChatError(error: any, account: Account): {
6
+ content: {
7
+ type: "text";
8
+ text: string;
9
+ }[];
10
+ isError: true;
11
+ };
@@ -1,11 +1,14 @@
1
1
  import { z } from 'zod';
2
2
  import { coerceJson } from './_coerce.js';
3
3
  import { chat as chatClient } from '@googleapis/chat';
4
- import { accountAliasSchema } from '../accounts.js';
4
+ import { accountArgLive } from '../accounts.js';
5
5
  import { getClient } from '../client.js';
6
6
  import { handleGoogleApiError, invalidParams } from './_errors.js';
7
- const accountEnum = accountAliasSchema.optional();
8
- export function registerChatTools(server) {
7
+ export function registerChatTools(server, deps = {}) {
8
+ // Per-registry, live account enum + injectable client: the schema follows
9
+ // the registry's account view at parse time, not a process-global default.
10
+ const accountEnum = accountArgLive(() => server.accountAliases()).optional();
11
+ const getClientFn = deps.getClientFn ?? getClient;
9
12
  server.registerTool('chat_spaces_list', {
10
13
  description: 'List Google Chat spaces the user is a member of',
11
14
  inputSchema: {
@@ -16,7 +19,7 @@ export function registerChatTools(server) {
16
19
  },
17
20
  }, async ({ account, pageSize, pageToken, filter }) => {
18
21
  try {
19
- const auth = await getClient(account);
22
+ const auth = await getClientFn(account);
20
23
  const chat = chatClient({ version: 'v1', auth });
21
24
  const res = await chat.spaces.list({
22
25
  pageSize: pageSize ?? 100,
@@ -39,7 +42,7 @@ export function registerChatTools(server) {
39
42
  },
40
43
  }, async ({ account, name }) => {
41
44
  try {
42
- const auth = await getClient(account);
45
+ const auth = await getClientFn(account);
43
46
  const chat = chatClient({ version: 'v1', auth });
44
47
  const res = await chat.spaces.get({ name });
45
48
  return {
@@ -65,7 +68,7 @@ export function registerChatTools(server) {
65
68
  if (!text && (!cardsV2 || cardsV2.length === 0)) {
66
69
  return invalidParams(account, 'A message needs content: neither text nor cardsV2 was supplied.', 'Pass text for a plain message, or cardsV2 for a Card v2 payload. Both may be sent together.');
67
70
  }
68
- const auth = await getClient(account);
71
+ const auth = await getClientFn(account);
69
72
  const chat = chatClient({ version: 'v1', auth });
70
73
  const requestBody = {};
71
74
  if (text)
@@ -99,7 +102,7 @@ export function registerChatTools(server) {
99
102
  },
100
103
  }, async ({ account, parent, pageSize, pageToken, filter, orderBy }) => {
101
104
  try {
102
- const auth = await getClient(account);
105
+ const auth = await getClientFn(account);
103
106
  const chat = chatClient({ version: 'v1', auth });
104
107
  const res = await chat.spaces.messages.list({
105
108
  parent,
@@ -117,9 +120,10 @@ export function registerChatTools(server) {
117
120
  }
118
121
  });
119
122
  }
120
- function handleChatError(error, account) {
123
+ export function handleChatError(error, account) {
121
124
  return handleGoogleApiError(error, account, {
122
125
  scope: 'Chat tools require the optional "chat" scope bundle: add it to this account\'s scope profile, then re-auth.',
123
126
  resource: `Google Chat denied this space or message to "${account}". The scope is not the problem: check that the account is a member of the space, and that Chat is turned on for the Workspace.`,
127
+ bundle: 'chat',
124
128
  });
125
129
  }
@@ -1,4 +1,5 @@
1
1
  import type { ToolRegistry } from '../registry.js';
2
+ import { type CuratedToolDeps } from '../client.js';
2
3
  export interface ResolveCandidate {
3
4
  resourceName: string;
4
5
  displayName: string;
@@ -39,4 +40,4 @@ export declare function resolveContacts(candidates: ResolveCandidate[], name: st
39
40
  }[];
40
41
  resolved?: undefined;
41
42
  };
42
- export declare function registerContactsTools(server: ToolRegistry): void;
43
+ export declare function registerContactsTools(server: ToolRegistry, deps?: CuratedToolDeps): void;
@@ -1,11 +1,10 @@
1
1
  import { z } from 'zod';
2
2
  import { people as peopleClient } from '@googleapis/people';
3
- import { accountAliasSchema } from '../accounts.js';
3
+ import { accountArgLive } from '../accounts.js';
4
4
  import { getClient } from '../client.js';
5
5
  import { coerceBoolean } from './_coerce.js';
6
6
  import { handleGoogleApiError, invalidParams } from './_errors.js';
7
7
  import { listResult } from '../trim.js';
8
- const accountEnum = accountAliasSchema.optional();
9
8
  const PERSON_FIELDS = 'names,emailAddresses,phoneNumbers,organizations,addresses,photos,memberships';
10
9
  // people.getBatchGet rejects more than 200 resource names per call.
11
10
  const BATCH_GET_LIMIT = 200;
@@ -35,7 +34,7 @@ function formatContact(person) {
35
34
  photo: person.photos?.[0]?.url ?? '',
36
35
  };
37
36
  }
38
- /** Accent-fold + lowercase for name comparison ("Rym" == "Rÿm"). */
37
+ /** Accent-fold + lowercase for name comparison ("Zoe" == "Zoë"). */
39
38
  const foldName = (s) => s.normalize('NFD').replace(/\p{Diacritic}/gu, '').toLowerCase().trim();
40
39
  /** Canonical-address preference within one contact: explicit primary, then
41
40
  * People type order work > home > other. */
@@ -53,7 +52,7 @@ function pickEmail(c) {
53
52
  return [...c.emails].sort((a, b) => emailRank(b) - emailRank(a))[0];
54
53
  }
55
54
  /** Deterministic rank tuple; higher wins, compared lexicographically so the
56
- * first discriminating rule decides (A10 tie-break table). */
55
+ * first discriminating rule decides. */
57
56
  function scoreTuple(c, foldedQuery) {
58
57
  const exact = foldName(c.displayName) === foldedQuery ? 1 : 0; // 1. exact over prefix
59
58
  const saved = c.source === 'contacts' ? 1 : 0; // 2. saved over other-contact
@@ -123,7 +122,11 @@ function dedupeCandidates(list) {
123
122
  }
124
123
  return [...byResource.values()];
125
124
  }
126
- export function registerContactsTools(server) {
125
+ export function registerContactsTools(server, deps = {}) {
126
+ // Per-registry, live account enum + injectable client: the schema follows
127
+ // the registry's account view at parse time, not a process-global default.
128
+ const accountEnum = accountArgLive(() => server.accountAliases()).optional();
129
+ const getClientFn = deps.getClientFn ?? getClient;
127
130
  server.registerTool('contacts_search', {
128
131
  description: 'Search contacts by name, email, phone, or organization',
129
132
  inputSchema: {
@@ -134,7 +137,7 @@ export function registerContactsTools(server) {
134
137
  },
135
138
  }, async ({ account, query, pageSize }) => {
136
139
  try {
137
- const auth = await getClient(account);
140
+ const auth = await getClientFn(account);
138
141
  const people = peopleClient({ version: 'v1', auth });
139
142
  // Warmup request required by the People API
140
143
  await people.people.searchContacts({
@@ -166,7 +169,7 @@ export function registerContactsTools(server) {
166
169
  description: 'Resolve a free-text name/alias to ONE canonical email address (ranked + tie-broken), so you need not hand-expand transliteration OR-queries. Returns a single confident match, an explicit ambiguous candidate list, or null when nothing matches. For picking a recipient before gmail_send.',
167
170
  inputSchema: {
168
171
  account: accountEnum.describe('Google account alias'),
169
- name: z.string().min(1).describe('Free-text name or alias to resolve (e.g. "Rym")'),
172
+ name: z.string().min(1).describe('Free-text name or alias to resolve (e.g. "Zoe")'),
170
173
  maxCandidates: z.number().min(1).max(10).default(5).optional()
171
174
  .describe('Max candidates to return when ambiguous (1-10, default: 5)'),
172
175
  includeOtherContacts: coerceBoolean.optional()
@@ -174,7 +177,7 @@ export function registerContactsTools(server) {
174
177
  },
175
178
  }, async ({ account, name, maxCandidates, includeOtherContacts }) => {
176
179
  try {
177
- const auth = await getClient(account);
180
+ const auth = await getClientFn(account);
178
181
  const people = peopleClient({ version: 'v1', auth });
179
182
  // Warmup request required by the People API before searchContacts returns hits.
180
183
  await people.people.searchContacts({ query: '', readMask: 'names' });
@@ -193,7 +196,7 @@ export function registerContactsTools(server) {
193
196
  candidates.push(...(other.data.results ?? []).map((r) => toCandidate(r.person, 'otherContacts')));
194
197
  }
195
198
  catch {
196
- // otherContacts unavailable (scope/5xx): degrade to saved-contacts only.
199
+ // otherContacts unavailable: degrade silently
197
200
  }
198
201
  }
199
202
  const result = resolveContacts(dedupeCandidates(candidates), name, maxCandidates ?? 5);
@@ -213,7 +216,7 @@ export function registerContactsTools(server) {
213
216
  },
214
217
  }, async ({ account, resourceName }) => {
215
218
  try {
216
- const auth = await getClient(account);
219
+ const auth = await getClientFn(account);
217
220
  const people = peopleClient({ version: 'v1', auth });
218
221
  const res = await people.people.get({
219
222
  resourceName,
@@ -244,7 +247,7 @@ export function registerContactsTools(server) {
244
247
  },
245
248
  }, async ({ account, pageSize, pageToken, sortOrder }) => {
246
249
  try {
247
- const auth = await getClient(account);
250
+ const auth = await getClientFn(account);
248
251
  const people = peopleClient({ version: 'v1', auth });
249
252
  const res = await people.people.connections.list({
250
253
  resourceName: 'people/me',
@@ -283,7 +286,7 @@ export function registerContactsTools(server) {
283
286
  },
284
287
  }, async ({ account, givenName, familyName, email, emailType, phone, phoneType, organization, jobTitle }) => {
285
288
  try {
286
- const auth = await getClient(account);
289
+ const auth = await getClientFn(account);
287
290
  const people = peopleClient({ version: 'v1', auth });
288
291
  const requestBody = {
289
292
  names: [{ givenName, familyName: familyName ?? '' }],
@@ -330,7 +333,7 @@ export function registerContactsTools(server) {
330
333
  },
331
334
  }, async ({ account, resourceName, givenName, familyName, email, emailType, phone, phoneType, organization, jobTitle }) => {
332
335
  try {
333
- const auth = await getClient(account);
336
+ const auth = await getClientFn(account);
334
337
  const people = peopleClient({ version: 'v1', auth });
335
338
  // Fetch current contact to get etag
336
339
  const current = await people.people.get({
@@ -387,7 +390,7 @@ export function registerContactsTools(server) {
387
390
  },
388
391
  }, async ({ account, resourceName }) => {
389
392
  try {
390
- const auth = await getClient(account);
393
+ const auth = await getClientFn(account);
391
394
  const people = peopleClient({ version: 'v1', auth });
392
395
  await people.people.deleteContact({ resourceName });
393
396
  return {
@@ -409,7 +412,7 @@ export function registerContactsTools(server) {
409
412
  },
410
413
  }, async ({ account, pageSize }) => {
411
414
  try {
412
- const auth = await getClient(account);
415
+ const auth = await getClientFn(account);
413
416
  const people = peopleClient({ version: 'v1', auth });
414
417
  const res = await people.contactGroups.list({
415
418
  pageSize: pageSize ?? 100,
@@ -439,7 +442,7 @@ export function registerContactsTools(server) {
439
442
  },
440
443
  }, async ({ account, groupResourceName, maxMembers }) => {
441
444
  try {
442
- const auth = await getClient(account);
445
+ const auth = await getClientFn(account);
443
446
  const people = peopleClient({ version: 'v1', auth });
444
447
  const groupRes = await people.contactGroups.get({
445
448
  resourceName: groupResourceName,
@@ -449,8 +452,8 @@ export function registerContactsTools(server) {
449
452
  const members = [];
450
453
  let fetchFailures = 0;
451
454
  let firstError;
452
- // getBatchGet caps at 200 names while maxMembers goes to 1000, so a
453
- // large group used to fail outright rather than come back in pages.
455
+ // getBatchGet caps at 200 names while maxMembers goes to 1000, so fetch
456
+ // in chunks rather than fail outright on a large group.
454
457
  for (let i = 0; i < memberResourceNames.length; i += BATCH_GET_LIMIT) {
455
458
  const chunk = memberResourceNames.slice(i, i + BATCH_GET_LIMIT);
456
459
  try {
@@ -503,7 +506,7 @@ export function registerContactsTools(server) {
503
506
  },
504
507
  }, async ({ account, name }) => {
505
508
  try {
506
- const auth = await getClient(account);
509
+ const auth = await getClientFn(account);
507
510
  const people = peopleClient({ version: 'v1', auth });
508
511
  const res = await people.contactGroups.create({
509
512
  requestBody: {
@@ -1,4 +1,5 @@
1
1
  import type { ToolRegistry } from '../registry.js';
2
+ import { type CuratedToolDeps } from '../client.js';
2
3
  export declare const DOCS_INSERT_CHUNK = 5000;
3
4
  export declare function splitInsertText(text: string, maxLen?: number): string[];
4
5
  export declare function buildInsertRequests(text: string, index?: number): any[];
@@ -24,7 +25,7 @@ export declare function listTabs(tabs: any[] | undefined): {
24
25
  tabId: string;
25
26
  title: string;
26
27
  }[];
27
- export declare function registerDocsTools(server: ToolRegistry): void;
28
+ export declare function registerDocsTools(server: ToolRegistry, deps?: CuratedToolDeps): void;
28
29
  export declare function buildParagraphStyle(input: {
29
30
  namedStyleType?: string;
30
31
  alignment?: string;
@@ -1,17 +1,12 @@
1
1
  import { z } from 'zod';
2
2
  import { coerceBoolean, coerceJson } from './_coerce.js';
3
3
  import { docs as docsClient } from '@googleapis/docs';
4
- import { accountAliasSchema } from '../accounts.js';
4
+ import { accountArgLive } from '../accounts.js';
5
5
  import { getClient } from '../client.js';
6
6
  import { handleGoogleApiError, invalidParams } from './_errors.js';
7
7
  import { capText } from '../trim.js';
8
- const accountEnum = accountAliasSchema.optional();
9
- // A single very large insertText has been observed to apply only partially while
10
- // the request still returns success, silently dropping the tail. Split a large
11
- // insert into bounded pieces sent as sequential insertText requests in one atomic
12
- // batchUpdate: small inserts keep the exact single-request path, and a large one
13
- // can no longer be silently truncated. Docs indices are UTF-16 code units (= JS
14
- // string length), so index math advances by `.length`; never split a surrogate pair.
8
+ // A single large insertText can silently apply only partially despite returning
9
+ // success; split into <=DOCS_INSERT_CHUNK sequential inserts in one atomic batchUpdate (UTF-16-safe, no split surrogate pairs).
15
10
  export const DOCS_INSERT_CHUNK = 5000;
16
11
  export function splitInsertText(text, maxLen = DOCS_INSERT_CHUNK) {
17
12
  if (text.length === 0)
@@ -170,7 +165,11 @@ export function listTabs(tabs) {
170
165
  }
171
166
  return out;
172
167
  }
173
- export function registerDocsTools(server) {
168
+ export function registerDocsTools(server, deps = {}) {
169
+ // Per-registry, live account enum + injectable client: the schema follows
170
+ // the registry's account view at parse time, not a process-global default.
171
+ const accountEnum = accountArgLive(() => server.accountAliases()).optional();
172
+ const getClientFn = deps.getClientFn ?? getClient;
174
173
  server.registerTool('docs_create', {
175
174
  description: 'Create a new Google Docs document',
176
175
  inputSchema: {
@@ -179,7 +178,7 @@ export function registerDocsTools(server) {
179
178
  },
180
179
  }, async ({ account, title }) => {
181
180
  try {
182
- const auth = await getClient(account);
181
+ const auth = await getClientFn(account);
183
182
  const docs = docsClient({ version: 'v1', auth });
184
183
  const res = await docs.documents.create({
185
184
  requestBody: { title },
@@ -211,7 +210,7 @@ export function registerDocsTools(server) {
211
210
  },
212
211
  }, async ({ account, documentId, includeTabsContent, suggestionsViewMode }) => {
213
212
  try {
214
- const auth = await getClient(account);
213
+ const auth = await getClientFn(account);
215
214
  const docs = docsClient({ version: 'v1', auth });
216
215
  const res = await docs.documents.get({
217
216
  documentId,
@@ -253,7 +252,7 @@ export function registerDocsTools(server) {
253
252
  if (heading !== undefined && headingIndex !== undefined) {
254
253
  return invalidParams(account, 'Both heading and headingIndex were supplied, and they address different sections.', 'Pass heading to look a section up by its text, or headingIndex to address one by position. Not both.');
255
254
  }
256
- const auth = await getClient(account);
255
+ const auth = await getClientFn(account);
257
256
  const docs = docsClient({ version: 'v1', auth });
258
257
  const res = await docs.documents.get({
259
258
  documentId,
@@ -352,7 +351,7 @@ export function registerDocsTools(server) {
352
351
  },
353
352
  }, async ({ account, documentId, text, index }) => {
354
353
  try {
355
- const auth = await getClient(account);
354
+ const auth = await getClientFn(account);
356
355
  const docs = docsClient({ version: 'v1', auth });
357
356
  const requests = buildInsertRequests(text, index);
358
357
  if (requests.length === 0) {
@@ -392,7 +391,7 @@ export function registerDocsTools(server) {
392
391
  },
393
392
  }, async ({ account, documentId, findText, replaceText, matchCase }) => {
394
393
  try {
395
- const auth = await getClient(account);
394
+ const auth = await getClientFn(account);
396
395
  const docs = docsClient({ version: 'v1', auth });
397
396
  const res = await docs.documents.batchUpdate({
398
397
  documentId,
@@ -427,7 +426,7 @@ export function registerDocsTools(server) {
427
426
  },
428
427
  }, async ({ account, documentId, startIndex, endIndex }) => {
429
428
  try {
430
- const auth = await getClient(account);
429
+ const auth = await getClientFn(account);
431
430
  const docs = docsClient({ version: 'v1', auth });
432
431
  const res = await docs.documents.batchUpdate({
433
432
  documentId,
@@ -465,7 +464,7 @@ export function registerDocsTools(server) {
465
464
  },
466
465
  }, async ({ account, documentId, startIndex, endIndex, bold, italic, underline, fontSize, fontFamily }) => {
467
466
  try {
468
- const auth = await getClient(account);
467
+ const auth = await getClientFn(account);
469
468
  const docs = docsClient({ version: 'v1', auth });
470
469
  const textStyle = {};
471
470
  const fields = [];
@@ -528,7 +527,7 @@ export function registerDocsTools(server) {
528
527
  },
529
528
  }, async ({ account, documentId, rows, columns, index }) => {
530
529
  try {
531
- const auth = await getClient(account);
530
+ const auth = await getClientFn(account);
532
531
  const docs = docsClient({ version: 'v1', auth });
533
532
  const request = { insertTable: { rows, columns } };
534
533
  if (index !== undefined) {
@@ -564,7 +563,7 @@ export function registerDocsTools(server) {
564
563
  },
565
564
  }, async ({ account, documentId, name, startIndex, endIndex }) => {
566
565
  try {
567
- const auth = await getClient(account);
566
+ const auth = await getClientFn(account);
568
567
  const docs = docsClient({ version: 'v1', auth });
569
568
  const res = await docs.documents.batchUpdate({
570
569
  documentId,
@@ -599,7 +598,7 @@ export function registerDocsTools(server) {
599
598
  if (!namedRangeId && !name) {
600
599
  return invalidParams(account, 'Neither namedRangeId nor name was supplied, so no named range is addressed.', 'Pass namedRangeId to delete one specific range, or name to delete every range carrying that name.');
601
600
  }
602
- const auth = await getClient(account);
601
+ const auth = await getClientFn(account);
603
602
  const docs = docsClient({ version: 'v1', auth });
604
603
  const req = {};
605
604
  if (namedRangeId)
@@ -632,7 +631,7 @@ export function registerDocsTools(server) {
632
631
  if (!namedRangeId && !namedRangeName) {
633
632
  return invalidParams(account, 'Neither namedRangeId nor namedRangeName was supplied, so no named range is addressed.', 'Pass namedRangeId to replace one specific range, or namedRangeName to replace every range carrying that name.');
634
633
  }
635
- const auth = await getClient(account);
634
+ const auth = await getClientFn(account);
636
635
  const docs = docsClient({ version: 'v1', auth });
637
636
  const req = { text };
638
637
  if (namedRangeId)
@@ -677,7 +676,7 @@ export function registerDocsTools(server) {
677
676
  },
678
677
  }, async ({ account, documentId, startIndex, endIndex, ...style }) => {
679
678
  try {
680
- const auth = await getClient(account);
679
+ const auth = await getClientFn(account);
681
680
  const docs = docsClient({ version: 'v1', auth });
682
681
  const built = buildParagraphStyle(style);
683
682
  if (built.fields.length === 0) {
@@ -723,7 +722,7 @@ export function registerDocsTools(server) {
723
722
  },
724
723
  }, async ({ account, documentId, ...style }) => {
725
724
  try {
726
- const auth = await getClient(account);
725
+ const auth = await getClientFn(account);
727
726
  const docs = docsClient({ version: 'v1', auth });
728
727
  const built = buildDocumentStyle(style);
729
728
  if (built.fields.length === 0) {
@@ -776,7 +775,7 @@ export function registerDocsTools(server) {
776
775
  },
777
776
  }, async ({ account, documentId, startIndex, endIndex, bulletPreset }) => {
778
777
  try {
779
- const auth = await getClient(account);
778
+ const auth = await getClientFn(account);
780
779
  const docs = docsClient({ version: 'v1', auth });
781
780
  await docs.documents.batchUpdate({
782
781
  documentId,
@@ -807,7 +806,7 @@ export function registerDocsTools(server) {
807
806
  },
808
807
  }, async ({ account, documentId, startIndex, endIndex }) => {
809
808
  try {
810
- const auth = await getClient(account);
809
+ const auth = await getClientFn(account);
811
810
  const docs = docsClient({ version: 'v1', auth });
812
811
  await docs.documents.batchUpdate({
813
812
  documentId,
@@ -840,7 +839,7 @@ export function registerDocsTools(server) {
840
839
  },
841
840
  }, async ({ account, documentId, uri, index, width, height }) => {
842
841
  try {
843
- const auth = await getClient(account);
842
+ const auth = await getClientFn(account);
844
843
  const docs = docsClient({ version: 'v1', auth });
845
844
  const request = { uri };
846
845
  if (index !== undefined)
@@ -876,7 +875,7 @@ export function registerDocsTools(server) {
876
875
  },
877
876
  }, async ({ account, documentId, index }) => {
878
877
  try {
879
- const auth = await getClient(account);
878
+ const auth = await getClientFn(account);
880
879
  const docs = docsClient({ version: 'v1', auth });
881
880
  const request = {};
882
881
  if (index !== undefined)
@@ -905,7 +904,7 @@ export function registerDocsTools(server) {
905
904
  },
906
905
  }, async ({ account, documentId, index, sectionType }) => {
907
906
  try {
908
- const auth = await getClient(account);
907
+ const auth = await getClientFn(account);
909
908
  const docs = docsClient({ version: 'v1', auth });
910
909
  const request = { sectionType: sectionType ?? 'NEXT_PAGE' };
911
910
  if (index !== undefined)
@@ -934,7 +933,7 @@ export function registerDocsTools(server) {
934
933
  },
935
934
  }, async ({ account, documentId, sectionBreakIndex }) => {
936
935
  try {
937
- const auth = await getClient(account);
936
+ const auth = await getClientFn(account);
938
937
  const docs = docsClient({ version: 'v1', auth });
939
938
  const request = { type: 'DEFAULT' };
940
939
  if (sectionBreakIndex !== undefined)
@@ -961,7 +960,7 @@ export function registerDocsTools(server) {
961
960
  },
962
961
  }, async ({ account, documentId, headerId }) => {
963
962
  try {
964
- const auth = await getClient(account);
963
+ const auth = await getClientFn(account);
965
964
  const docs = docsClient({ version: 'v1', auth });
966
965
  await docs.documents.batchUpdate({
967
966
  documentId,
@@ -984,7 +983,7 @@ export function registerDocsTools(server) {
984
983
  },
985
984
  }, async ({ account, documentId, sectionBreakIndex }) => {
986
985
  try {
987
- const auth = await getClient(account);
986
+ const auth = await getClientFn(account);
988
987
  const docs = docsClient({ version: 'v1', auth });
989
988
  const request = { type: 'DEFAULT' };
990
989
  if (sectionBreakIndex !== undefined)
@@ -1011,7 +1010,7 @@ export function registerDocsTools(server) {
1011
1010
  },
1012
1011
  }, async ({ account, documentId, footerId }) => {
1013
1012
  try {
1014
- const auth = await getClient(account);
1013
+ const auth = await getClientFn(account);
1015
1014
  const docs = docsClient({ version: 'v1', auth });
1016
1015
  await docs.documents.batchUpdate({
1017
1016
  documentId,
@@ -1044,7 +1043,7 @@ export function registerDocsTools(server) {
1044
1043
  },
1045
1044
  }, async ({ account, documentId, operation, tableStartIndex, rowIndex, columnIndex, insertBelow, insertRight, rowSpan, columnSpan }) => {
1046
1045
  try {
1047
- const auth = await getClient(account);
1046
+ const auth = await getClientFn(account);
1048
1047
  const docs = docsClient({ version: 'v1', auth });
1049
1048
  const cellLoc = {
1050
1049
  tableStartLocation: { index: tableStartIndex },
@@ -1112,7 +1111,7 @@ export function registerDocsTools(server) {
1112
1111
  },
1113
1112
  }, async ({ account, documentId, title, parentTabId, index }) => {
1114
1113
  try {
1115
- const auth = await getClient(account);
1114
+ const auth = await getClientFn(account);
1116
1115
  const docs = docsClient({ version: 'v1', auth });
1117
1116
  const tabProperties = { title };
1118
1117
  if (parentTabId)
@@ -1141,7 +1140,7 @@ export function registerDocsTools(server) {
1141
1140
  },
1142
1141
  }, async ({ account, documentId, tabId }) => {
1143
1142
  try {
1144
- const auth = await getClient(account);
1143
+ const auth = await getClientFn(account);
1145
1144
  const docs = docsClient({ version: 'v1', auth });
1146
1145
  await docs.documents.batchUpdate({
1147
1146
  documentId,
@@ -1167,7 +1166,7 @@ export function registerDocsTools(server) {
1167
1166
  },
1168
1167
  }, async ({ account, documentId, tabId, title, index, parentTabId }) => {
1169
1168
  try {
1170
- const auth = await getClient(account);
1169
+ const auth = await getClientFn(account);
1171
1170
  const docs = docsClient({ version: 'v1', auth });
1172
1171
  const tabProperties = { tabId };
1173
1172
  const fields = [];
@@ -1221,7 +1220,7 @@ export function registerDocsTools(server) {
1221
1220
  },
1222
1221
  }, async ({ account, documentId, requests, writeControl }) => {
1223
1222
  try {
1224
- const auth = await getClient(account);
1223
+ const auth = await getClientFn(account);
1225
1224
  const docs = docsClient({ version: 'v1', auth });
1226
1225
  const res = await docs.documents.batchUpdate({
1227
1226
  documentId,