protect-mcp 0.9.6 → 0.9.7

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/dist/cli.js CHANGED
@@ -6414,6 +6414,277 @@ var init_sample = __esm({
6414
6414
  }
6415
6415
  });
6416
6416
 
6417
+ // src/mcp-server.ts
6418
+ var mcp_server_exports = {};
6419
+ __export(mcp_server_exports, {
6420
+ runMcpServer: () => runMcpServer
6421
+ });
6422
+ async function loadArtifacts() {
6423
+ if (artifacts) return artifacts;
6424
+ const moduleName = "@veritasacta/artifacts";
6425
+ const mod2 = await import(
6426
+ /* @vite-ignore */
6427
+ moduleName
6428
+ );
6429
+ artifacts = mod2;
6430
+ return mod2;
6431
+ }
6432
+ function buildReceiptPayload(args) {
6433
+ return {
6434
+ tool: args.tool,
6435
+ decision: args.decision,
6436
+ reason_code: args.reason_code ?? (args.decision === "deny" ? "policy_denied" : "post_execution_receipt"),
6437
+ policy_digest: args.policy_digest ?? "none",
6438
+ scope: args.request_id,
6439
+ mode: "enforce",
6440
+ request_id: args.request_id,
6441
+ spec: "draft-farley-acta-signed-receipts-01",
6442
+ issuer_certification: "self-signed",
6443
+ public_key: args.public_key
6444
+ };
6445
+ }
6446
+ function newRequestId() {
6447
+ return `mcp-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
6448
+ }
6449
+ async function callEvaluate(args) {
6450
+ const tool = typeof args.tool === "string" ? args.tool : "";
6451
+ const policySource = typeof args.policy === "string" ? args.policy : "";
6452
+ const input = args.input && typeof args.input === "object" ? args.input : {};
6453
+ if (!tool) return { error: "missing_tool" };
6454
+ if (!policySource.trim()) return { allowed: false, decision: "deny", reason: "no policy provided (fail-closed)", policy_digest: "none" };
6455
+ const policySet = policySetFromSource(policySource);
6456
+ const decision = await evaluateCedar(
6457
+ policySet,
6458
+ { tool, tier: "unknown", context: { ...input, input }, toolInput: input },
6459
+ void 0,
6460
+ { failClosed: true }
6461
+ );
6462
+ return {
6463
+ allowed: decision.allowed,
6464
+ decision: decision.allowed ? "allow" : "deny",
6465
+ reason: decision.reason || (decision.allowed ? "allowed" : "denied by policy"),
6466
+ policy_digest: policySet.digest
6467
+ };
6468
+ }
6469
+ async function callSign(args) {
6470
+ const tool = typeof args.tool === "string" ? args.tool : "";
6471
+ const decision = args.decision === "deny" ? "deny" : args.decision === "allow" ? "allow" : null;
6472
+ if (!tool) return { error: "missing_tool" };
6473
+ if (!decision) return { error: 'decision must be "allow" or "deny"' };
6474
+ const a = await loadArtifacts();
6475
+ let privateKey = typeof args.private_key_hex === "string" ? args.private_key_hex : "";
6476
+ let publicKey;
6477
+ let ephemeral = false;
6478
+ if (privateKey) {
6479
+ publicKey = a.getPublicKey(privateKey);
6480
+ } else {
6481
+ const kp = a.generateKeypair();
6482
+ privateKey = kp.privateKey;
6483
+ publicKey = kp.publicKey;
6484
+ ephemeral = true;
6485
+ }
6486
+ const requestId = newRequestId();
6487
+ const payload = buildReceiptPayload({
6488
+ tool,
6489
+ decision,
6490
+ reason_code: typeof args.reason_code === "string" ? args.reason_code : void 0,
6491
+ policy_digest: typeof args.policy_digest === "string" ? args.policy_digest : void 0,
6492
+ request_id: requestId,
6493
+ public_key: publicKey
6494
+ });
6495
+ const artifactType = decision === "deny" ? "gateway_restraint" : "decision_receipt";
6496
+ const { artifact } = a.createSignedArtifact(artifactType, payload, privateKey, { kid: "mcp", issuer: "protect-mcp" });
6497
+ return { receipt: artifact, artifact_type: artifactType, public_key: publicKey, ephemeral };
6498
+ }
6499
+ async function callVerify(args) {
6500
+ const receipt = args.receipt;
6501
+ if (!receipt || typeof receipt !== "object") return { valid: false, error: "missing_receipt" };
6502
+ const a = await loadArtifacts();
6503
+ const embedded = receipt.payload?.public_key;
6504
+ const key = typeof args.public_key_hex === "string" && args.public_key_hex ? args.public_key_hex : typeof embedded === "string" ? embedded : null;
6505
+ if (!key) {
6506
+ return { valid: false, error: "no_public_key", type: receipt.type ?? "unknown", kid: null, issuer: null };
6507
+ }
6508
+ const result = a.verifyArtifact(receipt, key);
6509
+ return {
6510
+ valid: !!result.valid,
6511
+ error: result.valid ? null : result.error || "invalid_signature",
6512
+ type: receipt.type ?? "unknown",
6513
+ kid: receipt.kid ?? null,
6514
+ issuer: receipt.issuer ?? null
6515
+ };
6516
+ }
6517
+ async function callSelfTest() {
6518
+ const details = {};
6519
+ const denied = await callEvaluate({ tool: "Bash", input: { command: "rm -rf /" }, policy: SELF_TEST_POLICY });
6520
+ const gateDeniesForbidden = denied.allowed === false;
6521
+ details.forbidden_action = { tool: "Bash", command: "rm -rf /", allowed: denied.allowed, reason: denied.reason };
6522
+ const allowed = await callEvaluate({ tool: "Read", input: { path: "./notes.txt" }, policy: SELF_TEST_POLICY });
6523
+ details.safe_action = { tool: "Read", allowed: allowed.allowed };
6524
+ let signVerifyRoundtrip = false;
6525
+ try {
6526
+ const signed = await callSign({ tool: "Bash", decision: "deny", reason_code: "self_test" });
6527
+ if (signed.receipt && signed.public_key) {
6528
+ const ok = await callVerify({ receipt: signed.receipt, public_key_hex: signed.public_key });
6529
+ const tampered = JSON.parse(JSON.stringify(signed.receipt));
6530
+ if (tampered.payload) tampered.payload.tool = "tampered";
6531
+ const bad = await callVerify({ receipt: tampered, public_key_hex: signed.public_key });
6532
+ signVerifyRoundtrip = ok.valid === true && bad.valid === false;
6533
+ details.sign_verify = { valid_verifies: ok.valid, tampered_rejected: bad.valid === false };
6534
+ }
6535
+ } catch (err) {
6536
+ details.sign_verify_error = err instanceof Error ? err.message : String(err);
6537
+ }
6538
+ return {
6539
+ ok: gateDeniesForbidden && allowed.allowed === true && signVerifyRoundtrip,
6540
+ gate_denies_forbidden: gateDeniesForbidden,
6541
+ sign_verify_roundtrip: signVerifyRoundtrip,
6542
+ details,
6543
+ note: "No network was contacted."
6544
+ };
6545
+ }
6546
+ function textResult(id, value) {
6547
+ return JSON.stringify({ jsonrpc: "2.0", id, result: { content: [{ type: "text", text: JSON.stringify(value, null, 2) }] } });
6548
+ }
6549
+ async function handleRequest(request) {
6550
+ if (request.method === "initialize") {
6551
+ return JSON.stringify({
6552
+ jsonrpc: "2.0",
6553
+ id: request.id,
6554
+ result: {
6555
+ protocolVersion: "2024-11-05",
6556
+ serverInfo: { name: "protect-mcp", version: process.env.PROTECT_MCP_VERSION || "0.9.7" },
6557
+ capabilities: { tools: {} }
6558
+ }
6559
+ });
6560
+ }
6561
+ if (request.method === "notifications/initialized") return "";
6562
+ if (request.method === "tools/list") {
6563
+ return JSON.stringify({ jsonrpc: "2.0", id: request.id, result: { tools: TOOLS } });
6564
+ }
6565
+ if (request.method === "tools/call") {
6566
+ const name = request.params?.name || "";
6567
+ const args = request.params?.arguments || {};
6568
+ try {
6569
+ let value;
6570
+ switch (name) {
6571
+ case "evaluate_action":
6572
+ value = await callEvaluate(args);
6573
+ break;
6574
+ case "sign_decision":
6575
+ value = await callSign(args);
6576
+ break;
6577
+ case "verify_receipt":
6578
+ value = await callVerify(args);
6579
+ break;
6580
+ case "self_test":
6581
+ value = await callSelfTest();
6582
+ break;
6583
+ default:
6584
+ value = { error: `unknown tool: ${name}` };
6585
+ }
6586
+ return textResult(request.id, value);
6587
+ } catch (err) {
6588
+ return textResult(request.id, { error: err instanceof Error ? err.message : String(err) });
6589
+ }
6590
+ }
6591
+ if (request.id !== void 0) {
6592
+ return JSON.stringify({ jsonrpc: "2.0", id: request.id, error: { code: -32601, message: `Method not found: ${request.method}` } });
6593
+ }
6594
+ return "";
6595
+ }
6596
+ async function runMcpServer() {
6597
+ const rl = (0, import_node_readline2.createInterface)({ input: process.stdin, crlfDelay: Infinity });
6598
+ let chain = Promise.resolve();
6599
+ rl.on("line", (line) => {
6600
+ const trimmed = line.trim();
6601
+ if (!trimmed) return;
6602
+ chain = chain.then(async () => {
6603
+ try {
6604
+ const request = JSON.parse(trimmed);
6605
+ const response = await handleRequest(request);
6606
+ if (response) process.stdout.write(response + "\n");
6607
+ } catch {
6608
+ }
6609
+ });
6610
+ });
6611
+ process.stderr.write("[PROTECT_MCP] gate MCP server started \u2014 4 tools: evaluate_action, sign_decision, verify_receipt, self_test\n");
6612
+ await new Promise((resolve) => rl.on("close", () => resolve()));
6613
+ }
6614
+ var import_node_readline2, artifacts, RO, TOOLS, SELF_TEST_POLICY;
6615
+ var init_mcp_server = __esm({
6616
+ "src/mcp-server.ts"() {
6617
+ "use strict";
6618
+ import_node_readline2 = require("readline");
6619
+ init_cedar_evaluator();
6620
+ artifacts = null;
6621
+ RO = { readOnlyHint: true, destructiveHint: false, openWorldHint: false };
6622
+ TOOLS = [
6623
+ {
6624
+ name: "evaluate_action",
6625
+ description: `Decide whether a proposed agent tool call is allowed by a Cedar policy, fail-closed. Evaluates the call against the policy the same way the protect-mcp gate does at runtime, and on any policy error the decision is DENY (never a silent allow). Inputs: tool (the tool name, e.g. "Bash" or "send_email"), input (the tool's arguments object; the policy sees it at context.input.*), and policy (inline Cedar source text). Returns JSON { allowed: boolean, decision: "allow" | "deny", reason: string, policy_digest: string (sha256 prefix of the policy) }. A missing or unparseable policy denies. Use this before an agent acts; pair with sign_decision to make the decision auditable.`,
6626
+ inputSchema: {
6627
+ type: "object",
6628
+ properties: {
6629
+ tool: { type: "string", description: 'The tool name being evaluated, e.g. "Bash", "Write", "send_email".' },
6630
+ input: { type: "object", description: "The tool's arguments. Reachable in the policy at context.input.* (e.g. context.input.command)." },
6631
+ policy: { type: "string", description: 'Inline Cedar policy source. Use MCP::Tool::call as the action and Tool::"<name>" as the resource.' }
6632
+ },
6633
+ required: ["tool", "policy"]
6634
+ },
6635
+ annotations: { title: "Evaluate an action against a Cedar policy", idempotentHint: true, ...RO }
6636
+ },
6637
+ {
6638
+ name: "sign_decision",
6639
+ description: `Turn a gate decision into an Ed25519 signed receipt (Veritas Acta format, JCS-canonical). A denial signs a gateway_restraint artifact; an allow signs a decision_receipt. This is what makes 'what the agent was blocked from doing' provable after the fact. Inputs: tool (required), decision ("allow" | "deny", required), reason_code (optional), policy_digest (optional), and private_key_hex (optional 64-hex Ed25519 secret; if omitted, an ephemeral keypair is generated and its public key is returned so the receipt still verifies). Returns JSON { receipt: object (the signed artifact), artifact_type: "decision_receipt" | "gateway_restraint", public_key: string (hex, pass this to verify_receipt or pin it), ephemeral: boolean }. Writes nothing to disk and contacts no network.`,
6640
+ inputSchema: {
6641
+ type: "object",
6642
+ properties: {
6643
+ tool: { type: "string", description: "The tool the decision is about." },
6644
+ decision: { type: "string", enum: ["allow", "deny"], description: "The gate decision to receipt." },
6645
+ reason_code: { type: "string", description: 'Optional machine reason, e.g. "restricted_list" or "post_execution_receipt".' },
6646
+ policy_digest: { type: "string", description: "Optional digest of the policy that produced the decision (e.g. from evaluate_action)." },
6647
+ private_key_hex: { type: "string", description: "Optional 64-character hex Ed25519 secret key. If omitted, an ephemeral key is generated and its public key is returned." }
6648
+ },
6649
+ required: ["tool", "decision"]
6650
+ },
6651
+ // Not idempotent: an ephemeral key and a fresh request_id are minted per call.
6652
+ annotations: { title: "Sign a decision into a receipt", idempotentHint: false, ...RO }
6653
+ },
6654
+ {
6655
+ name: "verify_receipt",
6656
+ description: 'Verify a signed receipt offline against a public key. No network, no accounts: the Ed25519 signature is checked over the canonical bytes. Inputs: receipt (the signed artifact object, required) and public_key_hex (optional; falls back to a public_key embedded in the receipt payload). Returns JSON { valid: boolean, error: string | null (e.g. "invalid_signature", "no_public_key"), type: string, kid: string | null, issuer: string | null }. For authenticity you should verify against a key you pinned out of band, not only the one carried inside the receipt.',
6657
+ inputSchema: {
6658
+ type: "object",
6659
+ properties: {
6660
+ receipt: { type: "object", description: "The signed receipt/artifact to verify." },
6661
+ public_key_hex: { type: "string", description: "Optional Ed25519 public key hex to verify against. Defaults to a key embedded in the receipt payload." }
6662
+ },
6663
+ required: ["receipt"]
6664
+ },
6665
+ annotations: { title: "Verify a receipt offline", idempotentHint: true, ...RO }
6666
+ },
6667
+ {
6668
+ name: "self_test",
6669
+ description: "Prove the gate works, end to end, with no inputs. Runs a known-forbidden action (rm -rf) against a sample deny policy and asserts it is DENIED, then signs a decision and verifies the receipt round-trips. Returns JSON { ok: boolean, gate_denies_forbidden: boolean, sign_verify_roundtrip: boolean, details: object }. This is the 'a gate that cannot prove it denies does not start' check, exposed as a callable tool. Contacts no network.",
6670
+ inputSchema: { type: "object", properties: {} },
6671
+ annotations: { title: "Prove the gate denies and receipts verify", idempotentHint: true, ...RO }
6672
+ }
6673
+ ];
6674
+ SELF_TEST_POLICY = `
6675
+ permit(principal, action == Action::"MCP::Tool::call", resource);
6676
+ forbid(principal, action == Action::"MCP::Tool::call", resource == Tool::"Bash")
6677
+ when {
6678
+ context has input && context.input has command &&
6679
+ (context.input.command like "*rm -rf*" || context.input.command like "*mkfs*")
6680
+ };
6681
+ `;
6682
+ if (process.argv[1] && /mcp-server\.(js|mjs|cjs|ts)$/.test(process.argv[1])) {
6683
+ runMcpServer();
6684
+ }
6685
+ }
6686
+ });
6687
+
6417
6688
  // src/scopeblind-bridge.ts
6418
6689
  function getScopeBlindBridge() {
6419
6690
  if (!singleton) singleton = new ScopeBlindBridge();
@@ -8898,6 +9169,7 @@ protect-mcp: Enterprise security gateway for MCP servers & Claude Code hooks
8898
9169
  Usage:
8899
9170
  protect-mcp [options] -- <command> [args...]
8900
9171
  protect-mcp serve [--port <port>] [--enforce] [--policy <path>] [--cedar <dir>]
9172
+ protect-mcp mcp # the gate as an MCP server (evaluate/sign/verify/self_test tools)
8901
9173
  protect-mcp init-hooks [--dir <path>] [--port <port>]
8902
9174
  protect-mcp quickstart [--connect]
8903
9175
  protect-mcp wrap [--write] [--claude-desktop] [-- <command>]
@@ -11914,7 +12186,7 @@ async function handleKillerDemo(argv) {
11914
12186
  const { ed25519: ed255192 } = await Promise.resolve().then(() => (init_ed25519(), ed25519_exports));
11915
12187
  const { bytesToHex: bytesToHex2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
11916
12188
  const { randomBytes: randomBytes4 } = await import("crypto");
11917
- const artifacts = await import("@veritasacta/artifacts");
12189
+ const artifacts2 = await import("@veritasacta/artifacts");
11918
12190
  const {
11919
12191
  createSelectiveDisclosurePackage: createSelectiveDisclosurePackage2,
11920
12192
  signCommittedDecision: signCommittedDecision2,
@@ -12041,8 +12313,8 @@ async function handleKillerDemo(argv) {
12041
12313
  } else {
12042
12314
  tamperedArtifact.tool = "send_email";
12043
12315
  }
12044
- const validOriginal = artifacts.verifyArtifact(receiptArtifact, keypair.publicKey);
12045
- const validTampered = artifacts.verifyArtifact(tamperedArtifact, keypair.publicKey);
12316
+ const validOriginal = artifacts2.verifyArtifact(receiptArtifact, keypair.publicKey);
12317
+ const validTampered = artifacts2.verifyArtifact(tamperedArtifact, keypair.publicKey);
12046
12318
  (0, import_node_fs13.writeFileSync)((0, import_node_path9.join)(dir, "receipts", "tampered.receipt.json"), JSON.stringify(tamperedArtifact, null, 2) + "\n");
12047
12319
  const committed = signCommittedDecision2(
12048
12320
  executedEntry,
@@ -13187,6 +13459,10 @@ async function main() {
13187
13459
  await handleSign(args.slice(1));
13188
13460
  return;
13189
13461
  }
13462
+ if (args[0] === "mcp") {
13463
+ await (await Promise.resolve().then(() => (init_mcp_server(), mcp_server_exports))).runMcpServer();
13464
+ return;
13465
+ }
13190
13466
  if (args[0] === "serve") {
13191
13467
  const { startHookServer: startHookServer2 } = await Promise.resolve().then(() => (init_hook_server(), hook_server_exports));
13192
13468
  const portIdx = args.indexOf("--port");
package/dist/cli.mjs CHANGED
@@ -11,22 +11,24 @@ import {
11
11
  readInstalledConnectorPilots,
12
12
  simulate,
13
13
  writeConnectorPilots
14
- } from "./chunk-7MHK5RF4.mjs";
14
+ } from "./chunk-MOXINIMB.mjs";
15
15
  import {
16
16
  ProtectGateway,
17
17
  validateCredentials
18
- } from "./chunk-PB3TC7E3.mjs";
18
+ } from "./chunk-YM6SOJBR.mjs";
19
19
  import {
20
20
  buildActionReadback,
21
- evaluateCedar,
22
21
  initSigning,
22
+ loadPolicy,
23
+ signDecision
24
+ } from "./chunk-G6X763MH.mjs";
25
+ import {
26
+ evaluateCedar,
23
27
  isCedarAvailable,
24
28
  loadCedarPolicies,
25
- loadPolicy,
26
29
  policySetFromSource,
27
- runEvaluatorSelfTest,
28
- signDecision
29
- } from "./chunk-XLJUZ4WO.mjs";
30
+ runEvaluatorSelfTest
31
+ } from "./chunk-MWXDXYWH.mjs";
30
32
  import "./chunk-PQJP2ZCI.mjs";
31
33
 
32
34
  // src/cli.ts
@@ -41,6 +43,7 @@ protect-mcp: Enterprise security gateway for MCP servers & Claude Code hooks
41
43
  Usage:
42
44
  protect-mcp [options] -- <command> [args...]
43
45
  protect-mcp serve [--port <port>] [--enforce] [--policy <path>] [--cedar <dir>]
46
+ protect-mcp mcp # the gate as an MCP server (evaluate/sign/verify/self_test tools)
44
47
  protect-mcp init-hooks [--dir <path>] [--port <port>]
45
48
  protect-mcp quickstart [--connect]
46
49
  protect-mcp wrap [--write] [--claude-desktop] [-- <command>]
@@ -4330,6 +4333,10 @@ async function main() {
4330
4333
  await handleSign(args.slice(1));
4331
4334
  return;
4332
4335
  }
4336
+ if (args[0] === "mcp") {
4337
+ await (await import("./mcp-server.mjs")).runMcpServer();
4338
+ return;
4339
+ }
4333
4340
  if (args[0] === "serve") {
4334
4341
  const { startHookServer } = await import("./hook-server.mjs");
4335
4342
  const portIdx = args.indexOf("--port");
@@ -4568,7 +4575,7 @@ async function main() {
4568
4575
  if (useHttp) {
4569
4576
  const portIdx = args.indexOf("--port");
4570
4577
  const httpPort = portIdx >= 0 && args[portIdx + 1] ? parseInt(args[portIdx + 1]) : 3e3;
4571
- const { startHttpTransport } = await import("./http-transport-HLSMVBI6.mjs");
4578
+ const { startHttpTransport } = await import("./http-transport-VHD3YBT5.mjs");
4572
4579
  startHttpTransport({ port: httpPort, config, serverCommand: childCommand });
4573
4580
  return;
4574
4581
  }
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  startHookServer
3
- } from "./chunk-WCZJGEBO.mjs";
3
+ } from "./chunk-AUUAVWQM.mjs";
4
4
  import "./chunk-WWPQNIVF.mjs";
5
5
  import "./chunk-AYNQIEN7.mjs";
6
- import "./chunk-XLJUZ4WO.mjs";
6
+ import "./chunk-G6X763MH.mjs";
7
+ import "./chunk-MWXDXYWH.mjs";
7
8
  import "./chunk-JIDDQUSQ.mjs";
8
9
  import "./chunk-D733KAPG.mjs";
9
10
  import "./chunk-PQJP2ZCI.mjs";
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  ProtectGateway
3
- } from "./chunk-PB3TC7E3.mjs";
4
- import "./chunk-XLJUZ4WO.mjs";
3
+ } from "./chunk-YM6SOJBR.mjs";
4
+ import "./chunk-G6X763MH.mjs";
5
+ import "./chunk-MWXDXYWH.mjs";
5
6
  import "./chunk-PQJP2ZCI.mjs";
6
7
 
7
8
  // src/http-transport.ts
package/dist/index.mjs CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  readInstalledConnectorPilots,
28
28
  simulate,
29
29
  writeConnectorPilots
30
- } from "./chunk-7MHK5RF4.mjs";
30
+ } from "./chunk-MOXINIMB.mjs";
31
31
  import {
32
32
  ProtectGateway,
33
33
  buildDecisionContext,
@@ -39,7 +39,7 @@ import {
39
39
  resolveCredential,
40
40
  sendApprovalNotification,
41
41
  validateCredentials
42
- } from "./chunk-PB3TC7E3.mjs";
42
+ } from "./chunk-YM6SOJBR.mjs";
43
43
  import {
44
44
  createSandboxServer
45
45
  } from "./chunk-SETXVE2K.mjs";
@@ -54,26 +54,28 @@ import {
54
54
  forwardReceipt,
55
55
  getScopeBlindBridge,
56
56
  startHookServer
57
- } from "./chunk-WCZJGEBO.mjs";
57
+ } from "./chunk-AUUAVWQM.mjs";
58
58
  import "./chunk-WWPQNIVF.mjs";
59
59
  import {
60
60
  sha256 as sha2562
61
61
  } from "./chunk-AYNQIEN7.mjs";
62
62
  import {
63
63
  checkRateLimit,
64
- evaluateCedar,
65
64
  getSignerInfo,
66
65
  getToolPolicy,
67
66
  initSigning,
68
- isCedarAvailable,
69
67
  isSigningEnabled,
70
- loadCedarPolicies,
71
68
  loadPolicy,
72
69
  parseRateLimit,
73
- policySetFromSource,
74
- runEvaluatorSelfTest,
75
70
  signDecision
76
- } from "./chunk-XLJUZ4WO.mjs";
71
+ } from "./chunk-G6X763MH.mjs";
72
+ import {
73
+ evaluateCedar,
74
+ isCedarAvailable,
75
+ loadCedarPolicies,
76
+ policySetFromSource,
77
+ runEvaluatorSelfTest
78
+ } from "./chunk-MWXDXYWH.mjs";
77
79
  import {
78
80
  Field,
79
81
  _abool2,
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * protect-mcp — the gate as an MCP server (JSON-RPC over stdio).
4
+ *
5
+ * `protect-mcp mcp` exposes the gate's product surface as MCP tools, so an
6
+ * agent or orchestrator can ask "may I do this?" and "prove I was restrained"
7
+ * as ordinary tool calls, without wiring the PreToolUse/PostToolUse hooks. The
8
+ * four tools are the whole loop:
9
+ *
10
+ * evaluate_action decide a proposed tool call against a Cedar policy (fail-closed)
11
+ * sign_decision turn a decision into an Ed25519 signed receipt
12
+ * verify_receipt check a signed receipt offline against a public key
13
+ * self_test prove the gate denies a known-forbidden action and the
14
+ * sign -> verify round-trip holds
15
+ *
16
+ * Hand-rolled JSON-RPC (no MCP SDK dependency) to match src/demo-server.ts and
17
+ * keep the bundle lean. Every tool is read-only with respect to the world: it
18
+ * makes decisions and produces artifacts, but writes nothing and contacts no
19
+ * network. Receipts are byte-compatible with the ones the gate signs (same
20
+ * payload shape as src/signing.ts).
21
+ */
22
+ declare function runMcpServer(): Promise<void>;
23
+
24
+ export { runMcpServer };
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * protect-mcp — the gate as an MCP server (JSON-RPC over stdio).
4
+ *
5
+ * `protect-mcp mcp` exposes the gate's product surface as MCP tools, so an
6
+ * agent or orchestrator can ask "may I do this?" and "prove I was restrained"
7
+ * as ordinary tool calls, without wiring the PreToolUse/PostToolUse hooks. The
8
+ * four tools are the whole loop:
9
+ *
10
+ * evaluate_action decide a proposed tool call against a Cedar policy (fail-closed)
11
+ * sign_decision turn a decision into an Ed25519 signed receipt
12
+ * verify_receipt check a signed receipt offline against a public key
13
+ * self_test prove the gate denies a known-forbidden action and the
14
+ * sign -> verify round-trip holds
15
+ *
16
+ * Hand-rolled JSON-RPC (no MCP SDK dependency) to match src/demo-server.ts and
17
+ * keep the bundle lean. Every tool is read-only with respect to the world: it
18
+ * makes decisions and produces artifacts, but writes nothing and contacts no
19
+ * network. Receipts are byte-compatible with the ones the gate signs (same
20
+ * payload shape as src/signing.ts).
21
+ */
22
+ declare function runMcpServer(): Promise<void>;
23
+
24
+ export { runMcpServer };