@aaronsb/google-workspace-mcp 2.0.0-alpha.4
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/LICENSE +21 -0
- package/README.md +193 -0
- package/build/__helpers__/testSetup.d.ts +1 -0
- package/build/__helpers__/testSetup.js +6 -0
- package/build/__helpers__/testSetup.js.map +1 -0
- package/build/__tests__/accounts/credentials.test.d.ts +1 -0
- package/build/__tests__/accounts/credentials.test.js +129 -0
- package/build/__tests__/accounts/credentials.test.js.map +1 -0
- package/build/__tests__/accounts/registry.test.d.ts +1 -0
- package/build/__tests__/accounts/registry.test.js +74 -0
- package/build/__tests__/accounts/registry.test.js.map +1 -0
- package/build/__tests__/executor/errors.test.d.ts +1 -0
- package/build/__tests__/executor/errors.test.js +42 -0
- package/build/__tests__/executor/errors.test.js.map +1 -0
- package/build/__tests__/executor/gws.test.d.ts +1 -0
- package/build/__tests__/executor/gws.test.js +178 -0
- package/build/__tests__/executor/gws.test.js.map +1 -0
- package/build/__tests__/executor/paths.test.d.ts +1 -0
- package/build/__tests__/executor/paths.test.js +60 -0
- package/build/__tests__/executor/paths.test.js.map +1 -0
- package/build/__tests__/executor/workspace.test.d.ts +1 -0
- package/build/__tests__/executor/workspace.test.js +117 -0
- package/build/__tests__/executor/workspace.test.js.map +1 -0
- package/build/__tests__/factory/generator.test.d.ts +1 -0
- package/build/__tests__/factory/generator.test.js +178 -0
- package/build/__tests__/factory/generator.test.js.map +1 -0
- package/build/__tests__/factory/patch-coverage.test.d.ts +10 -0
- package/build/__tests__/factory/patch-coverage.test.js +148 -0
- package/build/__tests__/factory/patch-coverage.test.js.map +1 -0
- package/build/__tests__/factory/safety.test.d.ts +1 -0
- package/build/__tests__/factory/safety.test.js +107 -0
- package/build/__tests__/factory/safety.test.js.map +1 -0
- package/build/__tests__/integration/executor.test.d.ts +5 -0
- package/build/__tests__/integration/executor.test.js +46 -0
- package/build/__tests__/integration/executor.test.js.map +1 -0
- package/build/__tests__/integration/handlers.test.d.ts +6 -0
- package/build/__tests__/integration/handlers.test.js +95 -0
- package/build/__tests__/integration/handlers.test.js.map +1 -0
- package/build/__tests__/integration/setup.d.ts +19 -0
- package/build/__tests__/integration/setup.js +61 -0
- package/build/__tests__/integration/setup.js.map +1 -0
- package/build/__tests__/server/formatting/markdown.test.d.ts +1 -0
- package/build/__tests__/server/formatting/markdown.test.js +149 -0
- package/build/__tests__/server/formatting/markdown.test.js.map +1 -0
- package/build/__tests__/server/formatting/next-steps.test.d.ts +1 -0
- package/build/__tests__/server/formatting/next-steps.test.js +42 -0
- package/build/__tests__/server/formatting/next-steps.test.js.map +1 -0
- package/build/__tests__/server/handler.test.d.ts +1 -0
- package/build/__tests__/server/handler.test.js +97 -0
- package/build/__tests__/server/handler.test.js.map +1 -0
- package/build/__tests__/server/handlers/__mocks__/executor.d.ts +147 -0
- package/build/__tests__/server/handlers/__mocks__/executor.js +114 -0
- package/build/__tests__/server/handlers/__mocks__/executor.js.map +1 -0
- package/build/__tests__/server/handlers/accounts.test.d.ts +1 -0
- package/build/__tests__/server/handlers/accounts.test.js +127 -0
- package/build/__tests__/server/handlers/accounts.test.js.map +1 -0
- package/build/__tests__/server/handlers/calendar.test.d.ts +1 -0
- package/build/__tests__/server/handlers/calendar.test.js +95 -0
- package/build/__tests__/server/handlers/calendar.test.js.map +1 -0
- package/build/__tests__/server/handlers/drive.test.d.ts +1 -0
- package/build/__tests__/server/handlers/drive.test.js +81 -0
- package/build/__tests__/server/handlers/drive.test.js.map +1 -0
- package/build/__tests__/server/handlers/email.test.d.ts +1 -0
- package/build/__tests__/server/handlers/email.test.js +99 -0
- package/build/__tests__/server/handlers/email.test.js.map +1 -0
- package/build/__tests__/server/handlers/validate.test.d.ts +1 -0
- package/build/__tests__/server/handlers/validate.test.js +88 -0
- package/build/__tests__/server/handlers/validate.test.js.map +1 -0
- package/build/__tests__/server/queue.test.d.ts +1 -0
- package/build/__tests__/server/queue.test.js +194 -0
- package/build/__tests__/server/queue.test.js.map +1 -0
- package/build/__tests__/server/server.test.d.ts +7 -0
- package/build/__tests__/server/server.test.js +135 -0
- package/build/__tests__/server/server.test.js.map +1 -0
- package/build/__tests__/server/tools.test.d.ts +1 -0
- package/build/__tests__/server/tools.test.js +91 -0
- package/build/__tests__/server/tools.test.js.map +1 -0
- package/build/accounts/auth.d.ts +24 -0
- package/build/accounts/auth.js +118 -0
- package/build/accounts/auth.js.map +1 -0
- package/build/accounts/credentials.d.ts +11 -0
- package/build/accounts/credentials.js +52 -0
- package/build/accounts/credentials.js.map +1 -0
- package/build/accounts/index.d.ts +6 -0
- package/build/accounts/index.js +4 -0
- package/build/accounts/index.js.map +1 -0
- package/build/accounts/registry.d.ts +11 -0
- package/build/accounts/registry.js +62 -0
- package/build/accounts/registry.js.map +1 -0
- package/build/executor/errors.d.ts +15 -0
- package/build/executor/errors.js +37 -0
- package/build/executor/errors.js.map +1 -0
- package/build/executor/file-output.d.ts +23 -0
- package/build/executor/file-output.js +67 -0
- package/build/executor/file-output.js.map +1 -0
- package/build/executor/gws.d.ts +23 -0
- package/build/executor/gws.js +144 -0
- package/build/executor/gws.js.map +1 -0
- package/build/executor/index.d.ts +4 -0
- package/build/executor/index.js +4 -0
- package/build/executor/index.js.map +1 -0
- package/build/executor/paths.d.ts +6 -0
- package/build/executor/paths.js +25 -0
- package/build/executor/paths.js.map +1 -0
- package/build/executor/workspace.d.ts +38 -0
- package/build/executor/workspace.js +146 -0
- package/build/executor/workspace.js.map +1 -0
- package/build/factory/defaults.d.ts +8 -0
- package/build/factory/defaults.js +101 -0
- package/build/factory/defaults.js.map +1 -0
- package/build/factory/generator.d.ts +23 -0
- package/build/factory/generator.js +253 -0
- package/build/factory/generator.js.map +1 -0
- package/build/factory/manifest.yaml +852 -0
- package/build/factory/patches.d.ts +6 -0
- package/build/factory/patches.js +13 -0
- package/build/factory/patches.js.map +1 -0
- package/build/factory/registry.d.ts +12 -0
- package/build/factory/registry.js +18 -0
- package/build/factory/registry.js.map +1 -0
- package/build/factory/safety.d.ts +68 -0
- package/build/factory/safety.js +157 -0
- package/build/factory/safety.js.map +1 -0
- package/build/factory/types.d.ts +102 -0
- package/build/factory/types.js +6 -0
- package/build/factory/types.js.map +1 -0
- package/build/factory/yaml.d.ts +2 -0
- package/build/factory/yaml.js +3 -0
- package/build/factory/yaml.js.map +1 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +14 -0
- package/build/index.js.map +1 -0
- package/build/server/formatting/markdown.d.ts +21 -0
- package/build/server/formatting/markdown.js +324 -0
- package/build/server/formatting/markdown.js.map +1 -0
- package/build/server/formatting/next-steps.d.ts +9 -0
- package/build/server/formatting/next-steps.js +123 -0
- package/build/server/formatting/next-steps.js.map +1 -0
- package/build/server/handler.d.ts +3 -0
- package/build/server/handler.js +24 -0
- package/build/server/handler.js.map +1 -0
- package/build/server/handlers/accounts.d.ts +2 -0
- package/build/server/handlers/accounts.js +181 -0
- package/build/server/handlers/accounts.js.map +1 -0
- package/build/server/handlers/calendar.d.ts +2 -0
- package/build/server/handlers/calendar.js +93 -0
- package/build/server/handlers/calendar.js.map +1 -0
- package/build/server/handlers/drive.d.ts +2 -0
- package/build/server/handlers/drive.js +74 -0
- package/build/server/handlers/drive.js.map +1 -0
- package/build/server/handlers/email.d.ts +2 -0
- package/build/server/handlers/email.js +115 -0
- package/build/server/handlers/email.js.map +1 -0
- package/build/server/handlers/validate.d.ts +3 -0
- package/build/server/handlers/validate.js +22 -0
- package/build/server/handlers/validate.js.map +1 -0
- package/build/server/handlers/workspace.d.ts +9 -0
- package/build/server/handlers/workspace.js +110 -0
- package/build/server/handlers/workspace.js.map +1 -0
- package/build/server/index.d.ts +4 -0
- package/build/server/index.js +4 -0
- package/build/server/index.js.map +1 -0
- package/build/server/queue.d.ts +11 -0
- package/build/server/queue.js +141 -0
- package/build/server/queue.js.map +1 -0
- package/build/server/server.d.ts +3 -0
- package/build/server/server.js +214 -0
- package/build/server/server.js.map +1 -0
- package/build/server/tools.d.ts +13 -0
- package/build/server/tools.js +99 -0
- package/build/server/tools.js.map +1 -0
- package/build/services/calendar/patch.d.ts +11 -0
- package/build/services/calendar/patch.js +116 -0
- package/build/services/calendar/patch.js.map +1 -0
- package/build/services/drive/patch.d.ts +10 -0
- package/build/services/drive/patch.js +131 -0
- package/build/services/drive/patch.js.map +1 -0
- package/build/services/gmail/attachments.d.ts +19 -0
- package/build/services/gmail/attachments.js +90 -0
- package/build/services/gmail/attachments.js.map +1 -0
- package/build/services/gmail/patch.d.ts +10 -0
- package/build/services/gmail/patch.js +226 -0
- package/build/services/gmail/patch.js.map +1 -0
- package/package.json +34 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Patch registry — collects all per-service patches.
|
|
3
|
+
* Import this to get the complete patch map for the generator.
|
|
4
|
+
*/
|
|
5
|
+
import { gmailPatch } from '../services/gmail/patch.js';
|
|
6
|
+
import { calendarPatch } from '../services/calendar/patch.js';
|
|
7
|
+
import { drivePatch } from '../services/drive/patch.js';
|
|
8
|
+
export const patches = {
|
|
9
|
+
gmail: gmailPatch,
|
|
10
|
+
calendar: calendarPatch,
|
|
11
|
+
drive: drivePatch,
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=patches.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patches.js","sourceRoot":"","sources":["../../src/factory/patches.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAGxD,MAAM,CAAC,MAAM,OAAO,GAAiC;IACnD,KAAK,EAAE,UAAU;IACjB,QAAQ,EAAE,aAAa;IACvB,KAAK,EAAE,UAAU;CAClB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared factory registry — single instance of manifest + generated tools.
|
|
3
|
+
* Both handler.ts and tools.ts import from here instead of loading independently.
|
|
4
|
+
*
|
|
5
|
+
* This module uses import.meta.url (ESM only) to resolve manifest.yaml
|
|
6
|
+
* relative to the built output, which works when running via npx or mcpb
|
|
7
|
+
* where cwd is NOT the project root.
|
|
8
|
+
*/
|
|
9
|
+
import type { GeneratedTool } from './types.js';
|
|
10
|
+
import type { Manifest } from './types.js';
|
|
11
|
+
export declare const manifest: Manifest;
|
|
12
|
+
export declare const generatedTools: GeneratedTool[];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared factory registry — single instance of manifest + generated tools.
|
|
3
|
+
* Both handler.ts and tools.ts import from here instead of loading independently.
|
|
4
|
+
*
|
|
5
|
+
* This module uses import.meta.url (ESM only) to resolve manifest.yaml
|
|
6
|
+
* relative to the built output, which works when running via npx or mcpb
|
|
7
|
+
* where cwd is NOT the project root.
|
|
8
|
+
*/
|
|
9
|
+
import { dirname } from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { setModuleDir, loadManifest, generateTools } from './generator.js';
|
|
12
|
+
import { patches } from './patches.js';
|
|
13
|
+
// Inject module directory so loadManifest can find manifest.yaml
|
|
14
|
+
// relative to the built output (build/factory/registry.js → build/factory/manifest.yaml)
|
|
15
|
+
setModuleDir(dirname(fileURLToPath(import.meta.url)));
|
|
16
|
+
export const manifest = loadManifest();
|
|
17
|
+
export const generatedTools = generateTools(manifest, patches);
|
|
18
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/factory/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAIvC,iEAAiE;AACjE,yFAAyF;AACzF,YAAY,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAEtD,MAAM,CAAC,MAAM,QAAQ,GAAa,YAAY,EAAE,CAAC;AACjD,MAAM,CAAC,MAAM,cAAc,GAAoB,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safety policies — interceptors for destructive or sensitive operations.
|
|
3
|
+
*
|
|
4
|
+
* These run as beforeExecute hooks in the factory pipeline, before any
|
|
5
|
+
* service-specific patches. They provide a cross-service safety layer
|
|
6
|
+
* that can:
|
|
7
|
+
*
|
|
8
|
+
* - Block operations entirely (throw)
|
|
9
|
+
* - Downgrade operations (send → draft)
|
|
10
|
+
* - Require confirmation context (params that prove intent)
|
|
11
|
+
* - Log/audit destructive actions
|
|
12
|
+
*
|
|
13
|
+
* Policies are composable — multiple can apply to the same operation.
|
|
14
|
+
* They're configured per-deployment, not per-service.
|
|
15
|
+
*
|
|
16
|
+
* Example use cases:
|
|
17
|
+
* - "Draft-only mode" — agents can read email but not send
|
|
18
|
+
* - "No-delete mode" — prevent permanent deletion across all services
|
|
19
|
+
* - "Audit mode" — log all write operations to stderr
|
|
20
|
+
*/
|
|
21
|
+
import type { PatchContext } from './types.js';
|
|
22
|
+
/** Policy decision: what to do with an intercepted operation. */
|
|
23
|
+
export type PolicyAction = 'allow' | 'block' | 'downgrade';
|
|
24
|
+
/** Result of a policy check. */
|
|
25
|
+
export interface PolicyResult {
|
|
26
|
+
action: PolicyAction;
|
|
27
|
+
reason?: string;
|
|
28
|
+
/** For 'downgrade': replacement args to use instead. */
|
|
29
|
+
replacementArgs?: string[];
|
|
30
|
+
}
|
|
31
|
+
/** A safety policy that evaluates an operation before execution. */
|
|
32
|
+
export interface SafetyPolicy {
|
|
33
|
+
name: string;
|
|
34
|
+
description: string;
|
|
35
|
+
/** Which service.operation combinations this policy applies to. */
|
|
36
|
+
applies: (service: string, operation: string) => boolean;
|
|
37
|
+
/** Evaluate the operation and return a policy decision. */
|
|
38
|
+
evaluate: (args: string[], ctx: PatchContext, service: string) => PolicyResult;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Draft-only email policy — blocks send/reply/forward, allows everything else.
|
|
42
|
+
* Agents can read, search, triage, and label emails but cannot send on behalf of the user.
|
|
43
|
+
*/
|
|
44
|
+
export declare const draftOnlyEmail: SafetyPolicy;
|
|
45
|
+
/**
|
|
46
|
+
* No-delete policy — blocks permanent deletion across all services.
|
|
47
|
+
* Trash is allowed (reversible), but delete is blocked (permanent).
|
|
48
|
+
*/
|
|
49
|
+
export declare const noDelete: SafetyPolicy;
|
|
50
|
+
/**
|
|
51
|
+
* Read-only policy — blocks all write operations across all services.
|
|
52
|
+
* Only list, get, search, and read operations are allowed.
|
|
53
|
+
*/
|
|
54
|
+
export declare const readOnly: SafetyPolicy;
|
|
55
|
+
/**
|
|
56
|
+
* Audit policy — allows everything but logs destructive operations to stderr.
|
|
57
|
+
* Useful for monitoring what an agent does without blocking it.
|
|
58
|
+
*/
|
|
59
|
+
export declare const auditLog: SafetyPolicy;
|
|
60
|
+
/** Set the active safety policies. */
|
|
61
|
+
export declare function configurePolicies(policies: SafetyPolicy[]): void;
|
|
62
|
+
/** Get the active policies (defensive copy). */
|
|
63
|
+
export declare function getActivePolicies(): SafetyPolicy[];
|
|
64
|
+
/**
|
|
65
|
+
* Run all active policies against an operation.
|
|
66
|
+
* First block wins. Returns the most restrictive result.
|
|
67
|
+
*/
|
|
68
|
+
export declare function evaluatePolicies(args: string[], ctx: PatchContext, service: string): PolicyResult;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safety policies — interceptors for destructive or sensitive operations.
|
|
3
|
+
*
|
|
4
|
+
* These run as beforeExecute hooks in the factory pipeline, before any
|
|
5
|
+
* service-specific patches. They provide a cross-service safety layer
|
|
6
|
+
* that can:
|
|
7
|
+
*
|
|
8
|
+
* - Block operations entirely (throw)
|
|
9
|
+
* - Downgrade operations (send → draft)
|
|
10
|
+
* - Require confirmation context (params that prove intent)
|
|
11
|
+
* - Log/audit destructive actions
|
|
12
|
+
*
|
|
13
|
+
* Policies are composable — multiple can apply to the same operation.
|
|
14
|
+
* They're configured per-deployment, not per-service.
|
|
15
|
+
*
|
|
16
|
+
* Example use cases:
|
|
17
|
+
* - "Draft-only mode" — agents can read email but not send
|
|
18
|
+
* - "No-delete mode" — prevent permanent deletion across all services
|
|
19
|
+
* - "Audit mode" — log all write operations to stderr
|
|
20
|
+
*/
|
|
21
|
+
// ── Built-in policies ────────────────────────────────────────────────
|
|
22
|
+
/**
|
|
23
|
+
* Draft-only email policy — blocks send/reply/forward, allows everything else.
|
|
24
|
+
* Agents can read, search, triage, and label emails but cannot send on behalf of the user.
|
|
25
|
+
*/
|
|
26
|
+
export const draftOnlyEmail = {
|
|
27
|
+
name: 'draft-only-email',
|
|
28
|
+
description: 'Block outbound email — agents can read but not send',
|
|
29
|
+
applies: (service) => service === 'gmail',
|
|
30
|
+
evaluate: (_args, ctx) => {
|
|
31
|
+
const blocked = ['send', 'reply', 'replyAll', 'forward'];
|
|
32
|
+
if (blocked.includes(ctx.operation)) {
|
|
33
|
+
return {
|
|
34
|
+
action: 'block',
|
|
35
|
+
reason: `Operation '${ctx.operation}' is blocked by draft-only email policy. ` +
|
|
36
|
+
`The agent can read, search, and triage emails but cannot send on behalf of the user.`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return { action: 'allow' };
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* No-delete policy — blocks permanent deletion across all services.
|
|
44
|
+
* Trash is allowed (reversible), but delete is blocked (permanent).
|
|
45
|
+
*/
|
|
46
|
+
export const noDelete = {
|
|
47
|
+
name: 'no-delete',
|
|
48
|
+
description: 'Block permanent deletion — trash is allowed, delete is not',
|
|
49
|
+
applies: () => true,
|
|
50
|
+
evaluate: (_args, ctx, service) => {
|
|
51
|
+
// Drive delete is permanent (no trash via this operation)
|
|
52
|
+
// Task/tasklist delete is permanent
|
|
53
|
+
// Calendar delete is permanent
|
|
54
|
+
const permanentDeletes = {
|
|
55
|
+
gmail: [], // 'trash' is reversible, which is fine
|
|
56
|
+
calendar: ['delete'],
|
|
57
|
+
drive: ['delete'],
|
|
58
|
+
tasks: ['delete', 'deleteTaskList'],
|
|
59
|
+
docs: [],
|
|
60
|
+
sheets: [],
|
|
61
|
+
};
|
|
62
|
+
const blocked = permanentDeletes[service] ?? [];
|
|
63
|
+
if (blocked.includes(ctx.operation)) {
|
|
64
|
+
return {
|
|
65
|
+
action: 'block',
|
|
66
|
+
reason: `Operation '${ctx.operation}' on ${service} is blocked by no-delete policy. ` +
|
|
67
|
+
`This operation permanently destroys data. Use trash/archive instead where available.`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return { action: 'allow' };
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Classify an operation as read-only by name pattern.
|
|
75
|
+
* Uses prefix/name matching so new operations are automatically classified
|
|
76
|
+
* without maintaining a hardcoded list.
|
|
77
|
+
*/
|
|
78
|
+
function isReadOperation(operation) {
|
|
79
|
+
const op = operation.toLowerCase();
|
|
80
|
+
// Prefix patterns: get*, list*, search*
|
|
81
|
+
if (op.startsWith('get') || op.startsWith('list') || op.startsWith('search'))
|
|
82
|
+
return true;
|
|
83
|
+
// Exact read-only names
|
|
84
|
+
const readOps = ['read', 'triage', 'labels', 'threads', 'agenda', 'calendars', 'freebusy'];
|
|
85
|
+
return readOps.includes(op);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Read-only policy — blocks all write operations across all services.
|
|
89
|
+
* Only list, get, search, and read operations are allowed.
|
|
90
|
+
*/
|
|
91
|
+
export const readOnly = {
|
|
92
|
+
name: 'read-only',
|
|
93
|
+
description: 'Block all write operations — observation only',
|
|
94
|
+
applies: () => true,
|
|
95
|
+
evaluate: (_args, ctx) => {
|
|
96
|
+
if (!isReadOperation(ctx.operation)) {
|
|
97
|
+
return {
|
|
98
|
+
action: 'block',
|
|
99
|
+
reason: `Operation '${ctx.operation}' is blocked by read-only policy. ` +
|
|
100
|
+
`Only search, list, read, and get operations are allowed.`,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return { action: 'allow' };
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Audit policy — allows everything but logs destructive operations to stderr.
|
|
108
|
+
* Useful for monitoring what an agent does without blocking it.
|
|
109
|
+
*/
|
|
110
|
+
export const auditLog = {
|
|
111
|
+
name: 'audit-log',
|
|
112
|
+
description: 'Log all write operations to stderr — no blocking',
|
|
113
|
+
applies: () => true,
|
|
114
|
+
evaluate: (_args, ctx, service) => {
|
|
115
|
+
if (!isReadOperation(ctx.operation)) {
|
|
116
|
+
process.stderr.write(`[gws-mcp] AUDIT: ${service}.${ctx.operation} account=${ctx.account} ` +
|
|
117
|
+
`args=${JSON.stringify(ctx.params)}\n`);
|
|
118
|
+
}
|
|
119
|
+
return { action: 'allow' };
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
// ── Policy engine ────────────────────────────────────────────────────
|
|
123
|
+
/** Active policies — configured at startup. */
|
|
124
|
+
let activePolicies = [];
|
|
125
|
+
/** Set the active safety policies. */
|
|
126
|
+
export function configurePolicies(policies) {
|
|
127
|
+
activePolicies = policies;
|
|
128
|
+
if (policies.length > 0) {
|
|
129
|
+
process.stderr.write(`[gws-mcp] safety: ${policies.length} policy(ies) active: ` +
|
|
130
|
+
`${policies.map(p => p.name).join(', ')}\n`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/** Get the active policies (defensive copy). */
|
|
134
|
+
export function getActivePolicies() {
|
|
135
|
+
return [...activePolicies];
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Run all active policies against an operation.
|
|
139
|
+
* First block wins. Returns the most restrictive result.
|
|
140
|
+
*/
|
|
141
|
+
export function evaluatePolicies(args, ctx, service) {
|
|
142
|
+
for (const policy of activePolicies) {
|
|
143
|
+
if (!policy.applies(service, ctx.operation))
|
|
144
|
+
continue;
|
|
145
|
+
const result = policy.evaluate(args, ctx, service);
|
|
146
|
+
if (result.action === 'block') {
|
|
147
|
+
process.stderr.write(`[gws-mcp] safety: BLOCKED ${service}.${ctx.operation} by ${policy.name}: ${result.reason}\n`);
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
if (result.action === 'downgrade' && result.replacementArgs) {
|
|
151
|
+
process.stderr.write(`[gws-mcp] safety: DOWNGRADED ${service}.${ctx.operation} by ${policy.name}: ${result.reason}\n`);
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return { action: 'allow' };
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=safety.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safety.js","sourceRoot":"","sources":["../../src/factory/safety.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory type system — defines the manifest schema, patch hooks,
|
|
3
|
+
* and generated tool shapes for the service factory (ADR-300).
|
|
4
|
+
*/
|
|
5
|
+
import type { HandlerResponse } from '../server/formatting/markdown.js';
|
|
6
|
+
/** Parameter definition in the manifest. */
|
|
7
|
+
export interface ParamDef {
|
|
8
|
+
type: 'string' | 'number' | 'boolean';
|
|
9
|
+
description: string;
|
|
10
|
+
required?: boolean;
|
|
11
|
+
default?: string | number | boolean;
|
|
12
|
+
max?: number;
|
|
13
|
+
/** Maps this param name to a different key in the gws --params JSON. */
|
|
14
|
+
maps_to?: string;
|
|
15
|
+
enum?: string[];
|
|
16
|
+
}
|
|
17
|
+
/** Hydration config — fetch detail for each item in a list result. */
|
|
18
|
+
export interface HydrationDef {
|
|
19
|
+
/** gws resource path for the detail call (e.g. "users.messages.get"). */
|
|
20
|
+
resource: string;
|
|
21
|
+
/** Format parameter passed to the detail call. */
|
|
22
|
+
format?: string;
|
|
23
|
+
/** Headers to extract from payload (gmail-specific). */
|
|
24
|
+
headers?: string[];
|
|
25
|
+
}
|
|
26
|
+
/** A single operation within a service. */
|
|
27
|
+
export interface OperationDef {
|
|
28
|
+
/** Categorizes the operation for default formatting. */
|
|
29
|
+
type: 'list' | 'detail' | 'action';
|
|
30
|
+
description: string;
|
|
31
|
+
/** gws resource path (e.g. "users.messages.list"). */
|
|
32
|
+
resource?: string;
|
|
33
|
+
/** gws helper shorthand (e.g. "+triage", "+send"). */
|
|
34
|
+
helper?: string;
|
|
35
|
+
/** Named parameters the caller can provide. */
|
|
36
|
+
params?: Record<string, ParamDef>;
|
|
37
|
+
/** Default values merged into the gws --params JSON. */
|
|
38
|
+
defaults?: Record<string, unknown>;
|
|
39
|
+
/** Post-fetch hydration (list operations only). */
|
|
40
|
+
hydration?: HydrationDef;
|
|
41
|
+
/** Fields to extract from the gws response for the result. */
|
|
42
|
+
fields?: string;
|
|
43
|
+
/** CLI-style args instead of --params JSON (for helpers). */
|
|
44
|
+
cli_args?: Record<string, string>;
|
|
45
|
+
}
|
|
46
|
+
/** A service declaration in the manifest. */
|
|
47
|
+
export interface ServiceDef {
|
|
48
|
+
tool_name: string;
|
|
49
|
+
description: string;
|
|
50
|
+
/** Whether this service requires an email account param. */
|
|
51
|
+
requires_email: boolean;
|
|
52
|
+
/** The gws service name (e.g. "gmail", "calendar", "drive"). */
|
|
53
|
+
gws_service: string;
|
|
54
|
+
operations: Record<string, OperationDef>;
|
|
55
|
+
}
|
|
56
|
+
/** Top-level manifest shape. */
|
|
57
|
+
export interface Manifest {
|
|
58
|
+
services: Record<string, ServiceDef>;
|
|
59
|
+
}
|
|
60
|
+
/** Context passed to patch hooks. */
|
|
61
|
+
export interface PatchContext {
|
|
62
|
+
operation: string;
|
|
63
|
+
params: Record<string, unknown>;
|
|
64
|
+
account: string;
|
|
65
|
+
}
|
|
66
|
+
/** A patch hook that runs after arg construction, before gws execution. */
|
|
67
|
+
export type BeforeExecuteHook = (args: string[], ctx: PatchContext) => Promise<string[]> | string[];
|
|
68
|
+
/** A patch hook that runs after gws returns, before formatting. */
|
|
69
|
+
export type AfterExecuteHook = (result: unknown, ctx: PatchContext) => Promise<unknown> | unknown;
|
|
70
|
+
/** A formatter override for a specific operation type. */
|
|
71
|
+
export type FormatHook = (data: unknown, ctx: PatchContext) => HandlerResponse;
|
|
72
|
+
/** A next-steps override. */
|
|
73
|
+
export type NextStepsHook = (operation: string, context: Record<string, string>) => string;
|
|
74
|
+
/** Custom handler that completely replaces the factory for an operation. */
|
|
75
|
+
export type CustomHandler = (params: Record<string, unknown>, account: string) => Promise<HandlerResponse>;
|
|
76
|
+
/** Per-service patch — all hooks are optional. */
|
|
77
|
+
export interface ServicePatch {
|
|
78
|
+
/** Hooks keyed by operation name. */
|
|
79
|
+
beforeExecute?: Record<string, BeforeExecuteHook>;
|
|
80
|
+
afterExecute?: Record<string, AfterExecuteHook>;
|
|
81
|
+
/** Override formatting for list/detail/action response types. */
|
|
82
|
+
formatList?: FormatHook;
|
|
83
|
+
formatDetail?: FormatHook;
|
|
84
|
+
formatAction?: FormatHook;
|
|
85
|
+
/** Override next-steps generation. */
|
|
86
|
+
nextSteps?: NextStepsHook;
|
|
87
|
+
/** Completely custom handlers for operations that don't fit the factory pattern. */
|
|
88
|
+
customHandlers?: Record<string, CustomHandler>;
|
|
89
|
+
}
|
|
90
|
+
/** The tool schema exposed to MCP clients. */
|
|
91
|
+
export interface GeneratedToolSchema {
|
|
92
|
+
name: string;
|
|
93
|
+
description: string;
|
|
94
|
+
inputSchema: Record<string, unknown>;
|
|
95
|
+
}
|
|
96
|
+
/** A generated handler function. */
|
|
97
|
+
export type GeneratedHandler = (params: Record<string, unknown>) => Promise<HandlerResponse>;
|
|
98
|
+
/** Complete generated tool — schema + handler, ready to register. */
|
|
99
|
+
export interface GeneratedTool {
|
|
100
|
+
schema: GeneratedToolSchema;
|
|
101
|
+
handler: GeneratedHandler;
|
|
102
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/factory/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yaml.js","sourceRoot":"","sources":["../../src/factory/yaml.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC"}
|
package/build/index.d.ts
ADDED
package/build/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { startServer } from './server/server.js';
|
|
3
|
+
// Prevent unhandled rejections from crashing the server
|
|
4
|
+
process.on('unhandledRejection', (reason) => {
|
|
5
|
+
process.stderr.write(`[gws-mcp] unhandled rejection: ${reason}\n`);
|
|
6
|
+
});
|
|
7
|
+
process.on('uncaughtException', (err) => {
|
|
8
|
+
process.stderr.write(`[gws-mcp] uncaught exception: ${err.message}\n${err.stack}\n`);
|
|
9
|
+
});
|
|
10
|
+
startServer().catch((err) => {
|
|
11
|
+
process.stderr.write(`[gws-mcp] fatal: ${err.message}\n`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,wDAAwD;AACxD,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,EAAE;IAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,MAAM,IAAI,CAAC,CAAC;AACrE,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,GAAG,EAAE,EAAE;IACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iCAAiC,GAAG,CAAC,OAAO,KAAK,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC;AACvF,CAAC,CAAC,CAAC;AAEH,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;IAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response formatters — shape raw gws JSON into token-efficient
|
|
3
|
+
* markdown for AI consumption.
|
|
4
|
+
*
|
|
5
|
+
* Design:
|
|
6
|
+
* - Lists are compact and scannable (pipe-delimited, IDs included)
|
|
7
|
+
* - Detail views are natural prose an agent can relay to a user
|
|
8
|
+
* - Each formatter returns { text, refs } where refs are the
|
|
9
|
+
* structured values queue $N.field resolution needs
|
|
10
|
+
*/
|
|
11
|
+
/** Shared response shape — markdown text for agents, structured refs for queue $N.field. */
|
|
12
|
+
export interface HandlerResponse {
|
|
13
|
+
text: string;
|
|
14
|
+
refs: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
export declare function formatEmailList(data: unknown): HandlerResponse;
|
|
17
|
+
export declare function formatEmailDetail(data: unknown): HandlerResponse;
|
|
18
|
+
export declare function formatEventList(data: unknown): HandlerResponse;
|
|
19
|
+
export declare function formatEventDetail(data: unknown): HandlerResponse;
|
|
20
|
+
export declare function formatFileList(data: unknown): HandlerResponse;
|
|
21
|
+
export declare function formatFileDetail(data: unknown): HandlerResponse;
|