@pionex/pionex-trade-mcp 0.2.5 → 0.2.6
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/dist/run-server.js +462 -0
- package/dist/run-server.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/run-server.ts
|
|
4
|
+
import { dirname } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
+
|
|
9
|
+
// src/tools/market/index.ts
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
|
|
12
|
+
// src/tools/common/client.ts
|
|
13
|
+
import crypto from "crypto";
|
|
14
|
+
import process from "process";
|
|
15
|
+
import fetch from "node-fetch";
|
|
16
|
+
var API_KEY_ENV = "PIONEX_API_KEY";
|
|
17
|
+
var API_SECRET_ENV = "PIONEX_API_SECRET";
|
|
18
|
+
var BASE_URL_ENV = "PIONEX_BASE_URL";
|
|
19
|
+
function getBaseUrl() {
|
|
20
|
+
return process.env[BASE_URL_ENV] || "https://api.pionex.com";
|
|
21
|
+
}
|
|
22
|
+
function isAuthenticated() {
|
|
23
|
+
return Boolean(process.env[API_KEY_ENV] && process.env[API_SECRET_ENV]);
|
|
24
|
+
}
|
|
25
|
+
function requireAuth() {
|
|
26
|
+
if (!isAuthenticated()) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
"This tool requires authentication. Run 'pionex config init' to create ~/.pionex/config.toml, or set PIONEX_API_KEY and PIONEX_API_SECRET."
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function getApiKey() {
|
|
33
|
+
const v = process.env[API_KEY_ENV];
|
|
34
|
+
if (!v) throw new Error(`Environment variable ${API_KEY_ENV} is required.`);
|
|
35
|
+
return v;
|
|
36
|
+
}
|
|
37
|
+
function getApiSecret() {
|
|
38
|
+
const v = process.env[API_SECRET_ENV];
|
|
39
|
+
if (!v) throw new Error(`Environment variable ${API_SECRET_ENV} is required.`);
|
|
40
|
+
return v;
|
|
41
|
+
}
|
|
42
|
+
function buildSignedRequest(method, path, query, bodyJson = null) {
|
|
43
|
+
const baseUrl = getBaseUrl();
|
|
44
|
+
const apiKey = getApiKey();
|
|
45
|
+
const apiSecret = getApiSecret();
|
|
46
|
+
const timestamp = Date.now().toString();
|
|
47
|
+
const params = { ...query, timestamp };
|
|
48
|
+
const sortedKeys = Object.keys(params).sort();
|
|
49
|
+
const queryString = sortedKeys.map((k) => `${k}=${params[k]}`).join("&");
|
|
50
|
+
const pathUrl = `${path}?${queryString}`;
|
|
51
|
+
let payload = `${method}${pathUrl}`;
|
|
52
|
+
if (bodyJson != null) payload += bodyJson;
|
|
53
|
+
const signature = crypto.createHmac("sha256", apiSecret).update(payload).digest("hex");
|
|
54
|
+
const url = `${baseUrl}${pathUrl}`;
|
|
55
|
+
const headers = {
|
|
56
|
+
"PIONEX-KEY": apiKey,
|
|
57
|
+
"PIONEX-SIGNATURE": signature,
|
|
58
|
+
"Content-Type": "application/json"
|
|
59
|
+
};
|
|
60
|
+
return { url, headers, bodyJson };
|
|
61
|
+
}
|
|
62
|
+
async function publicGet(path, query = {}) {
|
|
63
|
+
const baseUrl = getBaseUrl();
|
|
64
|
+
const qs = new URLSearchParams(query).toString();
|
|
65
|
+
const url = qs ? `${baseUrl}${path}?${qs}` : `${baseUrl}${path}`;
|
|
66
|
+
const res = await fetch(url, { method: "GET", headers: { "Content-Type": "application/json" } });
|
|
67
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
|
|
68
|
+
return res.json();
|
|
69
|
+
}
|
|
70
|
+
async function signedGet(path, query = {}) {
|
|
71
|
+
requireAuth();
|
|
72
|
+
const { url, headers } = buildSignedRequest("GET", path, query);
|
|
73
|
+
const res = await fetch(url, { method: "GET", headers });
|
|
74
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
|
|
75
|
+
return res.json();
|
|
76
|
+
}
|
|
77
|
+
async function signedPost(path, body) {
|
|
78
|
+
requireAuth();
|
|
79
|
+
const bodyJson = JSON.stringify(body);
|
|
80
|
+
const { url, headers } = buildSignedRequest("POST", path, {}, bodyJson);
|
|
81
|
+
const res = await fetch(url, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers,
|
|
84
|
+
body: bodyJson
|
|
85
|
+
});
|
|
86
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
|
|
87
|
+
return res.json();
|
|
88
|
+
}
|
|
89
|
+
async function signedDelete(path, body) {
|
|
90
|
+
requireAuth();
|
|
91
|
+
const bodyJson = JSON.stringify(body);
|
|
92
|
+
const { url, headers } = buildSignedRequest("DELETE", path, {}, bodyJson);
|
|
93
|
+
const res = await fetch(url, {
|
|
94
|
+
method: "DELETE",
|
|
95
|
+
headers,
|
|
96
|
+
body: bodyJson
|
|
97
|
+
});
|
|
98
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
|
|
99
|
+
return res.json();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/tools/common/utils.ts
|
|
103
|
+
function toText(value) {
|
|
104
|
+
return JSON.stringify(
|
|
105
|
+
value,
|
|
106
|
+
(_, v) => typeof v === "bigint" ? v.toString() : v,
|
|
107
|
+
2
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
function textContent(value) {
|
|
111
|
+
return { content: [{ type: "text", text: toText(value) }] };
|
|
112
|
+
}
|
|
113
|
+
function errorContent(err) {
|
|
114
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
115
|
+
return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/tools/market/index.ts
|
|
119
|
+
function registerMarketTools(server2) {
|
|
120
|
+
server2.tool(
|
|
121
|
+
"pionex.market.get_depth",
|
|
122
|
+
"Get order book depth (bids and asks) for a symbol. Use for spread, liquidity, or best bid/ask.",
|
|
123
|
+
{
|
|
124
|
+
schema: z.object({
|
|
125
|
+
symbol: z.string().describe("e.g. BTC_USDT"),
|
|
126
|
+
limit: z.number().int().min(1).max(100).optional().describe("Price levels, default 5")
|
|
127
|
+
})
|
|
128
|
+
},
|
|
129
|
+
async (paramsRaw) => {
|
|
130
|
+
try {
|
|
131
|
+
const params = paramsRaw && typeof paramsRaw.schema === "object" ? paramsRaw.schema : paramsRaw;
|
|
132
|
+
const { symbol, limit } = params;
|
|
133
|
+
const q = { symbol };
|
|
134
|
+
if (limit != null) q.limit = limit;
|
|
135
|
+
const data = await publicGet("/api/v1/market/depth", q);
|
|
136
|
+
return textContent(data);
|
|
137
|
+
} catch (e) {
|
|
138
|
+
return errorContent(e);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
server2.tool(
|
|
143
|
+
"pionex.market.get_trades",
|
|
144
|
+
"Get recent trades for a symbol. Use for latest price and volume.",
|
|
145
|
+
{
|
|
146
|
+
schema: z.object({
|
|
147
|
+
symbol: z.string().describe("e.g. BTC_USDT"),
|
|
148
|
+
limit: z.number().int().min(1).max(100).optional().describe("Default 5")
|
|
149
|
+
})
|
|
150
|
+
},
|
|
151
|
+
async (paramsRaw) => {
|
|
152
|
+
try {
|
|
153
|
+
const params = paramsRaw && typeof paramsRaw.schema === "object" ? paramsRaw.schema : paramsRaw;
|
|
154
|
+
const { symbol, limit } = params;
|
|
155
|
+
const q = { symbol };
|
|
156
|
+
if (limit != null) q.limit = limit;
|
|
157
|
+
const data = await publicGet("/api/v1/market/trades", q);
|
|
158
|
+
return textContent(data);
|
|
159
|
+
} catch (e) {
|
|
160
|
+
return errorContent(e);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
server2.tool(
|
|
165
|
+
"pionex.market.get_symbol_info",
|
|
166
|
+
"Get symbol metadata (precision, min size, price limits). Call before placing orders to avoid TRADE_AMOUNT_FILTER_DENIED errors.",
|
|
167
|
+
{
|
|
168
|
+
schema: z.object({
|
|
169
|
+
symbols: z.string().optional().describe(
|
|
170
|
+
'Optional. One or more symbols, comma-separated, e.g. "BTC_USDT" or "BTC_USDT,ADA_USDT".'
|
|
171
|
+
),
|
|
172
|
+
type: z.enum(["SPOT", "PERP"]).optional().describe("Optional. If no symbols are specified, filter by type (default is SPOT).")
|
|
173
|
+
})
|
|
174
|
+
},
|
|
175
|
+
async (paramsRaw) => {
|
|
176
|
+
try {
|
|
177
|
+
const params = paramsRaw && typeof paramsRaw.schema === "object" ? paramsRaw.schema : paramsRaw;
|
|
178
|
+
const { symbols, type } = params ?? {};
|
|
179
|
+
const q = {};
|
|
180
|
+
if (symbols) q.symbols = symbols;
|
|
181
|
+
if (!symbols && type) q.type = type;
|
|
182
|
+
const data = await publicGet("/api/v1/common/symbols", q);
|
|
183
|
+
return textContent(data);
|
|
184
|
+
} catch (e) {
|
|
185
|
+
return errorContent(e);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
server2.tool(
|
|
190
|
+
"pionex.market.get_tickers",
|
|
191
|
+
"Get 24-hour ticker(s): open, close, high, low, volume, amount, count. Optional symbol or type (SPOT/PERP).",
|
|
192
|
+
{
|
|
193
|
+
schema: z.object({
|
|
194
|
+
symbol: z.string().optional().describe("e.g. BTC_USDT; if omitted, returns all tickers filtered by type"),
|
|
195
|
+
type: z.enum(["SPOT", "PERP"]).optional().describe("If symbol is not specified, filter by type (default SPOT).")
|
|
196
|
+
})
|
|
197
|
+
},
|
|
198
|
+
async (paramsRaw) => {
|
|
199
|
+
try {
|
|
200
|
+
const params = paramsRaw && typeof paramsRaw.schema === "object" ? paramsRaw.schema : paramsRaw;
|
|
201
|
+
const q = {};
|
|
202
|
+
if (params?.symbol) q.symbol = params.symbol;
|
|
203
|
+
if (params?.type) q.type = params.type;
|
|
204
|
+
const data = await publicGet("/api/v1/market/tickers", q);
|
|
205
|
+
return textContent(data);
|
|
206
|
+
} catch (e) {
|
|
207
|
+
return errorContent(e);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
server2.tool(
|
|
212
|
+
"pionex.market.get_klines",
|
|
213
|
+
"Get OHLCV klines (candlestick) for a symbol. Use for charts or historical price/volume.",
|
|
214
|
+
{
|
|
215
|
+
schema: z.object({
|
|
216
|
+
symbol: z.string().describe("e.g. BTC_USDT"),
|
|
217
|
+
interval: z.enum(["1M", "5M", "15M", "30M", "60M", "4H", "8H", "12H", "1D"]).describe("Kline interval."),
|
|
218
|
+
endTime: z.number().int().optional().describe("End time in milliseconds."),
|
|
219
|
+
limit: z.number().int().min(1).max(500).optional().describe("Default 100.")
|
|
220
|
+
})
|
|
221
|
+
},
|
|
222
|
+
async (paramsRaw) => {
|
|
223
|
+
try {
|
|
224
|
+
const params = paramsRaw && typeof paramsRaw.schema === "object" ? paramsRaw.schema : paramsRaw;
|
|
225
|
+
const { symbol, interval, endTime, limit } = params;
|
|
226
|
+
const q = { symbol, interval };
|
|
227
|
+
if (endTime != null) q.endTime = endTime;
|
|
228
|
+
if (limit != null) q.limit = limit;
|
|
229
|
+
const data = await publicGet("/api/v1/market/klines", q);
|
|
230
|
+
return textContent(data);
|
|
231
|
+
} catch (e) {
|
|
232
|
+
return errorContent(e);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/tools/account/index.ts
|
|
239
|
+
import { z as z2 } from "zod";
|
|
240
|
+
function registerAccountTools(server2) {
|
|
241
|
+
server2.tool(
|
|
242
|
+
"pionex.account.get_balance",
|
|
243
|
+
"Query spot account balances for all currencies. Requires API key and secret in ~/.pionex/config.toml or env.",
|
|
244
|
+
{
|
|
245
|
+
// Accept any input; handler ignores params.
|
|
246
|
+
// This avoids incompatibilities between clients that send {} vs { schema: {} }.
|
|
247
|
+
schema: z2.any()
|
|
248
|
+
},
|
|
249
|
+
async (paramsRaw) => {
|
|
250
|
+
try {
|
|
251
|
+
const _params = paramsRaw && typeof paramsRaw.schema === "object" ? paramsRaw.schema : paramsRaw ?? {};
|
|
252
|
+
void _params;
|
|
253
|
+
const data = await signedGet("/api/v1/account/balances");
|
|
254
|
+
return textContent(data);
|
|
255
|
+
} catch (e) {
|
|
256
|
+
return errorContent(e);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// src/tools/orders/index.ts
|
|
263
|
+
import { z as z3 } from "zod";
|
|
264
|
+
function registerOrdersTools(server2) {
|
|
265
|
+
server2.tool(
|
|
266
|
+
"pionex.orders.new_order",
|
|
267
|
+
'Create a spot order on Pionex. For LIMIT orders, you must provide `symbol`, `side`, `type="LIMIT"`, `price` and `size` (quantity). For MARKET BUY orders, you must provide `symbol`, `side="BUY"`, `type="MARKET"`, and `amount` (quote amount to spend). For MARKET SELL orders, you must provide `symbol`, `side="SELL"`, `type="MARKET"`, and `size` (base quantity). Common error codes: TRADE_AMOUNT_FILTER_DENIED, TRADE_SIZE_FILTER_DENIED, TRADE_NOT_ENOUGH_MONEY, TRADE_INVALID_SYMBOL.',
|
|
268
|
+
{
|
|
269
|
+
schema: z3.object({
|
|
270
|
+
symbol: z3.string().describe("e.g. BTC_USDT"),
|
|
271
|
+
side: z3.enum(["BUY", "SELL"]),
|
|
272
|
+
type: z3.enum(["LIMIT", "MARKET"]),
|
|
273
|
+
clientOrderId: z3.string().max(64).optional(),
|
|
274
|
+
size: z3.string().optional().describe("Quantity; required for limit and market sell"),
|
|
275
|
+
price: z3.string().optional().describe("Required for limit order"),
|
|
276
|
+
amount: z3.string().optional().describe("Buying amount; required for market buy"),
|
|
277
|
+
IOC: z3.boolean().optional().describe("Immediate-or-cancel, default false")
|
|
278
|
+
})
|
|
279
|
+
},
|
|
280
|
+
async (paramsRaw) => {
|
|
281
|
+
try {
|
|
282
|
+
const params = paramsRaw && typeof paramsRaw.schema === "object" ? paramsRaw.schema : paramsRaw;
|
|
283
|
+
const body = {};
|
|
284
|
+
if (params.symbol) body.symbol = params.symbol;
|
|
285
|
+
if (params.side) body.side = params.side;
|
|
286
|
+
if (params.type) body.type = params.type;
|
|
287
|
+
if (params.clientOrderId != null) body.clientOrderId = params.clientOrderId;
|
|
288
|
+
if (params.size != null) body.size = params.size;
|
|
289
|
+
if (params.price != null) body.price = params.price;
|
|
290
|
+
if (params.amount != null) body.amount = params.amount;
|
|
291
|
+
if (params.IOC != null) body.IOC = params.IOC;
|
|
292
|
+
const data = await signedPost("/api/v1/trade/order", body);
|
|
293
|
+
return textContent(data);
|
|
294
|
+
} catch (e) {
|
|
295
|
+
return errorContent(e);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
server2.tool(
|
|
300
|
+
"pionex.orders.get_order",
|
|
301
|
+
"Get a single order by order ID.",
|
|
302
|
+
{
|
|
303
|
+
schema: z3.object({
|
|
304
|
+
symbol: z3.string().describe("e.g. BTC_USDT"),
|
|
305
|
+
orderId: z3.number().int().describe("Order id")
|
|
306
|
+
})
|
|
307
|
+
},
|
|
308
|
+
async (paramsRaw) => {
|
|
309
|
+
try {
|
|
310
|
+
const params = paramsRaw && typeof paramsRaw.schema === "object" ? paramsRaw.schema : paramsRaw;
|
|
311
|
+
const { symbol, orderId } = params;
|
|
312
|
+
const data = await signedGet("/api/v1/trade/order", { symbol, orderId });
|
|
313
|
+
return textContent(data);
|
|
314
|
+
} catch (e) {
|
|
315
|
+
return errorContent(e);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
);
|
|
319
|
+
server2.tool(
|
|
320
|
+
"pionex.orders.get_order_by_client_order_id",
|
|
321
|
+
"Get a single order by client order ID.",
|
|
322
|
+
{
|
|
323
|
+
schema: z3.object({
|
|
324
|
+
symbol: z3.string().describe("e.g. BTC_USDT"),
|
|
325
|
+
clientOrderId: z3.string().describe("Client order id")
|
|
326
|
+
})
|
|
327
|
+
},
|
|
328
|
+
async (paramsRaw) => {
|
|
329
|
+
try {
|
|
330
|
+
const params = paramsRaw && typeof paramsRaw.schema === "object" ? paramsRaw.schema : paramsRaw;
|
|
331
|
+
const { symbol, clientOrderId } = params;
|
|
332
|
+
const data = await signedGet("/api/v1/trade/orderByClientOrderId", {
|
|
333
|
+
symbol,
|
|
334
|
+
clientOrderId
|
|
335
|
+
});
|
|
336
|
+
return textContent(data);
|
|
337
|
+
} catch (e) {
|
|
338
|
+
return errorContent(e);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
);
|
|
342
|
+
server2.tool(
|
|
343
|
+
"pionex.orders.get_open_orders",
|
|
344
|
+
"List open (unfilled) orders for a symbol.",
|
|
345
|
+
{ schema: z3.object({ symbol: z3.string().describe("e.g. BTC_USDT") }) },
|
|
346
|
+
async (paramsRaw) => {
|
|
347
|
+
try {
|
|
348
|
+
const params = paramsRaw && typeof paramsRaw.schema === "object" ? paramsRaw.schema : paramsRaw;
|
|
349
|
+
const { symbol } = params;
|
|
350
|
+
const data = await signedGet("/api/v1/trade/openOrders", { symbol });
|
|
351
|
+
return textContent(data);
|
|
352
|
+
} catch (e) {
|
|
353
|
+
return errorContent(e);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
);
|
|
357
|
+
server2.tool(
|
|
358
|
+
"pionex.orders.get_all_orders",
|
|
359
|
+
"List order history for a symbol (filled and cancelled), with optional limit.",
|
|
360
|
+
{
|
|
361
|
+
schema: z3.object({
|
|
362
|
+
symbol: z3.string().describe("e.g. BTC_USDT"),
|
|
363
|
+
limit: z3.number().int().min(1).max(100).optional().describe("Default 1")
|
|
364
|
+
})
|
|
365
|
+
},
|
|
366
|
+
async (paramsRaw) => {
|
|
367
|
+
try {
|
|
368
|
+
const params = paramsRaw && typeof paramsRaw.schema === "object" ? paramsRaw.schema : paramsRaw;
|
|
369
|
+
const { symbol, limit } = params;
|
|
370
|
+
const q = { symbol };
|
|
371
|
+
if (limit != null) q.limit = limit;
|
|
372
|
+
const data = await signedGet("/api/v1/trade/allOrders", q);
|
|
373
|
+
return textContent(data);
|
|
374
|
+
} catch (e) {
|
|
375
|
+
return errorContent(e);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
);
|
|
379
|
+
server2.tool(
|
|
380
|
+
"pionex.orders.cancel_order",
|
|
381
|
+
"Cancel an open order by order ID.",
|
|
382
|
+
{
|
|
383
|
+
schema: z3.object({
|
|
384
|
+
symbol: z3.string().describe("e.g. BTC_USDT"),
|
|
385
|
+
orderId: z3.number().int().describe("Order id")
|
|
386
|
+
})
|
|
387
|
+
},
|
|
388
|
+
async (paramsRaw) => {
|
|
389
|
+
try {
|
|
390
|
+
const params = paramsRaw && typeof paramsRaw.schema === "object" ? paramsRaw.schema : paramsRaw;
|
|
391
|
+
const { symbol, orderId } = params;
|
|
392
|
+
const data = await signedDelete("/api/v1/trade/order", { symbol, orderId });
|
|
393
|
+
return textContent(data);
|
|
394
|
+
} catch (e) {
|
|
395
|
+
return errorContent(e);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
);
|
|
399
|
+
server2.tool(
|
|
400
|
+
"pionex.orders.get_fills",
|
|
401
|
+
"Get filled trades (fills) for a symbol in a time range. Requires API key. Returns up to 100 latest fills.",
|
|
402
|
+
{
|
|
403
|
+
schema: z3.object({
|
|
404
|
+
symbol: z3.string().describe("e.g. BTC_USDT"),
|
|
405
|
+
startTime: z3.number().int().optional().describe("Start time in milliseconds."),
|
|
406
|
+
endTime: z3.number().int().optional().describe("End time in milliseconds.")
|
|
407
|
+
})
|
|
408
|
+
},
|
|
409
|
+
async (paramsRaw) => {
|
|
410
|
+
try {
|
|
411
|
+
const params = paramsRaw && typeof paramsRaw.schema === "object" ? paramsRaw.schema : paramsRaw;
|
|
412
|
+
const { symbol, startTime, endTime } = params;
|
|
413
|
+
const q = { symbol };
|
|
414
|
+
if (startTime != null) q.startTime = startTime;
|
|
415
|
+
if (endTime != null) q.endTime = endTime;
|
|
416
|
+
const data = await signedGet("/api/v1/trade/fills", q);
|
|
417
|
+
return textContent(data);
|
|
418
|
+
} catch (e) {
|
|
419
|
+
return errorContent(e);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
);
|
|
423
|
+
server2.tool(
|
|
424
|
+
"pionex.orders.cancel_all_orders",
|
|
425
|
+
"Cancel all open orders for a symbol.",
|
|
426
|
+
{
|
|
427
|
+
schema: z3.object({
|
|
428
|
+
symbol: z3.string().describe("e.g. BTC_USDT")
|
|
429
|
+
})
|
|
430
|
+
},
|
|
431
|
+
async (paramsRaw) => {
|
|
432
|
+
try {
|
|
433
|
+
const params = paramsRaw && typeof paramsRaw.schema === "object" ? paramsRaw.schema : paramsRaw;
|
|
434
|
+
const { symbol } = params;
|
|
435
|
+
const data = await signedDelete("/api/v1/trade/allOrders", { symbol });
|
|
436
|
+
return textContent(data);
|
|
437
|
+
} catch (e) {
|
|
438
|
+
return errorContent(e);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// src/run-server.ts
|
|
445
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
446
|
+
var server = new McpServer(
|
|
447
|
+
{
|
|
448
|
+
name: "pionex-trade-mcp",
|
|
449
|
+
version: "0.2.0"
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
capabilities: {
|
|
453
|
+
tools: {}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
);
|
|
457
|
+
registerMarketTools(server);
|
|
458
|
+
registerAccountTools(server);
|
|
459
|
+
registerOrdersTools(server);
|
|
460
|
+
var transport = new StdioServerTransport();
|
|
461
|
+
await server.connect(transport);
|
|
462
|
+
//# sourceMappingURL=run-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/run-server.ts","../src/tools/market/index.ts","../src/tools/common/client.ts","../src/tools/common/utils.ts","../src/tools/account/index.ts","../src/tools/orders/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { registerMarketTools } from \"./tools/market/index.js\";\nimport { registerAccountTools } from \"./tools/account/index.js\";\nimport { registerOrdersTools } from \"./tools/orders/index.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nconst server = new McpServer(\n {\n name: \"pionex-trade-mcp\",\n version: \"0.2.0\",\n },\n {\n capabilities: {\n tools: {},\n },\n },\n);\n\nregisterMarketTools(server);\nregisterAccountTools(server);\nregisterOrdersTools(server);\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n\n","import { z } from \"zod\";\nimport { publicGet } from \"../common/client.js\";\nimport { textContent, errorContent } from \"../common/utils.js\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nexport function registerMarketTools(server: McpServer): void {\n server.tool(\n \"pionex.market.get_depth\",\n \"Get order book depth (bids and asks) for a symbol. Use for spread, liquidity, or best bid/ask.\",\n {\n schema: z.object({\n symbol: z.string().describe(\"e.g. BTC_USDT\"),\n limit: z.number().int().min(1).max(100).optional().describe(\"Price levels, default 5\"),\n }),\n },\n async (paramsRaw) => {\n try {\n const params =\n paramsRaw && typeof (paramsRaw as any).schema === \"object\"\n ? (paramsRaw as any).schema\n : (paramsRaw as any);\n const { symbol, limit } = params;\n const q: Record<string, unknown> = { symbol };\n if (limit != null) q.limit = limit;\n const data = await publicGet(\"/api/v1/market/depth\", q);\n return textContent(data);\n } catch (e) {\n return errorContent(e);\n }\n },\n );\n\n server.tool(\n \"pionex.market.get_trades\",\n \"Get recent trades for a symbol. Use for latest price and volume.\",\n {\n schema: z.object({\n symbol: z.string().describe(\"e.g. BTC_USDT\"),\n limit: z.number().int().min(1).max(100).optional().describe(\"Default 5\"),\n }),\n },\n async (paramsRaw) => {\n try {\n const params =\n paramsRaw && typeof (paramsRaw as any).schema === \"object\"\n ? (paramsRaw as any).schema\n : (paramsRaw as any);\n const { symbol, limit } = params;\n const q: Record<string, unknown> = { symbol };\n if (limit != null) q.limit = limit;\n const data = await publicGet(\"/api/v1/market/trades\", q);\n return textContent(data);\n } catch (e) {\n return errorContent(e);\n }\n },\n );\n\n server.tool(\n \"pionex.market.get_symbol_info\",\n \"Get symbol metadata (precision, min size, price limits). Call before placing orders to avoid TRADE_AMOUNT_FILTER_DENIED errors.\",\n {\n schema: z.object({\n symbols: z\n .string()\n .optional()\n .describe(\n 'Optional. One or more symbols, comma-separated, e.g. \"BTC_USDT\" or \"BTC_USDT,ADA_USDT\".',\n ),\n type: z\n .enum([\"SPOT\", \"PERP\"])\n .optional()\n .describe(\"Optional. If no symbols are specified, filter by type (default is SPOT).\"),\n }),\n },\n async (paramsRaw) => {\n try {\n const params =\n paramsRaw && typeof (paramsRaw as any).schema === \"object\"\n ? (paramsRaw as any).schema\n : (paramsRaw as any);\n const { symbols, type } = params ?? {};\n const q: Record<string, unknown> = {};\n if (symbols) q.symbols = symbols;\n if (!symbols && type) q.type = type;\n const data = await publicGet(\"/api/v1/common/symbols\", q);\n return textContent(data);\n } catch (e) {\n return errorContent(e);\n }\n },\n );\n\n server.tool(\n \"pionex.market.get_tickers\",\n \"Get 24-hour ticker(s): open, close, high, low, volume, amount, count. Optional symbol or type (SPOT/PERP).\",\n {\n schema: z.object({\n symbol: z\n .string()\n .optional()\n .describe(\"e.g. BTC_USDT; if omitted, returns all tickers filtered by type\"),\n type: z\n .enum([\"SPOT\", \"PERP\"])\n .optional()\n .describe(\"If symbol is not specified, filter by type (default SPOT).\"),\n }),\n },\n async (paramsRaw) => {\n try {\n const params =\n paramsRaw && typeof (paramsRaw as any).schema === \"object\"\n ? (paramsRaw as any).schema\n : (paramsRaw as any);\n const q: Record<string, unknown> = {};\n if (params?.symbol) q.symbol = params.symbol;\n if (params?.type) q.type = params.type;\n const data = await publicGet(\"/api/v1/market/tickers\", q);\n return textContent(data);\n } catch (e) {\n return errorContent(e);\n }\n },\n );\n\n server.tool(\n \"pionex.market.get_klines\",\n \"Get OHLCV klines (candlestick) for a symbol. Use for charts or historical price/volume.\",\n {\n schema: z.object({\n symbol: z.string().describe(\"e.g. BTC_USDT\"),\n interval: z\n .enum([\"1M\", \"5M\", \"15M\", \"30M\", \"60M\", \"4H\", \"8H\", \"12H\", \"1D\"])\n .describe(\"Kline interval.\"),\n endTime: z.number().int().optional().describe(\"End time in milliseconds.\"),\n limit: z.number().int().min(1).max(500).optional().describe(\"Default 100.\"),\n }),\n },\n async (paramsRaw) => {\n try {\n const params =\n paramsRaw && typeof (paramsRaw as any).schema === \"object\"\n ? (paramsRaw as any).schema\n : (paramsRaw as any);\n const { symbol, interval, endTime, limit } = params;\n const q: Record<string, unknown> = { symbol, interval };\n if (endTime != null) q.endTime = endTime;\n if (limit != null) q.limit = limit;\n const data = await publicGet(\"/api/v1/market/klines\", q);\n return textContent(data);\n } catch (e) {\n return errorContent(e);\n }\n },\n );\n}\n\n","import crypto from \"crypto\";\nimport process from \"process\";\nimport fetch from \"node-fetch\";\n\nconst API_KEY_ENV = \"PIONEX_API_KEY\";\nconst API_SECRET_ENV = \"PIONEX_API_SECRET\";\nconst BASE_URL_ENV = \"PIONEX_BASE_URL\";\n\nexport function getBaseUrl(): string {\n return process.env[BASE_URL_ENV] || \"https://api.pionex.com\";\n}\n\nexport function isAuthenticated(): boolean {\n return Boolean(process.env[API_KEY_ENV] && process.env[API_SECRET_ENV]);\n}\n\nexport function requireAuth(): void {\n if (!isAuthenticated()) {\n throw new Error(\n \"This tool requires authentication. Run 'pionex config init' to create ~/.pionex/config.toml, or set PIONEX_API_KEY and PIONEX_API_SECRET.\",\n );\n }\n}\n\nfunction getApiKey(): string {\n const v = process.env[API_KEY_ENV];\n if (!v) throw new Error(`Environment variable ${API_KEY_ENV} is required.`);\n return v;\n}\n\nfunction getApiSecret(): string {\n const v = process.env[API_SECRET_ENV];\n if (!v) throw new Error(`Environment variable ${API_SECRET_ENV} is required.`);\n return v;\n}\n\nfunction buildSignedRequest(\n method: \"GET\" | \"POST\" | \"DELETE\",\n path: string,\n query: Record<string, unknown>,\n bodyJson: string | null = null,\n): { url: string; headers: Record<string, string>; bodyJson: string | null } {\n const baseUrl = getBaseUrl();\n const apiKey = getApiKey();\n const apiSecret = getApiSecret();\n const timestamp = Date.now().toString();\n const params: Record<string, unknown> = { ...query, timestamp };\n\n const sortedKeys = Object.keys(params).sort();\n const queryString = sortedKeys.map((k) => `${k}=${params[k]}`).join(\"&\");\n const pathUrl = `${path}?${queryString}`;\n let payload = `${method}${pathUrl}`;\n if (bodyJson != null) payload += bodyJson;\n\n const signature = crypto.createHmac(\"sha256\", apiSecret).update(payload).digest(\"hex\");\n\n const url = `${baseUrl}${pathUrl}`;\n const headers = {\n \"PIONEX-KEY\": apiKey,\n \"PIONEX-SIGNATURE\": signature,\n \"Content-Type\": \"application/json\",\n };\n return { url, headers, bodyJson };\n}\n\nexport async function publicGet(\n path: string,\n query: Record<string, unknown> = {},\n): Promise<unknown> {\n const baseUrl = getBaseUrl();\n const qs = new URLSearchParams(query as Record<string, string>).toString();\n const url = qs ? `${baseUrl}${path}?${qs}` : `${baseUrl}${path}`;\n const res = await fetch(url, { method: \"GET\", headers: { \"Content-Type\": \"application/json\" } });\n if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);\n return res.json();\n}\n\nexport async function signedGet(\n path: string,\n query: Record<string, unknown> = {},\n): Promise<unknown> {\n requireAuth();\n const { url, headers } = buildSignedRequest(\"GET\", path, query);\n const res = await fetch(url, { method: \"GET\", headers });\n if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);\n return res.json();\n}\n\nexport async function signedPost(path: string, body: Record<string, unknown>): Promise<unknown> {\n requireAuth();\n const bodyJson = JSON.stringify(body);\n const { url, headers } = buildSignedRequest(\"POST\", path, {}, bodyJson);\n const res = await fetch(url, {\n method: \"POST\",\n headers,\n body: bodyJson,\n });\n if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);\n return res.json();\n}\n\nexport async function signedDelete(\n path: string,\n body: Record<string, unknown>,\n): Promise<unknown> {\n requireAuth();\n const bodyJson = JSON.stringify(body);\n const { url, headers } = buildSignedRequest(\"DELETE\", path, {}, bodyJson);\n const res = await fetch(url, {\n method: \"DELETE\",\n headers,\n body: bodyJson,\n });\n if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);\n return res.json();\n}\n\n","export function toText(value: unknown): string {\n return JSON.stringify(\n value,\n (_, v) => (typeof v === \"bigint\" ? v.toString() : v),\n 2,\n );\n}\n\nexport function textContent(value: unknown): { content: { type: \"text\"; text: string }[] } {\n return { content: [{ type: \"text\", text: toText(value) }] };\n}\n\nexport function errorContent(\n err: unknown,\n): { content: { type: \"text\"; text: string }[]; isError: true } {\n const message = err instanceof Error ? err.message : String(err);\n return { content: [{ type: \"text\", text: `Error: ${message}` }], isError: true };\n}\n\n","import { z } from \"zod\";\nimport { signedGet } from \"../common/client.js\";\nimport { textContent, errorContent } from \"../common/utils.js\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nexport function registerAccountTools(server: McpServer): void {\n server.tool(\n \"pionex.account.get_balance\",\n \"Query spot account balances for all currencies. Requires API key and secret in ~/.pionex/config.toml or env.\",\n {\n // Accept any input; handler ignores params.\n // This avoids incompatibilities between clients that send {} vs { schema: {} }.\n schema: z.any(),\n },\n async (paramsRaw) => {\n try {\n // Some clients (or adapters) may wrap arguments as { schema: {...} }.\n // For this zero-arg tool we just normalize and ignore them.\n const _params =\n paramsRaw && typeof (paramsRaw as any).schema === \"object\"\n ? (paramsRaw as any).schema\n : paramsRaw ?? {};\n\n void _params;\n\n const data = await signedGet(\"/api/v1/account/balances\");\n return textContent(data);\n } catch (e) {\n return errorContent(e);\n }\n },\n );\n}\n\n","import { z } from \"zod\";\nimport { signedGet, signedPost, signedDelete } from \"../common/client.js\";\nimport { textContent, errorContent } from \"../common/utils.js\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nexport function registerOrdersTools(server: McpServer): void {\n server.tool(\n \"pionex.orders.new_order\",\n \"Create a spot order on Pionex. \" +\n \"For LIMIT orders, you must provide `symbol`, `side`, `type=\\\"LIMIT\\\"`, `price` and `size` (quantity). \" +\n \"For MARKET BUY orders, you must provide `symbol`, `side=\\\"BUY\\\"`, `type=\\\"MARKET\\\"`, and `amount` (quote amount to spend). \" +\n \"For MARKET SELL orders, you must provide `symbol`, `side=\\\"SELL\\\"`, `type=\\\"MARKET\\\"`, and `size` (base quantity). \" +\n \"Common error codes: TRADE_AMOUNT_FILTER_DENIED, TRADE_SIZE_FILTER_DENIED, TRADE_NOT_ENOUGH_MONEY, TRADE_INVALID_SYMBOL.\",\n {\n schema: z.object({\n symbol: z.string().describe(\"e.g. BTC_USDT\"),\n side: z.enum([\"BUY\", \"SELL\"]),\n type: z.enum([\"LIMIT\", \"MARKET\"]),\n clientOrderId: z.string().max(64).optional(),\n size: z.string().optional().describe(\"Quantity; required for limit and market sell\"),\n price: z.string().optional().describe(\"Required for limit order\"),\n amount: z.string().optional().describe(\"Buying amount; required for market buy\"),\n IOC: z.boolean().optional().describe(\"Immediate-or-cancel, default false\"),\n }),\n },\n async (paramsRaw) => {\n try {\n const params =\n paramsRaw && typeof (paramsRaw as any).schema === \"object\"\n ? (paramsRaw as any).schema\n : (paramsRaw as any);\n const body: Record<string, unknown> = {};\n if (params.symbol) body.symbol = params.symbol;\n if (params.side) body.side = params.side;\n if (params.type) body.type = params.type;\n if (params.clientOrderId != null) body.clientOrderId = params.clientOrderId;\n if (params.size != null) body.size = params.size;\n if (params.price != null) body.price = params.price;\n if (params.amount != null) body.amount = params.amount;\n if (params.IOC != null) body.IOC = params.IOC;\n const data = await signedPost(\"/api/v1/trade/order\", body);\n return textContent(data);\n } catch (e) {\n return errorContent(e);\n }\n },\n );\n\n server.tool(\n \"pionex.orders.get_order\",\n \"Get a single order by order ID.\",\n {\n schema: z.object({\n symbol: z.string().describe(\"e.g. BTC_USDT\"),\n orderId: z.number().int().describe(\"Order id\"),\n }),\n },\n async (paramsRaw) => {\n try {\n const params =\n paramsRaw && typeof (paramsRaw as any).schema === \"object\"\n ? (paramsRaw as any).schema\n : (paramsRaw as any);\n const { symbol, orderId } = params;\n const data = await signedGet(\"/api/v1/trade/order\", { symbol, orderId });\n return textContent(data);\n } catch (e) {\n return errorContent(e);\n }\n },\n );\n\n server.tool(\n \"pionex.orders.get_order_by_client_order_id\",\n \"Get a single order by client order ID.\",\n {\n schema: z.object({\n symbol: z.string().describe(\"e.g. BTC_USDT\"),\n clientOrderId: z.string().describe(\"Client order id\"),\n }),\n },\n async (paramsRaw) => {\n try {\n const params =\n paramsRaw && typeof (paramsRaw as any).schema === \"object\"\n ? (paramsRaw as any).schema\n : (paramsRaw as any);\n const { symbol, clientOrderId } = params;\n const data = await signedGet(\"/api/v1/trade/orderByClientOrderId\", {\n symbol,\n clientOrderId,\n });\n return textContent(data);\n } catch (e) {\n return errorContent(e);\n }\n },\n );\n\n server.tool(\n \"pionex.orders.get_open_orders\",\n \"List open (unfilled) orders for a symbol.\",\n { schema: z.object({ symbol: z.string().describe(\"e.g. BTC_USDT\") }) },\n async (paramsRaw) => {\n try {\n const params =\n paramsRaw && typeof (paramsRaw as any).schema === \"object\"\n ? (paramsRaw as any).schema\n : (paramsRaw as any);\n const { symbol } = params;\n const data = await signedGet(\"/api/v1/trade/openOrders\", { symbol });\n return textContent(data);\n } catch (e) {\n return errorContent(e);\n }\n },\n );\n\n server.tool(\n \"pionex.orders.get_all_orders\",\n \"List order history for a symbol (filled and cancelled), with optional limit.\",\n {\n schema: z.object({\n symbol: z.string().describe(\"e.g. BTC_USDT\"),\n limit: z.number().int().min(1).max(100).optional().describe(\"Default 1\"),\n }),\n },\n async (paramsRaw) => {\n try {\n const params =\n paramsRaw && typeof (paramsRaw as any).schema === \"object\"\n ? (paramsRaw as any).schema\n : (paramsRaw as any);\n const { symbol, limit } = params;\n const q: Record<string, unknown> = { symbol };\n if (limit != null) q.limit = limit;\n const data = await signedGet(\"/api/v1/trade/allOrders\", q);\n return textContent(data);\n } catch (e) {\n return errorContent(e);\n }\n },\n );\n\n server.tool(\n \"pionex.orders.cancel_order\",\n \"Cancel an open order by order ID.\",\n {\n schema: z.object({\n symbol: z.string().describe(\"e.g. BTC_USDT\"),\n orderId: z.number().int().describe(\"Order id\"),\n }),\n },\n async (paramsRaw) => {\n try {\n const params =\n paramsRaw && typeof (paramsRaw as any).schema === \"object\"\n ? (paramsRaw as any).schema\n : (paramsRaw as any);\n const { symbol, orderId } = params;\n const data = await signedDelete(\"/api/v1/trade/order\", { symbol, orderId });\n return textContent(data);\n } catch (e) {\n return errorContent(e);\n }\n },\n );\n\n server.tool(\n \"pionex.orders.get_fills\",\n \"Get filled trades (fills) for a symbol in a time range. Requires API key. Returns up to 100 latest fills.\",\n {\n schema: z.object({\n symbol: z.string().describe(\"e.g. BTC_USDT\"),\n startTime: z.number().int().optional().describe(\"Start time in milliseconds.\"),\n endTime: z.number().int().optional().describe(\"End time in milliseconds.\"),\n }),\n },\n async (paramsRaw) => {\n try {\n const params =\n paramsRaw && typeof (paramsRaw as any).schema === \"object\"\n ? (paramsRaw as any).schema\n : (paramsRaw as any);\n const { symbol, startTime, endTime } = params;\n const q: Record<string, unknown> = { symbol };\n if (startTime != null) q.startTime = startTime;\n if (endTime != null) q.endTime = endTime;\n const data = await signedGet(\"/api/v1/trade/fills\", q);\n return textContent(data);\n } catch (e) {\n return errorContent(e);\n }\n },\n );\n\n server.tool(\n \"pionex.orders.cancel_all_orders\",\n \"Cancel all open orders for a symbol.\",\n {\n schema: z.object({\n symbol: z.string().describe(\"e.g. BTC_USDT\"),\n }),\n },\n async (paramsRaw) => {\n try {\n const params =\n paramsRaw && typeof (paramsRaw as any).schema === \"object\"\n ? (paramsRaw as any).schema\n : (paramsRaw as any);\n const { symbol } = params;\n const data = await signedDelete(\"/api/v1/trade/allOrders\", { symbol });\n return textContent(data);\n } catch (e) {\n return errorContent(e);\n }\n },\n );\n}\n\n"],"mappings":"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","names":["server","z","server","z","z","server","z"]}
|