protect-mcp 0.5.4 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,73 @@
1
1
  # protect-mcp
2
2
 
3
- Enterprise security gateway for MCP servers and Claude Code hooks. Signed receipts, Cedar policies, and swarm-aware audit trails.
3
+ [![npm version](https://img.shields.io/npm/v/protect-mcp)](https://www.npmjs.com/package/protect-mcp)
4
+ [![Downloads](https://img.shields.io/npm/dm/protect-mcp)](https://www.npmjs.com/package/protect-mcp)
5
+ [![Claude Code Marketplace](https://img.shields.io/badge/Claude%20Code-marketplace-d97757?logo=anthropic&logoColor=white)](https://github.com/wshobson/agents/tree/main/plugins/protect-mcp)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](./LICENSE)
7
+
8
+ > A policy check that sits between your AI agent and the tools it calls.
9
+ > Every tool call is evaluated against a rule you wrote. Every decision is signed.
10
+
11
+ ## What it does, in plain English
12
+
13
+ When an AI agent (Claude Code, Cursor, a custom LangChain app, anything that
14
+ uses the Model Context Protocol) wants to run a command, edit a file, or call
15
+ an API, `protect-mcp` intercepts that request before it executes:
16
+
17
+ 1. **Checks a policy.** You write rules in [Cedar](https://www.cedarpolicy.com/)
18
+ — the same policy language AWS uses for IAM. Rules like *"never allow
19
+ `rm -rf /`"*, *"only allow `Bash` during working hours"*, or *"require
20
+ human approval for anything touching production."*
21
+ 2. **Returns a decision.** `allow`, `deny`, or `request_approval`. The agent
22
+ framework respects the decision — if it's `deny`, the tool call doesn't run.
23
+ 3. **Signs a receipt.** An Ed25519-signed, hash-chained record of the decision,
24
+ written to `.receipts/`. Verifiable offline by anyone with the public key.
25
+ When your auditor asks *"what did that agent do on 2026-03-14?"* you have
26
+ cryptographic proof — not a log file you might have tampered with.
27
+
28
+ You install it once. Your agent keeps working the same way. The difference:
29
+ every action it takes is now policy-checked and audit-evidenced.
30
+
31
+ ## Who this is for
32
+
33
+ - **Developers using Claude Code / Cursor / Cline** who want *"don't let the
34
+ agent delete my repo"* enforced rather than hoped for.
35
+ - **Security teams** shipping agents to engineering who need a portable
36
+ policy layer that travels across frameworks.
37
+ - **Compliance teams** who need tamper-evident evidence of what agents did,
38
+ verifiable without trusting the vendor.
39
+
40
+ ## How it relates to `sb-runtime`
41
+
42
+ `protect-mcp` is a **library** — it sits inside your agent framework and
43
+ gates tool calls cooperatively. Right tool when you own the agent's code and
44
+ trust its framework to honour decisions.
45
+
46
+ [`sb-runtime`](https://github.com/ScopeBlind/sb-runtime) is the companion
47
+ **binary** that wraps the whole agent *process* in an OS-level sandbox
48
+ (Landlock + seccomp on Linux). It enforces decisions at the kernel layer —
49
+ the agent can't ignore them even if it tried. Use `sb-runtime` when you're
50
+ running an agent you didn't write, or when you want belt-and-braces defence
51
+ in depth:
52
+
53
+ ```
54
+ ┌─────────────────────────────────────────────────┐
55
+ │ sb-runtime ← OS refuses forbidden syscalls │
56
+ │ ┌───────────────────────────────────────────┐ │
57
+ │ │ agent process (Claude Code, Python, …) │ │
58
+ │ │ ┌─────────────────────────────────────┐ │ │
59
+ │ │ │ protect-mcp │ │ │
60
+ │ │ │ ← Cedar decides per tool call, │ │ │
61
+ │ │ │ receipts every decision │ │ │
62
+ │ │ └─────────────────────────────────────┘ │ │
63
+ │ └───────────────────────────────────────────┘ │
64
+ └─────────────────────────────────────────────────┘
65
+ ```
66
+
67
+ **One-liner:** `protect-mcp` is the policy hook inside your agent.
68
+ `sb-runtime` is the OS sandbox around it. You want both.
69
+
70
+ ---
4
71
 
5
72
  ## Quick Start — Claude Code
6
73
 
@@ -268,12 +335,30 @@ Free tier: 20,000 receipts/month. No credit card required.
268
335
 
269
336
  [scopeblind.com/pricing](https://scopeblind.com/pricing)
270
337
 
338
+ ## Interoperability
339
+
340
+ The receipt format is independently implemented and verified across multiple systems:
341
+
342
+ | Evidence | Detail |
343
+ |----------|--------|
344
+ | **4 independent implementations** | TypeScript (protect-mcp), Python (protect-mcp-adk), Rust (Cedar WASM), APS ProxyGateway |
345
+ | **2 IETF Internet-Drafts** | [draft-farley-acta-signed-receipts-01](https://datatracker.ietf.org/doc/draft-farley-acta-signed-receipts/), [draft-pidlisnyi-aps-00](https://datatracker.ietf.org/doc/draft-pidlisnyi-aps/) |
346
+ | **8 cross-engine receipts** | [Composition test](https://github.com/ScopeBlind/examples/tree/main/interop/composition-test): 2 engines, 1 verifier, all VALID |
347
+ | **3 enterprise integrations** | Microsoft AGT [#667](https://github.com/microsoft/agent-governance-toolkit/pull/667), [#1159](https://github.com/microsoft/agent-governance-toolkit/pull/1159), [#1168](https://github.com/microsoft/agent-governance-toolkit/pull/1168) |
348
+ | **1 verifier, zero dependencies** | `npx @veritasacta/verify receipt.json --key <hex>` (Apache-2.0, offline) |
349
+
350
+ Verify any receipt from any implementation:
351
+
352
+ ```bash
353
+ npx @veritasacta/verify receipt.json --key <public-key-hex>
354
+ # Exit 0 = valid, 1 = tampered, 2 = malformed
355
+ ```
356
+
271
357
  ## Standards & IP
272
358
 
273
- - **IETF Internet-Draft**: [draft-farley-acta-signed-receipts-01](https://datatracker.ietf.org/doc/draft-farley-acta-signed-receipts/) — Signed Decision Receipts for Machine-to-Machine Access Control
274
- - **Patent Status**: 4 Australian provisional patents pending (2025-2026) covering decision receipts with configurable disclosure, tool-calling gateway, agent manifests, and portable identity
275
- - **Verification**: Apache-2.0 `npx @veritasacta/verify --self-test`
276
- - **Microsoft AGT Integration**: [PR #667](https://github.com/microsoft/agent-governance-toolkit/pull/667) — Cedar policy bridge for Agent Governance Toolkit
359
+ - **IETF Internet-Draft**: [draft-farley-acta-signed-receipts-01](https://datatracker.ietf.org/doc/draft-farley-acta-signed-receipts/)
360
+ - **Patent Status**: 4 Australian provisional patents pending (2025-2026)
361
+ - **Cedar WASM**: [PR #64](https://github.com/cedar-policy/cedar-for-agents/pull/64) merged + [PR #73](https://github.com/cedar-policy/cedar-for-agents/pull/73) (RequestGenerator, pending review)
277
362
 
278
363
  ## What's New in v0.5.3
279
364
 
@@ -282,12 +367,62 @@ Free tier: 20,000 receipts/month. No credit card required.
282
367
  - Anonymous install telemetry (opt-out: `PROTECT_MCP_TELEMETRY=off`)
283
368
  - Improved Cedar WASM detection
284
369
 
370
+ ## Cybersecurity: Vulnerability Disclosure Receipts
371
+
372
+ protect-mcp provides the infrastructure for receipt-signed vulnerability disclosure workflows. When AI security agents (Claude Code Security, Mythos, or similar) discover vulnerabilities, every step of the disclosure lifecycle can produce a signed, chain-linked receipt:
373
+
374
+ ```
375
+ DISCOVER → DISCLOSE → PATCH → DEPLOY
376
+ (Each step: Ed25519-signed, chain-linked, Cedar policy-bound)
377
+ ```
378
+
379
+ Cedar policies govern what the scanning agent is allowed to do:
380
+ - **CAN**: scan code, report findings internally
381
+ - **CANNOT**: disclose externally or deploy patches without human approval
382
+ - **MUST**: escalate critical findings to humans
383
+
384
+ See the [security vulnerability disclosure example](https://github.com/ScopeBlind/examples/tree/main/security-vulnerability-disclosure) for a complete working implementation with Cedar policies and example receipt chains.
385
+
386
+ Related: [Vulnerability Disclosure Receipt Design](https://github.com/scopeblind/scopeblind-gateway/issues/2)
387
+
285
388
  ## Examples
286
389
 
287
- See complete working examples at [github.com/ScopeBlind/examples](https://github.com/ScopeBlind/examples).
390
+ See complete working examples at [github.com/ScopeBlind/examples](https://github.com/ScopeBlind/examples):
391
+ - [Claude Code hooks](https://github.com/ScopeBlind/examples/tree/main/claude-code-hooks) — receipt signing for every tool call
392
+ - [Security vulnerability disclosure](https://github.com/ScopeBlind/examples/tree/main/security-vulnerability-disclosure) — receipt-signed disclosure lifecycle with Cedar governance
393
+ - [MCP server signing](https://github.com/ScopeBlind/examples/tree/main/mcp-server-signing) — Cedar WASM policy engine with audit bundles
394
+
395
+ ## ScopeBlind Dashboard
396
+
397
+ protect-mcp works fully offline, forever, for free. For teams that want visibility across agents, ScopeBlind offers a hosted dashboard:
398
+
399
+ ```bash
400
+ npx protect-mcp connect
401
+ ```
402
+
403
+ | | Free | Pro | Enterprise |
404
+ |---|---|---|---|
405
+ | Receipts/month | 20,000 | Pay-as-you-go | Annual commit |
406
+ | Price | $0 | $0.50 / 1K | $0.40 / 1K |
407
+ | Receipt explorer | Yes | Yes | Yes |
408
+ | Compliance reports | Yes | Yes | Yes |
409
+ | SSO / SAML | - | - | Yes |
410
+ | SLA | - | - | 99.9% |
411
+
412
+ No signup required for free tier. No card upfront.
413
+
414
+ [Dashboard](https://scopeblind.com) | [Docs](https://scopeblind.com/docs/protect-mcp) | [Pricing](https://scopeblind.com/pricing)
415
+
416
+ ## Telemetry
417
+
418
+ protect-mcp sends a single anonymous install beacon on first run (package name, version, OS, Node version). No PII. Disable with:
419
+
420
+ ```bash
421
+ PROTECT_MCP_TELEMETRY=off
422
+ ```
288
423
 
289
424
  ## License
290
425
 
291
426
  MIT — free to use, modify, distribute, and build upon without restriction.
292
427
 
293
- [scopeblind.com](https://scopeblind.com) · [npm](https://www.npmjs.com/package/protect-mcp) · [GitHub](https://github.com/scopeblind/scopeblind-gateway) · [IETF Draft](https://datatracker.ietf.org/doc/draft-farley-acta-signed-receipts/)
428
+ Built by [ScopeBlind](https://scopeblind.com) | [npm](https://www.npmjs.com/package/protect-mcp) | [GitHub](https://github.com/scopeblind/scopeblind-gateway) | [IETF Draft](https://datatracker.ietf.org/doc/draft-farley-acta-signed-receipts/)
@@ -12,10 +12,10 @@ import {
12
12
  hexToBytes,
13
13
  isBytes,
14
14
  randomBytes,
15
+ rotr,
15
16
  toBytes,
16
17
  utf8ToBytes
17
18
  } from "./chunk-D733KAPG.mjs";
18
- import "./chunk-PQJP2ZCI.mjs";
19
19
 
20
20
  // node_modules/@noble/hashes/esm/_md.js
21
21
  function setBigUint64(view, byteOffset, value, isLE) {
@@ -30,6 +30,12 @@ function setBigUint64(view, byteOffset, value, isLE) {
30
30
  view.setUint32(byteOffset + h, wh, isLE);
31
31
  view.setUint32(byteOffset + l, wl, isLE);
32
32
  }
33
+ function Chi(a, b, c) {
34
+ return a & b ^ ~a & c;
35
+ }
36
+ function Maj(a, b, c) {
37
+ return a & b ^ a & c ^ b & c;
38
+ }
33
39
  var HashMD = class extends Hash {
34
40
  constructor(blockLen, outputLen, padOffset, isLE) {
35
41
  super();
@@ -120,6 +126,16 @@ var HashMD = class extends Hash {
120
126
  return this._cloneInto();
121
127
  }
122
128
  };
129
+ var SHA256_IV = /* @__PURE__ */ Uint32Array.from([
130
+ 1779033703,
131
+ 3144134277,
132
+ 1013904242,
133
+ 2773480762,
134
+ 1359893119,
135
+ 2600822924,
136
+ 528734635,
137
+ 1541459225
138
+ ]);
123
139
  var SHA512_IV = /* @__PURE__ */ Uint32Array.from([
124
140
  1779033703,
125
141
  4089235720,
@@ -175,6 +191,143 @@ var add5L = (Al, Bl, Cl, Dl, El) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >
175
191
  var add5H = (low, Ah, Bh, Ch, Dh, Eh) => Ah + Bh + Ch + Dh + Eh + (low / 2 ** 32 | 0) | 0;
176
192
 
177
193
  // node_modules/@noble/hashes/esm/sha2.js
194
+ var SHA256_K = /* @__PURE__ */ Uint32Array.from([
195
+ 1116352408,
196
+ 1899447441,
197
+ 3049323471,
198
+ 3921009573,
199
+ 961987163,
200
+ 1508970993,
201
+ 2453635748,
202
+ 2870763221,
203
+ 3624381080,
204
+ 310598401,
205
+ 607225278,
206
+ 1426881987,
207
+ 1925078388,
208
+ 2162078206,
209
+ 2614888103,
210
+ 3248222580,
211
+ 3835390401,
212
+ 4022224774,
213
+ 264347078,
214
+ 604807628,
215
+ 770255983,
216
+ 1249150122,
217
+ 1555081692,
218
+ 1996064986,
219
+ 2554220882,
220
+ 2821834349,
221
+ 2952996808,
222
+ 3210313671,
223
+ 3336571891,
224
+ 3584528711,
225
+ 113926993,
226
+ 338241895,
227
+ 666307205,
228
+ 773529912,
229
+ 1294757372,
230
+ 1396182291,
231
+ 1695183700,
232
+ 1986661051,
233
+ 2177026350,
234
+ 2456956037,
235
+ 2730485921,
236
+ 2820302411,
237
+ 3259730800,
238
+ 3345764771,
239
+ 3516065817,
240
+ 3600352804,
241
+ 4094571909,
242
+ 275423344,
243
+ 430227734,
244
+ 506948616,
245
+ 659060556,
246
+ 883997877,
247
+ 958139571,
248
+ 1322822218,
249
+ 1537002063,
250
+ 1747873779,
251
+ 1955562222,
252
+ 2024104815,
253
+ 2227730452,
254
+ 2361852424,
255
+ 2428436474,
256
+ 2756734187,
257
+ 3204031479,
258
+ 3329325298
259
+ ]);
260
+ var SHA256_W = /* @__PURE__ */ new Uint32Array(64);
261
+ var SHA256 = class extends HashMD {
262
+ constructor(outputLen = 32) {
263
+ super(64, outputLen, 8, false);
264
+ this.A = SHA256_IV[0] | 0;
265
+ this.B = SHA256_IV[1] | 0;
266
+ this.C = SHA256_IV[2] | 0;
267
+ this.D = SHA256_IV[3] | 0;
268
+ this.E = SHA256_IV[4] | 0;
269
+ this.F = SHA256_IV[5] | 0;
270
+ this.G = SHA256_IV[6] | 0;
271
+ this.H = SHA256_IV[7] | 0;
272
+ }
273
+ get() {
274
+ const { A, B, C, D, E, F, G, H } = this;
275
+ return [A, B, C, D, E, F, G, H];
276
+ }
277
+ // prettier-ignore
278
+ set(A, B, C, D, E, F, G, H) {
279
+ this.A = A | 0;
280
+ this.B = B | 0;
281
+ this.C = C | 0;
282
+ this.D = D | 0;
283
+ this.E = E | 0;
284
+ this.F = F | 0;
285
+ this.G = G | 0;
286
+ this.H = H | 0;
287
+ }
288
+ process(view, offset) {
289
+ for (let i = 0; i < 16; i++, offset += 4)
290
+ SHA256_W[i] = view.getUint32(offset, false);
291
+ for (let i = 16; i < 64; i++) {
292
+ const W15 = SHA256_W[i - 15];
293
+ const W2 = SHA256_W[i - 2];
294
+ const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
295
+ const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
296
+ SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0;
297
+ }
298
+ let { A, B, C, D, E, F, G, H } = this;
299
+ for (let i = 0; i < 64; i++) {
300
+ const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
301
+ const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0;
302
+ const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
303
+ const T2 = sigma0 + Maj(A, B, C) | 0;
304
+ H = G;
305
+ G = F;
306
+ F = E;
307
+ E = D + T1 | 0;
308
+ D = C;
309
+ C = B;
310
+ B = A;
311
+ A = T1 + T2 | 0;
312
+ }
313
+ A = A + this.A | 0;
314
+ B = B + this.B | 0;
315
+ C = C + this.C | 0;
316
+ D = D + this.D | 0;
317
+ E = E + this.E | 0;
318
+ F = F + this.F | 0;
319
+ G = G + this.G | 0;
320
+ H = H + this.H | 0;
321
+ this.set(A, B, C, D, E, F, G, H);
322
+ }
323
+ roundClean() {
324
+ clean(SHA256_W);
325
+ }
326
+ destroy() {
327
+ this.set(0, 0, 0, 0, 0, 0, 0, 0);
328
+ clean(this.buffer);
329
+ }
330
+ };
178
331
  var K512 = /* @__PURE__ */ (() => split([
179
332
  "0x428a2f98d728ae22",
180
333
  "0x7137449123ef65cd",
@@ -372,6 +525,7 @@ var SHA512 = class extends HashMD {
372
525
  this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
373
526
  }
374
527
  };
528
+ var sha256 = /* @__PURE__ */ createHasher(() => new SHA256());
375
529
  var sha512 = /* @__PURE__ */ createHasher(() => new SHA512());
376
530
 
377
531
  // node_modules/@noble/curves/esm/utils.js
@@ -2260,23 +2414,25 @@ var hashToCurve = /* @__PURE__ */ (() => ed25519_hasher.hashToCurve)();
2260
2414
  var encodeToCurve = /* @__PURE__ */ (() => ed25519_hasher.encodeToCurve)();
2261
2415
  var hashToRistretto255 = /* @__PURE__ */ (() => ristretto255_hasher.hashToCurve)();
2262
2416
  var hash_to_ristretto255 = /* @__PURE__ */ (() => ristretto255_hasher.hashToCurve)();
2417
+
2263
2418
  export {
2264
- ED25519_TORSION_SUBGROUP,
2265
- RistrettoPoint,
2419
+ sha256,
2266
2420
  ed25519,
2267
- ed25519_hasher,
2268
2421
  ed25519ctx,
2269
2422
  ed25519ph,
2423
+ x25519,
2424
+ ed25519_hasher,
2425
+ ristretto255,
2426
+ ristretto255_hasher,
2427
+ ED25519_TORSION_SUBGROUP,
2428
+ edwardsToMontgomeryPub,
2270
2429
  edwardsToMontgomery,
2271
2430
  edwardsToMontgomeryPriv,
2272
- edwardsToMontgomeryPub,
2273
- encodeToCurve,
2431
+ RistrettoPoint,
2274
2432
  hashToCurve,
2433
+ encodeToCurve,
2275
2434
  hashToRistretto255,
2276
- hash_to_ristretto255,
2277
- ristretto255,
2278
- ristretto255_hasher,
2279
- x25519
2435
+ hash_to_ristretto255
2280
2436
  };
2281
2437
  /*! Bundled license information:
2282
2438
 
@@ -696,48 +696,53 @@ async function startHookServer(options = {}) {
696
696
  }));
697
697
  });
698
698
  server.listen(port, "127.0.0.1", () => {
699
- process.stderr.write(`
699
+ const w = (s) => process.stderr.write(s);
700
+ const pad = (s, n = 46) => s.padEnd(n);
701
+ w(`
700
702
  `);
701
- process.stderr.write(`[PROTECT_MCP] \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
703
+ w(` protect-mcp v0.5.4
702
704
  `);
703
- process.stderr.write(`[PROTECT_MCP] \u2502 protect-mcp Hook Server v0.5.3 \u2502
705
+ w(` ScopeBlind \u2014 https://scopeblind.com
704
706
  `);
705
- process.stderr.write(`[PROTECT_MCP] \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524
707
+ w(`
706
708
  `);
707
- process.stderr.write(`[PROTECT_MCP] \u2502 Listening: http://127.0.0.1:${String(port).padEnd(5)} \u2502
709
+ w(` Listening http://127.0.0.1:${port}
708
710
  `);
709
- process.stderr.write(`[PROTECT_MCP] \u2502 Mode: ${(enforce ? "enforce" : "shadow").padEnd(36)}\u2502
711
+ w(` Mode ${enforce ? "enforce" : "shadow"}
710
712
  `);
711
- process.stderr.write(`[PROTECT_MCP] \u2502 Policy: ${(cedarPolicies ? `Cedar (${cedarPolicies.fileCount} files)` : jsonPolicy ? "JSON" : "none").padEnd(36)}\u2502
713
+ w(` Policy ${cedarPolicies ? `Cedar (${cedarPolicies.fileCount} files)` : jsonPolicy ? "JSON" : "none"}
712
714
  `);
713
- process.stderr.write(`[PROTECT_MCP] \u2502 Signing: ${(isSigningEnabled() ? "Ed25519 \u2713" : "disabled").padEnd(36)}\u2502
715
+ w(` Signing ${isSigningEnabled() ? "Ed25519" : "disabled"}
714
716
  `);
715
- process.stderr.write(`[PROTECT_MCP] \u2502 Swarm: ${(state.swarmContext.team_name ? `${state.swarmContext.team_name} (${state.swarmContext.agent_type})` : "standalone").padEnd(36)}\u2502
717
+ if (state.swarmContext.team_name) {
718
+ w(` Swarm ${state.swarmContext.team_name} (${state.swarmContext.agent_type})
716
719
  `);
717
- process.stderr.write(`[PROTECT_MCP] \u2502 Sandbox: ${detectSandboxState().padEnd(36)}\u2502
720
+ }
721
+ w(`
718
722
  `);
719
- process.stderr.write(`[PROTECT_MCP] \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
723
+ w(` POST /hook Hook receiver
720
724
  `);
721
- process.stderr.write(`
725
+ w(` GET /health Health + signer info
722
726
  `);
723
- process.stderr.write(`[PROTECT_MCP] Endpoints:
727
+ w(` GET /receipts Signed receipts
724
728
  `);
725
- process.stderr.write(` POST /hook \u2014 Claude Code hook receiver
729
+ w(` GET /suggestions Cedar policy suggestions
726
730
  `);
727
- process.stderr.write(` GET /health \u2014 Health check + signer info
731
+ w(`
728
732
  `);
729
- process.stderr.write(` GET /receipts \u2014 Recent signed receipts
733
+ w(` deny is authoritative \u2014 cannot be overridden.
730
734
  `);
731
- process.stderr.write(` GET /suggestions \u2014 Auto-generated Cedar policy suggestions
735
+ w(`
732
736
  `);
733
- process.stderr.write(` GET /alerts \u2014 Config tamper alerts
737
+ const hasSlug = process.env.SCOPEBLIND_SLUG || existsSync(join(process.cwd(), ".scopeblind"));
738
+ if (!hasSlug) {
739
+ w(` Dashboard npx protect-mcp connect
734
740
  `);
735
- process.stderr.write(`
741
+ w(` Free up to 20,000 receipts/month
736
742
  `);
737
- process.stderr.write(`[PROTECT_MCP] deny decisions are AUTHORITATIVE \u2014 they cannot be overridden.
738
- `);
739
- process.stderr.write(`
743
+ w(`
740
744
  `);
745
+ }
741
746
  });
742
747
  const shutdown = () => {
743
748
  process.stderr.write("\n[PROTECT_MCP] Shutting down hook server...\n");
package/dist/cli.js CHANGED
@@ -5447,48 +5447,53 @@ async function startHookServer(options = {}) {
5447
5447
  }));
5448
5448
  });
5449
5449
  server.listen(port, "127.0.0.1", () => {
5450
- process.stderr.write(`
5450
+ const w = (s) => process.stderr.write(s);
5451
+ const pad = (s, n = 46) => s.padEnd(n);
5452
+ w(`
5451
5453
  `);
5452
- process.stderr.write(`[PROTECT_MCP] \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
5454
+ w(` protect-mcp v0.5.4
5453
5455
  `);
5454
- process.stderr.write(`[PROTECT_MCP] \u2502 protect-mcp Hook Server v0.5.3 \u2502
5456
+ w(` ScopeBlind \u2014 https://scopeblind.com
5455
5457
  `);
5456
- process.stderr.write(`[PROTECT_MCP] \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524
5458
+ w(`
5457
5459
  `);
5458
- process.stderr.write(`[PROTECT_MCP] \u2502 Listening: http://127.0.0.1:${String(port).padEnd(5)} \u2502
5460
+ w(` Listening http://127.0.0.1:${port}
5459
5461
  `);
5460
- process.stderr.write(`[PROTECT_MCP] \u2502 Mode: ${(enforce ? "enforce" : "shadow").padEnd(36)}\u2502
5462
+ w(` Mode ${enforce ? "enforce" : "shadow"}
5461
5463
  `);
5462
- process.stderr.write(`[PROTECT_MCP] \u2502 Policy: ${(cedarPolicies ? `Cedar (${cedarPolicies.fileCount} files)` : jsonPolicy ? "JSON" : "none").padEnd(36)}\u2502
5464
+ w(` Policy ${cedarPolicies ? `Cedar (${cedarPolicies.fileCount} files)` : jsonPolicy ? "JSON" : "none"}
5463
5465
  `);
5464
- process.stderr.write(`[PROTECT_MCP] \u2502 Signing: ${(isSigningEnabled() ? "Ed25519 \u2713" : "disabled").padEnd(36)}\u2502
5466
+ w(` Signing ${isSigningEnabled() ? "Ed25519" : "disabled"}
5465
5467
  `);
5466
- process.stderr.write(`[PROTECT_MCP] \u2502 Swarm: ${(state.swarmContext.team_name ? `${state.swarmContext.team_name} (${state.swarmContext.agent_type})` : "standalone").padEnd(36)}\u2502
5468
+ if (state.swarmContext.team_name) {
5469
+ w(` Swarm ${state.swarmContext.team_name} (${state.swarmContext.agent_type})
5467
5470
  `);
5468
- process.stderr.write(`[PROTECT_MCP] \u2502 Sandbox: ${detectSandboxState().padEnd(36)}\u2502
5471
+ }
5472
+ w(`
5469
5473
  `);
5470
- process.stderr.write(`[PROTECT_MCP] \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
5474
+ w(` POST /hook Hook receiver
5471
5475
  `);
5472
- process.stderr.write(`
5476
+ w(` GET /health Health + signer info
5473
5477
  `);
5474
- process.stderr.write(`[PROTECT_MCP] Endpoints:
5478
+ w(` GET /receipts Signed receipts
5475
5479
  `);
5476
- process.stderr.write(` POST /hook \u2014 Claude Code hook receiver
5480
+ w(` GET /suggestions Cedar policy suggestions
5477
5481
  `);
5478
- process.stderr.write(` GET /health \u2014 Health check + signer info
5482
+ w(`
5479
5483
  `);
5480
- process.stderr.write(` GET /receipts \u2014 Recent signed receipts
5484
+ w(` deny is authoritative \u2014 cannot be overridden.
5481
5485
  `);
5482
- process.stderr.write(` GET /suggestions \u2014 Auto-generated Cedar policy suggestions
5486
+ w(`
5483
5487
  `);
5484
- process.stderr.write(` GET /alerts \u2014 Config tamper alerts
5488
+ const hasSlug = process.env.SCOPEBLIND_SLUG || (0, import_node_fs8.existsSync)((0, import_node_path5.join)(process.cwd(), ".scopeblind"));
5489
+ if (!hasSlug) {
5490
+ w(` Dashboard npx protect-mcp connect
5485
5491
  `);
5486
- process.stderr.write(`
5492
+ w(` Free up to 20,000 receipts/month
5487
5493
  `);
5488
- process.stderr.write(`[PROTECT_MCP] deny decisions are AUTHORITATIVE \u2014 they cannot be overridden.
5489
- `);
5490
- process.stderr.write(`
5494
+ w(`
5491
5495
  `);
5496
+ }
5492
5497
  });
5493
5498
  const shutdown = () => {
5494
5499
  process.stderr.write("\n[PROTECT_MCP] Shutting down hook server...\n");
@@ -6125,6 +6130,12 @@ Examples:
6125
6130
  protect-mcp status
6126
6131
  protect-mcp bundle --output audit.json
6127
6132
 
6133
+ Dashboard:
6134
+ npx protect-mcp connect Create a free ScopeBlind dashboard
6135
+ Free up to 20,000 receipts/month
6136
+
6137
+ https://scopeblind.com Docs, pricing, enterprise
6138
+
6128
6139
  `);
6129
6140
  }
6130
6141
  function parseArgs(argv) {
@@ -7312,7 +7323,7 @@ async function sendInstallTelemetry() {
7312
7323
  }
7313
7324
  writeFileSync2(markerFile, String(Date.now()), "utf-8");
7314
7325
  process.stderr.write(
7315
- "[protect-mcp] Anonymous install telemetry sent (disable: PROTECT_MCP_TELEMETRY=off)\n"
7326
+ "[protect-mcp] Thanks for installing! Anonymous telemetry sent (disable: PROTECT_MCP_TELEMETRY=off)\n[protect-mcp] Free dashboard: npx protect-mcp connect | https://scopeblind.com\n"
7316
7327
  );
7317
7328
  } catch {
7318
7329
  }
package/dist/cli.mjs CHANGED
@@ -75,6 +75,12 @@ Examples:
75
75
  protect-mcp status
76
76
  protect-mcp bundle --output audit.json
77
77
 
78
+ Dashboard:
79
+ npx protect-mcp connect Create a free ScopeBlind dashboard
80
+ Free up to 20,000 receipts/month
81
+
82
+ https://scopeblind.com Docs, pricing, enterprise
83
+
78
84
  `);
79
85
  }
80
86
  function parseArgs(argv) {
@@ -139,7 +145,7 @@ async function handleInit(argv) {
139
145
  let keypair;
140
146
  {
141
147
  const { randomBytes } = await import("crypto");
142
- const { ed25519 } = await import("./ed25519-V7HDL2WC.mjs");
148
+ const { ed25519 } = await import("./ed25519-DZMMNNVE.mjs");
143
149
  const { bytesToHex } = await import("./utils-6AYZFE5A.mjs");
144
150
  const privateKey = randomBytes(32);
145
151
  const publicKey = ed25519.getPublicKey(privateKey);
@@ -759,7 +765,7 @@ ${bold("protect-mcp quickstart")}
759
765
  const { randomBytes } = await import("crypto");
760
766
  let keypair;
761
767
  try {
762
- const { ed25519 } = await import("./ed25519-V7HDL2WC.mjs");
768
+ const { ed25519 } = await import("./ed25519-DZMMNNVE.mjs");
763
769
  const { bytesToHex } = await import("./utils-6AYZFE5A.mjs");
764
770
  const privateKey = randomBytes(32);
765
771
  const publicKey = ed25519.getPublicKey(privateKey);
@@ -1105,7 +1111,7 @@ ${bold("protect-mcp init-hooks")}
1105
1111
  if (!existsSync(keysDir)) mkdirSync(keysDir, { recursive: true });
1106
1112
  const { randomBytes: rb } = await import("crypto");
1107
1113
  try {
1108
- const { ed25519 } = await import("./ed25519-V7HDL2WC.mjs");
1114
+ const { ed25519 } = await import("./ed25519-DZMMNNVE.mjs");
1109
1115
  const { bytesToHex } = await import("./utils-6AYZFE5A.mjs");
1110
1116
  const privateKey = rb(32);
1111
1117
  const publicKey = ed25519.getPublicKey(privateKey);
@@ -1262,7 +1268,7 @@ async function sendInstallTelemetry() {
1262
1268
  }
1263
1269
  writeFileSync(markerFile, String(Date.now()), "utf-8");
1264
1270
  process.stderr.write(
1265
- "[protect-mcp] Anonymous install telemetry sent (disable: PROTECT_MCP_TELEMETRY=off)\n"
1271
+ "[protect-mcp] Thanks for installing! Anonymous telemetry sent (disable: PROTECT_MCP_TELEMETRY=off)\n[protect-mcp] Free dashboard: npx protect-mcp connect | https://scopeblind.com\n"
1266
1272
  );
1267
1273
  } catch {
1268
1274
  }
@@ -0,0 +1,38 @@
1
+ import {
2
+ ED25519_TORSION_SUBGROUP,
3
+ RistrettoPoint,
4
+ ed25519,
5
+ ed25519_hasher,
6
+ ed25519ctx,
7
+ ed25519ph,
8
+ edwardsToMontgomery,
9
+ edwardsToMontgomeryPriv,
10
+ edwardsToMontgomeryPub,
11
+ encodeToCurve,
12
+ hashToCurve,
13
+ hashToRistretto255,
14
+ hash_to_ristretto255,
15
+ ristretto255,
16
+ ristretto255_hasher,
17
+ x25519
18
+ } from "./chunk-LYKNULYU.mjs";
19
+ import "./chunk-D733KAPG.mjs";
20
+ import "./chunk-PQJP2ZCI.mjs";
21
+ export {
22
+ ED25519_TORSION_SUBGROUP,
23
+ RistrettoPoint,
24
+ ed25519,
25
+ ed25519_hasher,
26
+ ed25519ctx,
27
+ ed25519ph,
28
+ edwardsToMontgomery,
29
+ edwardsToMontgomeryPriv,
30
+ edwardsToMontgomeryPub,
31
+ encodeToCurve,
32
+ hashToCurve,
33
+ hashToRistretto255,
34
+ hash_to_ristretto255,
35
+ ristretto255,
36
+ ristretto255_hasher,
37
+ x25519
38
+ };