@michaleffffff/mcp-trading-server 3.0.31 → 3.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.1.0 - 2026-03-20
4
+ ### Changed
5
+ - 发布 `3.1.0`,统一版本元数据到新的主次版本号。
6
+ - 运行环境现在只区分测试环境与正式环境,不再走 beta host / beta 地址分支。
7
+ - 修复测试环境下的市场发现链路,`list_pools` 恢复返回当前链池列表,`get_all_tickers` 在主接口不可用时继续走 fallback。
8
+
3
9
  ## 3.0.31 - 2026-03-20
4
10
  ### Fixed
5
11
  - 修复 `normalizeSlippageRatio` 在处理 1% 到 100% 之间数值时的歧义问题。
package/README.md CHANGED
@@ -6,7 +6,7 @@ A production-ready MCP (Model Context Protocol) server for deep integration with
6
6
 
7
7
  # Release Notes
8
8
 
9
- - **Current release: 3.0.24**
9
+ - **Current release: 3.1.0**
10
10
  - **SDK baseline**: `@myx-trade/sdk@^1.0.4-beta.4` compatibility completed.
11
11
  - **Refinement**: Consolidated 40+ specialized tools into ~26 high-level unified tools.
12
12
  - **Improved UX**: Enhanced AI parameter parsing, automated unit conversion, and structured error reporting.
@@ -37,7 +37,6 @@ CHAIN_ID=...
37
37
  BROKER_ADDRESS=0x...
38
38
  QUOTE_TOKEN_ADDRESS=0x...
39
39
  QUOTE_TOKEN_DECIMALS=...
40
- IS_BETA_MODE=true
41
40
  ```
42
41
 
43
42
  ## Testnet `MYXBroker` Reference
@@ -46,7 +45,6 @@ IS_BETA_MODE=true
46
45
  - Linea test: `0x634EfDC9dC76D7AbF6E49279875a31B02E9891e2`
47
46
 
48
47
  Use the broker that matches your active RPC and chain configuration.
49
- If `IS_BETA_MODE` is omitted, MCP now auto-detects beta mode for the two testnet brokers above.
50
48
 
51
49
  ---
52
50
 
@@ -91,7 +89,6 @@ If `IS_BETA_MODE` is omitted, MCP now auto-detects beta mode for the two testnet
91
89
 
92
90
  - **Explicit execution price**: `open_position_simple` no longer auto-fills a fresh Oracle price for `MARKET` orders; provide `price` explicitly when opening a position.
93
91
  - **SDK-delegated funding delta**: MCP no longer performs its own increase-order margin/deposit reconciliation. New increase orders delegate collateral shortfall handling to SDK `createIncreaseOrder`.
94
- - **Beta broker compatibility**: current Arbitrum Sepolia / Linea Sepolia test brokers auto-enable beta mode when `IS_BETA_MODE` is unset.
95
92
  - **Exact approvals by default**: local fallback flows now prefer exact approvals instead of implicit unlimited approval.
96
93
  - **Size semantics**: `size` always means base-asset quantity, not USD notional.
97
94
  - **Pre-check semantics**: `check_account_ready` now uses SDK `getAvailableMarginBalance`. When that read fails, the response marks `summary.degraded=true` and includes diagnostics instead of silently trusting stale `freeMargin`.
@@ -2,10 +2,6 @@ import { MyxClient } from "@myx-trade/sdk";
2
2
  import { JsonRpcProvider, Wallet } from "ethers";
3
3
  import { normalizeAddress } from "../utils/address.js";
4
4
  let cached = null;
5
- const BETA_BROKERS_BY_CHAIN = {
6
- 421614: ["0x895c4ae2a22bb26851011d733a9355f663a1f939"],
7
- 59141: ["0x634efdc9dc76d7abf6e49279875a31b02e9891e2"],
8
- };
9
5
  function getDefaultBrokerByChainId(chainId) {
10
6
  // Testnet mappings
11
7
  if (chainId === 421614)
@@ -22,14 +18,6 @@ function getDefaultQuoteTokenByChainId(chainId) {
22
18
  return "0xD984fd34f91F92DA0586e1bE82E262fF27DC431b"; // Linea Sepolia
23
19
  return "0xD984fd34f91F92DA0586e1bE82E262fF27DC431b";
24
20
  }
25
- function resolveIsBetaMode(chainId, brokerAddressRaw) {
26
- const explicit = String(process.env.IS_BETA_MODE ?? "").trim().toLowerCase();
27
- if (explicit === "true")
28
- return true;
29
- if (explicit === "false")
30
- return false;
31
- return Boolean(BETA_BROKERS_BY_CHAIN[chainId]?.includes(String(brokerAddressRaw ?? "").trim().toLowerCase()));
32
- }
33
21
  export async function resolveClient() {
34
22
  if (cached)
35
23
  return cached;
@@ -50,7 +38,6 @@ export async function resolveClient() {
50
38
  throw new Error("QUOTE_TOKEN_ADDRESS env var is required.");
51
39
  const brokerAddress = normalizeAddress(brokerAddressRaw, "BROKER_ADDRESS");
52
40
  const quoteToken = normalizeAddress(quoteTokenRaw, "QUOTE_TOKEN_ADDRESS");
53
- const isBetaMode = resolveIsBetaMode(chainId, brokerAddress);
54
41
  const provider = new JsonRpcProvider(rpcUrl);
55
42
  const signer = new Wallet(privateKey, provider);
56
43
  // Inject the EIP-1193 mock so SDK can sign transactions seamlessly
@@ -75,7 +62,7 @@ export async function resolveClient() {
75
62
  signer: signer,
76
63
  brokerAddress,
77
64
  isTestnet,
78
- isBetaMode,
65
+ isBetaMode: false,
79
66
  walletClient: walletClient
80
67
  });
81
68
  cached = { client, address: signer.address, signer, chainId, quoteToken, quoteDecimals };
@@ -12,7 +12,7 @@ export const tradingGuidePrompt = {
12
12
  content: {
13
13
  type: "text",
14
14
  text: `
15
- # MYX Trading MCP Best Practices (v3.0.31)
15
+ # MYX Trading MCP Best Practices (v3.1.0)
16
16
 
17
17
  You are an expert crypto trader using the MYX Protocol. To ensure successful execution and safe handling of user funds, follow these patterns:
18
18
 
@@ -36,7 +36,6 @@ You are an expert crypto trader using the MYX Protocol. To ensure successful exe
36
36
  - **Execution Price**: \`open_position_simple\` no longer auto-fills a fresh Oracle price for \`MARKET\`; provide \`price\` explicitly when you want MCP to compute size / fee previews.
37
37
  - **Funding Delta Ownership**: MCP no longer performs its own increase-order margin/deposit reconciliation. SDK \`createIncreaseOrder\` owns deposit-delta handling for new increase orders.
38
38
  - **Pre-check Diagnostics**: \`check_account_ready\` now reports SDK \`availableMarginBalance\` first. If that read degrades, inspect \`summary.degraded\` and \`diagnostics.availableMarginError\` before trusting fallback account fields.
39
- - **Beta Broker Mode**: Current Arbitrum Sepolia / Linea Sepolia test brokers auto-enable beta mode when \`IS_BETA_MODE\` is omitted.
40
39
  - **Approval Safety**: Local fallback flows prefer exact approval sizing. Do not assume unlimited approvals are necessary.
41
40
  - **Position Semantics**: \`size\` is BASE quantity, not USD notional. If a \`positionId\` is supplied, \`direction\` must match the live position.
42
41
  - **TP/SL Semantics**: LONG should use \`tpPrice > entryPrice\` and \`slPrice < entryPrice\`; SHORT uses the inverse. Plain integer strings like \`"65000"\` are treated as human prices, not implicit raw 30-decimal values.
@@ -49,7 +48,6 @@ You are an expert crypto trader using the MYX Protocol. To ensure successful exe
49
48
  - Arbitrum test: \`0x895C4ae2A22bB26851011d733A9355f663a1F939\`
50
49
  - Linea test: \`0x634EfDC9dC76D7AbF6E49279875a31B02E9891e2\`
51
50
  - Always keep \`RPC_URL\`, \`CHAIN_ID\`, and \`BROKER_ADDRESS\` on the same network.
52
- - If \`IS_BETA_MODE\` is unset, MCP auto-detects beta mode for those two testnet brokers.
53
51
 
54
52
  Current Session:
55
53
  - Wallet: ${address}
package/dist/server.js CHANGED
@@ -461,7 +461,7 @@ function zodSchemaToJsonSchema(zodSchema) {
461
461
  };
462
462
  }
463
463
  // ─── MCP Server ───
464
- const server = new Server({ name: "myx-mcp-trading-server", version: "3.0.31" }, { capabilities: { tools: {}, resources: {}, prompts: {} } });
464
+ const server = new Server({ name: "myx-mcp-trading-server", version: "3.1.0" }, { capabilities: { tools: {}, resources: {}, prompts: {} } });
465
465
  // List tools
466
466
  server.setRequestHandler(ListToolsRequestSchema, async () => {
467
467
  return {
@@ -582,7 +582,7 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
582
582
  async function main() {
583
583
  const transport = new StdioServerTransport();
584
584
  await server.connect(transport);
585
- logger.info("🚀 MYX Trading MCP Server v3.0.24 running (stdio, pure on-chain, prod ready)");
585
+ logger.info("🚀 MYX Trading MCP Server v3.1.0 running (stdio, pure on-chain, prod ready)");
586
586
  }
587
587
  main().catch((err) => {
588
588
  logger.error("Fatal Server Startup Error", err);
@@ -46,6 +46,12 @@ function normalizePoolId(row) {
46
46
  const poolId = row?.poolId ?? row?.pool_id ?? "";
47
47
  return String(poolId);
48
48
  }
49
+ function matchesChainId(row, chainId) {
50
+ const raw = row?.chainId ?? row?.chain_id;
51
+ if (raw === undefined || raw === null || raw === "")
52
+ return true;
53
+ return Number(raw) === chainId;
54
+ }
49
55
  function matchesKeyword(row, keywordUpper) {
50
56
  const poolId = normalizePoolId(row);
51
57
  const haystack = [
@@ -67,7 +73,7 @@ async function fetchApiMarketRows(client, chainId) {
67
73
  // Preferred path: documented markets.searchMarket
68
74
  try {
69
75
  const searchRes = await client.markets.searchMarket({ chainId, keyword: "", limit: 2000 });
70
- const searchRows = extractMarketRows(searchRes, chainId).filter((row) => normalizePoolId(row));
76
+ const searchRows = extractMarketRows(searchRes, chainId).filter((row) => normalizePoolId(row) && matchesChainId(row, chainId));
71
77
  if (searchRows.length > 0)
72
78
  return searchRows;
73
79
  }
@@ -76,7 +82,7 @@ async function fetchApiMarketRows(client, chainId) {
76
82
  // Secondary path: documented markets.getPoolSymbolAll
77
83
  try {
78
84
  const symbolsRes = await client.markets.getPoolSymbolAll();
79
- const symbolRows = collectRows(symbolsRes?.data ?? symbolsRes).filter((row) => normalizePoolId(row));
85
+ const symbolRows = collectRows(symbolsRes?.data ?? symbolsRes).filter((row) => normalizePoolId(row) && matchesChainId(row, chainId));
80
86
  if (symbolRows.length > 0)
81
87
  return symbolRows;
82
88
  }
@@ -84,12 +90,12 @@ async function fetchApiMarketRows(client, chainId) {
84
90
  }
85
91
  // Legacy fallback: internal api namespace (for backward compatibility)
86
92
  const marketListRes = await client.api?.getMarketList?.().catch(() => null);
87
- const marketRows = extractMarketRows(marketListRes, chainId);
93
+ const marketRows = extractMarketRows(marketListRes, chainId).filter((row) => matchesChainId(row, chainId));
88
94
  const marketRowsWithPoolId = marketRows.filter((row) => normalizePoolId(row));
89
95
  if (marketRowsWithPoolId.length > 0)
90
96
  return marketRowsWithPoolId;
91
97
  const poolListRes = await client.api?.getPoolList?.().catch(() => null);
92
- return collectRows(poolListRes?.data ?? poolListRes).filter((row) => normalizePoolId(row));
98
+ return collectRows(poolListRes?.data ?? poolListRes).filter((row) => normalizePoolId(row) && matchesChainId(row, chainId));
93
99
  }
94
100
  export async function getMarketPrice(client, poolId, chainIdOverride) {
95
101
  const chainId = chainIdOverride ?? getChainId();
@@ -230,7 +236,7 @@ export async function getPoolList(client, chainIdOverride) {
230
236
  const chainId = chainIdOverride ?? getChainId();
231
237
  try {
232
238
  const searchRes = await client.markets.searchMarket({ chainId, keyword: "", limit: 2000 });
233
- const rows = extractMarketRows(searchRes, chainId).filter((row) => normalizePoolId(row));
239
+ const rows = extractMarketRows(searchRes, chainId).filter((row) => normalizePoolId(row) && matchesChainId(row, chainId));
234
240
  if (rows.length > 0)
235
241
  return rows;
236
242
  }
@@ -238,13 +244,14 @@ export async function getPoolList(client, chainIdOverride) {
238
244
  }
239
245
  try {
240
246
  const symbolsRes = await client.markets.getPoolSymbolAll();
241
- const rows = collectRows(symbolsRes?.data ?? symbolsRes).filter((row) => normalizePoolId(row));
247
+ const rows = collectRows(symbolsRes?.data ?? symbolsRes).filter((row) => normalizePoolId(row) && matchesChainId(row, chainId));
242
248
  if (rows.length > 0)
243
249
  return rows;
244
250
  }
245
251
  catch {
246
252
  }
247
- return client.api?.getPoolList?.();
253
+ const poolListRes = await client.api?.getPoolList?.();
254
+ return collectRows(poolListRes?.data ?? poolListRes).filter((row) => normalizePoolId(row) && matchesChainId(row, chainId));
248
255
  }
249
256
  /**
250
257
  * 获取池子分级配置
@@ -13,12 +13,6 @@ const POOL_MANAGER_BY_CHAIN = {
13
13
  97: "0x4F917ef137b573D9790b87e3cF6dfb698cF00c9c",
14
14
  56: "0x13F2130c2F3bfd612BBCBF35FB9E467dd32bAF3A",
15
15
  };
16
- const POOL_MANAGER_BY_CHAIN_BETA = {
17
- 421614: "0x05314a21Fc97B74f168730153b2B63A870D25dE5",
18
- 59141: "0xcf51a6895864c6D8E507fC31EF16b9011287c5f4",
19
- 97: "0x9E84a999e15CCdb2F64a5AF10939c25769dF6b07",
20
- 56: "0x9E84a999e15CCdb2F64a5AF10939c25769dF6b07",
21
- };
22
16
  const POOL_MANAGER_ABI = [
23
17
  "function deployPool((bytes32 marketId,address baseToken))",
24
18
  "function getMarketPool(bytes32 marketId,address asset) view returns ((bytes32 marketId,bytes32 poolId,address baseToken,address quoteToken,uint8 riskTier,uint8 state,address basePoolToken,address quotePoolToken,uint16 maxPriceDeviation,bool compoundEnabled,uint64 windowCapUsd,address poolVault,address tradingVault))",
@@ -45,28 +39,6 @@ const LP_ROUTER_BY_CHAIN = {
45
39
  quotePool: "0xB1E6df749A602892FafB27bb39Fd4F044527121E",
46
40
  },
47
41
  };
48
- const LP_ROUTER_BY_CHAIN_BETA = {
49
- 421614: {
50
- router: "0xfb790ECE13Cd9e296b4a06ABF38D10431360c236",
51
- basePool: "0x1F767BEa83EDe5E0C18904f439C579f52c2c0F1b",
52
- quotePool: "0x50ad7da312c58c6689bEF028954a366cb5b8ee13",
53
- },
54
- 59141: {
55
- router: "0x0AE31989318565620c03d35889a0B2536b8fba5C",
56
- basePool: "0xc9e3826c42183207B418116A16827C1605132c51",
57
- quotePool: "0x9A9934D3b22103dE822d94A22A3177d728FE0e5a",
58
- },
59
- 97: {
60
- router: "0x0F4C6f18Fb136DD1eBd6Da3C5d86a86597CF79a3",
61
- basePool: "0x51B62554a76197d5DF2D5dC4D57FF54d40775938",
62
- quotePool: "0x783Ed065a12e1C1D33c2a8d6408385C1843D3084",
63
- },
64
- 56: {
65
- router: "0x0F4C6f18Fb136DD1eBd6Da3C5d86a86597CF79a3",
66
- basePool: "0x51B62554a76197d5DF2D5dC4D57FF54d40775938",
67
- quotePool: "0x783Ed065a12e1C1D33c2a8d6408385C1843D3084",
68
- },
69
- };
70
42
  const PREVIEW_POOL_ABI = [
71
43
  "function previewLpAmountOut(bytes32,uint256,uint256) view returns (uint256)",
72
44
  "function previewQuoteAmountOut(bytes32,uint256,uint256) view returns (uint256)",
@@ -165,24 +137,19 @@ async function withMutedSdkAbiMismatchLogs(runner) {
165
137
  console.error = original;
166
138
  }
167
139
  }
168
- function isBetaModeEnabled() {
169
- return String(process.env.IS_BETA_MODE ?? "").trim().toLowerCase() === "true";
170
- }
171
140
  function getPoolManagerAddress(chainId) {
172
141
  const envAddress = String(process.env.POOL_MANAGER_ADDRESS ?? "").trim();
173
142
  if (envAddress) {
174
143
  return normalizeAddress(envAddress, "POOL_MANAGER_ADDRESS");
175
144
  }
176
- const source = isBetaModeEnabled() ? POOL_MANAGER_BY_CHAIN_BETA : POOL_MANAGER_BY_CHAIN;
177
- const mapped = source[chainId];
145
+ const mapped = POOL_MANAGER_BY_CHAIN[chainId];
178
146
  if (!mapped) {
179
- throw new Error(`Pool manager address is not configured for chainId=${chainId} (beta=${isBetaModeEnabled()}). Set POOL_MANAGER_ADDRESS env var.`);
147
+ throw new Error(`Pool manager address is not configured for chainId=${chainId}. Set POOL_MANAGER_ADDRESS env var.`);
180
148
  }
181
149
  return mapped;
182
150
  }
183
151
  function getLpAddresses(chainId) {
184
- const source = isBetaModeEnabled() ? LP_ROUTER_BY_CHAIN_BETA : LP_ROUTER_BY_CHAIN;
185
- const matched = source[chainId];
152
+ const matched = LP_ROUTER_BY_CHAIN[chainId];
186
153
  if (!matched) {
187
154
  throw new Error(`Liquidity router config not found for chainId=${chainId}.`);
188
155
  }
@@ -15,7 +15,7 @@ export const getAllTickersTool = {
15
15
  // Fallback for networks/environments where getAllTickers endpoint is unavailable.
16
16
  const chainId = getChainId();
17
17
  const poolList = await getPoolList(client, chainId);
18
- const pools = Array.isArray(poolList?.data) ? poolList.data : (Array.isArray(poolList) ? poolList : []);
18
+ const pools = Array.isArray(poolList) ? poolList : (Array.isArray(poolList?.data) ? poolList.data : []);
19
19
  const poolIds = pools.map((p) => p?.poolId ?? p?.pool_id).filter((id) => !!id);
20
20
  if (poolIds.length === 0) {
21
21
  throw new Error("Failed to fetch all tickers and no pools were available for fallback query.");
@@ -21,7 +21,8 @@ export const listPoolsTool = {
21
21
  getPoolList(client),
22
22
  client.markets.getPoolSymbolAll().catch(() => ({ data: [] }))
23
23
  ]);
24
- const poolsRaw = collectRows(poolListRes?.data ?? poolListRes);
24
+ const poolsSource = Array.isArray(poolListRes) ? poolListRes : poolListRes?.data ?? poolListRes;
25
+ const poolsRaw = collectRows(poolsSource);
25
26
  const symbolsRaw = collectRows(symbolsRes?.data ?? symbolsRes);
26
27
  const symbolMap = new Map(symbolsRaw
27
28
  .filter((row) => row?.poolId || row?.pool_id)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michaleffffff/mcp-trading-server",
3
- "version": "3.0.31",
3
+ "version": "3.1.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "myx-mcp": "dist/server.js"