@praxis.guard/auditor-cli 0.0.49 → 0.0.50
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 +1 -0
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +5 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/mcp/gateway-config.d.ts +35 -0
- package/dist/mcp/gateway-config.d.ts.map +1 -0
- package/dist/mcp/gateway-config.js +111 -0
- package/dist/mcp/gateway-config.js.map +1 -0
- package/dist/mcp/gateway-evaluate.d.ts +32 -0
- package/dist/mcp/gateway-evaluate.d.ts.map +1 -0
- package/dist/mcp/gateway-evaluate.js +134 -0
- package/dist/mcp/gateway-evaluate.js.map +1 -0
- package/dist/mcp/gateway.d.ts +29 -0
- package/dist/mcp/gateway.d.ts.map +1 -0
- package/dist/mcp/gateway.js +188 -0
- package/dist/mcp/gateway.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -92,6 +92,7 @@ After `pnpm -C packages/auditor-cli build`, the **`auditor`** command is availab
|
|
|
92
92
|
| Command | Purpose |
|
|
93
93
|
|--------|---------|
|
|
94
94
|
| `auditor mcp` | **MCP stdio server** (registers tool `guard`). Use in Cursor `mcp.json` as the server command. |
|
|
95
|
+
| `auditor mcp gateway` | **MCP gateway server** (Phase 2a). Fronts the downstream MCP servers listed in `~/.praxis/gateway-servers.json` (override with `PRAXIS_GATEWAY_CONFIG`), re-exposes their tools namespaced as `<server>__<tool>`, and gates every proxied call through the guard policy before forwarding (READ allow, DESTRUCTIVE block, MUTATE deny + approval link). Additive and dormant: `auditor mcp` is unchanged. |
|
|
95
96
|
| `auditor hook before-shell` | Cursor hook: stdin JSON `{ "command", "cwd?" }` → stdout JSON `{ "permission", ... }` (fail-closed on crash). |
|
|
96
97
|
| `auditor doctor` | Prints resolved policy path, bridge dir, audit log path, `Node` version, and `PRAXIS_GUARD_AUDIT_LOG` status. |
|
|
97
98
|
| `auditor whoami` | Prints `uid`, profile email/display name, and token source via the `guardWhoAmI` callable (requires auth like `policies sync`). |
|
package/dist/cli/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAQA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAQA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuH1D"}
|
package/dist/cli/main.js
CHANGED
|
@@ -92,6 +92,11 @@ export async function runCli(argv) {
|
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
94
|
if (a0 === "mcp") {
|
|
95
|
+
if (a1 === "gateway") {
|
|
96
|
+
const { runMcpGateway } = await import("../mcp/gateway.js");
|
|
97
|
+
await runMcpGateway();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
95
100
|
const { runMcpStdioServer } = await import("../mcp/server.js");
|
|
96
101
|
await runMcpStdioServer();
|
|
97
102
|
return;
|
package/dist/cli/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type GatewayServer = {
|
|
2
|
+
/**
|
|
3
|
+
* Validated config key. Used BOTH as the exposed-tool namespace prefix
|
|
4
|
+
* (`${id}__${tool}`) AND as the policy identity (argv[1] in `["mcp", id, tool]`).
|
|
5
|
+
* Decision D4: the gateway keys policy on the config key, not a command/URL
|
|
6
|
+
* derivation, so `mcp.<id>.<tool>` rows gate it deterministically.
|
|
7
|
+
*/
|
|
8
|
+
id: string;
|
|
9
|
+
command: string;
|
|
10
|
+
args: string[];
|
|
11
|
+
env: Record<string, string> | undefined;
|
|
12
|
+
};
|
|
13
|
+
export type GatewayConfigResult = {
|
|
14
|
+
servers: GatewayServer[];
|
|
15
|
+
/** Non-fatal problems. Surfaced to stderr; never thrown into the request loop. */
|
|
16
|
+
errors: string[];
|
|
17
|
+
};
|
|
18
|
+
export declare function resolveGatewayConfigPath(): string;
|
|
19
|
+
/**
|
|
20
|
+
* Load + parse + validate the gateway config. Fail-safe by contract: any problem
|
|
21
|
+
* yields `{ servers, errors }` (possibly empty servers) and NEVER throws, so a bad
|
|
22
|
+
* config degrades the gateway to "fronts nothing" rather than crashing it.
|
|
23
|
+
*/
|
|
24
|
+
export declare function loadGatewayConfig(explicitPath?: string): Promise<GatewayConfigResult>;
|
|
25
|
+
/**
|
|
26
|
+
* Recover `(serverId, tool)` from an exposed `${serverId}__${tool}` name by
|
|
27
|
+
* matching against the KNOWN server ids, longest-first. We never naive-split on
|
|
28
|
+
* `__`, because a downstream tool name may itself contain `__` (decision D3).
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseExposedToolName(exposed: string, serverIds: readonly string[]): {
|
|
31
|
+
serverId: string;
|
|
32
|
+
tool: string;
|
|
33
|
+
} | null;
|
|
34
|
+
export declare function exposedToolName(serverId: string, tool: string): string;
|
|
35
|
+
//# sourceMappingURL=gateway-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-config.d.ts","sourceRoot":"","sources":["../../src/mcp/gateway-config.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,aAAa,GAAG;IAC1B;;;;;OAKG;IACH,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,kFAAkF;IAClF,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAUF,wBAAgB,wBAAwB,IAAI,MAAM,CAIjD;AAMD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA6D3F;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,SAAS,MAAM,EAAE,GAC3B;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAS3C;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtE"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
/**
|
|
6
|
+
* Downstream MCP servers the gateway fronts. Structurally identical to a
|
|
7
|
+
* `claude_desktop_config.json` `mcpServers` entry so the Phase 2b setup
|
|
8
|
+
* migration is a straight move (see docs/designs/claude-desktop-connector-fix-design.md).
|
|
9
|
+
*/
|
|
10
|
+
const GatewayServerSchema = z.object({
|
|
11
|
+
command: z.string().min(1),
|
|
12
|
+
args: z.array(z.string()).optional(),
|
|
13
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
14
|
+
});
|
|
15
|
+
const GatewayConfigFileSchema = z.object({
|
|
16
|
+
mcpServers: z.record(z.string(), GatewayServerSchema),
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* Exposed tool names and policy keys must be valid MCP tool-name characters and
|
|
20
|
+
* unambiguous. We REJECT (not silently rewrite) an invalid or colliding key so
|
|
21
|
+
* the config key stays identical everywhere it is used (exposed prefix == policy
|
|
22
|
+
* key); the error tells the user exactly what to rename.
|
|
23
|
+
*/
|
|
24
|
+
const SERVER_ID_RE = /^[a-zA-Z0-9_-]+$/;
|
|
25
|
+
export function resolveGatewayConfigPath() {
|
|
26
|
+
const override = process.env.PRAXIS_GATEWAY_CONFIG?.trim();
|
|
27
|
+
if (override)
|
|
28
|
+
return override;
|
|
29
|
+
return path.join(homedir(), ".praxis", "gateway-servers.json");
|
|
30
|
+
}
|
|
31
|
+
function errMsg(e) {
|
|
32
|
+
return e instanceof Error ? e.message : String(e);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Load + parse + validate the gateway config. Fail-safe by contract: any problem
|
|
36
|
+
* yields `{ servers, errors }` (possibly empty servers) and NEVER throws, so a bad
|
|
37
|
+
* config degrades the gateway to "fronts nothing" rather than crashing it.
|
|
38
|
+
*/
|
|
39
|
+
export async function loadGatewayConfig(explicitPath) {
|
|
40
|
+
const cfgPath = explicitPath?.trim() || resolveGatewayConfigPath();
|
|
41
|
+
let raw;
|
|
42
|
+
try {
|
|
43
|
+
raw = await readFile(cfgPath, "utf8");
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
if (e.code === "ENOENT") {
|
|
47
|
+
return {
|
|
48
|
+
servers: [],
|
|
49
|
+
errors: [`No gateway config at ${cfgPath}; no downstream servers to proxy.`],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return { servers: [], errors: [`Could not read gateway config at ${cfgPath}: ${errMsg(e)}`] };
|
|
53
|
+
}
|
|
54
|
+
let parsed;
|
|
55
|
+
try {
|
|
56
|
+
parsed = JSON.parse(raw);
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
return { servers: [], errors: [`Invalid JSON in gateway config ${cfgPath}: ${errMsg(e)}`] };
|
|
60
|
+
}
|
|
61
|
+
const result = GatewayConfigFileSchema.safeParse(parsed);
|
|
62
|
+
if (!result.success) {
|
|
63
|
+
return {
|
|
64
|
+
servers: [],
|
|
65
|
+
errors: [`Gateway config ${cfgPath} failed validation: ${result.error.message}`],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const errors = [];
|
|
69
|
+
const servers = [];
|
|
70
|
+
// Note: JSON object keys are already unique by the time Object.entries runs
|
|
71
|
+
// (JSON.parse keeps the last duplicate), so no duplicate-id check is needed here.
|
|
72
|
+
for (const [key, entry] of Object.entries(result.data.mcpServers)) {
|
|
73
|
+
if (!SERVER_ID_RE.test(key)) {
|
|
74
|
+
errors.push(`Skipping server "${key}": name must match [a-zA-Z0-9_-]. Rename it in ${cfgPath}.`);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
// Reject the tool-namespace separator in a server id. Exposed names are
|
|
78
|
+
// `${id}__${tool}` and parsed back by stripping a known id prefix; a `__`
|
|
79
|
+
// inside the id makes that recovery ambiguous (e.g. ids "a" and "a__b").
|
|
80
|
+
if (key.includes("__")) {
|
|
81
|
+
errors.push(`Skipping server "${key}": name must not contain "__" (the tool-namespace separator). Rename it in ${cfgPath}.`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
servers.push({
|
|
85
|
+
id: key,
|
|
86
|
+
command: entry.command,
|
|
87
|
+
args: entry.args ?? [],
|
|
88
|
+
env: entry.env,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return { servers, errors };
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Recover `(serverId, tool)` from an exposed `${serverId}__${tool}` name by
|
|
95
|
+
* matching against the KNOWN server ids, longest-first. We never naive-split on
|
|
96
|
+
* `__`, because a downstream tool name may itself contain `__` (decision D3).
|
|
97
|
+
*/
|
|
98
|
+
export function parseExposedToolName(exposed, serverIds) {
|
|
99
|
+
const candidates = [...serverIds].sort((a, b) => b.length - a.length);
|
|
100
|
+
for (const id of candidates) {
|
|
101
|
+
const prefix = `${id}__`;
|
|
102
|
+
if (exposed.startsWith(prefix) && exposed.length > prefix.length) {
|
|
103
|
+
return { serverId: id, tool: exposed.slice(prefix.length) };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
export function exposedToolName(serverId, tool) {
|
|
109
|
+
return `${serverId}__${tool}`;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=gateway-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-config.js","sourceRoot":"","sources":["../../src/mcp/gateway-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Tier } from "../policy/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Gateway gating decision for one proxied MCP call.
|
|
4
|
+
*
|
|
5
|
+
* This mirrors the orchestration in `hooks/run-before-mcp.ts` (the Cursor /
|
|
6
|
+
* Claude Code `beforeMCPExecution` hook) but returns a structured decision
|
|
7
|
+
* instead of writing the hook stdio contract. It REUSES the same building blocks
|
|
8
|
+
* read-only (`evaluateMcpProposal`, `resolveMutateHookPermission`, audit,
|
|
9
|
+
* telemetry), so MUTATE behaves identically to the hook hosts: deny + retry
|
|
10
|
+
* redemption by `(serverId, tool, tool_input)` fingerprint (decision D1). The
|
|
11
|
+
* ~30 lines of glue are intentionally not extracted from run-before-mcp.ts to
|
|
12
|
+
* avoid modifying that shared hook entrypoint.
|
|
13
|
+
*/
|
|
14
|
+
export type GatewayDecision = {
|
|
15
|
+
/** allow -> forward downstream. block -> synthesize a result, do not forward. */
|
|
16
|
+
decision: "allow" | "block";
|
|
17
|
+
tier: Tier;
|
|
18
|
+
reasons: string[];
|
|
19
|
+
ticketConsumed: boolean;
|
|
20
|
+
/** Present on a MUTATE block: where the human approves, and the request id. */
|
|
21
|
+
inlineApproval: {
|
|
22
|
+
request_id: string;
|
|
23
|
+
open_url: string;
|
|
24
|
+
} | null;
|
|
25
|
+
};
|
|
26
|
+
export declare function evaluateGatewayCall(input: {
|
|
27
|
+
serverId: string;
|
|
28
|
+
tool: string;
|
|
29
|
+
toolInput: unknown;
|
|
30
|
+
cwd?: string;
|
|
31
|
+
}): Promise<GatewayDecision>;
|
|
32
|
+
//# sourceMappingURL=gateway-evaluate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-evaluate.d.ts","sourceRoot":"","sources":["../../src/mcp/gateway-evaluate.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,KAAK,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAWvF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,iFAAiF;IACjF,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC;IAC5B,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,+EAA+E;IAC/E,cAAc,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACjE,CAAC;AAEF,wBAAsB,mBAAmB,CAAC,KAAK,EAAE;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC,eAAe,CAAC,CAiF3B"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { loadPoliciesV1, readPoliciesV1Revision } from "../policy/index.js";
|
|
2
|
+
import { appendAuditJsonl } from "../audit/jsonl.js";
|
|
3
|
+
import { getInstallId } from "../cli/install-id.js";
|
|
4
|
+
import { evaluateMcpProposal } from "../shell/evaluate.js";
|
|
5
|
+
import { resolveGuardStorageRoot } from "../bridge/guard-storage-root.js";
|
|
6
|
+
import { toolInputSha256 } from "../approval/fingerprint.js";
|
|
7
|
+
import { stringifyToolInput } from "../hooks/before-mcp-argv.js";
|
|
8
|
+
import { resolveMutateHookPermission } from "../hooks/before-mcp-mutate.js";
|
|
9
|
+
import { sendGuardEvent } from "../telemetry/guard-events.js";
|
|
10
|
+
export async function evaluateGatewayCall(input) {
|
|
11
|
+
const started = performance.now();
|
|
12
|
+
const argv = ["mcp", input.serverId, input.tool];
|
|
13
|
+
const rawToolName = `${input.serverId}__${input.tool}`;
|
|
14
|
+
const toolInputStr = stringifyToolInput(input.toolInput);
|
|
15
|
+
const [policy, policyRevision] = await Promise.all([loadPoliciesV1(), readPoliciesV1Revision()]);
|
|
16
|
+
const { skipped, evaluation } = evaluateMcpProposal(policy, argv);
|
|
17
|
+
const { tier, classification, flags } = evaluation;
|
|
18
|
+
const reasons = evaluation.reasons.map((r) => r.message);
|
|
19
|
+
const storageRoot = resolveGuardStorageRoot(input.cwd);
|
|
20
|
+
const toolInputHash = toolInputSha256(input.toolInput);
|
|
21
|
+
if (skipped) {
|
|
22
|
+
await emitTelemetry({
|
|
23
|
+
argv,
|
|
24
|
+
rawToolName,
|
|
25
|
+
toolInputStr,
|
|
26
|
+
tier,
|
|
27
|
+
decision: "allow",
|
|
28
|
+
status: "skipped",
|
|
29
|
+
reasons,
|
|
30
|
+
policyRevision,
|
|
31
|
+
storageRoot,
|
|
32
|
+
ticketConsumed: false,
|
|
33
|
+
inlineApproval: null,
|
|
34
|
+
latencyMs: performance.now() - started,
|
|
35
|
+
skipped: true,
|
|
36
|
+
classification,
|
|
37
|
+
flags,
|
|
38
|
+
toolInputHash,
|
|
39
|
+
});
|
|
40
|
+
return { decision: "allow", tier, reasons, ticketConsumed: false, inlineApproval: null };
|
|
41
|
+
}
|
|
42
|
+
let permission = tier === "READ" ? "allow" : "deny";
|
|
43
|
+
let ticketConsumed = false;
|
|
44
|
+
let inlineApproval = null;
|
|
45
|
+
let finalReasons = reasons;
|
|
46
|
+
// MUTATE only: deny + retry redemption. DESTRUCTIVE stays deny -> block.
|
|
47
|
+
if (permission === "deny" && tier === "MUTATE") {
|
|
48
|
+
const mutate = await resolveMutateHookPermission({
|
|
49
|
+
argv,
|
|
50
|
+
tier,
|
|
51
|
+
storageRoot,
|
|
52
|
+
toolInputHash,
|
|
53
|
+
rawToolName,
|
|
54
|
+
toolInputPreview: toolInputStr.slice(0, 200),
|
|
55
|
+
policyRevision,
|
|
56
|
+
initialReasons: reasons,
|
|
57
|
+
});
|
|
58
|
+
// resolveMutateHookPermission's type allows "ask"; for the gateway that is a block.
|
|
59
|
+
permission = mutate.permission === "allow" ? "allow" : "deny";
|
|
60
|
+
ticketConsumed = mutate.ticketConsumed;
|
|
61
|
+
inlineApproval = mutate.inlineApproval;
|
|
62
|
+
finalReasons = mutate.reasons;
|
|
63
|
+
}
|
|
64
|
+
const decision = permission === "allow" ? "allow" : "block";
|
|
65
|
+
await emitTelemetry({
|
|
66
|
+
argv,
|
|
67
|
+
rawToolName,
|
|
68
|
+
toolInputStr,
|
|
69
|
+
tier,
|
|
70
|
+
decision,
|
|
71
|
+
status: decision === "allow" ? "passed" : "blocked",
|
|
72
|
+
reasons: finalReasons,
|
|
73
|
+
policyRevision,
|
|
74
|
+
storageRoot,
|
|
75
|
+
ticketConsumed,
|
|
76
|
+
inlineApproval,
|
|
77
|
+
latencyMs: performance.now() - started,
|
|
78
|
+
skipped: false,
|
|
79
|
+
classification,
|
|
80
|
+
flags,
|
|
81
|
+
toolInputHash,
|
|
82
|
+
});
|
|
83
|
+
return { decision, tier, reasons: finalReasons, ticketConsumed, inlineApproval };
|
|
84
|
+
}
|
|
85
|
+
/** Local audit.jsonl (full fidelity) + redacted egress event. Never throws. */
|
|
86
|
+
async function emitTelemetry(e) {
|
|
87
|
+
try {
|
|
88
|
+
await appendAuditJsonl({
|
|
89
|
+
ts: new Date().toISOString(),
|
|
90
|
+
hook: "mcpGateway",
|
|
91
|
+
tool_name: e.rawToolName,
|
|
92
|
+
bare_tool: e.argv[2] ?? null,
|
|
93
|
+
tool_input: e.toolInputStr.slice(0, 8000),
|
|
94
|
+
argv: e.argv,
|
|
95
|
+
classification: e.classification,
|
|
96
|
+
flags: e.flags,
|
|
97
|
+
tier: e.tier,
|
|
98
|
+
permission: e.decision === "allow" ? "allow" : "deny",
|
|
99
|
+
ticketConsumed: e.ticketConsumed,
|
|
100
|
+
inline_request_id: e.inlineApproval?.request_id ?? null,
|
|
101
|
+
tool_input_sha256: e.toolInputHash,
|
|
102
|
+
reasons: e.reasons,
|
|
103
|
+
status: e.status,
|
|
104
|
+
...(e.skipped ? { skipped: true, skip_reason: "mcp_policy_unmatched" } : {}),
|
|
105
|
+
latency_ms: e.latencyMs,
|
|
106
|
+
}, e.storageRoot);
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
process.stderr.write(`[auditor] gateway audit append failed: ${String(err)}\n`);
|
|
110
|
+
}
|
|
111
|
+
await sendGuardEvent({
|
|
112
|
+
ts: new Date().toISOString(),
|
|
113
|
+
status: e.status,
|
|
114
|
+
...(e.skipped ? { skipped: true, skip_reason: "mcp_policy_unmatched" } : {}),
|
|
115
|
+
tool: "auditor-mcp-gateway",
|
|
116
|
+
command_path: e.argv[1] ?? null,
|
|
117
|
+
verb: e.argv[2] ?? null,
|
|
118
|
+
resource: e.toolInputStr ? e.toolInputStr.slice(0, 500) : null,
|
|
119
|
+
reason: e.reasons[0] ?? null,
|
|
120
|
+
cmd: e.rawToolName,
|
|
121
|
+
tier: e.tier,
|
|
122
|
+
decision: e.decision,
|
|
123
|
+
latency_ms: e.latencyMs,
|
|
124
|
+
installId: getInstallId(),
|
|
125
|
+
kind: "mcp",
|
|
126
|
+
...(e.policyRevision !== null ? { policy_revision: e.policyRevision } : {}),
|
|
127
|
+
meta: {
|
|
128
|
+
hook: "mcpGateway",
|
|
129
|
+
ticketConsumed: e.ticketConsumed,
|
|
130
|
+
approval_request_id: e.inlineApproval?.request_id ?? null,
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=gateway-evaluate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-evaluate.js","sourceRoot":"","sources":["../../src/mcp/gateway-evaluate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { type CallToolResult, type Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* One downstream MCP server, abstracted so tests can inject fakes without
|
|
5
|
+
* spawning real subprocesses. The real implementation is `connectStdioServer`.
|
|
6
|
+
*/
|
|
7
|
+
export interface GatewayConnection {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
/** Tools advertised by this downstream server (fetched at connect). */
|
|
10
|
+
tools: Tool[];
|
|
11
|
+
callTool(tool: string, args: Record<string, unknown>): Promise<CallToolResult>;
|
|
12
|
+
/** Re-establish the underlying transport after a failure. Returns false if it cannot. */
|
|
13
|
+
reconnect(): Promise<boolean>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Build the low-level MCP `Server` that fronts the given downstream connections.
|
|
17
|
+
* Exported for tests (inject fake connections); `runMcpGateway` wires the real ones.
|
|
18
|
+
* Uses the low-level `Server` (not `McpServer`) so downstream JSON Schemas pass
|
|
19
|
+
* through verbatim with no JSON-Schema -> Zod conversion (decision D6).
|
|
20
|
+
*/
|
|
21
|
+
export declare function createGatewayServer(connections: GatewayConnection[]): Server;
|
|
22
|
+
/**
|
|
23
|
+
* Connect to all configured downstream servers in parallel (eager, each bounded
|
|
24
|
+
* by a timeout), skipping and surfacing any that fail so one bad server does not
|
|
25
|
+
* sink the gateway (decision D2). Then serve the combined namespaced tool set on
|
|
26
|
+
* stdio. Blocks until disconnect.
|
|
27
|
+
*/
|
|
28
|
+
export declare function runMcpGateway(explicitConfigPath?: string): Promise<void>;
|
|
29
|
+
//# sourceMappingURL=gateway.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../src/mcp/gateway.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,IAAI,EACV,MAAM,oCAAoC,CAAC;AAkB5C;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAC/E,yFAAyF;IACzF,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/B;AAqFD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAyF5E;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB9E"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import { StdioClientTransport, getDefaultEnvironment, } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
import { AUDITOR_CLI_VERSION } from "../runtime/version.js";
|
|
7
|
+
import { exposedToolName, loadGatewayConfig, parseExposedToolName, } from "./gateway-config.js";
|
|
8
|
+
import { evaluateGatewayCall } from "./gateway-evaluate.js";
|
|
9
|
+
const CONNECT_TIMEOUT_MS = 10_000;
|
|
10
|
+
/** stderr only — stdout is the MCP protocol channel. */
|
|
11
|
+
function logErr(line) {
|
|
12
|
+
process.stderr.write(`[auditor-gateway] ${line}\n`);
|
|
13
|
+
}
|
|
14
|
+
function withTimeout(p, ms, label) {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
const t = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
|
|
17
|
+
p.then((v) => {
|
|
18
|
+
clearTimeout(t);
|
|
19
|
+
resolve(v);
|
|
20
|
+
}, (e) => {
|
|
21
|
+
clearTimeout(t);
|
|
22
|
+
reject(e);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/** Real stdio-backed connection to a downstream MCP server. */
|
|
27
|
+
async function connectStdioServer(server) {
|
|
28
|
+
const spawn = () => new StdioClientTransport({
|
|
29
|
+
command: server.command,
|
|
30
|
+
args: server.args,
|
|
31
|
+
env: server.env ? { ...getDefaultEnvironment(), ...server.env } : undefined,
|
|
32
|
+
});
|
|
33
|
+
let client = new Client({ name: `praxis-gateway-client-${server.id}`, version: AUDITOR_CLI_VERSION });
|
|
34
|
+
await withTimeout(client.connect(spawn()), CONNECT_TIMEOUT_MS, `connect ${server.id}`);
|
|
35
|
+
const listed = await withTimeout(client.listTools(), CONNECT_TIMEOUT_MS, `listTools ${server.id}`);
|
|
36
|
+
const conn = {
|
|
37
|
+
id: server.id,
|
|
38
|
+
tools: listed.tools,
|
|
39
|
+
async callTool(tool, args) {
|
|
40
|
+
return (await client.callTool({ name: tool, arguments: args }));
|
|
41
|
+
},
|
|
42
|
+
async reconnect() {
|
|
43
|
+
try {
|
|
44
|
+
// Close the old client first so the dead subprocess + pipes are reaped;
|
|
45
|
+
// otherwise a flapping downstream leaks one child process per failure.
|
|
46
|
+
await client.close().catch(() => { });
|
|
47
|
+
client = new Client({
|
|
48
|
+
name: `praxis-gateway-client-${server.id}`,
|
|
49
|
+
version: AUDITOR_CLI_VERSION,
|
|
50
|
+
});
|
|
51
|
+
await withTimeout(client.connect(spawn()), CONNECT_TIMEOUT_MS, `reconnect ${server.id}`);
|
|
52
|
+
const re = await withTimeout(client.listTools(), CONNECT_TIMEOUT_MS, `listTools ${server.id}`);
|
|
53
|
+
conn.tools = re.tools;
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
logErr(`reconnect to "${server.id}" failed: ${String(e)}`);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
return conn;
|
|
63
|
+
}
|
|
64
|
+
function textResult(text, isError) {
|
|
65
|
+
return { content: [{ type: "text", text }], isError };
|
|
66
|
+
}
|
|
67
|
+
/** Bound downstream-supplied error text so a hostile downstream can't flood or spoof the result. */
|
|
68
|
+
function truncErr(e) {
|
|
69
|
+
return String(e).slice(0, 300);
|
|
70
|
+
}
|
|
71
|
+
function blockedResult(serverId, tool, d) {
|
|
72
|
+
const reason = d.reasons[0] ?? "policy";
|
|
73
|
+
if (d.inlineApproval) {
|
|
74
|
+
return textResult(`Praxis guard requires human approval for ${serverId}__${tool} (tier=${d.tier}). ` +
|
|
75
|
+
`Approve at ${d.inlineApproval.open_url} then retry this exact tool call. ` +
|
|
76
|
+
`(request ${d.inlineApproval.request_id})`, true);
|
|
77
|
+
}
|
|
78
|
+
return textResult(`Praxis guard blocked ${serverId}__${tool} (tier=${d.tier}): ${reason}. ` +
|
|
79
|
+
(d.tier === "DESTRUCTIVE" ? "Destructive actions cannot be auto-approved." : ""), true);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Build the low-level MCP `Server` that fronts the given downstream connections.
|
|
83
|
+
* Exported for tests (inject fake connections); `runMcpGateway` wires the real ones.
|
|
84
|
+
* Uses the low-level `Server` (not `McpServer`) so downstream JSON Schemas pass
|
|
85
|
+
* through verbatim with no JSON-Schema -> Zod conversion (decision D6).
|
|
86
|
+
*/
|
|
87
|
+
export function createGatewayServer(connections) {
|
|
88
|
+
const byId = new Map(connections.map((c) => [c.id, c]));
|
|
89
|
+
const ids = connections.map((c) => c.id);
|
|
90
|
+
const server = new Server({ name: "praxis-guard-gateway", version: AUDITOR_CLI_VERSION }, { capabilities: { tools: {} } });
|
|
91
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
92
|
+
const tools = [];
|
|
93
|
+
for (const conn of connections) {
|
|
94
|
+
for (const t of conn.tools) {
|
|
95
|
+
tools.push({ ...t, name: exposedToolName(conn.id, t.name) });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { tools };
|
|
99
|
+
});
|
|
100
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
101
|
+
// Fail closed: any unexpected throw in parsing/gating (e.g. a corrupt policy
|
|
102
|
+
// file making evaluateGatewayCall reject) blocks the call rather than letting
|
|
103
|
+
// it slip through. Mirrors the hook's failClosedBeforeMcpHookErrorResponse.
|
|
104
|
+
try {
|
|
105
|
+
const exposed = request.params.name;
|
|
106
|
+
const args = (request.params.arguments ?? {});
|
|
107
|
+
const parsed = parseExposedToolName(exposed, ids);
|
|
108
|
+
if (!parsed) {
|
|
109
|
+
return textResult(`Praxis gateway: unknown tool "${exposed}".`, true);
|
|
110
|
+
}
|
|
111
|
+
const conn = byId.get(parsed.serverId);
|
|
112
|
+
if (!conn) {
|
|
113
|
+
return textResult(`Praxis gateway: downstream "${parsed.serverId}" is not connected.`, true);
|
|
114
|
+
}
|
|
115
|
+
// Only proxy tools the downstream actually advertised. Without this, any
|
|
116
|
+
// `knownServer__anything` would be forwarded (and classified skipped->allow),
|
|
117
|
+
// proxying tools the downstream never listed. The gateway owns this check, not
|
|
118
|
+
// the downstream (found in QA: ISSUE-001).
|
|
119
|
+
if (!conn.tools.some((t) => t.name === parsed.tool)) {
|
|
120
|
+
return textResult(`Praxis gateway: "${parsed.serverId}" does not expose a tool named "${parsed.tool}".`, true);
|
|
121
|
+
}
|
|
122
|
+
const decision = await evaluateGatewayCall({
|
|
123
|
+
serverId: parsed.serverId,
|
|
124
|
+
tool: parsed.tool,
|
|
125
|
+
toolInput: args,
|
|
126
|
+
});
|
|
127
|
+
if (decision.decision === "block") {
|
|
128
|
+
return blockedResult(parsed.serverId, parsed.tool, decision);
|
|
129
|
+
}
|
|
130
|
+
// allow -> forward, returning the downstream result VERBATIM (decision D5).
|
|
131
|
+
try {
|
|
132
|
+
return await conn.callTool(parsed.tool, args);
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
logErr(`downstream "${parsed.serverId}" call failed: ${truncErr(e)}; reconnecting`);
|
|
136
|
+
const ok = await conn.reconnect();
|
|
137
|
+
// Retry ONLY idempotent (READ) calls. A redeemed MUTATE/DESTRUCTIVE must not
|
|
138
|
+
// be auto-replayed: if the first attempt reached the downstream and mutated
|
|
139
|
+
// state before the transport died, a blind retry double-applies it. Reconnect
|
|
140
|
+
// still heals the connection for the next call.
|
|
141
|
+
if (ok && decision.tier === "READ") {
|
|
142
|
+
try {
|
|
143
|
+
return await conn.callTool(parsed.tool, args);
|
|
144
|
+
}
|
|
145
|
+
catch (e2) {
|
|
146
|
+
return textResult(`Praxis gateway: downstream "${parsed.serverId}" error after reconnect: ${truncErr(e2)}`, true);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return textResult(`Praxis gateway: downstream "${parsed.serverId}" call failed` +
|
|
150
|
+
(ok ? " (reconnected; not retried to avoid double-execution)" : " and is unavailable") +
|
|
151
|
+
`: ${truncErr(e)}`, true);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
logErr(`gating error for "${request.params?.name}": ${truncErr(e)}`);
|
|
156
|
+
return textResult("Praxis gateway: guard evaluation failed; blocking this call (fail-closed).", true);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
return server;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Connect to all configured downstream servers in parallel (eager, each bounded
|
|
163
|
+
* by a timeout), skipping and surfacing any that fail so one bad server does not
|
|
164
|
+
* sink the gateway (decision D2). Then serve the combined namespaced tool set on
|
|
165
|
+
* stdio. Blocks until disconnect.
|
|
166
|
+
*/
|
|
167
|
+
export async function runMcpGateway(explicitConfigPath) {
|
|
168
|
+
const { servers, errors } = await loadGatewayConfig(explicitConfigPath);
|
|
169
|
+
for (const e of errors)
|
|
170
|
+
logErr(e);
|
|
171
|
+
const settled = await Promise.allSettled(servers.map((s) => connectStdioServer(s)));
|
|
172
|
+
const connections = [];
|
|
173
|
+
settled.forEach((r, i) => {
|
|
174
|
+
if (r.status === "fulfilled") {
|
|
175
|
+
connections.push(r.value);
|
|
176
|
+
logErr(`connected "${servers[i].id}" (${r.value.tools.length} tools)`);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
logErr(`skipping "${servers[i].id}": ${String(r.reason)}`);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
if (connections.length === 0) {
|
|
183
|
+
logErr("no downstream servers connected; gateway will expose no tools.");
|
|
184
|
+
}
|
|
185
|
+
const server = createGatewayServer(connections);
|
|
186
|
+
await server.connect(new StdioServerTransport());
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=gateway.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway.js","sourceRoot":"","sources":["../../src/mcp/gateway.ts"],"names":[],"mappings":"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"}
|