@ironflowsh/mcp 0.7.1 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -20
- package/build/api.d.ts +2 -3
- package/build/api.d.ts.map +1 -1
- package/build/api.js +8 -10
- package/build/api.js.map +1 -1
- package/build/budget.d.ts +3 -0
- package/build/budget.d.ts.map +1 -0
- package/build/budget.js +61 -0
- package/build/budget.js.map +1 -0
- package/build/http.d.ts +3 -0
- package/build/http.d.ts.map +1 -0
- package/build/http.js +144 -0
- package/build/http.js.map +1 -0
- package/build/index.js +7 -65
- package/build/index.js.map +1 -1
- package/build/liquidations.d.ts +28 -0
- package/build/liquidations.d.ts.map +1 -0
- package/build/liquidations.js +77 -0
- package/build/liquidations.js.map +1 -0
- package/build/request.d.ts +8 -0
- package/build/request.d.ts.map +1 -0
- package/build/request.js +34 -0
- package/build/request.js.map +1 -0
- package/build/schemas.d.ts +3 -0
- package/build/schemas.d.ts.map +1 -0
- package/build/schemas.js +444 -0
- package/build/schemas.js.map +1 -0
- package/build/server.d.ts +6 -0
- package/build/server.d.ts.map +1 -0
- package/build/server.js +88 -0
- package/build/server.js.map +1 -0
- package/build/shape.d.ts +35 -0
- package/build/shape.d.ts.map +1 -0
- package/build/shape.js +56 -0
- package/build/shape.js.map +1 -0
- package/build/snapshot.d.ts +22 -0
- package/build/snapshot.d.ts.map +1 -0
- package/build/snapshot.js +40 -0
- package/build/snapshot.js.map +1 -0
- package/build/tools.d.ts.map +1 -1
- package/build/tools.js +162 -68
- package/build/tools.js.map +1 -1
- package/package.json +6 -4
package/build/server.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Builds the Ironflow MCP server. Shared by the stdio entry (index.ts) and
|
|
2
|
+
// the hosted Streamable HTTP entry (http.ts) so both expose the same tools.
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
7
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
8
|
+
import { fitToBudget } from "./budget.js";
|
|
9
|
+
import { outputSchemas } from "./schemas.js";
|
|
10
|
+
import { tools } from "./tools.js";
|
|
11
|
+
// One-line summary for MCP directories and client server lists.
|
|
12
|
+
const DESCRIPTION = "Read-only Hyperliquid market data and wallet analytics from our own nodes: trades, funding, liquidations, open interest, wallet PnL and trader rankings. Native perps, HIP-3, HIP-4 and spot. No API key needed.";
|
|
13
|
+
// Server-level guidance the client shows the model alongside the tool list.
|
|
14
|
+
const INSTRUCTIONS = "Read-only Hyperliquid market data from Ironflow's own nodes: trades, candles, funding, open interest, liquidations, mark prices, market snapshots and wallet analytics for native perps, HIP-3 builder markets and spot. Markets use display symbols such as BTC-PERP or xyz:NVDA-PERP; call list_markets when unsure. Keyless use allows 10 requests per minute and 24 hours of history; a free key from https://ironflow.sh/key raises that to 60 per minute and 30 days.";
|
|
15
|
+
// Reads the package version from package.json so MCP clients see the real
|
|
16
|
+
// version in serverInfo instead of a constant that drifts on every release.
|
|
17
|
+
export function packageVersion() {
|
|
18
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
try {
|
|
20
|
+
const raw = readFileSync(join(here, "..", "package.json"), "utf-8");
|
|
21
|
+
return JSON.parse(raw).version ?? "unknown";
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return "unknown";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// createServer wires the tool list and tool calls to one API client.
|
|
28
|
+
export function createServer(api, version) {
|
|
29
|
+
const server = new Server({
|
|
30
|
+
name: "ironflow",
|
|
31
|
+
title: "Ironflow Hyperliquid",
|
|
32
|
+
version,
|
|
33
|
+
description: DESCRIPTION,
|
|
34
|
+
websiteUrl: "https://ironflow.sh/api#mcp",
|
|
35
|
+
icons: [
|
|
36
|
+
{ src: "https://ironflow.sh/apple-touch-icon.png", mimeType: "image/png", sizes: ["256x256"] },
|
|
37
|
+
{ src: "https://ironflow.sh/favicon.svg", mimeType: "image/svg+xml", sizes: ["any"] },
|
|
38
|
+
],
|
|
39
|
+
}, { capabilities: { tools: {} }, instructions: INSTRUCTIONS });
|
|
40
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
41
|
+
tools: tools.map((t) => ({
|
|
42
|
+
name: t.name,
|
|
43
|
+
description: t.description,
|
|
44
|
+
inputSchema: t.inputSchema,
|
|
45
|
+
outputSchema: outputSchemas[t.name],
|
|
46
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
47
|
+
})),
|
|
48
|
+
}));
|
|
49
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
50
|
+
const { name, arguments: args } = request.params;
|
|
51
|
+
const tool = tools.find((t) => t.name === name);
|
|
52
|
+
if (!tool) {
|
|
53
|
+
return {
|
|
54
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
55
|
+
isError: true,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const structured = structuredFrom(await tool.handler(args ?? {}, api));
|
|
60
|
+
// Compact JSON, trimmed to fit a client's per-result limit.
|
|
61
|
+
const text = fitToBudget(structured);
|
|
62
|
+
return { content: [{ type: "text", text }], structuredContent: JSON.parse(text) };
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
66
|
+
return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return server;
|
|
70
|
+
}
|
|
71
|
+
// structuredFrom turns a tool's JSON text into MCP structured output. Every
|
|
72
|
+
// tool declares an output schema, and clients reject a successful result
|
|
73
|
+
// without structuredContent, so anything that is not a JSON object is
|
|
74
|
+
// wrapped as { result }. The schemas mark no field required, so the wrapper
|
|
75
|
+
// still validates.
|
|
76
|
+
export function structuredFrom(text) {
|
|
77
|
+
try {
|
|
78
|
+
const parsed = JSON.parse(text);
|
|
79
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
80
|
+
return parsed;
|
|
81
|
+
}
|
|
82
|
+
return { result: parsed };
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return { result: text };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,4EAA4E;AAE5E,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,gEAAgE;AAChE,MAAM,WAAW,GACf,kNAAkN,CAAC;AAErN,4EAA4E;AAC5E,MAAM,YAAY,GAChB,6cAA6c,CAAC;AAEhd,0EAA0E;AAC1E,4EAA4E;AAC5E,MAAM,UAAU,cAAc;IAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC;QACpE,OAAQ,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,OAAkB,IAAI,SAAS,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,YAAY,CAAC,GAAgB,EAAE,OAAe;IAC5D,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB;QACE,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,sBAAsB;QAC7B,OAAO;QACP,WAAW,EAAE,WAAW;QACxB,UAAU,EAAE,6BAA6B;QACzC,KAAK,EAAE;YACL,EAAE,GAAG,EAAE,0CAA0C,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE;YAC9F,EAAE,GAAG,EAAE,iCAAiC,EAAE,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE;SACtF;KACF,EACD,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,YAAY,EAAE,CAC5D,CAAC;IAEF,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QAC5D,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACvB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;YACnC,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;SACzD,CAAC,CAAC;KACJ,CAAC,CAAC,CAAC;IAEJ,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAChE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QACjD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QAChD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,IAAI,EAAE,EAAE,CAAC;gBAC1D,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QACD,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,cAAc,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;YACvE,4DAA4D;YAC5D,MAAM,IAAI,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;YACrC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,iBAAiB,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAA4B,EAAE,CAAC;QAC/G,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACnF,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,4EAA4E;AAC5E,yEAAyE;AACzE,sEAAsE;AACtE,4EAA4E;AAC5E,mBAAmB;AACnB,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACnE,OAAO,MAAiC,CAAC;QAC3C,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC1B,CAAC;AACH,CAAC"}
|
package/build/shape.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
type Row = Record<string, unknown>;
|
|
2
|
+
export declare const DEFAULT_MARKETS_LIMIT = 100;
|
|
3
|
+
export declare const DEFAULT_FUNDING_ROWS = 200;
|
|
4
|
+
export declare function shapeMarkets(resp: {
|
|
5
|
+
data?: Row[];
|
|
6
|
+
}, opts: {
|
|
7
|
+
search?: string;
|
|
8
|
+
limit?: number;
|
|
9
|
+
}): {
|
|
10
|
+
total_markets: number;
|
|
11
|
+
matched: number;
|
|
12
|
+
returned: number;
|
|
13
|
+
data: Row[];
|
|
14
|
+
};
|
|
15
|
+
export declare function shapeWalletLabels(resp: {
|
|
16
|
+
counts?: unknown;
|
|
17
|
+
labels?: Record<string, Row[]>;
|
|
18
|
+
} & Row, opts: {
|
|
19
|
+
addresses?: string[];
|
|
20
|
+
}): {
|
|
21
|
+
counts: unknown;
|
|
22
|
+
labels: {
|
|
23
|
+
[k: string]: Row[];
|
|
24
|
+
};
|
|
25
|
+
note: string;
|
|
26
|
+
};
|
|
27
|
+
export declare function shapeUserFunding(resp: {
|
|
28
|
+
data?: Row[];
|
|
29
|
+
} & Row, limit?: number): {
|
|
30
|
+
total_rows: number;
|
|
31
|
+
returned: number;
|
|
32
|
+
data: Row[];
|
|
33
|
+
};
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=shape.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape.d.ts","sourceRoot":"","sources":["../src/shape.ts"],"names":[],"mappings":"AAKA,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEnC,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAqBxC,wBAAgB,YAAY,CAAC,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;CAAE,EAAE,IAAI,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;EAU7F;AAOD,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;CAAE,GAAG,GAAG,EAChE,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE;;;;;;EAc/B;AAGD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;CAAE,GAAG,GAAG,EAAE,KAAK,SAAuB;;;;EAI1F"}
|
package/build/shape.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Reshaping for tools whose raw API responses are far larger than an MCP
|
|
2
|
+
// client accepts in one result: the market registry (about 9M characters with
|
|
3
|
+
// HIP-4 metadata), wallet labels (every vault address) and per-bucket wallet
|
|
4
|
+
// funding. Each keeps what an agent asks for and reports what it left out.
|
|
5
|
+
export const DEFAULT_MARKETS_LIMIT = 100;
|
|
6
|
+
export const DEFAULT_FUNDING_ROWS = 200;
|
|
7
|
+
const MARKET_FIELDS = [
|
|
8
|
+
"market_id",
|
|
9
|
+
"display_symbol",
|
|
10
|
+
"market_class",
|
|
11
|
+
"issuer",
|
|
12
|
+
"base_asset",
|
|
13
|
+
"quote_asset",
|
|
14
|
+
"base_market",
|
|
15
|
+
"hl_coin",
|
|
16
|
+
"max_leverage",
|
|
17
|
+
"active_from_ms",
|
|
18
|
+
];
|
|
19
|
+
const pick = (row, keys) => Object.fromEntries(keys.filter((k) => k in row).map((k) => [k, row[k]]));
|
|
20
|
+
// shapeMarkets filters the registry by a case-insensitive search over the
|
|
21
|
+
// symbol, base asset and Hyperliquid coin, drops the per-market metadata blob
|
|
22
|
+
// and keeps the first `limit` rows.
|
|
23
|
+
export function shapeMarkets(resp, opts) {
|
|
24
|
+
const rows = resp.data ?? [];
|
|
25
|
+
const q = opts.search?.trim().toLowerCase();
|
|
26
|
+
const matched = q
|
|
27
|
+
? rows.filter((r) => [r.display_symbol, r.base_asset, r.hl_coin].some((v) => String(v ?? "").toLowerCase().includes(q)))
|
|
28
|
+
: rows;
|
|
29
|
+
const data = matched.slice(0, opts.limit ?? DEFAULT_MARKETS_LIMIT).map((r) => pick(r, MARKET_FIELDS));
|
|
30
|
+
return { total_markets: rows.length, matched: matched.length, returned: data.length, data };
|
|
31
|
+
}
|
|
32
|
+
const LISTED_KINDS = new Set(["whale", "smart_money"]);
|
|
33
|
+
// shapeWalletLabels returns labels for the requested addresses, or, without
|
|
34
|
+
// addresses, every whale and smart-money wallet (plain vault entries run into
|
|
35
|
+
// the thousands and are left out).
|
|
36
|
+
export function shapeWalletLabels(resp, opts) {
|
|
37
|
+
const labels = resp.labels ?? {};
|
|
38
|
+
const wanted = opts.addresses?.map((a) => a.toLowerCase());
|
|
39
|
+
const keep = wanted
|
|
40
|
+
? Object.entries(labels).filter(([addr]) => wanted.includes(addr.toLowerCase()))
|
|
41
|
+
: Object.entries(labels).filter(([, ls]) => ls.some((l) => LISTED_KINDS.has(String(l.kind))));
|
|
42
|
+
return {
|
|
43
|
+
counts: resp.counts,
|
|
44
|
+
labels: Object.fromEntries(keep),
|
|
45
|
+
note: wanted
|
|
46
|
+
? "Addresses without an entry have no label."
|
|
47
|
+
: "Whales and smart money only. Pass addresses to check any wallet, including vaults and vault leaders.",
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// shapeUserFunding keeps the most recent `limit` funding rows.
|
|
51
|
+
export function shapeUserFunding(resp, limit = DEFAULT_FUNDING_ROWS) {
|
|
52
|
+
const rows = [...(resp.data ?? [])].sort((a, b) => Number(b.bucket_start ?? 0) - Number(a.bucket_start ?? 0));
|
|
53
|
+
const data = rows.slice(0, limit);
|
|
54
|
+
return { ...resp, total_rows: rows.length, returned: data.length, data };
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=shape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape.js","sourceRoot":"","sources":["../src/shape.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const SORT_KEYS: readonly ["volume", "open_interest", "funding", "abs_funding", "change_24h", "abs_change_24h"];
|
|
2
|
+
export type SortKey = (typeof SORT_KEYS)[number];
|
|
3
|
+
export declare const DEFAULT_LIMIT = 25;
|
|
4
|
+
export declare const MAX_LIMIT = 200;
|
|
5
|
+
type Row = Record<string, unknown>;
|
|
6
|
+
export interface RankOptions {
|
|
7
|
+
sortBy?: SortKey;
|
|
8
|
+
order?: "asc" | "desc";
|
|
9
|
+
limit?: number;
|
|
10
|
+
minVolumeUsd?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface RankedSnapshot {
|
|
13
|
+
sort_by: SortKey;
|
|
14
|
+
order: "asc" | "desc";
|
|
15
|
+
total_markets: number;
|
|
16
|
+
matched: number;
|
|
17
|
+
returned: number;
|
|
18
|
+
data: Row[];
|
|
19
|
+
}
|
|
20
|
+
export declare function rankSnapshot(rows: Row[], opts: RankOptions): RankedSnapshot;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,SAAS,gGAAiG,CAAC;AACxH,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjD,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,SAAS,MAAM,CAAC;AAE7B,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEnC,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,GAAG,EAAE,CAAC;CACb;AAoBD,wBAAgB,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,WAAW,GAAG,cAAc,CAgB3E"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Ranking for get_markets_snapshot. The API returns every active market
|
|
2
|
+
// (about 140k characters for perps alone), more than MCP clients accept in one
|
|
3
|
+
// tool result, so the tool sorts, filters and trims before answering.
|
|
4
|
+
export const SORT_KEYS = ["volume", "open_interest", "funding", "abs_funding", "change_24h", "abs_change_24h"];
|
|
5
|
+
export const DEFAULT_LIMIT = 25;
|
|
6
|
+
export const MAX_LIMIT = 200;
|
|
7
|
+
const num = (v) => {
|
|
8
|
+
const n = typeof v === "number" ? v : Number(v);
|
|
9
|
+
return Number.isFinite(n) ? n : 0;
|
|
10
|
+
};
|
|
11
|
+
// sortValue maps a row to the number it is ranked by.
|
|
12
|
+
const sortValue = {
|
|
13
|
+
volume: (r) => num(r.volume_24h_usd),
|
|
14
|
+
open_interest: (r) => num(r.open_interest_usd),
|
|
15
|
+
funding: (r) => num(r.funding_rate),
|
|
16
|
+
abs_funding: (r) => Math.abs(num(r.funding_rate)),
|
|
17
|
+
change_24h: (r) => num(r.change_24h_pct),
|
|
18
|
+
abs_change_24h: (r) => Math.abs(num(r.change_24h_pct)),
|
|
19
|
+
};
|
|
20
|
+
// rankSnapshot filters rows by 24h volume, sorts them and keeps the top
|
|
21
|
+
// `limit`. total_markets counts every row; matched counts rows past the
|
|
22
|
+
// volume floor.
|
|
23
|
+
export function rankSnapshot(rows, opts) {
|
|
24
|
+
const sortBy = opts.sortBy ?? "volume";
|
|
25
|
+
if (!SORT_KEYS.includes(sortBy)) {
|
|
26
|
+
throw new Error(`"sort_by" must be one of ${SORT_KEYS.join(", ")}`);
|
|
27
|
+
}
|
|
28
|
+
const order = opts.order ?? "desc";
|
|
29
|
+
const limit = opts.limit ?? DEFAULT_LIMIT;
|
|
30
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > MAX_LIMIT) {
|
|
31
|
+
throw new Error(`"limit" must be between 1 and ${MAX_LIMIT}`);
|
|
32
|
+
}
|
|
33
|
+
const floor = opts.minVolumeUsd ?? 0;
|
|
34
|
+
const value = sortValue[sortBy];
|
|
35
|
+
const matched = rows.filter((r) => num(r.volume_24h_usd) >= floor);
|
|
36
|
+
const sorted = [...matched].sort((a, b) => (order === "desc" ? value(b) - value(a) : value(a) - value(b)));
|
|
37
|
+
const data = sorted.slice(0, limit);
|
|
38
|
+
return { sort_by: sortBy, order, total_markets: rows.length, matched: matched.length, returned: data.length, data };
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"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"}
|
package/build/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAMvC,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC,CAAC;QACnF,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC/E;AAiCD,eAAO,MAAM,KAAK,EAAE,OAAO,EAgsB1B,CAAC"}
|