chain-insights 0.3.6 → 0.3.9

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