protect-mcp 0.2.2 → 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/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  ProtectGateway,
3
+ buildDecisionContext,
3
4
  checkRateLimit,
4
5
  evaluateTier,
5
6
  getSignerInfo,
@@ -10,181 +11,15 @@ import {
10
11
  loadPolicy,
11
12
  meetsMinTier,
12
13
  parseRateLimit,
14
+ queryExternalPDP,
13
15
  resolveCredential,
14
16
  signDecision,
15
17
  validateCredentials
16
- } from "./chunk-ZCKNFULF.mjs";
17
-
18
- // src/external-pdp.ts
19
- async function queryExternalPDP(context, config) {
20
- const timeout = config.timeout_ms || 500;
21
- const controller = new AbortController();
22
- const timer = setTimeout(() => controller.abort(), timeout);
23
- try {
24
- const body = formatRequest(context, config.format || "generic");
25
- const response = await fetch(config.endpoint, {
26
- method: "POST",
27
- headers: { "Content-Type": "application/json" },
28
- body: JSON.stringify(body),
29
- signal: controller.signal
30
- });
31
- clearTimeout(timer);
32
- if (!response.ok) {
33
- return fallbackDecision(config, `PDP returned HTTP ${response.status}`);
34
- }
35
- const result = await response.json();
36
- return parseResponse(result, config.format || "generic");
37
- } catch (err) {
38
- clearTimeout(timer);
39
- if (err instanceof Error && err.name === "AbortError") {
40
- return fallbackDecision(config, `PDP timeout after ${timeout}ms`);
41
- }
42
- return fallbackDecision(config, `PDP error: ${err instanceof Error ? err.message : "unknown"}`);
43
- }
44
- }
45
- function formatRequest(context, format) {
46
- switch (format) {
47
- case "opa":
48
- return {
49
- input: {
50
- actor: context.actor,
51
- action: context.action,
52
- target: context.target,
53
- credential_ref: context.credential_ref,
54
- mode: context.mode,
55
- metadata: context.request_metadata
56
- }
57
- };
58
- case "cerbos":
59
- return {
60
- principal: {
61
- id: context.actor.id || "unknown",
62
- roles: [context.actor.tier],
63
- attr: {
64
- manifest_hash: context.actor.manifest_hash
65
- }
66
- },
67
- resource: {
68
- kind: "tool",
69
- id: context.action.tool,
70
- attr: context.target
71
- },
72
- actions: [context.action.operation || "call"]
73
- };
74
- case "generic":
75
- default:
76
- return context;
77
- }
78
- }
79
- function parseResponse(result, format) {
80
- switch (format) {
81
- case "opa":
82
- if (typeof result.result === "boolean") {
83
- return { allowed: result.result };
84
- }
85
- if (result.result && typeof result.result === "object") {
86
- const r = result.result;
87
- return {
88
- allowed: Boolean(r.allow),
89
- reason: r.reason,
90
- metadata: r
91
- };
92
- }
93
- return { allowed: false, reason: "unrecognized OPA response" };
94
- case "cerbos":
95
- if (Array.isArray(result.results) && result.results.length > 0) {
96
- const actions = result.results[0].actions;
97
- if (actions) {
98
- const effect = Object.values(actions)[0];
99
- return { allowed: effect === "EFFECT_ALLOW" };
100
- }
101
- }
102
- return { allowed: false, reason: "unrecognized Cerbos response" };
103
- case "generic":
104
- default:
105
- return {
106
- allowed: Boolean(result.allowed),
107
- reason: result.reason,
108
- metadata: result.metadata
109
- };
110
- }
111
- }
112
- function fallbackDecision(config, reason) {
113
- const fallback = config.fallback || "deny";
114
- return {
115
- allowed: fallback === "allow",
116
- reason: `fallback_${fallback}: ${reason}`
117
- };
118
- }
119
- function buildDecisionContext(toolName, tier, opts) {
120
- return {
121
- v: 1,
122
- actor: {
123
- id: opts.agentId,
124
- tier,
125
- manifest_hash: opts.manifestHash
126
- },
127
- action: {
128
- tool: toolName,
129
- operation: "call"
130
- },
131
- target: {
132
- service: opts.slug || "default"
133
- },
134
- credential_ref: opts.credentialRef,
135
- mode: opts.mode,
136
- request_metadata: opts.requestMetadata || {}
137
- };
138
- }
139
-
140
- // src/bundle.ts
141
- function createAuditBundle(opts) {
142
- const receipts = opts.receipts.filter(
143
- (r) => r && typeof r === "object" && typeof r.signature === "string"
144
- );
145
- if (receipts.length === 0) {
146
- throw new Error("Audit bundle requires at least one signed receipt");
147
- }
148
- const keyMap = /* @__PURE__ */ new Map();
149
- for (const key of opts.signingKeys) {
150
- if (!keyMap.has(key.kid)) {
151
- keyMap.set(key.kid, key);
152
- }
153
- }
154
- let timeRange = opts.timeRange || null;
155
- if (!timeRange) {
156
- const timestamps = receipts.map((r) => r.issued_at || r.timestamp).filter(Boolean).sort();
157
- if (timestamps.length > 0) {
158
- timeRange = {
159
- from: timestamps[0],
160
- to: timestamps[timestamps.length - 1]
161
- };
162
- }
163
- }
164
- return {
165
- format: "scopeblind:audit-bundle",
166
- version: 1,
167
- exported_at: (/* @__PURE__ */ new Date()).toISOString(),
168
- tenant: opts.tenant,
169
- time_range: timeRange,
170
- receipts,
171
- anchors: opts.anchors || [],
172
- verification: {
173
- algorithm: "ed25519",
174
- signing_keys: Array.from(keyMap.values()),
175
- instructions: `Verify each receipt by: (1) remove the "signature" field, (2) canonicalize the remaining object with JCS (sorted keys at every level), (3) encode as UTF-8 bytes, (4) verify the Ed25519 signature using the signing key matching the receipt's "kid" field. CLI: npx @veritasacta/verify bundle.json --bundle`
176
- }
177
- };
178
- }
179
- function collectSignedReceipts(logs) {
180
- return logs.filter((log) => log.v === 2).map((log) => {
181
- const logRecord = log;
182
- if (logRecord.receipt) {
183
- return logRecord.receipt;
184
- }
185
- return logRecord;
186
- }).filter((r) => typeof r.signature === "string");
187
- }
18
+ } from "./chunk-U7TMVD3E.mjs";
19
+ import {
20
+ collectSignedReceipts,
21
+ createAuditBundle
22
+ } from "./chunk-5JXFV37Y.mjs";
188
23
 
189
24
  // src/manifest.ts
190
25
  function isAgentId(s) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "protect-mcp",
3
- "version": "0.2.2",
4
- "description": "Security gateway for MCP servers. Shadow-mode logs by default, per-tool policies, optional local signing, and offline-verifiable receipts.",
3
+ "version": "0.3.1",
4
+ "description": "Security gateway for MCP servers. Shadow-mode logs, per-tool policies, optional local Ed25519-signed receipts. Programmatic hooks for trust tiers, credential config, and external policy engines.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "module": "dist/index.mjs",
@@ -16,7 +16,7 @@
16
16
  }
17
17
  },
18
18
  "scripts": {
19
- "build": "tsup src/index.ts src/cli.ts --format cjs,esm --dts --clean",
19
+ "build": "tsup src/index.ts src/cli.ts src/demo-server.ts --format cjs,esm --dts --clean",
20
20
  "test": "vitest run",
21
21
  "prepublishOnly": "npm run build"
22
22
  },