mcp-google-multi 6.0.0-alpha.69 → 6.0.0-alpha.70

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/dist/compose.d.ts CHANGED
@@ -1,8 +1,15 @@
1
- import type { McpServer } from "@modelcontextprotocol/server";
1
+ import { McpServer } from "@modelcontextprotocol/server";
2
2
  import type { ServerTarget } from './http-transport.js';
3
3
  import { ToolRegistry, type DiscoveryMode } from './registry.js';
4
4
  import { type IdentityContext } from './identity.js';
5
5
  import type { Metrics } from './usage-metrics.js';
6
+ export interface McpServerOptions {
7
+ /** Sent in the initialize result; omitted entirely when absent or blank. */
8
+ instructions?: string;
9
+ }
10
+ /** The one place the MCP server is constructed, so an embedding host can pass
11
+ * per-session `instructions` without re-deriving the server identity. */
12
+ export declare function createMcpServer(version: string, opts?: McpServerOptions): McpServer;
6
13
  export declare function buildRegistry(server: McpServer, ctx: IdentityContext, mode?: DiscoveryMode, metrics?: Metrics | null): ToolRegistry;
7
14
  /** The per-request hooks bound to one registry: argument shapes, unknown-
8
15
  * argument screening and the account a validation envelope names. Both
package/dist/compose.js CHANGED
@@ -3,6 +3,7 @@
3
3
  // module must stay free of import-time side effects of its own: no argv
4
4
  // parsing, no process.exit, no prints (accounts.js module-load resolution is
5
5
  // the one inherited, pre-existing exception on the import chain).
6
+ import { McpServer } from "@modelcontextprotocol/server";
6
7
  import { GENERATED_SERVICES } from './tools/generated/index.js';
7
8
  import { GENERATED_GATES, SERVICES, servicesAwaitingRestart, unknownToolMessage } from './services.js';
8
9
  import { argNormalizationEnabled } from './arg-normalize.js';
@@ -16,6 +17,12 @@ import { registerAccountWizardTools } from './tools/account-wizard.js';
16
17
  import { getToolsets, toolsetEnabled } from './toolsets.js';
17
18
  import { isOwnerContext } from './identity.js';
18
19
  import { resolveScopesForAccount } from './auth.js';
20
+ /** The one place the MCP server is constructed, so an embedding host can pass
21
+ * per-session `instructions` without re-deriving the server identity. */
22
+ export function createMcpServer(version, opts = {}) {
23
+ const instructions = opts.instructions?.trim() ? opts.instructions : undefined;
24
+ return new McpServer({ name: 'mcp-google-multi', version }, instructions ? { instructions } : undefined);
25
+ }
19
26
  export function buildRegistry(server, ctx, mode, metrics = null) {
20
27
  const policy = ctx.policy;
21
28
  // The registry's account view IS the context's: fan-out expansion,
package/dist/index.js CHANGED
@@ -7,11 +7,10 @@ import { readFileSync } from 'node:fs';
7
7
  import { fileURLToPath } from 'node:url';
8
8
  import path from 'node:path';
9
9
  import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
10
- import { McpServer } from "@modelcontextprotocol/server";
11
10
  import { resolveDiscoveryMode } from './registry.js';
12
11
  import { isAllowed, describePolicy } from './write-control.js';
13
12
  import { buildIdentityContext } from './identity.js';
14
- import { buildRegistry, requestHooksFor } from './compose.js';
13
+ import { buildRegistry, createMcpServer, requestHooksFor } from './compose.js';
15
14
  import { registerSetupPrompt } from './setup-prompt.js';
16
15
  import { applyNetTuning } from './net-tuning.js';
17
16
  import { withArgNormalization, withValidationEnvelope } from './arg-normalize.js';
@@ -81,7 +80,7 @@ async function main() {
81
80
  if (process.argv.includes('config') && process.argv.includes('check')) {
82
81
  const ctx = buildIdentityContext();
83
82
  const policy = ctx.policy;
84
- const registry = buildRegistry(new McpServer({ name: 'mcp-google-multi', version: pkg.version }), ctx);
83
+ const registry = buildRegistry(createMcpServer(pkg.version), ctx);
85
84
  const cud = registry.tools.filter((t) => t.cud !== 'read');
86
85
  const disabled = cud.filter((t) => !isAllowed(t, policy));
87
86
  const counts = registry.visibleCount();
@@ -190,7 +189,7 @@ async function main() {
190
189
  // Build one McpServer + registry per transport at boot (P1 / BV gap #4:
191
190
  // never rebuilt per request); `both` runs the two concurrently.
192
191
  if (wantStdio) {
193
- const server = new McpServer({ name: 'mcp-google-multi', version: pkg.version });
192
+ const server = createMcpServer(pkg.version);
194
193
  const stdioMetrics = initMetricsFor('stdio', resolveDiscoveryMode());
195
194
  const stdioCtx = buildIdentityContext(process.env, { transport: 'stdio' });
196
195
  const registry = buildRegistry(server, stdioCtx, undefined, stdioMetrics);
@@ -239,7 +238,7 @@ async function main() {
239
238
  if (configuredMode && configuredMode !== 'curated') {
240
239
  process.stderr.write(`GOOGLE_DISCOVERY="${configuredMode}" is ignored over HTTP; the stateless transport forces "curated".\n`);
241
240
  }
242
- const httpServer = new McpServer({ name: 'mcp-google-multi', version: pkg.version });
241
+ const httpServer = createMcpServer(pkg.version);
243
242
  const httpMetrics = initMetricsFor('http', 'curated');
244
243
  const httpCtx = buildIdentityContext(process.env, { transport: 'http' });
245
244
  const registry = buildRegistry(httpServer, httpCtx, 'curated', httpMetrics);
package/dist/registry.js CHANGED
@@ -47,6 +47,10 @@ export function serviceOf(name) {
47
47
  }
48
48
  // read tools that write local files — same savePath fanned across accounts would clobber
49
49
  const FANOUT_EXCLUDE = new Set(['gmail_download_attachment', 'drive_download', 'drive_export']);
50
+ /** cud=read for gating/fan-out, yet they write (local files, the account
51
+ * registry, an MCP-client config): advertised as writes so annotation-keyed
52
+ * clients prompt instead of auto-running them. */
53
+ const WRITES_DESPITE_READ = new Set([...FANOUT_EXCLUDE, 'account_add', 'account_reauth', 'account_write_config']);
50
54
  /** Account-management tools: the `account` they take is the SUBJECT of the
51
55
  * operation, not the identity it runs as, so injecting the configured default
52
56
  * would silently retarget them. `account_add` would adopt the default as the
@@ -130,12 +134,14 @@ export class ToolRegistry {
130
134
  this.registerTool = ((name, config, handler) => {
131
135
  const service = serviceOf(name);
132
136
  const cud = config.cud ?? inferCud(name);
133
- // destructiveHint=false claims "additive only" (MCP spec) — updates overwrite, so they stay true.
137
+ // Every write is destructive: clients key per-call approval on it, and a
138
+ // create (send, share, upload) is no less consequential than an update.
134
139
  // idempotent: reads trivially, deletes (already-gone = same), updates
135
140
  // (overwrite converges); creates are not (send twice = two emails).
141
+ const writes = cud !== 'read' || WRITES_DESPITE_READ.has(name);
136
142
  const annotations = {
137
- readOnlyHint: cud === 'read',
138
- destructiveHint: cud === 'delete' || cud === 'update',
143
+ readOnlyHint: !writes,
144
+ destructiveHint: writes,
139
145
  idempotentHint: cud !== 'create',
140
146
  ...config.annotations,
141
147
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-google-multi",
3
- "version": "6.0.0-alpha.69",
3
+ "version": "6.0.0-alpha.70",
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",