chainhint-mcp 1.2.0 → 1.2.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.
Files changed (2) hide show
  1. package/dist/index.js +10 -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 };
@@ -281,7 +281,7 @@ server.tool("check_wallet_risk", "Fast risk check for a crypto wallet address ag
281
281
  // ── Tool 2: lookup_address ────────────────────────────────────────────────────
282
282
  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
283
  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)"),
284
+ chain: z.string().optional().describe("Blockchain (ethereum, bsc, polygon, arbitrum, optimism, base, avalanche, gnosis, bitcoin, solana, tron, ton)"),
285
285
  }, async ({ address, chain }) => {
286
286
  try {
287
287
  const params = { address };
@@ -416,6 +416,11 @@ server.tool("get_trace_status", "Fund-trace summary for a publicly tracked crypt
416
416
  lines.push(`**Hops traced:** ${hops} · **Addresses:** ${nodes.length} · **Transfers:** ${edges.length}`);
417
417
  }
418
418
  const endpoints = inc.endpoints ?? [];
419
+ // A graph none of whose edges carries amount_usd was never valued (legacy
420
+ // Bitcoin rows): endpoint.amount_usd then holds the raw asset quantity and
421
+ // must not be printed as dollars (multi-chain audit 2026-09-03).
422
+ const graphPriced = edges.length === 0 || edges.some((e) => e.amount_usd != null && Number(e.amount_usd) > 0);
423
+ const money = (n) => (graphPriced ? usd(n) : "unpriced");
419
424
  if (endpoints.length) {
420
425
  const byType = new Map();
421
426
  for (const e of endpoints) {
@@ -430,11 +435,11 @@ server.tool("get_trace_status", "Fund-trace summary for a publicly tracked crypt
430
435
  }
431
436
  const totalUsd = [...byType.values()].reduce((s, b) => s + b.usd, 0);
432
437
  // Σ 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)`);
438
+ 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
439
  for (const [t, b] of [...byType.entries()].sort((a, b) => b[1].usd - a[1].usd)) {
435
440
  const top = [...b.entities.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([n]) => n);
436
441
  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(", ")}` : ""}`);
442
+ lines.push(`- **${t}**: ${money(b.usd)}${graphPriced ? pct : ""} across ${b.n} address(es)${top.length ? ` — ${top.join(", ")}` : ""}`);
438
443
  }
439
444
  const topEndpoints = [...endpoints]
440
445
  .filter((e) => (e.amount_usd ?? 0) > 0)
@@ -444,7 +449,7 @@ server.tool("get_trace_status", "Fund-trace summary for a publicly tracked crypt
444
449
  lines.push(`**Largest endpoints:**`);
445
450
  for (const e of topEndpoints) {
446
451
  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)}`);
452
+ lines.push(`- ${truncateAddr(e.address)} — ${name} (${endpointBucket(e.entity_category, e.type, e.entity_name ?? e.entity)}): ${money(e.amount_usd)}`);
448
453
  }
449
454
  }
450
455
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chainhint-mcp",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "ChainHint MCP server — crypto risk intelligence tools for Claude Desktop and Cursor",
5
5
  "license": "MIT",
6
6
  "repository": {