mcp-google-multi 5.4.1-alpha.2 → 6.0.0-alpha.2

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 (116) hide show
  1. package/README.md +21 -16
  2. package/dist/accounts.d.ts +44 -5
  3. package/dist/accounts.js +267 -25
  4. package/dist/auth.d.ts +11 -4
  5. package/dist/auth.js +85 -78
  6. package/dist/client-config.d.ts +90 -0
  7. package/dist/client-config.js +282 -0
  8. package/dist/client.js +10 -4
  9. package/dist/config-file.d.ts +38 -0
  10. package/dist/config-file.js +117 -0
  11. package/dist/discover.js +57 -3
  12. package/dist/doctor.d.ts +86 -0
  13. package/dist/doctor.js +384 -0
  14. package/dist/env-load.d.ts +11 -0
  15. package/dist/env-load.js +52 -0
  16. package/dist/executor.d.ts +2 -0
  17. package/dist/executor.js +2 -1
  18. package/dist/fanout.js +8 -5
  19. package/dist/fs-atomic.d.ts +3 -0
  20. package/dist/fs-atomic.js +123 -0
  21. package/dist/http-config.d.ts +35 -0
  22. package/dist/http-config.js +113 -0
  23. package/dist/http-transport.d.ts +59 -0
  24. package/dist/http-transport.js +269 -0
  25. package/dist/identity.d.ts +24 -0
  26. package/dist/identity.js +19 -0
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +199 -18
  29. package/dist/master-key.d.ts +53 -0
  30. package/dist/master-key.js +242 -0
  31. package/dist/mcp-token.d.ts +81 -0
  32. package/dist/mcp-token.js +176 -0
  33. package/dist/migrate-config.d.ts +7 -0
  34. package/dist/migrate-config.js +103 -0
  35. package/dist/migrate-tokens.js +4 -3
  36. package/dist/net-tuning.d.ts +8 -0
  37. package/dist/net-tuning.js +25 -0
  38. package/dist/oauth-as.d.ts +52 -0
  39. package/dist/oauth-as.js +457 -0
  40. package/dist/oauth-consent.d.ts +28 -0
  41. package/dist/oauth-consent.js +109 -0
  42. package/dist/open-url.d.ts +1 -0
  43. package/dist/open-url.js +30 -0
  44. package/dist/reauth-hint.d.ts +2 -0
  45. package/dist/reauth-hint.js +15 -0
  46. package/dist/registry-transfer.d.ts +41 -0
  47. package/dist/registry-transfer.js +219 -0
  48. package/dist/registry.d.ts +27 -1
  49. package/dist/registry.js +117 -6
  50. package/dist/scope-catalog.d.ts +19 -0
  51. package/dist/scope-catalog.js +182 -0
  52. package/dist/scope-observability.d.ts +78 -0
  53. package/dist/scope-observability.js +192 -0
  54. package/dist/services.js +1 -1
  55. package/dist/setup-prompt.d.ts +9 -0
  56. package/dist/setup-prompt.js +59 -0
  57. package/dist/ssrf-guard.d.ts +48 -0
  58. package/dist/ssrf-guard.js +242 -0
  59. package/dist/token-store.js +23 -125
  60. package/dist/tools/_errors.d.ts +8 -2
  61. package/dist/tools/_errors.js +97 -6
  62. package/dist/tools/account-wizard.d.ts +33 -0
  63. package/dist/tools/account-wizard.js +284 -0
  64. package/dist/tools/accounts-tool.d.ts +8 -5
  65. package/dist/tools/accounts-tool.js +38 -15
  66. package/dist/tools/admin.js +2 -2
  67. package/dist/tools/calendar.js +2 -2
  68. package/dist/tools/chat.js +2 -2
  69. package/dist/tools/contacts.d.ts +40 -0
  70. package/dist/tools/contacts.js +135 -2
  71. package/dist/tools/docs.d.ts +3 -0
  72. package/dist/tools/docs.js +60 -9
  73. package/dist/tools/drive.d.ts +3 -0
  74. package/dist/tools/drive.js +60 -8
  75. package/dist/tools/forms.js +4 -2
  76. package/dist/tools/generated/_shared.d.ts +1 -3
  77. package/dist/tools/generated/_shared.js +3 -3
  78. package/dist/tools/generated/admin.js +175 -132
  79. package/dist/tools/generated/appsmarket.js +6 -2
  80. package/dist/tools/generated/calendar.js +45 -27
  81. package/dist/tools/generated/chat.js +81 -47
  82. package/dist/tools/generated/classroom.js +131 -104
  83. package/dist/tools/generated/cloudidentity.js +73 -61
  84. package/dist/tools/generated/cloudsearch.js +59 -49
  85. package/dist/tools/generated/contacts.js +22 -14
  86. package/dist/tools/generated/drive.js +51 -35
  87. package/dist/tools/generated/driveactivity.js +5 -1
  88. package/dist/tools/generated/drivelabels.js +30 -25
  89. package/dist/tools/generated/forms.js +7 -3
  90. package/dist/tools/generated/gmail.js +73 -58
  91. package/dist/tools/generated/groupsmigration.js +5 -1
  92. package/dist/tools/generated/groupssettings.js +7 -3
  93. package/dist/tools/generated/keep.js +12 -7
  94. package/dist/tools/generated/licensing.js +11 -7
  95. package/dist/tools/generated/meet.js +20 -13
  96. package/dist/tools/generated/postmaster.js +8 -4
  97. package/dist/tools/generated/reseller.js +22 -17
  98. package/dist/tools/generated/script.js +26 -16
  99. package/dist/tools/generated/sheets.js +11 -7
  100. package/dist/tools/generated/tasks.js +6 -2
  101. package/dist/tools/generated/vault.js +38 -33
  102. package/dist/tools/generated/workspaceevents.js +12 -7
  103. package/dist/tools/gmail-mime.d.ts +38 -8
  104. package/dist/tools/gmail-mime.js +75 -71
  105. package/dist/tools/gmail.d.ts +31 -0
  106. package/dist/tools/gmail.js +483 -99
  107. package/dist/tools/google-api.js +7 -4
  108. package/dist/tools/meet.js +2 -2
  109. package/dist/tools/searchconsole.js +2 -2
  110. package/dist/tools/sheets.js +6 -2
  111. package/dist/tools/slides.js +4 -2
  112. package/dist/tools/tasks.js +2 -2
  113. package/dist/types.d.ts +1 -1
  114. package/dist/write-control.d.ts +24 -1
  115. package/dist/write-control.js +26 -1
  116. package/package.json +26 -7
@@ -1,12 +1,12 @@
1
1
  import { z } from 'zod';
2
2
  import { isAllowed, writeDisabledResult } from '../write-control.js';
3
- import { ACCOUNTS } from '../accounts.js';
3
+ import { accountAliasSchema } 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
7
  import { executeApiMethod, jsonResult } from '../executor.js';
8
8
  import { WORKSPACE_APIS, cudFromMethod, loadMethodIndex, searchMethods, } from '../discovery-client.js';
9
- const accountEnum = z.enum(ACCOUNTS);
9
+ const accountEnum = accountAliasSchema.optional();
10
10
  // Policy/toolset namespace for each API alias must match the NAMED tools' service
11
11
  // names, or user deny globs and GOOGLE_TOOLSETS silently miss escape-hatch calls.
12
12
  const SERVICE_FOR_ALIAS = {
@@ -95,7 +95,7 @@ export function registerEscapeTools(registry, policy, deps = {}) {
95
95
  'policy as named tools. Returns JSON only — for binary/file content (media downloads, ' +
96
96
  'drive.files.export) use drive_download / drive_export instead.',
97
97
  inputSchema: {
98
- account: accountEnum.describe('Google account alias'),
98
+ account: accountEnum.describe('Google account alias (omit for the default account)'),
99
99
  api: z.string().describe(`API alias: ${apiList}`),
100
100
  methodId: z.string().describe('Discovery method id, e.g. "drive.revisions.list"'),
101
101
  pathParams: coerceJson(z.record(z.string(), z.union([z.string(), z.number()])).optional())
@@ -110,7 +110,10 @@ export function registerEscapeTools(registry, policy, deps = {}) {
110
110
  .optional()).describe('Query-string parameters; use an array for repeated params (e.g. resourceNames)'),
111
111
  body: coerceJson(z.record(z.string(), z.unknown()).optional()).describe('JSON request body'),
112
112
  },
113
- annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
113
+ // idempotentHint:false is load-bearing: the computed default (cud=read)
114
+ // would invite retries that duplicate sends through the escape hatch.
115
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
116
+ _meta: { 'anthropic/maxResultSizeChars': 100_000 },
114
117
  }, async ({ account, api, methodId, pathParams, queryParams, body }) => {
115
118
  if (!WORKSPACE_APIS[api]) {
116
119
  return jsonResult({ error: 'unknown_api', message: `Unknown api "${api}".`, hint: `Known APIs: ${apiList}`, retriable: false, account }, true);
@@ -1,9 +1,9 @@
1
1
  import { z } from 'zod';
2
2
  import { meet as meetClient } from '@googleapis/meet';
3
- import { ACCOUNTS } from '../accounts.js';
3
+ import { accountAliasSchema } from '../accounts.js';
4
4
  import { getClient } from '../client.js';
5
5
  import { handleGoogleApiError } from './_errors.js';
6
- const accountEnum = z.enum(ACCOUNTS);
6
+ const accountEnum = accountAliasSchema.optional();
7
7
  export function registerMeetTools(server) {
8
8
  // ─── Conference records (past meetings) ────────────────────────────────
9
9
  server.registerTool('meet_conference_records_list', {
@@ -2,10 +2,10 @@ import { z } from 'zod';
2
2
  import { coerceArray, coerceJson } from './_coerce.js';
3
3
  import { searchconsole as searchconsoleClient } from '@googleapis/searchconsole';
4
4
  import { webmasters as webmastersClient } from '@googleapis/webmasters';
5
- import { ACCOUNTS } from '../accounts.js';
5
+ import { accountAliasSchema } from '../accounts.js';
6
6
  import { getClient } from '../client.js';
7
7
  import { handleGoogleApiError } from './_errors.js';
8
- const accountEnum = z.enum(ACCOUNTS);
8
+ const accountEnum = accountAliasSchema.optional();
9
9
  export function registerSearchConsoleTools(server) {
10
10
  // ─── Sites ───────────────────────────────────────────────
11
11
  server.registerTool('searchconsole_sites_list', {
@@ -1,10 +1,10 @@
1
1
  import { z } from 'zod';
2
2
  import { coerceArray, coerceBoolean, coerceJson } from './_coerce.js';
3
3
  import { sheets as sheetsClient } from '@googleapis/sheets';
4
- import { ACCOUNTS } from '../accounts.js';
4
+ import { accountAliasSchema } from '../accounts.js';
5
5
  import { getClient } from '../client.js';
6
6
  import { handleGoogleApiError } from './_errors.js';
7
- const accountEnum = z.enum(ACCOUNTS);
7
+ const accountEnum = accountAliasSchema.optional();
8
8
  export function registerSheetsTools(server) {
9
9
  server.registerTool('sheets_create', {
10
10
  description: 'Create a new Google Sheets spreadsheet',
@@ -793,6 +793,8 @@ export function registerSheetsTools(server) {
793
793
  }
794
794
  });
795
795
  server.registerTool('sheets_find_replace', {
796
+ // Insert/append-capable or non-convergent: a retry duplicates content.
797
+ annotations: { idempotentHint: false },
796
798
  description: 'Find and replace text across a range, a sheet, or all sheets. Supports case sensitivity, full-cell match, regex, and formula scope.',
797
799
  inputSchema: {
798
800
  account: accountEnum.describe('Google account alias'),
@@ -1070,6 +1072,8 @@ export function registerSheetsTools(server) {
1070
1072
  }
1071
1073
  });
1072
1074
  server.registerTool('sheets_batch_update', {
1075
+ // Insert/append-capable or non-convergent: a retry duplicates content.
1076
+ annotations: { idempotentHint: false },
1073
1077
  description: 'Generic spreadsheets.batchUpdate pass-through. Accepts the full Request union (~70 types). Use this for advanced operations not covered by a dedicated tool. See https://developers.google.com/workspace/sheets/api/reference/rest/v4/spreadsheets/request',
1074
1078
  inputSchema: {
1075
1079
  account: accountEnum.describe('Google account alias'),
@@ -1,11 +1,11 @@
1
1
  import { z } from 'zod';
2
2
  import { slides as slidesClient } from '@googleapis/slides';
3
- import { ACCOUNTS } from '../accounts.js';
3
+ import { accountAliasSchema } from '../accounts.js';
4
4
  import { getClient } from '../client.js';
5
5
  import { handleGoogleApiError } from './_errors.js';
6
6
  import { coerceArray, coerceBoolean, coerceJson } from './_coerce.js';
7
7
  import { sliceClean } from '../trim.js';
8
- const accountEnum = z.enum(ACCOUNTS);
8
+ const accountEnum = accountAliasSchema.optional();
9
9
  const SLIDE_TEXT_DIGEST_CHARS = 200;
10
10
  export function registerSlidesTools(server) {
11
11
  server.registerTool('slides_create', {
@@ -102,6 +102,8 @@ export function registerSlidesTools(server) {
102
102
  }
103
103
  });
104
104
  server.registerTool('slides_batch_update', {
105
+ // Insert/append-capable or non-convergent: a retry duplicates content.
106
+ annotations: { idempotentHint: false },
105
107
  description: 'Generic presentations.batchUpdate pass-through. Accepts the full Request union (create/move/delete slides, insert text/shapes/images/tables, styling, replaceAllText, …). See https://developers.google.com/workspace/slides/api/reference/rest/v1/presentations/request',
106
108
  inputSchema: {
107
109
  account: accountEnum.describe('Google account alias'),
@@ -1,10 +1,10 @@
1
1
  import { z } from 'zod';
2
2
  import { coerceBoolean } from './_coerce.js';
3
3
  import { tasks as tasksClient } from '@googleapis/tasks';
4
- import { ACCOUNTS } from '../accounts.js';
4
+ import { accountAliasSchema } from '../accounts.js';
5
5
  import { getClient } from '../client.js';
6
6
  import { handleGoogleApiError } from './_errors.js';
7
- const accountEnum = z.enum(ACCOUNTS);
7
+ const accountEnum = accountAliasSchema.optional();
8
8
  export function registerTasksTools(server) {
9
9
  // ─── Tasklists ─────────────────────────────────────────────────────────
10
10
  server.registerTool('tasks_lists_list', {
package/dist/types.d.ts CHANGED
@@ -32,7 +32,7 @@ export interface GmailMessageFull {
32
32
  cc: string;
33
33
  date: string;
34
34
  body: string;
35
- bodyOrigin?: 'text/plain' | 'text/html';
35
+ bodyFormat: 'plain' | 'markdown' | 'html';
36
36
  bodyTruncated?: boolean;
37
37
  bodyTotalChars?: number;
38
38
  messageIdHeader?: string;
@@ -1,17 +1,40 @@
1
1
  import type { Cud } from './registry.js';
2
2
  export type Profile = 'read-only' | 'safe-writes' | 'full-writes';
3
+ export type Transport = 'stdio' | 'http';
3
4
  export interface Policy {
4
5
  profile: Profile;
5
6
  readOnly: boolean;
6
7
  allow: string[];
7
8
  deny: string[];
9
+ /**
10
+ * B14 reserved seam (cc-write-control "HTTP posture", LOCKED to OQ-5
11
+ * "annotations only"): the dispatch transport is threaded into the resolved
12
+ * policy for the EE/future seam, but in v6 alpha it does NOT stiffen the
13
+ * profile or alter any `isAllowed` verdict. "Stricter on HTTP" is achieved
14
+ * entirely by the `anthropic/requiresUserInteraction` annotation on the
15
+ * irreversible set (A12), emitted identically on both transports.
16
+ * Optional so existing Policy literals (codegen/tests) need no change;
17
+ * resolvePolicy always populates it.
18
+ */
19
+ transport?: Transport;
8
20
  }
9
21
  interface ToolRef {
10
22
  name: string;
11
23
  service: string;
12
24
  cud: Cud;
13
25
  }
14
- export declare function resolvePolicy(env?: NodeJS.ProcessEnv): Policy;
26
+ /** The irreversible set (frozen, cc-write-control): real sends leave the
27
+ * user's (or org's) identity unrecallably; permanent deletes bypass Trash.
28
+ * Includes the callable GENERATED twins of the curated ops — the surface is
29
+ * always callable by name, so a prompt-free twin would defeat the layer.
30
+ * Reversible mutations (trash/untrash, draft create/update, labels, calendar
31
+ * edits) are deliberately excluded so the approval prompt never nags. The
32
+ * escape hatch is documented as outside this layer (server write-control
33
+ * still gates it). */
34
+ export declare const IRREVERSIBLE_TOOLS: Set<string>;
35
+ export declare function resolvePolicy(env?: NodeJS.ProcessEnv, opts?: {
36
+ transport?: Transport;
37
+ }): Policy;
15
38
  export declare function isAllowed(tool: ToolRef, policy: Policy): boolean;
16
39
  export declare function writeDisabledResult(tool: ToolRef, policy: Policy): {
17
40
  content: {
@@ -1,8 +1,31 @@
1
1
  const PROFILES = ['read-only', 'safe-writes', 'full-writes'];
2
+ /** The irreversible set (frozen, cc-write-control): real sends leave the
3
+ * user's (or org's) identity unrecallably; permanent deletes bypass Trash.
4
+ * Includes the callable GENERATED twins of the curated ops — the surface is
5
+ * always callable by name, so a prompt-free twin would defeat the layer.
6
+ * Reversible mutations (trash/untrash, draft create/update, labels, calendar
7
+ * edits) are deliberately excluded so the approval prompt never nags. The
8
+ * escape hatch is documented as outside this layer (server write-control
9
+ * still gates it). */
10
+ export const IRREVERSIBLE_TOOLS = new Set([
11
+ // curated
12
+ 'gmail_send',
13
+ 'gmail_send_draft',
14
+ 'gmail_delete',
15
+ 'gmail_batch_delete',
16
+ 'drive_delete',
17
+ 'drive_empty_trash',
18
+ // generated twins (permanent deletes / real sends)
19
+ 'gmail_users_threads_delete',
20
+ 'gmail_users_drafts_delete',
21
+ 'chat_spaces_messages_delete',
22
+ 'cloudidentity_customers_userinvitations_send',
23
+ 'vault_matters_holds_delete',
24
+ ]);
2
25
  function parseGlobs(value) {
3
26
  return (value ?? '').split(',').map((s) => s.trim()).filter(Boolean);
4
27
  }
5
- export function resolvePolicy(env = process.env) {
28
+ export function resolvePolicy(env = process.env, opts = {}) {
6
29
  const raw = (env.GOOGLE_PROFILE ?? 'read-only').trim();
7
30
  if (raw && !PROFILES.includes(raw)) {
8
31
  // Fail-closed to read-only, but say so — a typo'd profile otherwise looks
@@ -14,6 +37,8 @@ export function resolvePolicy(env = process.env) {
14
37
  readOnly: /^(1|true|yes)$/i.test(env.GOOGLE_READ_ONLY ?? ''),
15
38
  allow: parseGlobs(env.GOOGLE_WRITE_ALLOW),
16
39
  deny: parseGlobs(env.GOOGLE_WRITE_DENY),
40
+ // Reserved seam only (see Policy.transport): does not affect the verdict.
41
+ transport: opts.transport ?? 'stdio',
17
42
  };
18
43
  }
19
44
  function globToRegExp(glob) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-google-multi",
3
- "version": "5.4.1-alpha.2",
3
+ "version": "6.0.0-alpha.2",
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",
@@ -12,7 +12,7 @@
12
12
  "dist"
13
13
  ],
14
14
  "engines": {
15
- "node": ">=20"
15
+ "node": ">=22"
16
16
  },
17
17
  "repository": {
18
18
  "type": "git",
@@ -57,7 +57,8 @@
57
57
  "test:watch": "vitest",
58
58
  "gen:discovery": "tsx scripts/fetch-discovery.ts",
59
59
  "gen:tools": "tsx scripts/gen-tools.ts",
60
- "gen:coverage": "tsx scripts/gen-coverage.ts"
60
+ "gen:coverage": "tsx scripts/gen-coverage.ts",
61
+ "pack:mcpb": "mcpb pack . mcp-google-multi.mcpb"
61
62
  },
62
63
  "dependencies": {
63
64
  "@googleapis/admin": "^33.0.0",
@@ -74,22 +75,40 @@
74
75
  "@googleapis/slides": "^6.0.0",
75
76
  "@googleapis/tasks": "^13.0.0",
76
77
  "@googleapis/webmasters": "^4.0.0",
77
- "@modelcontextprotocol/sdk": "^1.29.0",
78
- "dotenv": "^17.4.0",
78
+ "@modelcontextprotocol/sdk": "^1.30.0",
79
79
  "googleapis-common": "^8.0.3",
80
+ "jose": "^6.2.8",
81
+ "markdown-it": "15.0.0",
80
82
  "mime-types": "^3.0.2",
81
- "open": "^11.0.0",
83
+ "nodemailer": "9.1.1",
84
+ "turndown": "7.2.4",
85
+ "turndown-plugin-gfm": "1.0.2",
82
86
  "zod": "^4.3.6"
83
87
  },
84
88
  "devDependencies": {
89
+ "@anthropic-ai/mcpb": "^2.1.2",
85
90
  "@eslint/js": "^10.0.1",
91
+ "@types/js-yaml": "^4.0.9",
92
+ "@types/markdown-it": "^14.1.2",
86
93
  "@types/mime-types": "^3.0.1",
87
- "@types/node": "^25.6.0",
94
+ "@types/node": "^22.20.1",
95
+ "@types/nodemailer": "^8.0.1",
96
+ "@types/turndown": "^5.0.6",
88
97
  "eslint": "^10.2.1",
98
+ "js-yaml": "^4.3.2",
89
99
  "semantic-release": "^25.0.3",
90
100
  "tsx": "^4.23.1",
91
101
  "typescript": "^6.0.3",
92
102
  "typescript-eslint": "^8.59.1",
93
103
  "vitest": "^3.2.6"
104
+ },
105
+ "optionalDependencies": {
106
+ "@napi-rs/keyring": "1.3.0"
107
+ },
108
+ "overrides": {
109
+ "tmp": "^0.2.4",
110
+ "fast-uri": "^3.1.6",
111
+ "hono": "^4.13.5",
112
+ "qs": "^6.16.0"
94
113
  }
95
114
  }