@kaditang/402sentinel-mcp 0.4.0 → 0.6.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 +2 -1
- package/dist/index.js +51 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,8 +14,9 @@ Tools — vet the **seller**:
|
|
|
14
14
|
- `report_outcome` (free) — after paying, report delivery to train the reliability flywheel
|
|
15
15
|
|
|
16
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.
|
|
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. `agent_id` + a wallet-ownership signature are attached automatically from your configured wallet — trusted routing history with no extra steps.
|
|
18
18
|
- `firewall_record` (free) — seed your agent's payment history so the firewall has a behavioural baseline.
|
|
19
|
+
- `firewall_outcome` (free) — after a verdict, report what actually happened (fraud / legit / …) so the firewall learns which signals are predictive and downweights noisy ones (safety signals stay deterministic).
|
|
19
20
|
|
|
20
21
|
It's a thin client for the hosted service at **https://402sentinel.com** — the
|
|
21
22
|
scoring model and facilitator-identification logic live server-side (closed); this
|
package/dist/index.js
CHANGED
|
@@ -24,8 +24,27 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
24
24
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
25
25
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
26
26
|
import { GatewayClient } from "@circle-fin/x402-batching/client";
|
|
27
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
27
28
|
const BASE = (process.env.SENTINEL_URL ?? "https://402sentinel.com").replace(/\/$/, "");
|
|
28
29
|
const RAW_PK = process.env.CLIENT_PRIVATE_KEY ?? "";
|
|
30
|
+
const PK = (!RAW_PK || RAW_PK.startsWith("0xYour"))
|
|
31
|
+
? null
|
|
32
|
+
: (RAW_PK.startsWith("0x") ? RAW_PK : `0x${RAW_PK}`);
|
|
33
|
+
const account = PK ? privateKeyToAccount(PK) : null;
|
|
34
|
+
// Tools whose writes build TRUSTED history → auto-attach a wallet-ownership proof.
|
|
35
|
+
// SAFETY: we sign ONLY this exact namespaced template (never arbitrary or server-
|
|
36
|
+
// supplied content — no blind signing). It's an EIP-191 message signature, NOT a
|
|
37
|
+
// transaction: it can't move funds, isn't replayable as a tx, and only proves the
|
|
38
|
+
// signer controls its OWN agent_id. Scoped, harmless.
|
|
39
|
+
const SIGN_TOOLS = new Set(["firewall", "firewall_record", "firewall_outcome"]);
|
|
40
|
+
async function ownerProof(action) {
|
|
41
|
+
if (!account)
|
|
42
|
+
return {};
|
|
43
|
+
const owner_ts = Math.floor(Date.now() / 1000);
|
|
44
|
+
const agent = account.address.toLowerCase();
|
|
45
|
+
const owner_sig = await account.signMessage({ message: `402sentinel:${action}:${agent}:${owner_ts}` });
|
|
46
|
+
return { owner_sig, owner_ts };
|
|
47
|
+
}
|
|
29
48
|
const targetSchema = {
|
|
30
49
|
type: "object",
|
|
31
50
|
required: ["payto_address"],
|
|
@@ -113,10 +132,10 @@ const TOOLS = [
|
|
|
113
132
|
},
|
|
114
133
|
{
|
|
115
134
|
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 = fraudulent routing), velocity_anomaly (drain), amount_anomaly (overcharge), provenance_flag, counterparty_risk, injection_destination (if the payTo appears in the untrusted page/tool-output you're acting on, the destination was injected — pass it as context.untrusted_text), intent_mismatch (pass context.intended={payto,max_amount} so a mid-flight redirect is caught), new_counterparty_burst, recurring_flagged (poisoned-memory loop). STRONGLY recommended: pass untrusted_text + intended to catch prompt-injection payments.
|
|
135
|
+
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 = fraudulent routing), velocity_anomaly (drain), amount_anomaly (overcharge), provenance_flag, counterparty_risk, injection_destination (if the payTo appears in the untrusted page/tool-output you're acting on, the destination was injected — pass it as context.untrusted_text), intent_mismatch (pass context.intended={payto,max_amount} so a mid-flight redirect is caught), new_counterparty_burst, recurring_flagged (poisoned-memory loop). STRONGLY recommended: pass untrusted_text + intended to catch prompt-injection payments. agent_id and a wallet-ownership signature are attached AUTOMATICALLY from your configured wallet — you don't pass agent_id, and your routing history is trusted with no extra steps. Costs $0.002. Seed history free with firewall_record.",
|
|
117
136
|
inputSchema: {
|
|
118
137
|
type: "object",
|
|
119
|
-
required: ["
|
|
138
|
+
required: ["payment"],
|
|
120
139
|
properties: {
|
|
121
140
|
agent_id: { type: "string", description: "stable id for your agent — use your payer wallet address" },
|
|
122
141
|
payment: {
|
|
@@ -156,10 +175,10 @@ const TOOLS = [
|
|
|
156
175
|
},
|
|
157
176
|
{
|
|
158
177
|
name: "firewall_record",
|
|
159
|
-
description: "FREE. Seed your agent's payment history so the firewall has a behavioural baseline (record past/known-good payments).
|
|
178
|
+
description: "FREE. Seed your agent's payment history so the firewall has a behavioural baseline (record past/known-good payments). agent_id and the wallet-ownership signature are attached automatically from your configured wallet, so the seeded history is trusted.",
|
|
160
179
|
inputSchema: {
|
|
161
180
|
type: "object",
|
|
162
|
-
required: ["
|
|
181
|
+
required: ["payment"],
|
|
163
182
|
properties: {
|
|
164
183
|
agent_id: { type: "string", description: "use your payer wallet address" },
|
|
165
184
|
payment: {
|
|
@@ -177,15 +196,28 @@ const TOOLS = [
|
|
|
177
196
|
endpoint: "/api/firewall/record",
|
|
178
197
|
paid: false,
|
|
179
198
|
},
|
|
199
|
+
{
|
|
200
|
+
name: "firewall_outcome",
|
|
201
|
+
description: "FREE. After a firewall verdict, report what actually happened so 402Sentinel learns which signals are predictive and downweights noisy ones (hard-block safety signals stay deterministic). Pass the assessment_id (fw_…) from a prior firewall call.",
|
|
202
|
+
inputSchema: {
|
|
203
|
+
type: "object",
|
|
204
|
+
required: ["assessment_id", "outcome"],
|
|
205
|
+
properties: {
|
|
206
|
+
assessment_id: { type: "string", description: "the fw_… id from a prior firewall call" },
|
|
207
|
+
outcome: { type: "string", enum: ["fraud", "confirmed_fraud", "not_delivered", "overcharged", "drained", "scam", "delivered", "legit", "fine"], description: "bad: fraud/confirmed_fraud/not_delivered/overcharged/drained/scam · good: delivered/legit/fine" },
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
endpoint: "/api/firewall/outcome",
|
|
211
|
+
paid: false,
|
|
212
|
+
},
|
|
180
213
|
];
|
|
181
214
|
function clientOrNull() {
|
|
182
|
-
if (!
|
|
215
|
+
if (!PK)
|
|
183
216
|
return null;
|
|
184
|
-
|
|
185
|
-
return new GatewayClient({ chain: "base", privateKey: pk });
|
|
217
|
+
return new GatewayClient({ chain: "base", privateKey: PK });
|
|
186
218
|
}
|
|
187
219
|
async function main() {
|
|
188
|
-
const server = new Server({ name: "402sentinel", version: "0.
|
|
220
|
+
const server = new Server({ name: "402sentinel", version: "0.6.0" }, { capabilities: { tools: {} } });
|
|
189
221
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
190
222
|
tools: TOOLS.map(({ name, description, inputSchema }) => ({ name, description, inputSchema })),
|
|
191
223
|
}));
|
|
@@ -196,6 +228,15 @@ async function main() {
|
|
|
196
228
|
return { content: [{ type: "text", text: `unknown tool: ${name}` }], isError: true };
|
|
197
229
|
}
|
|
198
230
|
try {
|
|
231
|
+
// Build the request body; auto-attach the ownership proof + set agent_id = own
|
|
232
|
+
// wallet for the signable tools, so the agent's history is trusted automatically.
|
|
233
|
+
let body = (args ?? {});
|
|
234
|
+
if (account && SIGN_TOOLS.has(tool.name)) {
|
|
235
|
+
body = { ...body, ...(await ownerProof(tool.name)) };
|
|
236
|
+
if (tool.name === "firewall" || tool.name === "firewall_record") {
|
|
237
|
+
body = { ...body, agent_id: account.address }; // the wallet IS the agent identity
|
|
238
|
+
}
|
|
239
|
+
}
|
|
199
240
|
let data;
|
|
200
241
|
if (tool.paid) {
|
|
201
242
|
const client = clientOrNull();
|
|
@@ -210,14 +251,14 @@ async function main() {
|
|
|
210
251
|
isError: true,
|
|
211
252
|
};
|
|
212
253
|
}
|
|
213
|
-
({ data } = await client.pay(`${BASE}${tool.endpoint}`, { method: "POST", body
|
|
254
|
+
({ data } = await client.pay(`${BASE}${tool.endpoint}`, { method: "POST", body }));
|
|
214
255
|
}
|
|
215
256
|
else {
|
|
216
257
|
// free endpoint — plain POST, no payment
|
|
217
258
|
const res = await fetch(`${BASE}${tool.endpoint}`, {
|
|
218
259
|
method: "POST",
|
|
219
260
|
headers: { "Content-Type": "application/json" },
|
|
220
|
-
body: JSON.stringify(
|
|
261
|
+
body: JSON.stringify(body),
|
|
221
262
|
});
|
|
222
263
|
data = await res.json().catch(() => ({}));
|
|
223
264
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaditang/402sentinel-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
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" },
|