chainhint-mcp 1.2.0 → 1.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/dist/index.js +20 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -218,7 +218,7 @@ const server = new McpServer({
|
|
|
218
218
|
// ── Tool 1: check_wallet_risk ─────────────────────────────────────────────────
|
|
219
219
|
server.tool("check_wallet_risk", "Fast risk check for a crypto wallet address against ChainHint's 54M+ labeled address database (12 chains). Returns risk score 0-100, risk level (clean/low/medium/high/critical/sanctioned), entity name and category, labels, and sanctions hit. Use it to decide allow/warn/block before paying or interacting with a counterparty wallet. Free: 3 checks per day without a key; CHAINHINT_API_KEY (Agency plan) lifts it to 10,000/day. For a deeper report (risk factors, exposure, balance) use lookup_address.", {
|
|
220
220
|
address: z.string().describe("Wallet address to check (EVM 0x..., Bitcoin, or Solana)"),
|
|
221
|
-
chain: z.string().optional().describe("Blockchain: ethereum, bsc, polygon, arbitrum, optimism, base, avalanche,
|
|
221
|
+
chain: z.string().optional().describe("Blockchain: ethereum, bsc, polygon, arbitrum, optimism, base, avalanche, gnosis, bitcoin, solana, tron, ton (default: auto-detect from address format; the response chain is the address family for bitcoin/solana/tron/ton)"),
|
|
222
222
|
}, async ({ address, chain }) => {
|
|
223
223
|
try {
|
|
224
224
|
const params = { address };
|
|
@@ -264,6 +264,12 @@ server.tool("check_wallet_risk", "Fast risk check for a crypto wallet address ag
|
|
|
264
264
|
}
|
|
265
265
|
if (d.labels?.length)
|
|
266
266
|
lines.push(`**Labels:** ${[...new Set(d.labels)].join(", ")}`);
|
|
267
|
+
if (d.agent) {
|
|
268
|
+
// Registry fact (agent launchpad / factory / facilitator / wallet), not
|
|
269
|
+
// a behavioural classification — mirrors the API's honest wording.
|
|
270
|
+
const cap = d.agent.summary.charAt(0).toUpperCase() + d.agent.summary.slice(1);
|
|
271
|
+
lines.push(`🤖 **${cap}** — a registry fact, not a behavioural classification.`);
|
|
272
|
+
}
|
|
267
273
|
if (d.is_contract != null)
|
|
268
274
|
lines.push(`**Type:** ${d.is_contract ? "Smart Contract" : "EOA (wallet)"}`);
|
|
269
275
|
lines.push(`**In ChainHint DB:** ${d.found_in_db ? "yes" : "no"}${d.sources?.length ? ` (sources: ${d.sources.join(", ")})` : ""}`);
|
|
@@ -281,7 +287,7 @@ server.tool("check_wallet_risk", "Fast risk check for a crypto wallet address ag
|
|
|
281
287
|
// ── Tool 2: lookup_address ────────────────────────────────────────────────────
|
|
282
288
|
server.tool("lookup_address", "Detailed lookup of a blockchain address: entity attribution, risk score with the factors behind it, sanctions and GoPlus security flags, counterparty exposure (where funds came from / went to, by category with named entities), balance, token count and transaction count. Free: 10 lookups per day without a key; CHAINHINT_API_KEY lifts it. Supports EVM chains, Bitcoin, Solana, TRON, TON.", {
|
|
283
289
|
address: z.string().describe("Blockchain address to look up"),
|
|
284
|
-
chain: z.string().optional().describe("Blockchain (ethereum, bsc, polygon, arbitrum, optimism, base, avalanche,
|
|
290
|
+
chain: z.string().optional().describe("Blockchain (ethereum, bsc, polygon, arbitrum, optimism, base, avalanche, gnosis, bitcoin, solana, tron, ton)"),
|
|
285
291
|
}, async ({ address, chain }) => {
|
|
286
292
|
try {
|
|
287
293
|
const params = { address };
|
|
@@ -306,6 +312,10 @@ server.tool("lookup_address", "Detailed lookup of a blockchain address: entity a
|
|
|
306
312
|
else {
|
|
307
313
|
lines.push(`**Entity:** Unknown / unlabeled`);
|
|
308
314
|
}
|
|
315
|
+
if (d.agent) {
|
|
316
|
+
const cap = d.agent.summary.charAt(0).toUpperCase() + d.agent.summary.slice(1);
|
|
317
|
+
lines.push(`🤖 **${cap}** — a registry fact, not a behavioural classification.`);
|
|
318
|
+
}
|
|
309
319
|
if (d.labels?.length)
|
|
310
320
|
lines.push(`**Labels:** ${[...new Set(d.labels)].join(", ")}`);
|
|
311
321
|
if (d.risk) {
|
|
@@ -416,6 +426,11 @@ server.tool("get_trace_status", "Fund-trace summary for a publicly tracked crypt
|
|
|
416
426
|
lines.push(`**Hops traced:** ${hops} · **Addresses:** ${nodes.length} · **Transfers:** ${edges.length}`);
|
|
417
427
|
}
|
|
418
428
|
const endpoints = inc.endpoints ?? [];
|
|
429
|
+
// A graph none of whose edges carries amount_usd was never valued (legacy
|
|
430
|
+
// Bitcoin rows): endpoint.amount_usd then holds the raw asset quantity and
|
|
431
|
+
// must not be printed as dollars (multi-chain audit 2026-09-03).
|
|
432
|
+
const graphPriced = edges.length === 0 || edges.some((e) => e.amount_usd != null && Number(e.amount_usd) > 0);
|
|
433
|
+
const money = (n) => (graphPriced ? usd(n) : "unpriced");
|
|
419
434
|
if (endpoints.length) {
|
|
420
435
|
const byType = new Map();
|
|
421
436
|
for (const e of endpoints) {
|
|
@@ -430,11 +445,11 @@ server.tool("get_trace_status", "Fund-trace summary for a publicly tracked crypt
|
|
|
430
445
|
}
|
|
431
446
|
const totalUsd = [...byType.values()].reduce((s, b) => s + b.usd, 0);
|
|
432
447
|
// Σ endpoint amounts counts every hop's inflow (multi-hop), so it is larger than the loss — name the base.
|
|
433
|
-
lines.push(``, `### Where the funds went (${endpoints.length} endpoints, ${usd(totalUsd)} observed at endpoints — not the loss figure)`);
|
|
448
|
+
lines.push(``, `### Where the funds went (${endpoints.length} endpoints, ${graphPriced ? `${usd(totalUsd)} observed at endpoints — not the loss figure` : "this trace carries no USD valuation"})`);
|
|
434
449
|
for (const [t, b] of [...byType.entries()].sort((a, b) => b[1].usd - a[1].usd)) {
|
|
435
450
|
const top = [...b.entities.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([n]) => n);
|
|
436
451
|
const pct = totalUsd > 0 ? ` (${((b.usd / totalUsd) * 100).toFixed(1)}%)` : "";
|
|
437
|
-
lines.push(`- **${t}**: ${
|
|
452
|
+
lines.push(`- **${t}**: ${money(b.usd)}${graphPriced ? pct : ""} across ${b.n} address(es)${top.length ? ` — ${top.join(", ")}` : ""}`);
|
|
438
453
|
}
|
|
439
454
|
const topEndpoints = [...endpoints]
|
|
440
455
|
.filter((e) => (e.amount_usd ?? 0) > 0)
|
|
@@ -444,7 +459,7 @@ server.tool("get_trace_status", "Fund-trace summary for a publicly tracked crypt
|
|
|
444
459
|
lines.push(`**Largest endpoints:**`);
|
|
445
460
|
for (const e of topEndpoints) {
|
|
446
461
|
const name = e.entity_name ?? e.entity ?? "unattributed";
|
|
447
|
-
lines.push(`- ${truncateAddr(e.address)} — ${name} (${endpointBucket(e.entity_category, e.type, e.entity_name ?? e.entity)}): ${
|
|
462
|
+
lines.push(`- ${truncateAddr(e.address)} — ${name} (${endpointBucket(e.entity_category, e.type, e.entity_name ?? e.entity)}): ${money(e.amount_usd)}`);
|
|
448
463
|
}
|
|
449
464
|
}
|
|
450
465
|
}
|