@aicoin/trade-mcp 1.0.5

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/.env.example ADDED
@@ -0,0 +1,41 @@
1
+ # AiCoin Trade MCP Configuration
2
+ # Copy this file to .env and fill in your values
3
+
4
+ # Default exchange
5
+ DEFAULT_EXCHANGE=binance
6
+
7
+ # Supported: binance, binanceusdm, binancecoinm,
8
+ # okx, bybit, bitget, gate,
9
+ # huobi, pionex, hyperliquid
10
+
11
+ # --- Exchange API Keys ---
12
+
13
+ # BINANCE_API_KEY=your_api_key
14
+ # BINANCE_SECRET=your_secret
15
+
16
+ # OKX_API_KEY=your_api_key
17
+ # OKX_SECRET=your_secret
18
+ # OKX_PASSPHRASE=your_passphrase
19
+
20
+ # BYBIT_API_KEY=your_api_key
21
+ # BYBIT_SECRET=your_secret
22
+
23
+ # BITGET_API_KEY=your_api_key
24
+ # BITGET_SECRET=your_secret
25
+ # BITGET_PASSPHRASE=your_passphrase
26
+
27
+ # GATE_API_KEY=your_api_key
28
+ # GATE_SECRET=your_secret
29
+
30
+ # HUOBI_API_KEY=your_api_key
31
+ # HUOBI_SECRET=your_secret
32
+
33
+ # PIONEX_API_KEY=your_api_key
34
+ # PIONEX_SECRET=your_secret
35
+
36
+ # HYPERLIQUID_API_KEY=your_api_key
37
+ # HYPERLIQUID_SECRET=your_secret
38
+
39
+ # --- Proxy (optional) ---
40
+ # USE_PROXY=false
41
+ # PROXY_URL=http://127.0.0.1:7890
package/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # AiCoin Trade MCP Server
2
+
3
+ AI-powered cryptocurrency trading via ccxt.
4
+
5
+ ## Features
6
+
7
+ - 24 trading tools (9 public + 15 private)
8
+ - 9 supported exchanges
9
+ - Exchange API keys stored locally via env vars (never sent to cloud)
10
+ - stdio transport for local MCP clients
11
+
12
+ ## Quick Start
13
+
14
+ ```bash
15
+ npm install
16
+ cp .env.example .env
17
+ # Edit .env with your exchange API keys
18
+ npm run build
19
+ npm start
20
+ ```
21
+
22
+ ## Environment Variables
23
+
24
+ ```bash
25
+ DEFAULT_EXCHANGE=binance
26
+
27
+ BINANCE_API_KEY=your_key
28
+ BINANCE_SECRET=your_secret
29
+
30
+ OKX_API_KEY=your_key
31
+ OKX_SECRET=your_secret
32
+ OKX_PASSPHRASE=your_passphrase
33
+
34
+ BYBIT_API_KEY=your_key
35
+ BYBIT_SECRET=your_secret
36
+
37
+ USE_PROXY=false
38
+ PROXY_URL=http://127.0.0.1:7890
39
+ ```
40
+
41
+ ## Available Tools
42
+
43
+ ### Public (no API key required)
44
+
45
+ | Tool | Description |
46
+ |------|-------------|
47
+ | `list_exchanges` | List all 9 supported exchanges |
48
+ | `get_ticker` | Real-time ticker for a trading pair |
49
+ | `get_tickers` | Batch tickers for multiple pairs |
50
+ | `get_ohlcv` | K-line/candlestick data |
51
+ | `get_orderbook` | Order book depth |
52
+ | `get_trades` | Recent trades |
53
+ | `get_markets` | All trading pairs on an exchange |
54
+ | `get_funding_rates` | Perpetual contract funding rates |
55
+ | `get_funding_rate_history` | Historical funding rates |
56
+
57
+ ### Private (requires exchange API key)
58
+
59
+ | Tool | Description |
60
+ |------|-------------|
61
+ | `get_balance` | Account balance |
62
+ | `create_order` | Place market/limit order |
63
+ | `cancel_order` | Cancel an open order |
64
+ | `cancel_all_orders` | Cancel all open orders for a symbol |
65
+ | `get_order` | Get order details by ID |
66
+ | `get_open_orders` | List unfilled orders |
67
+ | `get_closed_orders` | List filled/cancelled orders |
68
+ | `get_my_trades` | Get personal trade history |
69
+ | `get_positions` | Open positions (futures/swap) |
70
+ | `set_leverage` | Set leverage multiplier |
71
+ | `set_margin_mode` | Set cross/isolated margin |
72
+ | `get_ledger` | Account ledger entries |
73
+ | `get_deposits` | Deposit history |
74
+ | `get_withdrawals` | Withdrawal history |
75
+ | `transfer` | Transfer between accounts |
76
+
77
+ ## Client Configuration
78
+
79
+ ### Claude Desktop
80
+
81
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
82
+
83
+ ```json
84
+ {
85
+ "mcpServers": {
86
+ "aicoin-trade": {
87
+ "command": "npx",
88
+ "args": ["-y", "@aicoin/trade-mcp"],
89
+ "env": {
90
+ "DEFAULT_EXCHANGE": "okx",
91
+ "OKX_API_KEY": "your_key",
92
+ "OKX_SECRET": "your_secret",
93
+ "OKX_PASSPHRASE": "your_passphrase"
94
+ }
95
+ }
96
+ }
97
+ }
98
+ ```
99
+
100
+ ### Cursor
101
+
102
+ Add to `.cursor/mcp.json`:
103
+
104
+ ```json
105
+ {
106
+ "mcpServers": {
107
+ "aicoin-trade": {
108
+ "command": "npx",
109
+ "args": ["-y", "@aicoin/trade-mcp"],
110
+ "env": {
111
+ "DEFAULT_EXCHANGE": "binance",
112
+ "BINANCE_API_KEY": "your_key",
113
+ "BINANCE_SECRET": "your_secret"
114
+ }
115
+ }
116
+ }
117
+ }
118
+ ```
119
+
120
+ ### Windsurf
121
+
122
+ Add to `~/.codeium/windsurf/mcp_config.json` (same format as Cursor).
123
+
124
+ ### VS Code (Copilot)
125
+
126
+ Add to `.vscode/mcp.json`:
127
+
128
+ ```json
129
+ {
130
+ "servers": {
131
+ "aicoin-trade": {
132
+ "type": "stdio",
133
+ "command": "npx",
134
+ "args": ["-y", "@aicoin/trade-mcp"],
135
+ "env": {
136
+ "DEFAULT_EXCHANGE": "okx",
137
+ "OKX_API_KEY": "your_key",
138
+ "OKX_SECRET": "your_secret",
139
+ "OKX_PASSPHRASE": "your_passphrase"
140
+ }
141
+ }
142
+ }
143
+ }
144
+ ```
145
+
146
+ ### Claude Code
147
+
148
+ ```bash
149
+ claude mcp add aicoin-trade -- npx -y @aicoin/trade-mcp
150
+ ```
151
+
152
+ ## Supported Exchanges
153
+
154
+ binance, binanceusdm, binancecoinm, okx, bybit, bitget, gate, huobi, hyperliquid
package/build/index.js ADDED
@@ -0,0 +1,721 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+
7
+ // src/tools/public.ts
8
+ import { z } from "zod";
9
+
10
+ // src/exchange/manager.ts
11
+ import * as ccxt from "ccxt";
12
+
13
+ // src/exchange/broker.ts
14
+ var BROKER_CONFIG = {
15
+ binance: {
16
+ options: {
17
+ broker: {
18
+ spot: "x-MGFCMH4U",
19
+ future: "x-FaeSBrMa",
20
+ delivery: "x-FaeSBrMa"
21
+ }
22
+ },
23
+ headers: {}
24
+ },
25
+ binanceusdm: {
26
+ options: {
27
+ broker: { future: "x-FaeSBrMa" }
28
+ },
29
+ headers: {}
30
+ },
31
+ binancecoinm: {
32
+ options: {
33
+ broker: { delivery: "x-FaeSBrMa" }
34
+ },
35
+ headers: {}
36
+ },
37
+ okx: {
38
+ options: { brokerId: "c6851dd5f01e4aBC" },
39
+ headers: {}
40
+ },
41
+ bybit: {
42
+ options: {},
43
+ headers: { Referer: "AiCoin" }
44
+ },
45
+ bitget: {
46
+ options: {},
47
+ headers: { "X-CHANNEL-API-CODE": "tpequ" }
48
+ },
49
+ gate: {
50
+ options: {},
51
+ headers: { "X-Gate-Channel-Id": "AiCoin1" }
52
+ },
53
+ huobi: {
54
+ options: {
55
+ broker: { id: "AAf0e4f2ef" }
56
+ },
57
+ headers: {}
58
+ },
59
+ pionex: {
60
+ options: { brokerId: "AiCoin2023" },
61
+ headers: {}
62
+ },
63
+ hyperliquid: {
64
+ options: {
65
+ broker: {
66
+ address: "0xc7580C3eFaeae84865B7Cd1ee11d9f3069F36E82"
67
+ }
68
+ },
69
+ headers: {}
70
+ }
71
+ };
72
+ function getBrokerOptions(exchangeId) {
73
+ const id = exchangeId.toLowerCase();
74
+ return BROKER_CONFIG[id] ?? { options: {}, headers: {} };
75
+ }
76
+
77
+ // src/exchange/manager.ts
78
+ var SUPPORTED_EXCHANGES = [
79
+ "binance",
80
+ "binanceusdm",
81
+ "binancecoinm",
82
+ "okx",
83
+ "bybit",
84
+ "bitget",
85
+ "gate",
86
+ "huobi",
87
+ "hyperliquid"
88
+ ];
89
+ var cache = {};
90
+ function buildOptions(config) {
91
+ const { exchangeId, marketType, apiKey, secret, passphrase, skipAuth } = config;
92
+ const id = exchangeId.toLowerCase();
93
+ const key = skipAuth ? void 0 : apiKey || process.env[`${id.toUpperCase()}_API_KEY`];
94
+ const sec = skipAuth ? void 0 : secret || process.env[`${id.toUpperCase()}_SECRET`];
95
+ const pass = skipAuth ? void 0 : passphrase || process.env[`${id.toUpperCase()}_PASSPHRASE`];
96
+ const broker = getBrokerOptions(id);
97
+ const opts = {
98
+ enableRateLimit: true,
99
+ options: {
100
+ ...broker.options,
101
+ ...marketType && marketType !== "spot" ? { defaultType: marketType } : {}
102
+ }
103
+ };
104
+ if (Object.keys(broker.headers).length > 0) {
105
+ opts.headers = broker.headers;
106
+ }
107
+ if (key) opts.apiKey = key;
108
+ if (sec) opts.secret = sec;
109
+ if (pass) opts.password = pass;
110
+ const proxyUrl = process.env.PROXY_URL;
111
+ if (process.env.USE_PROXY === "true" && proxyUrl) {
112
+ if (proxyUrl.startsWith("socks")) {
113
+ let socksUrl = proxyUrl;
114
+ if (socksUrl.startsWith("socks5://")) {
115
+ socksUrl = socksUrl.replace("socks5://", "socks5h://");
116
+ } else if (socksUrl.startsWith("socks4://")) {
117
+ socksUrl = socksUrl.replace("socks4://", "socks4a://");
118
+ }
119
+ opts.socksProxy = socksUrl;
120
+ } else if (proxyUrl.startsWith("https://")) {
121
+ opts.httpsProxy = proxyUrl;
122
+ } else if (proxyUrl.startsWith("http://")) {
123
+ opts.httpProxy = proxyUrl;
124
+ }
125
+ }
126
+ return opts;
127
+ }
128
+ function createInstance(id, opts) {
129
+ const ExchangeClass = ccxt[id];
130
+ if (typeof ExchangeClass !== "function") {
131
+ throw new Error(`Exchange '${id}' is not available in ccxt`);
132
+ }
133
+ return new ExchangeClass(opts);
134
+ }
135
+ function getExchange(exchangeId, marketType, options) {
136
+ const id = (exchangeId || process.env.DEFAULT_EXCHANGE || "binance").toLowerCase();
137
+ const type = marketType || "spot";
138
+ const skipAuth = options?.skipAuth ?? false;
139
+ const key = `${id}:${type}${skipAuth ? ":pub" : ""}`;
140
+ if (!cache[key]) {
141
+ if (!SUPPORTED_EXCHANGES.includes(id)) {
142
+ throw new Error(
143
+ `Exchange '${id}' not supported. Available: ${SUPPORTED_EXCHANGES.join(", ")}`
144
+ );
145
+ }
146
+ cache[key] = createInstance(id, buildOptions({ exchangeId: id, marketType: type, skipAuth }));
147
+ }
148
+ return cache[key];
149
+ }
150
+
151
+ // src/tools/utils.ts
152
+ function ok(data) {
153
+ return {
154
+ content: [{ type: "text", text: JSON.stringify(data) }]
155
+ };
156
+ }
157
+ function err(e) {
158
+ const message = e instanceof Error ? e.message : typeof e === "string" ? e : String(e);
159
+ return {
160
+ content: [{ type: "text", text: `Error: ${message}` }],
161
+ isError: true
162
+ };
163
+ }
164
+ function okList(data, max = 200) {
165
+ const truncated = data.length > max;
166
+ const items = truncated ? data.slice(0, max) : data;
167
+ return ok({
168
+ total: data.length,
169
+ returned: items.length,
170
+ truncated,
171
+ data: items
172
+ });
173
+ }
174
+
175
+ // src/tools/public.ts
176
+ var marketTypeSchema = z.enum(["spot", "future", "swap"]).optional().describe("Market type, default spot");
177
+ function registerPublicTools(server2) {
178
+ server2.tool(
179
+ "list_exchanges",
180
+ "List all supported cryptocurrency exchanges",
181
+ {},
182
+ async () => ok(SUPPORTED_EXCHANGES)
183
+ );
184
+ server2.tool(
185
+ "get_ticker",
186
+ "Get real-time ticker for a trading pair",
187
+ {
188
+ exchange: z.string().describe("Exchange ID, e.g. binance, okx, bybit"),
189
+ symbol: z.string().describe("Trading pair, e.g. BTC/USDT"),
190
+ market_type: marketTypeSchema
191
+ },
192
+ async ({ exchange, symbol, market_type }) => {
193
+ try {
194
+ const ex = getExchange(exchange, market_type, { skipAuth: true });
195
+ const ticker = await ex.fetchTicker(symbol);
196
+ return ok(ticker);
197
+ } catch (e) {
198
+ return err(e);
199
+ }
200
+ }
201
+ );
202
+ server2.tool(
203
+ "get_orderbook",
204
+ "Get order book depth for a trading pair",
205
+ {
206
+ exchange: z.string().describe("Exchange ID"),
207
+ symbol: z.string().describe("Trading pair, e.g. BTC/USDT"),
208
+ limit: z.number().min(1).optional().default(20).describe("Depth limit"),
209
+ market_type: marketTypeSchema
210
+ },
211
+ async ({ exchange, symbol, limit, market_type }) => {
212
+ try {
213
+ const ex = getExchange(exchange, market_type, { skipAuth: true });
214
+ const book = await ex.fetchOrderBook(symbol, limit);
215
+ return ok(book);
216
+ } catch (e) {
217
+ return err(e);
218
+ }
219
+ }
220
+ );
221
+ server2.tool(
222
+ "get_trades",
223
+ "Get recent trades for a trading pair",
224
+ {
225
+ exchange: z.string().describe("Exchange ID"),
226
+ symbol: z.string().describe("Trading pair, e.g. BTC/USDT"),
227
+ limit: z.number().optional().default(50).describe("Number of trades"),
228
+ market_type: marketTypeSchema
229
+ },
230
+ async ({ exchange, symbol, limit, market_type }) => {
231
+ try {
232
+ const ex = getExchange(exchange, market_type, { skipAuth: true });
233
+ const trades = await ex.fetchTrades(symbol, void 0, limit);
234
+ return ok(trades);
235
+ } catch (e) {
236
+ return err(e);
237
+ }
238
+ }
239
+ );
240
+ server2.tool(
241
+ "get_markets",
242
+ "Get available trading pairs on an exchange (filtered, paginated)",
243
+ {
244
+ exchange: z.string().describe("Exchange ID"),
245
+ market_type: marketTypeSchema.describe("Filter by market type"),
246
+ base: z.string().optional().describe("Filter by base currency, e.g. BTC"),
247
+ quote: z.string().optional().describe("Filter by quote currency, e.g. USDT"),
248
+ limit: z.number().int().min(1).max(500).optional().default(100).describe("Max results (1-500, default 100)")
249
+ },
250
+ async ({ exchange, market_type, base, quote, limit }) => {
251
+ try {
252
+ const ex = getExchange(exchange, market_type, { skipAuth: true });
253
+ await ex.loadMarkets();
254
+ let markets = Object.values(ex.markets).map((m) => ({
255
+ symbol: m.symbol,
256
+ base: m.base,
257
+ quote: m.quote,
258
+ type: m.type,
259
+ active: m.active
260
+ }));
261
+ if (market_type) markets = markets.filter((m) => m.type === market_type);
262
+ if (base) markets = markets.filter((m) => m.base === base.toUpperCase());
263
+ if (quote) markets = markets.filter((m) => m.quote === quote.toUpperCase());
264
+ return okList(markets.slice(0, limit), limit);
265
+ } catch (e) {
266
+ return err(e);
267
+ }
268
+ }
269
+ );
270
+ server2.tool(
271
+ "get_funding_rates",
272
+ "Get current funding rates for perpetual contracts",
273
+ {
274
+ exchange: z.string().describe("Exchange ID"),
275
+ symbols: z.array(z.string()).min(1).describe('Trading pairs to query, e.g. ["BTC/USDT:USDT"]')
276
+ },
277
+ async ({ exchange, symbols }) => {
278
+ try {
279
+ const ex = getExchange(exchange, "swap", { skipAuth: true });
280
+ const rates = await ex.fetchFundingRates(symbols);
281
+ return ok(rates);
282
+ } catch (e) {
283
+ return err(e);
284
+ }
285
+ }
286
+ );
287
+ server2.tool(
288
+ "get_tickers",
289
+ "Get tickers for multiple trading pairs at once",
290
+ {
291
+ exchange: z.string().describe("Exchange ID"),
292
+ symbols: z.array(z.string()).optional().describe('Trading pairs, e.g. ["BTC/USDT","ETH/USDT"]'),
293
+ market_type: marketTypeSchema
294
+ },
295
+ async ({ exchange, symbols, market_type }) => {
296
+ try {
297
+ const ex = getExchange(exchange, market_type, { skipAuth: true });
298
+ const syms = symbols && symbols.length > 0 ? symbols : void 0;
299
+ const tickers = await ex.fetchTickers(syms);
300
+ return okList(Object.values(tickers));
301
+ } catch (e) {
302
+ return err(e);
303
+ }
304
+ }
305
+ );
306
+ server2.tool(
307
+ "get_funding_rate_history",
308
+ "Get historical funding rates for a perpetual contract",
309
+ {
310
+ exchange: z.string().describe("Exchange ID"),
311
+ symbol: z.string().describe("Trading pair, e.g. BTC/USDT:USDT"),
312
+ limit: z.number().optional().default(100).describe("Number of records")
313
+ },
314
+ async ({ exchange, symbol, limit }) => {
315
+ try {
316
+ const ex = getExchange(exchange, "swap", { skipAuth: true });
317
+ const history = await ex.fetchFundingRateHistory(
318
+ symbol,
319
+ void 0,
320
+ limit
321
+ );
322
+ return ok(history);
323
+ } catch (e) {
324
+ return err(e);
325
+ }
326
+ }
327
+ );
328
+ server2.tool(
329
+ "get_ohlcv",
330
+ "Get OHLCV candlestick (K-line) data for a trading pair",
331
+ {
332
+ exchange: z.string().describe("Exchange ID"),
333
+ symbol: z.string().describe("Trading pair, e.g. BTC/USDT"),
334
+ timeframe: z.string().optional().default("1d").describe("Timeframe: 1m, 5m, 15m, 1h, 4h, 1d, 1w"),
335
+ limit: z.number().optional().default(100).describe("Number of candles, max 1000"),
336
+ market_type: marketTypeSchema
337
+ },
338
+ async ({ exchange, symbol, timeframe, limit, market_type }) => {
339
+ try {
340
+ const ex = getExchange(exchange, market_type, { skipAuth: true });
341
+ const data = await ex.fetchOHLCV(
342
+ symbol,
343
+ timeframe,
344
+ void 0,
345
+ Math.min(limit, 1e3)
346
+ );
347
+ return ok(data);
348
+ } catch (e) {
349
+ return err(e);
350
+ }
351
+ }
352
+ );
353
+ }
354
+
355
+ // src/tools/private.ts
356
+ import { z as z2 } from "zod";
357
+ var marketTypeSchema2 = z2.enum(["spot", "future", "swap", "margin"]).optional().describe("Market type, default spot");
358
+ function registerPrivateTools(server2) {
359
+ server2.tool(
360
+ "get_balance",
361
+ "Get account balance from exchange",
362
+ {
363
+ exchange: z2.string().describe("Exchange ID"),
364
+ market_type: marketTypeSchema2
365
+ },
366
+ async ({ exchange, market_type }) => {
367
+ try {
368
+ const ex = getExchange(exchange, market_type);
369
+ const bal = await ex.fetchBalance();
370
+ return ok({
371
+ total: bal.total,
372
+ free: bal.free,
373
+ used: bal.used
374
+ });
375
+ } catch (e) {
376
+ return err(e);
377
+ }
378
+ }
379
+ );
380
+ server2.tool(
381
+ "create_order",
382
+ "Place a new order (market or limit)",
383
+ {
384
+ exchange: z2.string().describe("Exchange ID"),
385
+ symbol: z2.string().describe("Trading pair, e.g. BTC/USDT"),
386
+ type: z2.enum(["market", "limit"]).describe("Order type"),
387
+ side: z2.enum(["buy", "sell"]).describe("Order side"),
388
+ amount: z2.number().positive().describe("Order amount"),
389
+ price: z2.number().positive().optional().describe("Limit price (required for limit orders)"),
390
+ pos_side: z2.enum(["long", "short"]).optional().describe("Position side for hedge mode (okx): long/short"),
391
+ margin_mode: z2.enum(["cross", "isolated"]).optional().describe("Margin mode for derivatives (okx): cross/isolated"),
392
+ market_type: marketTypeSchema2
393
+ },
394
+ async ({
395
+ exchange,
396
+ symbol,
397
+ type,
398
+ side,
399
+ amount,
400
+ price,
401
+ pos_side,
402
+ margin_mode,
403
+ market_type
404
+ }) => {
405
+ try {
406
+ if (type === "limit" && price == null) {
407
+ return err("price is required for limit orders");
408
+ }
409
+ const ex = getExchange(exchange, market_type);
410
+ const params = {};
411
+ if (pos_side) params.posSide = pos_side;
412
+ if (margin_mode) params.tdMode = margin_mode;
413
+ const order = await ex.createOrder(
414
+ symbol,
415
+ type,
416
+ side,
417
+ amount,
418
+ price,
419
+ params
420
+ );
421
+ return ok(order);
422
+ } catch (e) {
423
+ return err(e);
424
+ }
425
+ }
426
+ );
427
+ server2.tool(
428
+ "cancel_order",
429
+ "Cancel an open order",
430
+ {
431
+ exchange: z2.string().describe("Exchange ID"),
432
+ order_id: z2.string().describe("Order ID to cancel"),
433
+ symbol: z2.string().describe("Trading pair, e.g. BTC/USDT"),
434
+ market_type: marketTypeSchema2
435
+ },
436
+ async ({ exchange, order_id, symbol, market_type }) => {
437
+ try {
438
+ const ex = getExchange(exchange, market_type);
439
+ return ok(await ex.cancelOrder(order_id, symbol));
440
+ } catch (e) {
441
+ return err(e);
442
+ }
443
+ }
444
+ );
445
+ server2.tool(
446
+ "get_open_orders",
447
+ "Get all open (unfilled) orders",
448
+ {
449
+ exchange: z2.string().describe("Exchange ID"),
450
+ symbol: z2.string().optional().describe("Trading pair filter, e.g. BTC/USDT"),
451
+ market_type: marketTypeSchema2
452
+ },
453
+ async ({ exchange, symbol, market_type }) => {
454
+ try {
455
+ const ex = getExchange(exchange, market_type);
456
+ return ok(await ex.fetchOpenOrders(symbol));
457
+ } catch (e) {
458
+ return err(e);
459
+ }
460
+ }
461
+ );
462
+ server2.tool(
463
+ "get_closed_orders",
464
+ "Get closed (filled/cancelled) orders",
465
+ {
466
+ exchange: z2.string().describe("Exchange ID"),
467
+ symbol: z2.string().optional().describe("Trading pair filter"),
468
+ limit: z2.number().optional().default(20).describe("Number of orders to return"),
469
+ market_type: marketTypeSchema2
470
+ },
471
+ async ({ exchange, symbol, limit, market_type }) => {
472
+ try {
473
+ const ex = getExchange(exchange, market_type);
474
+ return ok(
475
+ await ex.fetchClosedOrders(symbol, void 0, limit)
476
+ );
477
+ } catch (e) {
478
+ return err(e);
479
+ }
480
+ }
481
+ );
482
+ server2.tool(
483
+ "get_positions",
484
+ "Get open positions (futures/swap only)",
485
+ {
486
+ exchange: z2.string().describe("Exchange ID"),
487
+ symbols: z2.array(z2.string()).optional().describe('Filter by symbols, e.g. ["BTC/USDT:USDT"]'),
488
+ market_type: z2.enum(["swap", "future"]).optional().default("swap").describe("Market type: swap (default) or future")
489
+ },
490
+ async ({ exchange, symbols, market_type }) => {
491
+ try {
492
+ const ex = getExchange(exchange, market_type);
493
+ return ok(await ex.fetchPositions(symbols));
494
+ } catch (e) {
495
+ return err(e);
496
+ }
497
+ }
498
+ );
499
+ server2.tool(
500
+ "get_order",
501
+ "Get a single order by ID",
502
+ {
503
+ exchange: z2.string().describe("Exchange ID"),
504
+ order_id: z2.string().describe("Order ID"),
505
+ symbol: z2.string().describe("Trading pair, e.g. BTC/USDT"),
506
+ market_type: marketTypeSchema2
507
+ },
508
+ async ({ exchange, order_id, symbol, market_type }) => {
509
+ try {
510
+ const ex = getExchange(exchange, market_type);
511
+ return ok(await ex.fetchOrder(order_id, symbol));
512
+ } catch (e) {
513
+ return err(e);
514
+ }
515
+ }
516
+ );
517
+ server2.tool(
518
+ "get_my_trades",
519
+ "Get your trade history (filled executions)",
520
+ {
521
+ exchange: z2.string().describe("Exchange ID"),
522
+ symbol: z2.string().optional().describe("Trading pair filter"),
523
+ limit: z2.number().optional().default(50).describe("Number of trades to return"),
524
+ market_type: marketTypeSchema2
525
+ },
526
+ async ({ exchange, symbol, limit, market_type }) => {
527
+ try {
528
+ const ex = getExchange(exchange, market_type);
529
+ return ok(
530
+ await ex.fetchMyTrades(symbol, void 0, limit)
531
+ );
532
+ } catch (e) {
533
+ return err(e);
534
+ }
535
+ }
536
+ );
537
+ server2.tool(
538
+ "cancel_all_orders",
539
+ "Cancel all open orders for a symbol",
540
+ {
541
+ exchange: z2.string().describe("Exchange ID"),
542
+ symbol: z2.string().describe("Trading pair, e.g. BTC/USDT"),
543
+ market_type: marketTypeSchema2
544
+ },
545
+ async ({ exchange, symbol, market_type }) => {
546
+ try {
547
+ const ex = getExchange(exchange, market_type);
548
+ if (ex.has["cancelAllOrders"]) {
549
+ return ok(await ex.cancelAllOrders(symbol));
550
+ }
551
+ const open = await ex.fetchOpenOrders(symbol);
552
+ const results = await Promise.allSettled(
553
+ open.map((o) => ex.cancelOrder(o.id, symbol))
554
+ );
555
+ const cancelled = results.filter((r) => r.status === "fulfilled").length;
556
+ const failed = results.filter((r) => r.status === "rejected").length;
557
+ return ok({ cancelled, failed, total: open.length });
558
+ } catch (e) {
559
+ return err(e);
560
+ }
561
+ }
562
+ );
563
+ server2.tool(
564
+ "set_leverage",
565
+ "Set leverage for a trading pair (futures/swap)",
566
+ {
567
+ exchange: z2.string().describe("Exchange ID"),
568
+ leverage: z2.number().int().positive().describe("Leverage multiplier, e.g. 10"),
569
+ symbol: z2.string().describe("Trading pair, e.g. BTC/USDT:USDT"),
570
+ market_type: marketTypeSchema2
571
+ },
572
+ async ({ exchange, leverage, symbol, market_type }) => {
573
+ try {
574
+ const ex = getExchange(
575
+ exchange,
576
+ market_type || "swap"
577
+ );
578
+ return ok(await ex.setLeverage(leverage, symbol));
579
+ } catch (e) {
580
+ return err(e);
581
+ }
582
+ }
583
+ );
584
+ server2.tool(
585
+ "set_margin_mode",
586
+ "Set margin mode (cross or isolated) for futures/swap",
587
+ {
588
+ exchange: z2.string().describe("Exchange ID"),
589
+ margin_mode: z2.enum(["cross", "isolated"]).describe("Margin mode"),
590
+ symbol: z2.string().describe("Trading pair, e.g. BTC/USDT:USDT"),
591
+ leverage: z2.number().int().positive().optional().describe("Leverage to set alongside margin mode. If omitted, auto-reads current leverage from the exchange"),
592
+ market_type: z2.enum(["swap", "future"]).optional().default("swap").describe("Market type: swap (default) or future")
593
+ },
594
+ async ({ exchange, margin_mode, symbol, leverage, market_type }) => {
595
+ try {
596
+ const ex = getExchange(exchange, market_type);
597
+ let lever = leverage;
598
+ if (lever == null) {
599
+ const info = await ex.fetchLeverage(symbol, { marginMode: margin_mode });
600
+ lever = info?.longLeverage ?? info?.shortLeverage;
601
+ }
602
+ const params = {};
603
+ if (lever != null) params.lever = String(lever);
604
+ return ok(
605
+ await ex.setMarginMode(margin_mode, symbol, params)
606
+ );
607
+ } catch (e) {
608
+ return err(e);
609
+ }
610
+ }
611
+ );
612
+ server2.tool(
613
+ "get_ledger",
614
+ "Get account ledger / transaction history",
615
+ {
616
+ exchange: z2.string().describe("Exchange ID"),
617
+ code: z2.string().optional().describe("Currency code filter, e.g. USDT"),
618
+ limit: z2.number().optional().default(20).describe("Number of entries"),
619
+ market_type: marketTypeSchema2
620
+ },
621
+ async ({ exchange, code, limit, market_type }) => {
622
+ try {
623
+ const ex = getExchange(exchange, market_type);
624
+ return ok(
625
+ await ex.fetchLedger(code, void 0, limit)
626
+ );
627
+ } catch (e) {
628
+ return err(e);
629
+ }
630
+ }
631
+ );
632
+ server2.tool(
633
+ "get_deposits",
634
+ "Get deposit history",
635
+ {
636
+ exchange: z2.string().describe("Exchange ID"),
637
+ code: z2.string().optional().describe("Currency code filter, e.g. USDT"),
638
+ limit: z2.number().optional().default(20).describe("Number of records"),
639
+ market_type: marketTypeSchema2
640
+ },
641
+ async ({ exchange, code, limit, market_type }) => {
642
+ try {
643
+ const ex = getExchange(exchange, market_type);
644
+ return ok(
645
+ await ex.fetchDeposits(code, void 0, limit)
646
+ );
647
+ } catch (e) {
648
+ return err(e);
649
+ }
650
+ }
651
+ );
652
+ server2.tool(
653
+ "get_withdrawals",
654
+ "Get withdrawal history",
655
+ {
656
+ exchange: z2.string().describe("Exchange ID"),
657
+ code: z2.string().optional().describe("Currency code filter, e.g. USDT"),
658
+ limit: z2.number().optional().default(20).describe("Number of records"),
659
+ market_type: marketTypeSchema2
660
+ },
661
+ async ({ exchange, code, limit, market_type }) => {
662
+ try {
663
+ const ex = getExchange(exchange, market_type);
664
+ return ok(
665
+ await ex.fetchWithdrawals(code, void 0, limit)
666
+ );
667
+ } catch (e) {
668
+ return err(e);
669
+ }
670
+ }
671
+ );
672
+ server2.tool(
673
+ "transfer",
674
+ "Transfer funds between accounts (e.g. spot to futures)",
675
+ {
676
+ exchange: z2.string().describe("Exchange ID"),
677
+ code: z2.string().describe("Currency code, e.g. USDT"),
678
+ amount: z2.number().positive().describe("Amount"),
679
+ from_account: z2.string().describe("Source account: spot, swap, future"),
680
+ to_account: z2.string().describe("Target account: spot, swap, future"),
681
+ market_type: marketTypeSchema2
682
+ },
683
+ async ({
684
+ exchange,
685
+ code,
686
+ amount,
687
+ from_account,
688
+ to_account,
689
+ market_type
690
+ }) => {
691
+ try {
692
+ const ex = getExchange(exchange, market_type);
693
+ return ok(
694
+ await ex.transfer(
695
+ code,
696
+ amount,
697
+ from_account,
698
+ to_account
699
+ )
700
+ );
701
+ } catch (e) {
702
+ return err(e);
703
+ }
704
+ }
705
+ );
706
+ }
707
+
708
+ // src/tools/index.ts
709
+ function registerAllTools(server2) {
710
+ registerPublicTools(server2);
711
+ registerPrivateTools(server2);
712
+ }
713
+
714
+ // src/index.ts
715
+ var server = new McpServer({
716
+ name: "aicoin-trade",
717
+ version: "1.0.3"
718
+ });
719
+ registerAllTools(server);
720
+ var transport = new StdioServerTransport();
721
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@aicoin/trade-mcp",
3
+ "version": "1.0.5",
4
+ "description": "AiCoin Trade MCP Server - AI-powered cryptocurrency trading via ccxt",
5
+ "main": "build/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "aicoin-trade-mcp": "./build/index.js"
9
+ },
10
+ "files": [
11
+ "build",
12
+ "README.md",
13
+ ".env.example"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsup src/index.ts --format esm --outDir build --clean",
17
+ "start": "node build/index.js",
18
+ "dev": "tsup src/index.ts --format esm --outDir build --watch",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "dependencies": {
22
+ "@modelcontextprotocol/sdk": "^1.8.0",
23
+ "ccxt": "^4.4.71",
24
+ "socks-proxy-agent": "^8.0.5",
25
+ "zod": "^3.22.2"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^20.6.0",
29
+ "tsup": "^8.5.1",
30
+ "typescript": "^5.2.2"
31
+ },
32
+ "engines": {
33
+ "node": ">=18.0.0"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "keywords": [
39
+ "mcp",
40
+ "aicoin",
41
+ "crypto",
42
+ "trading",
43
+ "ccxt"
44
+ ],
45
+ "license": "MIT"
46
+ }