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.
Files changed (2) hide show
  1. package/dist/index.js +20 -5
  2. 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, solana, bitcoin (default: auto-detect from address format)"),
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, solana, bitcoin, tron, ton)"),
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}**: ${usd(b.usd)}${pct} across ${b.n} address(es)${top.length ? ` — ${top.join(", ")}` : ""}`);
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)}): ${usd(e.amount_usd)}`);
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chainhint-mcp",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "ChainHint MCP server — crypto risk intelligence tools for Claude Desktop and Cursor",
5
5
  "license": "MIT",
6
6
  "repository": {