chain-insights 0.3.7 → 0.3.11

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 (56) hide show
  1. package/README.md +5 -0
  2. package/dist/{app-BxojXjtB.cjs → app-DjJn3irw.cjs} +1 -1
  3. package/dist/{app-CRd39JJ8.mjs → app-norpwdou.mjs} +2 -2
  4. package/dist/{app-CRd39JJ8.mjs.map → app-norpwdou.mjs.map} +1 -1
  5. package/dist/{artifact-server-XbN16DwU.cjs → artifact-server-C6_gtIql.cjs} +1 -1
  6. package/dist/{artifact-server-CP6LXQ9d.mjs → artifact-server-DHPM0lxS.mjs} +2 -2
  7. package/dist/{artifact-server-CP6LXQ9d.mjs.map → artifact-server-DHPM0lxS.mjs.map} +1 -1
  8. package/dist/{capabilities-BC3Y5EOi.mjs → capabilities-BCvkTkIu.mjs} +3 -6
  9. package/dist/capabilities-BCvkTkIu.mjs.map +1 -0
  10. package/dist/{capabilities-D5PSx9Hj.cjs → capabilities-DOa6EFO-.cjs} +2 -5
  11. package/dist/cli.cjs +58 -31
  12. package/dist/cli.mjs +58 -31
  13. package/dist/cli.mjs.map +1 -1
  14. package/dist/{client-D4JE7fFF.mjs → client-BgmHjBHQ.mjs} +15 -7
  15. package/dist/client-BgmHjBHQ.mjs.map +1 -0
  16. package/dist/{client-Db6IV1tv.cjs → client-Y_zqKqJT.cjs} +19 -5
  17. package/dist/{config-Drgc2HuF.mjs → config-C6zM8Xir.mjs} +3 -3
  18. package/dist/{config-Drgc2HuF.mjs.map → config-C6zM8Xir.mjs.map} +1 -1
  19. package/dist/{config-BwVx19Og.cjs → config-CkW404Cs.cjs} +2 -2
  20. package/dist/index.cjs +4 -4
  21. package/dist/index.d.cts.map +1 -1
  22. package/dist/index.d.mts.map +1 -1
  23. package/dist/index.mjs +4 -4
  24. package/dist/{init-CKQ6F07J.mjs → init-uAmPfio2.mjs} +2 -2
  25. package/dist/{init-CKQ6F07J.mjs.map → init-uAmPfio2.mjs.map} +1 -1
  26. package/dist/{init-Dhw8F23z.cjs → init-vj2v5PMP.cjs} +1 -1
  27. package/dist/{mcp-endpoint-DHs1cRFH.mjs → mcp-endpoint-QQ5Lbqc2.mjs} +5 -2
  28. package/dist/mcp-endpoint-QQ5Lbqc2.mjs.map +1 -0
  29. package/dist/{mcp-endpoint-BaV8h_lq.cjs → mcp-endpoint-cQIZSjkK.cjs} +4 -1
  30. package/dist/mcp-proxy.cjs +444 -410
  31. package/dist/mcp-proxy.d.cts +3 -1
  32. package/dist/mcp-proxy.d.cts.map +1 -1
  33. package/dist/mcp-proxy.d.mts +3 -1
  34. package/dist/mcp-proxy.d.mts.map +1 -1
  35. package/dist/mcp-proxy.mjs +444 -411
  36. package/dist/mcp-proxy.mjs.map +1 -1
  37. package/dist/{public-tools-xfVNz9NE.cjs → public-tools-BY3PTw6x.cjs} +59 -31
  38. package/dist/{public-tools-CyUZEz9B.mjs → public-tools-CvlZcysd.mjs} +60 -32
  39. package/dist/public-tools-CvlZcysd.mjs.map +1 -0
  40. package/dist/{runner-DWuSy1Se.mjs → runner-B9fXAP0t.mjs} +3 -3
  41. package/dist/{runner-DWuSy1Se.mjs.map → runner-B9fXAP0t.mjs.map} +1 -1
  42. package/dist/{runner-CVo41fjz.cjs → runner-CcZCrrkn.cjs} +2 -2
  43. package/dist/{schema-BFEWhzg7.mjs → schema-D_qwaQA5.mjs} +2 -2
  44. package/dist/{schema-BFEWhzg7.mjs.map → schema-D_qwaQA5.mjs.map} +1 -1
  45. package/dist/{schema-Vl9yuOFO.cjs → schema-Dr6JXSOF.cjs} +1 -1
  46. package/dist/{server-BXLX2j_A.mjs → server-86dyCsJO.mjs} +2 -2
  47. package/dist/{server-BXLX2j_A.mjs.map → server-86dyCsJO.mjs.map} +1 -1
  48. package/dist/{server-BqVdWath.cjs → server-B2NFmnCM.cjs} +1 -1
  49. package/dist/update-BJoXYucO.cjs +145 -0
  50. package/dist/update-CJUfGCxs.mjs +145 -0
  51. package/dist/update-CJUfGCxs.mjs.map +1 -0
  52. package/package.json +1 -1
  53. package/dist/capabilities-BC3Y5EOi.mjs.map +0 -1
  54. package/dist/client-D4JE7fFF.mjs.map +0 -1
  55. package/dist/mcp-endpoint-DHs1cRFH.mjs.map +0 -1
  56. package/dist/public-tools-CyUZEz9B.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { n as PACKAGE_VERSION } from "./version-BA3J8hu4.mjs";
2
- import { t as PaymentRequiredError } from "./client-D4JE7fFF.mjs";
2
+ import { t as PaymentRequiredError } from "./client-BgmHjBHQ.mjs";
3
3
  import { t as HIDDEN_REMOTE_TOOL_NAMES } from "./tool-visibility-BpyZHRBi.mjs";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import path from "node:path";
@@ -41,6 +41,12 @@ const GRAPH_ARRAY_KEYS = [
41
41
  "edge_anchors"
42
42
  ];
43
43
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
44
+ function resolveMcpProxyMode(env = process.env) {
45
+ const raw = env["CHAIN_INSIGHTS_MCP_PROXY_MODE"]?.trim().toLowerCase();
46
+ if (!raw || raw === "workspace") return "workspace";
47
+ if (raw === "stateless" || raw === "no-workspace" || raw === "workspace-less") return "stateless";
48
+ throw new Error(`CHAIN_INSIGHTS_MCP_PROXY_MODE must be workspace or stateless; got "${raw}"`);
49
+ }
44
50
  const COMMA_SEPARATED_ADDRESS_FIELDS = new Set([
45
51
  "victim_addresses",
46
52
  "known_suspect_addresses",
@@ -113,6 +119,13 @@ const SERVER_INSTRUCTIONS = [
113
119
  GRAPH_SCHEMA_HINTS,
114
120
  "Presentation rules: preserve tool summaries as returned; never truncate blockchain addresses; use case tools to preserve evidence when a case exists."
115
121
  ].join("\n\n");
122
+ const STATELESS_SERVER_INSTRUCTIONS = [
123
+ "Chain Insights is running as a stateless AML proxy for a host application.",
124
+ "Do not use local case, evidence, dossier, session, wallet, or graph report workflows in this mode.",
125
+ "Use network_capabilities first when network support is unknown, then call address_risk, stake_insights, trace_victim_funds, trace_suspect_funds, trace_deposit_sources, graph_query, or graph_query_batch as needed.",
126
+ GRAPH_SCHEMA_HINTS,
127
+ "Presentation rules: preserve tool summaries as returned; never truncate blockchain addresses."
128
+ ].join("\n\n");
116
129
  function readGraphAppHtml() {
117
130
  const candidates = [
118
131
  path.resolve(__dirname, "templates", "graph.html"),
@@ -566,12 +579,12 @@ function getRemoteGraphPayload(result) {
566
579
  if (!data || typeof data !== "object" || Array.isArray(data)) throw new Error("Invalid remote graph payload");
567
580
  return data;
568
581
  }
569
- async function normalizeRemoteToolResult(result, config, toolName = "remote-graph") {
582
+ async function normalizeRemoteToolResult(result, config, toolName = "remote-graph", includeAttachments = true) {
570
583
  const graphPayload = getRemoteGraphPayload(result);
571
584
  const meta = { ...result._meta ?? {} };
572
- if (graphPayload) {
585
+ if (graphPayload && includeAttachments) {
573
586
  const { writeGraphReport } = await import("./graph-reports-BDELxmpi.mjs");
574
- const { ensureArtifactServer } = await import("./artifact-server-CP6LXQ9d.mjs");
587
+ const { ensureArtifactServer } = await import("./artifact-server-DHPM0lxS.mjs");
575
588
  const report = await writeGraphReport(graphPayload, {
576
589
  serverPort: config.serverPort,
577
590
  slug: toolName || "remote-graph"
@@ -592,6 +605,26 @@ async function normalizeRemoteToolResult(result, config, toolName = "remote-grap
592
605
  isError: result.isError
593
606
  };
594
607
  }
608
+ function shouldIncludeAttachments(args, workspaceArtifactsEnabled) {
609
+ return workspaceArtifactsEnabled && args["include_attachments"] !== false;
610
+ }
611
+ async function writeLocalGraphMeta(graphData, config, slug, includeAttachments) {
612
+ if (!includeAttachments) return void 0;
613
+ const { writeGraphReport } = await import("./graph-reports-BDELxmpi.mjs");
614
+ const { ensureArtifactServer } = await import("./artifact-server-DHPM0lxS.mjs");
615
+ const report = await writeGraphReport(graphData, {
616
+ serverPort: config.serverPort,
617
+ slug
618
+ });
619
+ await ensureArtifactServer(config.serverPort);
620
+ return {
621
+ schema: report.schema,
622
+ url: report.url
623
+ };
624
+ }
625
+ function graphMetaResult(graph) {
626
+ return graph ? { chainInsights: { graph } } : void 0;
627
+ }
595
628
  /**
596
629
  * Core proxy logic — exported so tests can inject dependencies directly.
597
630
  * The IIFE at the bottom calls this with real dependencies.
@@ -600,20 +633,23 @@ async function normalizeRemoteToolResult(result, config, toolName = "remote-grap
600
633
  * All diagnostic output goes to console.error() or process.stderr.write().
601
634
  */
602
635
  async function createProxy() {
603
- const { loadConfig } = await import("./config-Drgc2HuF.mjs").then((n) => n.t);
636
+ const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
604
637
  const { activeDataDir, findActiveWorkspace } = await import("./active-ByNgjuAg.mjs").then((n) => n.n);
605
- const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await import("./client-D4JE7fFF.mjs").then((n) => n.n);
638
+ const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await import("./client-BgmHjBHQ.mjs").then((n) => n.r);
606
639
  const { loadSchema, saveSchema } = await import("./schema-cache-DwDvPy4e.mjs");
640
+ const proxyMode = resolveMcpProxyMode();
641
+ const workspaceArtifactsEnabled = proxyMode === "workspace";
607
642
  const loadedConfig = await loadConfig();
608
- const activeWorkspace = findActiveWorkspace();
643
+ const activeWorkspace = workspaceArtifactsEnabled ? findActiveWorkspace() : null;
609
644
  const config = {
610
645
  ...loadedConfig,
611
- dataDir: activeDataDir(loadedConfig.dataDir)
646
+ dataDir: workspaceArtifactsEnabled ? activeDataDir(loadedConfig.dataDir) : loadedConfig.dataDir
612
647
  };
613
648
  const logger = createMcpLogger(config);
614
649
  await logger.info("proxy.start", {
615
650
  data_dir: config.dataDir,
616
651
  workspace_root: activeWorkspace?.root,
652
+ proxy_mode: proxyMode,
617
653
  graph_mcp_mode: config.graphMcpMode,
618
654
  graph_mcp_endpoint: resolveGraphMcpEndpoint(config),
619
655
  log_path: logger.filePath
@@ -690,7 +726,7 @@ async function createProxy() {
690
726
  const server = new McpServer({
691
727
  name: "chain-insights",
692
728
  version: PACKAGE_VERSION
693
- }, { instructions: SERVER_INSTRUCTIONS });
729
+ }, { instructions: workspaceArtifactsEnabled ? SERVER_INSTRUCTIONS : STATELESS_SERVER_INSTRUCTIONS });
694
730
  installToolLogging(server, logger);
695
731
  const remotePrompts = [];
696
732
  if (remoteConnected) try {
@@ -718,337 +754,339 @@ async function createProxy() {
718
754
  if (typeof tags === "string") return tags.split(",").map((tag) => tag.trim()).filter(Boolean);
719
755
  return [];
720
756
  };
721
- server.registerTool("balance", {
722
- description: "Show the local Chain Insights payment wallet address and Base USDC balance.",
723
- inputSchema: z.object({}).passthrough()
724
- }, async () => {
725
- try {
726
- const { getWalletAccount, getWalletBalanceText } = await import("./tools-v6kcdojg.mjs").then((n) => n.c);
727
- return {
728
- content: [{
729
- type: "text",
730
- text: await getWalletBalanceText(await getWalletAccount())
731
- }],
732
- isError: false
733
- };
734
- } catch (err) {
735
- return {
736
- content: [{
737
- type: "text",
738
- text: `Balance failed: ${err.message}`
739
- }],
740
- isError: true
741
- };
742
- }
743
- });
744
- registerAppResource(server, "Fund Flow Graph", GRAPH_RESOURCE_URI, {
745
- description: "Interactive D3 force-directed graph for fund flow and pattern visualization. It loads local graph report URLs returned in _meta.chainInsights.graph.url.",
746
- _meta: { ui: { csp: {
747
- resourceDomains: graphArtifactOrigins(config),
748
- connectDomains: graphArtifactOrigins(config)
749
- } } }
750
- }, async () => ({ contents: [{
751
- uri: GRAPH_RESOURCE_URI,
752
- mimeType: RESOURCE_MIME_TYPE,
753
- text: readGraphAppHtml(),
754
- _meta: { ui: { csp: {
755
- resourceDomains: graphArtifactOrigins(config),
756
- connectDomains: graphArtifactOrigins(config)
757
- } } }
758
- }] }));
759
- server.registerTool("case_open", {
760
- description: "Create a local Chain Insights investigation case. Use this before saving evidence, dossiers, or session notes for a new investigation.",
761
- inputSchema: {
762
- name: z.string().min(1).describe("Case name"),
763
- tags: z.union([z.string(), z.array(z.string())]).optional().describe("Comma-separated tags or string array"),
764
- description: z.string().optional().describe("Brief investigation description")
765
- },
766
- annotations: {
767
- readOnlyHint: false,
768
- destructiveHint: false,
769
- idempotentHint: false,
770
- openWorldHint: false
771
- }
772
- }, async ({ name, tags, description }) => {
773
- try {
774
- const { CaseStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
775
- const created = await CaseStore.create({
776
- name,
777
- tags: parseTags(tags),
778
- description: description ?? ""
779
- });
780
- const { casesRoot } = await import("./store-C2B_AssI.mjs").then((n) => n.n);
781
- return {
782
- content: [{
783
- type: "text",
784
- text: JSON.stringify({
785
- case_id: created.id,
786
- name: created.name,
787
- status: created.status,
788
- tags: created.tags,
789
- directory: `${path.join(casesRoot(), created.id)}/`
790
- }, null, 2)
791
- }],
792
- isError: false
793
- };
794
- } catch (err) {
795
- return caseToolError("Case open", err);
796
- }
797
- });
798
- server.registerTool("case_list", {
799
- description: "List local Chain Insights investigation cases. Use before resuming when the user does not provide a case ID.",
800
- inputSchema: { status: z.enum([
801
- "open",
802
- "active",
803
- "suspended",
804
- "closed"
805
- ]).optional().describe("Optional status filter") },
806
- annotations: {
807
- readOnlyHint: true,
808
- destructiveHint: false,
809
- idempotentHint: true,
810
- openWorldHint: false
811
- }
812
- }, async ({ status }) => {
813
- try {
814
- const { CaseStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
815
- const cases = await CaseStore.list();
816
- const filtered = status ? cases.filter((entry) => entry.status === status) : cases;
817
- return {
818
- content: [{
819
- type: "text",
820
- text: JSON.stringify({ cases: filtered }, null, 2)
821
- }],
822
- isError: false
823
- };
824
- } catch (err) {
825
- return caseToolError("Case list", err);
826
- }
827
- });
828
- server.registerTool("case_resume", {
829
- description: "Load local Chain Insights case context: metadata, evidence count, dossier summaries, and latest session notes.",
830
- inputSchema: { case_id: z.string().min(1).describe("Chain Insights case ID") },
831
- annotations: {
832
- readOnlyHint: true,
833
- destructiveHint: false,
834
- idempotentHint: true,
835
- openWorldHint: false
836
- }
837
- }, async ({ case_id }) => {
838
- try {
839
- const { CaseStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
840
- const context = await CaseStore.loadContext(case_id);
841
- return {
842
- content: [{
843
- type: "text",
844
- text: JSON.stringify(context, null, 2)
845
- }],
846
- isError: false
847
- };
848
- } catch (err) {
849
- return caseToolError("Case resume", err);
850
- }
851
- });
852
- server.registerTool("case_add_evidence", {
853
- 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.",
854
- inputSchema: {
855
- case_id: z.string().min(1).describe("Chain Insights case ID"),
856
- source: z.string().min(1).describe("Source tool or evidence origin"),
857
- content: z.string().min(1).describe("Evidence markdown/text to store"),
858
- query_params: z.string().optional().describe("Original query parameters, for example \"network=bittensor address=...\"")
859
- },
860
- annotations: {
861
- readOnlyHint: false,
862
- destructiveHint: false,
863
- idempotentHint: false,
864
- openWorldHint: false
865
- }
866
- }, async ({ case_id, source, content, query_params }) => {
867
- try {
868
- const { EvidenceStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
869
- const saved = await EvidenceStore.append(case_id, {
870
- source,
871
- content,
872
- queryParams: query_params ?? ""
873
- });
874
- return {
875
- content: [{
876
- type: "text",
877
- text: JSON.stringify(saved, null, 2)
878
- }],
879
- isError: false
880
- };
881
- } catch (err) {
882
- return caseToolError("Evidence append", err);
883
- }
884
- });
885
- server.registerTool("case_verify_evidence", {
886
- description: "Verify a local case evidence manifest and report tampered or missing evidence files.",
887
- inputSchema: { case_id: z.string().min(1).describe("Chain Insights case ID") },
888
- annotations: {
889
- readOnlyHint: true,
890
- destructiveHint: false,
891
- idempotentHint: true,
892
- openWorldHint: false
893
- }
894
- }, async ({ case_id }) => {
895
- try {
896
- const { EvidenceStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
897
- const result = await EvidenceStore.verifyManifest(case_id);
898
- return {
899
- content: [{
900
- type: "text",
901
- text: JSON.stringify(result, null, 2)
902
- }],
903
- isError: false
904
- };
905
- } catch (err) {
906
- return caseToolError("Evidence verify", err);
907
- }
908
- });
909
- server.registerTool("case_export", {
910
- description: "Export a Chain Insights case to an Obsidian, LLM Wiki, Codex, Claude Code, and ChatGPT-friendly handoff bundle.",
911
- inputSchema: {
912
- case_id: z.string().min(1).describe("Chain Insights case ID to export"),
913
- target: z.enum(["obsidian-llmwiki"]).optional().describe("Export target. Default obsidian-llmwiki."),
914
- mode: z.enum([
915
- "private",
916
- "partner",
917
- "public"
918
- ]).optional().describe("Redaction mode. Default private."),
919
- output_dir: z.string().optional().describe("Optional output directory. Defaults to published/<case-slug>.")
920
- },
921
- annotations: {
922
- readOnlyHint: false,
923
- destructiveHint: false,
924
- idempotentHint: false,
925
- openWorldHint: false
926
- }
927
- }, async ({ case_id, target, mode, output_dir }) => {
928
- try {
929
- const { exportCase } = await import("./export-CBhcJuZ6.mjs");
930
- const result = await exportCase({
931
- caseId: case_id,
932
- target: target ?? "obsidian-llmwiki",
933
- mode: mode ?? "private",
934
- outputDir: output_dir
935
- });
936
- return {
937
- content: [{
938
- type: "text",
939
- text: [
940
- `Case exported: ${result.outputDir}`,
941
- `Manifest: ${result.manifestPath}`,
942
- `Files: ${result.fileCount}`,
943
- `Open first: ${result.nextFile}`,
944
- ...result.warnings.map((warning) => `Warning: ${warning}`)
945
- ].join("\n")
946
- }],
947
- structuredContent: result,
948
- isError: false
949
- };
950
- } catch (err) {
951
- return caseToolError("Case export", err);
952
- }
953
- });
954
- server.registerTool("case_update_dossier", {
955
- description: "Append a finding to an address/entity dossier inside a local Chain Insights case.",
956
- inputSchema: {
957
- case_id: z.string().min(1).describe("Chain Insights case ID"),
958
- address: z.string().min(1).describe("Full address or entity identifier"),
959
- finding: z.string().min(1).describe("Finding to append"),
960
- entity_type: z.enum([
961
- "eoa",
962
- "contract",
963
- "exchange",
964
- "mixer",
965
- "unknown"
966
- ]).optional().describe("Entity type")
967
- },
968
- annotations: {
969
- readOnlyHint: false,
970
- destructiveHint: false,
971
- idempotentHint: false,
972
- openWorldHint: false
973
- }
974
- }, async ({ case_id, address, finding, entity_type }) => {
975
- try {
976
- const { DossierStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
977
- await DossierStore.appendFinding(case_id, address, finding, entity_type ?? "unknown");
978
- return {
979
- content: [{
980
- type: "text",
981
- text: JSON.stringify({
982
- case_id,
983
- address,
984
- updated: true
985
- }, null, 2)
986
- }],
987
- isError: false
988
- };
989
- } catch (err) {
990
- return caseToolError("Dossier update", err);
991
- }
992
- });
993
- server.registerTool("case_start_session", {
994
- description: "Start a local investigation session file for a Chain Insights case.",
995
- inputSchema: { case_id: z.string().min(1).describe("Chain Insights case ID") },
996
- annotations: {
997
- readOnlyHint: false,
998
- destructiveHint: false,
999
- idempotentHint: false,
1000
- openWorldHint: false
1001
- }
1002
- }, async ({ case_id }) => {
1003
- try {
1004
- const { SessionStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
1005
- const session = await SessionStore.start(case_id);
1006
- return {
1007
- content: [{
1008
- type: "text",
1009
- text: JSON.stringify(session, null, 2)
1010
- }],
1011
- isError: false
1012
- };
1013
- } catch (err) {
1014
- return caseToolError("Session start", err);
1015
- }
1016
- });
1017
- server.registerTool("case_end_session", {
1018
- description: "End the latest local investigation session for a Chain Insights case with findings and next steps.",
1019
- inputSchema: {
1020
- case_id: z.string().min(1).describe("Chain Insights case ID"),
1021
- findings: z.string().optional().describe("Key findings from this session"),
1022
- next_steps: z.string().optional().describe("Next investigation steps")
1023
- },
1024
- annotations: {
1025
- readOnlyHint: false,
1026
- destructiveHint: false,
1027
- idempotentHint: false,
1028
- openWorldHint: false
1029
- }
1030
- }, async ({ case_id, findings, next_steps }) => {
1031
- try {
1032
- const { SessionStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
1033
- await SessionStore.end(case_id, {
1034
- findings: findings ?? "",
1035
- nextSteps: next_steps ?? ""
1036
- });
1037
- await SessionStore.archiveOldSessions(case_id);
1038
- return {
1039
- content: [{
1040
- type: "text",
1041
- text: JSON.stringify({
1042
- case_id,
1043
- ended: true
1044
- }, null, 2)
1045
- }],
1046
- isError: false
1047
- };
1048
- } catch (err) {
1049
- return caseToolError("Session end", err);
1050
- }
1051
- });
757
+ if (workspaceArtifactsEnabled) {
758
+ server.registerTool("balance", {
759
+ description: "Show the local Chain Insights payment wallet address and Base USDC balance.",
760
+ inputSchema: z.object({}).passthrough()
761
+ }, async () => {
762
+ try {
763
+ const { getWalletAccount, getWalletBalanceText } = await import("./tools-v6kcdojg.mjs").then((n) => n.c);
764
+ return {
765
+ content: [{
766
+ type: "text",
767
+ text: await getWalletBalanceText(await getWalletAccount())
768
+ }],
769
+ isError: false
770
+ };
771
+ } catch (err) {
772
+ return {
773
+ content: [{
774
+ type: "text",
775
+ text: `Balance failed: ${err.message}`
776
+ }],
777
+ isError: true
778
+ };
779
+ }
780
+ });
781
+ registerAppResource(server, "Fund Flow Graph", GRAPH_RESOURCE_URI, {
782
+ description: "Interactive D3 force-directed graph for fund flow and pattern visualization. It loads local graph report URLs returned in _meta.chainInsights.graph.url.",
783
+ _meta: { ui: { csp: {
784
+ resourceDomains: graphArtifactOrigins(config),
785
+ connectDomains: graphArtifactOrigins(config)
786
+ } } }
787
+ }, async () => ({ contents: [{
788
+ uri: GRAPH_RESOURCE_URI,
789
+ mimeType: RESOURCE_MIME_TYPE,
790
+ text: readGraphAppHtml(),
791
+ _meta: { ui: { csp: {
792
+ resourceDomains: graphArtifactOrigins(config),
793
+ connectDomains: graphArtifactOrigins(config)
794
+ } } }
795
+ }] }));
796
+ server.registerTool("case_open", {
797
+ description: "Create a local Chain Insights investigation case. Use this before saving evidence, dossiers, or session notes for a new investigation.",
798
+ inputSchema: {
799
+ name: z.string().min(1).describe("Case name"),
800
+ tags: z.union([z.string(), z.array(z.string())]).optional().describe("Comma-separated tags or string array"),
801
+ description: z.string().optional().describe("Brief investigation description")
802
+ },
803
+ annotations: {
804
+ readOnlyHint: false,
805
+ destructiveHint: false,
806
+ idempotentHint: false,
807
+ openWorldHint: false
808
+ }
809
+ }, async ({ name, tags, description }) => {
810
+ try {
811
+ const { CaseStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
812
+ const created = await CaseStore.create({
813
+ name,
814
+ tags: parseTags(tags),
815
+ description: description ?? ""
816
+ });
817
+ const { casesRoot } = await import("./store-C2B_AssI.mjs").then((n) => n.n);
818
+ return {
819
+ content: [{
820
+ type: "text",
821
+ text: JSON.stringify({
822
+ case_id: created.id,
823
+ name: created.name,
824
+ status: created.status,
825
+ tags: created.tags,
826
+ directory: `${path.join(casesRoot(), created.id)}/`
827
+ }, null, 2)
828
+ }],
829
+ isError: false
830
+ };
831
+ } catch (err) {
832
+ return caseToolError("Case open", err);
833
+ }
834
+ });
835
+ server.registerTool("case_list", {
836
+ description: "List local Chain Insights investigation cases. Use before resuming when the user does not provide a case ID.",
837
+ inputSchema: { status: z.enum([
838
+ "open",
839
+ "active",
840
+ "suspended",
841
+ "closed"
842
+ ]).optional().describe("Optional status filter") },
843
+ annotations: {
844
+ readOnlyHint: true,
845
+ destructiveHint: false,
846
+ idempotentHint: true,
847
+ openWorldHint: false
848
+ }
849
+ }, async ({ status }) => {
850
+ try {
851
+ const { CaseStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
852
+ const cases = await CaseStore.list();
853
+ const filtered = status ? cases.filter((entry) => entry.status === status) : cases;
854
+ return {
855
+ content: [{
856
+ type: "text",
857
+ text: JSON.stringify({ cases: filtered }, null, 2)
858
+ }],
859
+ isError: false
860
+ };
861
+ } catch (err) {
862
+ return caseToolError("Case list", err);
863
+ }
864
+ });
865
+ server.registerTool("case_resume", {
866
+ description: "Load local Chain Insights case context: metadata, evidence count, dossier summaries, and latest session notes.",
867
+ inputSchema: { case_id: z.string().min(1).describe("Chain Insights case ID") },
868
+ annotations: {
869
+ readOnlyHint: true,
870
+ destructiveHint: false,
871
+ idempotentHint: true,
872
+ openWorldHint: false
873
+ }
874
+ }, async ({ case_id }) => {
875
+ try {
876
+ const { CaseStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
877
+ const context = await CaseStore.loadContext(case_id);
878
+ return {
879
+ content: [{
880
+ type: "text",
881
+ text: JSON.stringify(context, null, 2)
882
+ }],
883
+ isError: false
884
+ };
885
+ } catch (err) {
886
+ return caseToolError("Case resume", err);
887
+ }
888
+ });
889
+ server.registerTool("case_add_evidence", {
890
+ 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.",
891
+ inputSchema: {
892
+ case_id: z.string().min(1).describe("Chain Insights case ID"),
893
+ source: z.string().min(1).describe("Source tool or evidence origin"),
894
+ content: z.string().min(1).describe("Evidence markdown/text to store"),
895
+ query_params: z.string().optional().describe("Original query parameters, for example \"network=bittensor address=...\"")
896
+ },
897
+ annotations: {
898
+ readOnlyHint: false,
899
+ destructiveHint: false,
900
+ idempotentHint: false,
901
+ openWorldHint: false
902
+ }
903
+ }, async ({ case_id, source, content, query_params }) => {
904
+ try {
905
+ const { EvidenceStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
906
+ const saved = await EvidenceStore.append(case_id, {
907
+ source,
908
+ content,
909
+ queryParams: query_params ?? ""
910
+ });
911
+ return {
912
+ content: [{
913
+ type: "text",
914
+ text: JSON.stringify(saved, null, 2)
915
+ }],
916
+ isError: false
917
+ };
918
+ } catch (err) {
919
+ return caseToolError("Evidence append", err);
920
+ }
921
+ });
922
+ server.registerTool("case_verify_evidence", {
923
+ description: "Verify a local case evidence manifest and report tampered or missing evidence files.",
924
+ inputSchema: { case_id: z.string().min(1).describe("Chain Insights case ID") },
925
+ annotations: {
926
+ readOnlyHint: true,
927
+ destructiveHint: false,
928
+ idempotentHint: true,
929
+ openWorldHint: false
930
+ }
931
+ }, async ({ case_id }) => {
932
+ try {
933
+ const { EvidenceStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
934
+ const result = await EvidenceStore.verifyManifest(case_id);
935
+ return {
936
+ content: [{
937
+ type: "text",
938
+ text: JSON.stringify(result, null, 2)
939
+ }],
940
+ isError: false
941
+ };
942
+ } catch (err) {
943
+ return caseToolError("Evidence verify", err);
944
+ }
945
+ });
946
+ server.registerTool("case_export", {
947
+ description: "Export a Chain Insights case to an Obsidian, LLM Wiki, Codex, Claude Code, and ChatGPT-friendly handoff bundle.",
948
+ inputSchema: {
949
+ case_id: z.string().min(1).describe("Chain Insights case ID to export"),
950
+ target: z.enum(["obsidian-llmwiki"]).optional().describe("Export target. Default obsidian-llmwiki."),
951
+ mode: z.enum([
952
+ "private",
953
+ "partner",
954
+ "public"
955
+ ]).optional().describe("Redaction mode. Default private."),
956
+ output_dir: z.string().optional().describe("Optional output directory. Defaults to published/<case-slug>.")
957
+ },
958
+ annotations: {
959
+ readOnlyHint: false,
960
+ destructiveHint: false,
961
+ idempotentHint: false,
962
+ openWorldHint: false
963
+ }
964
+ }, async ({ case_id, target, mode, output_dir }) => {
965
+ try {
966
+ const { exportCase } = await import("./export-CBhcJuZ6.mjs");
967
+ const result = await exportCase({
968
+ caseId: case_id,
969
+ target: target ?? "obsidian-llmwiki",
970
+ mode: mode ?? "private",
971
+ outputDir: output_dir
972
+ });
973
+ return {
974
+ content: [{
975
+ type: "text",
976
+ text: [
977
+ `Case exported: ${result.outputDir}`,
978
+ `Manifest: ${result.manifestPath}`,
979
+ `Files: ${result.fileCount}`,
980
+ `Open first: ${result.nextFile}`,
981
+ ...result.warnings.map((warning) => `Warning: ${warning}`)
982
+ ].join("\n")
983
+ }],
984
+ structuredContent: result,
985
+ isError: false
986
+ };
987
+ } catch (err) {
988
+ return caseToolError("Case export", err);
989
+ }
990
+ });
991
+ server.registerTool("case_update_dossier", {
992
+ description: "Append a finding to an address/entity dossier inside a local Chain Insights case.",
993
+ inputSchema: {
994
+ case_id: z.string().min(1).describe("Chain Insights case ID"),
995
+ address: z.string().min(1).describe("Full address or entity identifier"),
996
+ finding: z.string().min(1).describe("Finding to append"),
997
+ entity_type: z.enum([
998
+ "eoa",
999
+ "contract",
1000
+ "exchange",
1001
+ "mixer",
1002
+ "unknown"
1003
+ ]).optional().describe("Entity type")
1004
+ },
1005
+ annotations: {
1006
+ readOnlyHint: false,
1007
+ destructiveHint: false,
1008
+ idempotentHint: false,
1009
+ openWorldHint: false
1010
+ }
1011
+ }, async ({ case_id, address, finding, entity_type }) => {
1012
+ try {
1013
+ const { DossierStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
1014
+ await DossierStore.appendFinding(case_id, address, finding, entity_type ?? "unknown");
1015
+ return {
1016
+ content: [{
1017
+ type: "text",
1018
+ text: JSON.stringify({
1019
+ case_id,
1020
+ address,
1021
+ updated: true
1022
+ }, null, 2)
1023
+ }],
1024
+ isError: false
1025
+ };
1026
+ } catch (err) {
1027
+ return caseToolError("Dossier update", err);
1028
+ }
1029
+ });
1030
+ server.registerTool("case_start_session", {
1031
+ description: "Start a local investigation session file for a Chain Insights case.",
1032
+ inputSchema: { case_id: z.string().min(1).describe("Chain Insights case ID") },
1033
+ annotations: {
1034
+ readOnlyHint: false,
1035
+ destructiveHint: false,
1036
+ idempotentHint: false,
1037
+ openWorldHint: false
1038
+ }
1039
+ }, async ({ case_id }) => {
1040
+ try {
1041
+ const { SessionStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
1042
+ const session = await SessionStore.start(case_id);
1043
+ return {
1044
+ content: [{
1045
+ type: "text",
1046
+ text: JSON.stringify(session, null, 2)
1047
+ }],
1048
+ isError: false
1049
+ };
1050
+ } catch (err) {
1051
+ return caseToolError("Session start", err);
1052
+ }
1053
+ });
1054
+ server.registerTool("case_end_session", {
1055
+ description: "End the latest local investigation session for a Chain Insights case with findings and next steps.",
1056
+ inputSchema: {
1057
+ case_id: z.string().min(1).describe("Chain Insights case ID"),
1058
+ findings: z.string().optional().describe("Key findings from this session"),
1059
+ next_steps: z.string().optional().describe("Next investigation steps")
1060
+ },
1061
+ annotations: {
1062
+ readOnlyHint: false,
1063
+ destructiveHint: false,
1064
+ idempotentHint: false,
1065
+ openWorldHint: false
1066
+ }
1067
+ }, async ({ case_id, findings, next_steps }) => {
1068
+ try {
1069
+ const { SessionStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
1070
+ await SessionStore.end(case_id, {
1071
+ findings: findings ?? "",
1072
+ nextSteps: next_steps ?? ""
1073
+ });
1074
+ await SessionStore.archiveOldSessions(case_id);
1075
+ return {
1076
+ content: [{
1077
+ type: "text",
1078
+ text: JSON.stringify({
1079
+ case_id,
1080
+ ended: true
1081
+ }, null, 2)
1082
+ }],
1083
+ isError: false
1084
+ };
1085
+ } catch (err) {
1086
+ return caseToolError("Session end", err);
1087
+ }
1088
+ });
1089
+ }
1052
1090
  if (!remoteToolNames.has("address_risk")) registerAppTool(server, "address_risk", {
1053
1091
  title: "Address Risk",
1054
1092
  description: KNOWN_PUBLIC_TOOL_DESCRIPTIONS.address_risk,
@@ -1065,7 +1103,7 @@ async function createProxy() {
1065
1103
  idempotentHint: true,
1066
1104
  openWorldHint: true
1067
1105
  }
1068
- }, async ({ address, network, compare_address }) => {
1106
+ }, async ({ address, network, compare_address, include_attachments }) => {
1069
1107
  try {
1070
1108
  if (!remoteConnected) return {
1071
1109
  content: [{
@@ -1074,29 +1112,20 @@ async function createProxy() {
1074
1112
  }],
1075
1113
  isError: true
1076
1114
  };
1077
- const { addressRisk } = await import("./public-tools-CyUZEz9B.mjs");
1078
- const { writeGraphReport } = await import("./graph-reports-BDELxmpi.mjs");
1079
- const { ensureArtifactServer } = await import("./artifact-server-CP6LXQ9d.mjs");
1115
+ const { addressRisk } = await import("./public-tools-CvlZcysd.mjs");
1080
1116
  const result = await addressRisk(remoteClient, {
1081
1117
  address,
1082
1118
  network,
1083
1119
  compareAddress: compare_address
1084
1120
  });
1085
- const report = await writeGraphReport(result.graphData, {
1086
- serverPort: config.serverPort,
1087
- slug: `address-risk-${network}-${address}`
1088
- });
1089
- await ensureArtifactServer(config.serverPort);
1121
+ const graph = await writeLocalGraphMeta(result.graphData, config, `address-risk-${network}-${address}`, shouldIncludeAttachments({ include_attachments }, workspaceArtifactsEnabled));
1090
1122
  return {
1091
1123
  content: [{
1092
1124
  type: "text",
1093
1125
  text: result.summaryText
1094
1126
  }],
1095
1127
  structuredContent: result.structuredContent,
1096
- _meta: { chainInsights: { graph: {
1097
- schema: report.schema,
1098
- url: report.url
1099
- } } },
1128
+ _meta: graphMetaResult(graph),
1100
1129
  isError: false
1101
1130
  };
1102
1131
  } catch (err) {
@@ -1137,7 +1166,7 @@ async function createProxy() {
1137
1166
  idempotentHint: false,
1138
1167
  openWorldHint: true
1139
1168
  }
1140
- }, async ({ victim_addresses, known_suspect_addresses, network, case_id, incident_timestamp_ms, max_hops, per_address_limit, min_amount_sum }) => {
1169
+ }, async ({ victim_addresses, known_suspect_addresses, network, case_id, incident_timestamp_ms, max_hops, per_address_limit, min_amount_sum, include_attachments }) => {
1141
1170
  try {
1142
1171
  if (!remoteConnected) return {
1143
1172
  content: [{
@@ -1146,9 +1175,14 @@ async function createProxy() {
1146
1175
  }],
1147
1176
  isError: true
1148
1177
  };
1149
- const { traceVictimFunds } = await import("./public-tools-CyUZEz9B.mjs");
1150
- const { writeGraphReport } = await import("./graph-reports-BDELxmpi.mjs");
1151
- const { ensureArtifactServer } = await import("./artifact-server-CP6LXQ9d.mjs");
1178
+ if (!workspaceArtifactsEnabled && case_id) return {
1179
+ content: [{
1180
+ type: "text",
1181
+ text: "case_id requires Chain Insights workspace mode; omit case_id when CHAIN_INSIGHTS_MCP_PROXY_MODE=stateless."
1182
+ }],
1183
+ isError: true
1184
+ };
1185
+ const { traceVictimFunds } = await import("./public-tools-CvlZcysd.mjs");
1152
1186
  const result = await traceVictimFunds(remoteClient, config, {
1153
1187
  victimAddresses: victim_addresses,
1154
1188
  knownSuspectAddresses: known_suspect_addresses,
@@ -1157,23 +1191,17 @@ async function createProxy() {
1157
1191
  incidentTimestampMs: incident_timestamp_ms,
1158
1192
  maxHops: max_hops,
1159
1193
  perAddressLimit: per_address_limit,
1160
- minAmountSum: min_amount_sum
1161
- });
1162
- const report = await writeGraphReport(result.graphData, {
1163
- serverPort: config.serverPort,
1164
- slug: `trace-victim-funds-${network}`
1194
+ minAmountSum: min_amount_sum,
1195
+ writeArtifacts: workspaceArtifactsEnabled
1165
1196
  });
1166
- await ensureArtifactServer(config.serverPort);
1197
+ const graph = await writeLocalGraphMeta(result.graphData, config, `trace-victim-funds-${network}`, shouldIncludeAttachments({ include_attachments }, workspaceArtifactsEnabled));
1167
1198
  return {
1168
1199
  content: [{
1169
1200
  type: "text",
1170
1201
  text: result.summaryText
1171
1202
  }],
1172
1203
  structuredContent: result.structuredContent,
1173
- _meta: { chainInsights: { graph: {
1174
- schema: report.schema,
1175
- url: report.url
1176
- } } },
1204
+ _meta: graphMetaResult(graph),
1177
1205
  isError: false
1178
1206
  };
1179
1207
  } catch (err) {
@@ -1213,7 +1241,7 @@ async function createProxy() {
1213
1241
  idempotentHint: false,
1214
1242
  openWorldHint: true
1215
1243
  }
1216
- }, async ({ suspect_addresses, incident_timestamp_ms, network, max_hops, per_address_limit, min_amount_sum, case_id }) => {
1244
+ }, async ({ suspect_addresses, incident_timestamp_ms, network, max_hops, per_address_limit, min_amount_sum, case_id, include_attachments }) => {
1217
1245
  try {
1218
1246
  if (!remoteConnected) return {
1219
1247
  content: [{
@@ -1222,9 +1250,14 @@ async function createProxy() {
1222
1250
  }],
1223
1251
  isError: true
1224
1252
  };
1225
- const { traceSuspectFunds } = await import("./public-tools-CyUZEz9B.mjs");
1226
- const { writeGraphReport } = await import("./graph-reports-BDELxmpi.mjs");
1227
- const { ensureArtifactServer } = await import("./artifact-server-CP6LXQ9d.mjs");
1253
+ if (!workspaceArtifactsEnabled && case_id) return {
1254
+ content: [{
1255
+ type: "text",
1256
+ text: "case_id requires Chain Insights workspace mode; omit case_id when CHAIN_INSIGHTS_MCP_PROXY_MODE=stateless."
1257
+ }],
1258
+ isError: true
1259
+ };
1260
+ const { traceSuspectFunds } = await import("./public-tools-CvlZcysd.mjs");
1228
1261
  const result = await traceSuspectFunds(remoteClient, config, {
1229
1262
  suspectAddresses: suspect_addresses,
1230
1263
  network,
@@ -1232,23 +1265,17 @@ async function createProxy() {
1232
1265
  perAddressLimit: per_address_limit,
1233
1266
  minAmountSum: min_amount_sum,
1234
1267
  incidentTimestampMs: incident_timestamp_ms,
1235
- caseId: case_id
1236
- });
1237
- const report = await writeGraphReport(result.graphData, {
1238
- serverPort: config.serverPort,
1239
- slug: `trace-suspect-funds-${network}`
1268
+ caseId: case_id,
1269
+ writeArtifacts: workspaceArtifactsEnabled
1240
1270
  });
1241
- await ensureArtifactServer(config.serverPort);
1271
+ const graph = await writeLocalGraphMeta(result.graphData, config, `trace-suspect-funds-${network}`, shouldIncludeAttachments({ include_attachments }, workspaceArtifactsEnabled));
1242
1272
  return {
1243
1273
  content: [{
1244
1274
  type: "text",
1245
1275
  text: result.summaryText
1246
1276
  }],
1247
1277
  structuredContent: result.structuredContent,
1248
- _meta: { chainInsights: { graph: {
1249
- schema: report.schema,
1250
- url: report.url
1251
- } } },
1278
+ _meta: graphMetaResult(graph),
1252
1279
  isError: false
1253
1280
  };
1254
1281
  } catch (err) {
@@ -1285,7 +1312,7 @@ async function createProxy() {
1285
1312
  idempotentHint: false,
1286
1313
  openWorldHint: true
1287
1314
  }
1288
- }, async ({ deposit_addresses, network, max_hops, case_id }) => {
1315
+ }, async ({ deposit_addresses, network, max_hops, case_id, include_attachments }) => {
1289
1316
  try {
1290
1317
  if (!remoteConnected) return {
1291
1318
  content: [{
@@ -1294,30 +1321,29 @@ async function createProxy() {
1294
1321
  }],
1295
1322
  isError: true
1296
1323
  };
1297
- const { traceDepositSources } = await import("./public-tools-CyUZEz9B.mjs");
1298
- const { writeGraphReport } = await import("./graph-reports-BDELxmpi.mjs");
1299
- const { ensureArtifactServer } = await import("./artifact-server-CP6LXQ9d.mjs");
1324
+ if (!workspaceArtifactsEnabled && case_id) return {
1325
+ content: [{
1326
+ type: "text",
1327
+ text: "case_id requires Chain Insights workspace mode; omit case_id when CHAIN_INSIGHTS_MCP_PROXY_MODE=stateless."
1328
+ }],
1329
+ isError: true
1330
+ };
1331
+ const { traceDepositSources } = await import("./public-tools-CvlZcysd.mjs");
1300
1332
  const result = await traceDepositSources(remoteClient, config, {
1301
1333
  depositAddresses: deposit_addresses,
1302
1334
  network,
1303
1335
  maxHops: max_hops,
1304
- caseId: case_id
1305
- });
1306
- const report = await writeGraphReport(result.graphData, {
1307
- serverPort: config.serverPort,
1308
- slug: `trace-deposit-sources-${network}`
1336
+ caseId: case_id,
1337
+ writeArtifacts: workspaceArtifactsEnabled
1309
1338
  });
1310
- await ensureArtifactServer(config.serverPort);
1339
+ const graph = await writeLocalGraphMeta(result.graphData, config, `trace-deposit-sources-${network}`, shouldIncludeAttachments({ include_attachments }, workspaceArtifactsEnabled));
1311
1340
  return {
1312
1341
  content: [{
1313
1342
  type: "text",
1314
1343
  text: result.summaryText
1315
1344
  }],
1316
1345
  structuredContent: result.structuredContent,
1317
- _meta: { chainInsights: { graph: {
1318
- schema: report.schema,
1319
- url: report.url
1320
- } } },
1346
+ _meta: graphMetaResult(graph),
1321
1347
  isError: false
1322
1348
  };
1323
1349
  } catch (err) {
@@ -1360,7 +1386,7 @@ async function createProxy() {
1360
1386
  idempotentHint: true,
1361
1387
  openWorldHint: true
1362
1388
  }
1363
- }, async ({ network, address, coldkey, hotkey, netuid, start_timestamp_ms, end_timestamp_ms, start_block, end_block, depth }) => {
1389
+ }, async ({ network, address, coldkey, hotkey, netuid, start_timestamp_ms, end_timestamp_ms, start_block, end_block, depth, include_attachments }) => {
1364
1390
  try {
1365
1391
  if (!remoteConnected) return {
1366
1392
  content: [{
@@ -1369,9 +1395,7 @@ async function createProxy() {
1369
1395
  }],
1370
1396
  isError: true
1371
1397
  };
1372
- const { stakeInsights } = await import("./public-tools-CyUZEz9B.mjs");
1373
- const { writeGraphReport } = await import("./graph-reports-BDELxmpi.mjs");
1374
- const { ensureArtifactServer } = await import("./artifact-server-CP6LXQ9d.mjs");
1398
+ const { stakeInsights } = await import("./public-tools-CvlZcysd.mjs");
1375
1399
  const result = await stakeInsights(remoteClient, {
1376
1400
  network,
1377
1401
  address,
@@ -1385,21 +1409,14 @@ async function createProxy() {
1385
1409
  depth
1386
1410
  });
1387
1411
  const subject = address ?? coldkey ?? hotkey ?? "subject";
1388
- const report = await writeGraphReport(result.graphData, {
1389
- serverPort: config.serverPort,
1390
- slug: `stake-insights-${network}-${subject}`
1391
- });
1392
- await ensureArtifactServer(config.serverPort);
1412
+ const graph = await writeLocalGraphMeta(result.graphData, config, `stake-insights-${network}-${subject}`, shouldIncludeAttachments({ include_attachments }, workspaceArtifactsEnabled));
1393
1413
  return {
1394
1414
  content: [{
1395
1415
  type: "text",
1396
1416
  text: result.summaryText
1397
1417
  }],
1398
1418
  structuredContent: result.structuredContent,
1399
- _meta: { chainInsights: { graph: {
1400
- schema: report.schema,
1401
- url: report.url
1402
- } } },
1419
+ _meta: graphMetaResult(graph),
1403
1420
  isError: false
1404
1421
  };
1405
1422
  } catch (err) {
@@ -1425,7 +1442,7 @@ async function createProxy() {
1425
1442
  }, async () => ({
1426
1443
  content: [{
1427
1444
  type: "text",
1428
- text: [
1445
+ text: workspaceArtifactsEnabled ? [
1429
1446
  "Chain Insights AML investigation workspace for AI agents. Workspaces are Obsidian-compatible vaults backed by plain local files.",
1430
1447
  "",
1431
1448
  CHAIN_INSIGHTS_WORKFLOW,
@@ -1457,6 +1474,22 @@ async function createProxy() {
1457
1474
  GRAPH_REPORT_HINTS,
1458
1475
  "",
1459
1476
  GRAPH_SCHEMA_HINTS
1477
+ ].join("\n") : [
1478
+ "Chain Insights stateless AML proxy for host applications.",
1479
+ "",
1480
+ "Local workspace, case, evidence, dossier, session, wallet, and graph report attachment tools are disabled in this mode.",
1481
+ "",
1482
+ "Available graph-backed tools:",
1483
+ "- network_capabilities: inspect supported networks, data layers, tool availability, retention windows, and freshness.",
1484
+ "- address_risk: screen a full address for AML risk, behavior, neighborhood, exchange exposure, and optional compare_address connection checks.",
1485
+ "- stake_insights: explain Bittensor staking around one address, coldkey, or hotkey with net stake, movement amounts, counterparties, backend, and query evidence.",
1486
+ "- trace_victim_funds: trace up to five victim/source addresses forward to exchange deposit candidates.",
1487
+ "- trace_deposit_sources: trace backward from suspected deposit/cashout addresses to upstream funders and shared-source convergence.",
1488
+ "- trace_suspect_funds: trace up to five suspected scammer, mule, operator, or laundering-ring addresses forward to cashout topology.",
1489
+ "- graph_query: run read-only GQL/Cypher through the universal graph endpoint. Use USE live_topology, USE archive_topology, or USE facts.",
1490
+ "- graph_query_batch: run related read-only graph-language queries through one paid graph call.",
1491
+ "",
1492
+ GRAPH_SCHEMA_HINTS
1460
1493
  ].join("\n")
1461
1494
  }],
1462
1495
  isError: false
@@ -1488,7 +1521,7 @@ async function createProxy() {
1488
1521
  arguments: normalizedArgs
1489
1522
  };
1490
1523
  const requestOptions = remoteToolRequestOptions(tool.name);
1491
- return await normalizeRemoteToolResult(requestOptions ? await remoteClient.callTool(request, void 0, requestOptions) : await remoteClient.callTool(request), config, tool.name);
1524
+ return await normalizeRemoteToolResult(requestOptions ? await remoteClient.callTool(request, void 0, requestOptions) : await remoteClient.callTool(request), config, tool.name, shouldIncludeAttachments(normalizedArgs, workspaceArtifactsEnabled));
1492
1525
  } catch (err) {
1493
1526
  if (err instanceof PaymentRequiredError) return {
1494
1527
  content: [{
@@ -1545,6 +1578,6 @@ if (process.argv[1] && import.meta.url.includes(process.argv[1].replace(/\\/g, "
1545
1578
  process.exit(1);
1546
1579
  });
1547
1580
  //#endregion
1548
- export { createProxy };
1581
+ export { createProxy, resolveMcpProxyMode };
1549
1582
 
1550
1583
  //# sourceMappingURL=mcp-proxy.mjs.map