mcp-google-multi 6.0.0-alpha.31 → 6.0.0-alpha.32

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.
@@ -27,6 +27,16 @@ export type AddValidation = {
27
27
  /** Validate the collected form against the alias rules, dup check, and the
28
28
  * bundle catalog. Pure (no I/O) for unit testing. */
29
29
  export declare function validateAddForm(input: Partial<AddForm>, existingAliases: string[]): AddValidation;
30
+ /** URL-mode elicitation params. `elicitationId` is REQUIRED by the spec
31
+ * schema; omitting it made every url-mode request fail client-side
32
+ * validation, so the branch silently never worked. Pure, so the shape is
33
+ * testable against the SDK's own schema without a live client. */
34
+ export declare function urlElicitationParams(alias: string, url: string, elicitationId: string): {
35
+ mode: 'url';
36
+ elicitationId: string;
37
+ message: string;
38
+ url: string;
39
+ };
30
40
  /** Map direct tool arguments onto the elicitation form shape: the argument-
31
41
  * mode fallback for clients without form elicitation. All bundle picks travel
32
42
  * through otherBundles, which validateAddForm resolves and validates. Pure. */
@@ -71,6 +71,18 @@ export function validateAddForm(input, existingAliases) {
71
71
  }
72
72
  return { ok: true, alias, email, bundles, admin: input.admin === true };
73
73
  }
74
+ /** URL-mode elicitation params. `elicitationId` is REQUIRED by the spec
75
+ * schema; omitting it made every url-mode request fail client-side
76
+ * validation, so the branch silently never worked. Pure, so the shape is
77
+ * testable against the SDK's own schema without a live client. */
78
+ export function urlElicitationParams(alias, url, elicitationId) {
79
+ return {
80
+ mode: 'url',
81
+ elicitationId,
82
+ message: `Authorize the "${alias}" Google account in your browser.`,
83
+ url,
84
+ };
85
+ }
74
86
  /** Map direct tool arguments onto the elicitation form shape: the argument-
75
87
  * mode fallback for clients without form elicitation. All bundle picks travel
76
88
  * through otherBundles, which validateAddForm resolves and validates. Pure. */
@@ -125,11 +137,16 @@ async function runConsent(server, alias) {
125
137
  const scopes = resolveScopesForAccount(alias);
126
138
  const url = client.generateAuthUrl({ access_type: 'offline', prompt: 'consent', scope: scopes, login_hint: cfg.email, state: expectedState });
127
139
  const consent = loop.finish(client, expectedState);
140
+ // Capability probe: the spec advertises each elicitation mode as a PRESENT
141
+ // object, not a boolean, so test presence rather than truthiness.
128
142
  const caps = server.server.getClientCapabilities?.();
129
143
  let opened = false;
130
- if (caps?.elicitation?.url) {
144
+ if (caps?.elicitation?.url !== undefined) {
131
145
  try {
132
- const r = await server.server.elicitInput({ mode: 'url', message: `Authorize the "${alias}" Google account in your browser.`, url });
146
+ // elicitationId is REQUIRED by the spec schema; omitting it made every
147
+ // url-mode request fail client-side validation, so this branch always
148
+ // fell through to the server-side browser open.
149
+ const r = await server.server.elicitInput(urlElicitationParams(alias, url, randomBytes(16).toString('hex')));
133
150
  if (r.action !== 'accept') {
134
151
  loop.close();
135
152
  return { ok: false, text: 'confirmation_declined: consent was cancelled; the account row was kept but no token was stored (doctor will show it as "missing").' };
@@ -148,7 +165,9 @@ async function runConsent(server, alias) {
148
165
  tokens = await consent;
149
166
  }
150
167
  catch (e) {
151
- return { ok: false, text: `${e.message}${opened ? '' : `\nOpen this URL to authorize:\n${url}`}` };
168
+ // Always surface the URL: the browser hand-off can succeed and consent
169
+ // still fail (declined, timed out), and the URL is the only recovery.
170
+ return { ok: false, text: `${e.message}\nOpen this URL to authorize:\n${url}` };
152
171
  }
153
172
  writeToken(alias, tokens);
154
173
  return { ok: true, missing: scopeGrantDiff(scopes, typeof tokens.scope === 'string' ? tokens.scope : undefined) };
@@ -196,15 +215,16 @@ export function registerAccountWizardTools(registry, server) {
196
215
  input = argsToAddForm(a);
197
216
  }
198
217
  else {
218
+ // Modes are advertised as PRESENT objects, not booleans.
199
219
  const caps = server.server.getClientCapabilities?.();
200
- if (!caps?.elicitation?.form) {
220
+ if (caps?.elicitation?.form === undefined) {
201
221
  return textResult('E_NO_FORM_ELICITATION: this client does not support the interactive form. Call account_add again with arguments instead, e.g. {"alias": "work", "email": "you@example.com"} (optional: "bundles" as a comma-separated list, "allBundles": true, "admin": true).', true);
202
222
  }
203
223
  const form = await server.server.elicitInput({ message: 'Add a Google account', requestedSchema: addFormSchema() });
204
224
  if (form.action !== 'accept') {
205
225
  return textResult('confirmation_declined: no account was added.');
206
226
  }
207
- input = form.content ?? {};
227
+ input = (form.content ?? {});
208
228
  }
209
229
  const validated = validateAddForm(input, getAccountSet().aliases);
210
230
  if (!validated.ok)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-google-multi",
3
- "version": "6.0.0-alpha.31",
3
+ "version": "6.0.0-alpha.32",
4
4
  "mcpName": "io.github.bakissation/mcp-google-multi",
5
5
  "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.",
6
6
  "type": "module",