@adtl/gateway 0.0.0-stage → 0.1.1
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 +201 -0
- package/README.md +113 -2
- package/dist/audit.d.ts +33 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +25 -0
- package/dist/audit.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +82 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +207 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +96 -0
- package/dist/config.js.map +1 -0
- package/dist/gateway.d.ts +34 -0
- package/dist/gateway.d.ts.map +1 -0
- package/dist/gateway.js +166 -0
- package/dist/gateway.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/invoker.d.ts +65 -0
- package/dist/invoker.d.ts.map +1 -0
- package/dist/invoker.js +121 -0
- package/dist/invoker.js.map +1 -0
- package/dist/startup.d.ts +23 -0
- package/dist/startup.d.ts.map +1 -0
- package/dist/startup.js +136 -0
- package/dist/startup.js.map +1 -0
- package/package.json +59 -4
- package/src/audit.ts +50 -0
- package/src/cli.ts +84 -0
- package/src/config.ts +106 -0
- package/src/gateway.ts +213 -0
- package/src/index.ts +5 -0
- package/src/invoker.ts +186 -0
- package/src/startup.ts +161 -0
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `gateway:` section of adtl.yaml.
|
|
3
|
+
*
|
|
4
|
+
* gateway:
|
|
5
|
+
* audit: ./adtl-audit.jsonl # optional JSONL audit log
|
|
6
|
+
* mode: audit # optional: log what would be stopped, stop nothing
|
|
7
|
+
* upstreams:
|
|
8
|
+
* github: # id must match `servers.github` in the label config
|
|
9
|
+
* command: npx
|
|
10
|
+
* args: ["-y", "@modelcontextprotocol/server-github"]
|
|
11
|
+
* env: { GITHUB_PERSONAL_ACCESS_TOKEN: "${GITHUB_TOKEN}" }
|
|
12
|
+
* docs:
|
|
13
|
+
* url: https://example.com/mcp
|
|
14
|
+
* headers: { Authorization: "Bearer ${DOCS_TOKEN}" }
|
|
15
|
+
*
|
|
16
|
+
* `${VAR}` in command, args, env, cwd, url and headers is replaced from the
|
|
17
|
+
* gateway's environment. A missing variable is a config error.
|
|
18
|
+
*/
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
export declare const StdioUpstreamSchema: z.ZodObject<{
|
|
21
|
+
command: z.ZodString;
|
|
22
|
+
args: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
23
|
+
env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
24
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
25
|
+
}, "strict", z.ZodTypeAny, {
|
|
26
|
+
command: string;
|
|
27
|
+
args: string[];
|
|
28
|
+
env: Record<string, string>;
|
|
29
|
+
cwd?: string | undefined;
|
|
30
|
+
}, {
|
|
31
|
+
command: string;
|
|
32
|
+
args?: string[] | undefined;
|
|
33
|
+
env?: Record<string, string> | undefined;
|
|
34
|
+
cwd?: string | undefined;
|
|
35
|
+
}>;
|
|
36
|
+
export declare const HttpUpstreamSchema: z.ZodObject<{
|
|
37
|
+
url: z.ZodString;
|
|
38
|
+
headers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
39
|
+
}, "strict", z.ZodTypeAny, {
|
|
40
|
+
url: string;
|
|
41
|
+
headers: Record<string, string>;
|
|
42
|
+
}, {
|
|
43
|
+
url: string;
|
|
44
|
+
headers?: Record<string, string> | undefined;
|
|
45
|
+
}>;
|
|
46
|
+
export declare const UpstreamSchema: z.ZodUnion<[z.ZodObject<{
|
|
47
|
+
command: z.ZodString;
|
|
48
|
+
args: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
49
|
+
env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
50
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
51
|
+
}, "strict", z.ZodTypeAny, {
|
|
52
|
+
command: string;
|
|
53
|
+
args: string[];
|
|
54
|
+
env: Record<string, string>;
|
|
55
|
+
cwd?: string | undefined;
|
|
56
|
+
}, {
|
|
57
|
+
command: string;
|
|
58
|
+
args?: string[] | undefined;
|
|
59
|
+
env?: Record<string, string> | undefined;
|
|
60
|
+
cwd?: string | undefined;
|
|
61
|
+
}>, z.ZodObject<{
|
|
62
|
+
url: z.ZodString;
|
|
63
|
+
headers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
64
|
+
}, "strict", z.ZodTypeAny, {
|
|
65
|
+
url: string;
|
|
66
|
+
headers: Record<string, string>;
|
|
67
|
+
}, {
|
|
68
|
+
url: string;
|
|
69
|
+
headers?: Record<string, string> | undefined;
|
|
70
|
+
}>]>;
|
|
71
|
+
export type StdioUpstream = z.infer<typeof StdioUpstreamSchema>;
|
|
72
|
+
export type HttpUpstream = z.infer<typeof HttpUpstreamSchema>;
|
|
73
|
+
export type Upstream = z.infer<typeof UpstreamSchema>;
|
|
74
|
+
export declare const GatewayConfigSchema: z.ZodObject<{
|
|
75
|
+
upstreams: z.ZodEffects<z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodObject<{
|
|
76
|
+
command: z.ZodString;
|
|
77
|
+
args: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
78
|
+
env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
79
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
80
|
+
}, "strict", z.ZodTypeAny, {
|
|
81
|
+
command: string;
|
|
82
|
+
args: string[];
|
|
83
|
+
env: Record<string, string>;
|
|
84
|
+
cwd?: string | undefined;
|
|
85
|
+
}, {
|
|
86
|
+
command: string;
|
|
87
|
+
args?: string[] | undefined;
|
|
88
|
+
env?: Record<string, string> | undefined;
|
|
89
|
+
cwd?: string | undefined;
|
|
90
|
+
}>, z.ZodObject<{
|
|
91
|
+
url: z.ZodString;
|
|
92
|
+
headers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
93
|
+
}, "strict", z.ZodTypeAny, {
|
|
94
|
+
url: string;
|
|
95
|
+
headers: Record<string, string>;
|
|
96
|
+
}, {
|
|
97
|
+
url: string;
|
|
98
|
+
headers?: Record<string, string> | undefined;
|
|
99
|
+
}>]>>, Record<string, {
|
|
100
|
+
command: string;
|
|
101
|
+
args: string[];
|
|
102
|
+
env: Record<string, string>;
|
|
103
|
+
cwd?: string | undefined;
|
|
104
|
+
} | {
|
|
105
|
+
url: string;
|
|
106
|
+
headers: Record<string, string>;
|
|
107
|
+
}>, Record<string, {
|
|
108
|
+
command: string;
|
|
109
|
+
args?: string[] | undefined;
|
|
110
|
+
env?: Record<string, string> | undefined;
|
|
111
|
+
cwd?: string | undefined;
|
|
112
|
+
} | {
|
|
113
|
+
url: string;
|
|
114
|
+
headers?: Record<string, string> | undefined;
|
|
115
|
+
}>>, Record<string, {
|
|
116
|
+
command: string;
|
|
117
|
+
args: string[];
|
|
118
|
+
env: Record<string, string>;
|
|
119
|
+
cwd?: string | undefined;
|
|
120
|
+
} | {
|
|
121
|
+
url: string;
|
|
122
|
+
headers: Record<string, string>;
|
|
123
|
+
}>, Record<string, {
|
|
124
|
+
command: string;
|
|
125
|
+
args?: string[] | undefined;
|
|
126
|
+
env?: Record<string, string> | undefined;
|
|
127
|
+
cwd?: string | undefined;
|
|
128
|
+
} | {
|
|
129
|
+
url: string;
|
|
130
|
+
headers?: Record<string, string> | undefined;
|
|
131
|
+
}>>;
|
|
132
|
+
/** How to start the interceptor server. Default: the bundled adtl-interceptors with this same config file. */
|
|
133
|
+
interceptors: z.ZodOptional<z.ZodObject<{
|
|
134
|
+
command: z.ZodString;
|
|
135
|
+
args: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
136
|
+
env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
137
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
138
|
+
}, "strict", z.ZodTypeAny, {
|
|
139
|
+
command: string;
|
|
140
|
+
args: string[];
|
|
141
|
+
env: Record<string, string>;
|
|
142
|
+
cwd?: string | undefined;
|
|
143
|
+
}, {
|
|
144
|
+
command: string;
|
|
145
|
+
args?: string[] | undefined;
|
|
146
|
+
env?: Record<string, string> | undefined;
|
|
147
|
+
cwd?: string | undefined;
|
|
148
|
+
}>>;
|
|
149
|
+
/** Path of a JSONL audit log. Relative paths are resolved against the config file's directory. */
|
|
150
|
+
audit: z.ZodOptional<z.ZodString>;
|
|
151
|
+
/**
|
|
152
|
+
* `active` (default) enforces the policy. `audit` stops and changes nothing, and
|
|
153
|
+
* logs what would have been stopped: for trying a policy out safely.
|
|
154
|
+
*/
|
|
155
|
+
mode: z.ZodDefault<z.ZodEnum<["active", "audit"]>>;
|
|
156
|
+
/** Timeout for each interceptor invocation, in milliseconds. */
|
|
157
|
+
interceptorTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
158
|
+
}, "strict", z.ZodTypeAny, {
|
|
159
|
+
upstreams: Record<string, {
|
|
160
|
+
command: string;
|
|
161
|
+
args: string[];
|
|
162
|
+
env: Record<string, string>;
|
|
163
|
+
cwd?: string | undefined;
|
|
164
|
+
} | {
|
|
165
|
+
url: string;
|
|
166
|
+
headers: Record<string, string>;
|
|
167
|
+
}>;
|
|
168
|
+
mode: "audit" | "active";
|
|
169
|
+
interceptorTimeoutMs: number;
|
|
170
|
+
audit?: string | undefined;
|
|
171
|
+
interceptors?: {
|
|
172
|
+
command: string;
|
|
173
|
+
args: string[];
|
|
174
|
+
env: Record<string, string>;
|
|
175
|
+
cwd?: string | undefined;
|
|
176
|
+
} | undefined;
|
|
177
|
+
}, {
|
|
178
|
+
upstreams: Record<string, {
|
|
179
|
+
command: string;
|
|
180
|
+
args?: string[] | undefined;
|
|
181
|
+
env?: Record<string, string> | undefined;
|
|
182
|
+
cwd?: string | undefined;
|
|
183
|
+
} | {
|
|
184
|
+
url: string;
|
|
185
|
+
headers?: Record<string, string> | undefined;
|
|
186
|
+
}>;
|
|
187
|
+
audit?: string | undefined;
|
|
188
|
+
interceptors?: {
|
|
189
|
+
command: string;
|
|
190
|
+
args?: string[] | undefined;
|
|
191
|
+
env?: Record<string, string> | undefined;
|
|
192
|
+
cwd?: string | undefined;
|
|
193
|
+
} | undefined;
|
|
194
|
+
mode?: "audit" | "active" | undefined;
|
|
195
|
+
interceptorTimeoutMs?: number | undefined;
|
|
196
|
+
}>;
|
|
197
|
+
export type GatewayConfig = z.infer<typeof GatewayConfigSchema>;
|
|
198
|
+
export declare const TOOL_SEPARATOR = "__";
|
|
199
|
+
export declare class EnvExpansionError extends Error {
|
|
200
|
+
readonly variable: string;
|
|
201
|
+
constructor(variable: string, where: string);
|
|
202
|
+
}
|
|
203
|
+
/** Replace `${VAR}` with values from `env`. `$${VAR}` keeps a literal `${VAR}`. */
|
|
204
|
+
export declare function expandEnv(value: string, env: NodeJS.ProcessEnv, where: string): string;
|
|
205
|
+
export declare function expandUpstream<T extends Upstream>(id: string, u: T, env: NodeJS.ProcessEnv): T;
|
|
206
|
+
export declare function isHttpUpstream(u: Upstream): u is HttpUpstream;
|
|
207
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;EAOrB,CAAC;AAEZ,eAAO,MAAM,kBAAkB;;;;;;;;;EAKpB,CAAC;AAEZ,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;IAAqD,CAAC;AACjF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAQ5B,8GAA8G;;;;;;;;;;;;;;;;;IAE9G,kGAAkG;;IAElG;;;OAGG;;IAEH,gEAAgE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGzD,CAAC;AACZ,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,cAAc,OAAO,CAAC;AAEnC,qBAAa,iBAAkB,SAAQ,KAAK;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM;gBAAhB,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAIrD;AAED,mFAAmF;AACnF,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAOtF;AAMD,wBAAgB,cAAc,CAAC,CAAC,SAAS,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,CAAC,CAY9F;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,QAAQ,GAAG,CAAC,IAAI,YAAY,CAE7D"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `gateway:` section of adtl.yaml.
|
|
3
|
+
*
|
|
4
|
+
* gateway:
|
|
5
|
+
* audit: ./adtl-audit.jsonl # optional JSONL audit log
|
|
6
|
+
* mode: audit # optional: log what would be stopped, stop nothing
|
|
7
|
+
* upstreams:
|
|
8
|
+
* github: # id must match `servers.github` in the label config
|
|
9
|
+
* command: npx
|
|
10
|
+
* args: ["-y", "@modelcontextprotocol/server-github"]
|
|
11
|
+
* env: { GITHUB_PERSONAL_ACCESS_TOKEN: "${GITHUB_TOKEN}" }
|
|
12
|
+
* docs:
|
|
13
|
+
* url: https://example.com/mcp
|
|
14
|
+
* headers: { Authorization: "Bearer ${DOCS_TOKEN}" }
|
|
15
|
+
*
|
|
16
|
+
* `${VAR}` in command, args, env, cwd, url and headers is replaced from the
|
|
17
|
+
* gateway's environment. A missing variable is a config error.
|
|
18
|
+
*/
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
const UPSTREAM_ID = /^[A-Za-z0-9-]+$/;
|
|
21
|
+
export const StdioUpstreamSchema = z
|
|
22
|
+
.object({
|
|
23
|
+
command: z.string().min(1),
|
|
24
|
+
args: z.array(z.string()).default([]),
|
|
25
|
+
env: z.record(z.string()).default({}),
|
|
26
|
+
cwd: z.string().optional(),
|
|
27
|
+
})
|
|
28
|
+
.strict();
|
|
29
|
+
export const HttpUpstreamSchema = z
|
|
30
|
+
.object({
|
|
31
|
+
url: z.string().min(1),
|
|
32
|
+
headers: z.record(z.string()).default({}),
|
|
33
|
+
})
|
|
34
|
+
.strict();
|
|
35
|
+
export const UpstreamSchema = z.union([StdioUpstreamSchema, HttpUpstreamSchema]);
|
|
36
|
+
export const GatewayConfigSchema = z
|
|
37
|
+
.object({
|
|
38
|
+
upstreams: z
|
|
39
|
+
.record(UpstreamSchema)
|
|
40
|
+
.refine((u) => Object.keys(u).length > 0, "At least one upstream server is required")
|
|
41
|
+
.refine((u) => Object.keys(u).every((id) => UPSTREAM_ID.test(id)), {
|
|
42
|
+
message: "Upstream ids may only contain letters, digits and '-' (they prefix tool names as <id>__<tool>)",
|
|
43
|
+
}),
|
|
44
|
+
/** How to start the interceptor server. Default: the bundled adtl-interceptors with this same config file. */
|
|
45
|
+
interceptors: StdioUpstreamSchema.optional(),
|
|
46
|
+
/** Path of a JSONL audit log. Relative paths are resolved against the config file's directory. */
|
|
47
|
+
audit: z.string().min(1).optional(),
|
|
48
|
+
/**
|
|
49
|
+
* `active` (default) enforces the policy. `audit` stops and changes nothing, and
|
|
50
|
+
* logs what would have been stopped: for trying a policy out safely.
|
|
51
|
+
*/
|
|
52
|
+
mode: z.enum(["active", "audit"]).default("active"),
|
|
53
|
+
/** Timeout for each interceptor invocation, in milliseconds. */
|
|
54
|
+
interceptorTimeoutMs: z.number().int().positive().default(5000),
|
|
55
|
+
})
|
|
56
|
+
.strict();
|
|
57
|
+
export const TOOL_SEPARATOR = "__";
|
|
58
|
+
export class EnvExpansionError extends Error {
|
|
59
|
+
variable;
|
|
60
|
+
constructor(variable, where) {
|
|
61
|
+
super(`Environment variable ${variable} is not set (used in ${where})`);
|
|
62
|
+
this.variable = variable;
|
|
63
|
+
this.name = "EnvExpansionError";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Replace `${VAR}` with values from `env`. `$${VAR}` keeps a literal `${VAR}`. */
|
|
67
|
+
export function expandEnv(value, env, where) {
|
|
68
|
+
return value.replace(/(\$?)\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (match, escape, name) => {
|
|
69
|
+
if (escape)
|
|
70
|
+
return match.slice(1);
|
|
71
|
+
const v = env[name];
|
|
72
|
+
if (v === undefined)
|
|
73
|
+
throw new EnvExpansionError(name, where);
|
|
74
|
+
return v;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function expandRecord(rec, env, where) {
|
|
78
|
+
return Object.fromEntries(Object.entries(rec).map(([k, v]) => [k, expandEnv(v, env, `${where}.${k}`)]));
|
|
79
|
+
}
|
|
80
|
+
export function expandUpstream(id, u, env) {
|
|
81
|
+
const where = `gateway.upstreams.${id}`;
|
|
82
|
+
if ("url" in u) {
|
|
83
|
+
return { ...u, url: expandEnv(u.url, env, `${where}.url`), headers: expandRecord(u.headers, env, `${where}.headers`) };
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
...u,
|
|
87
|
+
command: expandEnv(u.command, env, `${where}.command`),
|
|
88
|
+
args: u.args.map((a, i) => expandEnv(a, env, `${where}.args[${i}]`)),
|
|
89
|
+
env: expandRecord(u.env, env, `${where}.env`),
|
|
90
|
+
...(u.cwd !== undefined ? { cwd: expandEnv(u.cwd, env, `${where}.cwd`) } : {}),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export function isHttpUpstream(u) {
|
|
94
|
+
return "url" in u;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { type CallToolResult, type Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import type { AuditSink } from "./audit.js";
|
|
5
|
+
import type { InterceptorInvoker } from "./invoker.js";
|
|
6
|
+
export declare const GATEWAY_VERSION = "0.1.1";
|
|
7
|
+
/** `_meta` key on gateway-generated error results describing why a call was stopped. */
|
|
8
|
+
export declare const GATEWAY_DECISION_KEY = "adtl/gateway";
|
|
9
|
+
export interface GatewayLogger {
|
|
10
|
+
info(msg: string): void;
|
|
11
|
+
warn(msg: string): void;
|
|
12
|
+
}
|
|
13
|
+
export interface AdtlGatewayOptions {
|
|
14
|
+
upstreams: Map<string, Client>;
|
|
15
|
+
invoker: InterceptorInvoker;
|
|
16
|
+
/** One gateway process serves one agent session over stdio. */
|
|
17
|
+
sessionId?: string;
|
|
18
|
+
audit?: AuditSink;
|
|
19
|
+
logger?: GatewayLogger;
|
|
20
|
+
}
|
|
21
|
+
export declare class AdtlGateway {
|
|
22
|
+
readonly sessionId: string;
|
|
23
|
+
private readonly upstreams;
|
|
24
|
+
private readonly invoker;
|
|
25
|
+
private readonly audit;
|
|
26
|
+
private readonly log;
|
|
27
|
+
private routes;
|
|
28
|
+
constructor(opts: AdtlGatewayOptions);
|
|
29
|
+
/** The downstream MCP server the agent connects to. */
|
|
30
|
+
createServer(): Server;
|
|
31
|
+
listTools(): Promise<Tool[]>;
|
|
32
|
+
callTool(name: string, args: Record<string, unknown>): Promise<CallToolResult>;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=gateway.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,IAAI,EACV,MAAM,oCAAoC,CAAC;AAE5C,OAAO,KAAK,EAAc,SAAS,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,KAAK,EAAyB,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAE9E,eAAO,MAAM,eAAe,UAAU,CAAC;AAEvC,wFAAwF;AACxF,eAAO,MAAM,oBAAoB,iBAAiB,CAAC;AAQnD,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;CACxB;AAoBD,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,MAAM,CAA4B;gBAE9B,IAAI,EAAE,kBAAkB;IAQpC,uDAAuD;IACvD,YAAY,IAAI,MAAM;IAehB,SAAS,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;IAuB5B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC;CA+ErF"}
|
package/dist/gateway.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The gateway: one MCP server facing the agent, many MCP clients facing the
|
|
3
|
+
* upstream servers, and an interceptor chain in between.
|
|
4
|
+
*
|
|
5
|
+
* tools/list → every upstream's tools, renamed <upstream>__<tool>
|
|
6
|
+
* tools/call → request interceptors (adtl.policy) → upstream → response
|
|
7
|
+
* interceptors (adtl.label) → agent
|
|
8
|
+
*
|
|
9
|
+
* When a call is stopped, the agent receives a normal tool result with
|
|
10
|
+
* `isError: true` explaining why, so the model can tell the user. In v0.1 an
|
|
11
|
+
* "ask" decision is treated as a block; approvals arrive in v0.2.
|
|
12
|
+
*
|
|
13
|
+
* In audit mode (the invoker runs every interceptor with `mode: "audit"`)
|
|
14
|
+
* nothing is stopped or changed: calls the policy would stop are forwarded,
|
|
15
|
+
* and the audit log records what would have happened.
|
|
16
|
+
*/
|
|
17
|
+
import { randomUUID } from "node:crypto";
|
|
18
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
19
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
20
|
+
import { TOOL_SEPARATOR } from "./config.js";
|
|
21
|
+
export const GATEWAY_VERSION = "0.1.1";
|
|
22
|
+
/** `_meta` key on gateway-generated error results describing why a call was stopped. */
|
|
23
|
+
export const GATEWAY_DECISION_KEY = "adtl/gateway";
|
|
24
|
+
const stderrLogger = {
|
|
25
|
+
info: (m) => console.error(`[adtl-gateway] ${m}`),
|
|
26
|
+
warn: (m) => console.error(`[adtl-gateway] warning: ${m}`),
|
|
27
|
+
};
|
|
28
|
+
function textError(text, meta) {
|
|
29
|
+
return { content: [{ type: "text", text }], isError: true, _meta: { [GATEWAY_DECISION_KEY]: meta } };
|
|
30
|
+
}
|
|
31
|
+
function blockedMessage(b) {
|
|
32
|
+
if (b.decision === "ask") {
|
|
33
|
+
return (`ADTL stopped this call because it needs a human's approval: ${b.reason}. ` +
|
|
34
|
+
`This version of ADTL cannot ask for approval yet, so the call was not run. Tell the user what you were trying to do.`);
|
|
35
|
+
}
|
|
36
|
+
return `ADTL blocked this call: ${b.reason}. The call was not run.`;
|
|
37
|
+
}
|
|
38
|
+
function infoOf(outcome, interceptor) {
|
|
39
|
+
return outcome.results.find((r) => r.interceptor === interceptor)?.info ?? {};
|
|
40
|
+
}
|
|
41
|
+
export class AdtlGateway {
|
|
42
|
+
sessionId;
|
|
43
|
+
upstreams;
|
|
44
|
+
invoker;
|
|
45
|
+
audit;
|
|
46
|
+
log;
|
|
47
|
+
routes = new Map();
|
|
48
|
+
constructor(opts) {
|
|
49
|
+
this.upstreams = opts.upstreams;
|
|
50
|
+
this.invoker = opts.invoker;
|
|
51
|
+
this.sessionId = opts.sessionId ?? randomUUID();
|
|
52
|
+
this.audit = opts.audit;
|
|
53
|
+
this.log = opts.logger ?? stderrLogger;
|
|
54
|
+
}
|
|
55
|
+
/** The downstream MCP server the agent connects to. */
|
|
56
|
+
createServer() {
|
|
57
|
+
const server = new Server({ name: "adtl-gateway", version: GATEWAY_VERSION }, {
|
|
58
|
+
capabilities: { tools: {} },
|
|
59
|
+
instructions: "Tools from several MCP servers, named <server>__<tool>. ADTL checks each call against trust labels; " +
|
|
60
|
+
"if a call is stopped, the result explains why.",
|
|
61
|
+
});
|
|
62
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: await this.listTools() }));
|
|
63
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => this.callTool(req.params.name, req.params.arguments ?? {}));
|
|
64
|
+
return server;
|
|
65
|
+
}
|
|
66
|
+
async listTools() {
|
|
67
|
+
const routes = new Map();
|
|
68
|
+
const tools = [];
|
|
69
|
+
for (const [upstream, client] of this.upstreams) {
|
|
70
|
+
let cursor;
|
|
71
|
+
try {
|
|
72
|
+
do {
|
|
73
|
+
const page = await client.listTools(cursor ? { cursor } : {});
|
|
74
|
+
for (const t of page.tools) {
|
|
75
|
+
const name = `${upstream}${TOOL_SEPARATOR}${t.name}`;
|
|
76
|
+
routes.set(name, { upstream, tool: t.name, ...(t.annotations ? { annotations: t.annotations } : {}) });
|
|
77
|
+
tools.push({ ...t, name });
|
|
78
|
+
}
|
|
79
|
+
cursor = page.nextCursor;
|
|
80
|
+
} while (cursor);
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
this.log.warn(`tools/list failed for ${upstream}: ${err.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
this.routes = routes;
|
|
87
|
+
return tools;
|
|
88
|
+
}
|
|
89
|
+
async callTool(name, args) {
|
|
90
|
+
const started = performance.now();
|
|
91
|
+
let route = this.routes.get(name);
|
|
92
|
+
if (!route) {
|
|
93
|
+
await this.listTools();
|
|
94
|
+
route = this.routes.get(name);
|
|
95
|
+
}
|
|
96
|
+
if (!route)
|
|
97
|
+
return textError(`Unknown tool: ${name}`, { reason: "unknown_tool" });
|
|
98
|
+
const client = this.upstreams.get(route.upstream);
|
|
99
|
+
const context = {
|
|
100
|
+
sessionId: this.sessionId,
|
|
101
|
+
timestamp: new Date().toISOString(),
|
|
102
|
+
principal: { type: "anonymous" },
|
|
103
|
+
};
|
|
104
|
+
const config = {
|
|
105
|
+
server: route.upstream,
|
|
106
|
+
...(route.annotations ? { toolAnnotations: route.annotations } : {}),
|
|
107
|
+
};
|
|
108
|
+
const base = { sessionId: this.sessionId, event: "tools/call", server: route.upstream, tool: route.tool };
|
|
109
|
+
const finish = (entry) => this.audit?.write({ ts: context.timestamp, ...base, ...entry, durationMs: Math.round(performance.now() - started) });
|
|
110
|
+
// 1. Request phase: adtl.policy decides whether the call may run.
|
|
111
|
+
const requestPayload = { method: "tools/call", params: { name: route.tool, arguments: args } };
|
|
112
|
+
const req = await this.invoker.runRequest({ event: "tools/call", payload: requestPayload, config, context });
|
|
113
|
+
const policy = infoOf(req, "adtl.policy");
|
|
114
|
+
if (req.blocked) {
|
|
115
|
+
finish({ outcome: "blocked", decision: req.blocked.decision, reason: req.blocked.reason, matched: policy.matched, callHash: policy.callHash, sessionLabel: policy.sessionLabel, warnings: req.warnings });
|
|
116
|
+
this.log.info(`${req.blocked.decision} ${name}: ${req.blocked.reason}`);
|
|
117
|
+
return textError(blockedMessage(req.blocked), { ...req.blocked, callHash: policy.callHash });
|
|
118
|
+
}
|
|
119
|
+
const forwarded = req.payload.params;
|
|
120
|
+
const decision = policy["adtl/decision"];
|
|
121
|
+
const audit = this.invoker.modeOverride === "audit";
|
|
122
|
+
// Only set in audit mode, for calls the policy would have stopped.
|
|
123
|
+
const wouldStop = audit && decision !== undefined && decision !== "allow"
|
|
124
|
+
? { reason: policy.reason, matched: policy.matched }
|
|
125
|
+
: undefined;
|
|
126
|
+
if (wouldStop)
|
|
127
|
+
this.log.info(`audit: would ${decision} ${name}: ${wouldStop.reason}`);
|
|
128
|
+
// 2. Forward to the upstream server.
|
|
129
|
+
let result;
|
|
130
|
+
try {
|
|
131
|
+
result = (await client.callTool({ name: forwarded.name, arguments: forwarded.arguments }));
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
const reason = err.message;
|
|
135
|
+
finish({ outcome: "upstream_error", ...(decision ? { decision } : {}), reason, callHash: policy.callHash, ...(audit ? { mode: "audit" } : {}), warnings: req.warnings });
|
|
136
|
+
return textError(`The ${route.upstream} server returned an error: ${reason}`, { reason: "upstream_error" });
|
|
137
|
+
}
|
|
138
|
+
// 3. Response phase: adtl.label labels the result and records it in the session.
|
|
139
|
+
const res = await this.invoker.runResponse({
|
|
140
|
+
event: "tools/call",
|
|
141
|
+
payload: { method: "tools/call", params: forwarded, result },
|
|
142
|
+
config,
|
|
143
|
+
context,
|
|
144
|
+
});
|
|
145
|
+
if (res.blocked) {
|
|
146
|
+
// Fail closed: an unlabeled result must not reach the agent.
|
|
147
|
+
finish({ outcome: "blocked", decision: res.blocked.decision, reason: res.blocked.reason, callHash: policy.callHash, warnings: [...req.warnings, ...res.warnings] });
|
|
148
|
+
this.log.warn(`result of ${name} withheld: ${res.blocked.reason}`);
|
|
149
|
+
return textError(`ADTL could not label the result of this call, so it was withheld: ${res.blocked.reason}`, { ...res.blocked });
|
|
150
|
+
}
|
|
151
|
+
const label = infoOf(res, "adtl.label");
|
|
152
|
+
finish({
|
|
153
|
+
outcome: "forwarded",
|
|
154
|
+
// Missing only if adtl.policy failed and was allowed to (audit mode); see warnings.
|
|
155
|
+
...(decision ? { decision } : {}),
|
|
156
|
+
...(wouldStop ?? {}),
|
|
157
|
+
...(audit ? { mode: "audit" } : {}),
|
|
158
|
+
callHash: policy.callHash,
|
|
159
|
+
label: label.label,
|
|
160
|
+
sessionLabel: label.sessionLabel,
|
|
161
|
+
warnings: [...req.warnings, ...res.warnings],
|
|
162
|
+
});
|
|
163
|
+
return res.payload.result;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=gateway.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway.js","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs interceptors in the order SEP-2624 requires:
|
|
3
|
+
*
|
|
4
|
+
* - Sending (request phase, gateway → upstream): Mutate (sequential, by
|
|
5
|
+
* priorityHint then name) → Validate (parallel).
|
|
6
|
+
* - Receiving (response phase, upstream → gateway): Validate (parallel) →
|
|
7
|
+
* Mutate (sequential).
|
|
8
|
+
*
|
|
9
|
+
* Only `severity: "error"` blocks. Audit-mode interceptors never block, and
|
|
10
|
+
* their mutations are not applied. An interceptor that fails (error or
|
|
11
|
+
* timeout) blocks the message unless it declares `failOpen: true`.
|
|
12
|
+
*
|
|
13
|
+
* The invoker may override every interceptor's `mode`, as SEP-2624 allows
|
|
14
|
+
* (e.g. audit in staging, active in production).
|
|
15
|
+
*/
|
|
16
|
+
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
17
|
+
import { type InterceptorDefinition, type InterceptorResult, type InvocationContext } from "@adtl/interceptors";
|
|
18
|
+
import type { Decision } from "@adtl/labels";
|
|
19
|
+
export type InterceptorMode = NonNullable<InterceptorDefinition["mode"]>;
|
|
20
|
+
export interface BoundInterceptor {
|
|
21
|
+
client: Client;
|
|
22
|
+
definition: InterceptorDefinition;
|
|
23
|
+
}
|
|
24
|
+
export interface Blocked {
|
|
25
|
+
interceptor: string;
|
|
26
|
+
decision: Decision;
|
|
27
|
+
reason: string;
|
|
28
|
+
}
|
|
29
|
+
export interface ChainOutcome {
|
|
30
|
+
payload: unknown;
|
|
31
|
+
blocked?: Blocked;
|
|
32
|
+
results: InterceptorResult[];
|
|
33
|
+
/** Interceptors that failed but were allowed to (failOpen or audit mode). */
|
|
34
|
+
warnings: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface InvokeOptions {
|
|
37
|
+
event: string;
|
|
38
|
+
payload: unknown;
|
|
39
|
+
config: Record<string, unknown>;
|
|
40
|
+
context: InvocationContext;
|
|
41
|
+
}
|
|
42
|
+
export declare class InterceptorInvoker {
|
|
43
|
+
private readonly interceptors;
|
|
44
|
+
private readonly timeoutMs;
|
|
45
|
+
/** Overrides the mode every interceptor declares. */
|
|
46
|
+
readonly modeOverride?: InterceptorMode | undefined;
|
|
47
|
+
constructor(interceptors: BoundInterceptor[], timeoutMs?: number,
|
|
48
|
+
/** Overrides the mode every interceptor declares. */
|
|
49
|
+
modeOverride?: InterceptorMode | undefined);
|
|
50
|
+
/** Discover the interceptors an interceptor server offers. */
|
|
51
|
+
static discover(client: Client): Promise<BoundInterceptor[]>;
|
|
52
|
+
get definitions(): InterceptorDefinition[];
|
|
53
|
+
runRequest(opts: InvokeOptions): Promise<ChainOutcome>;
|
|
54
|
+
runResponse(opts: InvokeOptions): Promise<ChainOutcome>;
|
|
55
|
+
private isAudit;
|
|
56
|
+
private select;
|
|
57
|
+
private invoke;
|
|
58
|
+
/** Handle an interceptor failure. Returns false if the chain must stop. */
|
|
59
|
+
private failed;
|
|
60
|
+
/** Sequential mutations, atomic: on failure the original payload is kept. */
|
|
61
|
+
private mutate;
|
|
62
|
+
/** Parallel validations. Only severity "error" blocks. */
|
|
63
|
+
private validate;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=invoker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invoker.d.ts","sourceRoot":"","sources":["../src/invoker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACxE,OAAO,EAIL,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EAIvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAa7C,MAAM,MAAM,eAAe,GAAG,WAAW,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC;AAEzE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,qBAAqB,CAAC;CACnC;AAED,MAAM,WAAW,OAAO;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAaD,qBAAa,kBAAkB;IAE3B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,qDAAqD;IACrD,QAAQ,CAAC,YAAY,CAAC,EAAE,eAAe;gBAHtB,YAAY,EAAE,gBAAgB,EAAE,EAChC,SAAS,SAAO;IACjC,qDAAqD;IAC5C,YAAY,CAAC,EAAE,eAAe,YAAA;IAGzC,8DAA8D;WACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAMlE,IAAI,WAAW,IAAI,qBAAqB,EAAE,CAEzC;IAEK,UAAU,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAOtD,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAO7D,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,MAAM;YAIA,MAAM;IAUpB,2EAA2E;IAC3E,OAAO,CAAC,MAAM;IAUd,6EAA6E;YAC/D,MAAM;IAoBpB,0DAA0D;YAC5C,QAAQ;CAsBvB"}
|