mcp-google-multi 6.0.0-alpha.20 → 6.0.0-alpha.21

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/discover.js CHANGED
@@ -1,8 +1,32 @@
1
1
  import { z } from 'zod';
2
2
  import { describePolicy } from './write-control.js';
3
+ // The description budget is the whole point of lazy mode: a full op list per
4
+ // service put lazy tools/list at ~11.5k tokens (RQ2), so descriptions carry a
5
+ // CAPPED vocabulary and the complete catalog stays in this tool's RESULT.
6
+ const CURATED_OPS_CAP = 10;
7
+ const GENERATED_GROUPS_CAP = 6;
3
8
  function opVocabulary(registry, service) {
4
- const ops = registry.catalog(service).map((o) => o.tool.startsWith(`${service}_`) ? o.tool.slice(service.length + 1) : o.tool);
5
- return [...new Set(ops)].join(', ');
9
+ const { curated, generated } = registry.opNames(service);
10
+ const parts = [];
11
+ if (curated.length > 0) {
12
+ const shown = curated.slice(0, CURATED_OPS_CAP);
13
+ const more = curated.length - shown.length;
14
+ parts.push(`${shown.join(', ')}${more > 0 ? ` +${more} more` : ''}`);
15
+ }
16
+ if (generated.length > 0) {
17
+ // The generated long tail compresses to its resource groups: denser and
18
+ // more selective than any truncated name list.
19
+ const counts = new Map();
20
+ for (const op of generated) {
21
+ const group = op.split('_')[0];
22
+ counts.set(group, (counts.get(group) ?? 0) + 1);
23
+ }
24
+ const groups = [...counts.entries()].sort((a, b) => b[1] - a[1]);
25
+ const shown = groups.slice(0, GENERATED_GROUPS_CAP).map(([g]) => g);
26
+ const more = groups.length - shown.length;
27
+ parts.push(`${generated.length} generated ops: ${shown.join(', ')}${more > 0 ? ` +${more} areas` : ''}`);
28
+ }
29
+ return parts.join('; ');
6
30
  }
7
31
  export function registerDiscoverTools(registry, policy) {
8
32
  const registerMeta = registry.registerMeta;
@@ -11,9 +35,8 @@ export function registerDiscoverTools(registry, policy) {
11
35
  // name/schema context budget after. stdio-only semantics — over stateless
12
36
  // HTTP the mode is forced curated and these are no-ops.
13
37
  registerMeta('discover_all', {
14
- description: 'Reveal ALL curated Google tools at once (instead of per-service discovery). ' +
15
- 'Use when starting substantial Google work so the shaped, high-quality tools are ' +
16
- 'directly callable; prefer them over google_api_call. Pair with discover_reset when done.',
38
+ description: 'Reveal ALL curated Google tools at once (instead of per-service discovery). Use when ' +
39
+ 'starting substantial Google work; prefer these over google_api_call. Pair with discover_reset.',
17
40
  inputSchema: {},
18
41
  _meta: { 'anthropic/alwaysLoad': true },
19
42
  }, async () => {
@@ -35,9 +58,8 @@ export function registerDiscoverTools(registry, policy) {
35
58
  };
36
59
  });
37
60
  registerMeta('discover_reset', {
38
- description: 'Collapse the tool surface back to the configured default (lean meta-tools-only under the ' +
39
- 'default lazy mode), reclaiming context budget after heavy Google work. All tools remain ' +
40
- 'callable by name after collapsing.',
61
+ description: 'Collapse the tool surface back to the configured default, reclaiming context budget ' +
62
+ 'after heavy Google work. All tools remain callable by name after collapsing.',
41
63
  inputSchema: {},
42
64
  _meta: { 'anthropic/alwaysLoad': true },
43
65
  }, async () => {
@@ -58,13 +80,12 @@ export function registerDiscoverTools(registry, policy) {
58
80
  });
59
81
  for (const service of registry.services()) {
60
82
  registerMeta(`${service}_discover`, {
61
- description: `List the available ${service} operations. ` +
62
- (registry.mode === 'lazy'
63
- ? `Operational ${service} tools are hidden until discovered — call this first, then call the tool you need by name. `
64
- : `Returns the ${service} catalog (and reveals any still-hidden ${service} tools). `) +
65
- `Operations: ${opVocabulary(registry, service)}.`,
83
+ description: (registry.mode === 'lazy'
84
+ ? `Discover ${service}: lists the catalog and reveals its hidden tools; call first, then call the tool by name. `
85
+ : `List the ${service} catalog (reveals any still-hidden ${service} tools). `) +
86
+ `Ops: ${opVocabulary(registry, service)}.`,
66
87
  inputSchema: {
67
- query: z.string().optional().describe('Keyword to filter the returned operations'),
88
+ query: z.string().optional().describe('Filter keyword'),
68
89
  },
69
90
  _meta: { 'anthropic/alwaysLoad': true },
70
91
  }, async ({ query }) => {
@@ -54,6 +54,13 @@ export declare class ToolRegistry {
54
54
  * normalization; the kind drives value coercion on renamed keys). */
55
55
  argShape(name: string): ArgShape | undefined;
56
56
  catalog(service: string, query?: string): CatalogOperation[];
57
+ /** Op-name vocabulary for a service, split by provenance so the capped
58
+ * discover descriptions can list curated ops and only summarize the
59
+ * generated long tail. */
60
+ opNames(service: string): {
61
+ curated: string[];
62
+ generated: string[];
63
+ };
57
64
  reveal(service: string): boolean;
58
65
  /** discover_all: advertise the full curated set at once. Idempotent. */
59
66
  expand(): boolean;
package/dist/registry.js CHANGED
@@ -237,6 +237,20 @@ export class ToolRegistry {
237
237
  cud: t.cud,
238
238
  }));
239
239
  }
240
+ /** Op-name vocabulary for a service, split by provenance so the capped
241
+ * discover descriptions can list curated ops and only summarize the
242
+ * generated long tail. */
243
+ opNames(service) {
244
+ const strip = (n) => (n.startsWith(`${service}_`) ? n.slice(service.length + 1) : n);
245
+ const curated = [];
246
+ const generated = [];
247
+ for (const t of this.tools) {
248
+ if (t.meta || t.service !== service)
249
+ continue;
250
+ (t.generated ? generated : curated).push(strip(t.name));
251
+ }
252
+ return { curated: [...new Set(curated)], generated: [...new Set(generated)] };
253
+ }
240
254
  reveal(service) {
241
255
  if (this.revealed.has(service))
242
256
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-google-multi",
3
- "version": "6.0.0-alpha.20",
3
+ "version": "6.0.0-alpha.21",
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",