protect-mcp 0.5.0 → 0.5.2

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
@@ -6,7 +6,7 @@ import {
6
6
  formatSimulation,
7
7
  parseLogFile,
8
8
  simulate
9
- } from "./chunk-YKM6W6T7.mjs";
9
+ } from "./chunk-W4U5VNEC.mjs";
10
10
  import {
11
11
  ProtectGateway,
12
12
  buildDecisionContext,
@@ -18,7 +18,7 @@ import {
18
18
  resolveCredential,
19
19
  sendApprovalNotification,
20
20
  validateCredentials
21
- } from "./chunk-IUFFDQYZ.mjs";
21
+ } from "./chunk-IWDR5WU3.mjs";
22
22
  import {
23
23
  createSandboxServer
24
24
  } from "./chunk-SU2FZH7U.mjs";
@@ -30,17 +30,20 @@ import {
30
30
  } from "./chunk-UEHLYOJY.mjs";
31
31
  import {
32
32
  startHookServer
33
- } from "./chunk-IAJJA5IW.mjs";
33
+ } from "./chunk-Q4KOQUKV.mjs";
34
34
  import {
35
35
  checkRateLimit,
36
+ evaluateCedar,
36
37
  getSignerInfo,
37
38
  getToolPolicy,
38
39
  initSigning,
40
+ isCedarAvailable,
39
41
  isSigningEnabled,
42
+ loadCedarPolicies,
40
43
  loadPolicy,
41
44
  parseRateLimit,
42
45
  signDecision
43
- } from "./chunk-V52W3XIN.mjs";
46
+ } from "./chunk-N4F76LTC.mjs";
44
47
  import {
45
48
  collectSignedReceipts,
46
49
  createAuditBundle
@@ -202,6 +205,273 @@ function validateEvidenceReceipt(receipt) {
202
205
  return errors;
203
206
  }
204
207
 
208
+ // src/cedar-schema.ts
209
+ function jsonSchemaToCedarType(schema, namespace, path) {
210
+ if (schema.enum) {
211
+ return "String";
212
+ }
213
+ const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
214
+ switch (type) {
215
+ case "string":
216
+ if (schema.format === "date-time") return "String";
217
+ if (schema.format === "uri") return "String";
218
+ return "String";
219
+ case "integer":
220
+ case "number":
221
+ return "Long";
222
+ case "boolean":
223
+ return "Bool";
224
+ case "array":
225
+ if (schema.items) {
226
+ const itemType = jsonSchemaToCedarType(schema.items, namespace, path + "_item");
227
+ return `Set<${itemType}>`;
228
+ }
229
+ return "Set<String>";
230
+ // Default to Set<String> for untyped arrays
231
+ case "object":
232
+ if (schema.properties && Object.keys(schema.properties).length > 0) {
233
+ const fields = Object.entries(schema.properties).map(([key, prop]) => {
234
+ const cedarType = jsonSchemaToCedarType(prop, namespace, path + "_" + sanitizeIdentifier(key));
235
+ const isRequired = schema.required?.includes(key) ?? false;
236
+ return ` "${sanitizeIdentifier(key)}": ${cedarType}${isRequired ? "" : "?"}`;
237
+ });
238
+ return `{
239
+ ${fields.join(",\n")}
240
+ }`;
241
+ }
242
+ return "Record";
243
+ // Empty objects
244
+ default:
245
+ return "String";
246
+ }
247
+ }
248
+ function sanitizeIdentifier(name) {
249
+ return name.replace(/[^a-zA-Z0-9_]/g, "_").replace(/^(\d)/, "_$1");
250
+ }
251
+ function cedarActionId(toolName) {
252
+ if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(toolName)) {
253
+ return toolName;
254
+ }
255
+ return toolName;
256
+ }
257
+ function generateCedarSchema(tools, config = {}) {
258
+ const ns = config.namespace || "ScopeBlind";
259
+ const includeTier = config.includeTier !== false;
260
+ const includeTimestamp = config.includeTimestamp !== false;
261
+ const includeAgentId = config.includeAgentId !== false;
262
+ const agentAttrs = [];
263
+ if (includeTier) agentAttrs.push(' "tier": String');
264
+ if (includeAgentId) agentAttrs.push(' "agent_id": String?');
265
+ const sessionFields = [];
266
+ if (includeTimestamp) sessionFields.push(' "timestamp": String?');
267
+ sessionFields.push(' "hook_event": String?');
268
+ const actionDeclarations = [];
269
+ const inputTypeDeclarations = [];
270
+ for (const tool of tools) {
271
+ const actionName = cedarActionId(tool.name);
272
+ const inputTypeName = `${sanitizeIdentifier(tool.name)}_Input`;
273
+ if (tool.inputSchema?.properties && Object.keys(tool.inputSchema.properties).length > 0) {
274
+ const fields = Object.entries(tool.inputSchema.properties).map(([key, prop]) => {
275
+ const cedarType = jsonSchemaToCedarType(prop, ns, sanitizeIdentifier(tool.name) + "_" + sanitizeIdentifier(key));
276
+ const isRequired = tool.inputSchema?.required?.includes(key) ?? false;
277
+ return ` "${sanitizeIdentifier(key)}": ${cedarType}${isRequired ? "" : "?"}`;
278
+ });
279
+ inputTypeDeclarations.push(
280
+ ` // Input type for tool: ${tool.name}` + (tool.description ? `
281
+ // ${tool.description}` : "") + `
282
+ type ${inputTypeName} = {
283
+ ${fields.join(",\n")}
284
+ };`
285
+ );
286
+ actionDeclarations.push(
287
+ ` action "${actionName}" in [Action::"MCP::Tool::call"] appliesTo {
288
+ principal: [Agent],
289
+ resource: [Tool],
290
+ context: {
291
+ "input": ${inputTypeName},
292
+ "tier": String${includeTimestamp ? ',\n "timestamp": String?' : ""}${includeAgentId ? ',\n "agent_id": String?' : ""}
293
+ }
294
+ };`
295
+ );
296
+ } else {
297
+ actionDeclarations.push(
298
+ ` action "${actionName}" in [Action::"MCP::Tool::call"] appliesTo {
299
+ principal: [Agent],
300
+ resource: [Tool],
301
+ context: {
302
+ "tier": String${includeTimestamp ? ',\n "timestamp": String?' : ""}${includeAgentId ? ',\n "agent_id": String?' : ""}
303
+ }
304
+ };`
305
+ );
306
+ }
307
+ }
308
+ actionDeclarations.push(
309
+ ` // Blanket action for policies matching any tool call
310
+ action "MCP::Tool::call" appliesTo {
311
+ principal: [Agent],
312
+ resource: [Tool],
313
+ context: {
314
+ "tier": String${includeTimestamp ? ',\n "timestamp": String?' : ""}${includeAgentId ? ',\n "agent_id": String?' : ""}
315
+ }
316
+ };`
317
+ );
318
+ const schemaText = [
319
+ `// Cedar schema for MCP tool governance`,
320
+ `// Generated by protect-mcp from ${tools.length} tool description(s)`,
321
+ `// Compatible with cedar-policy/cedar-for-agents`,
322
+ ``,
323
+ `namespace ${ns} {`,
324
+ ``,
325
+ ` // \u2500\u2500 Entity types \u2500\u2500`,
326
+ ``,
327
+ ` entity Agent${agentAttrs.length > 0 ? ` = {
328
+ ${agentAttrs.join(",\n")}
329
+ }` : ""};`,
330
+ ``,
331
+ ` entity Tool;`,
332
+ ``,
333
+ ...inputTypeDeclarations.length > 0 ? [` // \u2500\u2500 Tool input types \u2500\u2500`, ``, ...inputTypeDeclarations, ``] : [],
334
+ ` // \u2500\u2500 Actions \u2500\u2500`,
335
+ ``,
336
+ ...actionDeclarations,
337
+ ``,
338
+ `}`,
339
+ ``
340
+ ].join("\n");
341
+ const schemaJson = buildSchemaJson(tools, ns, config);
342
+ return {
343
+ schemaText,
344
+ schemaJson,
345
+ toolCount: tools.length,
346
+ tools: tools.map((t) => t.name)
347
+ };
348
+ }
349
+ function buildSchemaJson(tools, namespace, config) {
350
+ const entityTypes = {
351
+ Agent: {
352
+ shape: {
353
+ type: "Record",
354
+ attributes: {
355
+ ...config.includeTier !== false ? { tier: { type: "String", required: false } } : {},
356
+ ...config.includeAgentId !== false ? { agent_id: { type: "String", required: false } } : {}
357
+ }
358
+ },
359
+ memberOfTypes: []
360
+ },
361
+ Tool: {
362
+ shape: { type: "Record", attributes: {} },
363
+ memberOfTypes: []
364
+ }
365
+ };
366
+ const actions = {};
367
+ for (const tool of tools) {
368
+ const contextAttrs = {
369
+ tier: { type: "String", required: false }
370
+ };
371
+ if (config.includeTimestamp !== false) {
372
+ contextAttrs["timestamp"] = { type: "String", required: false };
373
+ }
374
+ if (config.includeAgentId !== false) {
375
+ contextAttrs["agent_id"] = { type: "String", required: false };
376
+ }
377
+ if (tool.inputSchema?.properties) {
378
+ const inputAttrs = {};
379
+ for (const [key, prop] of Object.entries(tool.inputSchema.properties)) {
380
+ inputAttrs[sanitizeIdentifier(key)] = {
381
+ type: jsonSchemaToSchemaJsonType(prop),
382
+ required: tool.inputSchema.required?.includes(key) ?? false
383
+ };
384
+ }
385
+ contextAttrs["input"] = {
386
+ type: "Record",
387
+ attributes: inputAttrs,
388
+ required: false
389
+ };
390
+ }
391
+ actions[tool.name] = {
392
+ appliesTo: {
393
+ principalTypes: ["Agent"],
394
+ resourceTypes: ["Tool"],
395
+ context: { type: "Record", attributes: contextAttrs }
396
+ },
397
+ memberOf: [{ id: "MCP::Tool::call" }]
398
+ };
399
+ }
400
+ const blanketContext = {
401
+ tier: { type: "String", required: false }
402
+ };
403
+ if (config.includeTimestamp !== false) {
404
+ blanketContext["timestamp"] = { type: "String", required: false };
405
+ }
406
+ if (config.includeAgentId !== false) {
407
+ blanketContext["agent_id"] = { type: "String", required: false };
408
+ }
409
+ actions["MCP::Tool::call"] = {
410
+ appliesTo: {
411
+ principalTypes: ["Agent"],
412
+ resourceTypes: ["Tool"],
413
+ context: { type: "Record", attributes: blanketContext }
414
+ }
415
+ };
416
+ return {
417
+ [namespace]: {
418
+ entityTypes,
419
+ actions
420
+ }
421
+ };
422
+ }
423
+ function jsonSchemaToSchemaJsonType(schema) {
424
+ if (schema.enum) return "String";
425
+ const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
426
+ switch (type) {
427
+ case "string":
428
+ return "String";
429
+ case "integer":
430
+ case "number":
431
+ return "Long";
432
+ case "boolean":
433
+ return "EntityOrCommon";
434
+ // Cedar JSON schema uses this for Bool
435
+ case "array":
436
+ return "Set";
437
+ default:
438
+ return "String";
439
+ }
440
+ }
441
+ function generateSchemaStub(namespace = "ScopeBlind") {
442
+ return [
443
+ `// Cedar schema stub for protect-mcp`,
444
+ `// This defines the principal and resource entity types.`,
445
+ `// Tool-specific actions are auto-generated from MCP tools/list.`,
446
+ `//`,
447
+ `// Compatible with cedar-policy/cedar-for-agents @mcp_principal/@mcp_resource annotations.`,
448
+ `// See: https://github.com/cedar-policy/cedar-for-agents`,
449
+ ``,
450
+ `namespace ${namespace} {`,
451
+ ``,
452
+ ` // @mcp_principal`,
453
+ ` entity Agent = {`,
454
+ ` "tier": String,`,
455
+ ` "agent_id": String?`,
456
+ ` };`,
457
+ ``,
458
+ ` // @mcp_resource`,
459
+ ` entity Tool;`,
460
+ ``,
461
+ ` // @mcp_action`,
462
+ ` action "MCP::Tool::call" appliesTo {`,
463
+ ` principal: [Agent],`,
464
+ ` resource: [Tool],`,
465
+ ` context: {`,
466
+ ` "tier": String`,
467
+ ` }`,
468
+ ` };`,
469
+ ``,
470
+ `}`,
471
+ ``
472
+ ].join("\n");
473
+ }
474
+
205
475
  // src/rekor-anchor.ts
206
476
  import { createHash } from "crypto";
207
477
  var REKOR_API = "https://rekor.sigstore.dev/api/v1";
@@ -1452,18 +1722,21 @@ export {
1452
1722
  createSandboxServer,
1453
1723
  destroySandbox,
1454
1724
  ed25519ToDIDKey,
1725
+ evaluateCedar,
1455
1726
  evaluateTier,
1456
1727
  exportC2PAManifestJSON,
1457
1728
  exportJSONL,
1458
1729
  formatReportMarkdown,
1459
1730
  formatSimulation,
1460
1731
  generateC2PACommand,
1732
+ generateCedarSchema,
1461
1733
  generateDatasetCard,
1462
1734
  generateHFMetadata,
1463
1735
  generateHookSettings,
1464
1736
  generateReport,
1465
1737
  generateSafetyTranscript,
1466
1738
  generateSampleCedarPolicy,
1739
+ generateSchemaStub,
1467
1740
  generateVerifyReceiptSkill,
1468
1741
  getSignerInfo,
1469
1742
  getToolPolicy,
@@ -1471,11 +1744,13 @@ export {
1471
1744
  hashResponseBody,
1472
1745
  initSigning,
1473
1746
  isAgentId,
1747
+ isCedarAvailable,
1474
1748
  isDisclosureMode,
1475
1749
  isEvidenceType,
1476
1750
  isManifestStatus,
1477
1751
  isSigningEnabled,
1478
1752
  listCredentialLabels,
1753
+ loadCedarPolicies,
1479
1754
  loadPolicy,
1480
1755
  manifestToVC,
1481
1756
  meetsMinTier,
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "protect-mcp",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "mcpName": "io.github.tomjwxf/protect-mcp",
5
- "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
+ "description": "Enterprise security gateway for MCP servers and Claude Code hooks. Cedar policies, Ed25519-signed receipts, swarm tracking, and tamper detection. Shadow or enforce mode.",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "module": "dist/index.mjs",
@@ -46,7 +46,10 @@
46
46
  "receipts",
47
47
  "trust-tiers",
48
48
  "agent-governance",
49
- "claude-code"
49
+ "claude-code",
50
+ "hooks",
51
+ "swarm",
52
+ "compliance"
50
53
  ],
51
54
  "author": "Tom Farley <tommy@scopeblind.com>",
52
55
  "license": "MIT",