chainhint-mcp 1.3.0 â 1.3.1
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 -0
- package/dist/index.js +27 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,6 +86,8 @@ Add an Agency key to the server entry (any client):
|
|
|
86
86
|
Free tier: 2 of 3 checks left today â set CHAINHINT_API_KEY (Agency plan, https://chainhint.com/pricing) for 10,000/day.
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
+
When the address is in ChainHint's agent-infrastructure registry (agent-token launchpads, deployer factories, routers, payment facilitators, known agent wallets), the report adds an `đ¤` line, e.g. *Known agent infrastructure: Virtuals launchpad* â a registry fact you can verify at its source, not a behavioural classification and not part of the risk score.
|
|
90
|
+
|
|
89
91
|
## Development (no build step)
|
|
90
92
|
|
|
91
93
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ const API_KEY = process.env.CHAINHINT_API_KEY;
|
|
|
22
22
|
const BASE_URL = process.env.CHAINHINT_API_URL ?? "https://kjiwfwymnuzxriokhcjk.supabase.co/functions/v1";
|
|
23
23
|
const SUPABASE_URL = process.env.CHAINHINT_SUPABASE_URL ?? "https://kjiwfwymnuzxriokhcjk.supabase.co";
|
|
24
24
|
const SUPABASE_ANON_KEY = process.env.CHAINHINT_SUPABASE_ANON_KEY ?? "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImtqaXdmd3ltbnV6eHJpb2toY2prIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzI3MzkxODgsImV4cCI6MjA4ODMxNTE4OH0.VqzzF_jI8zF072cbjWEDbYo3PnMDlIPy621iWkXEqyo";
|
|
25
|
-
const VERSION = "1.1
|
|
25
|
+
const VERSION = "1.3.1";
|
|
26
26
|
const USER_AGENT = `chainhint-mcp/${VERSION}`;
|
|
27
27
|
const FREE_CHECKS_PER_DAY = 3;
|
|
28
28
|
const UPGRADE_HINT = "set CHAINHINT_API_KEY (Agency plan, https://chainhint.com/pricing) for 10,000/day";
|
|
@@ -196,11 +196,29 @@ function formatExposure(label, buckets) {
|
|
|
196
196
|
}),
|
|
197
197
|
];
|
|
198
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Same rule as chainhint.com (src/lib/attackerAttribution.ts): an address is
|
|
201
|
+
* "the attacker" only when the attribution was checked against the exploit
|
|
202
|
+
* transaction (attacker_address_source = analyst_verified). Everything else is
|
|
203
|
+
* a report â shown as one, and never as a HIGH-risk verdict.
|
|
204
|
+
*/
|
|
205
|
+
function attackerAttribution(inc) {
|
|
206
|
+
if (!inc.attacker_address)
|
|
207
|
+
return { verified: false, label: "Attacker not identified" };
|
|
208
|
+
if (inc.attacker_address_source === "analyst_verified")
|
|
209
|
+
return { verified: true, label: "Attacker" };
|
|
210
|
+
return {
|
|
211
|
+
verified: false,
|
|
212
|
+
label: inc.source === "defillama"
|
|
213
|
+
? "Reported attacker (DeFiLlama), unverified"
|
|
214
|
+
: "Reported attacker (user-provided), unverified",
|
|
215
|
+
};
|
|
216
|
+
}
|
|
199
217
|
/** Public incident whose attacker_address matches, or null (errors swallowed â best effort). */
|
|
200
218
|
async function findPublicIncidentByAttacker(address) {
|
|
201
219
|
try {
|
|
202
220
|
const rows = await supabaseGet("public_incidents_view", {
|
|
203
|
-
select: "id,title,chain,amount_usd,estimated_loss_usd,risk_score",
|
|
221
|
+
select: "id,title,chain,amount_usd,estimated_loss_usd,risk_score,source,attacker_address_source",
|
|
204
222
|
attacker_address: `eq.${normalizeAddr(address)}`,
|
|
205
223
|
limit: "1",
|
|
206
224
|
});
|
|
@@ -241,7 +259,11 @@ server.tool("check_wallet_risk", "Fast risk check for a crypto wallet address ag
|
|
|
241
259
|
lines.push(`**Risk:** âĒ NO DATA â address is not in ChainHint's labeled database. This is not evidence it is clean.`);
|
|
242
260
|
const inc = await findPublicIncidentByAttacker(d.address);
|
|
243
261
|
if (inc) {
|
|
244
|
-
|
|
262
|
+
const attribution = attackerAttribution({ attacker_address: d.address, ...inc });
|
|
263
|
+
const where = `${inc.title ?? "Unnamed incident"} (${inc.chain}, loss ${usd(inc.amount_usd ?? inc.estimated_loss_usd)}${inc.risk_score != null ? `, incident risk ${inc.risk_score}/100` : ""})`;
|
|
264
|
+
lines.push(attribution.verified
|
|
265
|
+
? `â ī¸ **Verified attacker in a public hack incident:** ${where}. The attribution was checked against the exploit transaction. Treat as HIGH risk.`
|
|
266
|
+
: `âšī¸ **${attribution.label}, in a public hack incident:** ${where}. This attribution has not been verified against the exploit transaction â treat it as a lead to check, not as a risk verdict.`, `đ https://chainhint.com/incident/${inc.id}`);
|
|
245
267
|
}
|
|
246
268
|
lines.push(`Use lookup_address for an on-chain assessment (risk factors, GoPlus flags, counterparty exposure).`);
|
|
247
269
|
}
|
|
@@ -375,7 +397,7 @@ server.tool("get_trace_status", "Fund-trace summary for a publicly tracked crypt
|
|
|
375
397
|
// public_incidents_view is the canonical anonymous read path (SECURITY DEFINER,
|
|
376
398
|
// exposes only is_public rows and only public-safe columns).
|
|
377
399
|
const queryParams = {
|
|
378
|
-
select: "id,title,status,chain,attacker_address,amount_usd,estimated_loss_usd,risk_score,incident_type,hack_date,display_date,created_at,updated_at,source,endpoints,flow_graph,counterparty_exposure",
|
|
400
|
+
select: "id,title,status,chain,attacker_address,attacker_address_source,amount_usd,estimated_loss_usd,risk_score,incident_type,hack_date,display_date,created_at,updated_at,source,endpoints,flow_graph,counterparty_exposure",
|
|
379
401
|
limit: "1",
|
|
380
402
|
order: "created_at.desc",
|
|
381
403
|
};
|
|
@@ -406,7 +428,7 @@ server.tool("get_trace_status", "Fund-trace summary for a publicly tracked crypt
|
|
|
406
428
|
`**Incident ID:** ${inc.id}`,
|
|
407
429
|
`**Chain:** ${inc.chain}`,
|
|
408
430
|
`**Status:** ${status.toUpperCase()}`,
|
|
409
|
-
|
|
431
|
+
`**${attackerAttribution(inc).label}:** ${inc.attacker_address ?? "â"}`,
|
|
410
432
|
`**Loss:** ${usd(lossUsd)}`,
|
|
411
433
|
`**Date:** ${date.slice(0, 10)}`,
|
|
412
434
|
];
|