gainium-mcp 2.0.0
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/LICENSE +21 -0
- package/README.md +152 -0
- package/dist/gainium-client.js +77 -0
- package/dist/server.js +1121 -0
- package/package.json +51 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,1121 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { GainiumClient } from "./gainium-client.js";
|
|
6
|
+
// ── Environment ──────────────────────────────────────────────────────────────
|
|
7
|
+
const API_KEY = process.env.GAINIUM_API_KEY;
|
|
8
|
+
const API_SECRET = process.env.GAINIUM_API_SECRET;
|
|
9
|
+
const BASE_URL = process.env.GAINIUM_API_BASE_URL || "https://api.gainium.io";
|
|
10
|
+
if (!API_KEY || !API_SECRET) {
|
|
11
|
+
console.error("[gainium-mcp] Missing required environment variables: GAINIUM_API_KEY, GAINIUM_API_SECRET");
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const client = new GainiumClient(BASE_URL, API_KEY, API_SECRET);
|
|
15
|
+
// ── Shared schema fragments ─────────────────────────────────────────────────
|
|
16
|
+
const fieldsParam = {
|
|
17
|
+
fields: {
|
|
18
|
+
type: "string",
|
|
19
|
+
description: 'Field selection: preset ("minimal", "standard", "extended", "full") or comma-separated fields (e.g. "_id,uuid,settings.name,profit.total"). Default: "standard"',
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
const pageParam = {
|
|
23
|
+
page: {
|
|
24
|
+
type: "integer",
|
|
25
|
+
description: "Page number for pagination (1-based). Default: 1",
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
const statusParam = {
|
|
29
|
+
status: {
|
|
30
|
+
type: "string",
|
|
31
|
+
enum: ["open", "closed", "range", "error", "archive", "monitoring"],
|
|
32
|
+
description: "Filter by bot status",
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
const paperContextParam = {
|
|
36
|
+
paperContext: {
|
|
37
|
+
type: "boolean",
|
|
38
|
+
description: "Filter by paper trading context (true = paper, false = real). Default: false",
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
const botIdRequired = {
|
|
42
|
+
botId: {
|
|
43
|
+
type: "string",
|
|
44
|
+
description: "Bot ID (UUID or MongoDB _id)",
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
const botTypeParam = (types, defaultVal) => ({
|
|
48
|
+
botType: {
|
|
49
|
+
type: "string",
|
|
50
|
+
enum: types,
|
|
51
|
+
description: `Bot type. Default: "${defaultVal}"`,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
// ── Tool Definitions ────────────────────────────────────────────────────────
|
|
55
|
+
const tools = [
|
|
56
|
+
// ─── BOT LISTING ────────────────────────────────────────────────────────
|
|
57
|
+
{
|
|
58
|
+
name: "get_dca_bots",
|
|
59
|
+
description: "List DCA (Dollar Cost Averaging) bots. Supports field selection presets: minimal (~85% smaller), standard (default), extended, full. Supports filtering by status and paper/real trading context.",
|
|
60
|
+
inputSchema: {
|
|
61
|
+
type: "object",
|
|
62
|
+
properties: {
|
|
63
|
+
...fieldsParam,
|
|
64
|
+
...pageParam,
|
|
65
|
+
...statusParam,
|
|
66
|
+
...paperContextParam,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: "get_combo_bots",
|
|
72
|
+
description: "List Combo (Long/Short with grid-level) bots. Supports field selection presets: minimal, standard (default), extended, full.",
|
|
73
|
+
inputSchema: {
|
|
74
|
+
type: "object",
|
|
75
|
+
properties: {
|
|
76
|
+
...fieldsParam,
|
|
77
|
+
...pageParam,
|
|
78
|
+
...statusParam,
|
|
79
|
+
...paperContextParam,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "get_grid_bots",
|
|
85
|
+
description: "List Grid bots. Supports field selection presets: minimal, standard (default), extended, full.",
|
|
86
|
+
inputSchema: {
|
|
87
|
+
type: "object",
|
|
88
|
+
properties: {
|
|
89
|
+
...fieldsParam,
|
|
90
|
+
...pageParam,
|
|
91
|
+
...statusParam,
|
|
92
|
+
...paperContextParam,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
// ─── BOT CREATION ──────────────────────────────────────────────────────
|
|
97
|
+
{
|
|
98
|
+
name: "create_dca_bot",
|
|
99
|
+
description: "Create a new DCA bot. Requires write API key permission. The 'futures' and 'coinm' fields are auto-detected from the exchange — do not provide them.",
|
|
100
|
+
inputSchema: {
|
|
101
|
+
type: "object",
|
|
102
|
+
properties: {
|
|
103
|
+
exchangeUUID: {
|
|
104
|
+
type: "string",
|
|
105
|
+
description: "UUID of the exchange connection to use",
|
|
106
|
+
},
|
|
107
|
+
paperContext: {
|
|
108
|
+
type: "boolean",
|
|
109
|
+
description: "Create in paper trading mode. Default: false",
|
|
110
|
+
},
|
|
111
|
+
pair: {
|
|
112
|
+
type: "array",
|
|
113
|
+
items: { type: "string" },
|
|
114
|
+
description: 'Trading pairs in {base}_{quote} format, e.g. ["BTC_USDT"]',
|
|
115
|
+
},
|
|
116
|
+
name: { type: "string", description: "Bot name" },
|
|
117
|
+
strategy: {
|
|
118
|
+
type: "string",
|
|
119
|
+
enum: ["LONG", "SHORT"],
|
|
120
|
+
description: "Trading direction. Default: LONG",
|
|
121
|
+
},
|
|
122
|
+
baseOrderSize: {
|
|
123
|
+
type: "string",
|
|
124
|
+
description: 'Size of the initial base order, e.g. "100"',
|
|
125
|
+
},
|
|
126
|
+
orderSize: {
|
|
127
|
+
type: "string",
|
|
128
|
+
description: 'Size of each DCA order, e.g. "100"',
|
|
129
|
+
},
|
|
130
|
+
orderSizeType: {
|
|
131
|
+
type: "string",
|
|
132
|
+
enum: ["base", "quote", "percFree", "percTotal", "usd"],
|
|
133
|
+
description: "Order size reference currency. Default: quote",
|
|
134
|
+
},
|
|
135
|
+
tpPerc: {
|
|
136
|
+
type: "string",
|
|
137
|
+
description: 'Take profit percentage, e.g. "1.5"',
|
|
138
|
+
},
|
|
139
|
+
slPerc: {
|
|
140
|
+
type: "string",
|
|
141
|
+
description: 'Stop loss percentage, e.g. "-10"',
|
|
142
|
+
},
|
|
143
|
+
step: {
|
|
144
|
+
type: "string",
|
|
145
|
+
description: 'Price deviation % for next DCA order, e.g. "1.5"',
|
|
146
|
+
},
|
|
147
|
+
ordersCount: {
|
|
148
|
+
type: "integer",
|
|
149
|
+
description: "Maximum number of DCA orders",
|
|
150
|
+
},
|
|
151
|
+
maxNumberOfOpenDeals: {
|
|
152
|
+
type: "string",
|
|
153
|
+
description: 'Maximum concurrent open deals, e.g. "1"',
|
|
154
|
+
},
|
|
155
|
+
startCondition: {
|
|
156
|
+
type: "string",
|
|
157
|
+
enum: [
|
|
158
|
+
"ASAP",
|
|
159
|
+
"Manual",
|
|
160
|
+
"TradingviewSignals",
|
|
161
|
+
"Timer",
|
|
162
|
+
"TechnicalIndicators",
|
|
163
|
+
],
|
|
164
|
+
description: "Condition to start a new deal. Default: ASAP",
|
|
165
|
+
},
|
|
166
|
+
settings: {
|
|
167
|
+
type: "object",
|
|
168
|
+
description: "Additional DCA bot settings (any DCABotSettings fields not listed above). Merged into the request body.",
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
required: ["exchangeUUID", "pair"],
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: "create_combo_bot",
|
|
176
|
+
description: "Create a new Combo bot (DCA + grid levels). Requires write API key permission.",
|
|
177
|
+
inputSchema: {
|
|
178
|
+
type: "object",
|
|
179
|
+
properties: {
|
|
180
|
+
exchangeUUID: {
|
|
181
|
+
type: "string",
|
|
182
|
+
description: "UUID of the exchange connection",
|
|
183
|
+
},
|
|
184
|
+
paperContext: {
|
|
185
|
+
type: "boolean",
|
|
186
|
+
description: "Create in paper trading mode. Default: false",
|
|
187
|
+
},
|
|
188
|
+
pair: {
|
|
189
|
+
type: "array",
|
|
190
|
+
items: { type: "string" },
|
|
191
|
+
description: 'Trading pairs, e.g. ["BTC_USDT"]',
|
|
192
|
+
},
|
|
193
|
+
name: { type: "string", description: "Bot name" },
|
|
194
|
+
strategy: {
|
|
195
|
+
type: "string",
|
|
196
|
+
enum: ["LONG", "SHORT"],
|
|
197
|
+
description: "Trading direction",
|
|
198
|
+
},
|
|
199
|
+
baseOrderSize: { type: "string", description: "Base order size" },
|
|
200
|
+
orderSize: { type: "string", description: "DCA order size" },
|
|
201
|
+
gridLevel: {
|
|
202
|
+
type: "string",
|
|
203
|
+
description: 'Number of grid levels, e.g. "5"',
|
|
204
|
+
},
|
|
205
|
+
step: { type: "string", description: "Step % between DCA orders" },
|
|
206
|
+
ordersCount: { type: "integer", description: "DCA orders count" },
|
|
207
|
+
settings: {
|
|
208
|
+
type: "object",
|
|
209
|
+
description: "Additional Combo bot settings (ComboBotSettings fields). Merged into the request body.",
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
required: ["exchangeUUID", "pair"],
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: "create_grid_bot",
|
|
217
|
+
description: "Create a new Grid bot that buys/sells within a price range. Requires write API key permission.",
|
|
218
|
+
inputSchema: {
|
|
219
|
+
type: "object",
|
|
220
|
+
properties: {
|
|
221
|
+
exchangeUUID: {
|
|
222
|
+
type: "string",
|
|
223
|
+
description: "UUID of the exchange connection",
|
|
224
|
+
},
|
|
225
|
+
paperContext: {
|
|
226
|
+
type: "boolean",
|
|
227
|
+
description: "Create in paper trading mode. Default: false",
|
|
228
|
+
},
|
|
229
|
+
pair: {
|
|
230
|
+
type: "string",
|
|
231
|
+
description: 'Trading pair in {base}_{quote} format, e.g. "BTC_USDT"',
|
|
232
|
+
},
|
|
233
|
+
topPrice: { type: "number", description: "Top price of grid range" },
|
|
234
|
+
lowPrice: {
|
|
235
|
+
type: "number",
|
|
236
|
+
description: "Bottom price of grid range",
|
|
237
|
+
},
|
|
238
|
+
budget: {
|
|
239
|
+
type: "number",
|
|
240
|
+
description: "Total budget allocated to bot",
|
|
241
|
+
},
|
|
242
|
+
levels: { type: "number", description: "Number of grid levels" },
|
|
243
|
+
name: { type: "string", description: "Bot name" },
|
|
244
|
+
gridType: {
|
|
245
|
+
type: "string",
|
|
246
|
+
description: 'Grid type: "arithmetic" or "geometric"',
|
|
247
|
+
},
|
|
248
|
+
settings: {
|
|
249
|
+
type: "object",
|
|
250
|
+
description: "Additional Grid bot settings (BotSettings fields). Merged into the request body.",
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
required: ["exchangeUUID", "pair", "topPrice", "lowPrice", "budget"],
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
// ─── BOT UPDATE ─────────────────────────────────────────────────────────
|
|
257
|
+
{
|
|
258
|
+
name: "update_dca_bot",
|
|
259
|
+
description: "Update an existing DCA bot's settings. Only include fields you want to change. Requires write API key permission.",
|
|
260
|
+
inputSchema: {
|
|
261
|
+
type: "object",
|
|
262
|
+
properties: {
|
|
263
|
+
...botIdRequired,
|
|
264
|
+
...paperContextParam,
|
|
265
|
+
settings: {
|
|
266
|
+
type: "object",
|
|
267
|
+
description: "Settings to update: name, pair, step, ordersCount, tpPerc, slPerc, orderSize, baseOrderSize, orderSizeType, startCondition, maxNumberOfOpenDeals, useTp, useSl, useDca, volumeScale, stepScale, etc.",
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
required: ["botId", "settings"],
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
name: "update_combo_bot",
|
|
275
|
+
description: "Update an existing Combo bot's settings. Requires write API key permission.",
|
|
276
|
+
inputSchema: {
|
|
277
|
+
type: "object",
|
|
278
|
+
properties: {
|
|
279
|
+
...botIdRequired,
|
|
280
|
+
...paperContextParam,
|
|
281
|
+
settings: {
|
|
282
|
+
type: "object",
|
|
283
|
+
description: "Settings to update: name, step, ordersCount, tpPerc, slPerc, orderSize, baseOrderSize, gridLevel, baseStep, baseGridLevels, comboTpBase, etc.",
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
required: ["botId", "settings"],
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
// ─── BOT CLONE ──────────────────────────────────────────────────────────
|
|
290
|
+
{
|
|
291
|
+
name: "clone_dca_bot",
|
|
292
|
+
description: "Clone a DCA bot, optionally overriding settings. Requires write API key permission.",
|
|
293
|
+
inputSchema: {
|
|
294
|
+
type: "object",
|
|
295
|
+
properties: {
|
|
296
|
+
...botIdRequired,
|
|
297
|
+
...paperContextParam,
|
|
298
|
+
overrides: {
|
|
299
|
+
type: "object",
|
|
300
|
+
description: "Settings to override in the cloned bot (UpdateDCABotInput fields)",
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
required: ["botId"],
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
name: "clone_combo_bot",
|
|
308
|
+
description: "Clone a Combo bot, optionally overriding settings. Requires write API key permission.",
|
|
309
|
+
inputSchema: {
|
|
310
|
+
type: "object",
|
|
311
|
+
properties: {
|
|
312
|
+
...botIdRequired,
|
|
313
|
+
...paperContextParam,
|
|
314
|
+
overrides: {
|
|
315
|
+
type: "object",
|
|
316
|
+
description: "Settings to override in the cloned bot (UpdateComboBotInput fields, plus pair array)",
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
required: ["botId"],
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
// ─── BOT LIFECYCLE ──────────────────────────────────────────────────────
|
|
323
|
+
{
|
|
324
|
+
name: "start_bot",
|
|
325
|
+
description: "Start a bot. Requires write API key permission.",
|
|
326
|
+
inputSchema: {
|
|
327
|
+
type: "object",
|
|
328
|
+
properties: {
|
|
329
|
+
...botIdRequired,
|
|
330
|
+
type: {
|
|
331
|
+
type: "string",
|
|
332
|
+
enum: ["dca", "grid", "combo"],
|
|
333
|
+
description: "Bot type",
|
|
334
|
+
},
|
|
335
|
+
...paperContextParam,
|
|
336
|
+
},
|
|
337
|
+
required: ["botId", "type"],
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
name: "stop_bot",
|
|
342
|
+
description: "Stop a running bot. Requires write API key permission.",
|
|
343
|
+
inputSchema: {
|
|
344
|
+
type: "object",
|
|
345
|
+
properties: {
|
|
346
|
+
...botIdRequired,
|
|
347
|
+
...botTypeParam(["dca", "combo", "grid"], "grid"),
|
|
348
|
+
closeType: {
|
|
349
|
+
type: "string",
|
|
350
|
+
enum: ["cancel", "closeByLimit", "closeByMarket", "leave"],
|
|
351
|
+
description: "How to close DCA/Combo open deals. Default: leave",
|
|
352
|
+
},
|
|
353
|
+
closeGridType: {
|
|
354
|
+
type: "string",
|
|
355
|
+
enum: ["cancel", "closeByLimit", "closeByMarket"],
|
|
356
|
+
description: "How to close Grid orders. Default: cancel",
|
|
357
|
+
},
|
|
358
|
+
cancelPartiallyFilled: {
|
|
359
|
+
type: "boolean",
|
|
360
|
+
description: "For Grid bots: cancel partially filled orders. Default: false",
|
|
361
|
+
},
|
|
362
|
+
...paperContextParam,
|
|
363
|
+
},
|
|
364
|
+
required: ["botId", "botType"],
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
name: "archive_bot",
|
|
369
|
+
description: "Archive a stopped bot. Requires write API key permission.",
|
|
370
|
+
inputSchema: {
|
|
371
|
+
type: "object",
|
|
372
|
+
properties: {
|
|
373
|
+
...botIdRequired,
|
|
374
|
+
...botTypeParam(["dca", "combo", "grid"], "grid"),
|
|
375
|
+
...paperContextParam,
|
|
376
|
+
},
|
|
377
|
+
required: ["botId", "botType"],
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
name: "restore_bot",
|
|
382
|
+
description: "Restore an archived bot. Requires write API key permission.",
|
|
383
|
+
inputSchema: {
|
|
384
|
+
type: "object",
|
|
385
|
+
properties: {
|
|
386
|
+
...botIdRequired,
|
|
387
|
+
type: {
|
|
388
|
+
type: "string",
|
|
389
|
+
enum: ["dca", "combo", "grid"],
|
|
390
|
+
description: "Bot type",
|
|
391
|
+
},
|
|
392
|
+
...paperContextParam,
|
|
393
|
+
},
|
|
394
|
+
required: ["botId", "type"],
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
name: "change_bot_pairs",
|
|
399
|
+
description: 'Change trading pairs for a DCA or Combo bot. Pairs format: {base}_{quote} (e.g. "BTC_USDT"). Requires write API key permission.',
|
|
400
|
+
inputSchema: {
|
|
401
|
+
type: "object",
|
|
402
|
+
properties: {
|
|
403
|
+
botId: {
|
|
404
|
+
type: "string",
|
|
405
|
+
description: "Bot ID. Either botId or botName required (botId has priority)",
|
|
406
|
+
},
|
|
407
|
+
botName: {
|
|
408
|
+
type: "string",
|
|
409
|
+
description: "Bot name to search. Either botId or botName required",
|
|
410
|
+
},
|
|
411
|
+
pairsToSet: {
|
|
412
|
+
type: "array",
|
|
413
|
+
items: { type: "string" },
|
|
414
|
+
description: "Pairs to set (replaces existing). Has priority over pairsToAdd/pairsToRemove.",
|
|
415
|
+
},
|
|
416
|
+
pairsToSetMode: {
|
|
417
|
+
type: "string",
|
|
418
|
+
enum: ["add", "remove", "replace"],
|
|
419
|
+
description: "Mode for pairsToSet. Default: replace",
|
|
420
|
+
},
|
|
421
|
+
pairsToAdd: {
|
|
422
|
+
type: "array",
|
|
423
|
+
items: { type: "string" },
|
|
424
|
+
description: "Pairs to add",
|
|
425
|
+
},
|
|
426
|
+
pairsToRemove: {
|
|
427
|
+
type: "array",
|
|
428
|
+
items: { type: "string" },
|
|
429
|
+
description: "Pairs to remove",
|
|
430
|
+
},
|
|
431
|
+
...paperContextParam,
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
// ─── DEALS LISTING ──────────────────────────────────────────────────────
|
|
436
|
+
{
|
|
437
|
+
name: "get_deals",
|
|
438
|
+
description: "List deals (DCA and Combo) with filtering and field selection. Presets: minimal, standard (default), extended, full.",
|
|
439
|
+
inputSchema: {
|
|
440
|
+
type: "object",
|
|
441
|
+
properties: {
|
|
442
|
+
...fieldsParam,
|
|
443
|
+
...pageParam,
|
|
444
|
+
type: {
|
|
445
|
+
type: "string",
|
|
446
|
+
enum: ["dca", "combo"],
|
|
447
|
+
description: "Filter by deal type",
|
|
448
|
+
},
|
|
449
|
+
status: {
|
|
450
|
+
type: "string",
|
|
451
|
+
enum: ["open", "closed", "start", "error", "canceled"],
|
|
452
|
+
description: "Filter by deal status",
|
|
453
|
+
},
|
|
454
|
+
botId: {
|
|
455
|
+
type: "string",
|
|
456
|
+
description: "Filter by parent bot UUID",
|
|
457
|
+
},
|
|
458
|
+
terminal: {
|
|
459
|
+
type: "boolean",
|
|
460
|
+
description: "false = regular deals, true = terminal deals. Default: false",
|
|
461
|
+
},
|
|
462
|
+
...paperContextParam,
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
// ─── DEAL CREATION & MANAGEMENT ─────────────────────────────────────────
|
|
467
|
+
{
|
|
468
|
+
name: "create_terminal_deal",
|
|
469
|
+
description: "Create a one-time Terminal Deal for immediate execution. Unlike bots, it executes once and closes. Requires write API key permission.",
|
|
470
|
+
inputSchema: {
|
|
471
|
+
type: "object",
|
|
472
|
+
properties: {
|
|
473
|
+
exchangeUUID: {
|
|
474
|
+
type: "string",
|
|
475
|
+
description: "UUID of the exchange connection",
|
|
476
|
+
},
|
|
477
|
+
paperContext: {
|
|
478
|
+
type: "boolean",
|
|
479
|
+
description: "Paper trading mode. Default: false",
|
|
480
|
+
},
|
|
481
|
+
pair: {
|
|
482
|
+
type: "array",
|
|
483
|
+
items: { type: "string" },
|
|
484
|
+
description: 'Trading pairs, e.g. ["BTC_USDT"]',
|
|
485
|
+
},
|
|
486
|
+
terminalDealType: {
|
|
487
|
+
type: "string",
|
|
488
|
+
enum: ["simple", "smart", "import"],
|
|
489
|
+
description: "Terminal deal type",
|
|
490
|
+
},
|
|
491
|
+
strategy: {
|
|
492
|
+
type: "string",
|
|
493
|
+
enum: ["LONG", "SHORT"],
|
|
494
|
+
description: "Trading direction",
|
|
495
|
+
},
|
|
496
|
+
baseOrderSize: { type: "string", description: "Base order size" },
|
|
497
|
+
orderSize: { type: "string", description: "DCA order size" },
|
|
498
|
+
tpPerc: { type: "string", description: "Take profit %" },
|
|
499
|
+
slPerc: { type: "string", description: "Stop loss %" },
|
|
500
|
+
settings: {
|
|
501
|
+
type: "object",
|
|
502
|
+
description: "Additional terminal deal settings (DCABotSettings fields). Merged into the request body.",
|
|
503
|
+
},
|
|
504
|
+
},
|
|
505
|
+
required: ["exchangeUUID", "pair", "terminalDealType"],
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
name: "update_dca_deal",
|
|
510
|
+
description: "Update settings of an active DCA deal. Requires write API key permission.",
|
|
511
|
+
inputSchema: {
|
|
512
|
+
type: "object",
|
|
513
|
+
properties: {
|
|
514
|
+
dealId: { type: "string", description: "Deal ID" },
|
|
515
|
+
...paperContextParam,
|
|
516
|
+
settings: {
|
|
517
|
+
type: "object",
|
|
518
|
+
description: "Deal settings to update: ordersCount, step, tpPerc, slPerc, orderSize, useTp, useSl, useDca, activeOrdersCount, volumeScale, stepScale, useMultiTp, multiTp, useMultiSl, multiSl, trailingTp, trailingSl, moveSL, closeByTimer, dcaCondition, dcaCustom, etc.",
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
required: ["dealId", "settings"],
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
name: "update_combo_deal",
|
|
526
|
+
description: "Update settings of an active Combo deal. Requires write API key permission.",
|
|
527
|
+
inputSchema: {
|
|
528
|
+
type: "object",
|
|
529
|
+
properties: {
|
|
530
|
+
dealId: { type: "string", description: "Deal ID" },
|
|
531
|
+
...paperContextParam,
|
|
532
|
+
settings: {
|
|
533
|
+
type: "object",
|
|
534
|
+
description: "Deal settings to update: ordersCount, step, tpPerc, slPerc, useTp, useSl, useDca, activeOrdersCount, volumeScale, stepScale, comboTpBase, etc.",
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
required: ["dealId", "settings"],
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
name: "start_deal",
|
|
542
|
+
description: "Start a new deal for a bot. Requires write API key permission.",
|
|
543
|
+
inputSchema: {
|
|
544
|
+
type: "object",
|
|
545
|
+
properties: {
|
|
546
|
+
...botIdRequired,
|
|
547
|
+
...botTypeParam(["dca", "combo"], "dca"),
|
|
548
|
+
symbol: {
|
|
549
|
+
type: "string",
|
|
550
|
+
description: 'Symbol for multi-coin bots. Format: {base}_{quote}, e.g. "BTC_USDT"',
|
|
551
|
+
},
|
|
552
|
+
...paperContextParam,
|
|
553
|
+
},
|
|
554
|
+
required: ["botId", "botType"],
|
|
555
|
+
},
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
name: "close_deal",
|
|
559
|
+
description: "Close an active deal. Requires write API key permission.",
|
|
560
|
+
inputSchema: {
|
|
561
|
+
type: "object",
|
|
562
|
+
properties: {
|
|
563
|
+
dealId: { type: "string", description: "Deal ID to close" },
|
|
564
|
+
type: {
|
|
565
|
+
type: "string",
|
|
566
|
+
enum: ["cancel", "closeByLimit", "closeByMarket", "leave"],
|
|
567
|
+
description: "How to close the deal. Default: cancel",
|
|
568
|
+
},
|
|
569
|
+
...botTypeParam(["dca", "combo"], "dca"),
|
|
570
|
+
...paperContextParam,
|
|
571
|
+
},
|
|
572
|
+
required: ["dealId", "type", "botType"],
|
|
573
|
+
},
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
name: "add_funds",
|
|
577
|
+
description: "Add funds to an active deal. Requires write API key permission.",
|
|
578
|
+
inputSchema: {
|
|
579
|
+
type: "object",
|
|
580
|
+
properties: {
|
|
581
|
+
...botIdRequired,
|
|
582
|
+
dealId: {
|
|
583
|
+
type: "string",
|
|
584
|
+
description: "Deal ID (optional if symbol provided)",
|
|
585
|
+
},
|
|
586
|
+
qty: { type: "string", description: "Quantity to add" },
|
|
587
|
+
type: {
|
|
588
|
+
type: "string",
|
|
589
|
+
enum: ["fixed", "perc"],
|
|
590
|
+
description: 'Quantity type. Default: "fixed"',
|
|
591
|
+
},
|
|
592
|
+
asset: {
|
|
593
|
+
type: "string",
|
|
594
|
+
enum: ["base", "quote"],
|
|
595
|
+
description: "Asset reference (required if type is fixed)",
|
|
596
|
+
},
|
|
597
|
+
symbol: {
|
|
598
|
+
type: "string",
|
|
599
|
+
description: 'Target symbol, e.g. "BTC_USDT"',
|
|
600
|
+
},
|
|
601
|
+
...paperContextParam,
|
|
602
|
+
},
|
|
603
|
+
required: ["botId", "qty", "type"],
|
|
604
|
+
},
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
name: "reduce_funds",
|
|
608
|
+
description: "Reduce funds from an active deal. Requires write API key permission.",
|
|
609
|
+
inputSchema: {
|
|
610
|
+
type: "object",
|
|
611
|
+
properties: {
|
|
612
|
+
...botIdRequired,
|
|
613
|
+
dealId: {
|
|
614
|
+
type: "string",
|
|
615
|
+
description: "Deal ID (optional if symbol provided)",
|
|
616
|
+
},
|
|
617
|
+
qty: { type: "string", description: "Quantity to reduce" },
|
|
618
|
+
type: {
|
|
619
|
+
type: "string",
|
|
620
|
+
enum: ["fixed", "perc"],
|
|
621
|
+
description: 'Quantity type. Default: "fixed"',
|
|
622
|
+
},
|
|
623
|
+
asset: {
|
|
624
|
+
type: "string",
|
|
625
|
+
enum: ["base", "quote"],
|
|
626
|
+
description: "Asset reference (required if type is fixed)",
|
|
627
|
+
},
|
|
628
|
+
symbol: {
|
|
629
|
+
type: "string",
|
|
630
|
+
description: 'Target symbol, e.g. "BTC_USDT"',
|
|
631
|
+
},
|
|
632
|
+
...paperContextParam,
|
|
633
|
+
},
|
|
634
|
+
required: ["botId", "qty", "type"],
|
|
635
|
+
},
|
|
636
|
+
},
|
|
637
|
+
// ─── USER ───────────────────────────────────────────────────────────────
|
|
638
|
+
{
|
|
639
|
+
name: "get_balances",
|
|
640
|
+
description: "Get user balances across all exchanges. Supports field selection and filtering by exchange, asset, or paper context.",
|
|
641
|
+
inputSchema: {
|
|
642
|
+
type: "object",
|
|
643
|
+
properties: {
|
|
644
|
+
...fieldsParam,
|
|
645
|
+
exchangeId: {
|
|
646
|
+
type: "string",
|
|
647
|
+
description: "Filter by exchange connection UUID",
|
|
648
|
+
},
|
|
649
|
+
asset: {
|
|
650
|
+
type: "string",
|
|
651
|
+
description: 'Filter by single asset, e.g. "BTC"',
|
|
652
|
+
},
|
|
653
|
+
assets: {
|
|
654
|
+
type: "string",
|
|
655
|
+
description: 'Filter by multiple assets, e.g. "BTC,USDT,ETH"',
|
|
656
|
+
},
|
|
657
|
+
...paperContextParam,
|
|
658
|
+
},
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
name: "get_user_exchanges",
|
|
663
|
+
description: "Get user's connected exchange accounts.",
|
|
664
|
+
inputSchema: {
|
|
665
|
+
type: "object",
|
|
666
|
+
properties: { ...paperContextParam },
|
|
667
|
+
},
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
name: "get_global_variables",
|
|
671
|
+
description: "List user's global variables. Global variables can be referenced in bot configurations.",
|
|
672
|
+
inputSchema: {
|
|
673
|
+
type: "object",
|
|
674
|
+
properties: { ...pageParam },
|
|
675
|
+
},
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
name: "create_global_variable",
|
|
679
|
+
description: "Create a new global variable. Requires write API key permission.",
|
|
680
|
+
inputSchema: {
|
|
681
|
+
type: "object",
|
|
682
|
+
properties: {
|
|
683
|
+
name: { type: "string", description: "Variable name" },
|
|
684
|
+
type: {
|
|
685
|
+
type: "string",
|
|
686
|
+
enum: ["text", "int", "float"],
|
|
687
|
+
description: "Variable type",
|
|
688
|
+
},
|
|
689
|
+
value: {
|
|
690
|
+
type: "string",
|
|
691
|
+
description: "Variable value (always as string, validated against type)",
|
|
692
|
+
},
|
|
693
|
+
},
|
|
694
|
+
required: ["name", "type", "value"],
|
|
695
|
+
},
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
name: "update_global_variable",
|
|
699
|
+
description: "Update an existing global variable. Requires write API key permission.",
|
|
700
|
+
inputSchema: {
|
|
701
|
+
type: "object",
|
|
702
|
+
properties: {
|
|
703
|
+
id: { type: "string", description: "Variable ID" },
|
|
704
|
+
name: { type: "string", description: "New name" },
|
|
705
|
+
type: {
|
|
706
|
+
type: "string",
|
|
707
|
+
enum: ["text", "int", "float"],
|
|
708
|
+
description: "New type",
|
|
709
|
+
},
|
|
710
|
+
value: { type: "string", description: "New value (must match type)" },
|
|
711
|
+
},
|
|
712
|
+
required: ["id"],
|
|
713
|
+
},
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
name: "delete_global_variable",
|
|
717
|
+
description: "Delete a global variable. Must not be used by any bots. Requires write API key permission.",
|
|
718
|
+
inputSchema: {
|
|
719
|
+
type: "object",
|
|
720
|
+
properties: {
|
|
721
|
+
id: { type: "string", description: "Variable ID to delete" },
|
|
722
|
+
},
|
|
723
|
+
required: ["id"],
|
|
724
|
+
},
|
|
725
|
+
},
|
|
726
|
+
// ─── GENERAL ────────────────────────────────────────────────────────────
|
|
727
|
+
{
|
|
728
|
+
name: "get_supported_exchanges",
|
|
729
|
+
description: "Get a list of supported exchanges (code, market, type).",
|
|
730
|
+
inputSchema: { type: "object", properties: {} },
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
name: "get_screener",
|
|
734
|
+
description: "Get crypto screener data with market metrics. Requires active subscription. Supports field selection and filters for category, market cap, volume, sorting.",
|
|
735
|
+
inputSchema: {
|
|
736
|
+
type: "object",
|
|
737
|
+
properties: {
|
|
738
|
+
...fieldsParam,
|
|
739
|
+
...pageParam,
|
|
740
|
+
category: {
|
|
741
|
+
type: "string",
|
|
742
|
+
description: 'Filter by category, e.g. "Layer 1", "DeFi"',
|
|
743
|
+
},
|
|
744
|
+
minMarketCap: {
|
|
745
|
+
type: "number",
|
|
746
|
+
description: "Minimum market cap",
|
|
747
|
+
},
|
|
748
|
+
maxMarketCap: {
|
|
749
|
+
type: "number",
|
|
750
|
+
description: "Maximum market cap",
|
|
751
|
+
},
|
|
752
|
+
minVolume: {
|
|
753
|
+
type: "number",
|
|
754
|
+
description: "Minimum 24h volume",
|
|
755
|
+
},
|
|
756
|
+
sort: {
|
|
757
|
+
type: "string",
|
|
758
|
+
enum: [
|
|
759
|
+
"marketCapRank",
|
|
760
|
+
"currentPrice",
|
|
761
|
+
"priceChangePercentage24h",
|
|
762
|
+
"totalVolume",
|
|
763
|
+
"marketCap",
|
|
764
|
+
],
|
|
765
|
+
description: "Sort field. Default: marketCapRank",
|
|
766
|
+
},
|
|
767
|
+
order: {
|
|
768
|
+
type: "string",
|
|
769
|
+
enum: ["asc", "desc"],
|
|
770
|
+
description: "Sort order. Default: asc",
|
|
771
|
+
},
|
|
772
|
+
},
|
|
773
|
+
},
|
|
774
|
+
},
|
|
775
|
+
];
|
|
776
|
+
// ── Tool Handlers ───────────────────────────────────────────────────────────
|
|
777
|
+
async function handleToolCall(name, args) {
|
|
778
|
+
switch (name) {
|
|
779
|
+
// ── Bot Listing ──────────────────────────────────────────────────────
|
|
780
|
+
case "get_dca_bots": {
|
|
781
|
+
const res = await client.request("GET", "/api/v2/bots/dca", {
|
|
782
|
+
query: {
|
|
783
|
+
fields: args.fields,
|
|
784
|
+
page: args.page,
|
|
785
|
+
status: args.status,
|
|
786
|
+
paperContext: args.paperContext,
|
|
787
|
+
},
|
|
788
|
+
});
|
|
789
|
+
return JSON.stringify(res, null, 2);
|
|
790
|
+
}
|
|
791
|
+
case "get_combo_bots": {
|
|
792
|
+
const res = await client.request("GET", "/api/v2/bots/combo", {
|
|
793
|
+
query: {
|
|
794
|
+
fields: args.fields,
|
|
795
|
+
page: args.page,
|
|
796
|
+
status: args.status,
|
|
797
|
+
paperContext: args.paperContext,
|
|
798
|
+
},
|
|
799
|
+
});
|
|
800
|
+
return JSON.stringify(res, null, 2);
|
|
801
|
+
}
|
|
802
|
+
case "get_grid_bots": {
|
|
803
|
+
const res = await client.request("GET", "/api/v2/bots/grid", {
|
|
804
|
+
query: {
|
|
805
|
+
fields: args.fields,
|
|
806
|
+
page: args.page,
|
|
807
|
+
status: args.status,
|
|
808
|
+
paperContext: args.paperContext,
|
|
809
|
+
},
|
|
810
|
+
});
|
|
811
|
+
return JSON.stringify(res, null, 2);
|
|
812
|
+
}
|
|
813
|
+
// ── Bot Creation ─────────────────────────────────────────────────────
|
|
814
|
+
case "create_dca_bot": {
|
|
815
|
+
const { settings = {}, ...topLevel } = args;
|
|
816
|
+
const body = { ...settings, ...topLevel };
|
|
817
|
+
delete body.settings;
|
|
818
|
+
const res = await client.request("POST", "/api/v2/createDCABot", {
|
|
819
|
+
body,
|
|
820
|
+
});
|
|
821
|
+
return JSON.stringify(res, null, 2);
|
|
822
|
+
}
|
|
823
|
+
case "create_combo_bot": {
|
|
824
|
+
const { settings = {}, ...topLevel } = args;
|
|
825
|
+
const body = { ...settings, ...topLevel };
|
|
826
|
+
delete body.settings;
|
|
827
|
+
const res = await client.request("POST", "/api/v2/createComboBot", {
|
|
828
|
+
body,
|
|
829
|
+
});
|
|
830
|
+
return JSON.stringify(res, null, 2);
|
|
831
|
+
}
|
|
832
|
+
case "create_grid_bot": {
|
|
833
|
+
const { settings = {}, ...topLevel } = args;
|
|
834
|
+
const body = { ...settings, ...topLevel };
|
|
835
|
+
delete body.settings;
|
|
836
|
+
const res = await client.request("POST", "/api/v2/createGridBot", {
|
|
837
|
+
body,
|
|
838
|
+
});
|
|
839
|
+
return JSON.stringify(res, null, 2);
|
|
840
|
+
}
|
|
841
|
+
// ── Bot Update ───────────────────────────────────────────────────────
|
|
842
|
+
case "update_dca_bot": {
|
|
843
|
+
const res = await client.request("POST", "/api/v2/updateDCABot", {
|
|
844
|
+
query: { botId: args.botId, paperContext: args.paperContext },
|
|
845
|
+
body: args.settings,
|
|
846
|
+
});
|
|
847
|
+
return JSON.stringify(res, null, 2);
|
|
848
|
+
}
|
|
849
|
+
case "update_combo_bot": {
|
|
850
|
+
const res = await client.request("POST", "/api/v2/updateComboBot", {
|
|
851
|
+
query: { botId: args.botId, paperContext: args.paperContext },
|
|
852
|
+
body: args.settings,
|
|
853
|
+
});
|
|
854
|
+
return JSON.stringify(res, null, 2);
|
|
855
|
+
}
|
|
856
|
+
// ── Bot Clone ────────────────────────────────────────────────────────
|
|
857
|
+
case "clone_dca_bot": {
|
|
858
|
+
const res = await client.request("PUT", "/api/v2/cloneDCABot", {
|
|
859
|
+
query: { botId: args.botId, paperContext: args.paperContext },
|
|
860
|
+
body: args.overrides || {},
|
|
861
|
+
});
|
|
862
|
+
return JSON.stringify(res, null, 2);
|
|
863
|
+
}
|
|
864
|
+
case "clone_combo_bot": {
|
|
865
|
+
const res = await client.request("PUT", "/api/v2/cloneComboBot", {
|
|
866
|
+
query: { botId: args.botId, paperContext: args.paperContext },
|
|
867
|
+
body: args.overrides || {},
|
|
868
|
+
});
|
|
869
|
+
return JSON.stringify(res, null, 2);
|
|
870
|
+
}
|
|
871
|
+
// ── Bot Lifecycle ────────────────────────────────────────────────────
|
|
872
|
+
case "start_bot": {
|
|
873
|
+
const res = await client.request("POST", "/api/v2/startBot", {
|
|
874
|
+
query: {
|
|
875
|
+
botId: args.botId,
|
|
876
|
+
type: args.type,
|
|
877
|
+
paperContext: args.paperContext,
|
|
878
|
+
},
|
|
879
|
+
});
|
|
880
|
+
return JSON.stringify(res, null, 2);
|
|
881
|
+
}
|
|
882
|
+
case "stop_bot": {
|
|
883
|
+
const res = await client.request("DELETE", "/api/v2/stopBot", {
|
|
884
|
+
query: {
|
|
885
|
+
botId: args.botId,
|
|
886
|
+
botType: args.botType,
|
|
887
|
+
closeType: args.closeType,
|
|
888
|
+
closeGridType: args.closeGridType,
|
|
889
|
+
cancelPartiallyFilled: args.cancelPartiallyFilled,
|
|
890
|
+
paperContext: args.paperContext,
|
|
891
|
+
},
|
|
892
|
+
});
|
|
893
|
+
return JSON.stringify(res, null, 2);
|
|
894
|
+
}
|
|
895
|
+
case "archive_bot": {
|
|
896
|
+
const res = await client.request("DELETE", "/api/v2/archiveBot", {
|
|
897
|
+
query: {
|
|
898
|
+
botId: args.botId,
|
|
899
|
+
botType: args.botType,
|
|
900
|
+
paperContext: args.paperContext,
|
|
901
|
+
},
|
|
902
|
+
});
|
|
903
|
+
return JSON.stringify(res, null, 2);
|
|
904
|
+
}
|
|
905
|
+
case "restore_bot": {
|
|
906
|
+
const res = await client.request("POST", "/api/v2/restoreBot", {
|
|
907
|
+
query: {
|
|
908
|
+
botId: args.botId,
|
|
909
|
+
type: args.type,
|
|
910
|
+
paperContext: args.paperContext,
|
|
911
|
+
},
|
|
912
|
+
});
|
|
913
|
+
return JSON.stringify(res, null, 2);
|
|
914
|
+
}
|
|
915
|
+
case "change_bot_pairs": {
|
|
916
|
+
const query = {
|
|
917
|
+
botId: args.botId,
|
|
918
|
+
botName: args.botName,
|
|
919
|
+
paperContext: args.paperContext,
|
|
920
|
+
pairsToSetMode: args.pairsToSetMode,
|
|
921
|
+
};
|
|
922
|
+
if (args.pairsToSet)
|
|
923
|
+
query.pairsToSet = args.pairsToSet;
|
|
924
|
+
if (args.pairsToAdd)
|
|
925
|
+
query["pairsToChange[add]"] = args.pairsToAdd;
|
|
926
|
+
if (args.pairsToRemove)
|
|
927
|
+
query["pairsToChange[remove]"] = args.pairsToRemove;
|
|
928
|
+
const res = await client.request("POST", "/api/v2/changeBotPairs", {
|
|
929
|
+
query,
|
|
930
|
+
});
|
|
931
|
+
return JSON.stringify(res, null, 2);
|
|
932
|
+
}
|
|
933
|
+
// ── Deals Listing ────────────────────────────────────────────────────
|
|
934
|
+
case "get_deals": {
|
|
935
|
+
const res = await client.request("GET", "/api/v2/deals", {
|
|
936
|
+
query: {
|
|
937
|
+
fields: args.fields,
|
|
938
|
+
page: args.page,
|
|
939
|
+
type: args.type,
|
|
940
|
+
status: args.status,
|
|
941
|
+
botId: args.botId,
|
|
942
|
+
terminal: args.terminal,
|
|
943
|
+
paperContext: args.paperContext,
|
|
944
|
+
},
|
|
945
|
+
});
|
|
946
|
+
return JSON.stringify(res, null, 2);
|
|
947
|
+
}
|
|
948
|
+
// ── Deal Creation & Management ───────────────────────────────────────
|
|
949
|
+
case "create_terminal_deal": {
|
|
950
|
+
const { settings = {}, ...topLevel } = args;
|
|
951
|
+
const body = { ...settings, ...topLevel };
|
|
952
|
+
delete body.settings;
|
|
953
|
+
const res = await client.request("POST", "/api/v2/createTerminalDeal", {
|
|
954
|
+
body,
|
|
955
|
+
});
|
|
956
|
+
return JSON.stringify(res, null, 2);
|
|
957
|
+
}
|
|
958
|
+
case "update_dca_deal": {
|
|
959
|
+
const res = await client.request("POST", "/api/v2/updateDCADeal", {
|
|
960
|
+
query: { dealId: args.dealId, paperContext: args.paperContext },
|
|
961
|
+
body: args.settings,
|
|
962
|
+
});
|
|
963
|
+
return JSON.stringify(res, null, 2);
|
|
964
|
+
}
|
|
965
|
+
case "update_combo_deal": {
|
|
966
|
+
const res = await client.request("POST", "/api/v2/updateComboDeal", {
|
|
967
|
+
query: { dealId: args.dealId, paperContext: args.paperContext },
|
|
968
|
+
body: args.settings,
|
|
969
|
+
});
|
|
970
|
+
return JSON.stringify(res, null, 2);
|
|
971
|
+
}
|
|
972
|
+
case "start_deal": {
|
|
973
|
+
const res = await client.request("POST", "/api/v2/startDeal", {
|
|
974
|
+
query: {
|
|
975
|
+
botId: args.botId,
|
|
976
|
+
botType: args.botType,
|
|
977
|
+
symbol: args.symbol,
|
|
978
|
+
paperContext: args.paperContext,
|
|
979
|
+
},
|
|
980
|
+
});
|
|
981
|
+
return JSON.stringify(res, null, 2);
|
|
982
|
+
}
|
|
983
|
+
case "close_deal": {
|
|
984
|
+
const res = await client.request("DELETE", `/api/v2/closeDeal/${args.dealId}`, {
|
|
985
|
+
query: {
|
|
986
|
+
type: args.type,
|
|
987
|
+
botType: args.botType,
|
|
988
|
+
paperContext: args.paperContext,
|
|
989
|
+
},
|
|
990
|
+
});
|
|
991
|
+
return JSON.stringify(res, null, 2);
|
|
992
|
+
}
|
|
993
|
+
case "add_funds": {
|
|
994
|
+
const res = await client.request("POST", "/api/v2/addFunds", {
|
|
995
|
+
query: {
|
|
996
|
+
botId: args.botId,
|
|
997
|
+
dealId: args.dealId,
|
|
998
|
+
qty: args.qty,
|
|
999
|
+
type: args.type,
|
|
1000
|
+
asset: args.asset,
|
|
1001
|
+
symbol: args.symbol,
|
|
1002
|
+
paperContext: args.paperContext,
|
|
1003
|
+
},
|
|
1004
|
+
});
|
|
1005
|
+
return JSON.stringify(res, null, 2);
|
|
1006
|
+
}
|
|
1007
|
+
case "reduce_funds": {
|
|
1008
|
+
const res = await client.request("POST", "/api/v2/reduceFunds", {
|
|
1009
|
+
query: {
|
|
1010
|
+
botId: args.botId,
|
|
1011
|
+
dealId: args.dealId,
|
|
1012
|
+
qty: args.qty,
|
|
1013
|
+
type: args.type,
|
|
1014
|
+
asset: args.asset,
|
|
1015
|
+
symbol: args.symbol,
|
|
1016
|
+
paperContext: args.paperContext,
|
|
1017
|
+
},
|
|
1018
|
+
});
|
|
1019
|
+
return JSON.stringify(res, null, 2);
|
|
1020
|
+
}
|
|
1021
|
+
// ── User ─────────────────────────────────────────────────────────────
|
|
1022
|
+
case "get_balances": {
|
|
1023
|
+
const res = await client.request("GET", "/api/v2/user/balances", {
|
|
1024
|
+
query: {
|
|
1025
|
+
fields: args.fields,
|
|
1026
|
+
exchangeId: args.exchangeId,
|
|
1027
|
+
asset: args.asset,
|
|
1028
|
+
assets: args.assets,
|
|
1029
|
+
paperContext: args.paperContext,
|
|
1030
|
+
},
|
|
1031
|
+
});
|
|
1032
|
+
return JSON.stringify(res, null, 2);
|
|
1033
|
+
}
|
|
1034
|
+
case "get_user_exchanges": {
|
|
1035
|
+
const res = await client.request("GET", "/api/v2/user/exchanges", {
|
|
1036
|
+
query: { paperContext: args.paperContext },
|
|
1037
|
+
});
|
|
1038
|
+
return JSON.stringify(res, null, 2);
|
|
1039
|
+
}
|
|
1040
|
+
case "get_global_variables": {
|
|
1041
|
+
const res = await client.request("GET", "/api/v2/user/globalVars", {
|
|
1042
|
+
query: { page: args.page },
|
|
1043
|
+
});
|
|
1044
|
+
return JSON.stringify(res, null, 2);
|
|
1045
|
+
}
|
|
1046
|
+
case "create_global_variable": {
|
|
1047
|
+
const res = await client.request("POST", "/api/v2/user/globalVars", {
|
|
1048
|
+
body: { name: args.name, type: args.type, value: args.value },
|
|
1049
|
+
});
|
|
1050
|
+
return JSON.stringify(res, null, 2);
|
|
1051
|
+
}
|
|
1052
|
+
case "update_global_variable": {
|
|
1053
|
+
const body = {};
|
|
1054
|
+
if (args.name !== undefined)
|
|
1055
|
+
body.name = args.name;
|
|
1056
|
+
if (args.type !== undefined)
|
|
1057
|
+
body.type = args.type;
|
|
1058
|
+
if (args.value !== undefined)
|
|
1059
|
+
body.value = args.value;
|
|
1060
|
+
const res = await client.request("PUT", `/api/v2/user/globalVars/${args.id}`, { body });
|
|
1061
|
+
return JSON.stringify(res, null, 2);
|
|
1062
|
+
}
|
|
1063
|
+
case "delete_global_variable": {
|
|
1064
|
+
const res = await client.request("DELETE", `/api/v2/user/globalVars/${args.id}`);
|
|
1065
|
+
return JSON.stringify(res, null, 2);
|
|
1066
|
+
}
|
|
1067
|
+
// ── General ──────────────────────────────────────────────────────────
|
|
1068
|
+
case "get_supported_exchanges": {
|
|
1069
|
+
const res = await client.request("GET", "/api/v2/exchanges");
|
|
1070
|
+
return JSON.stringify(res, null, 2);
|
|
1071
|
+
}
|
|
1072
|
+
case "get_screener": {
|
|
1073
|
+
const res = await client.request("GET", "/api/v2/screener", {
|
|
1074
|
+
query: {
|
|
1075
|
+
fields: args.fields,
|
|
1076
|
+
page: args.page,
|
|
1077
|
+
category: args.category,
|
|
1078
|
+
minMarketCap: args.minMarketCap,
|
|
1079
|
+
maxMarketCap: args.maxMarketCap,
|
|
1080
|
+
minVolume: args.minVolume,
|
|
1081
|
+
sort: args.sort,
|
|
1082
|
+
order: args.order,
|
|
1083
|
+
},
|
|
1084
|
+
});
|
|
1085
|
+
return JSON.stringify(res, null, 2);
|
|
1086
|
+
}
|
|
1087
|
+
default:
|
|
1088
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
// ── MCP Server Setup ────────────────────────────────────────────────────────
|
|
1092
|
+
const server = new Server({ name: "gainium-mcp", version: "2.0.0" }, { capabilities: { tools: {} } });
|
|
1093
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
|
|
1094
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1095
|
+
const { name, arguments: toolArgs } = request.params;
|
|
1096
|
+
try {
|
|
1097
|
+
const result = await handleToolCall(name, toolArgs ?? {});
|
|
1098
|
+
return { content: [{ type: "text", text: result }] };
|
|
1099
|
+
}
|
|
1100
|
+
catch (error) {
|
|
1101
|
+
return {
|
|
1102
|
+
content: [
|
|
1103
|
+
{
|
|
1104
|
+
type: "text",
|
|
1105
|
+
text: `Error: ${error?.message ?? String(error)}`,
|
|
1106
|
+
},
|
|
1107
|
+
],
|
|
1108
|
+
isError: true,
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
});
|
|
1112
|
+
// ── Start ───────────────────────────────────────────────────────────────────
|
|
1113
|
+
async function main() {
|
|
1114
|
+
const transport = new StdioServerTransport();
|
|
1115
|
+
await server.connect(transport);
|
|
1116
|
+
console.error("[gainium-mcp] Server started");
|
|
1117
|
+
}
|
|
1118
|
+
main().catch((err) => {
|
|
1119
|
+
console.error("[gainium-mcp] Fatal error:", err);
|
|
1120
|
+
process.exit(1);
|
|
1121
|
+
});
|