@adtl/gateway 0.5.1 → 0.7.0

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 (102) hide show
  1. package/README.md +152 -9
  2. package/dist/approvals.d.ts +22 -6
  3. package/dist/approvals.d.ts.map +1 -1
  4. package/dist/approvals.js +56 -26
  5. package/dist/approvals.js.map +1 -1
  6. package/dist/audit.d.ts +8 -0
  7. package/dist/audit.d.ts.map +1 -1
  8. package/dist/audit.js.map +1 -1
  9. package/dist/auditCli.d.ts +2 -0
  10. package/dist/auditCli.d.ts.map +1 -1
  11. package/dist/auditCli.js +13 -10
  12. package/dist/auditCli.js.map +1 -1
  13. package/dist/cedar.d.ts +27 -0
  14. package/dist/cedar.d.ts.map +1 -0
  15. package/dist/cedar.js +134 -0
  16. package/dist/cedar.js.map +1 -0
  17. package/dist/cli.js +17 -1
  18. package/dist/cli.js.map +1 -1
  19. package/dist/config.d.ts +330 -21
  20. package/dist/config.d.ts.map +1 -1
  21. package/dist/config.js +74 -1
  22. package/dist/config.js.map +1 -1
  23. package/dist/elicitation.d.ts +25 -0
  24. package/dist/elicitation.d.ts.map +1 -0
  25. package/dist/elicitation.js +46 -0
  26. package/dist/elicitation.js.map +1 -0
  27. package/dist/gateway.d.ts +30 -5
  28. package/dist/gateway.d.ts.map +1 -1
  29. package/dist/gateway.js +96 -14
  30. package/dist/gateway.js.map +1 -1
  31. package/dist/hooks/claudeCode.d.ts +4 -0
  32. package/dist/hooks/claudeCode.d.ts.map +1 -1
  33. package/dist/hooks/claudeCode.js +8 -1
  34. package/dist/hooks/claudeCode.js.map +1 -1
  35. package/dist/hooks/run.d.ts.map +1 -1
  36. package/dist/hooks/run.js +6 -3
  37. package/dist/hooks/run.js.map +1 -1
  38. package/dist/index.d.ts +6 -0
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +6 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/init/presets.d.ts.map +1 -1
  43. package/dist/init/presets.js +54 -0
  44. package/dist/init/presets.js.map +1 -1
  45. package/dist/invoker.d.ts +21 -1
  46. package/dist/invoker.d.ts.map +1 -1
  47. package/dist/invoker.js +19 -2
  48. package/dist/invoker.js.map +1 -1
  49. package/dist/ledgerCli.d.ts +1 -1
  50. package/dist/ledgerCli.d.ts.map +1 -1
  51. package/dist/ledgerCli.js +9 -3
  52. package/dist/ledgerCli.js.map +1 -1
  53. package/dist/ntfy.d.ts +3 -1
  54. package/dist/ntfy.d.ts.map +1 -1
  55. package/dist/ntfy.js +2 -1
  56. package/dist/ntfy.js.map +1 -1
  57. package/dist/playgroundCli.d.ts +8 -0
  58. package/dist/playgroundCli.d.ts.map +1 -0
  59. package/dist/playgroundCli.js +57 -0
  60. package/dist/playgroundCli.js.map +1 -0
  61. package/dist/receipt.d.ts +62 -0
  62. package/dist/receipt.d.ts.map +1 -0
  63. package/dist/receipt.js +117 -0
  64. package/dist/receipt.js.map +1 -0
  65. package/dist/receiptCli.d.ts +8 -0
  66. package/dist/receiptCli.d.ts.map +1 -0
  67. package/dist/receiptCli.js +87 -0
  68. package/dist/receiptCli.js.map +1 -0
  69. package/dist/roles.d.ts +9 -0
  70. package/dist/roles.d.ts.map +1 -0
  71. package/dist/roles.js +41 -0
  72. package/dist/roles.js.map +1 -0
  73. package/dist/startup.d.ts +14 -0
  74. package/dist/startup.d.ts.map +1 -1
  75. package/dist/startup.js +92 -7
  76. package/dist/startup.js.map +1 -1
  77. package/package.json +16 -4
  78. package/playground/fake-github.mjs +98 -0
  79. package/playground/fake-memory.mjs +130 -0
  80. package/playground/index.html +411 -0
  81. package/playground/playground.yaml +40 -0
  82. package/playground/server.mjs +290 -0
  83. package/src/approvals.ts +63 -26
  84. package/src/audit.ts +5 -0
  85. package/src/auditCli.ts +11 -8
  86. package/src/cedar.ts +140 -0
  87. package/src/cli.ts +16 -1
  88. package/src/config.ts +80 -1
  89. package/src/elicitation.ts +69 -0
  90. package/src/gateway.ts +108 -17
  91. package/src/hooks/claudeCode.ts +13 -2
  92. package/src/hooks/run.ts +6 -3
  93. package/src/index.ts +6 -0
  94. package/src/init/presets.ts +54 -0
  95. package/src/invoker.ts +39 -2
  96. package/src/ledgerCli.ts +9 -3
  97. package/src/ntfy.ts +4 -1
  98. package/src/playgroundCli.ts +60 -0
  99. package/src/receipt.ts +148 -0
  100. package/src/receiptCli.ts +85 -0
  101. package/src/roles.ts +55 -0
  102. package/src/startup.ts +88 -6
package/dist/config.js CHANGED
@@ -12,15 +12,22 @@
12
12
  * docs:
13
13
  * url: https://example.com/mcp
14
14
  * headers: { Authorization: "Bearer ${DOCS_TOKEN}" }
15
+ * principal: { id: "${USER}" } # optional: who runs this gateway (default: anonymous)
16
+ * roles: # optional: who may use which tools (deny by default)
17
+ * maintainers: { members: [alice], tools: ["github.*"] }
18
+ * readers: { members: ["*"], tools: ["github.read_*", "github.get_*"] }
19
+ * cedar: { policies: ./adtl.cedar } # optional: attribute-based rules (see cedar.ts)
15
20
  * approvals: # optional: make "ask" really ask
16
21
  * timeoutSeconds: 50
17
22
  * ntfy: { topic: "${ADTL_NTFY_TOPIC}" }
23
+ * approvers: # optional: each approver's own channel (policy rule `approvers:`)
24
+ * alice: { ntfy: { topic: "${ALICE_NTFY_TOPIC}" } }
18
25
  * email:
19
26
  * smtp: { host: smtp.example.com, port: 465, user: "${SMTP_USER}", pass: "${SMTP_PASS}" }
20
27
  * from: adtl@example.com
21
28
  * to: you@example.com
22
29
  *
23
- * `${VAR}` in command, args, env, cwd, url, headers and approval settings is
30
+ * `${VAR}` in command, args, env, cwd, url, headers, approval settings and principal.id is
24
31
  * replaced from the gateway's environment. A missing variable is a config error,
25
32
  * unless a default is given: `${VAR:-default}`.
26
33
  */
@@ -78,6 +85,63 @@ export const ApprovalsConfigSchema = z
78
85
  timeoutSeconds: z.number().int().positive().max(3600).default(50),
79
86
  ntfy: NtfyConfigSchema.optional(),
80
87
  email: EmailConfigSchema.optional(),
88
+ /**
89
+ * Also ask in the MCP client (elicitation), when it supports it. Off by
90
+ * default: a client that accepts elicitations by itself would approve calls.
91
+ */
92
+ elicitation: z.boolean().default(false),
93
+ /**
94
+ * Each approver's own channel, by principal id. Policy rules with
95
+ * `approvers: [role]` ask only these people, so the answer names who approved.
96
+ */
97
+ approvers: z.record(z.object({ ntfy: NtfyConfigSchema }).strict()).optional(),
98
+ })
99
+ .strict();
100
+ /** `server.tool` (or `server.prompt`), with `*` wildcards, as in policy rules. */
101
+ const QualifiedPattern = z
102
+ .string()
103
+ .min(1)
104
+ .refine((p) => /^[^.]+\..+$/.test(p), { message: 'Use "server.name" with optional * wildcards, e.g. "github.read_*" or "github.*"' });
105
+ export const PrincipalConfigSchema = z
106
+ .object({
107
+ type: z.enum(["user", "service"]).default("user"),
108
+ id: z.string().min(1),
109
+ claims: z.record(z.unknown()).optional(),
110
+ })
111
+ .strict();
112
+ export const RoleSchema = z
113
+ .object({
114
+ /** Principal ids; "*" means everyone, including an anonymous caller. */
115
+ members: z.array(z.string().min(1)).nonempty("A role needs at least one member (\"*\" for everyone)"),
116
+ tools: z.array(QualifiedPattern).default([]),
117
+ prompts: z.array(QualifiedPattern).default([]),
118
+ /** Resource URI patterns, e.g. "github://acme/*". */
119
+ resources: z.array(z.string().min(1)).default([]),
120
+ })
121
+ .strict()
122
+ .refine((r) => r.tools.length + r.prompts.length + r.resources.length > 0, {
123
+ message: "A role must grant at least one of tools, prompts or resources",
124
+ });
125
+ export const CedarConfigSchema = z
126
+ .object({
127
+ /** Cedar policy file. Relative paths are resolved against the config file's directory. */
128
+ policies: z.string().min(1),
129
+ /** Optional Cedar entities JSON file: groups (`parents`) and attributes. */
130
+ entities: z.string().min(1).optional(),
131
+ /** IANA time zone for `context.time`, e.g. "Europe/Berlin". Default: the gateway's. */
132
+ timeZone: z
133
+ .string()
134
+ .min(1)
135
+ .refine((tz) => {
136
+ try {
137
+ new Intl.DateTimeFormat("en-US", { timeZone: tz });
138
+ return true;
139
+ }
140
+ catch {
141
+ return false;
142
+ }
143
+ }, "Unknown time zone")
144
+ .optional(),
81
145
  })
82
146
  .strict();
83
147
  export const GatewayConfigSchema = z
@@ -101,6 +165,15 @@ export const GatewayConfigSchema = z
101
165
  approvals: ApprovalsConfigSchema.optional(),
102
166
  /** Timeout for each interceptor invocation, in milliseconds. */
103
167
  interceptorTimeoutMs: z.number().int().positive().default(5000),
168
+ /** Who runs this gateway. Passed to the authorizer and the interceptors. Default: anonymous. */
169
+ principal: PrincipalConfigSchema.optional(),
170
+ /** Role-based authorization: when present, a call no role grants is denied. */
171
+ roles: z
172
+ .record(RoleSchema)
173
+ .refine((r) => Object.keys(r).length > 0, "roles: needs at least one role (remove it to allow everyone)")
174
+ .optional(),
175
+ /** Cedar authorization. With roles as well, both must allow. */
176
+ cedar: CedarConfigSchema.optional(),
104
177
  })
105
178
  .strict();
106
179
  export const TOOL_SEPARATOR = "__";
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Approvals inside the MCP client, through `elicitation/create`: the person
3
+ * using the client (for example Claude Code) sees the request and answers
4
+ * Allow or Deny there, no phone needed.
5
+ *
6
+ * The answer is turned into the same signed token a push button would send,
7
+ * so it answers only the one pending call it was asked about. Declining or
8
+ * dismissing the prompt counts as Deny. The gateway uses this channel only
9
+ * when `gateway.approvals.elicitation` is on and the client declares the
10
+ * elicitation capability, and never for rules with named approvers: the
11
+ * client can't say who answered.
12
+ */
13
+ import type { Server } from "@modelcontextprotocol/sdk/server/index.js";
14
+ import type { AnswerFn, ApprovalEvent, ApprovalRequest, Notifier } from "./approvals.js";
15
+ /** True if the connected client can answer elicitation requests. */
16
+ export declare function supportsElicitation(server: Server): boolean;
17
+ export declare class ElicitationChannel implements Notifier {
18
+ private readonly server;
19
+ readonly name = "elicitation";
20
+ private readonly listening;
21
+ constructor(server: Server);
22
+ listen(request: ApprovalRequest, answer: AnswerFn, signal: AbortSignal): Promise<void>;
23
+ notify(event: ApprovalEvent): Promise<void>;
24
+ }
25
+ //# sourceMappingURL=elicitation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elicitation.d.ts","sourceRoot":"","sources":["../src/elicitation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAExE,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAOzF,oEAAoE;AACpE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE3D;AAOD,qBAAa,kBAAmB,YAAW,QAAQ;IAIrC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,QAAQ,CAAC,IAAI,iBAAiB;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgC;gBAE7B,MAAM,EAAE,MAAM;IAErC,MAAM,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtF,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAyBlD"}
@@ -0,0 +1,46 @@
1
+ import { ElicitResultSchema } from "@modelcontextprotocol/sdk/types.js";
2
+ /** True if the connected client can answer elicitation requests. */
3
+ export function supportsElicitation(server) {
4
+ return server.getClientCapabilities()?.elicitation !== undefined;
5
+ }
6
+ function message(request) {
7
+ const rule = request.matched.join(", ") || "default";
8
+ return `ADTL: allow ${request.server}.${request.tool}?\n\n${request.reason}\n\nRule: ${rule}. Allow runs this one call; Deny or no answer stops it.`;
9
+ }
10
+ export class ElicitationChannel {
11
+ server;
12
+ name = "elicitation";
13
+ listening = new Map();
14
+ constructor(server) {
15
+ this.server = server;
16
+ }
17
+ async listen(request, answer, signal) {
18
+ this.listening.set(request.id, { answer, signal });
19
+ await new Promise((resolve) => signal.addEventListener("abort", () => resolve(), { once: true }));
20
+ this.listening.delete(request.id);
21
+ }
22
+ async notify(event) {
23
+ if (event.kind !== "pending")
24
+ return; // the client sees the call's result; no other notices
25
+ const l = this.listening.get(event.request.id);
26
+ if (!l)
27
+ throw new Error("not listening for this request");
28
+ // A plain request, not Server.elicitInput(): clients that declare only `elicitation: {}` support forms too.
29
+ const result = await this.server.request({
30
+ method: "elicitation/create",
31
+ params: {
32
+ message: message(event.request),
33
+ requestedSchema: {
34
+ type: "object",
35
+ properties: {
36
+ decision: { type: "string", title: "Decision", enum: ["allow", "deny"], description: "allow runs this one call; deny stops it" },
37
+ },
38
+ required: ["decision"],
39
+ },
40
+ },
41
+ }, ElicitResultSchema, { signal: l.signal, timeout: Math.max(1, event.request.expiresAt.getTime() - Date.now()) });
42
+ const allow = result.action === "accept" && result.content?.decision === "allow";
43
+ l.answer(allow ? event.tokens.allow : event.tokens.deny);
44
+ }
45
+ }
46
+ //# sourceMappingURL=elicitation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elicitation.js","sourceRoot":"","sources":["../src/elicitation.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAQxE,oEAAoE;AACpE,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,OAAO,MAAM,CAAC,qBAAqB,EAAE,EAAE,WAAW,KAAK,SAAS,CAAC;AACnE,CAAC;AAED,SAAS,OAAO,CAAC,OAAwB;IACvC,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC;IACrD,OAAO,eAAe,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,QAAQ,OAAO,CAAC,MAAM,aAAa,IAAI,yDAAyD,CAAC;AACvJ,CAAC;AAED,MAAM,OAAO,kBAAkB;IAIA;IAHpB,IAAI,GAAG,aAAa,CAAC;IACb,SAAS,GAAG,IAAI,GAAG,EAAqB,CAAC;IAE1D,YAA6B,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IAAG,CAAC;IAE/C,KAAK,CAAC,MAAM,CAAC,OAAwB,EAAE,MAAgB,EAAE,MAAmB;QAC1E,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACnD,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACxG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAoB;QAC/B,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,CAAC,sDAAsD;QAC5F,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC/C,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAC1D,4GAA4G;QAC5G,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CACtC;YACE,MAAM,EAAE,oBAAoB;YAC5B,MAAM,EAAE;gBACN,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;gBAC/B,eAAe,EAAE;oBACf,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,EAAE,yCAAyC,EAAE;qBACjI;oBACD,QAAQ,EAAE,CAAC,UAAU,CAAC;iBACvB;aACF;SACF,EACD,kBAAkB,EAClB,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAC3F,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC;QACjF,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC;CACF"}
package/dist/gateway.d.ts CHANGED
@@ -5,7 +5,7 @@ import type { ApprovalBroker } from "./approvals.js";
5
5
  import type { AuditSink } from "./audit.js";
6
6
  import { type Authorizer, type Principal } from "./authorizer.js";
7
7
  import type { InterceptorInvoker } from "./invoker.js";
8
- export declare const GATEWAY_VERSION = "0.5.1";
8
+ export declare const GATEWAY_VERSION = "0.7.0";
9
9
  /** `_meta` key on gateway-generated error results describing why a call was stopped. */
10
10
  export declare const GATEWAY_DECISION_KEY = "adtl/gateway";
11
11
  export interface GatewayLogger {
@@ -21,11 +21,20 @@ export interface AdtlGatewayOptions {
21
21
  logger?: GatewayLogger;
22
22
  /** Asks humans about "ask" decisions and sends notices. */
23
23
  approvals?: ApprovalBroker;
24
- /** Decides whether the caller may use a tool, resource or prompt at all, before the policy. */
25
- authorizer?: Authorizer;
24
+ /** Decides whether the caller may use a tool, resource or prompt at all, before the policy. Several must all allow. */
25
+ authorizer?: Authorizer | Authorizer[];
26
26
  /** The caller, passed to the authorizer and the interceptors. Default: anonymous. */
27
27
  principal?: Principal;
28
+ /** Members of each role that policy rules name as `approvers:`. */
29
+ approverRoles?: Map<string, string[]>;
30
+ /** Also ask for approval in the MCP client, when it supports elicitation. */
31
+ elicitApprovals?: boolean;
28
32
  }
33
+ /**
34
+ * Who may answer an "ask" whose rules name approver roles: someone in one of
35
+ * the roles of every list. Sorted, for stable notifications and tests.
36
+ */
37
+ export declare function eligibleApprovers(lists: string[][], roles: Map<string, string[]>): string[];
29
38
  export interface CallOptions {
30
39
  /** Report progress to the client while a call waits for approval. */
31
40
  progress?: (progress: number, message: string) => Promise<void>;
@@ -37,20 +46,36 @@ export declare class AdtlGateway {
37
46
  private readonly audit;
38
47
  private readonly log;
39
48
  private readonly approvals;
40
- private readonly authorizer;
49
+ private readonly authorizers;
41
50
  private readonly principal;
51
+ private readonly approverRoles;
52
+ private readonly elicitApprovals;
53
+ /** The server the agent is connected to, for elicitation. */
54
+ private server;
42
55
  private routes;
43
56
  private promptRoutes;
44
57
  /** Resource URI → the upstreams that list it. */
45
58
  private resourceRoutes;
46
59
  private templateRoutes;
47
60
  constructor(opts: AdtlGatewayOptions);
48
- /** Ask the authorizer, if any. Returns the denial; in audit mode the caller logs it and carries on. */
61
+ /** Ask each authorizer in turn. Returns the first denial; in audit mode the caller logs it and carries on. */
49
62
  private authorize;
50
63
  /** The downstream MCP server the agent connects to. */
51
64
  createServer(): Server;
52
65
  /** Every upstream's items from one list method, following cursors. Upstreams that fail are skipped with a warning. */
53
66
  private listAll;
67
+ /** tools/list as the caller sees it: without the tools the authorizer would deny. */
68
+ visibleTools(): Promise<Tool[]>;
69
+ /** prompts/list as the caller sees it. */
70
+ visiblePrompts(): Promise<Prompt[]>;
71
+ /** resources/list as the caller sees it. A URI several upstreams offer is listed as before (reading it is refused). */
72
+ visibleResources(): Promise<Resource[]>;
73
+ /**
74
+ * Leave out items the authorizer would deny, asked without arguments. Not in
75
+ * audit mode, which changes nothing. Hiding only saves the agent a denied
76
+ * call: routes are kept, and a call to a hidden item is still authorized.
77
+ */
78
+ private hideDenied;
54
79
  listTools(): Promise<Tool[]>;
55
80
  listPrompts(): Promise<Prompt[]>;
56
81
  listResources(): Promise<Resource[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EAUL,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,MAAM,EACX,KAAK,kBAAkB,EACvB,KAAK,QAAQ,EACb,KAAK,gBAAgB,EAErB,KAAK,IAAI,EACV,MAAM,oCAAoC,CAAC;AAG5C,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,gBAAgB,CAAC;AACrE,OAAO,KAAK,EAAc,SAAS,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAwC,KAAK,UAAU,EAAe,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAErH,OAAO,KAAK,EAAyB,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAE9E,eAAO,MAAM,eAAe,UAAU,CAAC;AAEvC,wFAAwF;AACxF,eAAO,MAAM,oBAAoB,iBAAiB,CAAC;AAuBnD,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAOD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,+FAA+F;IAC/F,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,qFAAqF;IACrF,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACjE;AAmCD,qBAAa,WAAW;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsB;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwB;IAC9C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgB;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;IACvD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyB;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,YAAY,CAAkC;IACtD,iDAAiD;IACjD,OAAO,CAAC,cAAc,CAAkC;IACxD,OAAO,CAAC,cAAc,CAAuB;gBAEjC,IAAI,EAAE,kBAAkB;IAWpC,uGAAuG;YACzF,SAAS;IAIvB,uDAAuD;IACvD,YAAY,IAAI,MAAM;IAuCtB,sHAAsH;YACxG,OAAO;IAsBf,SAAS,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;IAe5B,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAehC,aAAa,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAkBpC,qBAAqB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAiB1D,wFAAwF;YAC1E,gBAAgB;IAkBxB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAQtD,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC;IAiBtF;;;;;OAKG;YACW,cAAc;IAyEtB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,cAAc,CAAC;IAoJ5G,gFAAgF;YAClE,eAAe;CAY9B"}
1
+ {"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"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"}
package/dist/gateway.js CHANGED
@@ -21,8 +21,10 @@
21
21
  * unanswered or undeliverable calls are stopped. Without an approval channel,
22
22
  * "ask" blocks.
23
23
  *
24
- * An optional Authorizer decides first whether the caller may use the tool,
25
- * resource or prompt at all; a denial stops the call before the policy runs.
24
+ * Optional Authorizers decide first whether the caller may use the tool,
25
+ * resource or prompt at all; any denial stops the call before the policy runs.
26
+ * The lists the agent sees leave out what an authorizer would deny, so the
27
+ * agent doesn't try it; hiding is a convenience, and every call is still checked.
26
28
  *
27
29
  * In audit mode (the invoker runs every interceptor with `mode: "audit"`)
28
30
  * nothing is stopped or changed: calls the policy would stop are forwarded,
@@ -33,15 +35,25 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
33
35
  import { UriTemplate } from "@modelcontextprotocol/sdk/shared/uriTemplate.js";
34
36
  import { CallToolRequestSchema, ErrorCode, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
35
37
  import { callBindingHash } from "@adtl/labels";
38
+ import { ElicitationChannel, supportsElicitation } from "./elicitation.js";
36
39
  import { authorize } from "./authorizer.js";
37
40
  import { TOOL_SEPARATOR } from "./config.js";
38
- export const GATEWAY_VERSION = "0.5.1";
41
+ export const GATEWAY_VERSION = "0.7.0";
39
42
  /** `_meta` key on gateway-generated error results describing why a call was stopped. */
40
43
  export const GATEWAY_DECISION_KEY = "adtl/gateway";
41
44
  const stderrLogger = {
42
45
  info: (m) => console.error(`[adtl-gateway] ${m}`),
43
46
  warn: (m) => console.error(`[adtl-gateway] warning: ${m}`),
44
47
  };
48
+ /**
49
+ * Who may answer an "ask" whose rules name approver roles: someone in one of
50
+ * the roles of every list. Sorted, for stable notifications and tests.
51
+ */
52
+ export function eligibleApprovers(lists, roles) {
53
+ const sets = lists.map((list) => new Set(list.flatMap((role) => roles.get(role) ?? [])));
54
+ const [first, ...rest] = sets;
55
+ return [...(first ?? [])].filter((id) => id !== "*" && rest.every((s) => s.has(id))).sort();
56
+ }
45
57
  /** How often a waiting call reports progress, so clients that reset their timeout on progress keep waiting. */
46
58
  const PROGRESS_INTERVAL_MS = 10_000;
47
59
  function textError(text, meta) {
@@ -75,8 +87,12 @@ export class AdtlGateway {
75
87
  audit;
76
88
  log;
77
89
  approvals;
78
- authorizer;
90
+ authorizers;
79
91
  principal;
92
+ approverRoles;
93
+ elicitApprovals;
94
+ /** The server the agent is connected to, for elicitation. */
95
+ server;
80
96
  routes = new Map();
81
97
  promptRoutes = new Map();
82
98
  /** Resource URI → the upstreams that list it. */
@@ -89,12 +105,19 @@ export class AdtlGateway {
89
105
  this.audit = opts.audit;
90
106
  this.log = opts.logger ?? stderrLogger;
91
107
  this.approvals = opts.approvals;
92
- this.authorizer = opts.authorizer;
108
+ this.authorizers = opts.authorizer === undefined ? [] : [opts.authorizer].flat();
93
109
  this.principal = opts.principal ?? { type: "anonymous" };
110
+ this.approverRoles = opts.approverRoles ?? new Map();
111
+ this.elicitApprovals = opts.elicitApprovals === true;
94
112
  }
95
- /** Ask the authorizer, if any. Returns the denial; in audit mode the caller logs it and carries on. */
113
+ /** Ask each authorizer in turn. Returns the first denial; in audit mode the caller logs it and carries on. */
96
114
  async authorize(request) {
97
- return this.authorizer ? authorize(this.authorizer, { principal: this.principal, ...request }) : undefined;
115
+ for (const a of this.authorizers) {
116
+ const denied = await authorize(a, { principal: this.principal, ...request });
117
+ if (denied)
118
+ return denied;
119
+ }
120
+ return undefined;
98
121
  }
99
122
  /** The downstream MCP server the agent connects to. */
100
123
  createServer() {
@@ -110,7 +133,7 @@ export class AdtlGateway {
110
133
  instructions: "Tools from several MCP servers, named <server>__<tool>. ADTL checks each call against trust labels; " +
111
134
  "if a call is stopped, the result explains why.",
112
135
  });
113
- server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: await this.listTools() }));
136
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: await this.visibleTools() }));
114
137
  server.setRequestHandler(CallToolRequestSchema, async (req, extra) => {
115
138
  const progressToken = req.params._meta?.progressToken;
116
139
  const progress = progressToken === undefined
@@ -119,16 +142,17 @@ export class AdtlGateway {
119
142
  return this.callTool(req.params.name, req.params.arguments ?? {}, progress ? { progress } : {});
120
143
  });
121
144
  if (capabilities.resources) {
122
- server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: await this.listResources() }));
145
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: await this.visibleResources() }));
123
146
  server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
124
147
  resourceTemplates: await this.listResourceTemplates(),
125
148
  }));
126
149
  server.setRequestHandler(ReadResourceRequestSchema, async (req) => this.readResource(req.params.uri));
127
150
  }
128
151
  if (capabilities.prompts) {
129
- server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: await this.listPrompts() }));
152
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: await this.visiblePrompts() }));
130
153
  server.setRequestHandler(GetPromptRequestSchema, async (req) => this.getPrompt(req.params.name, req.params.arguments));
131
154
  }
155
+ this.server = server;
132
156
  return server;
133
157
  }
134
158
  /** Every upstream's items from one list method, following cursors. Upstreams that fail are skipped with a warning. */
@@ -152,6 +176,44 @@ export class AdtlGateway {
152
176
  }
153
177
  return out;
154
178
  }
179
+ /** tools/list as the caller sees it: without the tools the authorizer would deny. */
180
+ async visibleTools() {
181
+ const tools = await this.listTools();
182
+ return this.hideDenied(tools, (t) => {
183
+ const r = this.routes.get(t.name);
184
+ return { event: "tools/call", server: r.upstream, tool: r.tool };
185
+ });
186
+ }
187
+ /** prompts/list as the caller sees it. */
188
+ async visiblePrompts() {
189
+ const prompts = await this.listPrompts();
190
+ return this.hideDenied(prompts, (p) => {
191
+ const r = this.promptRoutes.get(p.name);
192
+ return { event: "prompts/get", server: r.upstream, prompt: r.prompt };
193
+ });
194
+ }
195
+ /** resources/list as the caller sees it. A URI several upstreams offer is listed as before (reading it is refused). */
196
+ async visibleResources() {
197
+ const resources = await this.listResources();
198
+ return this.hideDenied(resources, (r) => {
199
+ const ups = this.resourceRoutes.get(r.uri);
200
+ return ups?.size === 1 ? { event: "resources/read", server: [...ups][0], resource: r.uri } : undefined;
201
+ });
202
+ }
203
+ /**
204
+ * Leave out items the authorizer would deny, asked without arguments. Not in
205
+ * audit mode, which changes nothing. Hiding only saves the agent a denied
206
+ * call: routes are kept, and a call to a hidden item is still authorized.
207
+ */
208
+ async hideDenied(items, request) {
209
+ if (!this.authorizers.length || this.invoker.modeOverride === "audit")
210
+ return items;
211
+ const shown = await Promise.all(items.map(async (item) => {
212
+ const r = request(item);
213
+ return r === undefined || (await this.authorize(r)) === undefined;
214
+ }));
215
+ return items.filter((_, i) => shown[i]);
216
+ }
155
217
  async listTools() {
156
218
  const all = await this.listAll("tools/list", "tools", async (c, cursor) => {
157
219
  const p = await c.listTools(cursor ? { cursor } : {});
@@ -266,6 +328,7 @@ export class AdtlGateway {
266
328
  const config = { server: upstream };
267
329
  const audit = this.invoker.modeOverride === "audit" ? { mode: "audit" } : {};
268
330
  let denied;
331
+ const ran = [];
269
332
  const finish = (entry) => this.audit?.write({
270
333
  ts: context.timestamp,
271
334
  sessionId: this.sessionId,
@@ -274,6 +337,7 @@ export class AdtlGateway {
274
337
  ...target,
275
338
  ...(denied ? { denied } : {}),
276
339
  ...entry,
340
+ ...(ran.length ? { interceptors: [...ran] } : {}),
277
341
  durationMs: Math.round(performance.now() - started),
278
342
  });
279
343
  const what = "resource" in target ? `resource ${target.resource}` : `prompt ${upstream}${TOOL_SEPARATOR}${target.prompt}`;
@@ -291,6 +355,7 @@ export class AdtlGateway {
291
355
  if (denied)
292
356
  this.log.info(`audit: would deny ${what}: ${denied.reason}`);
293
357
  const req = await this.invoker.runRequest({ event, payload: { method: event, params }, config, context });
358
+ ran.push(...req.ran);
294
359
  if (req.blocked) {
295
360
  finish({ outcome: "blocked", decision: req.blocked.decision, reason: req.blocked.reason, warnings: req.warnings });
296
361
  this.log.info(`${req.blocked.decision} ${what}: ${req.blocked.reason}`);
@@ -306,6 +371,7 @@ export class AdtlGateway {
306
371
  throw err;
307
372
  }
308
373
  const res = await this.invoker.runResponse({ event, payload: { method: event, params: forwarded, result }, config, context });
374
+ ran.push(...res.ran);
309
375
  const warnings = [...req.warnings, ...res.warnings];
310
376
  if (res.blocked) {
311
377
  // Fail closed: unlabeled content must not reach the agent.
@@ -340,7 +406,15 @@ export class AdtlGateway {
340
406
  };
341
407
  const base = { sessionId: this.sessionId, event: "tools/call", server: route.upstream, tool: route.tool };
342
408
  let denied;
343
- const finish = (entry) => this.audit?.write({ ts: context.timestamp, ...base, ...(denied ? { denied } : {}), ...entry, durationMs: Math.round(performance.now() - started) });
409
+ const ran = [];
410
+ const finish = (entry) => this.audit?.write({
411
+ ts: context.timestamp,
412
+ ...base,
413
+ ...(denied ? { denied } : {}),
414
+ ...entry,
415
+ ...(ran.length ? { interceptors: [...ran] } : {}),
416
+ durationMs: Math.round(performance.now() - started),
417
+ });
344
418
  // 0. Authorization: may this caller use this tool at all? A denial wins over any policy decision.
345
419
  denied = await this.authorize({ event: "tools/call", server: route.upstream, tool: route.tool, arguments: args });
346
420
  if (denied && this.invoker.modeOverride !== "audit") {
@@ -358,6 +432,7 @@ export class AdtlGateway {
358
432
  // 1. Request phase: adtl.policy decides whether the call may run.
359
433
  const requestPayload = { method: "tools/call", params: { name: route.tool, arguments: args } };
360
434
  let req = await this.invoker.runRequest({ event: "tools/call", payload: requestPayload, config, context });
435
+ ran.push(...req.ran);
361
436
  const policy = infoOf(req, "adtl.policy");
362
437
  let approval;
363
438
  if (req.blocked) {
@@ -367,8 +442,12 @@ export class AdtlGateway {
367
442
  const stopped = { decision: b.decision, reason: b.reason, matched: policy.matched, callHash: policy.callHash, sessionLabel: policy.sessionLabel };
368
443
  const matched = Array.isArray(policy.matched) ? policy.matched : [];
369
444
  const hash = b.confirmation?.callHash ?? policy.callHash;
370
- if (b.decision === "ask" && this.approvals?.canApprove && typeof hash === "string") {
371
- this.log.info(`ask ${name}: waiting for approval (${b.reason})`);
445
+ const lists = Array.isArray(policy.approvers) ? policy.approvers : undefined;
446
+ const approvers = lists ? eligibleApprovers(lists, this.approverRoles) : undefined;
447
+ // The client's own prompt, if enabled and supported; never for named approvers (it can't say who answered).
448
+ const elicit = this.elicitApprovals && !approvers && this.server && supportsElicitation(this.server) ? [new ElicitationChannel(this.server)] : [];
449
+ if (b.decision === "ask" && this.approvals && (this.approvals.canApprove || elicit.length > 0) && typeof hash === "string") {
450
+ this.log.info(`ask ${name}: waiting for approval${approvers ? ` from ${approvers.join(", ") || "nobody"}` : ""} (${b.reason})`);
372
451
  approval = await this.waitForApproval(opts, () => this.approvals.request({
373
452
  sessionId: this.sessionId,
374
453
  server: route.upstream,
@@ -377,7 +456,8 @@ export class AdtlGateway {
377
456
  reason: b.reason,
378
457
  matched,
379
458
  ...(policy.sessionLabel !== undefined ? { sessionLabel: policy.sessionLabel } : {}),
380
- }));
459
+ ...(approvers ? { approvers } : {}),
460
+ }, elicit));
381
461
  this.log.info(`ask ${name}: ${approval.outcome}`);
382
462
  if (approval.outcome !== "approved") {
383
463
  finish({ outcome: "blocked", ...stopped, approval, warnings: req.warnings });
@@ -395,6 +475,7 @@ export class AdtlGateway {
395
475
  ...(approval.approver ? { principal: { type: "user", id: approval.approver } } : {}),
396
476
  }];
397
477
  req = await this.invoker.runRequest({ event: "tools/call", payload: requestPayload, config, context: { ...context, confirmations } });
478
+ ran.push(...req.ran);
398
479
  const sent = callBindingHash({ event: "tools/call", phase: "request", target: route.upstream, payload: req.payload });
399
480
  const recheck = req.blocked?.reason ?? (sent !== c.callHash ? "the call changed after it was approved" : undefined);
400
481
  if (recheck !== undefined) {
@@ -438,6 +519,7 @@ export class AdtlGateway {
438
519
  config,
439
520
  context,
440
521
  });
522
+ ran.push(...res.ran);
441
523
  if (res.blocked) {
442
524
  // Fail closed: an unlabeled result must not reach the agent.
443
525
  finish({ outcome: "blocked", decision: res.blocked.decision, reason: res.blocked.reason, callHash: policy.callHash, warnings: [...req.warnings, ...res.warnings] });