@michaleffffff/mcp-trading-server 3.0.10 → 3.0.16
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/CHANGELOG.md +57 -0
- package/README.md +93 -1
- package/TOOL_EXAMPLES.md +293 -54
- package/dist/prompts/tradingGuide.js +4 -2
- package/dist/server.js +96 -5
- package/dist/services/tradeService.js +62 -2
- package/dist/tools/closePosition.js +1 -1
- package/dist/tools/executeTrade.js +12 -1
- package/dist/tools/getBaseDetail.js +86 -0
- package/dist/tools/openPositionSimple.js +41 -13
- package/dist/tools/searchTools.js +237 -12
- package/dist/utils/address.js +12 -0
- package/dist/utils/mappings.js +11 -0
- package/package.json +1 -1
|
@@ -1,30 +1,255 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import * as allTools from "./index.js";
|
|
3
|
+
const TOOL_DISCOVERY_META = {
|
|
4
|
+
find_pool: {
|
|
5
|
+
category: "market-discovery",
|
|
6
|
+
aliases: ["search_market", "get_pool_by_symbol", "find market", "find symbol"],
|
|
7
|
+
intents: ["discover market", "lookup pool", "resolve pool id"],
|
|
8
|
+
commonArgs: ["keyword"],
|
|
9
|
+
},
|
|
10
|
+
list_pools: {
|
|
11
|
+
category: "market-discovery",
|
|
12
|
+
aliases: ["get_pool_list", "get_pool_symbol_all", "all pools"],
|
|
13
|
+
intents: ["browse markets", "list tradable pools"],
|
|
14
|
+
},
|
|
15
|
+
get_price: {
|
|
16
|
+
category: "market-data",
|
|
17
|
+
aliases: ["get_market_price", "get_oracle_price", "price"],
|
|
18
|
+
intents: ["read market price", "read oracle price"],
|
|
19
|
+
commonArgs: ["poolId", "priceType"],
|
|
20
|
+
},
|
|
21
|
+
get_pool_metadata: {
|
|
22
|
+
category: "market-data",
|
|
23
|
+
aliases: ["get_market_detail", "get_pool_info", "get_liquidity_info", "get_pool_level_config"],
|
|
24
|
+
intents: ["inspect pool", "read liquidity", "read config"],
|
|
25
|
+
commonArgs: ["poolId", "includeLiquidity", "includeConfig"],
|
|
26
|
+
},
|
|
27
|
+
get_kline: {
|
|
28
|
+
category: "market-data",
|
|
29
|
+
aliases: ["candles", "ohlcv", "chart"],
|
|
30
|
+
intents: ["read kline", "read candlestick"],
|
|
31
|
+
commonArgs: ["poolId", "interval", "limit"],
|
|
32
|
+
},
|
|
33
|
+
open_position_simple: {
|
|
34
|
+
category: "trading",
|
|
35
|
+
aliases: ["open trade", "open position", "quick trade"],
|
|
36
|
+
intents: ["open long", "open short", "trade with tp sl"],
|
|
37
|
+
commonArgs: ["poolId|keyword", "direction", "collateralAmount", "leverage", "orderType"],
|
|
38
|
+
},
|
|
39
|
+
execute_trade: {
|
|
40
|
+
category: "trading",
|
|
41
|
+
aliases: ["create increase order", "advanced trade"],
|
|
42
|
+
intents: ["low-level open order", "custom increase order"],
|
|
43
|
+
commonArgs: ["poolId", "marketId", "direction", "orderType", "size", "price"],
|
|
44
|
+
},
|
|
45
|
+
close_position: {
|
|
46
|
+
category: "trading",
|
|
47
|
+
aliases: ["reduce position", "close trade"],
|
|
48
|
+
intents: ["close long", "close short"],
|
|
49
|
+
commonArgs: ["poolId", "positionId", "direction", "orderType", "size"],
|
|
50
|
+
},
|
|
51
|
+
close_all_positions: {
|
|
52
|
+
category: "trading",
|
|
53
|
+
aliases: ["panic close", "emergency close"],
|
|
54
|
+
intents: ["close everything", "flatten exposure"],
|
|
55
|
+
commonArgs: ["poolId|keyword"],
|
|
56
|
+
},
|
|
57
|
+
manage_tp_sl: {
|
|
58
|
+
category: "trading",
|
|
59
|
+
aliases: ["set tp sl", "update tp sl", "delete tp sl"],
|
|
60
|
+
intents: ["set stop loss", "set take profit", "cancel protection"],
|
|
61
|
+
commonArgs: ["poolId", "positionId|orderId", "tpPrice", "slPrice"],
|
|
62
|
+
},
|
|
63
|
+
cancel_orders: {
|
|
64
|
+
category: "trading",
|
|
65
|
+
aliases: ["cancel_all_orders", "cancel order", "revoke order"],
|
|
66
|
+
intents: ["cancel pending orders", "clear orders"],
|
|
67
|
+
commonArgs: ["orderIds|poolId|cancelAll"],
|
|
68
|
+
},
|
|
69
|
+
manage_liquidity: {
|
|
70
|
+
category: "liquidity",
|
|
71
|
+
aliases: ["add lp", "remove lp", "deposit liquidity", "withdraw liquidity"],
|
|
72
|
+
intents: ["add base lp", "add quote lp", "withdraw lp"],
|
|
73
|
+
commonArgs: ["poolId", "poolType", "action", "amount", "slippage"],
|
|
74
|
+
},
|
|
75
|
+
get_lp_price: {
|
|
76
|
+
category: "liquidity",
|
|
77
|
+
aliases: ["lp nav", "pool token price"],
|
|
78
|
+
intents: ["read lp nav", "read lp token price"],
|
|
79
|
+
commonArgs: ["poolId", "poolType"],
|
|
80
|
+
},
|
|
81
|
+
get_my_lp_holdings: {
|
|
82
|
+
category: "liquidity",
|
|
83
|
+
aliases: ["my lp", "lp balances"],
|
|
84
|
+
intents: ["list my liquidity", "portfolio lp"],
|
|
85
|
+
},
|
|
86
|
+
get_account_snapshot: {
|
|
87
|
+
category: "account",
|
|
88
|
+
aliases: ["get_account", "get_account_info", "balances overview"],
|
|
89
|
+
intents: ["check balances", "account overview"],
|
|
90
|
+
commonArgs: ["poolId"],
|
|
91
|
+
},
|
|
92
|
+
get_orders: {
|
|
93
|
+
category: "account",
|
|
94
|
+
aliases: ["get_open_orders", "order history"],
|
|
95
|
+
intents: ["list orders", "pending orders", "filled orders"],
|
|
96
|
+
commonArgs: ["status", "poolId", "limit"],
|
|
97
|
+
},
|
|
98
|
+
get_positions_all: {
|
|
99
|
+
category: "account",
|
|
100
|
+
aliases: ["get_positions", "position history"],
|
|
101
|
+
intents: ["list positions", "open positions", "closed positions"],
|
|
102
|
+
commonArgs: ["status", "poolId", "limit"],
|
|
103
|
+
},
|
|
104
|
+
get_trade_flow: {
|
|
105
|
+
category: "account",
|
|
106
|
+
aliases: ["account flow", "tx history"],
|
|
107
|
+
intents: ["read trade history", "activity feed"],
|
|
108
|
+
},
|
|
109
|
+
account_deposit: {
|
|
110
|
+
category: "account",
|
|
111
|
+
aliases: ["deposit margin", "transfer to trading"],
|
|
112
|
+
intents: ["fund trading account"],
|
|
113
|
+
commonArgs: ["amount", "tokenAddress"],
|
|
114
|
+
},
|
|
115
|
+
account_withdraw: {
|
|
116
|
+
category: "account",
|
|
117
|
+
aliases: ["withdraw margin", "transfer to wallet"],
|
|
118
|
+
intents: ["withdraw trading funds"],
|
|
119
|
+
commonArgs: ["poolId", "amount", "isQuoteToken"],
|
|
120
|
+
},
|
|
121
|
+
check_account_ready: {
|
|
122
|
+
category: "account",
|
|
123
|
+
aliases: ["precheck balance", "ready to trade"],
|
|
124
|
+
intents: ["validate collateral", "pre-trade check"],
|
|
125
|
+
commonArgs: ["poolId|keyword", "collateralAmount"],
|
|
126
|
+
},
|
|
127
|
+
search_tools: {
|
|
128
|
+
category: "utils",
|
|
129
|
+
aliases: ["find tool", "discover tool"],
|
|
130
|
+
intents: ["which tool should i use", "tool discovery"],
|
|
131
|
+
commonArgs: ["keyword"],
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
function normalizeText(value) {
|
|
135
|
+
return String(value ?? "").trim().toLowerCase();
|
|
136
|
+
}
|
|
137
|
+
function uniqueStrings(values) {
|
|
138
|
+
return [...new Set(values.filter(Boolean))];
|
|
139
|
+
}
|
|
140
|
+
function scoreTextMatch(query, candidate, weight) {
|
|
141
|
+
if (!candidate)
|
|
142
|
+
return 0;
|
|
143
|
+
if (candidate === query)
|
|
144
|
+
return weight + 6;
|
|
145
|
+
if (candidate.startsWith(query))
|
|
146
|
+
return weight + 3;
|
|
147
|
+
if (candidate.includes(query))
|
|
148
|
+
return weight;
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
function isOptionalField(field) {
|
|
152
|
+
return Boolean(field?.isOptional?.()) || field?._def?.type === "optional" || field?._def?.type === "default";
|
|
153
|
+
}
|
|
154
|
+
function getFieldDescription(field) {
|
|
155
|
+
return String(field?._def?.description ?? "").trim();
|
|
156
|
+
}
|
|
157
|
+
function summarizeSchema(schema) {
|
|
158
|
+
if (!schema)
|
|
159
|
+
return { required: [], optional: [] };
|
|
160
|
+
const entries = Object.entries(schema);
|
|
161
|
+
const required = entries.filter(([, field]) => !isOptionalField(field)).map(([name]) => name);
|
|
162
|
+
const optional = entries.filter(([, field]) => isOptionalField(field)).map(([name]) => name);
|
|
163
|
+
return { required, optional };
|
|
164
|
+
}
|
|
3
165
|
export const searchToolsTool = {
|
|
4
166
|
name: "search_tools",
|
|
5
|
-
description: "[UTILS] Search for available tools by keyword
|
|
167
|
+
description: "[UTILS] Search for available tools by keyword, legacy tool name, or intent. Returns categories, aliases, and common parameters.",
|
|
6
168
|
schema: {
|
|
7
|
-
keyword: z.string().describe("Keyword
|
|
169
|
+
keyword: z.string().describe("Keyword, old tool name, or intent phrase to search for."),
|
|
8
170
|
},
|
|
9
171
|
handler: async (args) => {
|
|
10
172
|
try {
|
|
11
|
-
const keyword = args.keyword
|
|
173
|
+
const keyword = normalizeText(args.keyword);
|
|
12
174
|
const tools = Object.values(allTools);
|
|
13
175
|
const matches = tools
|
|
14
|
-
.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
176
|
+
.map((tool) => {
|
|
177
|
+
const meta = TOOL_DISCOVERY_META[tool.name] ?? { category: "general" };
|
|
178
|
+
const schemaSummary = summarizeSchema(tool.schema);
|
|
179
|
+
const aliases = uniqueStrings(meta.aliases ?? []);
|
|
180
|
+
const intents = uniqueStrings(meta.intents ?? []);
|
|
181
|
+
const score = scoreTextMatch(keyword, normalizeText(tool.name), 18) +
|
|
182
|
+
scoreTextMatch(keyword, normalizeText(tool.description), 10) +
|
|
183
|
+
aliases.reduce((sum, alias) => sum + scoreTextMatch(keyword, normalizeText(alias), 12), 0) +
|
|
184
|
+
intents.reduce((sum, intent) => sum + scoreTextMatch(keyword, normalizeText(intent), 9), 0) +
|
|
185
|
+
scoreTextMatch(keyword, normalizeText(meta.category), 4);
|
|
186
|
+
return {
|
|
187
|
+
tool,
|
|
188
|
+
meta,
|
|
189
|
+
schemaSummary,
|
|
190
|
+
aliases,
|
|
191
|
+
intents,
|
|
192
|
+
score,
|
|
193
|
+
};
|
|
194
|
+
})
|
|
195
|
+
.filter((entry) => entry.score > 0)
|
|
196
|
+
.sort((a, b) => b.score - a.score || a.tool.name.localeCompare(b.tool.name))
|
|
197
|
+
.map((entry) => {
|
|
198
|
+
const commonArgs = uniqueStrings([
|
|
199
|
+
...(entry.meta.commonArgs ?? []),
|
|
200
|
+
...entry.schemaSummary.required.slice(0, 4),
|
|
201
|
+
]);
|
|
202
|
+
const commonArgsWithHints = commonArgs.map((name) => {
|
|
203
|
+
const baseName = name.split("|")[0];
|
|
204
|
+
const field = entry.tool.schema?.[baseName];
|
|
205
|
+
const description = getFieldDescription(field);
|
|
206
|
+
return description ? `${name}: ${description}` : name;
|
|
207
|
+
});
|
|
208
|
+
return {
|
|
209
|
+
name: entry.tool.name,
|
|
210
|
+
category: entry.meta.category,
|
|
211
|
+
description: entry.tool.description,
|
|
212
|
+
aliases: entry.aliases,
|
|
213
|
+
intents: entry.intents,
|
|
214
|
+
requiredArgs: entry.schemaSummary.required,
|
|
215
|
+
commonArgs: commonArgsWithHints,
|
|
216
|
+
};
|
|
217
|
+
});
|
|
21
218
|
if (matches.length === 0) {
|
|
22
|
-
|
|
219
|
+
const suggestions = tools
|
|
220
|
+
.map((tool) => ({
|
|
221
|
+
name: tool.name,
|
|
222
|
+
description: tool.description,
|
|
223
|
+
category: TOOL_DISCOVERY_META[tool.name]?.category ?? "general",
|
|
224
|
+
}))
|
|
225
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
226
|
+
.slice(0, 8);
|
|
227
|
+
return {
|
|
228
|
+
content: [{
|
|
229
|
+
type: "text",
|
|
230
|
+
text: JSON.stringify({
|
|
231
|
+
status: "success",
|
|
232
|
+
data: {
|
|
233
|
+
count: 0,
|
|
234
|
+
keyword: args.keyword,
|
|
235
|
+
message: `No tools found matching: ${args.keyword}`,
|
|
236
|
+
suggestions,
|
|
237
|
+
},
|
|
238
|
+
}, null, 2),
|
|
239
|
+
}],
|
|
240
|
+
};
|
|
23
241
|
}
|
|
24
242
|
return {
|
|
25
243
|
content: [{
|
|
26
244
|
type: "text",
|
|
27
|
-
text: JSON.stringify({
|
|
245
|
+
text: JSON.stringify({
|
|
246
|
+
status: "success",
|
|
247
|
+
data: {
|
|
248
|
+
count: matches.length,
|
|
249
|
+
keyword: args.keyword,
|
|
250
|
+
tools: matches,
|
|
251
|
+
},
|
|
252
|
+
}, null, 2)
|
|
28
253
|
}]
|
|
29
254
|
};
|
|
30
255
|
}
|
package/dist/utils/address.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getAddress } from "ethers";
|
|
2
|
+
export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
2
3
|
export function normalizeAddress(value, label = "address") {
|
|
3
4
|
const raw = String(value || "").trim();
|
|
4
5
|
if (!raw)
|
|
@@ -15,3 +16,14 @@ export function normalizeAddress(value, label = "address") {
|
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
}
|
|
19
|
+
export function isZeroAddress(value) {
|
|
20
|
+
const raw = String(value ?? "").trim();
|
|
21
|
+
if (!raw)
|
|
22
|
+
return false;
|
|
23
|
+
try {
|
|
24
|
+
return getAddress(raw).toLowerCase() === ZERO_ADDRESS;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return raw.toLowerCase() === ZERO_ADDRESS;
|
|
28
|
+
}
|
|
29
|
+
}
|
package/dist/utils/mappings.js
CHANGED
|
@@ -170,3 +170,14 @@ export const mapTriggerType = (input) => {
|
|
|
170
170
|
return 2;
|
|
171
171
|
throw new Error(`Invalid triggerType: ${input}. Use 0/NONE, 1/GTE or 2/LTE.`);
|
|
172
172
|
};
|
|
173
|
+
/**
|
|
174
|
+
* SDK v1.0.2 currently exposes IOC only.
|
|
175
|
+
*/
|
|
176
|
+
export const mapTimeInForce = (input) => {
|
|
177
|
+
if (input === 0)
|
|
178
|
+
return 0;
|
|
179
|
+
const s = String(input ?? "").trim().toUpperCase();
|
|
180
|
+
if (s === "0" || s === "IOC")
|
|
181
|
+
return 0;
|
|
182
|
+
throw new Error(`Invalid timeInForce: ${input}. SDK v1.0.2 currently supports IOC only, use 0/IOC.`);
|
|
183
|
+
};
|