@michaleffffff/mcp-trading-server 2.3.4 → 2.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/dist/server.js +2 -2
- package/dist/services/poolService.js +4 -1
- package/dist/tools/accountInfo.js +2 -2
- package/dist/tools/accountTransfer.js +2 -1
- package/dist/tools/cancelAllOrders.js +36 -0
- package/dist/tools/checkApproval.js +3 -2
- package/dist/tools/closePosition.js +4 -2
- package/dist/tools/executeTrade.js +5 -3
- package/dist/tools/getAccountVipInfo.js +20 -0
- package/dist/tools/getAllTickers.js +35 -0
- package/dist/tools/getBaseDetail.js +21 -0
- package/dist/tools/getKline.js +1 -1
- package/dist/tools/getKlineLatestBar.js +27 -0
- package/dist/tools/getMarketList.js +1 -1
- package/dist/tools/getMarketListRaw.js +16 -0
- package/dist/tools/getNetworkFee.js +21 -0
- package/dist/tools/getPoolSymbolAll.js +16 -0
- package/dist/tools/getUserTradingFeeRate.js +22 -0
- package/dist/tools/index.js +9 -0
- package/dist/tools/manageLiquidity.js +9 -7
- package/dist/tools/orderQueries.js +2 -2
- package/dist/tools/positionHistory.js +2 -2
- package/dist/tools/searchMarket.js +1 -1
- package/dist/tools/setTpSl.js +4 -2
- package/dist/tools/updateOrderTpSl.js +2 -1
- package/dist/utils/schema.js +18 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -187,25 +187,32 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
|
187
187
|
|
|
188
188
|
# Tools
|
|
189
189
|
|
|
190
|
-
The server exposes
|
|
190
|
+
The server exposes 37 tools categorized for AI:
|
|
191
191
|
|
|
192
192
|
### Trading Operations
|
|
193
193
|
* **execute_trade**: Execute a new trade or add to an existing position.
|
|
194
194
|
* **close_position**: Close an open position.
|
|
195
195
|
* **close_all_positions**: Emergency: close ALL open positions in a pool at once.
|
|
196
196
|
* **cancel_order**: Cancel an open order by its order ID.
|
|
197
|
+
* **cancel_all_orders**: Cancel all open orders at once (or a provided order ID list).
|
|
197
198
|
* **set_tp_sl**: Set take profit and stop loss prices for an open position.
|
|
198
199
|
* **adjust_margin**: Adjust the margin (collateral) of an open position.
|
|
200
|
+
* **get_user_trading_fee_rate**: Query current maker/taker fee rates by asset class and risk tier.
|
|
201
|
+
* **get_network_fee**: Query estimated network fee requirements for a market.
|
|
199
202
|
|
|
200
203
|
### Market Data
|
|
201
204
|
* **search_market**: Search for an active market (e.g., "BTC") to retrieve its unique `poolId` (Crucial prerequisite).
|
|
202
205
|
* **get_market_price**: Get the current ticker market price for a specific pool.
|
|
203
206
|
* **get_oracle_price**: Get the current EVM oracle price for a specific pool.
|
|
204
|
-
* **
|
|
207
|
+
* **get_kline_latest_bar**: Get only the latest bar for an interval.
|
|
208
|
+
* **get_all_tickers**: Get all ticker snapshots in one request.
|
|
209
|
+
* **get_market_list**, **get_market_list_raw**, **get_kline**, **get_pool_info**...
|
|
210
|
+
* **get_pool_symbol_all**, **get_base_detail**, **get_pool_level_config**...
|
|
205
211
|
|
|
206
212
|
### Account & Portfolio
|
|
207
213
|
* **get_positions**: Get all open positions for the current account.
|
|
208
214
|
* **get_balances**: Get the account balances for different tokens.
|
|
215
|
+
* **get_account_vip_info**: Query account VIP tier details.
|
|
209
216
|
* **position_history**, **open_orders**...
|
|
210
217
|
|
|
211
218
|
### Liquidity Provision (LP)
|
|
@@ -269,7 +276,8 @@ npm run dev
|
|
|
269
276
|
Run tests:
|
|
270
277
|
|
|
271
278
|
```bash
|
|
272
|
-
|
|
279
|
+
npm test
|
|
280
|
+
npm run test:new-tools
|
|
273
281
|
```
|
|
274
282
|
|
|
275
283
|
---
|
package/dist/server.js
CHANGED
|
@@ -81,7 +81,7 @@ function zodSchemaToJsonSchema(zodSchema) {
|
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
83
|
// ─── MCP Server ───
|
|
84
|
-
const server = new Server({ name: "myx-mcp-trading-server", version: "2.3.
|
|
84
|
+
const server = new Server({ name: "myx-mcp-trading-server", version: "2.3.6" }, { capabilities: { tools: {}, resources: {}, prompts: {} } });
|
|
85
85
|
// List tools
|
|
86
86
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
87
87
|
return {
|
|
@@ -181,7 +181,7 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
|
181
181
|
async function main() {
|
|
182
182
|
const transport = new StdioServerTransport();
|
|
183
183
|
await server.connect(transport);
|
|
184
|
-
logger.info("🚀 MYX Trading MCP Server v2.3.
|
|
184
|
+
logger.info("🚀 MYX Trading MCP Server v2.3.6 running (stdio, pure on-chain, prod ready)");
|
|
185
185
|
}
|
|
186
186
|
main().catch((err) => {
|
|
187
187
|
logger.error("Fatal Server Startup Error", err);
|
|
@@ -67,5 +67,8 @@ export async function getLpPrice(poolType, poolId) {
|
|
|
67
67
|
if (poolType === "BASE") {
|
|
68
68
|
return base.getLpPrice(chainId, poolId);
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
if (poolType === "QUOTE") {
|
|
71
|
+
return quote.getLpPrice(chainId, poolId);
|
|
72
|
+
}
|
|
73
|
+
throw new Error("poolType must be 'BASE' or 'QUOTE'.");
|
|
71
74
|
}
|
|
@@ -40,8 +40,8 @@ export const getTradeFlowTool = {
|
|
|
40
40
|
name: "get_trade_flow",
|
|
41
41
|
description: "Get account trade flow / transaction history.",
|
|
42
42
|
schema: {
|
|
43
|
-
page: z.number().optional().describe("Page number (default 1)"),
|
|
44
|
-
limit: z.number().optional().describe("Results per page (default 20)"),
|
|
43
|
+
page: z.coerce.number().optional().describe("Page number (default 1)"),
|
|
44
|
+
limit: z.coerce.number().optional().describe("Results per page (default 20)"),
|
|
45
45
|
},
|
|
46
46
|
handler: async (args) => {
|
|
47
47
|
try {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { resolveClient, getChainId, getQuoteToken } from "../auth/resolveClient.js";
|
|
3
3
|
import { normalizeAddress } from "../utils/address.js";
|
|
4
|
+
import { booleanLike } from "../utils/schema.js";
|
|
4
5
|
export const accountDepositTool = {
|
|
5
6
|
name: "account_deposit",
|
|
6
7
|
description: "Deposit funds from wallet into the MYX trading account.",
|
|
@@ -31,7 +32,7 @@ export const accountWithdrawTool = {
|
|
|
31
32
|
schema: {
|
|
32
33
|
poolId: z.string().describe("Pool ID to withdraw from"),
|
|
33
34
|
amount: z.string().describe("Amount to withdraw (in token decimals)"),
|
|
34
|
-
isQuoteToken:
|
|
35
|
+
isQuoteToken: booleanLike.optional().describe("Whether to withdraw as quote token (default true)"),
|
|
35
36
|
},
|
|
36
37
|
handler: async (args) => {
|
|
37
38
|
try {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { resolveClient, getChainId } from "../auth/resolveClient.js";
|
|
3
|
+
export const cancelAllOrdersTool = {
|
|
4
|
+
name: "cancel_all_orders",
|
|
5
|
+
description: "Cancel all open orders, or cancel the provided orderIds in one call.",
|
|
6
|
+
schema: {
|
|
7
|
+
orderIds: z.array(z.string()).optional().describe("Optional explicit order IDs. If omitted, all open orders will be cancelled."),
|
|
8
|
+
},
|
|
9
|
+
handler: async (args) => {
|
|
10
|
+
try {
|
|
11
|
+
const { client, address } = await resolveClient();
|
|
12
|
+
const chainId = getChainId();
|
|
13
|
+
let orderIds = Array.isArray(args.orderIds) ? args.orderIds : [];
|
|
14
|
+
if (orderIds.length === 0) {
|
|
15
|
+
const openOrders = await client.order.getOrders(address);
|
|
16
|
+
const rows = Array.isArray(openOrders?.data) ? openOrders.data : [];
|
|
17
|
+
orderIds = rows
|
|
18
|
+
.map((o) => o.orderId ?? o.id ?? o.order_id)
|
|
19
|
+
.filter((id) => id !== undefined && id !== null)
|
|
20
|
+
.map((id) => String(id));
|
|
21
|
+
}
|
|
22
|
+
if (orderIds.length === 0) {
|
|
23
|
+
return {
|
|
24
|
+
content: [{ type: "text", text: JSON.stringify({ status: "success", data: { message: "No open orders to cancel.", cancelled: 0 } }, (_, v) => typeof v === "bigint" ? v.toString() : v, 2) }],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const result = await client.order.cancelAllOrders(orderIds, chainId);
|
|
28
|
+
return {
|
|
29
|
+
content: [{ type: "text", text: JSON.stringify({ status: "success", data: { cancelled: orderIds.length, orderIds, result } }, (_, v) => typeof v === "bigint" ? v.toString() : v, 2) }],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { resolveClient, getChainId, getQuoteToken } from "../auth/resolveClient.js";
|
|
3
3
|
import { normalizeAddress } from "../utils/address.js";
|
|
4
|
+
import { booleanLike } from "../utils/schema.js";
|
|
4
5
|
const MAX_UINT256 = "115792089237316195423570985008687907853269984665640564039457584007913129639935";
|
|
5
6
|
export const checkApprovalTool = {
|
|
6
7
|
name: "check_approval",
|
|
@@ -8,8 +9,8 @@ export const checkApprovalTool = {
|
|
|
8
9
|
schema: {
|
|
9
10
|
amount: z.string().describe("Amount to check approval for (in token decimals)"),
|
|
10
11
|
quoteToken: z.string().optional().describe("Token address to check. Uses default if omitted."),
|
|
11
|
-
autoApprove:
|
|
12
|
-
approveMax:
|
|
12
|
+
autoApprove: booleanLike.optional().describe("If true, automatically approve when needed."),
|
|
13
|
+
approveMax: booleanLike.optional().describe("If true with autoApprove, approve unlimited MaxUint256 (default false: approve exact amount only)."),
|
|
13
14
|
},
|
|
14
15
|
handler: async (args) => {
|
|
15
16
|
try {
|
|
@@ -7,8 +7,10 @@ export const closePositionTool = {
|
|
|
7
7
|
schema: {
|
|
8
8
|
poolId: z.string().describe("Pool ID"),
|
|
9
9
|
positionId: z.string().describe("Position ID to close"),
|
|
10
|
-
direction: z.
|
|
11
|
-
|
|
10
|
+
direction: z.coerce.number().int().refine((value) => value === 0 || value === 1, {
|
|
11
|
+
message: "direction must be 0 (LONG) or 1 (SHORT)",
|
|
12
|
+
}).describe("0 = LONG, 1 = SHORT (must match position direction)"),
|
|
13
|
+
leverage: z.coerce.number().describe("Position leverage"),
|
|
12
14
|
size: z.string().describe("Size to close (human-readable or 18-decimal raw units)"),
|
|
13
15
|
price: z.string().describe("Price in human-readable units"),
|
|
14
16
|
slippagePct: z.string().optional().describe("Slippage in bps, default '100'"),
|
|
@@ -6,12 +6,14 @@ export const executeTradeTool = {
|
|
|
6
6
|
description: "Execute a new trade or add to an existing position.",
|
|
7
7
|
schema: {
|
|
8
8
|
poolId: z.string().describe("Pool ID to trade in"),
|
|
9
|
-
direction: z.
|
|
9
|
+
direction: z.coerce.number().int().refine((value) => value === 0 || value === 1, {
|
|
10
|
+
message: "direction must be 0 (LONG) or 1 (SHORT)",
|
|
11
|
+
}).describe("0 = LONG, 1 = SHORT"),
|
|
10
12
|
collateralAmount: z.string().describe("Collateral in human-readable units (e.g. '100' for 100 USDC)"),
|
|
11
|
-
leverage: z.number().optional().describe("Leverage multiplier (e.g. 10, default 10)"),
|
|
13
|
+
leverage: z.coerce.number().optional().describe("Leverage multiplier (e.g. 10, default 10)"),
|
|
12
14
|
price: z.string().optional().describe("Limit price in human-readable units (e.g. '3000'). Required for LIMIT orders, omitted for MARKET orders."),
|
|
13
15
|
quoteToken: z.string().optional().describe("Quote token address. Uses env default if omitted."),
|
|
14
|
-
quoteDecimals: z.number().optional().describe("Quote token decimals (default 6)."),
|
|
16
|
+
quoteDecimals: z.coerce.number().optional().describe("Quote token decimals (default 6)."),
|
|
15
17
|
slippagePct: z.string().optional().describe("Slippage in bps, default '100' = 1%"),
|
|
16
18
|
positionId: z.string().optional().describe("Existing position ID to add to. Default '0' = new position."),
|
|
17
19
|
tpPrice: z.string().optional().describe("Take-profit price (human-readable)"),
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { resolveClient, getChainId } from "../auth/resolveClient.js";
|
|
3
|
+
export const getAccountVipInfoTool = {
|
|
4
|
+
name: "get_account_vip_info",
|
|
5
|
+
description: "Get account VIP/fee-tier information.",
|
|
6
|
+
schema: {
|
|
7
|
+
chainId: z.coerce.number().optional().describe("Optional chainId override"),
|
|
8
|
+
},
|
|
9
|
+
handler: async (args) => {
|
|
10
|
+
try {
|
|
11
|
+
const { client, address } = await resolveClient();
|
|
12
|
+
const chainId = args.chainId ?? getChainId();
|
|
13
|
+
const result = await client.account.getAccountVipInfo(chainId, address);
|
|
14
|
+
return { content: [{ type: "text", text: JSON.stringify({ status: "success", data: result }, (_, v) => typeof v === "bigint" ? v.toString() : v, 2) }] };
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { resolveClient, getChainId } from "../auth/resolveClient.js";
|
|
2
|
+
export const getAllTickersTool = {
|
|
3
|
+
name: "get_all_tickers",
|
|
4
|
+
description: "Get ticker snapshots for all markets.",
|
|
5
|
+
schema: {},
|
|
6
|
+
handler: async () => {
|
|
7
|
+
try {
|
|
8
|
+
const { client } = await resolveClient();
|
|
9
|
+
try {
|
|
10
|
+
const result = await client.api.getAllTickers();
|
|
11
|
+
return { content: [{ type: "text", text: JSON.stringify({ status: "success", data: result }, (_, v) => typeof v === "bigint" ? v.toString() : v, 2) }] };
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
// Fallback for networks/environments where getAllTickers endpoint is unavailable.
|
|
15
|
+
const chainId = getChainId();
|
|
16
|
+
const poolList = await client.api.getPoolList();
|
|
17
|
+
const pools = Array.isArray(poolList?.data) ? poolList.data : [];
|
|
18
|
+
const poolIds = pools.map((p) => p.poolId).filter((id) => !!id);
|
|
19
|
+
if (poolIds.length === 0) {
|
|
20
|
+
throw new Error("Failed to fetch all tickers and no pools were available for fallback query.");
|
|
21
|
+
}
|
|
22
|
+
const tickerRows = await client.markets.getTickerList({ chainId, poolIds });
|
|
23
|
+
return {
|
|
24
|
+
content: [{
|
|
25
|
+
type: "text",
|
|
26
|
+
text: JSON.stringify({ status: "success", data: { source: "markets.getTickerList_fallback", rows: tickerRows } }, (_, v) => typeof v === "bigint" ? v.toString() : v, 2),
|
|
27
|
+
}],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { resolveClient, getChainId } from "../auth/resolveClient.js";
|
|
3
|
+
export const getBaseDetailTool = {
|
|
4
|
+
name: "get_base_detail",
|
|
5
|
+
description: "Get base asset detail metrics for a pool.",
|
|
6
|
+
schema: {
|
|
7
|
+
poolId: z.string().describe("Pool ID"),
|
|
8
|
+
chainId: z.coerce.number().optional().describe("Optional chainId override"),
|
|
9
|
+
},
|
|
10
|
+
handler: async (args) => {
|
|
11
|
+
try {
|
|
12
|
+
const { client } = await resolveClient();
|
|
13
|
+
const chainId = args.chainId ?? getChainId();
|
|
14
|
+
const result = await client.markets.getBaseDetail({ chainId, poolId: args.poolId });
|
|
15
|
+
return { content: [{ type: "text", text: JSON.stringify({ status: "success", data: result }, (_, v) => typeof v === "bigint" ? v.toString() : v, 2) }] };
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
};
|
package/dist/tools/getKline.js
CHANGED
|
@@ -6,7 +6,7 @@ export const getKlineTool = {
|
|
|
6
6
|
schema: {
|
|
7
7
|
poolId: z.string().describe("Pool ID"),
|
|
8
8
|
interval: z.string().describe("K-line interval: '1m','5m','15m','30m','1h','4h','1d','1w','1M'"),
|
|
9
|
-
limit: z.number().optional().describe("Number of bars (default 100)"),
|
|
9
|
+
limit: z.coerce.number().optional().describe("Number of bars (default 100)"),
|
|
10
10
|
},
|
|
11
11
|
handler: async (args) => {
|
|
12
12
|
try {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { resolveClient, getChainId } from "../auth/resolveClient.js";
|
|
3
|
+
export const getKlineLatestBarTool = {
|
|
4
|
+
name: "get_kline_latest_bar",
|
|
5
|
+
description: "Get the latest single K-line/candlestick bar for a pool.",
|
|
6
|
+
schema: {
|
|
7
|
+
poolId: z.string().describe("Pool ID"),
|
|
8
|
+
interval: z.string().describe("K-line interval: '1m','5m','15m','30m','1h','4h','1d','1w','1M'"),
|
|
9
|
+
},
|
|
10
|
+
handler: async (args) => {
|
|
11
|
+
try {
|
|
12
|
+
const { client } = await resolveClient();
|
|
13
|
+
const chainId = getChainId();
|
|
14
|
+
const result = await client.markets.getKlineLatestBar({
|
|
15
|
+
poolId: args.poolId,
|
|
16
|
+
chainId,
|
|
17
|
+
interval: args.interval,
|
|
18
|
+
limit: 1,
|
|
19
|
+
endTime: Date.now(),
|
|
20
|
+
});
|
|
21
|
+
return { content: [{ type: "text", text: JSON.stringify({ status: "success", data: result }, (_, v) => typeof v === "bigint" ? v.toString() : v, 2) }] };
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
};
|
|
@@ -5,7 +5,7 @@ export const getMarketListTool = {
|
|
|
5
5
|
name: "get_market_list",
|
|
6
6
|
description: "Get tradable markets/pools (state=2 Active). Supports configurable result limit; backend may still enforce its own cap.",
|
|
7
7
|
schema: {
|
|
8
|
-
limit: z.number().optional().describe("Max results to request (default 1000)."),
|
|
8
|
+
limit: z.coerce.number().optional().describe("Max results to request (default 1000)."),
|
|
9
9
|
},
|
|
10
10
|
handler: async (args) => {
|
|
11
11
|
try {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { resolveClient } from "../auth/resolveClient.js";
|
|
2
|
+
export const getMarketListRawTool = {
|
|
3
|
+
name: "get_market_list_raw",
|
|
4
|
+
description: "Get raw market list directly from MYX API without MCP-side filtering.",
|
|
5
|
+
schema: {},
|
|
6
|
+
handler: async () => {
|
|
7
|
+
try {
|
|
8
|
+
const { client } = await resolveClient();
|
|
9
|
+
const result = await client.api.getMarketList();
|
|
10
|
+
return { content: [{ type: "text", text: JSON.stringify({ status: "success", data: result }, (_, v) => typeof v === "bigint" ? v.toString() : v, 2) }] };
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { resolveClient, getChainId } from "../auth/resolveClient.js";
|
|
3
|
+
export const getNetworkFeeTool = {
|
|
4
|
+
name: "get_network_fee",
|
|
5
|
+
description: "Estimate network fee requirements for a market.",
|
|
6
|
+
schema: {
|
|
7
|
+
marketId: z.string().describe("Market ID"),
|
|
8
|
+
chainId: z.coerce.number().optional().describe("Optional chainId override"),
|
|
9
|
+
},
|
|
10
|
+
handler: async (args) => {
|
|
11
|
+
try {
|
|
12
|
+
const { client } = await resolveClient();
|
|
13
|
+
const chainId = args.chainId ?? getChainId();
|
|
14
|
+
const result = await client.utils.getNetworkFee(args.marketId, chainId);
|
|
15
|
+
return { content: [{ type: "text", text: JSON.stringify({ status: "success", data: result }, (_, v) => typeof v === "bigint" ? v.toString() : v, 2) }] };
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { resolveClient } from "../auth/resolveClient.js";
|
|
2
|
+
export const getPoolSymbolAllTool = {
|
|
3
|
+
name: "get_pool_symbol_all",
|
|
4
|
+
description: "Get symbol/icon metadata for all pools.",
|
|
5
|
+
schema: {},
|
|
6
|
+
handler: async () => {
|
|
7
|
+
try {
|
|
8
|
+
const { client } = await resolveClient();
|
|
9
|
+
const result = await client.markets.getPoolSymbolAll();
|
|
10
|
+
return { content: [{ type: "text", text: JSON.stringify({ status: "success", data: result }, (_, v) => typeof v === "bigint" ? v.toString() : v, 2) }] };
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { resolveClient, getChainId } from "../auth/resolveClient.js";
|
|
3
|
+
export const getUserTradingFeeRateTool = {
|
|
4
|
+
name: "get_user_trading_fee_rate",
|
|
5
|
+
description: "Get maker/taker fee rates for a given assetClass and riskTier.",
|
|
6
|
+
schema: {
|
|
7
|
+
assetClass: z.coerce.number().describe("Asset class ID"),
|
|
8
|
+
riskTier: z.coerce.number().describe("Risk tier"),
|
|
9
|
+
chainId: z.coerce.number().optional().describe("Optional chainId override"),
|
|
10
|
+
},
|
|
11
|
+
handler: async (args) => {
|
|
12
|
+
try {
|
|
13
|
+
const { client } = await resolveClient();
|
|
14
|
+
const chainId = args.chainId ?? getChainId();
|
|
15
|
+
const result = await client.utils.getUserTradingFeeRate(args.assetClass, args.riskTier, chainId);
|
|
16
|
+
return { content: [{ type: "text", text: JSON.stringify({ status: "success", data: result }, (_, v) => typeof v === "bigint" ? v.toString() : v, 2) }] };
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
};
|
package/dist/tools/index.js
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
// Tools — 交易
|
|
2
2
|
export { executeTradeTool } from "./executeTrade.js";
|
|
3
3
|
export { cancelOrderTool } from "./cancelOrder.js";
|
|
4
|
+
export { cancelAllOrdersTool } from "./cancelAllOrders.js";
|
|
4
5
|
export { closePositionTool } from "./closePosition.js";
|
|
5
6
|
export { setTpSlTool } from "./setTpSl.js";
|
|
6
7
|
export { updateOrderTpSlTool } from "./updateOrderTpSl.js";
|
|
7
8
|
export { adjustMarginTool } from "./adjustMargin.js";
|
|
8
9
|
export { closeAllPositionsTool } from "./closeAllPositions.js";
|
|
9
10
|
export { checkApprovalTool } from "./checkApproval.js";
|
|
11
|
+
export { getUserTradingFeeRateTool } from "./getUserTradingFeeRate.js";
|
|
12
|
+
export { getNetworkFeeTool } from "./getNetworkFee.js";
|
|
10
13
|
// Tools — 市场数据
|
|
11
14
|
export { getMarketPriceTool } from "./getMarketPrice.js";
|
|
12
15
|
export { getOraclePriceTool } from "./getOraclePrice.js";
|
|
13
16
|
export { searchMarketTool } from "./searchMarket.js";
|
|
14
17
|
export { getKlineTool } from "./getKline.js";
|
|
18
|
+
export { getKlineLatestBarTool } from "./getKlineLatestBar.js";
|
|
19
|
+
export { getAllTickersTool } from "./getAllTickers.js";
|
|
15
20
|
export { getMarketDetailTool, getPoolInfoTool, getLiquidityInfoTool } from "./marketInfo.js";
|
|
16
21
|
export { getPoolListTool } from "./getPoolList.js";
|
|
22
|
+
export { getPoolSymbolAllTool } from "./getPoolSymbolAll.js";
|
|
17
23
|
export { getPoolLevelConfigTool } from "./poolConfig.js";
|
|
24
|
+
export { getBaseDetailTool } from "./getBaseDetail.js";
|
|
18
25
|
// Tools — 池子 & 流动性
|
|
19
26
|
export { createPerpMarketTool } from "./createPerpMarket.js";
|
|
20
27
|
export { manageLiquidityTool, getLpPriceTool } from "./manageLiquidity.js";
|
|
@@ -24,5 +31,7 @@ export { getBalancesTool } from "./getBalances.js";
|
|
|
24
31
|
export { getOpenOrdersTool, getOrderHistoryTool } from "./orderQueries.js";
|
|
25
32
|
export { getPositionHistoryTool } from "./positionHistory.js";
|
|
26
33
|
export { getAccountInfoTool, getTradeFlowTool, getMarginBalanceTool } from "./accountInfo.js";
|
|
34
|
+
export { getAccountVipInfoTool } from "./getAccountVipInfo.js";
|
|
27
35
|
export { accountDepositTool, accountWithdrawTool } from "./accountTransfer.js";
|
|
28
36
|
export { getMarketListTool } from "./getMarketList.js";
|
|
37
|
+
export { getMarketListRawTool } from "./getMarketListRaw.js";
|
|
@@ -5,8 +5,12 @@ export const manageLiquidityTool = {
|
|
|
5
5
|
name: "manage_liquidity",
|
|
6
6
|
description: "Add or withdraw liquidity from a BASE or QUOTE pool. Amount is in human-readable units (e.g. 2000 for USDC, 0.01 for ETH).",
|
|
7
7
|
schema: {
|
|
8
|
-
action: z.string().
|
|
9
|
-
|
|
8
|
+
action: z.string().trim().toLowerCase().refine((value) => value === "deposit" || value === "withdraw", {
|
|
9
|
+
message: "action must be 'deposit' or 'withdraw'.",
|
|
10
|
+
}).describe("'deposit' or 'withdraw'"),
|
|
11
|
+
poolType: z.string().trim().toUpperCase().refine((value) => value === "BASE" || value === "QUOTE", {
|
|
12
|
+
message: "poolType must be 'BASE' or 'QUOTE'.",
|
|
13
|
+
}).describe("'BASE' or 'QUOTE'"),
|
|
10
14
|
poolId: z.string().describe("Pool ID"),
|
|
11
15
|
amount: z.union([z.number(), z.string()]).describe("Amount in human-readable units (e.g. 2000 for USDC, 0.01 for ETH)"),
|
|
12
16
|
slippage: z.union([z.number(), z.string()]).describe("Slippage tolerance (e.g. 0.01 = 1%)"),
|
|
@@ -16,10 +20,6 @@ export const manageLiquidityTool = {
|
|
|
16
20
|
const { action, poolType, poolId } = args;
|
|
17
21
|
const amount = parseSafeNumber(args.amount, "amount");
|
|
18
22
|
const slippage = parseSafeNumber(args.slippage, "slippage");
|
|
19
|
-
if (!["deposit", "withdraw"].includes(action))
|
|
20
|
-
throw new Error("action must be 'deposit' or 'withdraw'.");
|
|
21
|
-
if (!["BASE", "QUOTE"].includes(poolType))
|
|
22
|
-
throw new Error("poolType must be 'BASE' or 'QUOTE'.");
|
|
23
23
|
if (amount <= 0)
|
|
24
24
|
throw new Error("amount must be a positive number.");
|
|
25
25
|
if (slippage < 0)
|
|
@@ -46,7 +46,9 @@ export const getLpPriceTool = {
|
|
|
46
46
|
name: "get_lp_price",
|
|
47
47
|
description: "Get the current internal net asset value (NAV) price of an LP token for a BASE or QUOTE pool. Note: This is NOT the underlying token's external Oracle market price (e.g. WETH's price), but rather the internal exchange rate / net worth of the LP token itself which fluctuates based on pool PnL and fees.",
|
|
48
48
|
schema: {
|
|
49
|
-
poolType: z.string().
|
|
49
|
+
poolType: z.string().trim().toUpperCase().refine((value) => value === "BASE" || value === "QUOTE", {
|
|
50
|
+
message: "poolType must be 'BASE' or 'QUOTE'.",
|
|
51
|
+
}).describe("'BASE' or 'QUOTE'"),
|
|
50
52
|
poolId: z.string().describe("Pool ID"),
|
|
51
53
|
},
|
|
52
54
|
handler: async (args) => {
|
|
@@ -27,8 +27,8 @@ export const getOrderHistoryTool = {
|
|
|
27
27
|
description: "Get historical orders with optional pool filter and pagination.",
|
|
28
28
|
schema: {
|
|
29
29
|
poolId: z.string().optional().describe("Filter by pool ID"),
|
|
30
|
-
page: z.number().optional().describe("Page number (default 1)"),
|
|
31
|
-
limit: z.number().optional().describe("Results per page (default 20)"),
|
|
30
|
+
page: z.coerce.number().optional().describe("Page number (default 1)"),
|
|
31
|
+
limit: z.coerce.number().optional().describe("Results per page (default 20)"),
|
|
32
32
|
},
|
|
33
33
|
handler: async (args) => {
|
|
34
34
|
try {
|
|
@@ -6,8 +6,8 @@ export const getPositionHistoryTool = {
|
|
|
6
6
|
description: "Get historical closed positions with optional pool filter and pagination.",
|
|
7
7
|
schema: {
|
|
8
8
|
poolId: z.string().optional().describe("Filter by pool ID"),
|
|
9
|
-
page: z.number().optional().describe("Page number (default 1)"),
|
|
10
|
-
limit: z.number().optional().describe("Results per page (default 20)"),
|
|
9
|
+
page: z.coerce.number().optional().describe("Page number (default 1)"),
|
|
10
|
+
limit: z.coerce.number().optional().describe("Results per page (default 20)"),
|
|
11
11
|
},
|
|
12
12
|
handler: async (args) => {
|
|
13
13
|
try {
|
|
@@ -6,7 +6,7 @@ export const searchMarketTool = {
|
|
|
6
6
|
description: "Search for an active market by keyword.",
|
|
7
7
|
schema: {
|
|
8
8
|
keyword: z.string().describe('Search keyword, e.g. "BTC", "ETH"'),
|
|
9
|
-
limit: z.number().optional().describe("Max results (default 100)"),
|
|
9
|
+
limit: z.coerce.number().optional().describe("Max results (default 100)"),
|
|
10
10
|
},
|
|
11
11
|
handler: async (args) => {
|
|
12
12
|
try {
|
package/dist/tools/setTpSl.js
CHANGED
|
@@ -7,8 +7,10 @@ export const setTpSlTool = {
|
|
|
7
7
|
schema: {
|
|
8
8
|
poolId: z.string().describe("Pool ID"),
|
|
9
9
|
positionId: z.string().describe("Position ID"),
|
|
10
|
-
direction: z.
|
|
11
|
-
|
|
10
|
+
direction: z.coerce.number().int().refine((value) => value === 0 || value === 1, {
|
|
11
|
+
message: "direction must be 0 (LONG) or 1 (SHORT)",
|
|
12
|
+
}).describe("0 = LONG, 1 = SHORT (position direction)"),
|
|
13
|
+
leverage: z.coerce.number().describe("Position leverage"),
|
|
12
14
|
tpPrice: z.string().optional().describe("Take-profit trigger price (human-readable)"),
|
|
13
15
|
tpSize: z.string().optional().describe("TP size (18 decimals). Omit or '0' for full position."),
|
|
14
16
|
slPrice: z.string().optional().describe("Stop-loss trigger price (human-readable)"),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { resolveClient } from "../auth/resolveClient.js";
|
|
3
3
|
import { updateOrderTpSl } from "../services/tradeService.js";
|
|
4
|
+
import { booleanLike } from "../utils/schema.js";
|
|
4
5
|
export const updateOrderTpSlTool = {
|
|
5
6
|
name: "update_order_tp_sl",
|
|
6
7
|
description: "Update an existing take profit or stop loss order.",
|
|
@@ -11,7 +12,7 @@ export const updateOrderTpSlTool = {
|
|
|
11
12
|
tpSize: z.string().optional().describe("New TP size (18 decimals). Omit or '0' for full position."),
|
|
12
13
|
slPrice: z.string().optional().describe("New stop-loss trigger price (human-readable)"),
|
|
13
14
|
slSize: z.string().optional().describe("New SL size (18 decimals). Omit or '0' for full position."),
|
|
14
|
-
isTpSlOrder:
|
|
15
|
+
isTpSlOrder: booleanLike.optional().describe("Whether this is a TP/SL order (default true)"),
|
|
15
16
|
quoteToken: z.string().optional().describe("Quote token address"),
|
|
16
17
|
},
|
|
17
18
|
handler: async (args) => {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const trueValues = new Set(["true", "1", "yes", "y", "on"]);
|
|
3
|
+
const falseValues = new Set(["false", "0", "no", "n", "off"]);
|
|
4
|
+
/**
|
|
5
|
+
* Safe boolean coercion for MCP inputs:
|
|
6
|
+
* - boolean: true/false
|
|
7
|
+
* - number: 1/0
|
|
8
|
+
* - string: true/false/1/0/yes/no/on/off (case-insensitive)
|
|
9
|
+
*/
|
|
10
|
+
export const booleanLike = z.union([
|
|
11
|
+
z.boolean(),
|
|
12
|
+
z.number().refine((value) => value === 0 || value === 1, {
|
|
13
|
+
message: "Expected 0 or 1 for boolean value.",
|
|
14
|
+
}).transform((value) => value === 1),
|
|
15
|
+
z.string().trim().toLowerCase().refine((value) => trueValues.has(value) || falseValues.has(value), {
|
|
16
|
+
message: "Expected boolean-like value: true/false/1/0/yes/no/on/off.",
|
|
17
|
+
}).transform((value) => trueValues.has(value)),
|
|
18
|
+
]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@michaleffffff/mcp-trading-server",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"myx-mcp": "dist/server.js"
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"build": "rm -rf dist && tsc",
|
|
13
13
|
"start": "node dist/server.js",
|
|
14
14
|
"dev": "tsx src/server.ts",
|
|
15
|
-
"test": "tsx tests/test_sepolia.ts"
|
|
15
|
+
"test": "tsx tests/test_sepolia.ts",
|
|
16
|
+
"test:new-tools": "tsx tests/test_new_tools_p0_p1.ts"
|
|
16
17
|
},
|
|
17
18
|
"dependencies": {
|
|
18
19
|
"@modelcontextprotocol/sdk": "^1.27.1",
|