@kaditang/402sentinel-mcp 0.1.0 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +26 -10
  2. package/dist/index.js +122 -54
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,10 +1,17 @@
1
1
  # 402sentinel-mcp
2
2
 
3
- An MCP tool that lets your AI agent **check an x402 counterparty's risk before it
4
- pays**. One tool, `assess_counterparty`: give it a payTo address, get back a
5
- 0–100 risk score + an `allow` / `review` / `block` decision, scored from on-chain
6
- settlement behaviour on Base (address age, facilitator-aware payer diversity,
7
- settlement maturity) with honest confidence/coverage.
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:
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
8
15
 
9
16
  It's a thin client for the hosted service at **https://402sentinel.com** — the
10
17
  scoring model and facilitator-identification logic live server-side (closed); this
@@ -33,8 +40,9 @@ Add to your MCP client (Claude Desktop, Cursor, etc.):
33
40
  }
34
41
  ```
35
42
 
36
- Each assessment costs **$0.01**, paid automatically in USDC via x402 (Circle
37
- Gateway, gas-free on Base) from the configured wallet.
43
+ Paid calls cost from **$0.002** (shallow) to **$0.02** (deep), paid automatically
44
+ in USDC via x402 (Circle Gateway, gas-free on Base) from the configured wallet.
45
+ `report_outcome` is free. (`CLIENT_PRIVATE_KEY` is only needed for the paid tools.)
38
46
 
39
47
  ## Use
40
48
 
@@ -46,13 +54,21 @@ assess_counterparty({
46
54
  payment_context: { amount: 10, asset: "USDC" },
47
55
  policy: { block_at_score: 70, review_at_score: 40 }
48
56
  })
49
- → { decision: "review", risk_score: 52, confidence: 0.41, coverage: {...}, dimensions: [...], recommendation: "..." }
57
+ → { decision: "review", risk_score: 52, confidence: 0.41, coverage: {...},
58
+ dimensions: [...], recommendation: "...",
59
+ recommended_policy: { action: "limit", max_payment_usdc: 5, daily_cap_usdc: 15,
60
+ add_to_denylist: false, require_human_approval: true } }
50
61
  ```
51
62
 
52
- - `block` → don't pay
53
- - `review` → cap exposure / escrow
63
+ - `block` / `deny` → don't pay
64
+ - `review` / `limit` → cap exposure / escrow (use `recommended_policy` for the caps)
54
65
  - `allow` → proceed
55
66
 
67
+ `recommend_policy(...)` returns just the decision + `recommended_policy` — apply
68
+ `max_payment_usdc` / `daily_cap_usdc` / `add_to_denylist` directly to your agent
69
+ wallet's spending limits. After paying, call `report_outcome({ assessment_id,
70
+ outcome })` to improve future scores.
71
+
56
72
  ## Disclaimer
57
73
 
58
74
  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
- * Exposes one tool, `assess_counterparty`, that an agent calls BEFORE paying an
6
- * x402 counterparty. It pays $0.01 (x402, Circle Gateway on Base) to the hosted
7
- * 402sentinel.com scoring service and returns a 0-100 risk score + allow/review/
8
- * block decision. The scoring model / facilitator logic live server-side
9
- * (closed); this client only forwards + pays, so it's safe to open-source.
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
- * (it pays $0.01 per assessment). Required.
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,90 @@ 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 and an allow/review/block decision relative to your policy, scored from on-chain settlement behaviour on Base (address age, facilitator-aware payer diversity, settlement maturity) with honest confidence/coverage. Call this before authorizing any x402 payment above your risk threshold. Costs $0.01 (paid automatically in USDC).",
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.",
26
102
  inputSchema: {
27
103
  type: "object",
28
- required: ["target"],
104
+ required: ["assessment_id", "outcome"],
29
105
  properties: {
30
- target: {
31
- type: "object",
32
- required: ["payto_address"],
33
- properties: {
34
- payto_address: { type: "string", description: "Chain address that will receive the payment" },
35
- resource_url: { type: "string", description: "The x402 resource/endpoint URL (optional)" },
36
- network: { type: "string", description: "CAIP-2 chain id, e.g. eip155:8453 (optional)" },
37
- },
38
- },
39
- payment_context: {
40
- type: "object",
41
- properties: {
42
- amount: { type: "number", description: "Payment amount you're about to make" },
43
- asset: { type: "string", description: "e.g. USDC" },
44
- },
45
- },
46
- policy: {
47
- type: "object",
48
- properties: {
49
- block_at_score: { type: "number", description: "risk >= this => block (default 70)" },
50
- review_at_score: { type: "number", description: "risk >= this => review (default 40)" },
51
- min_confidence: { type: "number", description: "below this => force review (default 0.5)" },
52
- },
53
- },
54
- depth: { type: "string", enum: ["shallow", "deep"], description: "shallow=cheap/cached, deep=fresh (default shallow)" },
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)" },
55
109
  },
56
110
  },
111
+ endpoint: "/api/report_outcome",
112
+ paid: false,
57
113
  },
58
114
  ];
59
115
  function clientOrNull() {
@@ -63,35 +119,47 @@ function clientOrNull() {
63
119
  return new GatewayClient({ chain: "base", privateKey: pk });
64
120
  }
65
121
  async function main() {
66
- const server = new Server({ name: "402sentinel", version: "0.1.0" }, { capabilities: { tools: {} } });
67
- server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
122
+ const server = new Server({ name: "402sentinel", version: "0.2.0" }, { capabilities: { tools: {} } });
123
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
124
+ tools: TOOLS.map(({ name, description, inputSchema }) => ({ name, description, inputSchema })),
125
+ }));
68
126
  server.setRequestHandler(CallToolRequestSchema, async (req) => {
69
127
  const { name, arguments: args } = req.params;
70
- if (name !== "assess_counterparty") {
128
+ const tool = TOOLS.find((t) => t.name === name);
129
+ if (!tool) {
71
130
  return { content: [{ type: "text", text: `unknown tool: ${name}` }], isError: true };
72
131
  }
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
132
  try {
86
- const { data } = await client.pay(`${BASE}/api/assess`, {
87
- method: "POST",
88
- body: args,
89
- });
133
+ let data;
134
+ if (tool.paid) {
135
+ const client = clientOrNull();
136
+ if (!client) {
137
+ return {
138
+ content: [{
139
+ type: "text",
140
+ text: JSON.stringify({
141
+ 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.",
142
+ }),
143
+ }],
144
+ isError: true,
145
+ };
146
+ }
147
+ ({ data } = await client.pay(`${BASE}${tool.endpoint}`, { method: "POST", body: args }));
148
+ }
149
+ else {
150
+ // free endpoint — plain POST, no payment
151
+ const res = await fetch(`${BASE}${tool.endpoint}`, {
152
+ method: "POST",
153
+ headers: { "Content-Type": "application/json" },
154
+ body: JSON.stringify(args ?? {}),
155
+ });
156
+ data = await res.json().catch(() => ({}));
157
+ }
90
158
  return { content: [{ type: "text", text: JSON.stringify(data) }] };
91
159
  }
92
160
  catch (e) {
93
161
  return {
94
- content: [{ type: "text", text: JSON.stringify({ error: `assessment failed: ${e.message}` }) }],
162
+ content: [{ type: "text", text: JSON.stringify({ error: `${name} failed: ${e.message}` }) }],
95
163
  isError: true,
96
164
  };
97
165
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kaditang/402sentinel-mcp",
3
- "version": "0.1.0",
4
- "description": "MCP tool to assess an x402 counterparty's risk BEFORE paying — allow/review/block, scored on-chain. Thin client for 402sentinel.com.",
3
+ "version": "0.2.0",
4
+ "description": "MCP tools to assess an x402 counterparty BEFORE paying — risk score, allow/review/block, and a wallet-ready spending policy. Thin client for 402sentinel.com.",
5
5
  "type": "module",
6
6
  "bin": { "402sentinel-mcp": "./dist/index.js" },
7
7
  "main": "dist/index.js",