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.
- package/README.md +54 -12
- package/bin/cli.js +2 -3
- package/bin/install.cjs +0 -1
- package/dist/{app-DxlQE_P5.cjs → app-BxojXjtB.cjs} +1 -1
- package/dist/{app-DdWQF_zb.mjs → app-CRd39JJ8.mjs} +2 -2
- package/dist/{app-DdWQF_zb.mjs.map → app-CRd39JJ8.mjs.map} +1 -1
- package/dist/{artifact-server-4DiMvwhC.mjs → artifact-server-CP6LXQ9d.mjs} +2 -2
- package/dist/{artifact-server-4DiMvwhC.mjs.map → artifact-server-CP6LXQ9d.mjs.map} +1 -1
- package/dist/{artifact-server-B-3ho4bk.cjs → artifact-server-XbN16DwU.cjs} +1 -1
- package/dist/cli.cjs +66 -25
- package/dist/cli.mjs +66 -25
- package/dist/cli.mjs.map +1 -1
- package/dist/{config-BhYbhLDI.cjs → config-BwVx19Og.cjs} +48 -15
- package/dist/config-Drgc2HuF.mjs +77 -0
- package/dist/config-Drgc2HuF.mjs.map +1 -0
- package/dist/frontmatter-D0ccQnUM.mjs.map +1 -1
- package/dist/index.cjs +4 -4
- package/dist/index.d.cts +3 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +4 -4
- package/dist/{init-CZbZegIW.mjs → init-4tn7jfhN.mjs} +3 -2
- package/dist/init-4tn7jfhN.mjs.map +1 -0
- package/dist/{init-BvpZtFiT.cjs → init-TCQY5RDJ.cjs} +2 -1
- package/dist/mcp-endpoint-BaV8h_lq.cjs +60 -0
- package/dist/mcp-endpoint-DHs1cRFH.mjs +39 -0
- package/dist/mcp-endpoint-DHs1cRFH.mjs.map +1 -0
- package/dist/mcp-proxy.cjs +108 -9
- package/dist/mcp-proxy.d.cts.map +1 -1
- package/dist/mcp-proxy.d.mts.map +1 -1
- package/dist/mcp-proxy.mjs +108 -9
- package/dist/mcp-proxy.mjs.map +1 -1
- package/dist/{public-tools-D6Q5MTcO.mjs → public-tools-B13J0MJZ.mjs} +465 -70
- package/dist/public-tools-B13J0MJZ.mjs.map +1 -0
- package/dist/{public-tools-V7ON7goq.cjs → public-tools-BC1fi0DV.cjs} +464 -68
- package/dist/resolver-D7VBb0uB.mjs.map +1 -1
- package/dist/{runner-BatyCxv7.mjs → runner-DIs04IhN.mjs} +2 -2
- package/dist/{runner-BatyCxv7.mjs.map → runner-DIs04IhN.mjs.map} +1 -1
- package/dist/{runner-CCA7SJ7X.cjs → runner-ZYowxCVl.cjs} +1 -1
- package/dist/schema-BFEWhzg7.mjs +60 -0
- package/dist/schema-BFEWhzg7.mjs.map +1 -0
- package/dist/{schema-DN-KLkYN.cjs → schema-Vl9yuOFO.cjs} +31 -8
- package/dist/{server-BDlbmGbL.mjs → server-BXLX2j_A.mjs} +2 -2
- package/dist/{server-BDlbmGbL.mjs.map → server-BXLX2j_A.mjs.map} +1 -1
- package/dist/{server-C3y1gQmZ.cjs → server-BqVdWath.cjs} +1 -1
- package/dist/{topup-server-6MH7q73X.mjs → topup-server-BJgVw6Jt.mjs} +100 -42
- package/dist/topup-server-BJgVw6Jt.mjs.map +1 -0
- package/dist/{topup-server-DjUjhNjv.cjs → topup-server-yAaXYkJP.cjs} +98 -40
- package/docs/architecture.md +4 -0
- package/docs/contributing.md +1 -0
- package/docs/debugging.md +10 -14
- package/docs/graph-tools.md +60 -2
- package/docs/mcp-proxy.md +44 -0
- package/package.json +2 -2
- package/skills/chain-insights-developer-experience/SKILL.md +4 -2
- package/skills/chain-insights-investigation/SKILL.md +1 -1
- package/skills/test-chain-insights-graphrag-mcp/SKILL.md +4 -5
- package/skills/test-chain-insights-graphrag-mcp/scripts/run-uat.sh +5 -24
- package/dist/config-9KYXaAv-.mjs +0 -44
- package/dist/config-9KYXaAv-.mjs.map +0 -1
- package/dist/init-CZbZegIW.mjs.map +0 -1
- package/dist/public-tools-D6Q5MTcO.mjs.map +0 -1
- package/dist/schema-BbQVXp36.mjs +0 -37
- package/dist/schema-BbQVXp36.mjs.map +0 -1
- package/dist/topup-server-6MH7q73X.mjs.map +0 -1
package/dist/mcp-proxy.mjs
CHANGED
|
@@ -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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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.",
|