perp-cli 0.6.0 → 0.6.2
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 +37 -0
- package/dist/mcp-server.js +83 -8
- package/package.json +11 -4
- package/skills/perp-cli/SKILL.md +20 -3
package/README.md
CHANGED
|
@@ -158,6 +158,43 @@ Exchange flag: `-e hyperliquid` / `-e pacifica` / `-e lighter` (aliases: `hl`, `
|
|
|
158
158
|
|
|
159
159
|
Global flags: `--json`, `--fields <f>`, `--ndjson`, `--dry-run`, `--dex <name>` (HIP-3), `-w, --wallet <name>`.
|
|
160
160
|
|
|
161
|
+
## MCP Server
|
|
162
|
+
|
|
163
|
+
[](https://glama.ai/mcp/servers/hypurrquant/perp-cli)
|
|
164
|
+
|
|
165
|
+
perp-cli includes a full-featured MCP server (18 tools, 3 resources, 2 prompts) for Claude Desktop, Cursor, and other MCP clients.
|
|
166
|
+
|
|
167
|
+
**No API keys required for market data** — explore prices, orderbooks, funding rates, and arb opportunities without any setup. Add keys only when you want to trade.
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"mcpServers": {
|
|
172
|
+
"perp-cli": {
|
|
173
|
+
"command": "npx",
|
|
174
|
+
"args": ["-y", "-p", "perp-cli", "perp-mcp"]
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Optional: add keys for trading and account data:
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"env": {
|
|
184
|
+
"HYPERLIQUID_PRIVATE_KEY": "your-evm-key",
|
|
185
|
+
"PACIFICA_PRIVATE_KEY": "your-solana-key"
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Read-only tools (no keys):** `get_markets`, `get_orderbook`, `get_funding_rates`, `get_prices`, `arb_scan`, `health_check`
|
|
191
|
+
|
|
192
|
+
**Account & trading tools (keys required):** `get_balance`, `get_positions`, `portfolio`, `trade_preview`, `trade_execute`, `trade_close`, `get_funding_analysis`, `get_pnl_analysis`, `get_arb_compare`
|
|
193
|
+
|
|
194
|
+
**Resources:** `market://prices`, `market://funding-rates`, `perp://schema`
|
|
195
|
+
|
|
196
|
+
**Prompts:** `trading-guide`, `arb-strategy`
|
|
197
|
+
|
|
161
198
|
## AI Agent Skill
|
|
162
199
|
|
|
163
200
|
Install as a skill for Claude Code, Cursor, Codex, Gemini CLI, etc.:
|
package/dist/mcp-server.js
CHANGED
|
@@ -51,6 +51,71 @@ async function getOrCreateAdapter(exchange) {
|
|
|
51
51
|
adapters.set(key, adapter);
|
|
52
52
|
return adapter;
|
|
53
53
|
}
|
|
54
|
+
/** Try to get adapter, returns null if no key configured (for read-only fallback) */
|
|
55
|
+
async function tryGetAdapter(exchange) {
|
|
56
|
+
const key = exchange.toLowerCase();
|
|
57
|
+
if (adapters.has(key))
|
|
58
|
+
return adapters.get(key);
|
|
59
|
+
try {
|
|
60
|
+
return await getOrCreateAdapter(exchange);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Fetch markets using public API (no key needed) */
|
|
67
|
+
async function fetchMarketsPublic(exchange) {
|
|
68
|
+
const ex = exchange.toLowerCase();
|
|
69
|
+
if (ex === "pacifica" || ex === "pac") {
|
|
70
|
+
const data = await fetchPacificaPrices();
|
|
71
|
+
return data.map(d => ({ symbol: d.symbol, markPrice: String(d.mark), indexPrice: String(d.mark), fundingRate: String(d.funding), volume24h: "0", openInterest: "0", maxLeverage: 10 }));
|
|
72
|
+
}
|
|
73
|
+
else if (ex === "hyperliquid" || ex === "hl") {
|
|
74
|
+
const data = await fetchHyperliquidMeta();
|
|
75
|
+
return data.map(d => ({ symbol: d.symbol, markPrice: String(d.markPx), indexPrice: String(d.markPx), fundingRate: String(d.funding), volume24h: "0", openInterest: "0", maxLeverage: 50 }));
|
|
76
|
+
}
|
|
77
|
+
else if (ex === "lighter" || ex === "lt") {
|
|
78
|
+
const data = await fetchLighterOrderBookDetails();
|
|
79
|
+
return data.map(d => ({ symbol: d.symbol, markPrice: String(d.lastTradePrice), indexPrice: String(d.lastTradePrice), fundingRate: "0", volume24h: "0", openInterest: "0", maxLeverage: 20 }));
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`Unknown exchange: ${exchange}. Supported: pacifica, hyperliquid, lighter`);
|
|
82
|
+
}
|
|
83
|
+
/** Fetch orderbook using public API (no key needed) */
|
|
84
|
+
async function fetchOrderbookPublic(exchange, symbol) {
|
|
85
|
+
const ex = exchange.toLowerCase();
|
|
86
|
+
if (ex === "hyperliquid" || ex === "hl") {
|
|
87
|
+
const { HYPERLIQUID_API_URL } = await import("./api/public/urls.js");
|
|
88
|
+
const res = await fetch(HYPERLIQUID_API_URL, {
|
|
89
|
+
method: "POST", headers: { "Content-Type": "application/json" },
|
|
90
|
+
body: JSON.stringify({ type: "l2Book", coin: symbol.toUpperCase() }),
|
|
91
|
+
});
|
|
92
|
+
const data = await res.json();
|
|
93
|
+
return {
|
|
94
|
+
bids: (data.levels?.[0] ?? []).map(l => [l.px, l.sz]),
|
|
95
|
+
asks: (data.levels?.[1] ?? []).map(l => [l.px, l.sz]),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
else if (ex === "lighter" || ex === "lt") {
|
|
99
|
+
const { LIGHTER_API_URL } = await import("./api/public/urls.js");
|
|
100
|
+
const res = await fetch(`${LIGHTER_API_URL}/api/v1/orderBookOrders?market_id=0&limit=20`);
|
|
101
|
+
const data = await res.json();
|
|
102
|
+
return {
|
|
103
|
+
bids: (data.bids ?? []).map(l => [l.price, l.remaining_base_amount ?? l.size ?? "0"]),
|
|
104
|
+
asks: (data.asks ?? []).map(l => [l.price, l.remaining_base_amount ?? l.size ?? "0"]),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
else if (ex === "pacifica" || ex === "pac") {
|
|
108
|
+
const { PACIFICA_API_URL } = await import("./api/public/urls.js");
|
|
109
|
+
const baseUrl = PACIFICA_API_URL.replace("/info/prices", "");
|
|
110
|
+
const res = await fetch(`${baseUrl}/info/l2Book?symbol=${symbol.toUpperCase()}`);
|
|
111
|
+
const data = await res.json();
|
|
112
|
+
return {
|
|
113
|
+
bids: (data.l?.[0] ?? []).map(e => [e.p, e.a]),
|
|
114
|
+
asks: (data.l?.[1] ?? []).map(e => [e.p, e.a]),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
throw new Error(`Unknown exchange: ${exchange}`);
|
|
118
|
+
}
|
|
54
119
|
// ── JSON envelope helpers ──
|
|
55
120
|
function ok(data, meta) {
|
|
56
121
|
return JSON.stringify({ ok: true, data, meta }, null, 2);
|
|
@@ -65,24 +130,34 @@ const server = new McpServer({ name: "perp-cli", version: _pkg.version }, { capa
|
|
|
65
130
|
// ============================================================
|
|
66
131
|
// Market Data tools (read-only, no private key needed)
|
|
67
132
|
// ============================================================
|
|
68
|
-
server.tool("get_markets", "Get all available perpetual futures markets on an exchange, including price, funding rate, volume, and max leverage", { exchange: z.string().describe("Exchange name: pacifica, hyperliquid, or lighter") }, async ({ exchange }) => {
|
|
133
|
+
server.tool("get_markets", "Get all available perpetual futures markets on an exchange, including price, funding rate, volume, and max leverage. Works without API keys.", { exchange: z.string().describe("Exchange name: pacifica, hyperliquid, or lighter") }, async ({ exchange }) => {
|
|
69
134
|
try {
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
135
|
+
// Try adapter first (richer data), fall back to public API (no key needed)
|
|
136
|
+
const adapter = await tryGetAdapter(exchange);
|
|
137
|
+
if (adapter) {
|
|
138
|
+
const markets = await adapter.getMarkets();
|
|
139
|
+
return { content: [{ type: "text", text: ok(markets, { exchange, count: markets.length }) }] };
|
|
140
|
+
}
|
|
141
|
+
const markets = await fetchMarketsPublic(exchange);
|
|
142
|
+
return { content: [{ type: "text", text: ok(markets, { exchange, count: markets.length, source: "public" }) }] };
|
|
73
143
|
}
|
|
74
144
|
catch (e) {
|
|
75
145
|
return { content: [{ type: "text", text: err(e instanceof Error ? e.message : String(e), { exchange }) }], isError: true };
|
|
76
146
|
}
|
|
77
147
|
});
|
|
78
|
-
server.tool("get_orderbook", "Get the order book (bids and asks) for a symbol on an exchange", {
|
|
148
|
+
server.tool("get_orderbook", "Get the order book (bids and asks) for a symbol on an exchange. Works without API keys.", {
|
|
79
149
|
exchange: z.string().describe("Exchange name: pacifica, hyperliquid, or lighter"),
|
|
80
150
|
symbol: z.string().describe("Trading pair symbol, e.g. BTC, ETH, SOL"),
|
|
81
151
|
}, async ({ exchange, symbol }) => {
|
|
82
152
|
try {
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
|
|
153
|
+
// Try adapter first (richer data), fall back to public API (no key needed)
|
|
154
|
+
const adapter = await tryGetAdapter(exchange);
|
|
155
|
+
if (adapter) {
|
|
156
|
+
const book = await adapter.getOrderbook(symbol);
|
|
157
|
+
return { content: [{ type: "text", text: ok(book, { exchange, symbol }) }] };
|
|
158
|
+
}
|
|
159
|
+
const book = await fetchOrderbookPublic(exchange, symbol);
|
|
160
|
+
return { content: [{ type: "text", text: ok(book, { exchange, symbol, source: "public" }) }] };
|
|
86
161
|
}
|
|
87
162
|
catch (e) {
|
|
88
163
|
return { content: [{ type: "text", text: err(e instanceof Error ? e.message : String(e), { exchange, symbol }) }], isError: true };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "perp-cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Multi-DEX Perpetual Futures CLI - Pacifica, Hyperliquid, Lighter",
|
|
5
5
|
"bin": {
|
|
6
6
|
"perp": "dist/index.js",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"build": "tsc",
|
|
16
16
|
"dev": "tsc --watch",
|
|
17
17
|
"test": "vitest run --exclude '**/integration/**'",
|
|
18
|
-
"test:integration": "vitest run
|
|
18
|
+
"test:integration": "vitest run -c vitest.integration.config.ts",
|
|
19
19
|
"test:all": "vitest run",
|
|
20
20
|
"prepublishOnly": "pnpm run build"
|
|
21
21
|
},
|
|
@@ -29,11 +29,18 @@
|
|
|
29
29
|
"cli",
|
|
30
30
|
"trading",
|
|
31
31
|
"funding-rate",
|
|
32
|
-
"arbitrage"
|
|
32
|
+
"arbitrage",
|
|
33
|
+
"mcp",
|
|
34
|
+
"mcp-server",
|
|
35
|
+
"model-context-protocol",
|
|
36
|
+
"ai-agent",
|
|
37
|
+
"perpetual-futures",
|
|
38
|
+
"defi",
|
|
39
|
+
"ethereum"
|
|
33
40
|
],
|
|
34
41
|
"license": "MIT",
|
|
35
42
|
"engines": {
|
|
36
|
-
"node": "^20.0.0 || ^22.0.0"
|
|
43
|
+
"node": "^20.0.0 || ^22.0.0 || ^24.0.0"
|
|
37
44
|
},
|
|
38
45
|
"dependencies": {
|
|
39
46
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
package/skills/perp-cli/SKILL.md
CHANGED
|
@@ -5,12 +5,29 @@ allowed-tools: "Bash(perp:*), Bash(npx perp-cli:*), Bash(npx -y perp-cli:*)"
|
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
7
|
author: hypurrquant
|
|
8
|
-
version: "0.
|
|
8
|
+
version: "0.6.1"
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# perp-cli Agent Guide
|
|
12
12
|
|
|
13
|
-
Multi-DEX perpetual futures CLI — Pacifica (Solana), Hyperliquid (HyperEVM), Lighter (Ethereum).
|
|
13
|
+
Multi-DEX perpetual futures CLI + MCP server — Pacifica (Solana), Hyperliquid (HyperEVM), Lighter (Ethereum).
|
|
14
|
+
|
|
15
|
+
[](https://glama.ai/mcp/servers/hypurrquant/perp-cli)
|
|
16
|
+
|
|
17
|
+
## MCP Server
|
|
18
|
+
|
|
19
|
+
18 tools, 3 resources, 2 prompts. No API keys required for market data — explore prices, orderbooks, funding rates, and arb opportunities without setup.
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"perp-cli": {
|
|
25
|
+
"command": "npx",
|
|
26
|
+
"args": ["-y", "-p", "perp-cli", "perp-mcp"]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
14
31
|
|
|
15
32
|
## Rules
|
|
16
33
|
|
|
@@ -24,7 +41,7 @@ Multi-DEX perpetual futures CLI — Pacifica (Solana), Hyperliquid (HyperEVM), L
|
|
|
24
41
|
## Install
|
|
25
42
|
|
|
26
43
|
```bash
|
|
27
|
-
perp --version 2>/dev/null # check if exists (must be >= 0.
|
|
44
|
+
perp --version 2>/dev/null # check if exists (must be >= 0.6.1)
|
|
28
45
|
npm install -g perp-cli@latest 2>/dev/null || npx -y perp-cli@latest --json --version
|
|
29
46
|
```
|
|
30
47
|
|