chain-insights 0.2.18 → 0.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -12
- package/bin/cli.js +2 -3
- package/bin/install.cjs +0 -1
- package/dist/{app-DxlQE_P5.cjs → app-BxojXjtB.cjs} +1 -1
- package/dist/{app-DdWQF_zb.mjs → app-CRd39JJ8.mjs} +2 -2
- package/dist/{app-DdWQF_zb.mjs.map → app-CRd39JJ8.mjs.map} +1 -1
- package/dist/{artifact-server-4DiMvwhC.mjs → artifact-server-CP6LXQ9d.mjs} +2 -2
- package/dist/{artifact-server-4DiMvwhC.mjs.map → artifact-server-CP6LXQ9d.mjs.map} +1 -1
- package/dist/{artifact-server-B-3ho4bk.cjs → artifact-server-XbN16DwU.cjs} +1 -1
- package/dist/cli.cjs +66 -25
- package/dist/cli.mjs +66 -25
- package/dist/cli.mjs.map +1 -1
- package/dist/{config-BhYbhLDI.cjs → config-BwVx19Og.cjs} +48 -15
- package/dist/config-Drgc2HuF.mjs +77 -0
- package/dist/config-Drgc2HuF.mjs.map +1 -0
- package/dist/frontmatter-D0ccQnUM.mjs.map +1 -1
- package/dist/index.cjs +4 -4
- package/dist/index.d.cts +3 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +4 -4
- package/dist/{init-CZbZegIW.mjs → init-4tn7jfhN.mjs} +3 -2
- package/dist/init-4tn7jfhN.mjs.map +1 -0
- package/dist/{init-BvpZtFiT.cjs → init-TCQY5RDJ.cjs} +2 -1
- package/dist/mcp-endpoint-BaV8h_lq.cjs +60 -0
- package/dist/mcp-endpoint-DHs1cRFH.mjs +39 -0
- package/dist/mcp-endpoint-DHs1cRFH.mjs.map +1 -0
- package/dist/mcp-proxy.cjs +108 -9
- package/dist/mcp-proxy.d.cts.map +1 -1
- package/dist/mcp-proxy.d.mts.map +1 -1
- package/dist/mcp-proxy.mjs +108 -9
- package/dist/mcp-proxy.mjs.map +1 -1
- package/dist/{public-tools-D6Q5MTcO.mjs → public-tools-B13J0MJZ.mjs} +465 -70
- package/dist/public-tools-B13J0MJZ.mjs.map +1 -0
- package/dist/{public-tools-V7ON7goq.cjs → public-tools-BC1fi0DV.cjs} +464 -68
- package/dist/resolver-D7VBb0uB.mjs.map +1 -1
- package/dist/{runner-BatyCxv7.mjs → runner-DIs04IhN.mjs} +2 -2
- package/dist/{runner-BatyCxv7.mjs.map → runner-DIs04IhN.mjs.map} +1 -1
- package/dist/{runner-CCA7SJ7X.cjs → runner-ZYowxCVl.cjs} +1 -1
- package/dist/schema-BFEWhzg7.mjs +60 -0
- package/dist/schema-BFEWhzg7.mjs.map +1 -0
- package/dist/{schema-DN-KLkYN.cjs → schema-Vl9yuOFO.cjs} +31 -8
- package/dist/{server-BDlbmGbL.mjs → server-BXLX2j_A.mjs} +2 -2
- package/dist/{server-BDlbmGbL.mjs.map → server-BXLX2j_A.mjs.map} +1 -1
- package/dist/{server-C3y1gQmZ.cjs → server-BqVdWath.cjs} +1 -1
- package/dist/{topup-server-6MH7q73X.mjs → topup-server-BJgVw6Jt.mjs} +100 -42
- package/dist/topup-server-BJgVw6Jt.mjs.map +1 -0
- package/dist/{topup-server-DjUjhNjv.cjs → topup-server-yAaXYkJP.cjs} +98 -40
- package/docs/architecture.md +4 -0
- package/docs/contributing.md +1 -0
- package/docs/debugging.md +10 -14
- package/docs/graph-tools.md +60 -2
- package/docs/mcp-proxy.md +44 -0
- package/package.json +2 -2
- package/skills/chain-insights-developer-experience/SKILL.md +4 -2
- package/skills/chain-insights-investigation/SKILL.md +1 -1
- package/skills/test-chain-insights-graphrag-mcp/SKILL.md +4 -5
- package/skills/test-chain-insights-graphrag-mcp/scripts/run-uat.sh +5 -24
- package/dist/config-9KYXaAv-.mjs +0 -44
- package/dist/config-9KYXaAv-.mjs.map +0 -1
- package/dist/init-CZbZegIW.mjs.map +0 -1
- package/dist/public-tools-D6Q5MTcO.mjs.map +0 -1
- package/dist/schema-BbQVXp36.mjs +0 -37
- package/dist/schema-BbQVXp36.mjs.map +0 -1
- package/dist/topup-server-6MH7q73X.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -4,9 +4,9 @@ import { Address, Hex } from "viem";
|
|
|
4
4
|
|
|
5
5
|
//#region src/config/schema.d.ts
|
|
6
6
|
declare const ConfigSchema: z.ZodObject<{
|
|
7
|
-
mcpEndpoint: z.ZodDefault<z.ZodString
|
|
7
|
+
mcpEndpoint: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
8
8
|
mcpAuthToken: z.ZodOptional<z.ZodString>;
|
|
9
|
-
graphMcpEndpoint: z.ZodDefault<z.ZodString
|
|
9
|
+
graphMcpEndpoint: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
10
10
|
graphMcpAuthToken: z.ZodOptional<z.ZodString>;
|
|
11
11
|
graphMcpMode: z.ZodDefault<z.ZodEnum<{
|
|
12
12
|
paid: "paid";
|
|
@@ -78,7 +78,7 @@ declare function getWalletBalanceText(account?: PaymentWalletAccount): Promise<s
|
|
|
78
78
|
declare function buildTopupInfo(address: string, topupUrl?: string): TopupInfo;
|
|
79
79
|
//#endregion
|
|
80
80
|
//#region src/wallet/mcp-proxy/topup-server.d.ts
|
|
81
|
-
declare function generateArtifactHtml(
|
|
81
|
+
declare function generateArtifactHtml(walletAddressInput: string, topupUrl: string): string;
|
|
82
82
|
//#endregion
|
|
83
83
|
//#region src/wallet/topup-server.d.ts
|
|
84
84
|
declare function getTopupUrl(): string | null;
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/config/schema.ts","../src/config/index.ts","../src/server/app.ts","../src/server/index.ts","../src/wallet/index.ts","../src/wallet/tools.ts","../src/wallet/mcp-proxy/topup-server.ts","../src/wallet/topup-server.ts","../src/mcp/client.ts","../src/viz/graph-model.ts","../src/viz/index.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/config/schema.ts","../src/config/index.ts","../src/server/app.ts","../src/server/index.ts","../src/wallet/index.ts","../src/wallet/tools.ts","../src/wallet/mcp-proxy/topup-server.ts","../src/wallet/topup-server.ts","../src/mcp/client.ts","../src/viz/graph-model.ts","../src/viz/index.ts"],"mappings":";;;;;cAmBa,YAAA,EAAY,CAAA,CAAA,SAAA;;;;;;;;;;;;;;KAYb,kBAAA,GAAqB,CAAA,CAAE,KAAK,QAAQ,YAAA;;;iBCoB1B,UAAA,CAAA,GAAc,OAAO,CAAC,kBAAA;AAAA,iBAMtB,UAAA,CAAW,OAAA,EAAS,OAAA,CAAQ,kBAAA,IAAsB,OAAA;AAAA,iBAclD,gBAAA,CAAA,GAAoB,OAAO;;;iBC4DjC,SAAA,CAAA,GAAa,IAAI;;;iBChIjB,WAAA,CAAY,IAAW;;;iBC0BvB,yBAAA,CAA0B,KAAA,WAAgB,GAAG;AAAA,iBAO7C,2BAAA,CAA4B,UAAA,WAAqB,OAAO;;AJjBxE;;;;;;iBI4BsB,UAAA,CAAW,UAAA,WAAqB,OAAO;AAAA,iBA2BvC,mBAAA,CAAoB,UAAA,WAAqB,OAAO,CAAC,OAAA;;;;;;;iBAajD,UAAA,CAAA,GAAc,OAAO;;;;;iBA2CrB,kBAAA,CAAA,GAAsB,OAAO;;;cC7HtC,aAAA;AAAA,cACA,YAAA;AAAA,UAmBI,oBAAA;EACf,OAAA,EAAS,OAAA;EACT,UAAA,EAAY,GAAG;AAAA;AAAA,UAGA,SAAA;EACf,cAAA;EACA,OAAA;EACA,QAAA,SAAiB,aAAA;EACjB,KAAA;EACA,cAAA,SAAuB,YAAY;EACnC,SAAA;AAAA;AAAA,iBAGoB,gBAAA,CAAA,GAAoB,OAAO,CAAC,oBAAA;AAAA,iBAM5B,cAAA,CACpB,OAAA,EAAS,OAAA,WACT,MAAA,wBACC,OAAO;AAAA,iBA2BY,aAAA,CACpB,OAAA,EAAS,OAAA,WACT,MAAA,wBACC,OAAO;AAAA,iBAsBM,mBAAA,CAAoB,OAAA,UAAiB,WAAA,UAAqB,UAAA;AAAA,iBAUpD,oBAAA,CAAqB,OAAA,GAAU,oBAAA,GAAuB,OAAO;AAAA,iBASnE,cAAA,CAAe,OAAA,UAAiB,QAAA,YAAoB,SAAS;;;iBCA7D,oBAAA,CAAqB,kBAAA,UAA4B,QAAgB;;;iBClBjE,WAAA,CAAA;AAAA,iBAcM,gBAAA,CAAiB,OAAA,EAAS,oBAAA,YAAgC,OAAO;;;;;;;;;;;;;iBC3BvE,oBAAA,CAAqB,UAAA,iBAA2B,SAAA,mBAAkB,KAAA;;;cChFrE,SAAA,EAAS,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;KAWV,SAAA,GAAY,CAAA,CAAE,KAAK,QAAQ,SAAA;AAAA,cAE1B,SAAA,EAAS,CAAA,CAAA,SAAA;;;;;;;;KAQV,SAAA,GAAY,CAAA,CAAE,KAAK,QAAQ,SAAA;AAAA,cAE1B,SAAA,EAAS,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAYV,SAAA,GAAY,CAAA,CAAE,KAAK,QAAQ,SAAA;;;iBClCjB,qBAAA,CAAsB,IAAA;EAC1C,MAAA;EACA,QAAA;AAAA,IACE,OAAO;EAAG,KAAA;EAAe,QAAA;AAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { i as saveConfig, n as loadConfig, r as resetConfigCache } from "./config-
|
|
2
|
-
import { t as createApp } from "./app-
|
|
3
|
-
import { n as startServer } from "./server-
|
|
1
|
+
import { i as saveConfig, n as loadConfig, r as resetConfigCache } from "./config-Drgc2HuF.mjs";
|
|
2
|
+
import { t as createApp } from "./app-CRd39JJ8.mjs";
|
|
3
|
+
import { n as startServer } from "./server-BXLX2j_A.mjs";
|
|
4
4
|
import { a as setWalletPrivateKey, i as normalizeWalletPrivateKey, n as encryptKey, o as walletAddressFromPrivateKey, r as isWalletConfigured, t as decryptKey } from "./wallet-D8IqFRKY.mjs";
|
|
5
5
|
import { a as getWalletAccount, i as getBalanceUsdc, n as formatWalletBalance, o as getWalletBalanceText, r as getBalanceEth, t as buildTopupInfo } from "./tools-Py6SXg6J.mjs";
|
|
6
|
-
import { i as generateArtifactHtml, n as startTopupServer, t as getTopupUrl } from "./topup-server-
|
|
6
|
+
import { i as generateArtifactHtml, n as startTopupServer, t as getTopupUrl } from "./topup-server-BJgVw6Jt.mjs";
|
|
7
7
|
import { i as createMcpFetchClient } from "./client-D4_hd4AP.mjs";
|
|
8
8
|
import { t as generateVisualization } from "./viz-DkJyqlUu.mjs";
|
|
9
9
|
export { buildTopupInfo, createApp, createMcpFetchClient, decryptKey, encryptKey, formatWalletBalance, generateArtifactHtml, generateVisualization, getBalanceEth, getBalanceUsdc, getTopupUrl, getWalletAccount, getWalletBalanceText, isWalletConfigured, loadConfig, normalizeWalletPrivateKey, resetConfigCache, saveConfig, setWalletPrivateKey, startServer, startTopupServer, walletAddressFromPrivateKey };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as LOCAL_GRAPH_MCP_ENDPOINT } from "./mcp-endpoint-DHs1cRFH.mjs";
|
|
1
2
|
import path from "node:path";
|
|
2
3
|
import { access, mkdir, writeFile } from "node:fs/promises";
|
|
3
4
|
//#region src/workspace/init.ts
|
|
@@ -23,7 +24,7 @@ function workspaceJson(workspaceRoot) {
|
|
|
23
24
|
name: "Chain Insights Investigations",
|
|
24
25
|
workspace_root: workspaceRoot,
|
|
25
26
|
default_network: "bittensor",
|
|
26
|
-
graph_mcp_endpoint:
|
|
27
|
+
graph_mcp_endpoint: LOCAL_GRAPH_MCP_ENDPOINT,
|
|
27
28
|
cases_dir: "cases",
|
|
28
29
|
imports_dir: "imports",
|
|
29
30
|
reports_dir: "reports",
|
|
@@ -228,4 +229,4 @@ async function initWorkspace(options) {
|
|
|
228
229
|
//#endregion
|
|
229
230
|
export { initWorkspace };
|
|
230
231
|
|
|
231
|
-
//# sourceMappingURL=init-
|
|
232
|
+
//# sourceMappingURL=init-4tn7jfhN.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-4tn7jfhN.mjs","names":[],"sources":["../src/workspace/init.ts"],"sourcesContent":["import { access, mkdir, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport { LOCAL_GRAPH_MCP_ENDPOINT } from '../config/mcp-endpoint.js'\n\nexport interface InitWorkspaceOptions {\n targetDir: string\n force?: boolean\n}\n\nexport interface InitWorkspaceResult {\n workspaceRoot: string\n filesWritten: string[]\n}\n\nconst WORKSPACE_DIRS = [\n '.chain-insights',\n '.chain-insights/schema',\n '.chain-insights/runtime',\n '.chain-insights/runtime/logs',\n '.chain-insights/runtime-skill',\n 'cases',\n 'imports',\n 'reports',\n 'reports/graphs',\n 'reports/tables',\n 'templates',\n]\n\nfunction todayIso(): string {\n return new Date().toISOString().slice(0, 10)\n}\n\nfunction workspaceJson(workspaceRoot: string): string {\n return JSON.stringify({\n schema: 'chain-insights.workspace.v1',\n name: 'Chain Insights Investigations',\n workspace_root: workspaceRoot,\n default_network: 'bittensor',\n graph_mcp_endpoint: LOCAL_GRAPH_MCP_ENDPOINT,\n cases_dir: 'cases',\n imports_dir: 'imports',\n reports_dir: 'reports',\n templates_dir: 'templates',\n created_at: todayIso(),\n }, null, 2) + '\\n'\n}\n\nconst README = `# Chain Insights Investigations\n\nThis is a workspace for Chain Insights AML investigations.\n\n## Start\n\n\\`\\`\\`bash\nchain-insights mcp tools --refresh\nchain-insights wallet balance\n\\`\\`\\`\n\n## Layout\n\n\\`\\`\\`text\n.chain-insights/ Workspace metadata\ncases/ Case exports and notes\nimports/ External reports, CSVs, screenshots, raw notes\nreports/ Final or interim analyst reports\nreports/graphs/ Graph JSON for visualization\nreports/tables/ Compact tabular extracts\ntemplates/ Reusable case/report templates\n.chain-insights/schema/ Runtime graph schema captures\n.chain-insights/runtime/ Workspace-local runtime process state and debug logs\n.chain-insights/runtime-skill/ Workspace-specific agent schema notes\n\\`\\`\\`\n`\n\nconst AGENTS = `# Agent Instructions\n\nYou are operating inside a Chain Insights investigation workspace.\n\n- Read README.md first.\n- If this directory is not initialized, run \\`cia init .\\` before investigation-producing commands.\n- Do not rerun init in an existing workspace unless replacing scaffolding with \\`--force\\`.\n- Read .chain-insights/runtime-skill/SKILL.md before graph queries.\n- Preserve full blockchain addresses exactly.\n- Do not guess the network for graph queries.\n- Capture or refresh graph schema before the first case query.\n- Save compact evidence with original graph field names.\n- Put canonical graph JSON in reports/graphs/ and analyst tables in reports/tables/.\n- Evidence files should summarize and point to graph/table outputs; do not paste large raw JSON blobs into evidence Markdown.\n- Investigation output must stay in this initialized workspace.\n- Never write cases, evidence, reports, graph JSON, HTML, schema captures, or logs to ~/.chain-insights.\n- Keep theories lightweight until evidence supports them.\n`\n\nconst CLAUDE = AGENTS\n\nconst CASE_BRIEF = `# Case Brief\n\n## Summary\n\nStatus:\nNetwork:\nCurrent Assessment:\n\n## Known Addresses\n\n## Claims To Validate\n\n## Evidence\n\n## Next Steps\n`\n\nconst IMPORTS_README = `# External Investigation Inputs\n\nPut user-provided or third-party investigation material here before turning it\ninto case evidence.\n\nExamples:\n\n- Exchange support exports\n- CSV extracts\n- Screenshots\n- Raw notes\n- Partner reports\n\nFiles in this directory are inputs, not verified evidence. When an import\nsupports a claim, summarize it into the case evidence manifest and reference\nthe original file path.\n`\n\nconst TEMPLATES_README = `# Reusable Workspace Templates\n\nStore local report, case, prompt, and evidence templates here.\n\nTemplates are optional workspace helpers. They are not evidence and should not\nbe treated as case state until copied into a case, evidence file, dossier, or\nreport.\n`\n\nconst RUNTIME_SKILL = `---\nname: chain-insights-runtime-schema\ndescription: Workspace-local Chain Insights runtime schema notes. Refresh this after connecting to a graph MCP endpoint.\n---\n\n# Runtime Graph Schema\n\nBefore the first investigation query, capture the live graph schema into:\n\n\\`\\`\\`text\n.chain-insights/schema/<network>.graph-schema.json\n\\`\\`\\`\n\nUse \\`graph_query_batch\\` for schema capture. Prefix current topology reads\nwith \\`USE live_topology\\`, historical topology reads with\n\\`USE archive_topology\\`, and fact reads with \\`USE facts\\`, for example:\n\n\\`\\`\\`bash\ncia mcp call graph_query_batch network=<network> 'queries=[{\"id\":\"node_labels\",\"query\":\"USE live_topology MATCH (n:Address) RETURN \\\"Address\\\" AS node_label, count(n) AS sample_count LIMIT 1\"},{\"id\":\"archive_flow_sample\",\"query\":\"USE archive_topology MATCH (:Address)-[f:FLOWS_TO]->(:Address) RETURN f.period_granularity AS granularity, f.amount_sum AS amount_sum LIMIT 20\"}]'\n\\`\\`\\`\n\nThen update this file with observed labels, relationship types, and allowed\nproperty names for the active network.\n\nRules:\n\n- Prefer \\`graph_query\\` and \\`graph_query_batch\\` for graph-language reads.\n- Use \\`USE live_topology\\` for recent topology, \\`USE archive_topology\\`\n for historical topology, and \\`USE facts\\` for labels, features,\n risk scores, assets, and enrichment. Address facts can be reached through\n relationships such as \\`(:Address)-[:HAS_FEATURE]->(:AddressFeature)\\`.\n Archived money-flow topology is exposed as\n \\`(:Address)-[:FLOWS_TO]->(:Address)\\` with \\`period_granularity\\`,\n \\`period_start_date\\`, and \\`period_end_date\\` on the relationship.\n- Preserve source schema field names in evidence and generated data files.\n- Do not rename, reinterpret, or add unit labels to graph fields unless the\n schema or query result explicitly supports that interpretation.\n- Keep evidence compact: select only the fields needed to support the claim.\n Avoid storing whole node or relationship property blobs in evidence unless\n the purpose of the query is schema discovery or debugging.\n- Keep analysis products separate from evidence: graph JSON belongs under\n \\`reports/graphs/\\`, tabular extracts under \\`reports/tables/\\`, and analyst\n narrative under \\`reports/\\`.\n- Evidence Markdown should be a short provenance record with key facts and\n pointers. Large JSON belongs in \\`reports/tables/\\`, not inline in evidence.\n`\n\nconst SCHEMA_README = `# Runtime Schema Captures\n\nStore graph schema captures here, for example:\n\n\\`\\`\\`text\nbittensor.graph-schema.json\n\\`\\`\\`\n\nSchema captures should be generated before the first case query in a fresh\nworkspace, then referenced by evidence, reports, and runtime skill notes.\n`\n\nfunction workspaceFiles(workspaceRoot: string): Array<[string, string]> {\n return [\n ['.chain-insights/workspace.json', workspaceJson(workspaceRoot)],\n ['README.md', README],\n ['AGENTS.md', AGENTS],\n ['CLAUDE.md', CLAUDE],\n ['imports/README.md', IMPORTS_README],\n ['templates/README.md', TEMPLATES_README],\n ['templates/case-brief.md', CASE_BRIEF],\n ['.chain-insights/runtime-skill/SKILL.md', RUNTIME_SKILL],\n ['.chain-insights/schema/README.md', SCHEMA_README],\n ['.chain-insights/runtime/.keep', ''],\n ['.chain-insights/runtime/logs/.keep', ''],\n ]\n}\n\nasync function assertNoFileCollisions(workspaceRoot: string): Promise<void> {\n for (const [relativePath] of workspaceFiles(workspaceRoot)) {\n const filePath = path.join(workspaceRoot, relativePath)\n try {\n await access(filePath)\n throw new Error(`Refusing to overwrite ${filePath}. Re-run with --force to replace workspace files.`)\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n continue\n }\n throw err\n }\n }\n}\n\nexport async function initWorkspace(options: InitWorkspaceOptions): Promise<InitWorkspaceResult> {\n const workspaceRoot = path.resolve(options.targetDir)\n if (!options.force) {\n await assertNoFileCollisions(workspaceRoot)\n }\n\n for (const dir of WORKSPACE_DIRS) {\n await mkdir(path.join(workspaceRoot, dir), { recursive: true })\n }\n\n const filesWritten: string[] = []\n const flag = options.force ? 'w' : 'wx'\n for (const [relativePath, content] of workspaceFiles(workspaceRoot)) {\n const filePath = path.join(workspaceRoot, relativePath)\n try {\n await writeFile(filePath, content, { mode: 0o600, flag })\n filesWritten.push(relativePath)\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'EEXIST') {\n throw new Error(`Refusing to overwrite ${filePath}. Re-run with --force to replace workspace files.`)\n }\n throw err\n }\n }\n\n return { workspaceRoot, filesWritten }\n}\n"],"mappings":";;;;AAcA,MAAM,iBAAiB;CACrB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,WAAmB;CAC1B,wBAAO,IAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAC7C;AAEA,SAAS,cAAc,eAA+B;CACpD,OAAO,KAAK,UAAU;EACpB,QAAQ;EACR,MAAM;EACN,gBAAgB;EAChB,iBAAiB;EACjB,oBAAoB;EACpB,WAAW;EACX,aAAa;EACb,aAAa;EACb,eAAe;EACf,YAAY,SAAS;CACvB,GAAG,MAAM,CAAC,IAAI;AAChB;AAEA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;AA2Bf,MAAM,SAAS;;;;;;;;;;;;;;;;;;AAmBf,MAAM,SAAS;AAEf,MAAM,aAAa;;;;;;;;;;;;;;;;AAiBnB,MAAM,iBAAiB;;;;;;;;;;;;;;;;;AAkBvB,MAAM,mBAAmB;;;;;;;;AASzB,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CtB,MAAM,gBAAgB;;;;;;;;;;;AAYtB,SAAS,eAAe,eAAgD;CACtE,OAAO;EACL,CAAC,kCAAkC,cAAc,aAAa,CAAC;EAC/D,CAAC,aAAa,MAAM;EACpB,CAAC,aAAa,MAAM;EACpB,CAAC,aAAa,MAAM;EACpB,CAAC,qBAAqB,cAAc;EACpC,CAAC,uBAAuB,gBAAgB;EACxC,CAAC,2BAA2B,UAAU;EACtC,CAAC,0CAA0C,aAAa;EACxD,CAAC,oCAAoC,aAAa;EAClD,CAAC,iCAAiC,EAAE;EACpC,CAAC,sCAAsC,EAAE;CAC3C;AACF;AAEA,eAAe,uBAAuB,eAAsC;CAC1E,KAAK,MAAM,CAAC,iBAAiB,eAAe,aAAa,GAAG;EAC1D,MAAM,WAAW,KAAK,KAAK,eAAe,YAAY;EACtD,IAAI;GACF,MAAM,OAAO,QAAQ;GACrB,MAAM,IAAI,MAAM,yBAAyB,SAAS,kDAAkD;EACtG,SAAS,KAAK;GACZ,IAAK,IAA8B,SAAS,UAC1C;GAEF,MAAM;EACR;CACF;AACF;AAEA,eAAsB,cAAc,SAA6D;CAC/F,MAAM,gBAAgB,KAAK,QAAQ,QAAQ,SAAS;CACpD,IAAI,CAAC,QAAQ,OACX,MAAM,uBAAuB,aAAa;CAG5C,KAAK,MAAM,OAAO,gBAChB,MAAM,MAAM,KAAK,KAAK,eAAe,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;CAGhE,MAAM,eAAyB,CAAC;CAChC,MAAM,OAAO,QAAQ,QAAQ,MAAM;CACnC,KAAK,MAAM,CAAC,cAAc,YAAY,eAAe,aAAa,GAAG;EACnE,MAAM,WAAW,KAAK,KAAK,eAAe,YAAY;EACtD,IAAI;GACF,MAAM,UAAU,UAAU,SAAS;IAAE,MAAM;IAAO;GAAK,CAAC;GACxD,aAAa,KAAK,YAAY;EAChC,SAAS,KAAK;GACZ,IAAK,IAA8B,SAAS,UAC1C,MAAM,IAAI,MAAM,yBAAyB,SAAS,kDAAkD;GAEtG,MAAM;EACR;CACF;CAEA,OAAO;EAAE;EAAe;CAAa;AACvC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-DakpK96I.cjs");
|
|
2
|
+
const require_mcp_endpoint = require("./mcp-endpoint-BaV8h_lq.cjs");
|
|
2
3
|
let node_path = require("node:path");
|
|
3
4
|
node_path = require_chunk.__toESM(node_path, 1);
|
|
4
5
|
let node_fs_promises = require("node:fs/promises");
|
|
@@ -25,7 +26,7 @@ function workspaceJson(workspaceRoot) {
|
|
|
25
26
|
name: "Chain Insights Investigations",
|
|
26
27
|
workspace_root: workspaceRoot,
|
|
27
28
|
default_network: "bittensor",
|
|
28
|
-
graph_mcp_endpoint:
|
|
29
|
+
graph_mcp_endpoint: require_mcp_endpoint.LOCAL_GRAPH_MCP_ENDPOINT,
|
|
29
30
|
cases_dir: "cases",
|
|
30
31
|
imports_dir: "imports",
|
|
31
32
|
reports_dir: "reports",
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
let node_net = require("node:net");
|
|
2
|
+
//#region src/config/mcp-endpoint.ts
|
|
3
|
+
const LOOPBACK_HOSTS = new Set([
|
|
4
|
+
"localhost",
|
|
5
|
+
"127.0.0.1",
|
|
6
|
+
"::1",
|
|
7
|
+
"[::1]"
|
|
8
|
+
]);
|
|
9
|
+
const LOCAL_GRAPH_MCP_ENDPOINT = "http://127.0.0.1:8012/mcp";
|
|
10
|
+
const LOCAL_LEGACY_MCP_ENDPOINT = "http://127.0.0.1:4000";
|
|
11
|
+
function isLoopbackHost(hostname) {
|
|
12
|
+
const normalized = hostname.toLowerCase().replace(/^\[(.*)\]$/, "$1");
|
|
13
|
+
if (LOOPBACK_HOSTS.has(normalized)) return true;
|
|
14
|
+
if ((0, node_net.isIP)(normalized) !== 4) return false;
|
|
15
|
+
return normalized.split(".")[0] === "127";
|
|
16
|
+
}
|
|
17
|
+
function graphMcpEndpointEnvOverride() {
|
|
18
|
+
const envEndpoint = process.env.CHAIN_INSIGHTS_GRAPH_MCP_ENDPOINT?.trim() || process.env.GRAPH_MCP_ENDPOINT?.trim();
|
|
19
|
+
return envEndpoint ? validateMcpEndpoint(envEndpoint, "graphMcpEndpoint") : void 0;
|
|
20
|
+
}
|
|
21
|
+
function validateMcpEndpoint(value, key) {
|
|
22
|
+
const trimmed = value.trim();
|
|
23
|
+
if (!trimmed) throw new Error(`${key} must be a non-empty absolute URL.`);
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = new URL(trimmed);
|
|
27
|
+
} catch {
|
|
28
|
+
throw new Error(`${key} must be an absolute URL (example: https://mcp.example.com/mcp or http://127.0.0.1:8012/mcp).`);
|
|
29
|
+
}
|
|
30
|
+
if (parsed.username || parsed.password) throw new Error(`${key} must not include URL credentials.`);
|
|
31
|
+
if (parsed.search || parsed.hash) throw new Error(`${key} must not include query parameters or URL fragments.`);
|
|
32
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw new Error(`${key} must use either https:// or http://.`);
|
|
33
|
+
if (parsed.protocol === "http:" && !isLoopbackHost(parsed.hostname)) throw new Error(`${key} must use https:// for remote hosts. http:// is allowed only for localhost or loopback addresses.`);
|
|
34
|
+
return trimmed;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
Object.defineProperty(exports, "LOCAL_GRAPH_MCP_ENDPOINT", {
|
|
38
|
+
enumerable: true,
|
|
39
|
+
get: function() {
|
|
40
|
+
return LOCAL_GRAPH_MCP_ENDPOINT;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
Object.defineProperty(exports, "LOCAL_LEGACY_MCP_ENDPOINT", {
|
|
44
|
+
enumerable: true,
|
|
45
|
+
get: function() {
|
|
46
|
+
return LOCAL_LEGACY_MCP_ENDPOINT;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
Object.defineProperty(exports, "graphMcpEndpointEnvOverride", {
|
|
50
|
+
enumerable: true,
|
|
51
|
+
get: function() {
|
|
52
|
+
return graphMcpEndpointEnvOverride;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
Object.defineProperty(exports, "validateMcpEndpoint", {
|
|
56
|
+
enumerable: true,
|
|
57
|
+
get: function() {
|
|
58
|
+
return validateMcpEndpoint;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { isIP } from "node:net";
|
|
2
|
+
//#region src/config/mcp-endpoint.ts
|
|
3
|
+
const LOOPBACK_HOSTS = new Set([
|
|
4
|
+
"localhost",
|
|
5
|
+
"127.0.0.1",
|
|
6
|
+
"::1",
|
|
7
|
+
"[::1]"
|
|
8
|
+
]);
|
|
9
|
+
const LOCAL_GRAPH_MCP_ENDPOINT = "http://127.0.0.1:8012/mcp";
|
|
10
|
+
const LOCAL_LEGACY_MCP_ENDPOINT = "http://127.0.0.1:4000";
|
|
11
|
+
function isLoopbackHost(hostname) {
|
|
12
|
+
const normalized = hostname.toLowerCase().replace(/^\[(.*)\]$/, "$1");
|
|
13
|
+
if (LOOPBACK_HOSTS.has(normalized)) return true;
|
|
14
|
+
if (isIP(normalized) !== 4) return false;
|
|
15
|
+
return normalized.split(".")[0] === "127";
|
|
16
|
+
}
|
|
17
|
+
function graphMcpEndpointEnvOverride() {
|
|
18
|
+
const envEndpoint = process.env.CHAIN_INSIGHTS_GRAPH_MCP_ENDPOINT?.trim() || process.env.GRAPH_MCP_ENDPOINT?.trim();
|
|
19
|
+
return envEndpoint ? validateMcpEndpoint(envEndpoint, "graphMcpEndpoint") : void 0;
|
|
20
|
+
}
|
|
21
|
+
function validateMcpEndpoint(value, key) {
|
|
22
|
+
const trimmed = value.trim();
|
|
23
|
+
if (!trimmed) throw new Error(`${key} must be a non-empty absolute URL.`);
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = new URL(trimmed);
|
|
27
|
+
} catch {
|
|
28
|
+
throw new Error(`${key} must be an absolute URL (example: https://mcp.example.com/mcp or http://127.0.0.1:8012/mcp).`);
|
|
29
|
+
}
|
|
30
|
+
if (parsed.username || parsed.password) throw new Error(`${key} must not include URL credentials.`);
|
|
31
|
+
if (parsed.search || parsed.hash) throw new Error(`${key} must not include query parameters or URL fragments.`);
|
|
32
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw new Error(`${key} must use either https:// or http://.`);
|
|
33
|
+
if (parsed.protocol === "http:" && !isLoopbackHost(parsed.hostname)) throw new Error(`${key} must use https:// for remote hosts. http:// is allowed only for localhost or loopback addresses.`);
|
|
34
|
+
return trimmed;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { validateMcpEndpoint as i, LOCAL_LEGACY_MCP_ENDPOINT as n, graphMcpEndpointEnvOverride as r, LOCAL_GRAPH_MCP_ENDPOINT as t };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=mcp-endpoint-DHs1cRFH.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-endpoint-DHs1cRFH.mjs","names":[],"sources":["../src/config/mcp-endpoint.ts"],"sourcesContent":["import { isIP } from 'node:net'\n\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '::1', '[::1]'])\n\nexport const LOCAL_GRAPH_MCP_ENDPOINT = 'http://127.0.0.1:8012/mcp'\nexport const LOCAL_LEGACY_MCP_ENDPOINT = 'http://127.0.0.1:4000'\n\nfunction isLoopbackHost(hostname: string): boolean {\n const normalized = hostname.toLowerCase().replace(/^\\[(.*)\\]$/, '$1')\n if (LOOPBACK_HOSTS.has(normalized)) return true\n if (isIP(normalized) !== 4) return false\n return normalized.split('.')[0] === '127'\n}\n\nexport function graphMcpEndpointEnvOverride(): string | undefined {\n const envEndpoint = process.env.CHAIN_INSIGHTS_GRAPH_MCP_ENDPOINT?.trim()\n || process.env.GRAPH_MCP_ENDPOINT?.trim()\n return envEndpoint ? validateMcpEndpoint(envEndpoint, 'graphMcpEndpoint') : undefined\n}\n\nexport function validateMcpEndpoint(value: string, key: string): string {\n const trimmed = value.trim()\n if (!trimmed) {\n throw new Error(`${key} must be a non-empty absolute URL.`)\n }\n\n let parsed: URL\n try {\n parsed = new URL(trimmed)\n } catch {\n throw new Error(\n `${key} must be an absolute URL (example: https://mcp.example.com/mcp or http://127.0.0.1:8012/mcp).`,\n )\n }\n\n if (parsed.username || parsed.password) {\n throw new Error(`${key} must not include URL credentials.`)\n }\n if (parsed.search || parsed.hash) {\n throw new Error(`${key} must not include query parameters or URL fragments.`)\n }\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {\n throw new Error(`${key} must use either https:// or http://.`)\n }\n if (parsed.protocol === 'http:' && !isLoopbackHost(parsed.hostname)) {\n throw new Error(\n `${key} must use https:// for remote hosts. http:// is allowed only for localhost or loopback addresses.`,\n )\n }\n\n return trimmed\n}\n"],"mappings":";;AAEA,MAAM,iBAAiB,IAAI,IAAI;CAAC;CAAa;CAAa;CAAO;AAAO,CAAC;AAEzE,MAAa,2BAA2B;AACxC,MAAa,4BAA4B;AAEzC,SAAS,eAAe,UAA2B;CACjD,MAAM,aAAa,SAAS,YAAY,EAAE,QAAQ,cAAc,IAAI;CACpE,IAAI,eAAe,IAAI,UAAU,GAAG,OAAO;CAC3C,IAAI,KAAK,UAAU,MAAM,GAAG,OAAO;CACnC,OAAO,WAAW,MAAM,GAAG,EAAE,OAAO;AACtC;AAEA,SAAgB,8BAAkD;CAChE,MAAM,cAAc,QAAQ,IAAI,mCAAmC,KAAK,KACnE,QAAQ,IAAI,oBAAoB,KAAK;CAC1C,OAAO,cAAc,oBAAoB,aAAa,kBAAkB,IAAI,KAAA;AAC9E;AAEA,SAAgB,oBAAoB,OAAe,KAAqB;CACtE,MAAM,UAAU,MAAM,KAAK;CAC3B,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,GAAG,IAAI,mCAAmC;CAG5D,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,OAAO;CAC1B,QAAQ;EACN,MAAM,IAAI,MACR,GAAG,IAAI,8FACT;CACF;CAEA,IAAI,OAAO,YAAY,OAAO,UAC5B,MAAM,IAAI,MAAM,GAAG,IAAI,mCAAmC;CAE5D,IAAI,OAAO,UAAU,OAAO,MAC1B,MAAM,IAAI,MAAM,GAAG,IAAI,qDAAqD;CAE9E,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SACtD,MAAM,IAAI,MAAM,GAAG,IAAI,sCAAsC;CAE/D,IAAI,OAAO,aAAa,WAAW,CAAC,eAAe,OAAO,QAAQ,GAChE,MAAM,IAAI,MACR,GAAG,IAAI,kGACT;CAGF,OAAO;AACT"}
|
package/dist/mcp-proxy.cjs
CHANGED
|
@@ -33,6 +33,7 @@ const GRAPH_RESOURCE_URI = "ui://chain-insights/graph";
|
|
|
33
33
|
const GRAPH_APP_TOOL_NAMES = new Set([
|
|
34
34
|
"address_risk",
|
|
35
35
|
"scam_topology",
|
|
36
|
+
"stake_insights",
|
|
36
37
|
"track_funds"
|
|
37
38
|
]);
|
|
38
39
|
const GRAPH_ARRAY_KEYS = [
|
|
@@ -50,6 +51,7 @@ const KNOWN_PUBLIC_TOOL_REQUIRED_ARGS = {
|
|
|
50
51
|
"incident_timestamp_ms",
|
|
51
52
|
"network"
|
|
52
53
|
],
|
|
54
|
+
stake_insights: ["network"],
|
|
53
55
|
track_funds: ["trusted_addresses", "network"],
|
|
54
56
|
graph_query: ["query", "network"],
|
|
55
57
|
graph_query_batch: ["network", "queries"]
|
|
@@ -58,6 +60,7 @@ const KNOWN_PUBLIC_TOOL_DESCRIPTIONS = {
|
|
|
58
60
|
network_capabilities: "Return supported Chain Insights networks, capability layers, tool availability, data retention windows, and freshness. Use this before choosing network-specific tools.",
|
|
59
61
|
address_risk: "Screen one full blockchain address for AML risk, behavior patterns, neighborhood context, exchange exposure, and optional comparison with compare_address. This includes the exchange-behavior analysis formerly covered by money_flows_between_exchanges. Use this as the first tool for a single-address investigation. The tool returns an investigator-ready summary; preserve full addresses exactly.",
|
|
60
62
|
scam_topology: "Build victim-incident laundering topology from one victim/source address and the earliest known incident timestamp. Traversal uses one explicit activity policy: node_relative_only by default, or global_incident_only when requested. Repeated targets are kept as non-expanding convergence edges. Returns ML-ready scam_labels plus review context and a track_funds-compatible graph report: primary flows, deposits, reverse_leads. Victims, exchange endpoints, and generic labeled context nodes are not automatic scam labels; preserve full addresses exactly.",
|
|
63
|
+
stake_insights: "Explain Bittensor staking behavior around one full address, coldkey, or hotkey. Requires network plus exactly one of address, coldkey, or hotkey. Returns net staked/unstaked amounts, active coldkey-hotkey-netuid relationships, aggregate stake movement amounts, top counterparties, first/last activity, source backend, query evidence, and optional graph report metadata.",
|
|
61
64
|
track_funds: "Trace funds from trusted victim/source addresses through intermediaries to exchange deposit addresses. Use this when the user has a victim/source address or known untrusted/scammer addresses. The tool returns an investigator-ready fund-flow report and recommended next actions.",
|
|
62
65
|
graph_query: "Run a read-only GQL/Cypher query through the Chain Insights graph endpoint. Use USE live_topology for recent topology, USE archive_topology for historical topology, and USE facts for labels, features, risk scores, assets, and enrichment. Cross-layer correlated joins may be limited by the active graph endpoint; preserve full addresses exactly.",
|
|
63
66
|
graph_query_batch: "Run multiple read-only GQL/Cypher queries through the Chain Insights graph endpoint in one paid batch. Prefer this for related topology/facts reads."
|
|
@@ -68,7 +71,7 @@ const CHAIN_INSIGHTS_WORKFLOW = [
|
|
|
68
71
|
"Workflow:",
|
|
69
72
|
"1. If the user is starting or continuing an investigation, use case_open or case_list/case_resume first.",
|
|
70
73
|
"2. Do not call investigation tools until required arguments are known. Network is required; use network_capabilities to check supported networks, data layers, retention, and freshness, or ask the user if missing.",
|
|
71
|
-
"3. Use address_risk first for a single address when facts and topology are available. Use track_funds for victim/source fund tracing when topology is available. Use scam_topology when known victim incident ground truth should become ML-ready scam labels. Use graph_query(_batch) for the universal graph-language path over topology and facts.",
|
|
74
|
+
"3. Use address_risk first for a single address when facts and topology are available. Use stake_insights for Bittensor coldkey/hotkey staking behavior. Use track_funds for victim/source fund tracing when topology is available. Use scam_topology when known victim incident ground truth should become ML-ready scam labels. Use graph_query(_batch) for the universal graph-language path over topology and facts.",
|
|
72
75
|
"4. After a material result, preserve it with case_add_evidence when a case is active or ask whether to create/select a case.",
|
|
73
76
|
"5. Use case_update_dossier for durable address/entity findings and case_start_session/case_end_session for session notes."
|
|
74
77
|
].join("\n");
|
|
@@ -158,6 +161,19 @@ function knownPublicToolInputSchema(toolName) {
|
|
|
158
161
|
activity_policy: zod.enum(["node_relative_only", "global_incident_only"]).optional().describe("Traversal activity policy. Default node_relative_only."),
|
|
159
162
|
case_id: zod.string().optional().describe("Optional Chain Insights case ID. When provided, compact evidence is appended to the case manifest.")
|
|
160
163
|
};
|
|
164
|
+
case "stake_insights": return {
|
|
165
|
+
network: zod.string().min(1).describe(NETWORK_DESCRIPTION),
|
|
166
|
+
address: zod.string().optional().describe("Full Bittensor address to inspect as either coldkey or hotkey. Provide exactly one of address, coldkey, or hotkey."),
|
|
167
|
+
coldkey: zod.string().optional().describe("Full Bittensor coldkey address to inspect. Provide exactly one of address, coldkey, or hotkey."),
|
|
168
|
+
hotkey: zod.string().optional().describe("Full Bittensor hotkey address to inspect. Provide exactly one of address, coldkey, or hotkey."),
|
|
169
|
+
netuid: zod.number().int().min(0).optional().describe("Optional subnet netuid filter."),
|
|
170
|
+
start_timestamp_ms: zod.number().min(0).optional().describe("Optional inclusive lower activity timestamp bound in milliseconds."),
|
|
171
|
+
end_timestamp_ms: zod.number().min(0).optional().describe("Optional inclusive upper activity timestamp bound in milliseconds."),
|
|
172
|
+
start_block: zod.number().int().min(0).optional().describe("Optional start block. Current stake graph parity may require timestamp windows instead."),
|
|
173
|
+
end_block: zod.number().int().min(0).optional().describe("Optional end block. Current stake graph parity may require timestamp windows instead."),
|
|
174
|
+
depth: zod.number().int().min(1).max(3).optional().describe("Optional expansion depth limit. First release returns direct STAKES_IN relationships; default 1, max 3."),
|
|
175
|
+
include_attachments: zod.boolean().optional().describe("Include graph app report metadata")
|
|
176
|
+
};
|
|
161
177
|
case "graph_query": return {
|
|
162
178
|
query: zod.string().min(1).describe("Read-only GQL/Cypher query. Use USE live_topology for recent topology, USE archive_topology for historical topology, and USE facts for labels, features, risk scores, assets, and enrichment."),
|
|
163
179
|
network: zod.string().min(1).describe(NETWORK_DESCRIPTION)
|
|
@@ -531,7 +547,7 @@ async function normalizeRemoteToolResult(result, config, toolName = "remote-grap
|
|
|
531
547
|
const meta = { ...result._meta ?? {} };
|
|
532
548
|
if (graphPayload) {
|
|
533
549
|
const { writeGraphReport } = await Promise.resolve().then(() => require("./graph-reports-B3mkLP8Z.cjs"));
|
|
534
|
-
const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-
|
|
550
|
+
const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-XbN16DwU.cjs"));
|
|
535
551
|
const report = await writeGraphReport(graphPayload, {
|
|
536
552
|
serverPort: config.serverPort,
|
|
537
553
|
slug: toolName || "remote-graph"
|
|
@@ -560,7 +576,7 @@ async function normalizeRemoteToolResult(result, config, toolName = "remote-grap
|
|
|
560
576
|
* All diagnostic output goes to console.error() or process.stderr.write().
|
|
561
577
|
*/
|
|
562
578
|
async function createProxy() {
|
|
563
|
-
const { loadConfig } = await Promise.resolve().then(() => require("./config-
|
|
579
|
+
const { loadConfig } = await Promise.resolve().then(() => require("./config-BwVx19Og.cjs")).then((n) => n.config_exports);
|
|
564
580
|
const { activeDataDir, findActiveWorkspace } = await Promise.resolve().then(() => require("./active-BVr55kvW.cjs")).then((n) => n.active_exports);
|
|
565
581
|
const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await Promise.resolve().then(() => require("./client-DPc2eyVN.cjs")).then((n) => n.client_exports);
|
|
566
582
|
const { loadSchema, saveSchema } = await Promise.resolve().then(() => require("./schema-cache-CJk1EL3L.cjs"));
|
|
@@ -979,9 +995,9 @@ async function createProxy() {
|
|
|
979
995
|
}],
|
|
980
996
|
isError: true
|
|
981
997
|
};
|
|
982
|
-
const { addressRisk } = await Promise.resolve().then(() => require("./public-tools-
|
|
998
|
+
const { addressRisk } = await Promise.resolve().then(() => require("./public-tools-BC1fi0DV.cjs"));
|
|
983
999
|
const { writeGraphReport } = await Promise.resolve().then(() => require("./graph-reports-B3mkLP8Z.cjs"));
|
|
984
|
-
const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-
|
|
1000
|
+
const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-XbN16DwU.cjs"));
|
|
985
1001
|
const result = await addressRisk(remoteClient, {
|
|
986
1002
|
address,
|
|
987
1003
|
network,
|
|
@@ -1050,9 +1066,9 @@ async function createProxy() {
|
|
|
1050
1066
|
}],
|
|
1051
1067
|
isError: true
|
|
1052
1068
|
};
|
|
1053
|
-
const { trackFunds } = await Promise.resolve().then(() => require("./public-tools-
|
|
1069
|
+
const { trackFunds } = await Promise.resolve().then(() => require("./public-tools-BC1fi0DV.cjs"));
|
|
1054
1070
|
const { writeGraphReport } = await Promise.resolve().then(() => require("./graph-reports-B3mkLP8Z.cjs"));
|
|
1055
|
-
const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-
|
|
1071
|
+
const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-XbN16DwU.cjs"));
|
|
1056
1072
|
const result = await trackFunds(remoteClient, config, {
|
|
1057
1073
|
trustedAddresses: trusted_addresses,
|
|
1058
1074
|
untrustedAddresses: untrusted_addresses,
|
|
@@ -1123,9 +1139,9 @@ async function createProxy() {
|
|
|
1123
1139
|
}],
|
|
1124
1140
|
isError: true
|
|
1125
1141
|
};
|
|
1126
|
-
const { scamTopology } = await Promise.resolve().then(() => require("./public-tools-
|
|
1142
|
+
const { scamTopology } = await Promise.resolve().then(() => require("./public-tools-BC1fi0DV.cjs"));
|
|
1127
1143
|
const { writeGraphReport } = await Promise.resolve().then(() => require("./graph-reports-B3mkLP8Z.cjs"));
|
|
1128
|
-
const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-
|
|
1144
|
+
const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-XbN16DwU.cjs"));
|
|
1129
1145
|
const result = await scamTopology(remoteClient, config, {
|
|
1130
1146
|
victimAddress: victim_address,
|
|
1131
1147
|
network,
|
|
@@ -1168,6 +1184,88 @@ async function createProxy() {
|
|
|
1168
1184
|
};
|
|
1169
1185
|
}
|
|
1170
1186
|
});
|
|
1187
|
+
if (!remoteToolNames.has("stake_insights")) (0, _modelcontextprotocol_ext_apps_server.registerAppTool)(server, "stake_insights", {
|
|
1188
|
+
title: "Stake Insights",
|
|
1189
|
+
description: KNOWN_PUBLIC_TOOL_DESCRIPTIONS.stake_insights,
|
|
1190
|
+
inputSchema: {
|
|
1191
|
+
network: zod.string().min(1).describe(NETWORK_DESCRIPTION),
|
|
1192
|
+
address: zod.string().optional().describe("Full Bittensor address to inspect as either coldkey or hotkey. Provide exactly one of address, coldkey, or hotkey."),
|
|
1193
|
+
coldkey: zod.string().optional().describe("Full Bittensor coldkey address to inspect. Provide exactly one of address, coldkey, or hotkey."),
|
|
1194
|
+
hotkey: zod.string().optional().describe("Full Bittensor hotkey address to inspect. Provide exactly one of address, coldkey, or hotkey."),
|
|
1195
|
+
netuid: zod.number().int().min(0).optional().describe("Optional subnet netuid filter."),
|
|
1196
|
+
start_timestamp_ms: zod.number().min(0).optional().describe("Optional inclusive lower activity timestamp bound in milliseconds."),
|
|
1197
|
+
end_timestamp_ms: zod.number().min(0).optional().describe("Optional inclusive upper activity timestamp bound in milliseconds."),
|
|
1198
|
+
start_block: zod.number().int().min(0).optional().describe("Optional start block. Current stake graph parity may require timestamp windows instead."),
|
|
1199
|
+
end_block: zod.number().int().min(0).optional().describe("Optional end block. Current stake graph parity may require timestamp windows instead."),
|
|
1200
|
+
depth: zod.number().int().min(1).max(3).optional().describe("Optional expansion depth limit. First release returns direct STAKES_IN relationships; default 1, max 3."),
|
|
1201
|
+
include_attachments: zod.boolean().optional().describe("Include graph app report metadata")
|
|
1202
|
+
},
|
|
1203
|
+
_meta: { ui: { resourceUri: GRAPH_RESOURCE_URI } },
|
|
1204
|
+
annotations: {
|
|
1205
|
+
readOnlyHint: true,
|
|
1206
|
+
destructiveHint: false,
|
|
1207
|
+
idempotentHint: true,
|
|
1208
|
+
openWorldHint: true
|
|
1209
|
+
}
|
|
1210
|
+
}, async ({ network, address, coldkey, hotkey, netuid, start_timestamp_ms, end_timestamp_ms, start_block, end_block, depth }) => {
|
|
1211
|
+
try {
|
|
1212
|
+
if (!remoteConnected) return {
|
|
1213
|
+
content: [{
|
|
1214
|
+
type: "text",
|
|
1215
|
+
text: `${remoteUnavailableMessage ?? `Graph MCP is not connected at ${graphMcpEndpoint}`}. Restart the Chain Insights MCP proxy after the endpoint is reachable.`
|
|
1216
|
+
}],
|
|
1217
|
+
isError: true
|
|
1218
|
+
};
|
|
1219
|
+
const { stakeInsights } = await Promise.resolve().then(() => require("./public-tools-BC1fi0DV.cjs"));
|
|
1220
|
+
const { writeGraphReport } = await Promise.resolve().then(() => require("./graph-reports-B3mkLP8Z.cjs"));
|
|
1221
|
+
const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-XbN16DwU.cjs"));
|
|
1222
|
+
const result = await stakeInsights(remoteClient, {
|
|
1223
|
+
network,
|
|
1224
|
+
address,
|
|
1225
|
+
coldkey,
|
|
1226
|
+
hotkey,
|
|
1227
|
+
netuid,
|
|
1228
|
+
startTimestampMs: start_timestamp_ms,
|
|
1229
|
+
endTimestampMs: end_timestamp_ms,
|
|
1230
|
+
startBlock: start_block,
|
|
1231
|
+
endBlock: end_block,
|
|
1232
|
+
depth
|
|
1233
|
+
});
|
|
1234
|
+
const subject = address ?? coldkey ?? hotkey ?? "subject";
|
|
1235
|
+
const report = await writeGraphReport(result.graphData, {
|
|
1236
|
+
serverPort: config.serverPort,
|
|
1237
|
+
slug: `stake-insights-${network}-${subject}`
|
|
1238
|
+
});
|
|
1239
|
+
await ensureArtifactServer(config.serverPort);
|
|
1240
|
+
return {
|
|
1241
|
+
content: [{
|
|
1242
|
+
type: "text",
|
|
1243
|
+
text: result.summaryText
|
|
1244
|
+
}],
|
|
1245
|
+
structuredContent: result.structuredContent,
|
|
1246
|
+
_meta: { chainInsights: { graph: {
|
|
1247
|
+
schema: report.schema,
|
|
1248
|
+
url: report.url
|
|
1249
|
+
} } },
|
|
1250
|
+
isError: false
|
|
1251
|
+
};
|
|
1252
|
+
} catch (err) {
|
|
1253
|
+
if (err instanceof require_client.PaymentRequiredError) return {
|
|
1254
|
+
content: [{
|
|
1255
|
+
type: "text",
|
|
1256
|
+
text: err.message
|
|
1257
|
+
}],
|
|
1258
|
+
isError: true
|
|
1259
|
+
};
|
|
1260
|
+
return {
|
|
1261
|
+
content: [{
|
|
1262
|
+
type: "text",
|
|
1263
|
+
text: `Stake insights failed: ${err.message}`
|
|
1264
|
+
}],
|
|
1265
|
+
isError: true
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
});
|
|
1171
1269
|
server.registerTool("help", {
|
|
1172
1270
|
description: "Show Chain Insights overview, available tools, and investigation workflow.",
|
|
1173
1271
|
inputSchema: zod.object({}).passthrough()
|
|
@@ -1182,6 +1280,7 @@ async function createProxy() {
|
|
|
1182
1280
|
"Investigation tools:",
|
|
1183
1281
|
"- network_capabilities: inspect supported networks, data layers, tool availability, retention windows, and freshness.",
|
|
1184
1282
|
"- address_risk: screen a full address for AML risk, behavior, neighborhood, exchange exposure, and optional compare_address connection checks.",
|
|
1283
|
+
"- stake_insights: explain Bittensor staking around one address, coldkey, or hotkey with net stake, movement amounts, counterparties, backend, and query evidence.",
|
|
1185
1284
|
"- track_funds: trace up to five trusted/victim addresses plus up to five known untrusted/scammer addresses through intermediaries to exchange deposit addresses.",
|
|
1186
1285
|
"- scam_topology: derive ML-ready scam_labels from one victim incident address and incident_timestamp_ms.",
|
|
1187
1286
|
"- graph_query: run read-only GQL/Cypher through the universal graph endpoint. Use USE live_topology, USE archive_topology, or USE facts.",
|
package/dist/mcp-proxy.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-proxy.d.cts","names":[],"sources":["../src/mcp/proxy.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"mcp-proxy.d.cts","names":[],"sources":["../src/mcp/proxy.ts"],"mappings":";;AA6xBA;;;;AAA4C;;iBAAtB,WAAA,CAAA,GAAe,OAAO"}
|
package/dist/mcp-proxy.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-proxy.d.mts","names":[],"sources":["../src/mcp/proxy.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"mcp-proxy.d.mts","names":[],"sources":["../src/mcp/proxy.ts"],"mappings":";;AA6xBA;;;;AAA4C;;iBAAtB,WAAA,CAAA,GAAe,OAAO"}
|