@adtl/gateway 0.2.0 → 0.3.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/README.md +63 -3
- package/dist/audit.d.ts +9 -4
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +1 -1
- package/dist/audit.js.map +1 -1
- package/dist/cli.js +82 -2
- package/dist/cli.js.map +1 -1
- package/dist/gateway.d.ts +22 -2
- package/dist/gateway.d.ts.map +1 -1
- package/dist/gateway.js +189 -14
- package/dist/gateway.js.map +1 -1
- package/dist/hooks/claudeCode.d.ts +43 -0
- package/dist/hooks/claudeCode.d.ts.map +1 -0
- package/dist/hooks/claudeCode.js +148 -0
- package/dist/hooks/claudeCode.js.map +1 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +4 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/preset.d.ts +16 -0
- package/dist/hooks/preset.d.ts.map +1 -0
- package/dist/hooks/preset.js +32 -0
- package/dist/hooks/preset.js.map +1 -0
- package/dist/hooks/run.d.ts +9 -0
- package/dist/hooks/run.d.ts.map +1 -0
- package/dist/hooks/run.js +44 -0
- package/dist/hooks/run.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/init/index.d.ts +4 -0
- package/dist/init/index.d.ts.map +1 -0
- package/dist/init/index.js +4 -0
- package/dist/init/index.js.map +1 -0
- package/dist/init/presets.d.ts +30 -0
- package/dist/init/presets.d.ts.map +1 -0
- package/dist/init/presets.js +34 -0
- package/dist/init/presets.js.map +1 -0
- package/dist/init/run.d.ts +18 -0
- package/dist/init/run.d.ts.map +1 -0
- package/dist/init/run.js +60 -0
- package/dist/init/run.js.map +1 -0
- package/dist/init/suggest.d.ts +50 -0
- package/dist/init/suggest.d.ts.map +1 -0
- package/dist/init/suggest.js +82 -0
- package/dist/init/suggest.js.map +1 -0
- package/dist/startup.d.ts +13 -2
- package/dist/startup.d.ts.map +1 -1
- package/dist/startup.js +25 -6
- package/dist/startup.js.map +1 -1
- package/package.json +3 -3
- package/src/audit.ts +10 -5
- package/src/cli.ts +75 -2
- package/src/gateway.ts +238 -13
- package/src/hooks/claudeCode.ts +194 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/preset.ts +47 -0
- package/src/hooks/run.ts +49 -0
- package/src/index.ts +2 -0
- package/src/init/index.ts +3 -0
- package/src/init/presets.ts +64 -0
- package/src/init/run.ts +79 -0
- package/src/init/suggest.ts +127 -0
- package/src/startup.ts +25 -7
package/dist/init/run.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `adtl-gateway init`: connect to every upstream in adtl.yaml, list its
|
|
3
|
+
* tools, and draft a labeling rule for each tool not configured yet.
|
|
4
|
+
*
|
|
5
|
+
* Without `write`, the updated file is only returned (the CLI prints it).
|
|
6
|
+
* With `write`, the original is kept as `<file>.bak` and the file is updated
|
|
7
|
+
* in place. Existing entries and comments are never changed.
|
|
8
|
+
*/
|
|
9
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
10
|
+
import { dirname } from "node:path";
|
|
11
|
+
import { parseDocument } from "yaml";
|
|
12
|
+
import { expandUpstream } from "../config.js";
|
|
13
|
+
import { connectUpstream, loadGatewayConfig } from "../startup.js";
|
|
14
|
+
import { mergeIntoDocument, suggest } from "./suggest.js";
|
|
15
|
+
async function listAllTools(client) {
|
|
16
|
+
const tools = [];
|
|
17
|
+
let cursor;
|
|
18
|
+
do {
|
|
19
|
+
const page = await client.listTools(cursor ? { cursor } : {});
|
|
20
|
+
tools.push(...page.tools);
|
|
21
|
+
cursor = page.nextCursor;
|
|
22
|
+
} while (cursor);
|
|
23
|
+
return tools;
|
|
24
|
+
}
|
|
25
|
+
async function scan(id, connect) {
|
|
26
|
+
const client = await connect();
|
|
27
|
+
try {
|
|
28
|
+
const name = client.getServerVersion()?.name;
|
|
29
|
+
const tools = await listAllTools(client);
|
|
30
|
+
return { id, ...(name ? { serverName: name } : {}), tools: tools.map((t) => ({ name: t.name, annotations: t.annotations })) };
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
await client.close().catch(() => undefined);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function runInit(opts) {
|
|
37
|
+
const env = opts.env ?? process.env;
|
|
38
|
+
const loaded = await loadGatewayConfig(opts.path);
|
|
39
|
+
const baseDir = dirname(loaded.path);
|
|
40
|
+
const original = await readFile(loaded.path, "utf8");
|
|
41
|
+
const ids = Object.keys(loaded.gateway.upstreams);
|
|
42
|
+
const settled = await Promise.allSettled(ids.map((id) => scan(id, () => connectUpstream(id, expandUpstream(id, loaded.gateway.upstreams[id], env), baseDir))));
|
|
43
|
+
const scans = [];
|
|
44
|
+
const failed = [];
|
|
45
|
+
settled.forEach((s, i) => {
|
|
46
|
+
if (s.status === "fulfilled")
|
|
47
|
+
scans.push(s.value);
|
|
48
|
+
else
|
|
49
|
+
failed.push({ id: ids[i], error: s.reason.message });
|
|
50
|
+
});
|
|
51
|
+
const doc = parseDocument(original);
|
|
52
|
+
const reports = mergeIntoDocument(doc, scans.map((s) => suggest(s)));
|
|
53
|
+
const text = doc.toString();
|
|
54
|
+
if (opts.write && text !== original) {
|
|
55
|
+
await writeFile(`${loaded.path}.bak`, original, "utf8");
|
|
56
|
+
await writeFile(loaded.path, text, "utf8");
|
|
57
|
+
}
|
|
58
|
+
return { text, reports, failed };
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/init/run.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAqC,MAAM,cAAc,CAAC;AAgB7F,KAAK,UAAU,YAAY,CAAC,MAAc;IACxC,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,IAAI,MAA0B,CAAC;IAC/B,GAAG,CAAC;QACF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC9D,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC;IAC3B,CAAC,QAAQ,MAAM,EAAE;IACjB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,KAAK,UAAU,IAAI,CAAC,EAAU,EAAE,OAA8B;IAC5D,MAAM,MAAM,GAAG,MAAM,OAAO,EAAE,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,CAAC,gBAAgB,EAAE,EAAE,IAAI,CAAC;QAC7C,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;QACzC,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC;IAChI,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAiB;IAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACpC,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAErD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CACtC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,EAAE,EAAE,cAAc,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAE,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CACtH,CAAC;IACF,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAyB,EAAE,CAAC;IACxC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACvB,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW;YAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;;YAC7C,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,CAAE,EAAE,KAAK,EAAG,CAAC,CAAC,MAAgB,CAAC,OAAO,EAAE,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IAC5B,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpC,MAAM,SAAS,CAAC,GAAG,MAAM,CAAC,IAAI,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QACxD,MAAM,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Draft ADTL rules for an upstream's tools, and merge them into adtl.yaml.
|
|
3
|
+
*
|
|
4
|
+
* Without a preset, rules come from the MCP tool annotations. Those are the
|
|
5
|
+
* server's own claims, so they only decide egress; integrity is always
|
|
6
|
+
* untrusted and confidentiality private until the owner says otherwise.
|
|
7
|
+
* Missing hints take the MCP spec defaults (readOnlyHint false, openWorldHint true).
|
|
8
|
+
*
|
|
9
|
+
* Merging never changes an entry that is already in the file: the owner's
|
|
10
|
+
* choices win, and `init` can be re-run to pick up new tools.
|
|
11
|
+
*/
|
|
12
|
+
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
13
|
+
import type { ToolRule } from "@adtl/labels";
|
|
14
|
+
import { type Document } from "yaml";
|
|
15
|
+
import { type Preset } from "./presets.js";
|
|
16
|
+
export interface ServerScan {
|
|
17
|
+
/** Upstream id in gateway.upstreams. */
|
|
18
|
+
id: string;
|
|
19
|
+
/** `serverInfo.name` the server reported. */
|
|
20
|
+
serverName?: string;
|
|
21
|
+
tools: Pick<Tool, "name" | "annotations">[];
|
|
22
|
+
}
|
|
23
|
+
export interface ToolSuggestion {
|
|
24
|
+
name: string;
|
|
25
|
+
rule: ToolRule;
|
|
26
|
+
/** Short reason, written as a comment next to the rule. */
|
|
27
|
+
why: string;
|
|
28
|
+
}
|
|
29
|
+
export interface ServerSuggestion {
|
|
30
|
+
id: string;
|
|
31
|
+
preset?: Preset;
|
|
32
|
+
defaults: ToolRule;
|
|
33
|
+
tools: ToolSuggestion[];
|
|
34
|
+
}
|
|
35
|
+
/** A rule from the tool's MCP annotations alone. */
|
|
36
|
+
export declare function ruleFromHints(annotations: Tool["annotations"]): Omit<ToolSuggestion, "name">;
|
|
37
|
+
export declare function suggest(scan: ServerScan, presets?: Preset[]): ServerSuggestion;
|
|
38
|
+
export interface MergeReport {
|
|
39
|
+
id: string;
|
|
40
|
+
preset?: string;
|
|
41
|
+
added: string[];
|
|
42
|
+
/** Tools already configured; left as they were. */
|
|
43
|
+
kept: string[];
|
|
44
|
+
/** Configured tools the server no longer offers. Left in place, reported only. */
|
|
45
|
+
stale: string[];
|
|
46
|
+
addedDefaults: boolean;
|
|
47
|
+
}
|
|
48
|
+
/** Add the suggestions to the document's `servers:` section, keeping every existing entry. */
|
|
49
|
+
export declare function mergeIntoDocument(doc: Document, suggestions: ServerSuggestion[]): MergeReport[];
|
|
50
|
+
//# sourceMappingURL=suggest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suggest.d.ts","sourceRoot":"","sources":["../../src/init/suggest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAmB,KAAK,QAAQ,EAAgB,MAAM,MAAM,CAAC;AACpE,OAAO,EAAyB,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAElE,MAAM,WAAW,UAAU;IACzB,wCAAwC;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,2DAA2D;IAC3D,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,cAAc,EAAE,CAAC;CACzB;AAID,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,CAM5F;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,WAAU,GAAG,gBAAgB,CAU7E;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,mDAAmD;IACnD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,kFAAkF;IAClF,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;CACxB;AAYD,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,QAAQ,EAAE,WAAW,EAAE,gBAAgB,EAAE,GAAG,WAAW,EAAE,CA2C/F"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { isMap, isScalar } from "yaml";
|
|
2
|
+
import { choosePreset, PRESETS } from "./presets.js";
|
|
3
|
+
const UNKNOWN_DEFAULTS = { integrity: "untrusted", confidentiality: "private" };
|
|
4
|
+
/** A rule from the tool's MCP annotations alone. */
|
|
5
|
+
export function ruleFromHints(annotations) {
|
|
6
|
+
const readOnly = annotations?.readOnlyHint === true;
|
|
7
|
+
const openWorld = annotations?.openWorldHint !== false;
|
|
8
|
+
const egress = readOnly ? (openWorld ? "external" : "none") : openWorld ? "public" : "internal";
|
|
9
|
+
const why = `hints: ${readOnly ? "read-only" : "writes"}, ${openWorld ? "open world" : "closed world"} (review)`;
|
|
10
|
+
return { rule: { integrity: "untrusted", confidentiality: "private", egress }, why };
|
|
11
|
+
}
|
|
12
|
+
export function suggest(scan, presets = PRESETS) {
|
|
13
|
+
const preset = choosePreset(scan.id, scan.serverName, presets);
|
|
14
|
+
const tools = scan.tools.map(({ name, annotations }) => {
|
|
15
|
+
if (!preset)
|
|
16
|
+
return { name, ...ruleFromHints(annotations) };
|
|
17
|
+
const override = preset.tools?.[name];
|
|
18
|
+
if (override)
|
|
19
|
+
return { name, rule: override, why: `preset ${preset.id}` };
|
|
20
|
+
const read = annotations?.readOnlyHint === true;
|
|
21
|
+
return { name, rule: read ? preset.read : preset.write, why: `preset ${preset.id}: ${read ? "read" : "write"}` };
|
|
22
|
+
});
|
|
23
|
+
return { id: scan.id, ...(preset ? { preset } : {}), defaults: preset?.defaults ?? UNKNOWN_DEFAULTS, tools };
|
|
24
|
+
}
|
|
25
|
+
function mapAt(doc, parent, key, commentBefore) {
|
|
26
|
+
const existing = parent.get(key, true);
|
|
27
|
+
if (isMap(existing))
|
|
28
|
+
return existing;
|
|
29
|
+
const map = doc.createNode({});
|
|
30
|
+
const k = doc.createNode(key);
|
|
31
|
+
if (commentBefore && isScalar(k))
|
|
32
|
+
k.commentBefore = ` ${commentBefore}`;
|
|
33
|
+
parent.add(doc.createPair(k, map));
|
|
34
|
+
return map;
|
|
35
|
+
}
|
|
36
|
+
/** Add the suggestions to the document's `servers:` section, keeping every existing entry. */
|
|
37
|
+
export function mergeIntoDocument(doc, suggestions) {
|
|
38
|
+
let servers = doc.get("servers", true);
|
|
39
|
+
if (!isMap(servers)) {
|
|
40
|
+
servers = doc.createNode({});
|
|
41
|
+
// Put servers: first, ahead of policy: and gateway:.
|
|
42
|
+
const root = doc.contents;
|
|
43
|
+
if (isMap(root))
|
|
44
|
+
root.items.unshift(doc.createPair("servers", servers));
|
|
45
|
+
else
|
|
46
|
+
doc.contents = doc.createNode({ servers });
|
|
47
|
+
}
|
|
48
|
+
const serversMap = servers;
|
|
49
|
+
return suggestions.map((s) => {
|
|
50
|
+
const report = { id: s.id, added: [], kept: [], stale: [], addedDefaults: false };
|
|
51
|
+
if (s.preset)
|
|
52
|
+
report.preset = s.preset.id;
|
|
53
|
+
const isNew = !serversMap.has(s.id);
|
|
54
|
+
const server = mapAt(doc, serversMap, s.id, isNew ? (s.preset?.note ?? "from MCP tool hints: review every line") : undefined);
|
|
55
|
+
if (!server.has("defaults")) {
|
|
56
|
+
const d = doc.createNode(s.defaults, { flow: true });
|
|
57
|
+
d.comment = " resources, prompts, and tools not listed below";
|
|
58
|
+
server.items.unshift(doc.createPair("defaults", d));
|
|
59
|
+
report.addedDefaults = true;
|
|
60
|
+
}
|
|
61
|
+
const tools = mapAt(doc, server, "tools");
|
|
62
|
+
tools.flow = false;
|
|
63
|
+
const offered = new Set(s.tools.map((t) => t.name));
|
|
64
|
+
for (const t of s.tools) {
|
|
65
|
+
if (tools.has(t.name)) {
|
|
66
|
+
report.kept.push(t.name);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const v = doc.createNode(t.rule, { flow: true });
|
|
70
|
+
v.comment = ` ${t.why}`;
|
|
71
|
+
tools.set(t.name, v);
|
|
72
|
+
report.added.push(t.name);
|
|
73
|
+
}
|
|
74
|
+
for (const item of tools.items) {
|
|
75
|
+
const name = isScalar(item.key) ? String(item.key.value) : String(item.key);
|
|
76
|
+
if (!offered.has(name))
|
|
77
|
+
report.stale.push(name);
|
|
78
|
+
}
|
|
79
|
+
return report;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=suggest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suggest.js","sourceRoot":"","sources":["../../src/init/suggest.ts"],"names":[],"mappings":"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"}
|
package/dist/startup.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import { type AdtlConfig } from "@adtl/labels";
|
|
2
3
|
import { ApprovalBroker } from "./approvals.js";
|
|
3
|
-
import { type GatewayConfig } from "./config.js";
|
|
4
|
+
import { type GatewayConfig, type Upstream } from "./config.js";
|
|
4
5
|
import { AdtlGateway, type GatewayLogger } from "./gateway.js";
|
|
5
6
|
export declare class GatewayConfigError extends Error {
|
|
6
7
|
constructor(message: string);
|
|
@@ -13,8 +14,18 @@ export interface LoadedConfig {
|
|
|
13
14
|
export declare function loadGatewayConfig(path: string): Promise<LoadedConfig>;
|
|
14
15
|
/** Path of the bundled adtl-interceptors CLI. */
|
|
15
16
|
export declare function bundledInterceptorsCli(): string;
|
|
17
|
+
export declare function connectUpstream(id: string, u: Upstream, baseDir: string): Promise<Client>;
|
|
16
18
|
/** Build the approval broker from `gateway.approvals`, or undefined if not configured. */
|
|
17
19
|
export declare function createApprovals(cfg: GatewayConfig, env: NodeJS.ProcessEnv, log: GatewayLogger): ApprovalBroker | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Under Claude Code, which passes `CLAUDE_CODE_SESSION_ID` to stdio MCP
|
|
22
|
+
* servers, the gateway uses that id and keeps session labels in files, so the
|
|
23
|
+
* Claude Code hooks (`adtl-gateway hook`) add to the same session label.
|
|
24
|
+
*/
|
|
25
|
+
export declare function sharedSession(env: NodeJS.ProcessEnv): {
|
|
26
|
+
sessionId: string;
|
|
27
|
+
dir: string;
|
|
28
|
+
} | undefined;
|
|
18
29
|
export interface StartedGateway {
|
|
19
30
|
gateway: AdtlGateway;
|
|
20
31
|
close(): Promise<void>;
|
package/dist/startup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"startup.d.ts","sourceRoot":"","sources":["../src/startup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"startup.d.ts","sourceRoot":"","sources":["../src/startup.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAInE,OAAO,EAAsB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAGnE,OAAO,EAAE,cAAc,EAAiB,MAAM,gBAAgB,CAAC;AAE/D,OAAO,EAKL,KAAK,aAAa,EAElB,KAAK,QAAQ,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAmB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAIhF,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,aAAa,CAAC;CACxB;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA2B3E;AAED,iDAAiD;AACjD,wBAAgB,sBAAsB,IAAI,MAAM,CAI/C;AAaD,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO/F;AAED,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,cAAc,GAAG,SAAS,CAiB1H;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAGpG;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,YAAY,EACpB,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAAC,MAAM,CAAC,EAAE,aAAa,CAAA;CAAO,GAC7D,OAAO,CAAC,cAAc,CAAC,CAwEzB"}
|
package/dist/startup.js
CHANGED
|
@@ -9,6 +9,7 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
9
9
|
import { StdioClientTransport, getDefaultEnvironment } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
10
10
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
11
11
|
import { INTERCEPTORS_EXTENSION, LABEL_INTERCEPTOR, POLICY_INTERCEPTOR, parseConfigText } from "@adtl/interceptors";
|
|
12
|
+
import { defaultSessionsDir } from "@adtl/labels";
|
|
12
13
|
import { parse as parseYaml } from "yaml";
|
|
13
14
|
import { ZodError } from "zod";
|
|
14
15
|
import { ApprovalBroker } from "./approvals.js";
|
|
@@ -71,7 +72,7 @@ function stdioTransport(u, baseDir) {
|
|
|
71
72
|
stderr: "inherit",
|
|
72
73
|
});
|
|
73
74
|
}
|
|
74
|
-
async function
|
|
75
|
+
export async function connectUpstream(id, u, baseDir) {
|
|
75
76
|
const client = new Client({ name: `adtl-gateway→${id}`, version: GATEWAY_VERSION });
|
|
76
77
|
const transport = isHttpUpstream(u)
|
|
77
78
|
? new StreamableHTTPClientTransport(new URL(u.url), { requestInit: { headers: u.headers } })
|
|
@@ -101,6 +102,15 @@ export function createApprovals(cfg, env, log) {
|
|
|
101
102
|
log.info(`approvals: ${notifiers.map((n) => n.name).join(", ")} (timeout ${a.timeoutSeconds}s)`);
|
|
102
103
|
return new ApprovalBroker({ notifiers, timeoutMs: a.timeoutSeconds * 1000, log });
|
|
103
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Under Claude Code, which passes `CLAUDE_CODE_SESSION_ID` to stdio MCP
|
|
107
|
+
* servers, the gateway uses that id and keeps session labels in files, so the
|
|
108
|
+
* Claude Code hooks (`adtl-gateway hook`) add to the same session label.
|
|
109
|
+
*/
|
|
110
|
+
export function sharedSession(env) {
|
|
111
|
+
const sessionId = env.CLAUDE_CODE_SESSION_ID?.trim();
|
|
112
|
+
return sessionId ? { sessionId, dir: defaultSessionsDir(env) } : undefined;
|
|
113
|
+
}
|
|
104
114
|
export async function startGateway(loaded, opts = {}) {
|
|
105
115
|
const env = opts.env ?? process.env;
|
|
106
116
|
const baseDir = dirname(loaded.path);
|
|
@@ -109,10 +119,16 @@ export async function startGateway(loaded, opts = {}) {
|
|
|
109
119
|
const closeAll = () => Promise.allSettled(clients.map((c) => c.close())).then(() => undefined);
|
|
110
120
|
try {
|
|
111
121
|
// 1. Interceptor server (out of process, per the Interceptors WG).
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
122
|
+
const shared = sharedSession(env);
|
|
123
|
+
const custom = loaded.gateway.interceptors && expandUpstream("interceptors", loaded.gateway.interceptors, env);
|
|
124
|
+
const icfg = custom
|
|
125
|
+
? { ...custom, env: { ...custom.env, ...(shared ? { ADTL_SESSIONS_DIR: shared.dir } : {}) } }
|
|
126
|
+
: {
|
|
127
|
+
command: process.execPath,
|
|
128
|
+
args: [bundledInterceptorsCli(), "--config", loaded.path, ...(shared ? ["--sessions-dir", shared.dir] : [])],
|
|
129
|
+
env: {},
|
|
130
|
+
};
|
|
131
|
+
const interceptors = await connectUpstream("interceptors", icfg, baseDir);
|
|
116
132
|
clients.push(interceptors);
|
|
117
133
|
if (!interceptors.getServerCapabilities()?.extensions?.[INTERCEPTORS_EXTENSION]) {
|
|
118
134
|
throw new GatewayConfigError(`The interceptor server does not declare the ${INTERCEPTORS_EXTENSION} extension`);
|
|
@@ -127,12 +143,14 @@ export async function startGateway(loaded, opts = {}) {
|
|
|
127
143
|
const audit = loaded.gateway.mode === "audit";
|
|
128
144
|
const invoker = new InterceptorInvoker(bound, loaded.gateway.interceptorTimeoutMs, audit ? "audit" : undefined);
|
|
129
145
|
log.info(`interceptors: ${names.join(", ")}`);
|
|
146
|
+
if (shared)
|
|
147
|
+
log.info(`sharing session labels with Claude Code hooks (${shared.dir})`);
|
|
130
148
|
if (audit)
|
|
131
149
|
log.warn("audit mode: calls are logged but never stopped or changed");
|
|
132
150
|
// 2. Upstream servers. One failing upstream does not stop the others.
|
|
133
151
|
const upstreams = new Map();
|
|
134
152
|
const ids = Object.keys(loaded.gateway.upstreams);
|
|
135
|
-
const settled = await Promise.allSettled(ids.map((id) =>
|
|
153
|
+
const settled = await Promise.allSettled(ids.map((id) => connectUpstream(id, expandUpstream(id, loaded.gateway.upstreams[id], env), baseDir)));
|
|
136
154
|
settled.forEach((s, i) => {
|
|
137
155
|
const id = ids[i];
|
|
138
156
|
if (s.status === "fulfilled") {
|
|
@@ -152,6 +170,7 @@ export async function startGateway(loaded, opts = {}) {
|
|
|
152
170
|
const auditLog = loaded.gateway.audit ? new JsonlAuditLog(resolve(baseDir, loaded.gateway.audit)) : undefined;
|
|
153
171
|
const approvals = createApprovals(loaded.gateway, env, log);
|
|
154
172
|
const gateway = new AdtlGateway({
|
|
173
|
+
...(shared ? { sessionId: shared.sessionId } : {}),
|
|
155
174
|
upstreams,
|
|
156
175
|
invoker,
|
|
157
176
|
...(auditLog ? { audit: auditLog } : {}),
|
package/dist/startup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"startup.js","sourceRoot":"","sources":["../src/startup.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,2CAA2C,CAAC;AACxG,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"startup.js","sourceRoot":"","sources":["../src/startup.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adtl/gateway",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Experimental: MCP gateway that labels every tool result and blocks risky tool calls using ADTL trust labels",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"url": "https://github.com/probita-ai/adtl/issues"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@adtl/interceptors": "0.
|
|
50
|
-
"@adtl/labels": "0.
|
|
49
|
+
"@adtl/interceptors": "0.3.1",
|
|
50
|
+
"@adtl/labels": "0.3.1",
|
|
51
51
|
"@modelcontextprotocol/sdk": "1.31.0",
|
|
52
52
|
"nodemailer": "10.0.12",
|
|
53
53
|
"yaml": "2.9.1",
|
package/src/audit.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Append-only JSONL audit log. One line per tool call.
|
|
2
|
+
* Append-only JSONL audit log. One line per tool call, resource read or prompt get.
|
|
3
3
|
*
|
|
4
4
|
* Arguments and results are never written; the call is identified by its
|
|
5
5
|
* hash, so the log can be shared without leaking the data it describes.
|
|
@@ -10,11 +10,16 @@ import { dirname } from "node:path";
|
|
|
10
10
|
export interface AuditEntry {
|
|
11
11
|
ts: string;
|
|
12
12
|
sessionId: string;
|
|
13
|
-
event: "tools/call";
|
|
13
|
+
event: "tools/call" | "resources/read" | "prompts/get";
|
|
14
14
|
server: string;
|
|
15
|
-
tool
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
/** Exactly one of tool, resource or prompt is set, matching `event`. */
|
|
16
|
+
tool?: string;
|
|
17
|
+
resource?: string;
|
|
18
|
+
prompt?: string;
|
|
19
|
+
/** Final outcome of the call. "asked": handed to Claude Code's permission prompt by the hook. */
|
|
20
|
+
outcome: "forwarded" | "blocked" | "asked" | "upstream_error";
|
|
21
|
+
/** Present when the entry was written by the Claude Code hook rather than the gateway. */
|
|
22
|
+
via?: "claude-code-hook";
|
|
18
23
|
/** The policy's decision. In audit mode a call can be forwarded with decision "ask" or "block". */
|
|
19
24
|
decision?: string;
|
|
20
25
|
/** Present when the gateway ran in audit mode: nothing was stopped or changed. */
|
package/src/cli.ts
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* servers through ADTL's interceptors.
|
|
5
5
|
*
|
|
6
6
|
* adtl-gateway --config adtl.yaml [--audit]
|
|
7
|
+
* adtl-gateway init --config adtl.yaml [--write]
|
|
8
|
+
* adtl-gateway hook --config adtl.yaml [--gateway-server adtl]
|
|
7
9
|
*
|
|
8
10
|
* Point your MCP client (e.g. Claude Code's .mcp.json) at this command.
|
|
9
11
|
* Logs go to stderr; stdout carries MCP JSON-RPC only.
|
|
@@ -11,9 +13,13 @@
|
|
|
11
13
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
14
|
import { EnvExpansionError } from "./config.js";
|
|
13
15
|
import { GATEWAY_VERSION } from "./gateway.js";
|
|
16
|
+
import { runHook } from "./hooks/run.js";
|
|
17
|
+
import { runInit, type InitResult } from "./init/run.js";
|
|
14
18
|
import { GatewayConfigError, loadGatewayConfig, startGateway } from "./startup.js";
|
|
15
19
|
|
|
16
20
|
const USAGE = `Usage: adtl-gateway --config <adtl.yaml> [--audit]
|
|
21
|
+
adtl-gateway init --config <adtl.yaml> [--write]
|
|
22
|
+
adtl-gateway hook --config <adtl.yaml> [--gateway-server <name>]
|
|
17
23
|
|
|
18
24
|
Runs an MCP server over stdio that forwards tool calls to the upstream
|
|
19
25
|
servers in the config, checking and labeling each call with ADTL.
|
|
@@ -23,9 +29,31 @@ Options:
|
|
|
23
29
|
--audit Log what would be stopped, but stop nothing
|
|
24
30
|
(same as gateway.mode: audit).
|
|
25
31
|
-v, --version Print the version and exit.
|
|
32
|
+
|
|
33
|
+
init:
|
|
34
|
+
Connects to every upstream, lists its tools, and drafts a labeling rule
|
|
35
|
+
for each tool the config doesn't cover yet (presets for GitHub,
|
|
36
|
+
filesystem and fetch; MCP tool hints otherwise). Existing entries are
|
|
37
|
+
never changed. Prints the updated config; review it before using it.
|
|
38
|
+
--write Update the config file in place (original kept as .bak).
|
|
39
|
+
|
|
40
|
+
hook:
|
|
41
|
+
The Claude Code hook for PreToolUse, PostToolUse and PostToolUseFailure.
|
|
42
|
+
Labels built-in tools (WebFetch, Read, Bash, ...) and MCP servers outside
|
|
43
|
+
the gateway, and runs the same policy on them, sharing the session label
|
|
44
|
+
with the gateway. Reads the hook event from stdin.
|
|
45
|
+
--gateway-server <name>
|
|
46
|
+
The gateway's name in Claude Code's MCP config; its
|
|
47
|
+
tools are checked by the gateway and skipped here.
|
|
48
|
+
Default: adtl.
|
|
49
|
+
|
|
26
50
|
-h, --help Show this help.`;
|
|
27
51
|
|
|
28
52
|
interface Args {
|
|
53
|
+
init?: boolean;
|
|
54
|
+
hook?: boolean;
|
|
55
|
+
gatewayServer?: string;
|
|
56
|
+
write?: boolean;
|
|
29
57
|
config?: string;
|
|
30
58
|
audit?: boolean;
|
|
31
59
|
help?: boolean;
|
|
@@ -36,11 +64,15 @@ function parseArgs(argv: string[]): Args {
|
|
|
36
64
|
const out: Args = {};
|
|
37
65
|
for (let i = 0; i < argv.length; i++) {
|
|
38
66
|
const a = argv[i];
|
|
39
|
-
if (
|
|
67
|
+
if (i === 0 && a === "init") out.init = true;
|
|
68
|
+
else if (i === 0 && a === "hook") out.hook = true;
|
|
69
|
+
else if (a === "--gateway-server" && out.hook) out.gatewayServer = argv[++i];
|
|
70
|
+
else if (a === "-h" || a === "--help") out.help = true;
|
|
40
71
|
else if (a === "-v" || a === "--version") out.version = true;
|
|
41
72
|
else if (a === "-c" || a === "--config") out.config = argv[++i];
|
|
42
73
|
else if (a?.startsWith("--config=")) out.config = a.slice("--config=".length);
|
|
43
74
|
else if (a === "--audit") out.audit = true;
|
|
75
|
+
else if (a === "--write" && out.init) out.write = true;
|
|
44
76
|
else throw new GatewayConfigError(`Unknown argument: ${a}\n\n${USAGE}`);
|
|
45
77
|
}
|
|
46
78
|
return out;
|
|
@@ -52,6 +84,8 @@ async function main(): Promise<void> {
|
|
|
52
84
|
if (args.help) return void console.log(USAGE);
|
|
53
85
|
if (args.version) return void console.log(GATEWAY_VERSION);
|
|
54
86
|
if (!args.config) throw new GatewayConfigError(`Missing --config\n\n${USAGE}`);
|
|
87
|
+
if (args.init) return init(args.config, args.write === true);
|
|
88
|
+
if (args.hook) return hook(args.config, args.gatewayServer);
|
|
55
89
|
|
|
56
90
|
const loaded = await loadGatewayConfig(args.config);
|
|
57
91
|
if (args.audit) loaded.gateway.mode = "audit";
|
|
@@ -77,8 +111,47 @@ async function main(): Promise<void> {
|
|
|
77
111
|
console.error(`[adtl-gateway] ${GATEWAY_VERSION} ready (session ${started.gateway.sessionId})`);
|
|
78
112
|
}
|
|
79
113
|
|
|
114
|
+
function summarize(r: InitResult, path: string, write: boolean): string {
|
|
115
|
+
const lines = r.reports.map((s) => {
|
|
116
|
+
const parts = [`${s.added.length} tools added`];
|
|
117
|
+
if (s.kept.length) parts.push(`${s.kept.length} kept`);
|
|
118
|
+
if (s.addedDefaults) parts.push("defaults added");
|
|
119
|
+
if (s.stale.length) parts.push(`no longer offered: ${s.stale.join(", ")}`);
|
|
120
|
+
return ` ${s.id} (${s.preset ? `preset ${s.preset}` : "from tool hints, review"}): ${parts.join("; ")}`;
|
|
121
|
+
});
|
|
122
|
+
for (const f of r.failed) lines.push(` ${f.id}: could not connect, skipped (${f.error})`);
|
|
123
|
+
const changed = r.reports.some((s) => s.added.length || s.addedDefaults);
|
|
124
|
+
const tail = !changed
|
|
125
|
+
? "Nothing to add."
|
|
126
|
+
: write
|
|
127
|
+
? `Updated ${path} (original saved as ${path}.bak). Review every line marked "review".`
|
|
128
|
+
: "Nothing written. Review the config above, then re-run with --write.";
|
|
129
|
+
return `${lines.join("\n")}\n${tail}`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function readStdin(): Promise<string> {
|
|
133
|
+
const chunks: Buffer[] = [];
|
|
134
|
+
for await (const c of process.stdin) chunks.push(c as Buffer);
|
|
135
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function hook(config: string, gatewayServer?: string): Promise<void> {
|
|
139
|
+
const out = await runHook({ configPath: config, stdin: await readStdin(), ...(gatewayServer ? { gatewayServer } : {}) });
|
|
140
|
+
if (out.stdout) process.stdout.write(`${JSON.stringify(out.stdout)}\n`);
|
|
141
|
+
if (out.stderr) process.stderr.write(`${out.stderr}\n`);
|
|
142
|
+
process.exitCode = out.exitCode;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function init(path: string, write: boolean): Promise<void> {
|
|
146
|
+
const result = await runInit({ path, write });
|
|
147
|
+
if (!write) process.stdout.write(result.text);
|
|
148
|
+
console.error(`[adtl-gateway] init\n${summarize(result, path, write)}`);
|
|
149
|
+
if (result.failed.length && !result.reports.length) process.exitCode = 1;
|
|
150
|
+
}
|
|
151
|
+
|
|
80
152
|
main().catch((err: unknown) => {
|
|
81
153
|
const known = err instanceof GatewayConfigError || err instanceof EnvExpansionError;
|
|
82
154
|
console.error(known ? `[adtl-gateway] ${(err as Error).message}` : err);
|
|
83
|
-
|
|
155
|
+
// For Claude Code hooks only exit code 2 stops the call; any other code lets it run.
|
|
156
|
+
process.exit(process.argv[2] === "hook" ? 2 : 1);
|
|
84
157
|
});
|