protect-mcp 0.4.4 → 0.4.6
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 +12 -0
- package/dist/{bundle-TXOTFJIJ.mjs → bundle-XTR3YMPO.mjs} +1 -0
- package/dist/chunk-PQJP2ZCI.mjs +8 -0
- package/dist/chunk-SU2FZH7U.mjs +35167 -0
- package/dist/cli.mjs +8 -7
- package/dist/demo-server.d.mts +2 -87
- package/dist/demo-server.d.ts +2 -87
- package/dist/demo-server.js +35026 -4
- package/dist/demo-server.mjs +2 -1
- package/dist/{ed25519-EDO4K4EP.mjs → ed25519-V7HDL2WC.mjs} +1 -0
- package/dist/{http-transport-VLIPOPIC.mjs → http-transport-XCHIKTYG.mjs} +1 -0
- package/dist/index.js +35037 -15
- package/dist/index.mjs +2 -1
- package/dist/{report-ENQ3KUI2.mjs → report-5XCNW6FB.mjs} +1 -0
- package/dist/{utils-IDWBSHJU.mjs → utils-6AYZFE5A.mjs} +1 -0
- package/package.json +2 -1
- package/policies/cedar/spending-authority.cedar +134 -0
- package/dist/chunk-U76JZVH6.mjs +0 -144
package/dist/index.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-VIA2B65K.mjs";
|
|
6
6
|
import {
|
|
7
7
|
createSandboxServer
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-SU2FZH7U.mjs";
|
|
9
9
|
import {
|
|
10
10
|
collectSignedReceipts,
|
|
11
11
|
createAuditBundle
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
formatReportMarkdown,
|
|
35
35
|
generateReport
|
|
36
36
|
} from "./chunk-JQDVKZBN.mjs";
|
|
37
|
+
import "./chunk-PQJP2ZCI.mjs";
|
|
37
38
|
|
|
38
39
|
// src/manifest.ts
|
|
39
40
|
function isAgentId(s) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "protect-mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"mcpName": "io.github.tomjwxf/protect-mcp",
|
|
5
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.",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
},
|
|
64
64
|
"optionalDependencies": {
|
|
65
65
|
"@cedar-policy/cedar-wasm": "^4.9.1",
|
|
66
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
66
67
|
"@noble/curves": "^1.8.0",
|
|
67
68
|
"@noble/hashes": "^1.7.0"
|
|
68
69
|
},
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Spending Authority — Cedar Edition
|
|
3
|
+
//
|
|
4
|
+
// AI agents spending money on behalf of humans is the highest-
|
|
5
|
+
// stakes MCP action class. Without cryptographic proof of
|
|
6
|
+
// authorization, merchants cannot distinguish legitimate agent
|
|
7
|
+
// purchases from unauthorized ones, and organizations cannot
|
|
8
|
+
// enforce budget controls across autonomous agent fleets.
|
|
9
|
+
//
|
|
10
|
+
// The receipt proves authorization; it never touches money.
|
|
11
|
+
// The merchant settles through their existing payment rail.
|
|
12
|
+
//
|
|
13
|
+
// Controls: SOC2-CC6.1, SOC2-CC6.3, SOC2-CC6.6, SOC2-CC8.1
|
|
14
|
+
// EU-AI-Art.9, EU-AI-Art.14, EU-AI-Art.52
|
|
15
|
+
// OWASP-A01, OWASP-A04
|
|
16
|
+
// MCP-01, MCP-05, MCP-06
|
|
17
|
+
// NIST-GOVERN-1.1, NIST-GOVERN-1.2, NIST-MAP-3.1
|
|
18
|
+
// NIST-MEASURE-2.1, NIST-MANAGE-2.3
|
|
19
|
+
// ============================================================
|
|
20
|
+
|
|
21
|
+
// ─── Auto-approve: low-value transactions under threshold ────
|
|
22
|
+
// Threshold is configurable per scope (default: $25.00 / 2500 cents)
|
|
23
|
+
@id("sb-spending-001")
|
|
24
|
+
permit (
|
|
25
|
+
principal,
|
|
26
|
+
action == Action::"MCP::Spend::authorize",
|
|
27
|
+
resource
|
|
28
|
+
)
|
|
29
|
+
when {
|
|
30
|
+
resource.amount_cents < context.auto_approve_threshold_cents &&
|
|
31
|
+
context.budget_utilization != "exceeded" &&
|
|
32
|
+
context.category_allowed == true
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// ─── Require human approval: high-value transactions ─────────
|
|
36
|
+
// Amounts at or above the auto-approve threshold require explicit
|
|
37
|
+
// human confirmation before a receipt is issued.
|
|
38
|
+
@id("sb-spending-002")
|
|
39
|
+
forbid (
|
|
40
|
+
principal,
|
|
41
|
+
action == Action::"MCP::Spend::authorize",
|
|
42
|
+
resource
|
|
43
|
+
)
|
|
44
|
+
when {
|
|
45
|
+
resource.amount_cents >= context.auto_approve_threshold_cents &&
|
|
46
|
+
context.approval_method != "human_confirmed"
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// ─── Block: budget exhausted ─────────────────────────────────
|
|
50
|
+
// When cumulative spend reaches or exceeds the ceiling, all
|
|
51
|
+
// spending is blocked regardless of amount or approval method.
|
|
52
|
+
@id("sb-spending-003")
|
|
53
|
+
forbid (
|
|
54
|
+
principal,
|
|
55
|
+
action == Action::"MCP::Spend::authorize",
|
|
56
|
+
resource
|
|
57
|
+
)
|
|
58
|
+
when {
|
|
59
|
+
context.budget_utilization == "exceeded"
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// ─── Block: disallowed merchant category ─────────────────────
|
|
63
|
+
// Organizations configure allowed merchant category codes.
|
|
64
|
+
// Any transaction to a disallowed category is rejected.
|
|
65
|
+
@id("sb-spending-004")
|
|
66
|
+
forbid (
|
|
67
|
+
principal,
|
|
68
|
+
action == Action::"MCP::Spend::authorize",
|
|
69
|
+
resource
|
|
70
|
+
)
|
|
71
|
+
when {
|
|
72
|
+
context.category_allowed == false
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// ─── Per-agent-tier spending limits ──────────────────────────
|
|
76
|
+
// Unknown agents: $5 max per transaction (500 cents)
|
|
77
|
+
@id("sb-spending-005")
|
|
78
|
+
forbid (
|
|
79
|
+
principal == Agent::"unknown",
|
|
80
|
+
action == Action::"MCP::Spend::authorize",
|
|
81
|
+
resource
|
|
82
|
+
)
|
|
83
|
+
when {
|
|
84
|
+
resource.amount_cents > 500
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Signed-known agents: $50 max per transaction (5000 cents)
|
|
88
|
+
@id("sb-spending-006")
|
|
89
|
+
forbid (
|
|
90
|
+
principal == Agent::"signed-known",
|
|
91
|
+
action == Action::"MCP::Spend::authorize",
|
|
92
|
+
resource
|
|
93
|
+
)
|
|
94
|
+
when {
|
|
95
|
+
resource.amount_cents > 5000
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Evidenced agents: $500 max per transaction (50000 cents)
|
|
99
|
+
@id("sb-spending-007")
|
|
100
|
+
forbid (
|
|
101
|
+
principal == Agent::"evidenced",
|
|
102
|
+
action == Action::"MCP::Spend::authorize",
|
|
103
|
+
resource
|
|
104
|
+
)
|
|
105
|
+
when {
|
|
106
|
+
resource.amount_cents > 50000
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Privileged agents: $5000 max per transaction (500000 cents)
|
|
110
|
+
// Even the highest trust tier has a ceiling — no unlimited spend.
|
|
111
|
+
@id("sb-spending-008")
|
|
112
|
+
forbid (
|
|
113
|
+
principal == Agent::"privileged",
|
|
114
|
+
action == Action::"MCP::Spend::authorize",
|
|
115
|
+
resource
|
|
116
|
+
)
|
|
117
|
+
when {
|
|
118
|
+
resource.amount_cents > 500000
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// ─── Block: high utilization + large transaction ─────────────
|
|
122
|
+
// When budget is 75%+ consumed, block any single transaction
|
|
123
|
+
// that exceeds 10% of the original ceiling. Prevents a single
|
|
124
|
+
// large purchase from blowing through the remaining budget.
|
|
125
|
+
@id("sb-spending-009")
|
|
126
|
+
forbid (
|
|
127
|
+
principal,
|
|
128
|
+
action == Action::"MCP::Spend::authorize",
|
|
129
|
+
resource
|
|
130
|
+
)
|
|
131
|
+
when {
|
|
132
|
+
context.budget_utilization == "high" &&
|
|
133
|
+
resource.amount_cents > context.ceiling_tenth_cents
|
|
134
|
+
};
|
package/dist/chunk-U76JZVH6.mjs
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
// src/demo-server.ts
|
|
2
|
-
import { createInterface } from "readline";
|
|
3
|
-
var TOOLS = [
|
|
4
|
-
{
|
|
5
|
-
name: "read_file",
|
|
6
|
-
description: "Read the contents of a file",
|
|
7
|
-
inputSchema: {
|
|
8
|
-
type: "object",
|
|
9
|
-
properties: { path: { type: "string", description: "File path to read" } },
|
|
10
|
-
required: ["path"]
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
name: "write_file",
|
|
15
|
-
description: "Write content to a file",
|
|
16
|
-
inputSchema: {
|
|
17
|
-
type: "object",
|
|
18
|
-
properties: {
|
|
19
|
-
path: { type: "string", description: "File path to write" },
|
|
20
|
-
content: { type: "string", description: "Content to write" }
|
|
21
|
-
},
|
|
22
|
-
required: ["path", "content"]
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
name: "delete_file",
|
|
27
|
-
description: "Delete a file from the filesystem",
|
|
28
|
-
inputSchema: {
|
|
29
|
-
type: "object",
|
|
30
|
-
properties: { path: { type: "string", description: "File path to delete" } },
|
|
31
|
-
required: ["path"]
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
name: "web_search",
|
|
36
|
-
description: "Search the web for information",
|
|
37
|
-
inputSchema: {
|
|
38
|
-
type: "object",
|
|
39
|
-
properties: { query: { type: "string", description: "Search query" } },
|
|
40
|
-
required: ["query"]
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
name: "deploy",
|
|
45
|
-
description: "Deploy the application to production",
|
|
46
|
-
inputSchema: {
|
|
47
|
-
type: "object",
|
|
48
|
-
properties: {
|
|
49
|
-
environment: { type: "string", description: "Target environment", enum: ["staging", "production"] },
|
|
50
|
-
reason: { type: "string", description: "Deployment reason" }
|
|
51
|
-
},
|
|
52
|
-
required: ["environment"]
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
];
|
|
56
|
-
function handleRequest(request) {
|
|
57
|
-
if (request.method === "initialize") {
|
|
58
|
-
return JSON.stringify({
|
|
59
|
-
jsonrpc: "2.0",
|
|
60
|
-
id: request.id,
|
|
61
|
-
result: {
|
|
62
|
-
protocolVersion: "2024-11-05",
|
|
63
|
-
serverInfo: { name: "protect-mcp-demo", version: "0.2.0" },
|
|
64
|
-
capabilities: { tools: {} }
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
if (request.method === "notifications/initialized") {
|
|
69
|
-
return "";
|
|
70
|
-
}
|
|
71
|
-
if (request.method === "tools/list") {
|
|
72
|
-
return JSON.stringify({
|
|
73
|
-
jsonrpc: "2.0",
|
|
74
|
-
id: request.id,
|
|
75
|
-
result: { tools: TOOLS }
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
if (request.method === "tools/call") {
|
|
79
|
-
const toolName = request.params?.name || "unknown";
|
|
80
|
-
const args = request.params?.arguments || {};
|
|
81
|
-
let resultText;
|
|
82
|
-
switch (toolName) {
|
|
83
|
-
case "read_file":
|
|
84
|
-
resultText = `[demo] Read file: ${args.path || "/example.txt"}
|
|
85
|
-
Contents: Hello from protect-mcp demo server!`;
|
|
86
|
-
break;
|
|
87
|
-
case "write_file":
|
|
88
|
-
resultText = `[demo] Wrote ${String(args.content || "").length} bytes to ${args.path || "/example.txt"}`;
|
|
89
|
-
break;
|
|
90
|
-
case "delete_file":
|
|
91
|
-
resultText = `[demo] Deleted file: ${args.path || "/example.txt"}`;
|
|
92
|
-
break;
|
|
93
|
-
case "web_search":
|
|
94
|
-
resultText = `[demo] Search results for "${args.query || "test"}":
|
|
95
|
-
1. Example result \u2014 scopeblind.com
|
|
96
|
-
2. MCP security \u2014 modelcontextprotocol.io`;
|
|
97
|
-
break;
|
|
98
|
-
case "deploy":
|
|
99
|
-
resultText = `[demo] Deployed to ${args.environment || "staging"}${args.reason ? ` (reason: ${args.reason})` : ""}`;
|
|
100
|
-
break;
|
|
101
|
-
default:
|
|
102
|
-
resultText = `[demo] Unknown tool: ${toolName}`;
|
|
103
|
-
}
|
|
104
|
-
return JSON.stringify({
|
|
105
|
-
jsonrpc: "2.0",
|
|
106
|
-
id: request.id,
|
|
107
|
-
result: {
|
|
108
|
-
content: [{ type: "text", text: resultText }]
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
if (request.id !== void 0) {
|
|
113
|
-
return JSON.stringify({
|
|
114
|
-
jsonrpc: "2.0",
|
|
115
|
-
id: request.id,
|
|
116
|
-
error: { code: -32601, message: `Method not found: ${request.method}` }
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
return "";
|
|
120
|
-
}
|
|
121
|
-
var rl = createInterface({ input: process.stdin, crlfDelay: Infinity });
|
|
122
|
-
rl.on("line", (line) => {
|
|
123
|
-
const trimmed = line.trim();
|
|
124
|
-
if (!trimmed) return;
|
|
125
|
-
try {
|
|
126
|
-
const request = JSON.parse(trimmed);
|
|
127
|
-
const response = handleRequest(request);
|
|
128
|
-
if (response) {
|
|
129
|
-
process.stdout.write(response + "\n");
|
|
130
|
-
}
|
|
131
|
-
} catch {
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
process.stderr.write("[DEMO_SERVER] protect-mcp demo server started \u2014 5 tools registered\n");
|
|
135
|
-
function createSandboxServer() {
|
|
136
|
-
return {
|
|
137
|
-
tools: TOOLS,
|
|
138
|
-
handleRequest
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export {
|
|
143
|
-
createSandboxServer
|
|
144
|
-
};
|