@kaditang/402sentinel-mcp 0.1.0 → 0.3.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 +30 -10
- package/dist/index.js +172 -40
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
# 402sentinel-mcp
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
pays
|
|
5
|
-
0–100 risk score + an `allow` / `review` / `block`
|
|
6
|
-
settlement behaviour on Base (address age,
|
|
7
|
-
settlement maturity)
|
|
3
|
+
MCP tools that let your AI agent **check an x402 counterparty's risk before it
|
|
4
|
+
pays** — and turn that risk into an enforceable wallet spending policy. Give it a
|
|
5
|
+
payTo address, get back a 0–100 risk score + an `allow` / `review` / `block`
|
|
6
|
+
decision, scored from on-chain settlement behaviour on Base (address age,
|
|
7
|
+
facilitator-aware payer diversity, settlement maturity) + a delivery-outcome
|
|
8
|
+
flywheel, with honest confidence/coverage.
|
|
9
|
+
|
|
10
|
+
Tools — vet the **seller**:
|
|
11
|
+
- `assess_counterparty` ($0.002) — risk score + decision + a ready-to-apply `recommended_policy`
|
|
12
|
+
- `assess_counterparty_deep` ($0.02) — same, scans more on-chain history
|
|
13
|
+
- `recommend_policy` ($0.002) — decision + wallet-ready spending policy (caps, denylist, approval)
|
|
14
|
+
- `report_outcome` (free) — after paying, report delivery to train the reliability flywheel
|
|
15
|
+
|
|
16
|
+
Tools — vet the **payment itself** (buyer-side):
|
|
17
|
+
- `firewall` ($0.002) — should YOUR agent make THIS payment now? Catches fraudulent routing (payTo swapped vs the address you usually pay), drain velocity, overcharge, and injection-sourced instructions. Pass your payer wallet as `agent_id`.
|
|
18
|
+
- `firewall_record` (free) — seed your agent's payment history so the firewall has a behavioural baseline.
|
|
8
19
|
|
|
9
20
|
It's a thin client for the hosted service at **https://402sentinel.com** — the
|
|
10
21
|
scoring model and facilitator-identification logic live server-side (closed); this
|
|
@@ -33,8 +44,9 @@ Add to your MCP client (Claude Desktop, Cursor, etc.):
|
|
|
33
44
|
}
|
|
34
45
|
```
|
|
35
46
|
|
|
36
|
-
|
|
37
|
-
Gateway, gas-free on Base) from the configured wallet.
|
|
47
|
+
Paid calls cost from **$0.002** (shallow) to **$0.02** (deep), paid automatically
|
|
48
|
+
in USDC via x402 (Circle Gateway, gas-free on Base) from the configured wallet.
|
|
49
|
+
`report_outcome` is free. (`CLIENT_PRIVATE_KEY` is only needed for the paid tools.)
|
|
38
50
|
|
|
39
51
|
## Use
|
|
40
52
|
|
|
@@ -46,13 +58,21 @@ assess_counterparty({
|
|
|
46
58
|
payment_context: { amount: 10, asset: "USDC" },
|
|
47
59
|
policy: { block_at_score: 70, review_at_score: 40 }
|
|
48
60
|
})
|
|
49
|
-
→ { decision: "review", risk_score: 52, confidence: 0.41, coverage: {...},
|
|
61
|
+
→ { decision: "review", risk_score: 52, confidence: 0.41, coverage: {...},
|
|
62
|
+
dimensions: [...], recommendation: "...",
|
|
63
|
+
recommended_policy: { action: "limit", max_payment_usdc: 5, daily_cap_usdc: 15,
|
|
64
|
+
add_to_denylist: false, require_human_approval: true } }
|
|
50
65
|
```
|
|
51
66
|
|
|
52
|
-
- `block` → don't pay
|
|
53
|
-
- `review` → cap exposure / escrow
|
|
67
|
+
- `block` / `deny` → don't pay
|
|
68
|
+
- `review` / `limit` → cap exposure / escrow (use `recommended_policy` for the caps)
|
|
54
69
|
- `allow` → proceed
|
|
55
70
|
|
|
71
|
+
`recommend_policy(...)` returns just the decision + `recommended_policy` — apply
|
|
72
|
+
`max_payment_usdc` / `daily_cap_usdc` / `add_to_denylist` directly to your agent
|
|
73
|
+
wallet's spending limits. After paying, call `report_outcome({ assessment_id,
|
|
74
|
+
outcome })` to improve future scores.
|
|
75
|
+
|
|
56
76
|
## Disclaimer
|
|
57
77
|
|
|
58
78
|
Algorithmic risk signal, informational only — **not advice, not an endorsement,
|
package/dist/index.js
CHANGED
|
@@ -2,15 +2,22 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* 402Sentinel MCP — thin, open-source client.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* (
|
|
5
|
+
* Tools an agent calls around an x402 payment:
|
|
6
|
+
* - assess_counterparty ($0.002) — risk score + allow/review/block + a
|
|
7
|
+
* ready-to-apply spending policy
|
|
8
|
+
* - assess_counterparty_deep ($0.02) — same, deeper on-chain history
|
|
9
|
+
* - recommend_policy ($0.002) — trimmed view: decision + wallet-ready
|
|
10
|
+
* spending policy (caps, denylist, approval)
|
|
11
|
+
* - report_outcome (FREE) — after paying, report delivery to train
|
|
12
|
+
* the settlement-reliability flywheel
|
|
13
|
+
*
|
|
14
|
+
* Payments settle via x402 (Circle Gateway on Base). The scoring model /
|
|
15
|
+
* facilitator logic / flywheel live server-side (closed); this client only
|
|
16
|
+
* forwards + pays, so it's safe to open-source.
|
|
10
17
|
*
|
|
11
18
|
* Config (env):
|
|
12
|
-
* CLIENT_PRIVATE_KEY — a Base wallet with USDC in its Circle Gateway balance
|
|
13
|
-
*
|
|
19
|
+
* CLIENT_PRIVATE_KEY — a Base wallet with USDC in its Circle Gateway balance.
|
|
20
|
+
* Required for the paid tools (not for report_outcome).
|
|
14
21
|
* SENTINEL_URL — override base URL (default https://402sentinel.com).
|
|
15
22
|
*/
|
|
16
23
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -19,41 +26,154 @@ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextpro
|
|
|
19
26
|
import { GatewayClient } from "@circle-fin/x402-batching/client";
|
|
20
27
|
const BASE = (process.env.SENTINEL_URL ?? "https://402sentinel.com").replace(/\/$/, "");
|
|
21
28
|
const RAW_PK = process.env.CLIENT_PRIVATE_KEY ?? "";
|
|
29
|
+
const targetSchema = {
|
|
30
|
+
type: "object",
|
|
31
|
+
required: ["payto_address"],
|
|
32
|
+
properties: {
|
|
33
|
+
payto_address: { type: "string", description: "Chain address that will receive the payment" },
|
|
34
|
+
resource_url: { type: "string", description: "The x402 resource/endpoint URL (optional)" },
|
|
35
|
+
network: { type: "string", description: "CAIP-2 chain id, e.g. eip155:8453 (optional)" },
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
const paymentContextSchema = {
|
|
39
|
+
type: "object",
|
|
40
|
+
properties: {
|
|
41
|
+
amount: { type: "number", description: "Payment amount you're about to make" },
|
|
42
|
+
asset: { type: "string", description: "e.g. USDC" },
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
const assessInput = {
|
|
46
|
+
type: "object",
|
|
47
|
+
required: ["target"],
|
|
48
|
+
properties: {
|
|
49
|
+
target: targetSchema,
|
|
50
|
+
payment_context: paymentContextSchema,
|
|
51
|
+
policy: {
|
|
52
|
+
type: "object",
|
|
53
|
+
properties: {
|
|
54
|
+
block_at_score: { type: "number", description: "risk >= this => block (default 70)" },
|
|
55
|
+
review_at_score: { type: "number", description: "risk >= this => review (default 40)" },
|
|
56
|
+
min_confidence: { type: "number", description: "below this => force review (default 0.5)" },
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
const policyInput = {
|
|
62
|
+
type: "object",
|
|
63
|
+
required: ["target"],
|
|
64
|
+
properties: {
|
|
65
|
+
target: targetSchema,
|
|
66
|
+
payment_context: paymentContextSchema,
|
|
67
|
+
policy: {
|
|
68
|
+
type: "object",
|
|
69
|
+
properties: {
|
|
70
|
+
max_payment_usdc: { type: "number", description: "most you'd expose to ONE counterparty (default 50)" },
|
|
71
|
+
review_ceiling_usdc: { type: "number", description: "hard per-payment cap on the review tier (default 5)" },
|
|
72
|
+
min_confidence: { type: "number", description: "below this => require human approval (default 0.5)" },
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
};
|
|
22
77
|
const TOOLS = [
|
|
23
78
|
{
|
|
24
79
|
name: "assess_counterparty",
|
|
25
|
-
description: "Assess the risk of an x402 counterparty (a payTo address) BEFORE paying. Returns a 0-100 risk_score
|
|
80
|
+
description: "Assess the risk of an x402 counterparty (a payTo address) BEFORE paying. Returns a 0-100 risk_score, an allow/review/block decision, honest confidence/coverage, and a ready-to-apply recommended_policy (per-counterparty caps + approval/denylist), scored from on-chain settlement behaviour on Base + a delivery-outcome flywheel. Call before authorizing any x402 payment above your risk threshold. Costs $0.002 (paid automatically in USDC).",
|
|
81
|
+
inputSchema: assessInput,
|
|
82
|
+
endpoint: "/api/assess",
|
|
83
|
+
paid: true,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "assess_counterparty_deep",
|
|
87
|
+
description: "Like assess_counterparty but scans more on-chain settlement history for a higher-confidence read. Use for larger or higher-stakes payments. Costs $0.02 (paid automatically in USDC).",
|
|
88
|
+
inputSchema: assessInput,
|
|
89
|
+
endpoint: "/api/assess/deep",
|
|
90
|
+
paid: true,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "recommend_policy",
|
|
94
|
+
description: "Turn a counterparty's risk into an enforceable spending policy you can apply to your agent wallet. Returns an allow/limit/deny decision plus recommended_policy: max_payment_usdc (per-counterparty cap), daily_cap_usdc, add_to_denylist, require_human_approval. Costs $0.002 (paid automatically in USDC).",
|
|
95
|
+
inputSchema: policyInput,
|
|
96
|
+
endpoint: "/api/policy",
|
|
97
|
+
paid: true,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: "report_outcome",
|
|
101
|
+
description: "FREE. After you pay a counterparty, report whether they delivered so 402Sentinel's settlement-reliability flywheel can learn. Pass the assessment_id returned by a prior assessment.",
|
|
102
|
+
inputSchema: {
|
|
103
|
+
type: "object",
|
|
104
|
+
required: ["assessment_id", "outcome"],
|
|
105
|
+
properties: {
|
|
106
|
+
assessment_id: { type: "string", description: "assessment_id from a prior assess/policy call" },
|
|
107
|
+
outcome: { type: "string", enum: ["delivered", "partial", "not_delivered", "overcharged"] },
|
|
108
|
+
tx_hash: { type: "string", description: "settlement tx hash (optional)" },
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
endpoint: "/api/report_outcome",
|
|
112
|
+
paid: false,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: "firewall",
|
|
116
|
+
description: "Buyer-side payment firewall: should YOUR agent make THIS payment now? Where assess_counterparty vets the seller, this vets the payment instruction in the context of your agent's own history + provenance. Returns allow/hold/block + signals: routing_anomaly (payTo swapped vs the address you usually pay for this resource = fraudulent routing), velocity_anomaly (drain), amount_anomaly (overcharge), provenance_flag (injection/untrusted source), counterparty_risk. Pass your payer wallet as agent_id. Costs $0.002. Seed history free with firewall_record.",
|
|
26
117
|
inputSchema: {
|
|
27
118
|
type: "object",
|
|
28
|
-
required: ["
|
|
119
|
+
required: ["agent_id", "payment"],
|
|
29
120
|
properties: {
|
|
30
|
-
|
|
121
|
+
agent_id: { type: "string", description: "stable id for your agent — use your payer wallet address" },
|
|
122
|
+
payment: {
|
|
31
123
|
type: "object",
|
|
32
124
|
required: ["payto_address"],
|
|
33
125
|
properties: {
|
|
34
|
-
payto_address: { type: "string", description: "
|
|
35
|
-
|
|
36
|
-
|
|
126
|
+
payto_address: { type: "string", description: "address you're about to pay" },
|
|
127
|
+
amount: { type: "number" },
|
|
128
|
+
asset: { type: "string", description: "e.g. USDC" },
|
|
129
|
+
resource_url: { type: "string", description: "what you're paying for" },
|
|
37
130
|
},
|
|
38
131
|
},
|
|
39
|
-
|
|
132
|
+
context: {
|
|
40
133
|
type: "object",
|
|
41
134
|
properties: {
|
|
42
|
-
|
|
43
|
-
|
|
135
|
+
source: { type: "string", enum: ["tool_output", "web_content", "user", "unknown"], description: "where the payTo/instruction came from" },
|
|
136
|
+
metadata: { type: "object", description: "x402 description/reason strings (scanned for injection)" },
|
|
137
|
+
expected_payto: { type: "string", description: "known-good address for this resource (optional)" },
|
|
44
138
|
},
|
|
45
139
|
},
|
|
46
140
|
policy: {
|
|
47
141
|
type: "object",
|
|
48
142
|
properties: {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
143
|
+
max_payment_usdc: { type: "number" },
|
|
144
|
+
velocity_window_min: { type: "number" },
|
|
145
|
+
velocity_cap_usdc: { type: "number" },
|
|
146
|
+
check_counterparty: { type: "boolean" },
|
|
147
|
+
block_on: { type: "array", items: { type: "string" } },
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
endpoint: "/api/firewall",
|
|
153
|
+
paid: true,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: "firewall_record",
|
|
157
|
+
description: "FREE. Seed your agent's payment history so the firewall has a behavioural baseline (record past/known-good payments). Pass your payer wallet as agent_id.",
|
|
158
|
+
inputSchema: {
|
|
159
|
+
type: "object",
|
|
160
|
+
required: ["agent_id", "payment"],
|
|
161
|
+
properties: {
|
|
162
|
+
agent_id: { type: "string", description: "use your payer wallet address" },
|
|
163
|
+
payment: {
|
|
164
|
+
type: "object",
|
|
165
|
+
required: ["payto_address"],
|
|
166
|
+
properties: {
|
|
167
|
+
payto_address: { type: "string" },
|
|
168
|
+
amount: { type: "number" },
|
|
169
|
+
asset: { type: "string" },
|
|
170
|
+
resource_url: { type: "string" },
|
|
52
171
|
},
|
|
53
172
|
},
|
|
54
|
-
depth: { type: "string", enum: ["shallow", "deep"], description: "shallow=cheap/cached, deep=fresh (default shallow)" },
|
|
55
173
|
},
|
|
56
174
|
},
|
|
175
|
+
endpoint: "/api/firewall/record",
|
|
176
|
+
paid: false,
|
|
57
177
|
},
|
|
58
178
|
];
|
|
59
179
|
function clientOrNull() {
|
|
@@ -63,35 +183,47 @@ function clientOrNull() {
|
|
|
63
183
|
return new GatewayClient({ chain: "base", privateKey: pk });
|
|
64
184
|
}
|
|
65
185
|
async function main() {
|
|
66
|
-
const server = new Server({ name: "402sentinel", version: "0.
|
|
67
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
186
|
+
const server = new Server({ name: "402sentinel", version: "0.3.0" }, { capabilities: { tools: {} } });
|
|
187
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
188
|
+
tools: TOOLS.map(({ name, description, inputSchema }) => ({ name, description, inputSchema })),
|
|
189
|
+
}));
|
|
68
190
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
69
191
|
const { name, arguments: args } = req.params;
|
|
70
|
-
|
|
192
|
+
const tool = TOOLS.find((t) => t.name === name);
|
|
193
|
+
if (!tool) {
|
|
71
194
|
return { content: [{ type: "text", text: `unknown tool: ${name}` }], isError: true };
|
|
72
195
|
}
|
|
73
|
-
const client = clientOrNull();
|
|
74
|
-
if (!client) {
|
|
75
|
-
return {
|
|
76
|
-
content: [{
|
|
77
|
-
type: "text",
|
|
78
|
-
text: JSON.stringify({
|
|
79
|
-
error: "CLIENT_PRIVATE_KEY not set. Provide a Base wallet (with USDC in its Circle Gateway balance) so this tool can pay $0.01 per assessment.",
|
|
80
|
-
}),
|
|
81
|
-
}],
|
|
82
|
-
isError: true,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
196
|
try {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
197
|
+
let data;
|
|
198
|
+
if (tool.paid) {
|
|
199
|
+
const client = clientOrNull();
|
|
200
|
+
if (!client) {
|
|
201
|
+
return {
|
|
202
|
+
content: [{
|
|
203
|
+
type: "text",
|
|
204
|
+
text: JSON.stringify({
|
|
205
|
+
error: "CLIENT_PRIVATE_KEY not set. Provide a Base wallet (with USDC in its Circle Gateway balance) so this tool can pay for the call.",
|
|
206
|
+
}),
|
|
207
|
+
}],
|
|
208
|
+
isError: true,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
({ data } = await client.pay(`${BASE}${tool.endpoint}`, { method: "POST", body: args }));
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
// free endpoint — plain POST, no payment
|
|
215
|
+
const res = await fetch(`${BASE}${tool.endpoint}`, {
|
|
216
|
+
method: "POST",
|
|
217
|
+
headers: { "Content-Type": "application/json" },
|
|
218
|
+
body: JSON.stringify(args ?? {}),
|
|
219
|
+
});
|
|
220
|
+
data = await res.json().catch(() => ({}));
|
|
221
|
+
}
|
|
90
222
|
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
91
223
|
}
|
|
92
224
|
catch (e) {
|
|
93
225
|
return {
|
|
94
|
-
content: [{ type: "text", text: JSON.stringify({ error:
|
|
226
|
+
content: [{ type: "text", text: JSON.stringify({ error: `${name} failed: ${e.message}` }) }],
|
|
95
227
|
isError: true,
|
|
96
228
|
};
|
|
97
229
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaditang/402sentinel-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "MCP tools for x402 payment safety — vet the counterparty (risk score, allow/review/block, spending policy) AND vet the payment itself (buyer-side firewall: routing/drain/injection). Thin client for 402sentinel.com.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": { "402sentinel-mcp": "./dist/index.js" },
|
|
7
7
|
"main": "dist/index.js",
|