mcp-google-multi 6.0.0-alpha.10 → 6.0.0-alpha.11

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.
@@ -1,13 +1,19 @@
1
1
  import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
2
2
  import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
3
3
  export declare function argNormalizationEnabled(env?: NodeJS.ProcessEnv): boolean;
4
- export declare function normalizeCallArguments(shapeKeys: ReadonlySet<string>, args: Record<string, unknown>): {
4
+ /** Declared scalar kind per schema key; drives value coercion on RENAMED keys
5
+ * only. Clients string-encode values for keys absent from the advertised
6
+ * schema, so a renamed key almost always arrives as a string — without
7
+ * coercion the rename would just move the -32602 from the key to the value. */
8
+ export type ArgKind = 'number' | 'boolean' | 'other';
9
+ export type ArgShape = ReadonlyMap<string, ArgKind>;
10
+ export declare function normalizeCallArguments(shape: ArgShape, args: Record<string, unknown>): {
5
11
  args: Record<string, unknown>;
6
12
  renamed: [string, string][];
7
13
  };
8
- export declare function normalizeMessage(msg: JSONRPCMessage, shapeFor: (tool: string) => ReadonlySet<string> | undefined, log?: (line: string) => void): JSONRPCMessage;
14
+ export declare function normalizeMessage(msg: JSONRPCMessage, shapeFor: (tool: string) => ArgShape | undefined, log?: (line: string) => void): JSONRPCMessage;
9
15
  /** Wrap a server-side transport so tools/call argument keys are normalized
10
16
  * before the SDK validates them. The Protocol assigns `onmessage` during
11
17
  * connect(); the interceptor lives in that setter, so the wrapper works
12
18
  * identically for stdio and (per-request, stateless) HTTP transports. */
13
- export declare function withArgNormalization(transport: Transport, shapeFor: (tool: string) => ReadonlySet<string> | undefined, log?: (line: string) => void): Transport;
19
+ export declare function withArgNormalization(transport: Transport, shapeFor: (tool: string) => ArgShape | undefined, log?: (line: string) => void): Transport;
@@ -10,16 +10,26 @@ export function argNormalizationEnabled(env = process.env) {
10
10
  return !/^(0|false|off|no)$/i.test((env.GOOGLE_ARG_NORMALIZE ?? '').trim());
11
11
  }
12
12
  const snakeToCamel = (key) => key.replace(/_([a-z0-9])/g, (_m, c) => c.toUpperCase());
13
- export function normalizeCallArguments(shapeKeys, args) {
13
+ function coerceRenamedValue(value, kind) {
14
+ if (typeof value !== 'string')
15
+ return value;
16
+ const v = value.trim();
17
+ if (kind === 'number' && /^-?\d+(\.\d+)?$/.test(v))
18
+ return Number(v);
19
+ if (kind === 'boolean' && /^(true|false)$/i.test(v))
20
+ return v.toLowerCase() === 'true';
21
+ return value;
22
+ }
23
+ export function normalizeCallArguments(shape, args) {
14
24
  const renamed = [];
15
25
  let out;
16
26
  for (const key of Object.keys(args)) {
17
- if (shapeKeys.has(key) || !key.includes('_'))
27
+ if (shape.has(key) || !key.includes('_'))
18
28
  continue;
19
29
  const camel = snakeToCamel(key);
20
- if (camel !== key && shapeKeys.has(camel) && !(camel in args)) {
30
+ if (camel !== key && shape.has(camel) && !(camel in args)) {
21
31
  out ??= { ...args };
22
- out[camel] = out[key];
32
+ out[camel] = coerceRenamedValue(out[key], shape.get(camel));
23
33
  delete out[key];
24
34
  renamed.push([key, camel]);
25
35
  }
@@ -1,6 +1,7 @@
1
1
  import { type IncomingMessage, type ServerResponse } from 'node:http';
2
2
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import type { HttpConfig } from './http-config.js';
4
+ import { type ArgShape } from './arg-normalize.js';
4
5
  export type AuthOutcome = {
5
6
  ok: true;
6
7
  } | {
@@ -29,7 +30,7 @@ export interface HttpHostOptions {
29
30
  * shared lock instead of wedging the transport (default 120s). */
30
31
  dispatchTimeoutMs?: number;
31
32
  /** tools/call argument-key normalization lookup (arg-normalize.ts); absent = off. */
32
- argShapeFor?: (tool: string) => ReadonlySet<string> | undefined;
33
+ argShapeFor?: (tool: string) => ArgShape | undefined;
33
34
  }
34
35
  export declare function parseOwnerEmails(env?: NodeJS.ProcessEnv): string[];
35
36
  /** Front guard: an Origin, if present, must be allowlisted; a Host must be
@@ -1,6 +1,7 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { z } from 'zod';
3
3
  import { type Policy } from './write-control.js';
4
+ import type { ArgShape } from './arg-normalize.js';
4
5
  export type Cud = 'read' | 'create' | 'update' | 'delete';
5
6
  export type DiscoveryMode = 'lazy' | 'curated' | 'eager';
6
7
  export declare function resolveDiscoveryMode(env?: NodeJS.ProcessEnv): DiscoveryMode;
@@ -49,8 +50,9 @@ export declare class ToolRegistry {
49
50
  constructor(server: McpServer, policy: Policy, mode?: DiscoveryMode);
50
51
  registerMeta: McpServer['registerTool'];
51
52
  services(): string[];
52
- /** Declared input-schema keys for one tool (tools/call arg normalization). */
53
- argShape(name: string): ReadonlySet<string> | undefined;
53
+ /** Declared input-schema keys + scalar kinds for one tool (tools/call arg
54
+ * normalization; the kind drives value coercion on renamed keys). */
55
+ argShape(name: string): ArgShape | undefined;
54
56
  catalog(service: string, query?: string): CatalogOperation[];
55
57
  reveal(service: string): boolean;
56
58
  /** discover_all: advertise the full curated set at once. Idempotent. */
package/dist/registry.js CHANGED
@@ -38,6 +38,23 @@ const SERVICE_OVERRIDES = {
38
38
  };
39
39
  // read tools that write local files — same savePath fanned across accounts would clobber
40
40
  const FANOUT_EXCLUDE = new Set(['gmail_download_attachment', 'drive_download', 'drive_export']);
41
+ /** Unwrap optional/default/nullable to the declared scalar kind (zod 4 defs). */
42
+ function scalarKindOf(field) {
43
+ let cur = field;
44
+ for (let i = 0; i < 4 && cur?._zod?.def; i++) {
45
+ const def = cur._zod.def;
46
+ if (def.type === 'number')
47
+ return 'number';
48
+ if (def.type === 'boolean')
49
+ return 'boolean';
50
+ if (def.type === 'optional' || def.type === 'default' || def.type === 'nullable') {
51
+ cur = def.innerType;
52
+ continue;
53
+ }
54
+ return 'other';
55
+ }
56
+ return 'other';
57
+ }
41
58
  function isAccountEnum(field) {
42
59
  const def = field?._zod?.def;
43
60
  if (!def)
@@ -193,7 +210,8 @@ export class ToolRegistry {
193
210
  services() {
194
211
  return [...new Set(this.tools.filter((t) => !t.meta).map((t) => t.service))];
195
212
  }
196
- /** Declared input-schema keys for one tool (tools/call arg normalization). */
213
+ /** Declared input-schema keys + scalar kinds for one tool (tools/call arg
214
+ * normalization; the kind drives value coercion on renamed keys). */
197
215
  argShape(name) {
198
216
  const cached = this.argShapeCache.get(name);
199
217
  if (cached)
@@ -201,9 +219,11 @@ export class ToolRegistry {
201
219
  const entry = this.tools.find((t) => t.name === name);
202
220
  if (!entry)
203
221
  return undefined;
204
- const keys = new Set(Object.keys(entry.inputShape));
205
- this.argShapeCache.set(name, keys);
206
- return keys;
222
+ const shape = new Map();
223
+ for (const [key, field] of Object.entries(entry.inputShape))
224
+ shape.set(key, scalarKindOf(field));
225
+ this.argShapeCache.set(name, shape);
226
+ return shape;
207
227
  }
208
228
  catalog(service, query) {
209
229
  const q = query?.trim().toLowerCase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-google-multi",
3
- "version": "6.0.0-alpha.10",
3
+ "version": "6.0.0-alpha.11",
4
4
  "description": "Local MCP server for Google Workspace (Gmail, Drive, Calendar, Sheets, Docs, Contacts, Tasks, Meet, Search Console, +Forms/Chat/Admin) across multiple accounts — OAuth-only, encrypted token storage, deny-by-default writes.",
5
5
  "type": "module",
6
6
  "license": "MIT",