chain-insights 0.68.2 → 0.69.0
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 +1 -0
- package/dist/cli.cjs +30 -4
- package/dist/cli.mjs +30 -4
- package/dist/cli.mjs.map +1 -1
- package/dist/dex-registry-05wAhSa0.mjs +383 -0
- package/dist/dex-registry-05wAhSa0.mjs.map +1 -0
- package/dist/dex-registry-KOTJhAQf.cjs +411 -0
- package/dist/dex-tool-DIKN4dXB.cjs +536 -0
- package/dist/dex-tool-DpQKS7Te.mjs +497 -0
- package/dist/dex-tool-DpQKS7Te.mjs.map +1 -0
- package/dist/{format-DB2IH2U5.mjs → format-CSesEBvS.mjs} +4 -4
- package/dist/format-CSesEBvS.mjs.map +1 -0
- package/dist/{format-BjZuYDr8.cjs → format-DfhRQq4L.cjs} +3 -3
- package/dist/mcp-proxy.cjs +53 -59
- package/dist/mcp-proxy.d.cts +2 -0
- package/dist/mcp-proxy.d.cts.map +1 -1
- package/dist/mcp-proxy.d.mts +2 -0
- package/dist/mcp-proxy.d.mts.map +1 -1
- package/dist/mcp-proxy.mjs +51 -59
- package/dist/mcp-proxy.mjs.map +1 -1
- package/dist/{tool-visibility-B4RAFMaB.cjs → tool-visibility-BopFnGsT.cjs} +5 -0
- package/dist/{tool-visibility-kclgGKL5.mjs → tool-visibility-Dq-Ruj-8.mjs} +6 -1
- package/dist/tool-visibility-Dq-Ruj-8.mjs.map +1 -0
- package/docs/mcp-proxy.md +71 -0
- package/docs/stability.md +3 -2
- package/package.json +3 -2
- package/registry/README.md +133 -0
- package/registry/robinhood.json +769 -0
- package/registry/robinhood.md +102 -0
- package/skills/chain-insights-dex/SKILL.md +104 -96
- package/skills/chain-insights-dex/agents/openai.yaml +1 -1
- package/skills/chain-insights-dex/reference.md +8 -27
- package/skills/chain-insights-dex-gauge/SKILL.md +6 -4
- package/skills/chain-insights-dex-launchpad/SKILL.md +6 -3
- package/skills/chain-insights-dex-vault/SKILL.md +4 -2
- package/skills/chain-insights-virtuals/SKILL.md +8 -4
- package/dist/format-DB2IH2U5.mjs.map +0 -1
- package/dist/tool-visibility-kclgGKL5.mjs.map +0 -1
package/dist/mcp-proxy.cjs
CHANGED
|
@@ -2,12 +2,14 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
|
|
3
3
|
const require_print_result = require("./print-result-qVhhuSdo.cjs");
|
|
4
4
|
const require_client = require("./client-Ypw9BNbn.cjs");
|
|
5
|
-
const require_tool_visibility = require("./tool-visibility-
|
|
5
|
+
const require_tool_visibility = require("./tool-visibility-BopFnGsT.cjs");
|
|
6
6
|
const require_usage_status = require("./usage-status-DonwoLR3.cjs");
|
|
7
7
|
const require_subscription_status = require("./subscription-status-ru99UCPZ.cjs");
|
|
8
8
|
const require_capabilities = require("./capabilities-DGavTSoW.cjs");
|
|
9
9
|
const require_graph_schema = require("./graph-schema-C_7aZ8RL.cjs");
|
|
10
10
|
const require_action_log = require("./action-log-HniMMF7f.cjs");
|
|
11
|
+
const require_dex_tool = require("./dex-tool-DIKN4dXB.cjs");
|
|
12
|
+
const require_dex_registry = require("./dex-registry-KOTJhAQf.cjs");
|
|
11
13
|
let node_url = require("node:url");
|
|
12
14
|
let node_path = require("node:path");
|
|
13
15
|
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
@@ -101,17 +103,17 @@ function rateLimitedResult(toolName, err) {
|
|
|
101
103
|
const BUSY_CODE = "chain_busy";
|
|
102
104
|
const BUSY_WAIT_MS = 1100;
|
|
103
105
|
const defaultSleep = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
104
|
-
function isRecord$
|
|
106
|
+
function isRecord$2(value) {
|
|
105
107
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
106
108
|
}
|
|
107
109
|
function membersOf(result) {
|
|
108
|
-
const facts = isRecord$
|
|
109
|
-
const queries = isRecord$
|
|
110
|
-
return Array.isArray(queries) && queries.every(isRecord$
|
|
110
|
+
const facts = isRecord$2(result.structuredContent) ? result.structuredContent.facts : void 0;
|
|
111
|
+
const queries = isRecord$2(facts) ? facts.queries : void 0;
|
|
112
|
+
return Array.isArray(queries) && queries.every(isRecord$2) ? queries : null;
|
|
111
113
|
}
|
|
112
114
|
function isBusyMember(member) {
|
|
113
115
|
const detail = member.error_detail;
|
|
114
|
-
return isRecord$
|
|
116
|
+
return isRecord$2(detail) && detail.code === BUSY_CODE;
|
|
115
117
|
}
|
|
116
118
|
/** The ids of the members the server refused as chain_busy. */
|
|
117
119
|
function busyMemberIds(result) {
|
|
@@ -142,7 +144,7 @@ function mergeBatchAnswers(first, again) {
|
|
|
142
144
|
const merged = members.map((m) => isBusyMember(m) && byId.has(String(m.id)) ? byId.get(String(m.id)) : m);
|
|
143
145
|
const structured = first.structuredContent;
|
|
144
146
|
const facts = structured.facts;
|
|
145
|
-
const batch = isRecord$
|
|
147
|
+
const batch = isRecord$2(facts.batch) ? recount(facts.batch, merged) : facts.batch;
|
|
146
148
|
const nextStructured = {
|
|
147
149
|
...structured,
|
|
148
150
|
facts: {
|
|
@@ -186,7 +188,7 @@ async function withBusyMemberRetry(args, call, sleep = defaultSleep) {
|
|
|
186
188
|
function isBusyResult(result) {
|
|
187
189
|
if (!result.isError) return false;
|
|
188
190
|
const first = Array.isArray(result.content) ? result.content[0] : void 0;
|
|
189
|
-
const text = isRecord$
|
|
191
|
+
const text = isRecord$2(first) && typeof first.text === "string" ? first.text : "";
|
|
190
192
|
return /^(Query execution failed: )?chain_busy\b/.test(text.trim());
|
|
191
193
|
}
|
|
192
194
|
/**
|
|
@@ -333,7 +335,7 @@ function send(res, status, contentType, body) {
|
|
|
333
335
|
function sendJson(res, status, value) {
|
|
334
336
|
send(res, status, "application/json; charset=utf-8", JSON.stringify(value));
|
|
335
337
|
}
|
|
336
|
-
function isRecord$
|
|
338
|
+
function isRecord$1(value) {
|
|
337
339
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
338
340
|
}
|
|
339
341
|
var BodyTooLarge = class extends Error {};
|
|
@@ -424,8 +426,8 @@ async function startViewServer(options) {
|
|
|
424
426
|
sendJson(res, 400, { error: "invalid JSON" });
|
|
425
427
|
return;
|
|
426
428
|
}
|
|
427
|
-
const args = isRecord$
|
|
428
|
-
if (!isRecord$
|
|
429
|
+
const args = isRecord$1(body) ? body.arguments ?? {} : void 0;
|
|
430
|
+
if (!isRecord$1(args)) {
|
|
429
431
|
sendJson(res, 400, { error: "arguments must be an object" });
|
|
430
432
|
return;
|
|
431
433
|
}
|
|
@@ -606,52 +608,6 @@ async function connectRemote(options) {
|
|
|
606
608
|
};
|
|
607
609
|
}
|
|
608
610
|
//#endregion
|
|
609
|
-
//#region src/mcp/answer-hint.ts
|
|
610
|
-
/**
|
|
611
|
-
* The two answer rules that still slip in long answers, carried on every
|
|
612
|
-
* graph reply in the result's `hint` field. Claude Code has a Stop hook that
|
|
613
|
-
* reads the answer; Codex and Claude Desktop have no such hook, and in the
|
|
614
|
-
* loop's third prompt set they broke one of these two rules in three of
|
|
615
|
-
* thirteen conversations. The reply is the one thing every host hands the
|
|
616
|
-
* model right before it writes, so the rules ride on it. A `hint` the
|
|
617
|
-
* server already set is kept.
|
|
618
|
-
*/
|
|
619
|
-
const ANSWER_HINT = "Answer: write every address in full (0x plus 40 characters), also when it repeats one named earlier and also when the user asks for shorter ones (name them instead); a transfer moves tokens, so write \"tokens worth about N USD\", never \"sent N USD\" or a $ figure; never draw the flows in text, ASCII or Mermaid, the host draws them.";
|
|
620
|
-
function isRecord$1(value) {
|
|
621
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
622
|
-
}
|
|
623
|
-
/** The structured content with the hint set when the server left it empty. */
|
|
624
|
-
function withAnswerHint(structuredContent) {
|
|
625
|
-
if (!isRecord$1(structuredContent) || !isRecord$1(structuredContent.facts)) return structuredContent;
|
|
626
|
-
if (typeof structuredContent.hint === "string" && structuredContent.hint.trim()) return structuredContent;
|
|
627
|
-
return {
|
|
628
|
-
...structuredContent,
|
|
629
|
-
hint: ANSWER_HINT
|
|
630
|
-
};
|
|
631
|
-
}
|
|
632
|
-
/** A graph tool result whose structured content and single JSON text block both carry the hint. */
|
|
633
|
-
function withAnswerHintResult(result) {
|
|
634
|
-
const structured = withAnswerHint(result.structuredContent);
|
|
635
|
-
if (structured === result.structuredContent) return result;
|
|
636
|
-
const content = result.content ?? [];
|
|
637
|
-
const only = content.length === 1 && content[0]?.type === "text" ? content[0] : null;
|
|
638
|
-
let parsed = void 0;
|
|
639
|
-
try {
|
|
640
|
-
parsed = only?.text ? JSON.parse(only.text) : void 0;
|
|
641
|
-
} catch {
|
|
642
|
-
parsed = void 0;
|
|
643
|
-
}
|
|
644
|
-
const textIsStructured = parsed !== void 0 && JSON.stringify(parsed) === JSON.stringify(result.structuredContent);
|
|
645
|
-
return {
|
|
646
|
-
...result,
|
|
647
|
-
structuredContent: structured,
|
|
648
|
-
content: textIsStructured ? [{
|
|
649
|
-
...only,
|
|
650
|
-
text: JSON.stringify(structured)
|
|
651
|
-
}] : content
|
|
652
|
-
};
|
|
653
|
-
}
|
|
654
|
-
//#endregion
|
|
655
611
|
//#region src/mcp/proxy.ts
|
|
656
612
|
const LOCAL_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
657
613
|
"meta_network_capabilities",
|
|
@@ -660,6 +616,7 @@ const LOCAL_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
660
616
|
"meta_subscription_status",
|
|
661
617
|
"meta_help",
|
|
662
618
|
"meta_rules",
|
|
619
|
+
require_dex_tool.META_DEX_LOOKUP_TOOL,
|
|
663
620
|
"wallet_balance",
|
|
664
621
|
"wallet_topup",
|
|
665
622
|
require_graph_schema.GRAPH_EXPAND_TOOL
|
|
@@ -717,15 +674,17 @@ function resolveMcpProxyMode(env = process.env) {
|
|
|
717
674
|
}
|
|
718
675
|
const GRAPH_LAYERS_TEXT = "Use USE topology for topology (address/FLOWS_TO/OPERATED_BY/LINKED graph with SWAPPED, ADDED_LIQUIDITY, REMOVED_LIQUIDITY, BRIDGED and the Pool, Token and NFT labels, unified recent+historical) and USE facts for bounded TRANSFER, SWAP, LIQUIDITY_ADD, LIQUIDITY_REMOVE and BRIDGE_CROSSING rows and enrichment, and USE chain for the chain node's own record of one known transaction, block, address or the head.";
|
|
719
676
|
const TOOL_RULES_FIRST = "Rules first: if the chain-insights-cypher skill is not loaded in this chat, call meta_rules once before this tool. For a graph, alias the columns from_address and to_address; the host draws the picture from the answer itself. Never draw it again with another tool, such as a visualize or widget tool.";
|
|
677
|
+
const GRAPH_QUERY_DEX_DOOR_SENTENCE = "A question that names an exchange and no address starts with meta_dex_lookup, never with a scan of :Protocol, :Pool or labels.";
|
|
720
678
|
const KNOWN_PUBLIC_TOOL_DESCRIPTIONS = {
|
|
721
679
|
meta_network_capabilities: "Return the current Chain Insights network and tool support matrix. Takes no arguments: send {}.",
|
|
722
680
|
meta_schema: require_graph_schema.META_SCHEMA_DESCRIPTION,
|
|
681
|
+
[require_dex_tool.META_DEX_LOOKUP_TOOL]: require_dex_tool.META_DEX_LOOKUP_DESCRIPTION,
|
|
723
682
|
meta_usage_status: "Return the caller's public free graph_query quota for the current UTC day.",
|
|
724
683
|
meta_subscription_status: "Return the caller's CIA subscription window end, daily allowance, consumption, and tier.",
|
|
725
684
|
meta_help: "Show a short guide to Chain Insights tools and workflow.",
|
|
726
685
|
meta_rules: "Return the Chain Insights rules: how to write graph_query and graph_query_batch reads that are served, how to name columns so the answer is drawn as a graph, a time series or a table, and how to write the answer. Call it once per chat, before the first graph query, when the chain-insights-cypher skill is not loaded. Takes no arguments: send {}.",
|
|
727
686
|
wallet_balance: "Show the local Chain Insights payment wallet address, payment network, token, and amount.",
|
|
728
|
-
graph_query: `${TOOL_RULES_FIRST} Run a read-only GQL/Cypher query through the Chain Insights graph endpoint. ${GRAPH_LAYERS_TEXT} ${ROUTING_NOT_SERVED} Preserve full addresses exactly, and write every address in full, all 42 characters, in your answer: never shorten one with ... or ….`,
|
|
687
|
+
graph_query: `${TOOL_RULES_FIRST} Run a read-only GQL/Cypher query through the Chain Insights graph endpoint. ${GRAPH_LAYERS_TEXT} ${GRAPH_QUERY_DEX_DOOR_SENTENCE} ${ROUTING_NOT_SERVED} Preserve full addresses exactly, and write every address in full, all 42 characters, in your answer: never shorten one with ... or ….`,
|
|
729
688
|
graph_query_batch: `${TOOL_RULES_FIRST} Run multiple read-only GQL/Cypher queries through the Chain Insights graph endpoint in one paid batch. Prefer this for related topology/facts reads. For a time series across days, send one USE facts total per day with the query id set to the day, "YYYY-MM-DD": the view joins them into one series. Write every address in full in your answer, all 42 characters.`
|
|
730
689
|
};
|
|
731
690
|
const KNOWN_PUBLIC_TOOL_TITLES = {
|
|
@@ -1184,7 +1143,7 @@ function normalizeRemoteToolResult(toolName, result) {
|
|
|
1184
1143
|
_meta: sanitizeRemoteMeta(result._meta),
|
|
1185
1144
|
isError: result.isError
|
|
1186
1145
|
};
|
|
1187
|
-
return toolName === "graph_query" || toolName === "graph_query_batch" ? require_print_result.withTimestampTextResult(withAnswerHintResult(normalized)) : normalized;
|
|
1146
|
+
return toolName === "graph_query" || toolName === "graph_query_batch" ? require_print_result.withTimestampTextResult(require_dex_tool.withAnswerHintResult(normalized)) : normalized;
|
|
1188
1147
|
}
|
|
1189
1148
|
function cleanNetworkCapabilities(value) {
|
|
1190
1149
|
const structuredContent = isRecord(value) ? value.structuredContent : void 0;
|
|
@@ -1421,6 +1380,37 @@ async function createProxy() {
|
|
|
1421
1380
|
openWorldHint: true
|
|
1422
1381
|
}
|
|
1423
1382
|
}, async (args) => require_graph_schema.handleMetaSchema(args, schemaDependencies));
|
|
1383
|
+
const dexDependencies = {
|
|
1384
|
+
loadRegistry: (network) => require_dex_registry.loadRegistry(network, { log: (line) => {
|
|
1385
|
+
logger.info("dex_registry.fault", { line });
|
|
1386
|
+
} }),
|
|
1387
|
+
graphQueryBatch: async (args) => {
|
|
1388
|
+
const options = remoteToolRequestOptions("graph_query_batch");
|
|
1389
|
+
const call = (requestArgs) => withRateLimitRetry(() => options ? remoteClient.callTool({
|
|
1390
|
+
name: "graph_query_batch",
|
|
1391
|
+
arguments: requestArgs
|
|
1392
|
+
}, void 0, options) : remoteClient.callTool({
|
|
1393
|
+
name: "graph_query_batch",
|
|
1394
|
+
arguments: requestArgs
|
|
1395
|
+
}));
|
|
1396
|
+
return await withBusyMemberRetry(args, call);
|
|
1397
|
+
},
|
|
1398
|
+
unavailable: async () => {
|
|
1399
|
+
await ensureRemote();
|
|
1400
|
+
return remoteConnected ? void 0 : `${remoteUnavailableMessage ?? `Chain Insights Graph is not connected at ${graphMcpEndpoint}`}.`;
|
|
1401
|
+
},
|
|
1402
|
+
describeFailure: (err) => remoteCallFailureText("graph_query_batch", err)
|
|
1403
|
+
};
|
|
1404
|
+
server.registerTool(require_dex_tool.META_DEX_LOOKUP_TOOL, {
|
|
1405
|
+
title: require_dex_tool.META_DEX_LOOKUP_TITLE,
|
|
1406
|
+
description: KNOWN_PUBLIC_TOOL_DESCRIPTIONS[require_dex_tool.META_DEX_LOOKUP_TOOL],
|
|
1407
|
+
inputSchema: {
|
|
1408
|
+
network: NETWORK_SCHEMA,
|
|
1409
|
+
name: zod.string().optional().describe("The exchange as the user wrote it: Aero, Aerodrome, uni v4. With no name, every covered exchange is listed."),
|
|
1410
|
+
pools: zod.boolean().optional().describe("Read the biggest pools of the exchange (default true). false sends only Head and the contract reads.")
|
|
1411
|
+
},
|
|
1412
|
+
annotations: require_dex_tool.META_DEX_LOOKUP_ANNOTATIONS
|
|
1413
|
+
}, async (args) => require_dex_tool.handleMetaDexLookup(args, dexDependencies));
|
|
1424
1414
|
server.registerTool("meta_usage_status", {
|
|
1425
1415
|
title: "Usage Status",
|
|
1426
1416
|
description: KNOWN_PUBLIC_TOOL_DESCRIPTIONS.meta_usage_status,
|
|
@@ -1664,6 +1654,7 @@ async function createProxy() {
|
|
|
1664
1654
|
"Investigation tools:",
|
|
1665
1655
|
"- meta_network_capabilities: supported networks and available tools. Takes no arguments.",
|
|
1666
1656
|
"- meta_schema: read the live graph schema of one network: labels, link types and field names. Cached for 24 hours.",
|
|
1657
|
+
"- meta_dex_lookup: turn an exchange name (Aero, Uniswap, Virtuals) into its factories, routers, pools and first reads. Call it first when a question names an exchange and gives no address.",
|
|
1667
1658
|
"- meta_usage_status: check the caller public free graph_query quota.",
|
|
1668
1659
|
"- meta_subscription_status: check the caller CIA subscription window end, daily allowance, consumption, and tier.",
|
|
1669
1660
|
"- graph_query: run read-only GQL/Cypher through the universal graph endpoint. Use USE topology or USE facts.",
|
|
@@ -1681,6 +1672,7 @@ async function createProxy() {
|
|
|
1681
1672
|
"Available graph-backed tools:",
|
|
1682
1673
|
"- meta_network_capabilities: supported networks and available tools. Takes no arguments.",
|
|
1683
1674
|
"- meta_schema: read the live graph schema of one network: labels, link types and field names. Cached for 24 hours.",
|
|
1675
|
+
"- meta_dex_lookup: turn an exchange name (Aero, Uniswap, Virtuals) into its factories, routers, pools and first reads. Call it first when a question names an exchange and gives no address.",
|
|
1684
1676
|
"- meta_usage_status: check the caller public free graph_query quota.",
|
|
1685
1677
|
"- meta_subscription_status: check the caller CIA subscription window end, daily allowance, consumption, and tier.",
|
|
1686
1678
|
"- graph_query: run read-only GQL/Cypher through the universal graph endpoint. Use USE topology or USE facts.",
|
|
@@ -1792,6 +1784,8 @@ if (process.argv[1] && require("url").pathToFileURL(__filename).href.includes(pr
|
|
|
1792
1784
|
//#endregion
|
|
1793
1785
|
exports.APP_ONLY_LOCAL_TOOL_NAMES = APP_ONLY_LOCAL_TOOL_NAMES;
|
|
1794
1786
|
exports.CLAUDE_VIEW_URI = CLAUDE_VIEW_URI;
|
|
1787
|
+
exports.GRAPH_QUERY_DEX_DOOR_SENTENCE = GRAPH_QUERY_DEX_DOOR_SENTENCE;
|
|
1788
|
+
exports.KNOWN_PUBLIC_TOOL_DESCRIPTIONS = KNOWN_PUBLIC_TOOL_DESCRIPTIONS;
|
|
1795
1789
|
exports.LOCAL_TOOL_NAMES = LOCAL_TOOL_NAMES;
|
|
1796
1790
|
exports.MCP_APP_MIME_TYPE = MCP_APP_MIME_TYPE;
|
|
1797
1791
|
exports.PICTURE_RULES = PICTURE_RULES;
|
package/dist/mcp-proxy.d.cts
CHANGED
|
@@ -11,6 +11,8 @@ export declare const MCP_APP_MIME_TYPE = "text/html;profile=mcp-app";
|
|
|
11
11
|
export declare function readClaudeViewHtml(): string;
|
|
12
12
|
export type McpProxyMode = 'workspace' | 'stateless';
|
|
13
13
|
export declare function resolveMcpProxyMode(env?: NodeJS.ProcessEnv): McpProxyMode;
|
|
14
|
+
export declare const GRAPH_QUERY_DEX_DOOR_SENTENCE = "A question that names an exchange and no address starts with meta_dex_lookup, never with a scan of :Protocol, :Pool or labels.";
|
|
15
|
+
export declare const KNOWN_PUBLIC_TOOL_DESCRIPTIONS: Record<string, string>;
|
|
14
16
|
type ToolInputShape = Record<string, z.ZodTypeAny>;
|
|
15
17
|
export declare const PICTURE_RULES: string;
|
|
16
18
|
export declare function knownPublicToolInputSchema(toolName: string): ToolInputShape | null;
|
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":";;qBA4Da,kBAAgB;qBAahB,2BAAyB;qBAUzB;qBACA;;;;;wBAyBG;YAkBJ;wBAEI,oBAAoB,MAAK,OAAO,aAA2B;qBAiB9D;qBAGA,gCAAgC;KAuBxC,iBAAiB,eAAe,EAAE;qBAgB1B;wBA2GG,2BAA2B,mBAAmB;;;;;;;;wBAomBxC,eAAe"}
|
package/dist/mcp-proxy.d.mts
CHANGED
|
@@ -11,6 +11,8 @@ export declare const MCP_APP_MIME_TYPE = "text/html;profile=mcp-app";
|
|
|
11
11
|
export declare function readClaudeViewHtml(): string;
|
|
12
12
|
export type McpProxyMode = 'workspace' | 'stateless';
|
|
13
13
|
export declare function resolveMcpProxyMode(env?: NodeJS.ProcessEnv): McpProxyMode;
|
|
14
|
+
export declare const GRAPH_QUERY_DEX_DOOR_SENTENCE = "A question that names an exchange and no address starts with meta_dex_lookup, never with a scan of :Protocol, :Pool or labels.";
|
|
15
|
+
export declare const KNOWN_PUBLIC_TOOL_DESCRIPTIONS: Record<string, string>;
|
|
14
16
|
type ToolInputShape = Record<string, z.ZodTypeAny>;
|
|
15
17
|
export declare const PICTURE_RULES: string;
|
|
16
18
|
export declare function knownPublicToolInputSchema(toolName: string): ToolInputShape | null;
|
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":";;qBA4Da,kBAAgB;qBAahB,2BAAyB;qBAUzB;qBACA;;;;;wBAyBG;YAkBJ;wBAEI,oBAAoB,MAAK,OAAO,aAA2B;qBAiB9D;qBAGA,gCAAgC;KAuBxC,iBAAiB,eAAe,EAAE;qBAgB1B;wBA2GG,2BAA2B,mBAAmB;;;;;;;;wBAomBxC,eAAe"}
|
package/dist/mcp-proxy.mjs
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { a as withTimestampTextResult, c as PACKAGE_VERSION, o as withoutUtcProperties } from "./print-result-CJ8d7lmz.mjs";
|
|
2
2
|
import { t as PaymentRequiredError } from "./client-gsLi528K.mjs";
|
|
3
|
-
import { i as isAppOnlyTool, n as PUBLIC_MCP_TOOL_ALLOWED_ARGS, r as PUBLIC_MCP_TOOL_REQUIRED_ARGS, t as HIDDEN_REMOTE_TOOL_NAMES } from "./tool-visibility-
|
|
3
|
+
import { i as isAppOnlyTool, n as PUBLIC_MCP_TOOL_ALLOWED_ARGS, r as PUBLIC_MCP_TOOL_REQUIRED_ARGS, t as HIDDEN_REMOTE_TOOL_NAMES } from "./tool-visibility-Dq-Ruj-8.mjs";
|
|
4
4
|
import { n as primitiveBackendUsageStatus, t as freeTierSpentText } from "./usage-status-Frot6fPG.mjs";
|
|
5
5
|
import { n as unavailableSubscriptionStatus } from "./subscription-status-DA9DCFrC.mjs";
|
|
6
6
|
import { r as mirrorGraphNetworkCapabilities } from "./capabilities-BBVDIECk.mjs";
|
|
7
7
|
import { a as handleMetaSchema, c as GRAPH_EXPAND_TITLE, l as GRAPH_EXPAND_TOOL, n as META_SCHEMA_DESCRIPTION, o as FLOWS_MAX_OFFSET, r as META_SCHEMA_TITLE, s as GRAPH_EXPAND_DESCRIPTION, t as GraphSchemaError, u as handleGraphExpand } from "./graph-schema-DHd2fGbr.mjs";
|
|
8
8
|
import { r as appendActionLog, t as actionLogSignalsFromResult } from "./action-log-D49Par9P.mjs";
|
|
9
|
+
import { i as META_DEX_LOOKUP_TOOL, n as META_DEX_LOOKUP_DESCRIPTION, o as handleMetaDexLookup, r as META_DEX_LOOKUP_TITLE, s as withAnswerHintResult, t as META_DEX_LOOKUP_ANNOTATIONS } from "./dex-tool-DpQKS7Te.mjs";
|
|
10
|
+
import { r as loadRegistry } from "./dex-registry-05wAhSa0.mjs";
|
|
9
11
|
import { fileURLToPath } from "node:url";
|
|
10
12
|
import path from "node:path";
|
|
11
13
|
import { readFileSync } from "node:fs";
|
|
@@ -97,17 +99,17 @@ function rateLimitedResult(toolName, err) {
|
|
|
97
99
|
const BUSY_CODE = "chain_busy";
|
|
98
100
|
const BUSY_WAIT_MS = 1100;
|
|
99
101
|
const defaultSleep = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
100
|
-
function isRecord$
|
|
102
|
+
function isRecord$2(value) {
|
|
101
103
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
102
104
|
}
|
|
103
105
|
function membersOf(result) {
|
|
104
|
-
const facts = isRecord$
|
|
105
|
-
const queries = isRecord$
|
|
106
|
-
return Array.isArray(queries) && queries.every(isRecord$
|
|
106
|
+
const facts = isRecord$2(result.structuredContent) ? result.structuredContent.facts : void 0;
|
|
107
|
+
const queries = isRecord$2(facts) ? facts.queries : void 0;
|
|
108
|
+
return Array.isArray(queries) && queries.every(isRecord$2) ? queries : null;
|
|
107
109
|
}
|
|
108
110
|
function isBusyMember(member) {
|
|
109
111
|
const detail = member.error_detail;
|
|
110
|
-
return isRecord$
|
|
112
|
+
return isRecord$2(detail) && detail.code === BUSY_CODE;
|
|
111
113
|
}
|
|
112
114
|
/** The ids of the members the server refused as chain_busy. */
|
|
113
115
|
function busyMemberIds(result) {
|
|
@@ -138,7 +140,7 @@ function mergeBatchAnswers(first, again) {
|
|
|
138
140
|
const merged = members.map((m) => isBusyMember(m) && byId.has(String(m.id)) ? byId.get(String(m.id)) : m);
|
|
139
141
|
const structured = first.structuredContent;
|
|
140
142
|
const facts = structured.facts;
|
|
141
|
-
const batch = isRecord$
|
|
143
|
+
const batch = isRecord$2(facts.batch) ? recount(facts.batch, merged) : facts.batch;
|
|
142
144
|
const nextStructured = {
|
|
143
145
|
...structured,
|
|
144
146
|
facts: {
|
|
@@ -182,7 +184,7 @@ async function withBusyMemberRetry(args, call, sleep = defaultSleep) {
|
|
|
182
184
|
function isBusyResult(result) {
|
|
183
185
|
if (!result.isError) return false;
|
|
184
186
|
const first = Array.isArray(result.content) ? result.content[0] : void 0;
|
|
185
|
-
const text = isRecord$
|
|
187
|
+
const text = isRecord$2(first) && typeof first.text === "string" ? first.text : "";
|
|
186
188
|
return /^(Query execution failed: )?chain_busy\b/.test(text.trim());
|
|
187
189
|
}
|
|
188
190
|
/**
|
|
@@ -329,7 +331,7 @@ function send(res, status, contentType, body) {
|
|
|
329
331
|
function sendJson(res, status, value) {
|
|
330
332
|
send(res, status, "application/json; charset=utf-8", JSON.stringify(value));
|
|
331
333
|
}
|
|
332
|
-
function isRecord$
|
|
334
|
+
function isRecord$1(value) {
|
|
333
335
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
334
336
|
}
|
|
335
337
|
var BodyTooLarge = class extends Error {};
|
|
@@ -420,8 +422,8 @@ async function startViewServer(options) {
|
|
|
420
422
|
sendJson(res, 400, { error: "invalid JSON" });
|
|
421
423
|
return;
|
|
422
424
|
}
|
|
423
|
-
const args = isRecord$
|
|
424
|
-
if (!isRecord$
|
|
425
|
+
const args = isRecord$1(body) ? body.arguments ?? {} : void 0;
|
|
426
|
+
if (!isRecord$1(args)) {
|
|
425
427
|
sendJson(res, 400, { error: "arguments must be an object" });
|
|
426
428
|
return;
|
|
427
429
|
}
|
|
@@ -602,52 +604,6 @@ async function connectRemote(options) {
|
|
|
602
604
|
};
|
|
603
605
|
}
|
|
604
606
|
//#endregion
|
|
605
|
-
//#region src/mcp/answer-hint.ts
|
|
606
|
-
/**
|
|
607
|
-
* The two answer rules that still slip in long answers, carried on every
|
|
608
|
-
* graph reply in the result's `hint` field. Claude Code has a Stop hook that
|
|
609
|
-
* reads the answer; Codex and Claude Desktop have no such hook, and in the
|
|
610
|
-
* loop's third prompt set they broke one of these two rules in three of
|
|
611
|
-
* thirteen conversations. The reply is the one thing every host hands the
|
|
612
|
-
* model right before it writes, so the rules ride on it. A `hint` the
|
|
613
|
-
* server already set is kept.
|
|
614
|
-
*/
|
|
615
|
-
const ANSWER_HINT = "Answer: write every address in full (0x plus 40 characters), also when it repeats one named earlier and also when the user asks for shorter ones (name them instead); a transfer moves tokens, so write \"tokens worth about N USD\", never \"sent N USD\" or a $ figure; never draw the flows in text, ASCII or Mermaid, the host draws them.";
|
|
616
|
-
function isRecord$1(value) {
|
|
617
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
618
|
-
}
|
|
619
|
-
/** The structured content with the hint set when the server left it empty. */
|
|
620
|
-
function withAnswerHint(structuredContent) {
|
|
621
|
-
if (!isRecord$1(structuredContent) || !isRecord$1(structuredContent.facts)) return structuredContent;
|
|
622
|
-
if (typeof structuredContent.hint === "string" && structuredContent.hint.trim()) return structuredContent;
|
|
623
|
-
return {
|
|
624
|
-
...structuredContent,
|
|
625
|
-
hint: ANSWER_HINT
|
|
626
|
-
};
|
|
627
|
-
}
|
|
628
|
-
/** A graph tool result whose structured content and single JSON text block both carry the hint. */
|
|
629
|
-
function withAnswerHintResult(result) {
|
|
630
|
-
const structured = withAnswerHint(result.structuredContent);
|
|
631
|
-
if (structured === result.structuredContent) return result;
|
|
632
|
-
const content = result.content ?? [];
|
|
633
|
-
const only = content.length === 1 && content[0]?.type === "text" ? content[0] : null;
|
|
634
|
-
let parsed = void 0;
|
|
635
|
-
try {
|
|
636
|
-
parsed = only?.text ? JSON.parse(only.text) : void 0;
|
|
637
|
-
} catch {
|
|
638
|
-
parsed = void 0;
|
|
639
|
-
}
|
|
640
|
-
const textIsStructured = parsed !== void 0 && JSON.stringify(parsed) === JSON.stringify(result.structuredContent);
|
|
641
|
-
return {
|
|
642
|
-
...result,
|
|
643
|
-
structuredContent: structured,
|
|
644
|
-
content: textIsStructured ? [{
|
|
645
|
-
...only,
|
|
646
|
-
text: JSON.stringify(structured)
|
|
647
|
-
}] : content
|
|
648
|
-
};
|
|
649
|
-
}
|
|
650
|
-
//#endregion
|
|
651
607
|
//#region src/mcp/proxy.ts
|
|
652
608
|
const LOCAL_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
653
609
|
"meta_network_capabilities",
|
|
@@ -656,6 +612,7 @@ const LOCAL_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
656
612
|
"meta_subscription_status",
|
|
657
613
|
"meta_help",
|
|
658
614
|
"meta_rules",
|
|
615
|
+
META_DEX_LOOKUP_TOOL,
|
|
659
616
|
"wallet_balance",
|
|
660
617
|
"wallet_topup",
|
|
661
618
|
GRAPH_EXPAND_TOOL
|
|
@@ -713,15 +670,17 @@ function resolveMcpProxyMode(env = process.env) {
|
|
|
713
670
|
}
|
|
714
671
|
const GRAPH_LAYERS_TEXT = "Use USE topology for topology (address/FLOWS_TO/OPERATED_BY/LINKED graph with SWAPPED, ADDED_LIQUIDITY, REMOVED_LIQUIDITY, BRIDGED and the Pool, Token and NFT labels, unified recent+historical) and USE facts for bounded TRANSFER, SWAP, LIQUIDITY_ADD, LIQUIDITY_REMOVE and BRIDGE_CROSSING rows and enrichment, and USE chain for the chain node's own record of one known transaction, block, address or the head.";
|
|
715
672
|
const TOOL_RULES_FIRST = "Rules first: if the chain-insights-cypher skill is not loaded in this chat, call meta_rules once before this tool. For a graph, alias the columns from_address and to_address; the host draws the picture from the answer itself. Never draw it again with another tool, such as a visualize or widget tool.";
|
|
673
|
+
const GRAPH_QUERY_DEX_DOOR_SENTENCE = "A question that names an exchange and no address starts with meta_dex_lookup, never with a scan of :Protocol, :Pool or labels.";
|
|
716
674
|
const KNOWN_PUBLIC_TOOL_DESCRIPTIONS = {
|
|
717
675
|
meta_network_capabilities: "Return the current Chain Insights network and tool support matrix. Takes no arguments: send {}.",
|
|
718
676
|
meta_schema: META_SCHEMA_DESCRIPTION,
|
|
677
|
+
[META_DEX_LOOKUP_TOOL]: META_DEX_LOOKUP_DESCRIPTION,
|
|
719
678
|
meta_usage_status: "Return the caller's public free graph_query quota for the current UTC day.",
|
|
720
679
|
meta_subscription_status: "Return the caller's CIA subscription window end, daily allowance, consumption, and tier.",
|
|
721
680
|
meta_help: "Show a short guide to Chain Insights tools and workflow.",
|
|
722
681
|
meta_rules: "Return the Chain Insights rules: how to write graph_query and graph_query_batch reads that are served, how to name columns so the answer is drawn as a graph, a time series or a table, and how to write the answer. Call it once per chat, before the first graph query, when the chain-insights-cypher skill is not loaded. Takes no arguments: send {}.",
|
|
723
682
|
wallet_balance: "Show the local Chain Insights payment wallet address, payment network, token, and amount.",
|
|
724
|
-
graph_query: `${TOOL_RULES_FIRST} Run a read-only GQL/Cypher query through the Chain Insights graph endpoint. ${GRAPH_LAYERS_TEXT} ${ROUTING_NOT_SERVED} Preserve full addresses exactly, and write every address in full, all 42 characters, in your answer: never shorten one with ... or ….`,
|
|
683
|
+
graph_query: `${TOOL_RULES_FIRST} Run a read-only GQL/Cypher query through the Chain Insights graph endpoint. ${GRAPH_LAYERS_TEXT} ${GRAPH_QUERY_DEX_DOOR_SENTENCE} ${ROUTING_NOT_SERVED} Preserve full addresses exactly, and write every address in full, all 42 characters, in your answer: never shorten one with ... or ….`,
|
|
725
684
|
graph_query_batch: `${TOOL_RULES_FIRST} Run multiple read-only GQL/Cypher queries through the Chain Insights graph endpoint in one paid batch. Prefer this for related topology/facts reads. For a time series across days, send one USE facts total per day with the query id set to the day, "YYYY-MM-DD": the view joins them into one series. Write every address in full in your answer, all 42 characters.`
|
|
726
685
|
};
|
|
727
686
|
const KNOWN_PUBLIC_TOOL_TITLES = {
|
|
@@ -1417,6 +1376,37 @@ async function createProxy() {
|
|
|
1417
1376
|
openWorldHint: true
|
|
1418
1377
|
}
|
|
1419
1378
|
}, async (args) => handleMetaSchema(args, schemaDependencies));
|
|
1379
|
+
const dexDependencies = {
|
|
1380
|
+
loadRegistry: (network) => loadRegistry(network, { log: (line) => {
|
|
1381
|
+
logger.info("dex_registry.fault", { line });
|
|
1382
|
+
} }),
|
|
1383
|
+
graphQueryBatch: async (args) => {
|
|
1384
|
+
const options = remoteToolRequestOptions("graph_query_batch");
|
|
1385
|
+
const call = (requestArgs) => withRateLimitRetry(() => options ? remoteClient.callTool({
|
|
1386
|
+
name: "graph_query_batch",
|
|
1387
|
+
arguments: requestArgs
|
|
1388
|
+
}, void 0, options) : remoteClient.callTool({
|
|
1389
|
+
name: "graph_query_batch",
|
|
1390
|
+
arguments: requestArgs
|
|
1391
|
+
}));
|
|
1392
|
+
return await withBusyMemberRetry(args, call);
|
|
1393
|
+
},
|
|
1394
|
+
unavailable: async () => {
|
|
1395
|
+
await ensureRemote();
|
|
1396
|
+
return remoteConnected ? void 0 : `${remoteUnavailableMessage ?? `Chain Insights Graph is not connected at ${graphMcpEndpoint}`}.`;
|
|
1397
|
+
},
|
|
1398
|
+
describeFailure: (err) => remoteCallFailureText("graph_query_batch", err)
|
|
1399
|
+
};
|
|
1400
|
+
server.registerTool(META_DEX_LOOKUP_TOOL, {
|
|
1401
|
+
title: META_DEX_LOOKUP_TITLE,
|
|
1402
|
+
description: KNOWN_PUBLIC_TOOL_DESCRIPTIONS[META_DEX_LOOKUP_TOOL],
|
|
1403
|
+
inputSchema: {
|
|
1404
|
+
network: NETWORK_SCHEMA,
|
|
1405
|
+
name: z.string().optional().describe("The exchange as the user wrote it: Aero, Aerodrome, uni v4. With no name, every covered exchange is listed."),
|
|
1406
|
+
pools: z.boolean().optional().describe("Read the biggest pools of the exchange (default true). false sends only Head and the contract reads.")
|
|
1407
|
+
},
|
|
1408
|
+
annotations: META_DEX_LOOKUP_ANNOTATIONS
|
|
1409
|
+
}, async (args) => handleMetaDexLookup(args, dexDependencies));
|
|
1420
1410
|
server.registerTool("meta_usage_status", {
|
|
1421
1411
|
title: "Usage Status",
|
|
1422
1412
|
description: KNOWN_PUBLIC_TOOL_DESCRIPTIONS.meta_usage_status,
|
|
@@ -1660,6 +1650,7 @@ async function createProxy() {
|
|
|
1660
1650
|
"Investigation tools:",
|
|
1661
1651
|
"- meta_network_capabilities: supported networks and available tools. Takes no arguments.",
|
|
1662
1652
|
"- meta_schema: read the live graph schema of one network: labels, link types and field names. Cached for 24 hours.",
|
|
1653
|
+
"- meta_dex_lookup: turn an exchange name (Aero, Uniswap, Virtuals) into its factories, routers, pools and first reads. Call it first when a question names an exchange and gives no address.",
|
|
1663
1654
|
"- meta_usage_status: check the caller public free graph_query quota.",
|
|
1664
1655
|
"- meta_subscription_status: check the caller CIA subscription window end, daily allowance, consumption, and tier.",
|
|
1665
1656
|
"- graph_query: run read-only GQL/Cypher through the universal graph endpoint. Use USE topology or USE facts.",
|
|
@@ -1677,6 +1668,7 @@ async function createProxy() {
|
|
|
1677
1668
|
"Available graph-backed tools:",
|
|
1678
1669
|
"- meta_network_capabilities: supported networks and available tools. Takes no arguments.",
|
|
1679
1670
|
"- meta_schema: read the live graph schema of one network: labels, link types and field names. Cached for 24 hours.",
|
|
1671
|
+
"- meta_dex_lookup: turn an exchange name (Aero, Uniswap, Virtuals) into its factories, routers, pools and first reads. Call it first when a question names an exchange and gives no address.",
|
|
1680
1672
|
"- meta_usage_status: check the caller public free graph_query quota.",
|
|
1681
1673
|
"- meta_subscription_status: check the caller CIA subscription window end, daily allowance, consumption, and tier.",
|
|
1682
1674
|
"- graph_query: run read-only GQL/Cypher through the universal graph endpoint. Use USE topology or USE facts.",
|
|
@@ -1786,6 +1778,6 @@ if (process.argv[1] && import.meta.url.includes(process.argv[1].replace(/\\/g, "
|
|
|
1786
1778
|
process.exit(1);
|
|
1787
1779
|
});
|
|
1788
1780
|
//#endregion
|
|
1789
|
-
export { APP_ONLY_LOCAL_TOOL_NAMES, CLAUDE_VIEW_URI, LOCAL_TOOL_NAMES, MCP_APP_MIME_TYPE, PICTURE_RULES, createProxy, knownPublicToolInputSchema, readClaudeViewHtml, resolveMcpProxyMode };
|
|
1781
|
+
export { APP_ONLY_LOCAL_TOOL_NAMES, CLAUDE_VIEW_URI, GRAPH_QUERY_DEX_DOOR_SENTENCE, KNOWN_PUBLIC_TOOL_DESCRIPTIONS, LOCAL_TOOL_NAMES, MCP_APP_MIME_TYPE, PICTURE_RULES, createProxy, knownPublicToolInputSchema, readClaudeViewHtml, resolveMcpProxyMode };
|
|
1790
1782
|
|
|
1791
1783
|
//# sourceMappingURL=mcp-proxy.mjs.map
|