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 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
+ [![Glama MCP server](https://glama.ai/mcp/servers/hypurrquant/perp-cli/badges/score.svg)](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.:
@@ -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
- const adapter = await getOrCreateAdapter(exchange);
71
- const markets = await adapter.getMarkets();
72
- return { content: [{ type: "text", text: ok(markets, { exchange, count: markets.length }) }] };
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
- const adapter = await getOrCreateAdapter(exchange);
84
- const book = await adapter.getOrderbook(symbol);
85
- return { content: [{ type: "text", text: ok(book, { exchange, symbol }) }] };
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.0",
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 '**/*.integration.test.ts'",
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",
@@ -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.5.1"
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
+ [![Glama MCP server](https://glama.ai/mcp/servers/hypurrquant/perp-cli/badges/score.svg)](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.5.1)
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