chain-insights 0.2.31 → 0.3.3

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 (67) hide show
  1. package/README.md +40 -14
  2. package/dist/cases-Cp9DUbEV.mjs +6 -0
  3. package/dist/{cases-c0iV-XLI.cjs → cases-sTY5aXav.cjs} +3 -3
  4. package/dist/cli.cjs +122 -66
  5. package/dist/cli.mjs +122 -66
  6. package/dist/cli.mjs.map +1 -1
  7. package/dist/{viz-Da9YWN_I.cjs → data-extractor-Cavd7wHk.cjs} +11 -34
  8. package/dist/{viz-DkJyqlUu.mjs → data-extractor-DZUJu1Bz.mjs} +3 -32
  9. package/dist/data-extractor-DZUJu1Bz.mjs.map +1 -0
  10. package/dist/{dossier-Br62hCG7.cjs → dossier-BXy57V4-.cjs} +13 -1
  11. package/dist/{dossier-Bl0NkJKC.mjs → dossier-Bjpcbcxa.mjs} +4 -2
  12. package/dist/{dossier-Bl0NkJKC.mjs.map → dossier-Bjpcbcxa.mjs.map} +1 -1
  13. package/dist/export-BqTCO9lP.mjs +591 -0
  14. package/dist/export-BqTCO9lP.mjs.map +1 -0
  15. package/dist/export-DsXgtCwO.cjs +592 -0
  16. package/dist/index.cjs +1 -1
  17. package/dist/index.mjs +1 -1
  18. package/dist/{init-DBC9Ml33.mjs → init-DLBL_nVG.mjs} +27 -1
  19. package/dist/{init-DBC9Ml33.mjs.map → init-DLBL_nVG.mjs.map} +1 -1
  20. package/dist/{init-CFaUWgjK.cjs → init-zqbd7i-_.cjs} +26 -0
  21. package/dist/mcp-proxy.cjs +215 -77
  22. package/dist/mcp-proxy.d.cts.map +1 -1
  23. package/dist/mcp-proxy.d.mts.map +1 -1
  24. package/dist/mcp-proxy.mjs +215 -77
  25. package/dist/mcp-proxy.mjs.map +1 -1
  26. package/dist/{public-tools-BwguvIsf.cjs → public-tools-BvMb3H2P.cjs} +701 -1479
  27. package/dist/{public-tools-DoRNhMn9.mjs → public-tools-wJoAFDFa.mjs} +700 -1479
  28. package/dist/public-tools-wJoAFDFa.mjs.map +1 -0
  29. package/dist/{resolver-D7VBb0uB.mjs → resolver-2jXNtWQO.mjs} +12 -29
  30. package/dist/resolver-2jXNtWQO.mjs.map +1 -0
  31. package/dist/{resolver-BUU7ZgW-.cjs → resolver-CZdQwKvh.cjs} +11 -28
  32. package/dist/{runner-BCDeBYsR.cjs → runner-BhZ4lnF1.cjs} +2 -2
  33. package/dist/{runner-CTFK0Qcg.mjs → runner-DIJSbkjc.mjs} +3 -3
  34. package/dist/{runner-CTFK0Qcg.mjs.map → runner-DIJSbkjc.mjs.map} +1 -1
  35. package/dist/{selector-CTUiQrzI.mjs → selector-CF2o5gxN.mjs} +2 -2
  36. package/dist/{selector-CTUiQrzI.mjs.map → selector-CF2o5gxN.mjs.map} +1 -1
  37. package/dist/{selector-DBS2jYH4.cjs → selector-DfAMZEC9.cjs} +1 -1
  38. package/dist/{session-DwyikazY.cjs → session-BT7VpbAd.cjs} +13 -1
  39. package/dist/{session-Bha3zFrx.mjs → session-DROyhebe.mjs} +4 -2
  40. package/dist/{session-Bha3zFrx.mjs.map → session-DROyhebe.mjs.map} +1 -1
  41. package/dist/{store-BT2SCcQr.mjs → store-CTtqQtaE.mjs} +10 -4
  42. package/dist/{store-BT2SCcQr.mjs.map → store-CTtqQtaE.mjs.map} +1 -1
  43. package/dist/{store-DogLawSj.cjs → store-CqPfs47P.cjs} +37 -7
  44. package/dist/{tool-visibility-BHRFLXuU.mjs → tool-visibility-BpyZHRBi.mjs} +4 -2
  45. package/dist/tool-visibility-BpyZHRBi.mjs.map +1 -0
  46. package/dist/{tool-visibility-iAVQV3t0.cjs → tool-visibility-Buq7YdUZ.cjs} +3 -1
  47. package/dist/viz-5y24S5X1.mjs +35 -0
  48. package/dist/viz-5y24S5X1.mjs.map +1 -0
  49. package/dist/viz-Dqp3C5kb.cjs +44 -0
  50. package/docs/contributing.md +3 -2
  51. package/docs/graph-tools.md +129 -118
  52. package/docs/investigation-workspaces.md +14 -0
  53. package/docs/mcp-proxy.md +20 -2
  54. package/package.json +1 -1
  55. package/skills/chain-insights-bittensor-cypher/SKILL.md +24 -0
  56. package/skills/chain-insights-cypher/SKILL.md +16 -1
  57. package/skills/chain-insights-cypher/references/memgraph-examples.md +193 -0
  58. package/skills/chain-insights-developer-experience/SKILL.md +26 -6
  59. package/skills/chain-insights-investigation/SKILL.md +64 -48
  60. package/skills/chain-insights-trace-funds/SKILL.md +80 -197
  61. package/skills/test-chain-insights-graphrag-mcp/SKILL.md +1 -1
  62. package/skills/test-chain-insights-graphrag-mcp/scripts/run-uat.sh +4 -4
  63. package/dist/cases-qjPtbnUd.mjs +0 -6
  64. package/dist/public-tools-DoRNhMn9.mjs.map +0 -1
  65. package/dist/resolver-D7VBb0uB.mjs.map +0 -1
  66. package/dist/tool-visibility-BHRFLXuU.mjs.map +0 -1
  67. package/dist/viz-DkJyqlUu.mjs.map +0 -1
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("./chunk-DakpK96I.cjs");
3
3
  const require_version = require("./version-CO9Or_YV.cjs");
4
4
  const require_client = require("./client-Db6IV1tv.cjs");
5
- const require_tool_visibility = require("./tool-visibility-iAVQV3t0.cjs");
5
+ const require_tool_visibility = require("./tool-visibility-Buq7YdUZ.cjs");
6
6
  let node_url = require("node:url");
7
7
  let node_path = require("node:path");
8
8
  node_path = require_chunk.__toESM(node_path, 1);
@@ -24,17 +24,19 @@ const LOCAL_TOOL_NAMES = new Set([
24
24
  "case_resume",
25
25
  "case_add_evidence",
26
26
  "case_verify_evidence",
27
+ "case_export",
27
28
  "case_update_dossier",
28
29
  "case_start_session",
29
30
  "case_end_session"
30
31
  ]);
31
- const PUBLIC_GRAPHRAG_PROMPT_NAMES = new Set(["address-risk", "track-funds"]);
32
+ const PUBLIC_GRAPHRAG_PROMPT_NAMES = new Set(["address-risk", "trace-tools"]);
32
33
  const GRAPH_RESOURCE_URI = "ui://chain-insights/graph";
33
34
  const GRAPH_APP_TOOL_NAMES = new Set([
34
35
  "address_risk",
35
- "scam_topology",
36
36
  "stake_insights",
37
- "track_funds"
37
+ "trace_victim_funds",
38
+ "trace_suspect_funds",
39
+ "trace_deposit_sources"
38
40
  ]);
39
41
  const GRAPH_ARRAY_KEYS = [
40
42
  "nodes",
@@ -43,25 +45,28 @@ const GRAPH_ARRAY_KEYS = [
43
45
  "edge_anchors"
44
46
  ];
45
47
  const __dirname$1 = node_path.default.dirname((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
46
- const COMMA_SEPARATED_ADDRESS_FIELDS = new Set(["trusted_addresses", "untrusted_addresses"]);
48
+ const COMMA_SEPARATED_ADDRESS_FIELDS = new Set([
49
+ "victim_addresses",
50
+ "known_suspect_addresses",
51
+ "suspect_addresses",
52
+ "deposit_addresses"
53
+ ]);
47
54
  const KNOWN_PUBLIC_TOOL_REQUIRED_ARGS = {
48
55
  address_risk: ["address", "network"],
49
- scam_topology: [
50
- "victim_address",
51
- "incident_timestamp_ms",
52
- "network"
53
- ],
54
56
  stake_insights: ["network"],
55
- track_funds: ["trusted_addresses", "network"],
57
+ trace_victim_funds: ["victim_addresses", "network"],
58
+ trace_suspect_funds: ["suspect_addresses", "network"],
59
+ trace_deposit_sources: ["deposit_addresses", "network"],
56
60
  graph_query: ["query", "network"],
57
61
  graph_query_batch: ["network", "queries"]
58
62
  };
59
63
  const KNOWN_PUBLIC_TOOL_DESCRIPTIONS = {
60
64
  network_capabilities: "Return supported Chain Insights networks, capability layers, tool availability, data retention windows, and freshness. Use this before choosing network-specific tools.",
61
65
  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.",
62
- 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.",
63
66
  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.",
64
- 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.",
67
+ trace_victim_funds: "Trace victim/source funds forward through intermediaries to exchange deposit candidates. Use only when the input addresses are victims or trusted stolen-source addresses; do not use for suspected deposit addresses because traceback belongs to trace_deposit_sources. Exchange hot wallets are terminal only, never candidate deposits. Returns chain-insights.trace.v1 and preserves full addresses exactly.",
68
+ trace_suspect_funds: "Trace suspected scammer, mule, operator, or laundering-ring funds forward to cashout topology. Use when the input addresses are suspect-controlled seeds; incident_timestamp_ms is optional. Do not use for victim/source addresses or suspected deposit endpoints. Exchange hot wallets are terminal only, never candidate suspects or intermediates. Returns chain-insights.trace.v1 and preserves full addresses exactly.",
69
+ trace_deposit_sources: "Trace backward from suspected deposit/cashout addresses to upstream sources, shared funders, and convergence. Use only when the input addresses are suspected non-exchange deposit endpoints; do not treat these seeds as scammers and do not continue forward from discovered suspects here. Exchange hot wallets are excluded as seeds and upstream sources. Returns chain-insights.trace.v1 and preserves full addresses exactly.",
65
70
  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.",
66
71
  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."
67
72
  };
@@ -76,9 +81,10 @@ const CHAIN_INSIGHTS_WORKFLOW = [
76
81
  "Workflow:",
77
82
  "1. If the user is starting or continuing an investigation, use case_open or case_list/case_resume first.",
78
83
  "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.",
79
- "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.",
84
+ "3. Use address_risk for single-address enrichment. Use trace_victim_funds for victim/source forward tracing, trace_deposit_sources for reverse traceback from suspected deposit endpoints, and trace_suspect_funds for suspect-controlled outbound laundering/cashout topology. Use stake_insights for Bittensor staking behavior. Use graph_query(_batch) only when the high-level trace tools do not answer the exact question.",
80
85
  "4. After a material result, preserve it with case_add_evidence when a case is active or ask whether to create/select a case.",
81
- "5. Use case_update_dossier for durable address/entity findings and case_start_session/case_end_session for session notes."
86
+ "5. Use case_update_dossier for durable address/entity findings and case_start_session/case_end_session for session notes.",
87
+ "6. When a case reaches a useful checkpoint, use case_verify_evidence and case_export to produce Obsidian, LLMWiki, Codex, Claude Code, and ChatGPT-ready files."
82
88
  ].join("\n");
83
89
  const GRAPH_SCHEMA_HINTS = [
84
90
  "Graph query hints for network=bittensor:",
@@ -89,6 +95,7 @@ const GRAPH_SCHEMA_HINTS = [
89
95
  "- Risk and ML properties may appear as live hints, but source-of-truth risk rows are RiskScore facts.",
90
96
  "- Common relationships include FLOWS_TO, OPERATED_FROM, SERVED_FROM, REGISTERED_NEURON, BELONGS_TO, SYBIL_CLUSTER, LAYERING_HOP, BURST_ACTIVITY, CYCLE_PARTICIPANT, SMURFING_CLUSTER.",
91
97
  "- FLOWS_TO properties are scoped to the selected topology graph and commonly carry amount_sum, amount_usd_sum, tx_count, first_seen_timestamp, last_seen_timestamp, first_tx_id, last_tx_id. Confirm available fields through runtime schema before relying on them.",
98
+ "- Traversal rule: for BFS, fixed-hop fallback, shortest-path, or manual FLOWS_TO traversal, exchange hot wallets are terminal endpoints only. Do not expand from, through, or classify exchange nodes as deposit, suspect, or intermediate candidates; filter every non-terminal node with is_exchange IS NULL.",
92
99
  "- Start schema discovery with endpoint-safe property reads: MATCH (n:Address) WHERE n.address IS NOT NULL RETURN n.labels AS labels, n.address AS address LIMIT 20",
93
100
  "- Relationship discovery: MATCH (:Address)-[r:FLOWS_TO]->(:Address) RETURN r.amount_sum AS amount_sum, r.amount_usd_sum AS amount_usd_sum LIMIT 20",
94
101
  "- graph_query uses the active 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.",
@@ -152,20 +159,27 @@ function knownPublicToolInputSchema(toolName) {
152
159
  compare_address: zod.string().optional().describe("Optional second full address for comparison"),
153
160
  include_attachments: zod.boolean().optional().describe("Include graph app report metadata")
154
161
  };
155
- case "track_funds": return {
156
- trusted_addresses: zod.string().min(1).describe("Comma-separated full trusted victim addresses. Min 1, max 5."),
162
+ case "trace_victim_funds": return {
163
+ victim_addresses: zod.string().min(1).describe("Comma-separated full victim/source addresses. Min 1, max 5."),
157
164
  network: zod.string().min(1).describe(NETWORK_DESCRIPTION),
158
- untrusted_addresses: zod.string().optional().describe("Comma-separated full untrusted/scammer addresses. Max 5."),
165
+ known_suspect_addresses: zod.string().optional().describe("Optional known suspect addresses for context only. They are not reverse-traced by this tool. Max 5."),
166
+ incident_timestamp_ms: zod.number().min(0).optional().describe("Optional incident timestamp in milliseconds."),
159
167
  include_attachments: zod.boolean().optional().describe("Include graph app report metadata")
160
168
  };
161
- case "scam_topology": return {
169
+ case "trace_suspect_funds": return {
162
170
  network: zod.string().min(1).describe(NETWORK_DESCRIPTION),
163
- victim_address: zod.string().min(1).describe("Full victim/source address that anchors the scam incident. Victims are not risky labels."),
164
- incident_timestamp_ms: zod.number().min(0).describe("Earliest known incident transfer timestamp in milliseconds. Primary traversal uses node-relative wave-arrival filtering."),
165
- max_hops: zod.number().int().min(1).max(64).optional().describe("Maximum forward expansion depth. Default 16."),
166
- activity_policy: zod.enum(["node_relative_only", "global_incident_only"]).optional().describe("Traversal activity policy. Default node_relative_only."),
171
+ suspect_addresses: zod.string().min(1).describe("Comma-separated full suspected scammer, mule, operator, or laundering-ring addresses. Min 1, max 5."),
172
+ incident_timestamp_ms: zod.number().min(0).optional().describe("Optional incident timestamp in milliseconds. This tool also works without a timestamp."),
173
+ max_hops: zod.number().int().min(1).max(5).optional().describe("Maximum forward trace hops. Default 3."),
167
174
  case_id: zod.string().optional().describe("Optional Chain Insights case ID. When provided, compact evidence is appended to the case manifest.")
168
175
  };
176
+ case "trace_deposit_sources": return {
177
+ network: zod.string().min(1).describe(NETWORK_DESCRIPTION),
178
+ deposit_addresses: zod.string().min(1).describe("Comma-separated full suspected deposit/cashout addresses. Min 1, max 5."),
179
+ max_hops: zod.number().int().min(1).max(5).optional().describe("Maximum reverse traceback hops. Default 2."),
180
+ case_id: zod.string().optional().describe("Optional Chain Insights case ID. When provided, compact evidence is appended to the case manifest."),
181
+ include_attachments: zod.boolean().optional().describe("Include graph app report metadata")
182
+ };
169
183
  case "stake_insights": return {
170
184
  network: zod.string().min(1).describe(NETWORK_DESCRIPTION),
171
185
  address: zod.string().optional().describe("Full Bittensor address to inspect as either coldkey or hotkey. Provide exactly one of address, coldkey, or hotkey."),
@@ -421,24 +435,27 @@ function registerLocalPrompts(server, remotePromptNames) {
421
435
  "",
422
436
  "Present the summary as-is. Do not add analysis, verdicts, or risk assessments; the tool output already contains the risk assessment."
423
437
  ].join("\n"), "Address risk screening"));
424
- if (!remotePromptNames.has("track-funds")) server.registerPrompt("track-funds", {
425
- title: "Track Funds",
426
- description: "Trace stolen funds from victim addresses through intermediaries to exchange deposit addresses.",
438
+ if (!remotePromptNames.has("trace-tools")) server.registerPrompt("trace-tools", {
439
+ title: "Trace Tools",
440
+ description: "Choose trace_victim_funds, trace_deposit_sources, or trace_suspect_funds based on the evidence role.",
427
441
  argsSchema: {
428
- trusted_addresses: zod.string().describe("Victim/trusted addresses, comma-separated full addresses"),
429
- untrusted_addresses: zod.string().optional().describe("Known scammer/untrusted addresses, comma-separated full addresses"),
442
+ addresses: zod.string().describe("Input addresses, comma-separated full addresses"),
443
+ role: zod.enum([
444
+ "victim",
445
+ "suspect",
446
+ "deposit"
447
+ ]).describe("Role of the supplied addresses"),
430
448
  network: zod.string().describe(NETWORK_DESCRIPTION)
431
449
  }
432
- }, async ({ trusted_addresses, untrusted_addresses, network }) => {
433
- const untrusted = untrusted_addresses?.trim() ? `\nKnown untrusted addresses:\n${untrusted_addresses}\n` : "";
450
+ }, async ({ addresses, role, network }) => {
434
451
  return promptResult([
435
- `Use Chain Insights track_funds on ${network}.`,
452
+ `Use Chain Insights ${role === "deposit" ? "trace_deposit_sources" : `trace_${role}_funds`} on ${network}.`,
453
+ "",
454
+ "Full addresses:",
455
+ addresses,
436
456
  "",
437
- "Trusted victim addresses:",
438
- trusted_addresses,
439
- untrusted,
440
- "Present the summary as-is and include recommended next actions exactly as returned."
441
- ].join("\n"), "Trace stolen funds");
457
+ role === "deposit" ? "For deposit role, use trace_deposit_sources rather than trace_deposit_funds." : "Present the summary as-is and use continuation.recommended_next_tools for follow-up."
458
+ ].join("\n"), "Trace role-specific funds");
442
459
  });
443
460
  server.registerPrompt("graph-query", {
444
461
  title: "Federated Graph Query",
@@ -758,13 +775,13 @@ async function createProxy() {
758
775
  }
759
776
  }, async ({ name, tags, description }) => {
760
777
  try {
761
- const { CaseStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
778
+ const { CaseStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
762
779
  const created = await CaseStore.create({
763
780
  name,
764
781
  tags: parseTags(tags),
765
782
  description: description ?? ""
766
783
  });
767
- const { casesRoot } = await Promise.resolve().then(() => require("./store-DogLawSj.cjs"));
784
+ const { casesRoot } = await Promise.resolve().then(() => require("./store-CqPfs47P.cjs")).then((n) => n.store_exports);
768
785
  return {
769
786
  content: [{
770
787
  type: "text",
@@ -798,7 +815,7 @@ async function createProxy() {
798
815
  }
799
816
  }, async ({ status }) => {
800
817
  try {
801
- const { CaseStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
818
+ const { CaseStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
802
819
  const cases = await CaseStore.list();
803
820
  const filtered = status ? cases.filter((entry) => entry.status === status) : cases;
804
821
  return {
@@ -823,7 +840,7 @@ async function createProxy() {
823
840
  }
824
841
  }, async ({ case_id }) => {
825
842
  try {
826
- const { CaseStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
843
+ const { CaseStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
827
844
  const context = await CaseStore.loadContext(case_id);
828
845
  return {
829
846
  content: [{
@@ -837,7 +854,7 @@ async function createProxy() {
837
854
  }
838
855
  });
839
856
  server.registerTool("case_add_evidence", {
840
- description: "Append a tool result or analyst note to a local case evidence manifest. Use after address_risk, track_funds, graph_query, or manual findings that should be preserved.",
857
+ description: "Append a tool result or analyst note to a local case evidence manifest. Use after address_risk, trace_victim_funds, trace_suspect_funds, trace_deposit_sources, graph_query, or manual findings that should be preserved.",
841
858
  inputSchema: {
842
859
  case_id: zod.string().min(1).describe("Chain Insights case ID"),
843
860
  source: zod.string().min(1).describe("Source tool or evidence origin"),
@@ -852,7 +869,7 @@ async function createProxy() {
852
869
  }
853
870
  }, async ({ case_id, source, content, query_params }) => {
854
871
  try {
855
- const { EvidenceStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
872
+ const { EvidenceStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
856
873
  const saved = await EvidenceStore.append(case_id, {
857
874
  source,
858
875
  content,
@@ -880,7 +897,7 @@ async function createProxy() {
880
897
  }
881
898
  }, async ({ case_id }) => {
882
899
  try {
883
- const { EvidenceStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
900
+ const { EvidenceStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
884
901
  const result = await EvidenceStore.verifyManifest(case_id);
885
902
  return {
886
903
  content: [{
@@ -893,6 +910,51 @@ async function createProxy() {
893
910
  return caseToolError("Evidence verify", err);
894
911
  }
895
912
  });
913
+ server.registerTool("case_export", {
914
+ description: "Export a Chain Insights case to an Obsidian, LLMWiki, Codex, Claude Code, and ChatGPT-friendly local bundle.",
915
+ inputSchema: {
916
+ case_id: zod.string().min(1).describe("Chain Insights case ID to export"),
917
+ target: zod.enum(["obsidian-llmwiki"]).optional().describe("Export target. Default obsidian-llmwiki."),
918
+ mode: zod.enum([
919
+ "private",
920
+ "partner",
921
+ "public"
922
+ ]).optional().describe("Redaction mode. Default private."),
923
+ output_dir: zod.string().optional().describe("Optional output directory. Defaults to published/<case-slug>.")
924
+ },
925
+ annotations: {
926
+ readOnlyHint: false,
927
+ destructiveHint: false,
928
+ idempotentHint: false,
929
+ openWorldHint: false
930
+ }
931
+ }, async ({ case_id, target, mode, output_dir }) => {
932
+ try {
933
+ const { exportCase } = await Promise.resolve().then(() => require("./export-DsXgtCwO.cjs"));
934
+ const result = await exportCase({
935
+ caseId: case_id,
936
+ target: target ?? "obsidian-llmwiki",
937
+ mode: mode ?? "private",
938
+ outputDir: output_dir
939
+ });
940
+ return {
941
+ content: [{
942
+ type: "text",
943
+ text: [
944
+ `Case exported: ${result.outputDir}`,
945
+ `Manifest: ${result.manifestPath}`,
946
+ `Files: ${result.fileCount}`,
947
+ `Open first: ${result.nextFile}`,
948
+ ...result.warnings.map((warning) => `Warning: ${warning}`)
949
+ ].join("\n")
950
+ }],
951
+ structuredContent: result,
952
+ isError: false
953
+ };
954
+ } catch (err) {
955
+ return caseToolError("Case export", err);
956
+ }
957
+ });
896
958
  server.registerTool("case_update_dossier", {
897
959
  description: "Append a finding to an address/entity dossier inside a local Chain Insights case.",
898
960
  inputSchema: {
@@ -915,7 +977,7 @@ async function createProxy() {
915
977
  }
916
978
  }, async ({ case_id, address, finding, entity_type }) => {
917
979
  try {
918
- const { DossierStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
980
+ const { DossierStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
919
981
  await DossierStore.appendFinding(case_id, address, finding, entity_type ?? "unknown");
920
982
  return {
921
983
  content: [{
@@ -943,7 +1005,7 @@ async function createProxy() {
943
1005
  }
944
1006
  }, async ({ case_id }) => {
945
1007
  try {
946
- const { SessionStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
1008
+ const { SessionStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
947
1009
  const session = await SessionStore.start(case_id);
948
1010
  return {
949
1011
  content: [{
@@ -971,7 +1033,7 @@ async function createProxy() {
971
1033
  }
972
1034
  }, async ({ case_id, findings, next_steps }) => {
973
1035
  try {
974
- const { SessionStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
1036
+ const { SessionStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
975
1037
  await SessionStore.end(case_id, {
976
1038
  findings: findings ?? "",
977
1039
  nextSteps: next_steps ?? ""
@@ -1016,7 +1078,7 @@ async function createProxy() {
1016
1078
  }],
1017
1079
  isError: true
1018
1080
  };
1019
- const { addressRisk } = await Promise.resolve().then(() => require("./public-tools-BwguvIsf.cjs"));
1081
+ const { addressRisk } = await Promise.resolve().then(() => require("./public-tools-BvMb3H2P.cjs"));
1020
1082
  const { writeGraphReport } = await Promise.resolve().then(() => require("./graph-reports-B3mkLP8Z.cjs"));
1021
1083
  const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-XbN16DwU.cjs"));
1022
1084
  const result = await addressRisk(remoteClient, {
@@ -1058,15 +1120,16 @@ async function createProxy() {
1058
1120
  };
1059
1121
  }
1060
1122
  });
1061
- if (!remoteToolNames.has("track_funds")) (0, _modelcontextprotocol_ext_apps_server.registerAppTool)(server, "track_funds", {
1062
- title: "Track Funds",
1063
- description: KNOWN_PUBLIC_TOOL_DESCRIPTIONS.track_funds,
1123
+ if (!remoteToolNames.has("trace_victim_funds")) (0, _modelcontextprotocol_ext_apps_server.registerAppTool)(server, "trace_victim_funds", {
1124
+ title: "Trace Victim Funds",
1125
+ description: KNOWN_PUBLIC_TOOL_DESCRIPTIONS.trace_victim_funds,
1064
1126
  inputSchema: {
1065
- trusted_addresses: zod.union([zod.string().min(1), zod.array(zod.string().min(1))]).describe("Comma-separated full trusted victim addresses, or an array. Min 1, max 5."),
1127
+ victim_addresses: zod.union([zod.string().min(1), zod.array(zod.string().min(1))]).describe("Comma-separated full victim/source addresses, or an array. Min 1, max 5."),
1066
1128
  network: zod.string().min(1).describe(NETWORK_DESCRIPTION),
1067
- untrusted_addresses: zod.union([zod.string(), zod.array(zod.string())]).optional().describe("Known scammer/untrusted addresses. Max 5."),
1129
+ known_suspect_addresses: zod.union([zod.string(), zod.array(zod.string())]).optional().describe("Known suspect addresses for context only. This tool does not reverse-trace them. Max 5."),
1068
1130
  include_attachments: zod.boolean().optional().describe("Include graph app report metadata"),
1069
1131
  case_id: zod.string().optional().describe("Optional Chain Insights case ID. When provided, compact evidence is appended to the case manifest."),
1132
+ incident_timestamp_ms: zod.number().min(0).optional().describe("Optional incident timestamp in milliseconds."),
1070
1133
  max_hops: zod.number().int().min(1).max(5).optional(),
1071
1134
  per_address_limit: zod.number().int().min(1).max(10).optional(),
1072
1135
  min_amount_sum: zod.number().min(0).optional()
@@ -1078,7 +1141,7 @@ async function createProxy() {
1078
1141
  idempotentHint: false,
1079
1142
  openWorldHint: true
1080
1143
  }
1081
- }, async ({ trusted_addresses, untrusted_addresses, network, case_id, max_hops, per_address_limit, min_amount_sum }) => {
1144
+ }, async ({ victim_addresses, known_suspect_addresses, network, case_id, incident_timestamp_ms, max_hops, per_address_limit, min_amount_sum }) => {
1082
1145
  try {
1083
1146
  if (!remoteConnected) return {
1084
1147
  content: [{
@@ -1087,21 +1150,22 @@ async function createProxy() {
1087
1150
  }],
1088
1151
  isError: true
1089
1152
  };
1090
- const { trackFunds } = await Promise.resolve().then(() => require("./public-tools-BwguvIsf.cjs"));
1153
+ const { traceVictimFunds } = await Promise.resolve().then(() => require("./public-tools-BvMb3H2P.cjs"));
1091
1154
  const { writeGraphReport } = await Promise.resolve().then(() => require("./graph-reports-B3mkLP8Z.cjs"));
1092
1155
  const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-XbN16DwU.cjs"));
1093
- const result = await trackFunds(remoteClient, config, {
1094
- trustedAddresses: trusted_addresses,
1095
- untrustedAddresses: untrusted_addresses,
1156
+ const result = await traceVictimFunds(remoteClient, config, {
1157
+ victimAddresses: victim_addresses,
1158
+ knownSuspectAddresses: known_suspect_addresses,
1096
1159
  network,
1097
1160
  caseId: case_id,
1161
+ incidentTimestampMs: incident_timestamp_ms,
1098
1162
  maxHops: max_hops,
1099
1163
  perAddressLimit: per_address_limit,
1100
1164
  minAmountSum: min_amount_sum
1101
1165
  });
1102
1166
  const report = await writeGraphReport(result.graphData, {
1103
1167
  serverPort: config.serverPort,
1104
- slug: `track-funds-${network}`
1168
+ slug: `trace-victim-funds-${network}`
1105
1169
  });
1106
1170
  await ensureArtifactServer(config.serverPort);
1107
1171
  return {
@@ -1127,22 +1191,24 @@ async function createProxy() {
1127
1191
  return {
1128
1192
  content: [{
1129
1193
  type: "text",
1130
- text: `Track funds failed: ${err.message}`
1194
+ text: `Trace victim funds failed: ${err.message}`
1131
1195
  }],
1132
1196
  isError: true
1133
1197
  };
1134
1198
  }
1135
1199
  });
1136
- if (!remoteToolNames.has("scam_topology")) (0, _modelcontextprotocol_ext_apps_server.registerAppTool)(server, "scam_topology", {
1137
- title: "Scam Topology",
1138
- description: KNOWN_PUBLIC_TOOL_DESCRIPTIONS.scam_topology,
1200
+ if (!remoteToolNames.has("trace_suspect_funds")) (0, _modelcontextprotocol_ext_apps_server.registerAppTool)(server, "trace_suspect_funds", {
1201
+ title: "Trace Suspect Funds",
1202
+ description: KNOWN_PUBLIC_TOOL_DESCRIPTIONS.trace_suspect_funds,
1139
1203
  inputSchema: {
1140
1204
  network: zod.string().min(1).describe(NETWORK_DESCRIPTION),
1141
- victim_address: zod.string().min(1).describe("Full victim/source address that anchors the scam incident. Victims are not risky labels."),
1142
- incident_timestamp_ms: zod.number().min(0).describe("Earliest known incident transfer timestamp in milliseconds. Primary traversal uses node-relative wave-arrival filtering."),
1143
- max_hops: zod.number().int().min(1).max(64).optional().describe("Maximum forward expansion depth. Default 16."),
1144
- activity_policy: zod.enum(["node_relative_only", "global_incident_only"]).optional().describe("Traversal activity policy. Default node_relative_only."),
1145
- case_id: zod.string().optional().describe("Optional Chain Insights case ID. When provided, compact evidence is appended to the case manifest.")
1205
+ suspect_addresses: zod.union([zod.string().min(1), zod.array(zod.string().min(1))]).describe("Comma-separated full suspect-controlled addresses, or an array. Min 1, max 5."),
1206
+ incident_timestamp_ms: zod.number().min(0).optional().describe("Optional incident timestamp in milliseconds. This tool works without it."),
1207
+ max_hops: zod.number().int().min(1).max(5).optional().describe("Maximum forward trace hops. Default 3."),
1208
+ per_address_limit: zod.number().int().min(1).max(10).optional(),
1209
+ min_amount_sum: zod.number().min(0).optional(),
1210
+ case_id: zod.string().optional().describe("Optional Chain Insights case ID. When provided, compact evidence is appended to the case manifest."),
1211
+ include_attachments: zod.boolean().optional().describe("Include graph app report metadata")
1146
1212
  },
1147
1213
  _meta: { ui: { resourceUri: GRAPH_RESOURCE_URI } },
1148
1214
  annotations: {
@@ -1151,7 +1217,7 @@ async function createProxy() {
1151
1217
  idempotentHint: false,
1152
1218
  openWorldHint: true
1153
1219
  }
1154
- }, async ({ victim_address, incident_timestamp_ms, network, max_hops, activity_policy, case_id }) => {
1220
+ }, async ({ suspect_addresses, incident_timestamp_ms, network, max_hops, per_address_limit, min_amount_sum, case_id }) => {
1155
1221
  try {
1156
1222
  if (!remoteConnected) return {
1157
1223
  content: [{
@@ -1160,20 +1226,90 @@ async function createProxy() {
1160
1226
  }],
1161
1227
  isError: true
1162
1228
  };
1163
- const { scamTopology } = await Promise.resolve().then(() => require("./public-tools-BwguvIsf.cjs"));
1229
+ const { traceSuspectFunds } = await Promise.resolve().then(() => require("./public-tools-BvMb3H2P.cjs"));
1164
1230
  const { writeGraphReport } = await Promise.resolve().then(() => require("./graph-reports-B3mkLP8Z.cjs"));
1165
1231
  const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-XbN16DwU.cjs"));
1166
- const result = await scamTopology(remoteClient, config, {
1167
- victimAddress: victim_address,
1232
+ const result = await traceSuspectFunds(remoteClient, config, {
1233
+ suspectAddresses: suspect_addresses,
1168
1234
  network,
1169
1235
  maxHops: max_hops,
1236
+ perAddressLimit: per_address_limit,
1237
+ minAmountSum: min_amount_sum,
1170
1238
  incidentTimestampMs: incident_timestamp_ms,
1171
- activityPolicyMode: activity_policy,
1172
1239
  caseId: case_id
1173
1240
  });
1174
1241
  const report = await writeGraphReport(result.graphData, {
1175
1242
  serverPort: config.serverPort,
1176
- slug: `scam-topology-${network}`
1243
+ slug: `trace-suspect-funds-${network}`
1244
+ });
1245
+ await ensureArtifactServer(config.serverPort);
1246
+ return {
1247
+ content: [{
1248
+ type: "text",
1249
+ text: result.summaryText
1250
+ }],
1251
+ structuredContent: result.structuredContent,
1252
+ _meta: { chainInsights: { graph: {
1253
+ schema: report.schema,
1254
+ url: report.url
1255
+ } } },
1256
+ isError: false
1257
+ };
1258
+ } catch (err) {
1259
+ if (err instanceof require_client.PaymentRequiredError) return {
1260
+ content: [{
1261
+ type: "text",
1262
+ text: err.message
1263
+ }],
1264
+ isError: true
1265
+ };
1266
+ return {
1267
+ content: [{
1268
+ type: "text",
1269
+ text: `Trace suspect funds failed: ${err.message}`
1270
+ }],
1271
+ isError: true
1272
+ };
1273
+ }
1274
+ });
1275
+ if (!remoteToolNames.has("trace_deposit_sources")) (0, _modelcontextprotocol_ext_apps_server.registerAppTool)(server, "trace_deposit_sources", {
1276
+ title: "Trace Deposit Sources",
1277
+ description: KNOWN_PUBLIC_TOOL_DESCRIPTIONS.trace_deposit_sources,
1278
+ inputSchema: {
1279
+ network: zod.string().min(1).describe(NETWORK_DESCRIPTION),
1280
+ deposit_addresses: zod.union([zod.string().min(1), zod.array(zod.string().min(1))]).describe("Comma-separated full suspected deposit/cashout addresses, or an array. Min 1, max 5."),
1281
+ max_hops: zod.number().int().min(1).max(5).optional().describe("Maximum reverse traceback hops. Default 2."),
1282
+ case_id: zod.string().optional().describe("Optional Chain Insights case ID. When provided, compact evidence is appended to the case manifest."),
1283
+ include_attachments: zod.boolean().optional().describe("Include graph app report metadata")
1284
+ },
1285
+ _meta: { ui: { resourceUri: GRAPH_RESOURCE_URI } },
1286
+ annotations: {
1287
+ readOnlyHint: false,
1288
+ destructiveHint: false,
1289
+ idempotentHint: false,
1290
+ openWorldHint: true
1291
+ }
1292
+ }, async ({ deposit_addresses, network, max_hops, case_id }) => {
1293
+ try {
1294
+ if (!remoteConnected) return {
1295
+ content: [{
1296
+ type: "text",
1297
+ text: `${remoteUnavailableMessage ?? `Graph MCP is not connected at ${graphMcpEndpoint}`}. Restart the Chain Insights MCP proxy after the endpoint is reachable.`
1298
+ }],
1299
+ isError: true
1300
+ };
1301
+ const { traceDepositSources } = await Promise.resolve().then(() => require("./public-tools-BvMb3H2P.cjs"));
1302
+ const { writeGraphReport } = await Promise.resolve().then(() => require("./graph-reports-B3mkLP8Z.cjs"));
1303
+ const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-XbN16DwU.cjs"));
1304
+ const result = await traceDepositSources(remoteClient, config, {
1305
+ depositAddresses: deposit_addresses,
1306
+ network,
1307
+ maxHops: max_hops,
1308
+ caseId: case_id
1309
+ });
1310
+ const report = await writeGraphReport(result.graphData, {
1311
+ serverPort: config.serverPort,
1312
+ slug: `trace-deposit-sources-${network}`
1177
1313
  });
1178
1314
  await ensureArtifactServer(config.serverPort);
1179
1315
  return {
@@ -1199,7 +1335,7 @@ async function createProxy() {
1199
1335
  return {
1200
1336
  content: [{
1201
1337
  type: "text",
1202
- text: `Scam topology failed: ${err.message}`
1338
+ text: `Trace deposit sources failed: ${err.message}`
1203
1339
  }],
1204
1340
  isError: true
1205
1341
  };
@@ -1237,7 +1373,7 @@ async function createProxy() {
1237
1373
  }],
1238
1374
  isError: true
1239
1375
  };
1240
- const { stakeInsights } = await Promise.resolve().then(() => require("./public-tools-BwguvIsf.cjs"));
1376
+ const { stakeInsights } = await Promise.resolve().then(() => require("./public-tools-BvMb3H2P.cjs"));
1241
1377
  const { writeGraphReport } = await Promise.resolve().then(() => require("./graph-reports-B3mkLP8Z.cjs"));
1242
1378
  const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-XbN16DwU.cjs"));
1243
1379
  const result = await stakeInsights(remoteClient, {
@@ -1302,8 +1438,9 @@ async function createProxy() {
1302
1438
  "- network_capabilities: inspect supported networks, data layers, tool availability, retention windows, and freshness.",
1303
1439
  "- address_risk: screen a full address for AML risk, behavior, neighborhood, exchange exposure, and optional compare_address connection checks.",
1304
1440
  "- stake_insights: explain Bittensor staking around one address, coldkey, or hotkey with net stake, movement amounts, counterparties, backend, and query evidence.",
1305
- "- track_funds: trace up to five trusted/victim addresses plus up to five known untrusted/scammer addresses through intermediaries to exchange deposit addresses.",
1306
- "- scam_topology: derive ML-ready scam_labels from one victim incident address and incident_timestamp_ms.",
1441
+ "- trace_victim_funds: trace up to five victim/source addresses forward to exchange deposit candidates.",
1442
+ "- trace_deposit_sources: trace backward from suspected deposit/cashout addresses to upstream funders and shared-source convergence.",
1443
+ "- trace_suspect_funds: trace up to five suspected scammer, mule, operator, or laundering-ring addresses forward to cashout topology.",
1307
1444
  "- graph_query: run read-only GQL/Cypher through the universal graph endpoint. Use USE live_topology, USE archive_topology, or USE facts.",
1308
1445
  "- graph_query_batch: run related read-only graph-language queries through one paid graph call.",
1309
1446
  "",
@@ -1313,6 +1450,7 @@ async function createProxy() {
1313
1450
  "- case_resume: load case context, evidence count, dossiers, and latest session.",
1314
1451
  "- case_add_evidence: append a report or note to the case evidence manifest.",
1315
1452
  "- case_verify_evidence: verify saved evidence integrity.",
1453
+ "- case_export: export a case for Obsidian, LLMWiki, Codex, Claude Code, and ChatGPT.",
1316
1454
  "- case_update_dossier: add a finding to an address/entity dossier.",
1317
1455
  "- case_start_session and case_end_session: record session notes.",
1318
1456
  "",
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-proxy.d.cts","names":[],"sources":["../src/mcp/proxy.ts"],"mappings":";;AAqyBA;;;;AAA4C;;iBAAtB,WAAA,CAAA,GAAe,OAAO"}
1
+ {"version":3,"file":"mcp-proxy.d.cts","names":[],"sources":["../src/mcp/proxy.ts"],"mappings":";;AAkzBA;;;;AAA4C;;iBAAtB,WAAA,CAAA,GAAe,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-proxy.d.mts","names":[],"sources":["../src/mcp/proxy.ts"],"mappings":";;AAqyBA;;;;AAA4C;;iBAAtB,WAAA,CAAA,GAAe,OAAO"}
1
+ {"version":3,"file":"mcp-proxy.d.mts","names":[],"sources":["../src/mcp/proxy.ts"],"mappings":";;AAkzBA;;;;AAA4C;;iBAAtB,WAAA,CAAA,GAAe,OAAO"}