@michaleffffff/mcp-trading-server 3.0.16 → 3.0.20
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 +31 -0
- package/README.md +2 -1
- package/TOOL_EXAMPLES.md +5 -1
- package/dist/server.js +2 -2
- package/dist/tools/getPoolMetadata.js +181 -0
- package/dist/tools/manageLiquidity.js +29 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.0.20 - 2026-03-19
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Hardened `get_pool_metadata` funding-rate formatting:
|
|
7
|
+
- `fundingInfo.nextFundingRate` now preserves readable `%/秒` and `%/天` output for negative rates too.
|
|
8
|
+
- Regression coverage now asserts both numeric funding-rate views and display strings.
|
|
9
|
+
|
|
10
|
+
## 3.0.19 - 2026-03-19
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Refined `fundingInfo.nextFundingRate` formatting in `get_pool_metadata`:
|
|
14
|
+
- Displays percent per second as `%/秒`
|
|
15
|
+
- Displays derived percent per day as `%/天`
|
|
16
|
+
- Keeps raw integer and comma-separated views for audit/debug use
|
|
17
|
+
|
|
18
|
+
## 3.0.18 - 2026-03-19
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- Further improved `fundingInfo` / `ioTracker` readability in `get_pool_metadata`:
|
|
22
|
+
- `fundingInfo.nextEpochTime` now includes UTC timestamp and seconds-until-next-epoch.
|
|
23
|
+
- `fundingInfo.nextFundingRate` and `lastFundingFeeTracker` now include comma-separated raw views.
|
|
24
|
+
- `ioTracker` now includes derived notional-at-entry views based on `poolEntryPrice`.
|
|
25
|
+
|
|
26
|
+
## 3.0.17 - 2026-03-19
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
- Improved precision handling for liquidity and pool read tools:
|
|
30
|
+
- `get_pool_metadata` now adds `poolInfoFormatted` for exchange rate, LP token price, LP supply, debt, collateral, reserves, and open interest.
|
|
31
|
+
- `get_lp_price` now returns both raw and human-readable formatted values.
|
|
32
|
+
- Added regression coverage to ensure formatted precision fields are present.
|
|
33
|
+
|
|
3
34
|
## 3.0.16 - 2026-03-18
|
|
4
35
|
|
|
5
36
|
### Fixed
|
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.
|
|
9
|
+
- **Current release: 3.0.20**
|
|
10
10
|
- **SDK baseline**: `@myx-trade/sdk@^1.0.2` 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.
|
|
@@ -20,6 +20,7 @@ A production-ready MCP (Model Context Protocol) server for deep integration with
|
|
|
20
20
|
* **AI-First Design**: Automated Pool ID resolution and flexible unit handling (`human:` vs `raw:`).
|
|
21
21
|
* **Deep Liquidity Support**: Tools for both traders and liquidity providers.
|
|
22
22
|
* **Production Ready**: Robust error handling with actionable hints for LLMs.
|
|
23
|
+
* **Precision-Aware Reads**: Pool and LP read tools expose human-readable formatted values alongside raw on-chain integers.
|
|
23
24
|
* **Compliant**: Full Model Context Protocol (MCP) support.
|
|
24
25
|
|
|
25
26
|
---
|
package/TOOL_EXAMPLES.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# MYX MCP Tool Examples Handbook (v3.0.
|
|
1
|
+
# MYX MCP Tool Examples Handbook (v3.0.20)
|
|
2
2
|
|
|
3
3
|
This guide provides practical MCP payload examples for the current unified toolset.
|
|
4
4
|
All examples use the MCP format:
|
|
@@ -233,6 +233,8 @@ Unified pool detail, config, and liquidity info.
|
|
|
233
233
|
}
|
|
234
234
|
```
|
|
235
235
|
|
|
236
|
+
`get_pool_metadata` returns raw values in `poolInfo` and precision-safe human-readable values in `poolInfoFormatted`, including readable funding epoch timestamps, funding-rate `%/秒` and `%/天`, and IO notional-at-entry views.
|
|
237
|
+
|
|
236
238
|
### `get_kline`
|
|
237
239
|
Read chart data. Use `limit: 1` for the latest bar.
|
|
238
240
|
|
|
@@ -295,6 +297,8 @@ Read LP NAV price for BASE or QUOTE side.
|
|
|
295
297
|
}
|
|
296
298
|
```
|
|
297
299
|
|
|
300
|
+
`get_lp_price` returns both `raw` and `formatted` NAV price values.
|
|
301
|
+
|
|
298
302
|
### `get_my_lp_holdings`
|
|
299
303
|
Read current LP balances across pools.
|
|
300
304
|
|
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.
|
|
464
|
+
const server = new Server({ name: "myx-mcp-trading-server", version: "3.0.20" }, { 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.
|
|
585
|
+
logger.info("🚀 MYX Trading MCP Server v3.0.20 running (stdio, pure on-chain, prod ready)");
|
|
586
586
|
}
|
|
587
587
|
main().catch((err) => {
|
|
588
588
|
logger.error("Fatal Server Startup Error", err);
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { formatUnits } from "ethers";
|
|
2
3
|
import { resolveClient, getChainId } from "../auth/resolveClient.js";
|
|
3
4
|
import { getMarketDetail, resolvePool } from "../services/marketService.js";
|
|
4
5
|
import { getPoolInfo, getLiquidityInfo } from "../services/poolService.js";
|
|
5
6
|
import { extractErrorMessage } from "../utils/errorMessage.js";
|
|
6
7
|
import { parseUserPrice30 } from "../utils/units.js";
|
|
7
8
|
const INTEGER_RE = /^\d+$/;
|
|
9
|
+
const SIGNED_INTEGER_RE = /^-?\d+$/;
|
|
8
10
|
const DECIMAL_RE = /^\d+(\.\d+)?$/;
|
|
9
11
|
function normalizeMarketPrice30Input(value) {
|
|
10
12
|
const text = String(value ?? "").trim();
|
|
@@ -29,6 +31,180 @@ function compactWarning(scope, err) {
|
|
|
29
31
|
}
|
|
30
32
|
return `${scope}: ${flat}`;
|
|
31
33
|
}
|
|
34
|
+
function formatRawValue(value, decimals) {
|
|
35
|
+
const text = String(value ?? "").trim();
|
|
36
|
+
if (!INTEGER_RE.test(text))
|
|
37
|
+
return null;
|
|
38
|
+
try {
|
|
39
|
+
return formatUnits(text, decimals);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function buildFormattedAmount(value, decimals, symbol) {
|
|
46
|
+
const raw = String(value ?? "");
|
|
47
|
+
const formatted = formatRawValue(value, decimals);
|
|
48
|
+
return {
|
|
49
|
+
raw,
|
|
50
|
+
decimals,
|
|
51
|
+
formatted,
|
|
52
|
+
display: formatted && symbol ? `${formatted} ${symbol}` : formatted,
|
|
53
|
+
symbol: symbol ?? null,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function buildFormattedPrice30(value, quoteSymbol) {
|
|
57
|
+
const raw = String(value ?? "");
|
|
58
|
+
const formatted = formatRawValue(value, 30);
|
|
59
|
+
return {
|
|
60
|
+
raw,
|
|
61
|
+
decimals: 30,
|
|
62
|
+
formatted,
|
|
63
|
+
display: formatted && quoteSymbol ? `${formatted} ${quoteSymbol}` : formatted,
|
|
64
|
+
symbol: quoteSymbol ?? null,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function buildFormattedRatio18(value) {
|
|
68
|
+
const raw = String(value ?? "");
|
|
69
|
+
const formatted = formatRawValue(value, 18);
|
|
70
|
+
return {
|
|
71
|
+
raw,
|
|
72
|
+
decimals: 18,
|
|
73
|
+
formatted,
|
|
74
|
+
display: formatted,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function buildRawScalar(value) {
|
|
78
|
+
const raw = String(value ?? "").trim();
|
|
79
|
+
if (!SIGNED_INTEGER_RE.test(raw)) {
|
|
80
|
+
return { raw, withCommas: raw || null };
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
return {
|
|
84
|
+
raw,
|
|
85
|
+
withCommas: BigInt(raw).toLocaleString("en-US"),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return { raw, withCommas: raw };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function formatScaledIntegerString(raw, decimals) {
|
|
93
|
+
if (!SIGNED_INTEGER_RE.test(raw))
|
|
94
|
+
return null;
|
|
95
|
+
const negative = raw.startsWith("-");
|
|
96
|
+
const digits = negative ? raw.slice(1) : raw;
|
|
97
|
+
const padded = digits.padStart(decimals + 1, "0");
|
|
98
|
+
const intPart = padded.slice(0, -decimals) || "0";
|
|
99
|
+
const fracPart = padded.slice(-decimals).replace(/0+$/, "");
|
|
100
|
+
const normalized = fracPart ? `${intPart}.${fracPart}` : intPart;
|
|
101
|
+
return negative ? `-${normalized}` : normalized;
|
|
102
|
+
}
|
|
103
|
+
function buildFundingRateInfo(value) {
|
|
104
|
+
const base = buildRawScalar(value);
|
|
105
|
+
const raw = String(value ?? "").trim();
|
|
106
|
+
if (!SIGNED_INTEGER_RE.test(raw))
|
|
107
|
+
return base;
|
|
108
|
+
try {
|
|
109
|
+
const rawBig = BigInt(raw);
|
|
110
|
+
const perDayRaw = rawBig * 86400n;
|
|
111
|
+
const percentPerSecond = formatScaledIntegerString(raw, 12);
|
|
112
|
+
const percentPerDay = formatScaledIntegerString(perDayRaw.toString(), 12);
|
|
113
|
+
return {
|
|
114
|
+
...base,
|
|
115
|
+
scale: "1e12 => percent",
|
|
116
|
+
percentPerSecond,
|
|
117
|
+
displayPerSecond: percentPerSecond ? `${percentPerSecond}%/秒` : null,
|
|
118
|
+
percentPerDay,
|
|
119
|
+
displayPerDay: percentPerDay ? `${percentPerDay}%/天` : null,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return base;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function buildTimestampInfo(value) {
|
|
127
|
+
const raw = String(value ?? "").trim();
|
|
128
|
+
const base = buildRawScalar(value);
|
|
129
|
+
if (!INTEGER_RE.test(raw)) {
|
|
130
|
+
return base;
|
|
131
|
+
}
|
|
132
|
+
const seconds = Number(raw);
|
|
133
|
+
if (!Number.isFinite(seconds) || seconds <= 0) {
|
|
134
|
+
return base;
|
|
135
|
+
}
|
|
136
|
+
const isoUtc = new Date(seconds * 1000).toISOString();
|
|
137
|
+
const secondsUntil = seconds - Math.floor(Date.now() / 1000);
|
|
138
|
+
return {
|
|
139
|
+
...base,
|
|
140
|
+
isoUtc,
|
|
141
|
+
secondsUntil,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function computeQuoteNotionalDisplay(baseRaw, price30Raw, baseDecimals, quoteDecimals, quoteSymbol) {
|
|
145
|
+
const baseText = String(baseRaw ?? "").trim();
|
|
146
|
+
const priceText = String(price30Raw ?? "").trim();
|
|
147
|
+
if (!INTEGER_RE.test(baseText) || !INTEGER_RE.test(priceText))
|
|
148
|
+
return null;
|
|
149
|
+
try {
|
|
150
|
+
const notionalRaw = (BigInt(baseText) * BigInt(priceText) * (10n ** BigInt(quoteDecimals))) /
|
|
151
|
+
(10n ** BigInt(baseDecimals + 30));
|
|
152
|
+
return buildFormattedAmount(notionalRaw.toString(), quoteDecimals, quoteSymbol);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function formatPoolInfoSnapshot(poolInfo, marketDetail) {
|
|
159
|
+
if (!poolInfo || typeof poolInfo !== "object")
|
|
160
|
+
return null;
|
|
161
|
+
const baseSymbol = String(marketDetail?.baseSymbol ?? "BASE");
|
|
162
|
+
const quoteSymbol = String(marketDetail?.quoteSymbol ?? "QUOTE");
|
|
163
|
+
const baseDecimals = Number(marketDetail?.baseDecimals ?? 18);
|
|
164
|
+
const quoteDecimals = Number(marketDetail?.quoteDecimals ?? 6);
|
|
165
|
+
return {
|
|
166
|
+
quotePool: poolInfo.quotePool ? {
|
|
167
|
+
poolToken: poolInfo.quotePool.poolToken ?? null,
|
|
168
|
+
exchangeRate: buildFormattedRatio18(poolInfo.quotePool.exchangeRate),
|
|
169
|
+
poolTokenPrice: buildFormattedPrice30(poolInfo.quotePool.poolTokenPrice, quoteSymbol),
|
|
170
|
+
poolTokenSupply: buildFormattedAmount(poolInfo.quotePool.poolTokenSupply, 18, `m${quoteSymbol}.${baseSymbol}`),
|
|
171
|
+
totalDebt: buildFormattedAmount(poolInfo.quotePool.totalDebt, quoteDecimals, quoteSymbol),
|
|
172
|
+
baseCollateral: buildFormattedAmount(poolInfo.quotePool.baseCollateral, baseDecimals, baseSymbol),
|
|
173
|
+
} : null,
|
|
174
|
+
basePool: poolInfo.basePool ? {
|
|
175
|
+
poolToken: poolInfo.basePool.poolToken ?? null,
|
|
176
|
+
exchangeRate: buildFormattedRatio18(poolInfo.basePool.exchangeRate),
|
|
177
|
+
poolTokenPrice: buildFormattedPrice30(poolInfo.basePool.poolTokenPrice, quoteSymbol),
|
|
178
|
+
poolTokenSupply: buildFormattedAmount(poolInfo.basePool.poolTokenSupply, 18, `m${baseSymbol}.${quoteSymbol}`),
|
|
179
|
+
totalDebt: buildFormattedAmount(poolInfo.basePool.totalDebt, quoteDecimals, quoteSymbol),
|
|
180
|
+
baseCollateral: buildFormattedAmount(poolInfo.basePool.baseCollateral, baseDecimals, baseSymbol),
|
|
181
|
+
} : null,
|
|
182
|
+
reserveInfo: poolInfo.reserveInfo ? {
|
|
183
|
+
baseTotalAmount: buildFormattedAmount(poolInfo.reserveInfo.baseTotalAmount, baseDecimals, baseSymbol),
|
|
184
|
+
baseReservedAmount: buildFormattedAmount(poolInfo.reserveInfo.baseReservedAmount, baseDecimals, baseSymbol),
|
|
185
|
+
quoteTotalAmount: buildFormattedAmount(poolInfo.reserveInfo.quoteTotalAmount, quoteDecimals, quoteSymbol),
|
|
186
|
+
quoteReservedAmount: buildFormattedAmount(poolInfo.reserveInfo.quoteReservedAmount, quoteDecimals, quoteSymbol),
|
|
187
|
+
} : null,
|
|
188
|
+
fundingInfo: poolInfo.fundingInfo ? {
|
|
189
|
+
nextFundingRate: buildFundingRateInfo(poolInfo.fundingInfo.nextFundingRate),
|
|
190
|
+
lastFundingFeeTracker: buildRawScalar(poolInfo.fundingInfo.lastFundingFeeTracker),
|
|
191
|
+
nextEpochTime: buildTimestampInfo(poolInfo.fundingInfo.nextEpochTime),
|
|
192
|
+
} : null,
|
|
193
|
+
ioTracker: poolInfo.ioTracker ? {
|
|
194
|
+
tracker: buildFormattedAmount(poolInfo.ioTracker.tracker, baseDecimals, baseSymbol),
|
|
195
|
+
longSize: buildFormattedAmount(poolInfo.ioTracker.longSize, baseDecimals, baseSymbol),
|
|
196
|
+
shortSize: buildFormattedAmount(poolInfo.ioTracker.shortSize, baseDecimals, baseSymbol),
|
|
197
|
+
poolEntryPrice: buildFormattedPrice30(poolInfo.ioTracker.poolEntryPrice, quoteSymbol),
|
|
198
|
+
trackerNotionalAtEntry: computeQuoteNotionalDisplay(poolInfo.ioTracker.tracker, poolInfo.ioTracker.poolEntryPrice, baseDecimals, quoteDecimals, quoteSymbol),
|
|
199
|
+
longNotionalAtEntry: computeQuoteNotionalDisplay(poolInfo.ioTracker.longSize, poolInfo.ioTracker.poolEntryPrice, baseDecimals, quoteDecimals, quoteSymbol),
|
|
200
|
+
shortNotionalAtEntry: computeQuoteNotionalDisplay(poolInfo.ioTracker.shortSize, poolInfo.ioTracker.poolEntryPrice, baseDecimals, quoteDecimals, quoteSymbol),
|
|
201
|
+
} : null,
|
|
202
|
+
liquidityInfo: poolInfo.liquidityInfo ? {
|
|
203
|
+
windowCaps: buildFormattedAmount(poolInfo.liquidityInfo.windowCaps, quoteDecimals, quoteSymbol),
|
|
204
|
+
openInterest: buildFormattedAmount(poolInfo.liquidityInfo.openInterest, 18, quoteSymbol),
|
|
205
|
+
} : null,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
32
208
|
export const getPoolMetadataTool = {
|
|
33
209
|
name: "get_pool_metadata",
|
|
34
210
|
description: "[MARKET] Get comprehensive metadata for a pool (market detail, on-chain info, liquidity, and limits).",
|
|
@@ -57,6 +233,11 @@ export const getPoolMetadataTool = {
|
|
|
57
233
|
// 2. Pool Info (Reserves, Utilization)
|
|
58
234
|
try {
|
|
59
235
|
results.poolInfo = await getPoolInfo(poolId, chainId, client);
|
|
236
|
+
const rawMarketDetail = results.marketDetail?.data ?? results.marketDetail;
|
|
237
|
+
const formatted = formatPoolInfoSnapshot(results.poolInfo, rawMarketDetail);
|
|
238
|
+
if (formatted) {
|
|
239
|
+
results.poolInfoFormatted = formatted;
|
|
240
|
+
}
|
|
60
241
|
}
|
|
61
242
|
catch (err) {
|
|
62
243
|
errors.push(compactWarning("poolInfo", err));
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { formatUnits } from "ethers";
|
|
2
3
|
import { quoteDeposit, quoteWithdraw, baseDeposit, baseWithdraw, getLpPrice, } from "../services/poolService.js";
|
|
3
4
|
import { resolveClient, getChainId } from "../auth/resolveClient.js";
|
|
4
5
|
import { resolvePool } from "../services/marketService.js";
|
|
@@ -45,6 +46,20 @@ function resolveLpAssetNames(detail) {
|
|
|
45
46
|
quoteLpAssetName,
|
|
46
47
|
};
|
|
47
48
|
}
|
|
49
|
+
function formatLpPricePayload(value, quoteSymbol) {
|
|
50
|
+
const raw = String(value ?? "").trim();
|
|
51
|
+
if (!/^\d+$/.test(raw)) {
|
|
52
|
+
return { raw, formatted: null, decimals: 30, symbol: quoteSymbol ?? null };
|
|
53
|
+
}
|
|
54
|
+
const formatted = formatUnits(raw, 30);
|
|
55
|
+
return {
|
|
56
|
+
raw,
|
|
57
|
+
formatted,
|
|
58
|
+
decimals: 30,
|
|
59
|
+
symbol: quoteSymbol ?? null,
|
|
60
|
+
display: quoteSymbol ? `${formatted} ${quoteSymbol}` : formatted,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
48
63
|
export const manageLiquidityTool = {
|
|
49
64
|
name: "manage_liquidity",
|
|
50
65
|
description: "[LIQUIDITY] Add or withdraw liquidity from a BASE or QUOTE pool. Success response includes LP naming metadata: base `mBASE.QUOTE`, quote `mQUOTE.BASE`, plus `operatedLpAssetName` based on poolType.",
|
|
@@ -135,8 +150,20 @@ export const getLpPriceTool = {
|
|
|
135
150
|
},
|
|
136
151
|
handler: async (args) => {
|
|
137
152
|
try {
|
|
138
|
-
const
|
|
139
|
-
|
|
153
|
+
const { client } = await resolveClient();
|
|
154
|
+
const chainId = args.chainId ?? getChainId();
|
|
155
|
+
const poolId = await resolvePool(client, args.poolId, undefined, chainId);
|
|
156
|
+
const detailRes = await client.markets.getMarketDetail({ chainId, poolId }).catch(() => null);
|
|
157
|
+
const detail = detailRes?.data || (detailRes?.marketId ? detailRes : null);
|
|
158
|
+
const quoteSymbol = String(detail?.quoteSymbol ?? "").trim() || null;
|
|
159
|
+
const rawPrice = await getLpPrice(args.poolType, poolId, chainId);
|
|
160
|
+
const payload = {
|
|
161
|
+
raw: String(rawPrice ?? ""),
|
|
162
|
+
formatted: formatLpPricePayload(rawPrice, quoteSymbol ?? undefined),
|
|
163
|
+
poolType: args.poolType,
|
|
164
|
+
poolId,
|
|
165
|
+
};
|
|
166
|
+
return { content: [{ type: "text", text: JSON.stringify({ status: "success", data: payload }, (k, v) => typeof v === 'bigint' ? v.toString() : v, 2) }] };
|
|
140
167
|
}
|
|
141
168
|
catch (error) {
|
|
142
169
|
return { content: [{ type: "text", text: `Error: ${extractErrorMessage(error)}` }], isError: true };
|