chain-insights 0.2.18 → 0.2.21

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 (66) hide show
  1. package/README.md +54 -12
  2. package/bin/cli.js +2 -3
  3. package/bin/install.cjs +0 -1
  4. package/dist/{app-DxlQE_P5.cjs → app-BxojXjtB.cjs} +1 -1
  5. package/dist/{app-DdWQF_zb.mjs → app-CRd39JJ8.mjs} +2 -2
  6. package/dist/{app-DdWQF_zb.mjs.map → app-CRd39JJ8.mjs.map} +1 -1
  7. package/dist/{artifact-server-4DiMvwhC.mjs → artifact-server-CP6LXQ9d.mjs} +2 -2
  8. package/dist/{artifact-server-4DiMvwhC.mjs.map → artifact-server-CP6LXQ9d.mjs.map} +1 -1
  9. package/dist/{artifact-server-B-3ho4bk.cjs → artifact-server-XbN16DwU.cjs} +1 -1
  10. package/dist/cli.cjs +66 -25
  11. package/dist/cli.mjs +66 -25
  12. package/dist/cli.mjs.map +1 -1
  13. package/dist/{config-BhYbhLDI.cjs → config-BwVx19Og.cjs} +48 -15
  14. package/dist/config-Drgc2HuF.mjs +77 -0
  15. package/dist/config-Drgc2HuF.mjs.map +1 -0
  16. package/dist/frontmatter-D0ccQnUM.mjs.map +1 -1
  17. package/dist/index.cjs +4 -4
  18. package/dist/index.d.cts +3 -3
  19. package/dist/index.d.cts.map +1 -1
  20. package/dist/index.d.mts +3 -3
  21. package/dist/index.d.mts.map +1 -1
  22. package/dist/index.mjs +4 -4
  23. package/dist/{init-CZbZegIW.mjs → init-4tn7jfhN.mjs} +3 -2
  24. package/dist/init-4tn7jfhN.mjs.map +1 -0
  25. package/dist/{init-BvpZtFiT.cjs → init-TCQY5RDJ.cjs} +2 -1
  26. package/dist/mcp-endpoint-BaV8h_lq.cjs +60 -0
  27. package/dist/mcp-endpoint-DHs1cRFH.mjs +39 -0
  28. package/dist/mcp-endpoint-DHs1cRFH.mjs.map +1 -0
  29. package/dist/mcp-proxy.cjs +108 -9
  30. package/dist/mcp-proxy.d.cts.map +1 -1
  31. package/dist/mcp-proxy.d.mts.map +1 -1
  32. package/dist/mcp-proxy.mjs +108 -9
  33. package/dist/mcp-proxy.mjs.map +1 -1
  34. package/dist/{public-tools-D6Q5MTcO.mjs → public-tools-B13J0MJZ.mjs} +465 -70
  35. package/dist/public-tools-B13J0MJZ.mjs.map +1 -0
  36. package/dist/{public-tools-V7ON7goq.cjs → public-tools-BC1fi0DV.cjs} +464 -68
  37. package/dist/resolver-D7VBb0uB.mjs.map +1 -1
  38. package/dist/{runner-BatyCxv7.mjs → runner-DIs04IhN.mjs} +2 -2
  39. package/dist/{runner-BatyCxv7.mjs.map → runner-DIs04IhN.mjs.map} +1 -1
  40. package/dist/{runner-CCA7SJ7X.cjs → runner-ZYowxCVl.cjs} +1 -1
  41. package/dist/schema-BFEWhzg7.mjs +60 -0
  42. package/dist/schema-BFEWhzg7.mjs.map +1 -0
  43. package/dist/{schema-DN-KLkYN.cjs → schema-Vl9yuOFO.cjs} +31 -8
  44. package/dist/{server-BDlbmGbL.mjs → server-BXLX2j_A.mjs} +2 -2
  45. package/dist/{server-BDlbmGbL.mjs.map → server-BXLX2j_A.mjs.map} +1 -1
  46. package/dist/{server-C3y1gQmZ.cjs → server-BqVdWath.cjs} +1 -1
  47. package/dist/{topup-server-6MH7q73X.mjs → topup-server-BJgVw6Jt.mjs} +100 -42
  48. package/dist/topup-server-BJgVw6Jt.mjs.map +1 -0
  49. package/dist/{topup-server-DjUjhNjv.cjs → topup-server-yAaXYkJP.cjs} +98 -40
  50. package/docs/architecture.md +4 -0
  51. package/docs/contributing.md +1 -0
  52. package/docs/debugging.md +10 -14
  53. package/docs/graph-tools.md +60 -2
  54. package/docs/mcp-proxy.md +44 -0
  55. package/package.json +2 -2
  56. package/skills/chain-insights-developer-experience/SKILL.md +4 -2
  57. package/skills/chain-insights-investigation/SKILL.md +1 -1
  58. package/skills/test-chain-insights-graphrag-mcp/SKILL.md +4 -5
  59. package/skills/test-chain-insights-graphrag-mcp/scripts/run-uat.sh +5 -24
  60. package/dist/config-9KYXaAv-.mjs +0 -44
  61. package/dist/config-9KYXaAv-.mjs.map +0 -1
  62. package/dist/init-CZbZegIW.mjs.map +0 -1
  63. package/dist/public-tools-D6Q5MTcO.mjs.map +0 -1
  64. package/dist/schema-BbQVXp36.mjs +0 -37
  65. package/dist/schema-BbQVXp36.mjs.map +0 -1
  66. package/dist/topup-server-6MH7q73X.mjs.map +0 -1
@@ -29,6 +29,7 @@ const GRAPH_RESOURCE_URI = "ui://chain-insights/graph";
29
29
  const GRAPH_APP_TOOL_NAMES = new Set([
30
30
  "address_risk",
31
31
  "scam_topology",
32
+ "stake_insights",
32
33
  "track_funds"
33
34
  ]);
34
35
  const GRAPH_ARRAY_KEYS = [
@@ -46,6 +47,7 @@ const KNOWN_PUBLIC_TOOL_REQUIRED_ARGS = {
46
47
  "incident_timestamp_ms",
47
48
  "network"
48
49
  ],
50
+ stake_insights: ["network"],
49
51
  track_funds: ["trusted_addresses", "network"],
50
52
  graph_query: ["query", "network"],
51
53
  graph_query_batch: ["network", "queries"]
@@ -54,6 +56,7 @@ const KNOWN_PUBLIC_TOOL_DESCRIPTIONS = {
54
56
  network_capabilities: "Return supported Chain Insights networks, capability layers, tool availability, data retention windows, and freshness. Use this before choosing network-specific tools.",
55
57
  address_risk: "Screen one full blockchain address for AML risk, behavior patterns, neighborhood context, exchange exposure, and optional comparison with compare_address. This includes the exchange-behavior analysis formerly covered by money_flows_between_exchanges. Use this as the first tool for a single-address investigation. The tool returns an investigator-ready summary; preserve full addresses exactly.",
56
58
  scam_topology: "Build victim-incident laundering topology from one victim/source address and the earliest known incident timestamp. Traversal uses one explicit activity policy: node_relative_only by default, or global_incident_only when requested. Repeated targets are kept as non-expanding convergence edges. Returns ML-ready scam_labels plus review context and a track_funds-compatible graph report: primary flows, deposits, reverse_leads. Victims, exchange endpoints, and generic labeled context nodes are not automatic scam labels; preserve full addresses exactly.",
59
+ stake_insights: "Explain Bittensor staking behavior around one full address, coldkey, or hotkey. Requires network plus exactly one of address, coldkey, or hotkey. Returns net staked/unstaked amounts, active coldkey-hotkey-netuid relationships, aggregate stake movement amounts, top counterparties, first/last activity, source backend, query evidence, and optional graph report metadata.",
57
60
  track_funds: "Trace funds from trusted victim/source addresses through intermediaries to exchange deposit addresses. Use this when the user has a victim/source address or known untrusted/scammer addresses. The tool returns an investigator-ready fund-flow report and recommended next actions.",
58
61
  graph_query: "Run a read-only GQL/Cypher query through the Chain Insights graph endpoint. Use USE live_topology for recent topology, USE archive_topology for historical topology, and USE facts for labels, features, risk scores, assets, and enrichment. Cross-layer correlated joins may be limited by the active graph endpoint; preserve full addresses exactly.",
59
62
  graph_query_batch: "Run multiple read-only GQL/Cypher queries through the Chain Insights graph endpoint in one paid batch. Prefer this for related topology/facts reads."
@@ -64,7 +67,7 @@ const CHAIN_INSIGHTS_WORKFLOW = [
64
67
  "Workflow:",
65
68
  "1. If the user is starting or continuing an investigation, use case_open or case_list/case_resume first.",
66
69
  "2. Do not call investigation tools until required arguments are known. Network is required; use network_capabilities to check supported networks, data layers, retention, and freshness, or ask the user if missing.",
67
- "3. Use address_risk first for a single address when facts and topology are available. Use track_funds for victim/source fund tracing when topology is available. Use scam_topology when known victim incident ground truth should become ML-ready scam labels. Use graph_query(_batch) for the universal graph-language path over topology and facts.",
70
+ "3. Use address_risk first for a single address when facts and topology are available. Use stake_insights for Bittensor coldkey/hotkey staking behavior. Use track_funds for victim/source fund tracing when topology is available. Use scam_topology when known victim incident ground truth should become ML-ready scam labels. Use graph_query(_batch) for the universal graph-language path over topology and facts.",
68
71
  "4. After a material result, preserve it with case_add_evidence when a case is active or ask whether to create/select a case.",
69
72
  "5. Use case_update_dossier for durable address/entity findings and case_start_session/case_end_session for session notes."
70
73
  ].join("\n");
@@ -154,6 +157,19 @@ function knownPublicToolInputSchema(toolName) {
154
157
  activity_policy: z.enum(["node_relative_only", "global_incident_only"]).optional().describe("Traversal activity policy. Default node_relative_only."),
155
158
  case_id: z.string().optional().describe("Optional Chain Insights case ID. When provided, compact evidence is appended to the case manifest.")
156
159
  };
160
+ case "stake_insights": return {
161
+ network: z.string().min(1).describe(NETWORK_DESCRIPTION),
162
+ address: z.string().optional().describe("Full Bittensor address to inspect as either coldkey or hotkey. Provide exactly one of address, coldkey, or hotkey."),
163
+ coldkey: z.string().optional().describe("Full Bittensor coldkey address to inspect. Provide exactly one of address, coldkey, or hotkey."),
164
+ hotkey: z.string().optional().describe("Full Bittensor hotkey address to inspect. Provide exactly one of address, coldkey, or hotkey."),
165
+ netuid: z.number().int().min(0).optional().describe("Optional subnet netuid filter."),
166
+ start_timestamp_ms: z.number().min(0).optional().describe("Optional inclusive lower activity timestamp bound in milliseconds."),
167
+ end_timestamp_ms: z.number().min(0).optional().describe("Optional inclusive upper activity timestamp bound in milliseconds."),
168
+ start_block: z.number().int().min(0).optional().describe("Optional start block. Current stake graph parity may require timestamp windows instead."),
169
+ end_block: z.number().int().min(0).optional().describe("Optional end block. Current stake graph parity may require timestamp windows instead."),
170
+ depth: z.number().int().min(1).max(3).optional().describe("Optional expansion depth limit. First release returns direct STAKES_IN relationships; default 1, max 3."),
171
+ include_attachments: z.boolean().optional().describe("Include graph app report metadata")
172
+ };
157
173
  case "graph_query": return {
158
174
  query: z.string().min(1).describe("Read-only GQL/Cypher query. Use USE live_topology for recent topology, USE archive_topology for historical topology, and USE facts for labels, features, risk scores, assets, and enrichment."),
159
175
  network: z.string().min(1).describe(NETWORK_DESCRIPTION)
@@ -527,7 +543,7 @@ async function normalizeRemoteToolResult(result, config, toolName = "remote-grap
527
543
  const meta = { ...result._meta ?? {} };
528
544
  if (graphPayload) {
529
545
  const { writeGraphReport } = await import("./graph-reports-BDELxmpi.mjs");
530
- const { ensureArtifactServer } = await import("./artifact-server-4DiMvwhC.mjs");
546
+ const { ensureArtifactServer } = await import("./artifact-server-CP6LXQ9d.mjs");
531
547
  const report = await writeGraphReport(graphPayload, {
532
548
  serverPort: config.serverPort,
533
549
  slug: toolName || "remote-graph"
@@ -556,7 +572,7 @@ async function normalizeRemoteToolResult(result, config, toolName = "remote-grap
556
572
  * All diagnostic output goes to console.error() or process.stderr.write().
557
573
  */
558
574
  async function createProxy() {
559
- const { loadConfig } = await import("./config-9KYXaAv-.mjs").then((n) => n.t);
575
+ const { loadConfig } = await import("./config-Drgc2HuF.mjs").then((n) => n.t);
560
576
  const { activeDataDir, findActiveWorkspace } = await import("./active-ByNgjuAg.mjs").then((n) => n.n);
561
577
  const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await import("./client-D4_hd4AP.mjs").then((n) => n.n);
562
578
  const { loadSchema, saveSchema } = await import("./schema-cache-DwDvPy4e.mjs");
@@ -975,9 +991,9 @@ async function createProxy() {
975
991
  }],
976
992
  isError: true
977
993
  };
978
- const { addressRisk } = await import("./public-tools-D6Q5MTcO.mjs");
994
+ const { addressRisk } = await import("./public-tools-B13J0MJZ.mjs");
979
995
  const { writeGraphReport } = await import("./graph-reports-BDELxmpi.mjs");
980
- const { ensureArtifactServer } = await import("./artifact-server-4DiMvwhC.mjs");
996
+ const { ensureArtifactServer } = await import("./artifact-server-CP6LXQ9d.mjs");
981
997
  const result = await addressRisk(remoteClient, {
982
998
  address,
983
999
  network,
@@ -1046,9 +1062,9 @@ async function createProxy() {
1046
1062
  }],
1047
1063
  isError: true
1048
1064
  };
1049
- const { trackFunds } = await import("./public-tools-D6Q5MTcO.mjs");
1065
+ const { trackFunds } = await import("./public-tools-B13J0MJZ.mjs");
1050
1066
  const { writeGraphReport } = await import("./graph-reports-BDELxmpi.mjs");
1051
- const { ensureArtifactServer } = await import("./artifact-server-4DiMvwhC.mjs");
1067
+ const { ensureArtifactServer } = await import("./artifact-server-CP6LXQ9d.mjs");
1052
1068
  const result = await trackFunds(remoteClient, config, {
1053
1069
  trustedAddresses: trusted_addresses,
1054
1070
  untrustedAddresses: untrusted_addresses,
@@ -1119,9 +1135,9 @@ async function createProxy() {
1119
1135
  }],
1120
1136
  isError: true
1121
1137
  };
1122
- const { scamTopology } = await import("./public-tools-D6Q5MTcO.mjs");
1138
+ const { scamTopology } = await import("./public-tools-B13J0MJZ.mjs");
1123
1139
  const { writeGraphReport } = await import("./graph-reports-BDELxmpi.mjs");
1124
- const { ensureArtifactServer } = await import("./artifact-server-4DiMvwhC.mjs");
1140
+ const { ensureArtifactServer } = await import("./artifact-server-CP6LXQ9d.mjs");
1125
1141
  const result = await scamTopology(remoteClient, config, {
1126
1142
  victimAddress: victim_address,
1127
1143
  network,
@@ -1164,6 +1180,88 @@ async function createProxy() {
1164
1180
  };
1165
1181
  }
1166
1182
  });
1183
+ if (!remoteToolNames.has("stake_insights")) registerAppTool(server, "stake_insights", {
1184
+ title: "Stake Insights",
1185
+ description: KNOWN_PUBLIC_TOOL_DESCRIPTIONS.stake_insights,
1186
+ inputSchema: {
1187
+ network: z.string().min(1).describe(NETWORK_DESCRIPTION),
1188
+ address: z.string().optional().describe("Full Bittensor address to inspect as either coldkey or hotkey. Provide exactly one of address, coldkey, or hotkey."),
1189
+ coldkey: z.string().optional().describe("Full Bittensor coldkey address to inspect. Provide exactly one of address, coldkey, or hotkey."),
1190
+ hotkey: z.string().optional().describe("Full Bittensor hotkey address to inspect. Provide exactly one of address, coldkey, or hotkey."),
1191
+ netuid: z.number().int().min(0).optional().describe("Optional subnet netuid filter."),
1192
+ start_timestamp_ms: z.number().min(0).optional().describe("Optional inclusive lower activity timestamp bound in milliseconds."),
1193
+ end_timestamp_ms: z.number().min(0).optional().describe("Optional inclusive upper activity timestamp bound in milliseconds."),
1194
+ start_block: z.number().int().min(0).optional().describe("Optional start block. Current stake graph parity may require timestamp windows instead."),
1195
+ end_block: z.number().int().min(0).optional().describe("Optional end block. Current stake graph parity may require timestamp windows instead."),
1196
+ depth: z.number().int().min(1).max(3).optional().describe("Optional expansion depth limit. First release returns direct STAKES_IN relationships; default 1, max 3."),
1197
+ include_attachments: z.boolean().optional().describe("Include graph app report metadata")
1198
+ },
1199
+ _meta: { ui: { resourceUri: GRAPH_RESOURCE_URI } },
1200
+ annotations: {
1201
+ readOnlyHint: true,
1202
+ destructiveHint: false,
1203
+ idempotentHint: true,
1204
+ openWorldHint: true
1205
+ }
1206
+ }, async ({ network, address, coldkey, hotkey, netuid, start_timestamp_ms, end_timestamp_ms, start_block, end_block, depth }) => {
1207
+ try {
1208
+ if (!remoteConnected) return {
1209
+ content: [{
1210
+ type: "text",
1211
+ text: `${remoteUnavailableMessage ?? `Graph MCP is not connected at ${graphMcpEndpoint}`}. Restart the Chain Insights MCP proxy after the endpoint is reachable.`
1212
+ }],
1213
+ isError: true
1214
+ };
1215
+ const { stakeInsights } = await import("./public-tools-B13J0MJZ.mjs");
1216
+ const { writeGraphReport } = await import("./graph-reports-BDELxmpi.mjs");
1217
+ const { ensureArtifactServer } = await import("./artifact-server-CP6LXQ9d.mjs");
1218
+ const result = await stakeInsights(remoteClient, {
1219
+ network,
1220
+ address,
1221
+ coldkey,
1222
+ hotkey,
1223
+ netuid,
1224
+ startTimestampMs: start_timestamp_ms,
1225
+ endTimestampMs: end_timestamp_ms,
1226
+ startBlock: start_block,
1227
+ endBlock: end_block,
1228
+ depth
1229
+ });
1230
+ const subject = address ?? coldkey ?? hotkey ?? "subject";
1231
+ const report = await writeGraphReport(result.graphData, {
1232
+ serverPort: config.serverPort,
1233
+ slug: `stake-insights-${network}-${subject}`
1234
+ });
1235
+ await ensureArtifactServer(config.serverPort);
1236
+ return {
1237
+ content: [{
1238
+ type: "text",
1239
+ text: result.summaryText
1240
+ }],
1241
+ structuredContent: result.structuredContent,
1242
+ _meta: { chainInsights: { graph: {
1243
+ schema: report.schema,
1244
+ url: report.url
1245
+ } } },
1246
+ isError: false
1247
+ };
1248
+ } catch (err) {
1249
+ if (err instanceof PaymentRequiredError) return {
1250
+ content: [{
1251
+ type: "text",
1252
+ text: err.message
1253
+ }],
1254
+ isError: true
1255
+ };
1256
+ return {
1257
+ content: [{
1258
+ type: "text",
1259
+ text: `Stake insights failed: ${err.message}`
1260
+ }],
1261
+ isError: true
1262
+ };
1263
+ }
1264
+ });
1167
1265
  server.registerTool("help", {
1168
1266
  description: "Show Chain Insights overview, available tools, and investigation workflow.",
1169
1267
  inputSchema: z.object({}).passthrough()
@@ -1178,6 +1276,7 @@ async function createProxy() {
1178
1276
  "Investigation tools:",
1179
1277
  "- network_capabilities: inspect supported networks, data layers, tool availability, retention windows, and freshness.",
1180
1278
  "- address_risk: screen a full address for AML risk, behavior, neighborhood, exchange exposure, and optional compare_address connection checks.",
1279
+ "- stake_insights: explain Bittensor staking around one address, coldkey, or hotkey with net stake, movement amounts, counterparties, backend, and query evidence.",
1181
1280
  "- track_funds: trace up to five trusted/victim addresses plus up to five known untrusted/scammer addresses through intermediaries to exchange deposit addresses.",
1182
1281
  "- scam_topology: derive ML-ready scam_labels from one victim incident address and incident_timestamp_ms.",
1183
1282
  "- graph_query: run read-only GQL/Cypher through the universal graph endpoint. Use USE live_topology, USE archive_topology, or USE facts.",