@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 +41 -0
- package/README.md +154 -0
- package/build/index.js +721 -0
- package/package.json +46 -0
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
|
+
}
|