@zebpay_rajesh/zebpay-mcp-server 0.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.

Potentially problematic release.


This version of @zebpay_rajesh/zebpay-mcp-server might be problematic. Click here for more details.

Files changed (120) hide show
  1. package/.env.example +14 -0
  2. package/README.md +223 -0
  3. package/dist/__tests__/errors.test.d.ts +5 -0
  4. package/dist/__tests__/errors.test.js +147 -0
  5. package/dist/__tests__/errors.test.js.map +1 -0
  6. package/dist/__tests__/prompts.test.d.ts +1 -0
  7. package/dist/__tests__/prompts.test.js +73 -0
  8. package/dist/__tests__/prompts.test.js.map +1 -0
  9. package/dist/__tests__/resources.test.d.ts +1 -0
  10. package/dist/__tests__/resources.test.js +79 -0
  11. package/dist/__tests__/resources.test.js.map +1 -0
  12. package/dist/__tests__/validation.test.d.ts +15 -0
  13. package/dist/__tests__/validation.test.js +64 -0
  14. package/dist/__tests__/validation.test.js.map +1 -0
  15. package/dist/config.d.ts +19 -0
  16. package/dist/config.js +81 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/http/httpClient.d.ts +40 -0
  19. package/dist/http/httpClient.js +341 -0
  20. package/dist/http/httpClient.js.map +1 -0
  21. package/dist/index.d.ts +1 -0
  22. package/dist/index.js +60 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/mcp/errors.d.ts +21 -0
  25. package/dist/mcp/errors.js +214 -0
  26. package/dist/mcp/errors.js.map +1 -0
  27. package/dist/mcp/logging.d.ts +21 -0
  28. package/dist/mcp/logging.js +241 -0
  29. package/dist/mcp/logging.js.map +1 -0
  30. package/dist/mcp/prompts.d.ts +9 -0
  31. package/dist/mcp/prompts.js +165 -0
  32. package/dist/mcp/prompts.js.map +1 -0
  33. package/dist/mcp/resources.d.ts +9 -0
  34. package/dist/mcp/resources.js +125 -0
  35. package/dist/mcp/resources.js.map +1 -0
  36. package/dist/mcp/tools_futures.d.ts +5 -0
  37. package/dist/mcp/tools_futures.js +694 -0
  38. package/dist/mcp/tools_futures.js.map +1 -0
  39. package/dist/mcp/tools_spot.d.ts +11 -0
  40. package/dist/mcp/tools_spot.js +2225 -0
  41. package/dist/mcp/tools_spot.js.map +1 -0
  42. package/dist/private/FuturesClient.d.ts +57 -0
  43. package/dist/private/FuturesClient.js +181 -0
  44. package/dist/private/FuturesClient.js.map +1 -0
  45. package/dist/private/SpotClient.d.ts +44 -0
  46. package/dist/private/SpotClient.js +201 -0
  47. package/dist/private/SpotClient.js.map +1 -0
  48. package/dist/private/ZebpayAPI.d.ts +19 -0
  49. package/dist/private/ZebpayAPI.js +172 -0
  50. package/dist/private/ZebpayAPI.js.map +1 -0
  51. package/dist/public/PublicClient.d.ts +79 -0
  52. package/dist/public/PublicClient.js +283 -0
  53. package/dist/public/PublicClient.js.map +1 -0
  54. package/dist/public/PublicFuturesClient.d.ts +27 -0
  55. package/dist/public/PublicFuturesClient.js +187 -0
  56. package/dist/public/PublicFuturesClient.js.map +1 -0
  57. package/dist/security/credentials.d.ts +42 -0
  58. package/dist/security/credentials.js +80 -0
  59. package/dist/security/credentials.js.map +1 -0
  60. package/dist/security/signing.d.ts +33 -0
  61. package/dist/security/signing.js +56 -0
  62. package/dist/security/signing.js.map +1 -0
  63. package/dist/types/responses.d.ts +130 -0
  64. package/dist/types/responses.js +6 -0
  65. package/dist/types/responses.js.map +1 -0
  66. package/dist/utils/cache.d.ts +29 -0
  67. package/dist/utils/cache.js +72 -0
  68. package/dist/utils/cache.js.map +1 -0
  69. package/dist/utils/fileLogger.d.ts +10 -0
  70. package/dist/utils/fileLogger.js +81 -0
  71. package/dist/utils/fileLogger.js.map +1 -0
  72. package/dist/utils/metrics.d.ts +35 -0
  73. package/dist/utils/metrics.js +94 -0
  74. package/dist/utils/metrics.js.map +1 -0
  75. package/dist/utils/responseFormatter.d.ts +93 -0
  76. package/dist/utils/responseFormatter.js +268 -0
  77. package/dist/utils/responseFormatter.js.map +1 -0
  78. package/dist/validation/schemas.d.ts +70 -0
  79. package/dist/validation/schemas.js +48 -0
  80. package/dist/validation/schemas.js.map +1 -0
  81. package/dist/validation/validators.d.ts +28 -0
  82. package/dist/validation/validators.js +129 -0
  83. package/dist/validation/validators.js.map +1 -0
  84. package/docs/LOGGING.md +371 -0
  85. package/docs/zebpay-ai-trading-beginner.png +0 -0
  86. package/mcp-config.json.example +20 -0
  87. package/package.json +54 -0
  88. package/scripts/README.md +103 -0
  89. package/scripts/clear-logs.js +52 -0
  90. package/scripts/log-stats.js +264 -0
  91. package/scripts/log-viewer.js +288 -0
  92. package/server.json +31 -0
  93. package/src/__tests__/errors.test.ts +180 -0
  94. package/src/__tests__/prompts.test.ts +89 -0
  95. package/src/__tests__/resources.test.ts +95 -0
  96. package/src/__tests__/validation.test.ts +88 -0
  97. package/src/config.ts +108 -0
  98. package/src/http/httpClient.ts +398 -0
  99. package/src/index.ts +71 -0
  100. package/src/mcp/errors.ts +262 -0
  101. package/src/mcp/logging.ts +284 -0
  102. package/src/mcp/prompts.ts +206 -0
  103. package/src/mcp/resources.ts +163 -0
  104. package/src/mcp/tools_futures.ts +874 -0
  105. package/src/mcp/tools_spot.ts +2702 -0
  106. package/src/private/FuturesClient.ts +189 -0
  107. package/src/private/SpotClient.ts +250 -0
  108. package/src/private/ZebpayAPI.ts +205 -0
  109. package/src/public/PublicClient.ts +381 -0
  110. package/src/public/PublicFuturesClient.ts +228 -0
  111. package/src/security/credentials.ts +114 -0
  112. package/src/security/signing.ts +98 -0
  113. package/src/types/responses.ts +146 -0
  114. package/src/utils/cache.ts +90 -0
  115. package/src/utils/fileLogger.ts +88 -0
  116. package/src/utils/metrics.ts +135 -0
  117. package/src/utils/responseFormatter.ts +361 -0
  118. package/src/validation/schemas.ts +66 -0
  119. package/src/validation/validators.ts +189 -0
  120. package/tsconfig.json +21 -0
@@ -0,0 +1,2225 @@
1
+ /*
2
+ MCP tool registrations using the MCP SDK.
3
+ */
4
+ import { z } from "zod";
5
+ import { withLogging } from "./logging.js";
6
+ import { validateSymbol, validateQuantity, validateClientOrderId, symbolSchema, quantitySchema } from "../validation/validators.js";
7
+ import { createInternalError, createInvalidParamsError } from "./errors.js";
8
+ import { formatResponse, formatBalanceResponse, formatTickerResponse, formatOrderResponse, formatOrderBookResponse, formatTradesResponse, formatKlinesResponse, generateCorrelationId } from "../utils/responseFormatter.js";
9
+ import { metricsCollector } from "../utils/metrics.js";
10
+ export function registerSpotTools(server, spot, // null if no credentials provided - tools still registered but will error if called
11
+ publicClient, config) {
12
+ console.error(`Registering SPOT tools - Authenticated tools will check credentials at runtime`);
13
+ // Always register authenticated tools - they will check credentials at runtime
14
+ console.error("Registering authenticated tools (spot trading, balances, orders)...");
15
+ try {
16
+ server.tool("zebpay_spot_placeMarketOrder", `Place a market order on Zebpay spot trading exchange with automatic currency conversion.
17
+
18
+ This tool executes an immediate buy or sell order at the current market price. Market orders are filled instantly at the best available price, unlike limit orders which wait for a specific price.
19
+
20
+ **When to use this tool:**
21
+ - User wants to buy or sell cryptocurrency immediately
22
+ - User needs to execute a trade quickly without waiting for a specific price
23
+ - User wants to convert one cryptocurrency to another or to fiat currency
24
+
25
+ **Important notes:**
26
+ - Market orders execute immediately at current market price
27
+ - Supports BOTH quote currency (INR) and base currency (BTC) specifications
28
+ - Automatic conversion using current market price when needed
29
+ - All quantities are trimmed to 6 decimal places
30
+ - Symbol format is typically BASE-QUOTE (e.g., BTC-INR means buying/selling Bitcoin with Indian Rupees)
31
+
32
+ **How conversion works:**
33
+
34
+ For BUY orders:
35
+ - User specifies quoteOrderAmount (100 INR) → Used directly as quoteOrderAmount
36
+ - User specifies amount (0.0001 BTC) → Converted: 0.0001 × market_price = INR value → Used as quoteOrderAmount
37
+
38
+ For SELL orders:
39
+ - User specifies amount (0.0005 BTC) → Used directly as amount
40
+ - User specifies quoteOrderAmount (200 INR) → Converted: 200 ÷ market_price = BTC quantity → Used as amount
41
+
42
+ **Example use cases:**
43
+ 1. User says "Buy 100 INR worth of BTC" → Use symbol="BTC-INR", side="BUY", quoteOrderAmount="100"
44
+ 2. User says "Sell 0.0005 BTC" → Use symbol="BTC-INR", side="SELL", amount="0.0005"
45
+ 3. User says "Buy 0.0001 BTC" → Use symbol="BTC-INR", side="BUY", amount="0.0001" (auto-converts to INR)
46
+ 4. User says "Sell 200 INR worth of BTC" → Use symbol="BTC-INR", side="SELL", quoteOrderAmount="200" (auto-converts to BTC)
47
+
48
+ **Example request (BUY with quote currency):**
49
+ {
50
+ "symbol": "BTC-INR",
51
+ "side": "BUY",
52
+ "quoteOrderAmount": "100"
53
+ }
54
+ → Body sent to API: { symbol: "BTC-INR", side: "BUY", type: "MARKET", quoteOrderAmount: "100" }
55
+
56
+ **Example request (BUY with base currency - auto-converted):**
57
+ {
58
+ "symbol": "BTC-INR",
59
+ "side": "BUY",
60
+ "amount": "0.0001"
61
+ }
62
+ → At market price 8900000: 0.0001 × 8900000 = 890 INR
63
+ → Body sent to API: { symbol: "BTC-INR", side: "BUY", type: "MARKET", quoteOrderAmount: "890" }
64
+
65
+ **Example request (SELL with base currency):**
66
+ {
67
+ "symbol": "BTC-INR",
68
+ "side": "SELL",
69
+ "amount": "0.0005"
70
+ }
71
+ → Body sent to API: { symbol: "BTC-INR", side: "SELL", type: "MARKET", amount: "0.0005" }
72
+
73
+ **Example request (SELL with quote currency - auto-converted):**
74
+ {
75
+ "symbol": "BTC-INR",
76
+ "side": "SELL",
77
+ "quoteOrderAmount": "200"
78
+ }
79
+ → At market price 8900000: 200 ÷ 8900000 = 0.000022 BTC
80
+ → Body sent to API: { symbol: "BTC-INR", side: "SELL", type: "MARKET", amount: "0.000022" }
81
+
82
+ **Example response:**
83
+ The tool returns order details including order ID, status, executed price, and quantity filled.
84
+
85
+ **Rate Limits:**
86
+ - Maximum 10 requests per second for authenticated endpoints
87
+ - Burst limit: 20 requests
88
+ - Rate limit headers (X-RateLimit-*) are included in responses
89
+ - If rate limited, wait 1 second before retrying
90
+
91
+ **Related Tools:**
92
+ - Use zebpay_spot_getBalance before placing orders to check available funds
93
+ - Use zebpay_public_getTicker to check current market price before trading
94
+ - Use zebpay_public_getOrderBook to analyze market depth and liquidity`, {
95
+ symbol: symbolSchema.describe(`Trading pair symbol in format BASE-QUOTE.
96
+ Examples: "BTC-INR" (Bitcoin/Indian Rupees), "ETH-INR" (Ethereum/Indian Rupees), "BTC-USDT" (Bitcoin/Tether).
97
+ The symbol determines which market you're trading in. Always use uppercase currency codes separated by a hyphen.`),
98
+ side: z.enum(["BUY", "SELL"]).describe(`Order side determines whether you're buying or selling the base currency.
99
+ - "BUY": Purchase the base currency (e.g., buying BTC with BTC-INR pair)
100
+ - "SELL": Sell the base currency (e.g., selling BTC with BTC-INR pair)
101
+ Example: For symbol "BTC-INR", side "BUY" means buying Bitcoin with Indian Rupees.`),
102
+ quoteOrderAmount: quantitySchema.optional().describe(`Quote currency value as a string (use this OR amount, not both).
103
+ Use this when user specifies the order value in QUOTE currency (e.g., INR, USDT).
104
+ Examples:
105
+ - "100" for BTC-INR means 100 INR worth
106
+ - "50" for ETH-USDT means 50 USDT worth
107
+ For BUY orders: used directly in API body as quoteOrderAmount.
108
+ For SELL orders: will be converted to base currency amount using current market price.
109
+ Always provide as a string to avoid floating-point precision issues.`),
110
+ amount: quantitySchema.optional().describe(`Base currency amount as a string (use this OR quoteOrderAmount, not both).
111
+ Use this when user specifies the quantity in BASE currency (e.g., BTC, ETH).
112
+ Examples:
113
+ - "0.0005" for BTC-INR means 0.0005 BTC
114
+ - "0.1" for ETH-INR means 0.1 ETH
115
+ For SELL orders: used directly in API body as amount.
116
+ For BUY orders: will be converted to quote currency value using current market price.
117
+ Always provide as a string to avoid floating-point precision issues.`),
118
+ clientOrderId: z.string().min(1).max(36).optional().describe(`Optional custom identifier for tracking your order.
119
+ If provided, you can use this ID to reference the order later. Must be unique.
120
+ Must contain only letters, numbers, dots, colons, slashes, underscores, and hyphens, and be 1-36 characters long.
121
+ Example: "my-trade-2024-01-15-001" or "arbitrage-order-123"
122
+ If not provided, the exchange will generate an order ID automatically.`),
123
+ platform: z.string().optional().describe(`Optional platform identifier for the order.`),
124
+ }, withLogging("spot_placeMarketOrder", config.logLevel, async ({ symbol, side, quoteOrderAmount, amount, clientOrderId, platform }) => {
125
+ const correlationId = generateCorrelationId();
126
+ const startTime = Date.now();
127
+ // Check if credentials are available
128
+ if (!spot) {
129
+ metricsCollector.record("zebpay_spot_placeMarketOrder", Date.now() - startTime, false, "missing_credentials");
130
+ throw createInvalidParamsError("API credentials are required for this operation. Please provide ZEBPAY_API_KEY and ZEBPAY_API_SECRET headers or include credentials in initialization params.", {});
131
+ }
132
+ // Validate that at least one of quoteOrderAmount or amount is provided
133
+ if (!quoteOrderAmount && !amount) {
134
+ metricsCollector.record("zebpay_spot_placeMarketOrder", Date.now() - startTime, false, "validation_error");
135
+ throw createInvalidParamsError("Either quoteOrderAmount or amount must be provided for market orders.", { symbol, side });
136
+ }
137
+ // Validate that both are not provided
138
+ if (quoteOrderAmount && amount) {
139
+ metricsCollector.record("zebpay_spot_placeMarketOrder", Date.now() - startTime, false, "validation_error");
140
+ throw createInvalidParamsError("Cannot provide both quoteOrderAmount and amount. Please specify only one.", { symbol, side });
141
+ }
142
+ // Additional validation with helpful error messages
143
+ try {
144
+ validateSymbol(symbol);
145
+ if (quoteOrderAmount) {
146
+ validateQuantity(quoteOrderAmount);
147
+ }
148
+ if (amount) {
149
+ validateQuantity(amount);
150
+ }
151
+ if (clientOrderId) {
152
+ validateClientOrderId(clientOrderId);
153
+ }
154
+ }
155
+ catch (error) {
156
+ // Re-throw validation errors (they're already MCP errors)
157
+ metricsCollector.record("zebpay_spot_placeMarketOrder", Date.now() - startTime, false, "validation_error");
158
+ throw error;
159
+ }
160
+ // Helper function to trim to 6 decimals
161
+ const trimToSixDecimals = (value) => {
162
+ const num = parseFloat(value);
163
+ // Round to 6 decimals and remove trailing zeros
164
+ const fixed = num.toFixed(6);
165
+ // Remove trailing zeros after decimal point, but keep at least one digit
166
+ return fixed.replace(/(\.\d*?)0+$/, '$1').replace(/\.$/, '');
167
+ };
168
+ let finalQuoteOrderAmount;
169
+ let finalAmount;
170
+ // Handle conversion based on side and provided parameter
171
+ if (side === "BUY") {
172
+ if (quoteOrderAmount) {
173
+ // User provided quote currency (INR) - use directly
174
+ finalQuoteOrderAmount = trimToSixDecimals(quoteOrderAmount.trim());
175
+ }
176
+ else if (amount) {
177
+ // User provided base currency (BTC) - need to convert to quote currency (INR)
178
+ // Get current market price
179
+ try {
180
+ const ticker = await publicClient.getTicker({ symbol: symbol.trim().toUpperCase() });
181
+ const currentPrice = parseFloat(ticker.lastPrice || ticker.price || "0");
182
+ if (currentPrice === 0) {
183
+ throw createInternalError("Unable to fetch current market price for conversion", { symbol });
184
+ }
185
+ // Convert: amount (BTC) * price = quoteOrderAmount (INR)
186
+ const baseAmount = parseFloat(amount.trim());
187
+ const convertedQuoteAmount = baseAmount * currentPrice;
188
+ finalQuoteOrderAmount = trimToSixDecimals(convertedQuoteAmount.toString());
189
+ }
190
+ catch (error) {
191
+ metricsCollector.record("zebpay_spot_placeMarketOrder", Date.now() - startTime, false, "conversion_error");
192
+ throw error;
193
+ }
194
+ }
195
+ }
196
+ else {
197
+ // SELL order
198
+ if (amount) {
199
+ // User provided base currency (BTC) - use directly
200
+ finalAmount = trimToSixDecimals(amount.trim());
201
+ }
202
+ else if (quoteOrderAmount) {
203
+ // User provided quote currency (INR) - need to convert to base currency (BTC)
204
+ // Get current market price
205
+ try {
206
+ const ticker = await publicClient.getTicker({ symbol: symbol.trim().toUpperCase() });
207
+ const currentPrice = parseFloat(ticker.lastPrice || ticker.price || "0");
208
+ if (currentPrice === 0) {
209
+ throw createInternalError("Unable to fetch current market price for conversion", { symbol });
210
+ }
211
+ // Convert: quoteOrderAmount (INR) / price = amount (BTC)
212
+ const quoteAmount = parseFloat(quoteOrderAmount.trim());
213
+ const convertedBaseAmount = quoteAmount / currentPrice;
214
+ finalAmount = trimToSixDecimals(convertedBaseAmount.toString());
215
+ }
216
+ catch (error) {
217
+ metricsCollector.record("zebpay_spot_placeMarketOrder", Date.now() - startTime, false, "conversion_error");
218
+ throw error;
219
+ }
220
+ }
221
+ }
222
+ const params = {
223
+ symbol: symbol.trim().toUpperCase(),
224
+ side,
225
+ ...(finalQuoteOrderAmount && { quoteOrderAmount: finalQuoteOrderAmount }),
226
+ ...(finalAmount && { amount: finalAmount }),
227
+ ...(clientOrderId && { clientOrderId }),
228
+ ...(platform && { platform }),
229
+ };
230
+ try {
231
+ const result = await spot.placeMarketOrder(params);
232
+ const durationMs = Date.now() - startTime;
233
+ metricsCollector.record("zebpay_spot_placeMarketOrder", durationMs, true);
234
+ return formatOrderResponse(result, {
235
+ correlationId,
236
+ executionTimeMs: durationMs,
237
+ });
238
+ }
239
+ catch (error) {
240
+ const durationMs = Date.now() - startTime;
241
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
242
+ metricsCollector.record("zebpay_spot_placeMarketOrder", durationMs, false, errorType);
243
+ // Re-throw MCP errors as-is, wrap others
244
+ if (error && typeof error === "object" && "code" in error) {
245
+ throw error;
246
+ }
247
+ throw createInternalError(`Failed to place market order: ${error instanceof Error ? error.message : String(error)}`, { symbol, side, quoteOrderAmount, amount });
248
+ }
249
+ }));
250
+ console.error("Registered tool: zebpay_spot_placeMarketOrder");
251
+ }
252
+ catch (error) {
253
+ console.error("Error registering spot_placeMarketOrder:", error);
254
+ }
255
+ // Limit order
256
+ try {
257
+ server.tool("zebpay_spot_placeLimitOrder", `Place a LIMIT order on Zebpay spot exchange with automatic currency conversion.
258
+
259
+ Use this to buy or sell at a specific price. The order rests on the order book until it can be filled at the specified price or better.
260
+
261
+ **Important notes:**
262
+ - Supports BOTH quote currency (INR) and base currency (BTC) specifications
263
+ - Automatic conversion using the provided limit price when needed
264
+ - All quantities are trimmed to 6 decimal places
265
+ - You must specify EITHER quoteOrderAmount OR amount (not both)
266
+
267
+ **How conversion works:**
268
+
269
+ For BUY orders:
270
+ - User specifies quoteOrderAmount (100 INR) → Used directly as quoteOrderAmount
271
+ - User specifies amount (0.0001 BTC) → Converted: 0.0001 × limit_price = INR value → Used as quoteOrderAmount
272
+
273
+ For SELL orders:
274
+ - User specifies amount (0.0005 BTC) → Used directly as amount
275
+ - User specifies quoteOrderAmount (200 INR) → Converted: 200 ÷ limit_price = BTC quantity → Used as amount
276
+
277
+ **Example use cases:**
278
+ 1. User says "Buy 100 INR worth of BTC at 6000000" → Use quoteOrderAmount="100", price="6000000"
279
+ 2. User says "Sell 0.0005 BTC at 6000000" → Use amount="0.0005", price="6000000"
280
+ 3. User says "Buy 0.0001 BTC at 6000000" → Use amount="0.0001", price="6000000" (auto-converts to INR)
281
+ 4. User says "Sell 200 INR worth at 6000000" → Use quoteOrderAmount="200", price="6000000" (auto-converts to BTC)
282
+
283
+ **Example:**
284
+ {
285
+ "symbol": "BTC-INR",
286
+ "side": "BUY",
287
+ "quoteOrderAmount": "100",
288
+ "price": "6000000",
289
+ "clientOrderId": "my-limit-1"
290
+ }
291
+ → Body sent to API: { symbol: "BTC-INR", side: "BUY", type: "LIMIT", quoteOrderAmount: "100", price: "6000000" }`, {
292
+ symbol: symbolSchema.describe(`Trading pair symbol in format BASE-QUOTE, e.g., "BTC-INR", "ETH-USDT".`),
293
+ side: z.enum(["BUY", "SELL"]).describe(`Order side determines whether you're buying or selling the base currency.
294
+ - "BUY": Purchase the base currency at the specified limit price
295
+ - "SELL": Sell the base currency at the specified limit price`),
296
+ quoteOrderAmount: quantitySchema.optional().describe(`Quote currency value as a string (use this OR amount, not both).
297
+ Use this when user specifies the order value in QUOTE currency (e.g., INR, USDT).
298
+ Examples:
299
+ - "100" for BTC-INR means 100 INR worth
300
+ - "50" for ETH-USDT means 50 USDT worth
301
+ For BUY orders: used directly in API body as quoteOrderAmount.
302
+ For SELL orders: will be converted to base currency amount using the provided limit price.
303
+ Always provide as a string to avoid floating-point precision issues.`),
304
+ amount: quantitySchema.optional().describe(`Base currency amount as a string (use this OR quoteOrderAmount, not both).
305
+ Use this when user specifies the quantity in BASE currency (e.g., BTC, ETH).
306
+ Examples:
307
+ - "0.001" for BTC-INR means 0.001 BTC
308
+ - "0.1" for ETH-INR means 0.1 ETH
309
+ For SELL orders: used directly in API body as amount.
310
+ For BUY orders: will be converted to quote currency value using the provided limit price.
311
+ Always provide as a string to avoid floating-point precision issues.`),
312
+ price: quantitySchema.describe(`Limit price as a string in quote currency.
313
+ This is the price at which you want to buy or sell.
314
+ Example: "6000000" means 60,00,000 INR for BTC-INR pair.
315
+ Always provide as a string to preserve precision.`),
316
+ clientOrderId: z.string().min(1).max(36).optional().describe(`Optional custom identifier for tracking your order.
317
+ Must contain only letters, numbers, dots, colons, slashes, underscores, and hyphens, and be 1-36 characters long.
318
+ Example: "my-limit-order-123"`),
319
+ platform: z.string().optional().describe(`Optional platform identifier for the order.`),
320
+ }, withLogging("spot_placeLimitOrder", config.logLevel, async ({ symbol, side, quoteOrderAmount, amount, price, clientOrderId, platform }) => {
321
+ const correlationId = generateCorrelationId();
322
+ const startTime = Date.now();
323
+ if (!spot) {
324
+ metricsCollector.record("zebpay_spot_placeLimitOrder", Date.now() - startTime, false, "missing_credentials");
325
+ throw createInvalidParamsError("API credentials are required for this operation. Please provide ZEBPAY_API_KEY and ZEBPAY_API_SECRET.", {});
326
+ }
327
+ // Validate that at least one of quoteOrderAmount or amount is provided
328
+ if (!quoteOrderAmount && !amount) {
329
+ metricsCollector.record("zebpay_spot_placeLimitOrder", Date.now() - startTime, false, "validation_error");
330
+ throw createInvalidParamsError("Either quoteOrderAmount or amount must be provided for limit orders.", { symbol, side });
331
+ }
332
+ // Validate that both are not provided
333
+ if (quoteOrderAmount && amount) {
334
+ metricsCollector.record("zebpay_spot_placeLimitOrder", Date.now() - startTime, false, "validation_error");
335
+ throw createInvalidParamsError("Cannot provide both quoteOrderAmount and amount. Please specify only one.", { symbol, side });
336
+ }
337
+ try {
338
+ validateSymbol(symbol);
339
+ validateQuantity(price);
340
+ if (quoteOrderAmount) {
341
+ validateQuantity(quoteOrderAmount);
342
+ }
343
+ if (amount) {
344
+ validateQuantity(amount);
345
+ }
346
+ if (clientOrderId) {
347
+ validateClientOrderId(clientOrderId);
348
+ }
349
+ }
350
+ catch (error) {
351
+ metricsCollector.record("zebpay_spot_placeLimitOrder", Date.now() - startTime, false, "validation_error");
352
+ throw error;
353
+ }
354
+ // Helper function to trim to 6 decimals
355
+ const trimToSixDecimals = (value) => {
356
+ const num = parseFloat(value);
357
+ // Round to 6 decimals and remove trailing zeros
358
+ const fixed = num.toFixed(6);
359
+ // Remove trailing zeros after decimal point, but keep at least one digit
360
+ return fixed.replace(/(\.\d*?)0+$/, '$1').replace(/\.$/, '');
361
+ };
362
+ let finalQuoteOrderAmount;
363
+ let finalAmount;
364
+ const limitPrice = parseFloat(price.trim());
365
+ if (limitPrice === 0) {
366
+ metricsCollector.record("zebpay_spot_placeLimitOrder", Date.now() - startTime, false, "validation_error");
367
+ throw createInvalidParamsError("Price must be greater than 0", { symbol, side, price });
368
+ }
369
+ // Handle conversion based on side and provided parameter
370
+ if (side === "BUY") {
371
+ if (quoteOrderAmount) {
372
+ // User provided quote currency (INR) - use directly
373
+ finalQuoteOrderAmount = trimToSixDecimals(quoteOrderAmount.trim());
374
+ }
375
+ else if (amount) {
376
+ // User provided base currency (BTC) - convert to quote currency (INR) using limit price
377
+ const baseAmount = parseFloat(amount.trim());
378
+ const convertedQuoteAmount = baseAmount * limitPrice;
379
+ finalQuoteOrderAmount = trimToSixDecimals(convertedQuoteAmount.toString());
380
+ }
381
+ }
382
+ else {
383
+ // SELL order
384
+ if (amount) {
385
+ // User provided base currency (BTC) - use directly
386
+ finalAmount = trimToSixDecimals(amount.trim());
387
+ }
388
+ else if (quoteOrderAmount) {
389
+ // User provided quote currency (INR) - convert to base currency (BTC) using limit price
390
+ const quoteAmount = parseFloat(quoteOrderAmount.trim());
391
+ const convertedBaseAmount = quoteAmount / limitPrice;
392
+ finalAmount = trimToSixDecimals(convertedBaseAmount.toString());
393
+ }
394
+ }
395
+ const params = {
396
+ symbol: symbol.trim().toUpperCase(),
397
+ side,
398
+ ...(finalQuoteOrderAmount && { quoteOrderAmount: finalQuoteOrderAmount }),
399
+ ...(finalAmount && { amount: finalAmount }),
400
+ price: price.trim(),
401
+ ...(clientOrderId && { clientOrderId }),
402
+ ...(platform && { platform }),
403
+ };
404
+ try {
405
+ const result = await spot.placeLimitOrder(params);
406
+ const durationMs = Date.now() - startTime;
407
+ metricsCollector.record("zebpay_spot_placeLimitOrder", durationMs, true);
408
+ return formatOrderResponse(result, {
409
+ correlationId,
410
+ executionTimeMs: durationMs,
411
+ });
412
+ }
413
+ catch (error) {
414
+ const durationMs = Date.now() - startTime;
415
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
416
+ metricsCollector.record("zebpay_spot_placeLimitOrder", durationMs, false, errorType);
417
+ if (error && typeof error === "object" && "code" in error)
418
+ throw error;
419
+ throw createInternalError(`Failed to place limit order: ${error instanceof Error ? error.message : String(error)}`, { symbol, side, quoteOrderAmount, amount, price });
420
+ }
421
+ }));
422
+ console.error("Registered tool: zebpay_spot_placeLimitOrder");
423
+ }
424
+ catch (error) {
425
+ console.error("Error registering zebpay_spot_placeLimitOrder:", error);
426
+ }
427
+ try {
428
+ server.tool("zebpay_spot_getBalance", `Fetch the user's spot trading account balance from Zebpay exchange.
429
+
430
+ This tool retrieves the current balance of cryptocurrencies and fiat currencies in the user's spot trading account. Use this before placing orders to check available funds.
431
+
432
+ **When to use this tool:**
433
+ - User asks about their account balance or available funds
434
+ - Before placing a buy order to verify sufficient funds
435
+ - User wants to check holdings of specific cryptocurrencies
436
+ - User asks "How much BTC do I have?" or "What's my balance?"
437
+
438
+ **Important notes:**
439
+ - Returns balances for all currencies by default
440
+ - Can filter to specific currencies using the currencies parameter
441
+ - Shows both available balance and any locked/held balance
442
+ - Balance is returned in the account's native format
443
+
444
+ **Example use cases:**
445
+ 1. User says "Check my balance" → Call without currencies parameter to get all balances
446
+ 2. User says "How much Bitcoin do I have?" → Use currencies="BTC" to filter
447
+ 3. Before buying BTC → Check INR balance with currencies="INR"
448
+ 4. User wants to see multiple currencies → Use currencies="BTC,ETH,INR"
449
+
450
+ **Example request (all balances):**
451
+ {}
452
+
453
+ **Example request (filtered):**
454
+ {
455
+ "currencies": "BTC,ETH,INR"
456
+ }
457
+
458
+ **Example response:**
459
+ Returns an object with balance information including available amounts, locked amounts, and currency codes for each currency in the account.
460
+
461
+ **Rate Limits:**
462
+ - Maximum 10 requests per second for authenticated endpoints
463
+ - Burst limit: 20 requests
464
+ - Rate limit headers (X-RateLimit-*) are included in responses
465
+ - If rate limited, wait 1 second before retrying
466
+
467
+ **Related Tools:**
468
+ - Use before zebpay_spot_placeMarketOrder to verify sufficient funds
469
+ - Use zebpay_public_getTicker to check current prices for balance calculations
470
+ - Access balance as a resource via zebpay://account/balance`, {
471
+ currencies: z.string().optional().describe(`Optional comma-separated list of currency codes to filter the balance response.
472
+ If not provided, returns balances for all currencies in the account.
473
+ Format: "CURRENCY1,CURRENCY2,CURRENCY3" (no spaces, uppercase currency codes).
474
+ Examples:
475
+ - "BTC" (only Bitcoin balance)
476
+ - "BTC,ETH" (Bitcoin and Ethereum balances)
477
+ - "BTC,ETH,INR" (Bitcoin, Ethereum, and Indian Rupees balances)
478
+ - undefined or omit parameter (all currencies)
479
+ Use this to get specific currency balances when the user asks about particular assets.`),
480
+ }, withLogging("zebpay_spot_getBalance", config.logLevel, async ({ currencies }) => {
481
+ const correlationId = generateCorrelationId();
482
+ const startTime = Date.now();
483
+ // Check if credentials are available
484
+ if (!spot) {
485
+ metricsCollector.record("zebpay_spot_getBalance", Date.now() - startTime, false, "missing_credentials");
486
+ throw createInvalidParamsError("API credentials are required for this operation. Please provide ZEBPAY_API_KEY and ZEBPAY_API_SECRET headers or include credentials in initialization params.", {});
487
+ }
488
+ try {
489
+ const result = await spot.getBalance(currencies);
490
+ const durationMs = Date.now() - startTime;
491
+ metricsCollector.record("zebpay_spot_getBalance", durationMs, true);
492
+ return formatBalanceResponse(result, {
493
+ correlationId,
494
+ executionTimeMs: durationMs,
495
+ });
496
+ }
497
+ catch (error) {
498
+ const durationMs = Date.now() - startTime;
499
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
500
+ metricsCollector.record("zebpay_spot_getBalance", durationMs, false, errorType);
501
+ throw error;
502
+ }
503
+ }));
504
+ console.error("Registered tool: zebpay_spot_getBalance");
505
+ }
506
+ catch (error) {
507
+ console.error("Error registering spot_getBalance:", error);
508
+ }
509
+ try {
510
+ server.tool("zebpay_spot_getExchangeFee", `Get exchange fee details for a specific trading pair and order side.
511
+
512
+ This tool retrieves the maker and taker fee rates for a specific trading pair, including GST and TDS information. Use this to understand the fees that will be applied to your trades.
513
+
514
+ **When to use this tool:**
515
+ - User wants to know the trading fees for a specific pair
516
+ - User needs to calculate the cost of a trade before placing an order
517
+ - User wants to understand maker vs taker fee rates
518
+ - User needs to check GST and TDS percentages
519
+
520
+ **Important notes:**
521
+ - Requires authentication (API credentials)
522
+ - Returns maker fee rate (for limit orders that add liquidity)
523
+ - Returns taker fee rate (for orders that take liquidity immediately)
524
+ - Fee rates are typically in percentage format
525
+ - GST and TDS information is included in the response
526
+
527
+ **Example use cases:**
528
+ 1. User says "What are the fees for BTC-INR?" → Use symbol="BTC-INR", side="BUY" (or "SELL")
529
+ 2. User wants to know trading costs before buying ETH → Use symbol="ETH-INR", side="BUY"
530
+
531
+ **Example request:**
532
+ {
533
+ "symbol": "BTC-INR",
534
+ "side": "BUY"
535
+ }
536
+
537
+ **Example response:**
538
+ Returns fee details including maker/taker fee rates, whether fees are percentage-based, GST, and TDS.
539
+
540
+ Endpoint: GET /api/v2/ex/tradefee?symbol=SYMBOL&side=SIDE`, {
541
+ symbol: symbolSchema.describe(`Trading pair symbol in format BASE-QUOTE (required).
542
+ Examples: "BTC-INR", "ETH-INR", "BTC-USDT".`),
543
+ side: z.enum(["BUY", "SELL"]).describe(`Order side (required).
544
+ - "BUY": Get fees for buy orders
545
+ - "SELL": Get fees for sell orders`),
546
+ }, withLogging("zebpay_spot_getExchangeFee", config.logLevel, async ({ symbol, side }) => {
547
+ const correlationId = generateCorrelationId();
548
+ const startTime = Date.now();
549
+ if (!spot) {
550
+ metricsCollector.record("zebpay_spot_getExchangeFee", Date.now() - startTime, false, "missing_credentials");
551
+ throw createInvalidParamsError("API credentials are required for this operation. Please provide ZEBPAY_API_KEY and ZEBPAY_API_SECRET.", {});
552
+ }
553
+ try {
554
+ validateSymbol(symbol);
555
+ }
556
+ catch (error) {
557
+ metricsCollector.record("zebpay_spot_getExchangeFee", Date.now() - startTime, false, "validation_error");
558
+ throw error;
559
+ }
560
+ try {
561
+ const result = await spot.getExchangeFee({
562
+ symbol: symbol.trim().toUpperCase(),
563
+ side,
564
+ });
565
+ const durationMs = Date.now() - startTime;
566
+ metricsCollector.record("zebpay_spot_getExchangeFee", durationMs, true);
567
+ return formatResponse(result, {
568
+ correlationId,
569
+ executionTimeMs: durationMs,
570
+ });
571
+ }
572
+ catch (error) {
573
+ const durationMs = Date.now() - startTime;
574
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
575
+ metricsCollector.record("zebpay_spot_getExchangeFee", durationMs, false, errorType);
576
+ throw error;
577
+ }
578
+ }));
579
+ console.error("Registered tool: zebpay_spot_getExchangeFee");
580
+ }
581
+ catch (error) {
582
+ console.error("Error registering zebpay_spot_getExchangeFee:", error);
583
+ }
584
+ // Public API tools (always available, no credentials required)
585
+ console.error("Registering public tools (market data, tickers, order books)...");
586
+ try {
587
+ server.tool("zebpay_public_getAllTickers", `Get ticker/price information for all trading pairs on Zebpay exchange.
588
+
589
+ This tool retrieves the current market price, 24h statistics, and trading volume for all available trading pairs. No authentication required.
590
+
591
+ **When to use this tool:**
592
+ - User asks to see all market prices
593
+ - User wants to check prices for multiple trading pairs
594
+ - User says "Show me all prices" or "List all tickers"
595
+ - User wants an overview of all market data
596
+
597
+ **Important notes:**
598
+ - Returns ticker data for all trading pairs including last price, bid/ask, 24h high/low, volume
599
+ - Uses endpoint: api/v2/market/allTickers
600
+ - No authentication required - this is public market data
601
+
602
+ **Example use cases:**
603
+ 1. User says "Show me all prices" → Call without parameters
604
+ 2. User wants market overview → Use this tool
605
+ 3. User asks "List all tickers" → Use this tool
606
+
607
+ **Example request:**
608
+ {}
609
+
610
+ **Example response:**
611
+ Returns ticker data for all trading pairs with current price, 24h statistics, trading volume, and price changes.
612
+
613
+ **Rate Limits:**
614
+ - Maximum 20 requests per second for public endpoints
615
+ - Burst limit: 40 requests
616
+ - Rate limit headers (X-RateLimit-*) are included in responses
617
+ - Public endpoints have higher rate limits than authenticated endpoints
618
+
619
+ **Related Tools:**
620
+ - Use zebpay_public_getTicker for a specific trading pair
621
+ - Use zebpay_public_getOrderBook to see market depth
622
+ - Access all tickers as a resource via zebpay://market/tickers`, {}, withLogging("zebpay_public_getAllTickers", config.logLevel, async () => {
623
+ const correlationId = generateCorrelationId();
624
+ const startTime = Date.now();
625
+ try {
626
+ const result = await publicClient.getAllTickers();
627
+ const durationMs = Date.now() - startTime;
628
+ metricsCollector.record("zebpay_public_getAllTickers", durationMs, true);
629
+ return formatResponse(result, {
630
+ toolName: "zebpay_public_getAllTickers",
631
+ correlationId,
632
+ executionTimeMs: durationMs,
633
+ });
634
+ }
635
+ catch (error) {
636
+ const durationMs = Date.now() - startTime;
637
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
638
+ metricsCollector.record("zebpay_public_getAllTickers", durationMs, false, errorType);
639
+ throw error;
640
+ }
641
+ }));
642
+ console.error("Registered tool: zebpay_public_getAllTickers");
643
+ }
644
+ catch (error) {
645
+ console.error("Error registering public_getAllTickers:", error);
646
+ }
647
+ try {
648
+ server.tool("zebpay_public_getTicker", `Get ticker/price information for a specific trading pair on Zebpay exchange.
649
+
650
+ This tool retrieves the current market price, 24h statistics, and trading volume for a specific trading pair. No authentication required.
651
+
652
+ **When to use this tool:**
653
+ - User asks about current price of a specific cryptocurrency
654
+ - User wants to check market price or price changes for a symbol
655
+ - User asks "What's the price of BTC?" or "Show me ETH price"
656
+ - User wants ticker data for a specific trading pair
657
+
658
+ **Important notes:**
659
+ - Returns ticker data including last price, bid/ask, 24h high/low, volume
660
+ - Requires a specific symbol parameter
661
+ - Uses endpoint: api/v2/market/ticker?symbol={symbol}
662
+ - No authentication required - this is public market data
663
+
664
+ **Example use cases:**
665
+ 1. User says "What's the price of Bitcoin?" → Use symbol="BTC-INR"
666
+ 2. User asks "How much is ETH?" → Use symbol="ETH-INR"
667
+ 3. User wants BTC price → Use symbol="BTC-INR"
668
+
669
+ **Example request:**
670
+ {
671
+ "symbol": "BTC-INR"
672
+ }
673
+
674
+ **Example response:**
675
+ Returns ticker data with current price, 24h statistics, trading volume, and price changes for the specified symbol.
676
+
677
+ **Rate Limits:**
678
+ - Maximum 20 requests per second for public endpoints
679
+ - Burst limit: 40 requests
680
+ - Rate limit headers (X-RateLimit-*) are included in responses
681
+ - Public endpoints have higher rate limits than authenticated endpoints
682
+
683
+ **Related Tools:**
684
+ - Use zebpay_public_getAllTickers to get prices for all pairs at once
685
+ - Use zebpay_public_getOrderBook to see market depth and liquidity
686
+ - Use zebpay_public_getTrades to see recent trading activity
687
+ - Use before zebpay_spot_placeMarketOrder to check current market price`, {
688
+ symbol: symbolSchema.describe(`Trading pair symbol in format BASE-QUOTE (required).
689
+ Examples: "BTC-INR" (Bitcoin/Indian Rupees), "ETH-INR" (Ethereum/Indian Rupees), "BTC-USDT" (Bitcoin/Tether).
690
+ The symbol determines which market's ticker to retrieve.
691
+ Always use uppercase currency codes separated by a hyphen.`),
692
+ }, withLogging("zebpay_public_getTicker", config.logLevel, async ({ symbol }) => {
693
+ const correlationId = generateCorrelationId();
694
+ const startTime = Date.now();
695
+ try {
696
+ validateSymbol(symbol);
697
+ }
698
+ catch (error) {
699
+ metricsCollector.record("zebpay_public_getTicker", Date.now() - startTime, false, "validation_error");
700
+ throw error;
701
+ }
702
+ try {
703
+ const result = await publicClient.getTicker({ symbol: symbol.trim().toUpperCase() });
704
+ const durationMs = Date.now() - startTime;
705
+ metricsCollector.record("zebpay_public_getTicker", durationMs, true);
706
+ return formatTickerResponse(result, {
707
+ correlationId,
708
+ executionTimeMs: durationMs,
709
+ });
710
+ }
711
+ catch (error) {
712
+ const durationMs = Date.now() - startTime;
713
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
714
+ metricsCollector.record("zebpay_public_getTicker", durationMs, false, errorType);
715
+ if (error && typeof error === "object" && "code" in error) {
716
+ throw error;
717
+ }
718
+ throw createInternalError(`Failed to get ticker: ${error instanceof Error ? error.message : String(error)}`, { symbol });
719
+ }
720
+ }));
721
+ console.error("Registered tool: zebpay_public_getTicker");
722
+ }
723
+ catch (error) {
724
+ console.error("Error registering public_getTicker:", error);
725
+ }
726
+ try {
727
+ server.tool("zebpay_public_getOrderBook", `Get order book (market depth) for a trading pair on Zebpay exchange.
728
+
729
+ This tool retrieves the current buy and sell orders (bids and asks) for a specific trading pair, showing market depth and liquidity. No authentication required.
730
+
731
+ **When to use this tool:**
732
+ - User asks about order book or market depth
733
+ - User wants to see buy/sell orders for a trading pair
734
+ - User asks "Show me the order book for BTC" or "What's the market depth?"
735
+ - Before placing a large order to check liquidity
736
+
737
+ **Important notes:**
738
+ - Shows bids (buy orders) and asks (sell orders) with prices and quantities
739
+ - Can limit the depth using the limit parameter
740
+ - Uses endpoint: api/v2/market/orderbook?symbol={symbol}&limit={limit}
741
+ - No authentication required - this is public market data
742
+
743
+ **Example use cases:**
744
+ 1. User says "Show me order book for BTC" → Use symbol="BTC-INR"
745
+ 2. User wants to check market depth → Use symbol with optional limit
746
+ 3. User asks "What are the current buy and sell orders?" → Use symbol parameter
747
+
748
+ **Example request:**
749
+ {
750
+ "symbol": "BTC-INR",
751
+ "limit": 10
752
+ }
753
+
754
+ **Example response:**
755
+ Returns order book data with bids (buy orders) and asks (sell orders), showing price levels and quantities at each level.
756
+
757
+ **Rate Limits:**
758
+ - Maximum 20 requests per second for public endpoints
759
+ - Burst limit: 40 requests
760
+ - Rate limit headers (X-RateLimit-*) are included in responses
761
+ - Lower limit values return faster responses
762
+
763
+ **Related Tools:**
764
+ - Use zebpay_public_getTicker to get current price summary
765
+ - Use zebpay_public_getOrderBookTicker for lightweight best bid/ask prices
766
+ - Use zebpay_public_getTrades to see recent executed trades
767
+ - Use before placing large orders to assess market liquidity`, {
768
+ symbol: symbolSchema.describe(`Trading pair symbol in format BASE-QUOTE (required).
769
+ Examples: "BTC-INR" (Bitcoin/Indian Rupees), "ETH-INR" (Ethereum/Indian Rupees).
770
+ The symbol determines which market's order book to retrieve.
771
+ Always use uppercase currency codes separated by a hyphen.`),
772
+ limit: z.number().int().positive().optional().describe(`Optional limit for the number of price levels to return (positive integer).
773
+ **Default behavior:** If not provided, returns default depth (usually 100 levels).
774
+ **Performance:** Lower limits return fewer levels but provide faster responses.
775
+ **Examples:** 10 (top 10 bids/asks), 20 (top 20 bids/asks), 100 (full depth).
776
+ **Use case:** Use lower limits for quick overview, higher limits for detailed market depth analysis.`),
777
+ }, withLogging("zebpay_public_getOrderBook", config.logLevel, async ({ symbol, limit }) => {
778
+ const correlationId = generateCorrelationId();
779
+ const startTime = Date.now();
780
+ try {
781
+ validateSymbol(symbol);
782
+ }
783
+ catch (error) {
784
+ metricsCollector.record("zebpay_public_getOrderBook", Date.now() - startTime, false, "validation_error");
785
+ throw error;
786
+ }
787
+ try {
788
+ const result = await publicClient.getOrderBook({ symbol: symbol.trim().toUpperCase(), limit });
789
+ const durationMs = Date.now() - startTime;
790
+ metricsCollector.record("zebpay_public_getOrderBook", durationMs, true);
791
+ return formatOrderBookResponse(result, {
792
+ toolName: "zebpay_public_getOrderBook",
793
+ correlationId,
794
+ executionTimeMs: durationMs,
795
+ });
796
+ }
797
+ catch (error) {
798
+ const durationMs = Date.now() - startTime;
799
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
800
+ metricsCollector.record("zebpay_public_getOrderBook", durationMs, false, errorType);
801
+ if (error && typeof error === "object" && "code" in error) {
802
+ throw error;
803
+ }
804
+ throw createInternalError(`Failed to get order book: ${error instanceof Error ? error.message : String(error)}`, { symbol, limit });
805
+ }
806
+ }));
807
+ console.error("Registered tool: zebpay_public_getOrderBook");
808
+ }
809
+ catch (error) {
810
+ console.error("Error registering public_getOrderBook:", error);
811
+ }
812
+ try {
813
+ server.tool("zebpay_public_getOrderBookTicker", `Get order book ticker for a trading pair on Zebpay exchange.
814
+
815
+ This tool retrieves the best bid and ask prices (ticker) from the order book for a specific trading pair. This is a lightweight endpoint that returns only the top-of-book prices without the full order book depth. No authentication required.
816
+
817
+ **When to use this tool:**
818
+ - User asks about best bid/ask prices
819
+ - User wants to see the current best buy and sell prices
820
+ - User asks "What's the best bid and ask for BTC?" or "Show me the order book ticker"
821
+ - User wants a quick price check without full order book data
822
+
823
+ **Important notes:**
824
+ - Returns best bid (highest buy order) and best ask (lowest sell order) prices
825
+ - Lightweight endpoint - faster than full order book
826
+ - Uses endpoint: api/v2/market/orderbook/ticker?symbol={symbol}
827
+ - No authentication required - this is public market data
828
+
829
+ **Example use cases:**
830
+ 1. User says "Show me best bid/ask for BTC" → Use symbol="BTC-INR"
831
+ 2. User wants quick price check → Use this tool instead of full order book
832
+ 3. User asks "What are the current best prices?" → Use symbol parameter
833
+
834
+ **Example request:**
835
+ {
836
+ "symbol": "BTC-INR"
837
+ }
838
+
839
+ **Example response:**
840
+ Returns order book ticker data with best bid price, best ask price, and related market information.
841
+
842
+ **Rate Limits:**
843
+ - Maximum 20 requests per second for public endpoints
844
+ - Burst limit: 40 requests
845
+ - Rate limit headers (X-RateLimit-*) are included in responses
846
+ - This is a lightweight endpoint, faster than full order book
847
+
848
+ **Related Tools:**
849
+ - Use zebpay_public_getOrderBook for full market depth
850
+ - Use zebpay_public_getTicker for comprehensive price information
851
+ - Use for quick price checks before trading`, {
852
+ symbol: symbolSchema.describe(`Trading pair symbol in format BASE-QUOTE (required).
853
+ Examples: "BTC-INR" (Bitcoin/Indian Rupees), "ETH-INR" (Ethereum/Indian Rupees).
854
+ The symbol determines which market's order book ticker to retrieve.
855
+ Always use uppercase currency codes separated by a hyphen.`),
856
+ }, withLogging("zebpay_public_getOrderBookTicker", config.logLevel, async ({ symbol }) => {
857
+ const correlationId = generateCorrelationId();
858
+ const startTime = Date.now();
859
+ try {
860
+ validateSymbol(symbol);
861
+ }
862
+ catch (error) {
863
+ metricsCollector.record("zebpay_public_getOrderBookTicker", Date.now() - startTime, false, "validation_error");
864
+ throw error;
865
+ }
866
+ try {
867
+ const result = await publicClient.getOrderBookTicker({ symbol: symbol.trim().toUpperCase() });
868
+ const durationMs = Date.now() - startTime;
869
+ metricsCollector.record("zebpay_public_getOrderBookTicker", durationMs, true);
870
+ return formatResponse(result, {
871
+ toolName: "zebpay_public_getOrderBookTicker",
872
+ correlationId,
873
+ executionTimeMs: durationMs,
874
+ });
875
+ }
876
+ catch (error) {
877
+ const durationMs = Date.now() - startTime;
878
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
879
+ metricsCollector.record("zebpay_public_getOrderBookTicker", durationMs, false, errorType);
880
+ if (error && typeof error === "object" && "code" in error) {
881
+ throw error;
882
+ }
883
+ throw createInternalError(`Failed to get order book ticker: ${error instanceof Error ? error.message : String(error)}`, { symbol });
884
+ }
885
+ }));
886
+ console.error("Registered tool: zebpay_public_getOrderBookTicker");
887
+ }
888
+ catch (error) {
889
+ console.error("Error registering public_getOrderBookTicker:", error);
890
+ }
891
+ try {
892
+ server.tool("zebpay_public_getTrades", `Get recent trades/transactions for a trading pair on Zebpay exchange.
893
+
894
+ This tool retrieves the most recent trades executed on the exchange for a specific trading pair, showing price, quantity, and time. Supports pagination for accessing older trades. No authentication required.
895
+
896
+ **When to use this tool:**
897
+ - User asks about recent trades or transaction history
898
+ - User wants to see recent market activity
899
+ - User asks "Show me recent BTC trades" or "What trades happened?"
900
+ - User wants to analyze trading activity
901
+ - User wants to paginate through historical trades
902
+
903
+ **Important notes:**
904
+ - Shows recent trades with price, quantity, side (buy/sell), and timestamp
905
+ - Can limit the number of trades returned per page
906
+ - Supports pagination with the page parameter
907
+ - Uses endpoint: api/v2/market/trades?symbol={symbol}&limit={limit}&page={page}
908
+ - No authentication required - this is public market data
909
+
910
+ **Example use cases:**
911
+ 1. User says "Show me recent BTC trades" → Use symbol="BTC-INR"
912
+ 2. User wants to see market activity → Use symbol with optional limit
913
+ 3. User asks "What trades happened recently?" → Use symbol parameter
914
+ 4. User wants page 2 of trades → Use symbol="BTC-INR", limit=50, page=2
915
+
916
+ **Example request:**
917
+ {
918
+ "symbol": "BTC-INR",
919
+ "limit": 50,
920
+ "page": 1
921
+ }
922
+
923
+ **Example response:**
924
+ Returns array of recent trades with price, quantity, side (buy/sell), and timestamp for each trade.
925
+
926
+ **Rate Limits:**
927
+ - Maximum 20 requests per second for public endpoints
928
+ - Burst limit: 40 requests
929
+ - Rate limit headers (X-RateLimit-*) are included in responses
930
+ - Use pagination (page parameter) to access historical trades efficiently
931
+
932
+ **Related Tools:**
933
+ - Use zebpay_public_getTicker to get current price summary
934
+ - Use zebpay_public_getOrderBook to see pending orders
935
+ - Use zebpay_public_getKlines for historical price analysis`, {
936
+ symbol: symbolSchema.describe(`Trading pair symbol in format BASE-QUOTE (required).
937
+ Examples: "BTC-INR" (Bitcoin/Indian Rupees), "ETH-INR" (Ethereum/Indian Rupees).
938
+ The symbol determines which market's trades to retrieve.
939
+ Always use uppercase currency codes separated by a hyphen.`),
940
+ limit: z.number().int().positive().optional().describe(`Optional limit for the number of recent trades to return per page (positive integer).
941
+ **Default behavior:** If not provided, returns default number (usually 100 trades).
942
+ **Performance:** Lower limits return fewer trades but provide faster responses.
943
+ **Examples:** 10 (last 10 trades), 50 (last 50 trades), 100 (last 100 trades).
944
+ **Use case:** Combine with page parameter for pagination. Use lower limits for quick market activity overview.`),
945
+ page: z.number().int().positive().optional().describe(`Optional page number for pagination (positive integer, starts from 1).
946
+ **Default behavior:** If not provided, returns the first page of results (page 1).
947
+ **Pagination:** Use this to access older trades beyond the first page.
948
+ **Examples:** 1 (first page), 2 (second page), 3 (third page).
949
+ **Use case:** Combine with limit parameter to control how many trades per page. Use page=1 for most recent trades, higher page numbers for historical data.`),
950
+ }, withLogging("zebpay_public_getTrades", config.logLevel, async ({ symbol, limit, page }) => {
951
+ const correlationId = generateCorrelationId();
952
+ const startTime = Date.now();
953
+ try {
954
+ validateSymbol(symbol);
955
+ }
956
+ catch (error) {
957
+ metricsCollector.record("zebpay_public_getTrades", Date.now() - startTime, false, "validation_error");
958
+ throw error;
959
+ }
960
+ try {
961
+ const result = await publicClient.getTrades({ symbol: symbol.trim().toUpperCase(), limit, page });
962
+ const durationMs = Date.now() - startTime;
963
+ metricsCollector.record("zebpay_public_getTrades", durationMs, true);
964
+ return formatTradesResponse(result, {
965
+ toolName: "zebpay_public_getTrades",
966
+ correlationId,
967
+ executionTimeMs: durationMs,
968
+ });
969
+ }
970
+ catch (error) {
971
+ const durationMs = Date.now() - startTime;
972
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
973
+ metricsCollector.record("zebpay_public_getTrades", durationMs, false, errorType);
974
+ if (error && typeof error === "object" && "code" in error) {
975
+ throw error;
976
+ }
977
+ throw createInternalError(`Failed to get trades: ${error instanceof Error ? error.message : String(error)}`, { symbol, limit, page });
978
+ }
979
+ }));
980
+ console.error("Registered tool: zebpay_public_getTrades");
981
+ }
982
+ catch (error) {
983
+ console.error("Error registering public_getTrades:", error);
984
+ }
985
+ try {
986
+ server.tool("zebpay_public_getKlines", `Get candlestick/K-line data for a trading pair on Zebpay exchange.
987
+
988
+ This tool retrieves historical price data in candlestick format (OHLCV: Open, High, Low, Close, Volume) for charting and analysis. Requires a time range to be specified. Supports optional filtering by category. No authentication required.
989
+
990
+ **When to use this tool:**
991
+ - User asks about price history or charts
992
+ - User wants to analyze price trends over time
993
+ - User asks "Show me BTC price history" or "What was the price last week?"
994
+ - User wants candlestick data for technical analysis
995
+ - User wants spot or futures market data
996
+
997
+ **Important notes:**
998
+ - Returns OHLCV (Open, High, Low, Close, Volume) data
999
+ - Supports different time intervals (1m, 5m, 15m, 1h, 1d, etc.)
1000
+ - Requires startTime and endTime to specify the time range (in seconds since epoch)
1001
+ - Supports optional category parameter to specify market type (e.g., "spot")
1002
+ - Uses endpoint: api/v2/market/klines?symbol={symbol}&interval={interval}&startTime={startTime}&endTime={endTime}&category={category}
1003
+ - No authentication required - this is public market data
1004
+
1005
+ **Example use cases:**
1006
+ 1. User says "Show me BTC price history" → Use symbol="BTC-INR", interval="1h", startTime and endTime
1007
+ 2. User wants daily charts → Use interval="1d" with time range
1008
+ 3. User asks "What was the price last week?" → Use interval="1d" with time range
1009
+ 4. User wants spot market data → Use category="spot"
1010
+
1011
+ **Example request:**
1012
+ {
1013
+ "symbol": "BTC-INR",
1014
+ "interval": "15m",
1015
+ "startTime": 1756634020,
1016
+ "endTime": 1756740279,
1017
+ "category": "spot"
1018
+ }
1019
+
1020
+ **Example response:**
1021
+ Returns array of candlestick data with open, high, low, close prices and volume for each time period.
1022
+
1023
+ **Rate Limits:**
1024
+ - Maximum 20 requests per second for public endpoints
1025
+ - Burst limit: 40 requests
1026
+ - Rate limit headers (X-RateLimit-*) are included in responses
1027
+ - Larger time ranges may require multiple requests with pagination
1028
+
1029
+ **Related Tools:**
1030
+ - Use zebpay_public_getTicker for current price information
1031
+ - Use zebpay_public_getTrades to see recent trading activity
1032
+ - Use zebpay_public_getOrderBook to analyze current market depth
1033
+ - Combine with zebpay_public_getTicker for comprehensive market analysis`, {
1034
+ symbol: symbolSchema.describe(`Trading pair symbol in format BASE-QUOTE (required).
1035
+ Examples: "BTC-INR" (Bitcoin/Indian Rupees), "ETH-INR" (Ethereum/Indian Rupees).
1036
+ The symbol determines which market's data to retrieve.
1037
+ Always use uppercase currency codes separated by a hyphen.`),
1038
+ interval: z.string().min(1).describe(`Time interval for each candlestick (required).
1039
+ Common intervals: "1m" (1 minute), "5m" (5 minutes), "15m" (15 minutes), "1h" (1 hour), "4h" (4 hours), "1d" (1 day), "1w" (1 week).
1040
+ The interval determines the granularity of the price data.
1041
+ Examples: "1m" for minute-by-minute data, "1d" for daily data.`),
1042
+ startTime: z.number().describe(`Start timestamp in seconds since epoch (required).
1043
+ Returns candlesticks starting from this time.
1044
+ Use with endTime to get data for a specific time range.
1045
+ Example: 1756634020`),
1046
+ endTime: z.number().describe(`End timestamp in seconds since epoch (required).
1047
+ Returns candlesticks up to this time.
1048
+ Use with startTime to get data for a specific time range.
1049
+ Example: 1756740279`),
1050
+ limit: z.number().int().positive().optional().describe(`Optional limit for the number of candlesticks to return (positive integer).
1051
+ **Default behavior:** If not provided, returns default number (usually 500 candlesticks).
1052
+ **Performance:** Lower limits return fewer data points but provide faster responses.
1053
+ **Examples:** 100 (last 100 candles), 500 (last 500 candles).
1054
+ **Use case:** Use lower limits for quick price history overview, higher limits for detailed technical analysis. Consider time range (startTime/endTime) when setting limit.`),
1055
+ category: z.string().optional().describe(`Optional category parameter to specify market type (string).
1056
+ **Default behavior:** If not provided, returns default market data.
1057
+ **Common values:** "spot" (spot trading market).
1058
+ **Use case:** Use this to filter data by market category. For spot trading analysis, use category="spot".`),
1059
+ }, withLogging("zebpay_public_getKlines", config.logLevel, async ({ symbol, interval, limit, startTime, endTime, category }) => {
1060
+ const correlationId = generateCorrelationId();
1061
+ const executionStartTime = Date.now();
1062
+ try {
1063
+ validateSymbol(symbol);
1064
+ }
1065
+ catch (error) {
1066
+ metricsCollector.record("zebpay_public_getKlines", Date.now() - executionStartTime, false, "validation_error");
1067
+ throw error;
1068
+ }
1069
+ try {
1070
+ const result = await publicClient.getKlines({
1071
+ symbol: symbol.trim().toUpperCase(),
1072
+ interval,
1073
+ startTime,
1074
+ endTime,
1075
+ limit,
1076
+ category,
1077
+ });
1078
+ const durationMs = Date.now() - executionStartTime;
1079
+ metricsCollector.record("zebpay_public_getKlines", durationMs, true);
1080
+ return formatKlinesResponse(result, {
1081
+ toolName: "zebpay_public_getKlines",
1082
+ correlationId,
1083
+ executionTimeMs: durationMs,
1084
+ });
1085
+ }
1086
+ catch (error) {
1087
+ const durationMs = Date.now() - executionStartTime;
1088
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
1089
+ metricsCollector.record("zebpay_public_getKlines", durationMs, false, errorType);
1090
+ if (error && typeof error === "object" && "code" in error) {
1091
+ throw error;
1092
+ }
1093
+ throw createInternalError(`Failed to get klines: ${error instanceof Error ? error.message : String(error)}`, { symbol, interval, startTime, endTime });
1094
+ }
1095
+ }));
1096
+ console.error("Registered tool: zebpay_public_getKlines");
1097
+ }
1098
+ catch (error) {
1099
+ console.error("Error registering public_getKlines:", error);
1100
+ }
1101
+ try {
1102
+ server.tool("zebpay_public_getExchangeInfo", `Get exchange information including trading rules, symbols, and filters.
1103
+
1104
+ This tool retrieves general information about the Zebpay exchange, including available trading pairs, trading rules, order limits, and other exchange metadata. No authentication required.
1105
+
1106
+ **When to use this tool:**
1107
+ - User asks about available trading pairs
1108
+ - User wants to know exchange rules or limits
1109
+ - User asks "What pairs can I trade?" or "What are the trading rules?"
1110
+ - User needs to check symbol information before trading
1111
+
1112
+ **Important notes:**
1113
+ - Returns exchange-wide information including all trading pairs
1114
+ - Includes trading rules, filters, and limits
1115
+ - No authentication required - this is public exchange information
1116
+
1117
+ **Example use cases:**
1118
+ 1. User says "What pairs are available?" → Call without parameters
1119
+ 2. User wants to check trading rules → Call to get exchange info
1120
+ 3. User asks "What can I trade?" → Use this tool
1121
+
1122
+ **Example request:**
1123
+ {}
1124
+
1125
+ **Example response:**
1126
+ Returns exchange information including available symbols, trading rules, order limits, and other exchange metadata.
1127
+
1128
+ **Rate Limits:**
1129
+ - Maximum 20 requests per second for public endpoints
1130
+ - Burst limit: 40 requests
1131
+ - Rate limit headers (X-RateLimit-*) are included in responses
1132
+ - This data changes infrequently, consider caching results
1133
+
1134
+ **Related Tools:**
1135
+ - Use zebpay_public_getCurrencies to see supported currencies
1136
+ - Use zebpay_public_getAllTickers to see all available trading pairs
1137
+ - Access exchange info as a resource via zebpay://exchange/info`, {}, withLogging("zebpay_public_getExchangeInfo", config.logLevel, async () => {
1138
+ const correlationId = generateCorrelationId();
1139
+ const startTime = Date.now();
1140
+ try {
1141
+ const result = await publicClient.getExchangeInfo();
1142
+ const durationMs = Date.now() - startTime;
1143
+ metricsCollector.record("zebpay_public_getExchangeInfo", durationMs, true);
1144
+ return formatResponse(result, {
1145
+ toolName: "zebpay_public_getExchangeInfo",
1146
+ correlationId,
1147
+ executionTimeMs: durationMs,
1148
+ });
1149
+ }
1150
+ catch (error) {
1151
+ const durationMs = Date.now() - startTime;
1152
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
1153
+ metricsCollector.record("zebpay_public_getExchangeInfo", durationMs, false, errorType);
1154
+ if (error && typeof error === "object" && "code" in error) {
1155
+ throw error;
1156
+ }
1157
+ throw createInternalError(`Failed to get exchange info: ${error instanceof Error ? error.message : String(error)}`, {});
1158
+ }
1159
+ }));
1160
+ console.error("Registered tool: zebpay_public_getExchangeInfo");
1161
+ }
1162
+ catch (error) {
1163
+ console.error("Error registering public_getExchangeInfo:", error);
1164
+ }
1165
+ try {
1166
+ server.tool("zebpay_public_getCurrencies", `Get list of supported currencies and tokens on Zebpay exchange.
1167
+
1168
+ This tool retrieves a comprehensive list of all supported currencies and tokens, including their details such as precision, type (crypto/fiat), withdrawal/deposit settings, supported blockchain chains, fees, and limits. No authentication required.
1169
+
1170
+ **When to use this tool:**
1171
+ - User asks about supported currencies or tokens
1172
+ - User wants to see what currencies are available
1173
+ - User asks "What currencies does Zebpay support?" or "List all currencies"
1174
+ - User needs to check withdrawal/deposit limits or fees
1175
+ - User wants to verify if a specific currency is supported
1176
+ - User needs information about supported blockchain chains for a currency
1177
+
1178
+ **Important notes:**
1179
+ - Returns detailed information about each currency including:
1180
+ - Currency code, name, and full name
1181
+ - Precision and type (crypto/fiat)
1182
+ - Withdrawal and deposit settings
1183
+ - Supported blockchain chains with fees and limits
1184
+ - Contract addresses for tokens
1185
+ - Address validation regex patterns
1186
+ - Uses endpoint: api/v2/ex/currencies
1187
+ - No authentication required - this is public exchange information
1188
+
1189
+ **Example use cases:**
1190
+ 1. User says "What currencies are supported?" → Call without parameters
1191
+ 2. User wants to check BTC details → Use this tool to find BTC information
1192
+ 3. User asks "Can I deposit ETH?" → Use this tool to check ETH deposit settings
1193
+ 4. User wants withdrawal fees → Use this tool to see fees for each chain
1194
+ 5. User asks "What chains support USDT?" → Use this tool to see USDT chain details
1195
+
1196
+ **Example request:**
1197
+ {}
1198
+
1199
+ **Example response:**
1200
+ Returns array of currency objects with details including:
1201
+ - currency: Currency code (e.g., "BTC")
1202
+ - name: Currency name
1203
+ - fullName: Full currency name (e.g., "Bitcoin")
1204
+ - precision: Decimal precision
1205
+ - type: Currency type (e.g., "crypto")
1206
+ - isDebitEnabled: Whether debit is enabled
1207
+ - chains: Array of supported blockchain chains with:
1208
+ - chainName: Name of the blockchain
1209
+ - withdrawalMinSize: Minimum withdrawal amount
1210
+ - depositMinSize: Minimum deposit amount
1211
+ - withdrawalFee: Fee for withdrawals
1212
+ - isWithdrawEnabled: Whether withdrawals are enabled
1213
+ - isDepositEnabled: Whether deposits are enabled
1214
+ - contractAddress: Contract address (for tokens)
1215
+ - withdrawPrecision: Withdrawal precision
1216
+ - maxWithdraw: Maximum withdrawal amount
1217
+ - maxDeposit: Maximum deposit amount
1218
+ - needTag: Whether tag/memo is required
1219
+ - chainId: Chain identifier
1220
+ - AddressRegex: Address validation regex pattern
1221
+
1222
+ **Rate Limits:**
1223
+ - Maximum 20 requests per second for public endpoints
1224
+ - Burst limit: 40 requests
1225
+ - Rate limit headers (X-RateLimit-*) are included in responses
1226
+ - This data changes infrequently, consider caching results
1227
+
1228
+ **Related Tools:**
1229
+ - Use zebpay_public_getExchangeInfo to see trading pairs and rules
1230
+ - Access currencies as a resource via zebpay://currencies`, {}, withLogging("zebpay_public_getCurrencies", config.logLevel, async () => {
1231
+ const correlationId = generateCorrelationId();
1232
+ const startTime = Date.now();
1233
+ try {
1234
+ const result = await publicClient.getCurrencies();
1235
+ const durationMs = Date.now() - startTime;
1236
+ metricsCollector.record("zebpay_public_getCurrencies", durationMs, true);
1237
+ return formatResponse(result, {
1238
+ toolName: "zebpay_public_getCurrencies",
1239
+ correlationId,
1240
+ executionTimeMs: durationMs,
1241
+ });
1242
+ }
1243
+ catch (error) {
1244
+ const durationMs = Date.now() - startTime;
1245
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
1246
+ metricsCollector.record("zebpay_public_getCurrencies", durationMs, false, errorType);
1247
+ if (error && typeof error === "object" && "code" in error) {
1248
+ throw error;
1249
+ }
1250
+ throw createInternalError(`Failed to get currencies: ${error instanceof Error ? error.message : String(error)}`, {});
1251
+ }
1252
+ }));
1253
+ console.error("Registered tool: zebpay_public_getCurrencies");
1254
+ }
1255
+ catch (error) {
1256
+ console.error("Error registering public_getCurrencies:", error);
1257
+ }
1258
+ // Spot private tools: cancel and query orders
1259
+ try {
1260
+ server.tool("zebpay_spot_cancelOrdersBySymbol", `Cancel all open orders for a given symbol on Zebpay spot exchange.
1261
+
1262
+ Endpoint: DELETE /api/v2/ex/orders?symbol=SYMBOL&timestamp=...`, {
1263
+ symbol: symbolSchema.describe(`Trading pair symbol in format BASE-QUOTE, e.g., "BTC-INR".`),
1264
+ }, withLogging("zebpay_spot_cancelOrdersBySymbol", config.logLevel, async ({ symbol }) => {
1265
+ const correlationId = generateCorrelationId();
1266
+ const startTime = Date.now();
1267
+ if (!spot) {
1268
+ metricsCollector.record("zebpay_spot_cancelOrdersBySymbol", Date.now() - startTime, false, "missing_credentials");
1269
+ throw createInvalidParamsError("API credentials are required for this operation. Please provide ZEBPAY_API_KEY and ZEBPAY_API_SECRET.", {});
1270
+ }
1271
+ try {
1272
+ validateSymbol(symbol);
1273
+ }
1274
+ catch (error) {
1275
+ metricsCollector.record("zebpay_spot_cancelOrdersBySymbol", Date.now() - startTime, false, "validation_error");
1276
+ throw error;
1277
+ }
1278
+ try {
1279
+ const result = await spot.cancelOrdersBySymbol(symbol.trim().toUpperCase());
1280
+ const durationMs = Date.now() - startTime;
1281
+ metricsCollector.record("zebpay_spot_cancelOrdersBySymbol", durationMs, true);
1282
+ return formatResponse(result, {
1283
+ correlationId,
1284
+ executionTimeMs: durationMs,
1285
+ });
1286
+ }
1287
+ catch (error) {
1288
+ const durationMs = Date.now() - startTime;
1289
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
1290
+ metricsCollector.record("zebpay_spot_cancelOrdersBySymbol", durationMs, false, errorType);
1291
+ throw error;
1292
+ }
1293
+ }));
1294
+ console.error("Registered tool: zebpay_spot_cancelOrdersBySymbol");
1295
+ }
1296
+ catch (error) {
1297
+ console.error("Error registering zebpay_spot_cancelOrdersBySymbol:", error);
1298
+ }
1299
+ try {
1300
+ server.tool("zebpay_spot_cancelAllOrders", `Cancel all open user orders on Zebpay spot exchange.
1301
+
1302
+ Endpoint: DELETE /api/v2/ex/orders/cancelAll?timestamp=...`, {}, withLogging("zebpay_spot_cancelAllOrders", config.logLevel, async () => {
1303
+ const correlationId = generateCorrelationId();
1304
+ const startTime = Date.now();
1305
+ if (!spot) {
1306
+ metricsCollector.record("zebpay_spot_cancelAllOrders", Date.now() - startTime, false, "missing_credentials");
1307
+ throw createInvalidParamsError("API credentials are required for this operation. Please provide ZEBPAY_API_KEY and ZEBPAY_API_SECRET.", {});
1308
+ }
1309
+ try {
1310
+ const result = await spot.cancelAllOrders();
1311
+ const durationMs = Date.now() - startTime;
1312
+ metricsCollector.record("zebpay_spot_cancelAllOrders", durationMs, true);
1313
+ return formatResponse(result, {
1314
+ correlationId,
1315
+ executionTimeMs: durationMs,
1316
+ });
1317
+ }
1318
+ catch (error) {
1319
+ const durationMs = Date.now() - startTime;
1320
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
1321
+ metricsCollector.record("zebpay_spot_cancelAllOrders", durationMs, false, errorType);
1322
+ throw error;
1323
+ }
1324
+ }));
1325
+ console.error("Registered tool: zebpay_spot_cancelAllOrders");
1326
+ }
1327
+ catch (error) {
1328
+ console.error("Error registering zebpay_spot_cancelAllOrders:", error);
1329
+ }
1330
+ try {
1331
+ server.tool("zebpay_spot_getOrderFills", `Get trade fills for a specific order.
1332
+
1333
+ Endpoint: GET /api/v2/ex/order/fills/?orderId=ID&timestamp=...`, {
1334
+ orderId: z.string().min(1).describe(`Order ID as a non-empty string.`),
1335
+ }, withLogging("zebpay_spot_getOrderFills", config.logLevel, async ({ orderId }) => {
1336
+ const correlationId = generateCorrelationId();
1337
+ const startTime = Date.now();
1338
+ if (!spot) {
1339
+ metricsCollector.record("zebpay_spot_getOrderFills", Date.now() - startTime, false, "missing_credentials");
1340
+ throw createInvalidParamsError("API credentials are required for this operation. Please provide ZEBPAY_API_KEY and ZEBPAY_API_SECRET.", {});
1341
+ }
1342
+ if (!orderId || typeof orderId !== "string" || orderId.trim().length === 0) {
1343
+ metricsCollector.record("zebpay_spot_getOrderFills", Date.now() - startTime, false, "validation_error");
1344
+ throw createInvalidParamsError("Invalid orderId: must be a non-empty string.", { orderId });
1345
+ }
1346
+ try {
1347
+ const result = await spot.getOrderFills(orderId.trim());
1348
+ const durationMs = Date.now() - startTime;
1349
+ metricsCollector.record("zebpay_spot_getOrderFills", durationMs, true);
1350
+ return formatResponse(result, {
1351
+ correlationId,
1352
+ executionTimeMs: durationMs,
1353
+ });
1354
+ }
1355
+ catch (error) {
1356
+ const durationMs = Date.now() - startTime;
1357
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
1358
+ metricsCollector.record("zebpay_spot_getOrderFills", durationMs, false, errorType);
1359
+ throw error;
1360
+ }
1361
+ }));
1362
+ console.error("Registered tool: zebpay_spot_getOrderFills");
1363
+ }
1364
+ catch (error) {
1365
+ console.error("Error registering zebpay_spot_getOrderFills:", error);
1366
+ }
1367
+ try {
1368
+ server.tool("zebpay_spot_getOrders", `Get orders with optional filtering from Zebpay spot exchange.
1369
+
1370
+ This tool retrieves a list of orders based on the specified symbol and optional filters such as status, pagination, and time range.
1371
+
1372
+ **When to use this tool:**
1373
+ - User wants to see their active, filled, or cancelled orders
1374
+ - User needs to check order history for a specific trading pair
1375
+ - User wants to paginate through their order list
1376
+ - User needs to filter orders by time range
1377
+
1378
+ **Important notes:**
1379
+ - The symbol parameter is required
1380
+ - Default status filter is ACTIVE if not specified
1381
+ - Default pagination is page 1 with 20 records per page
1382
+ - Time filters (startTime, endTime) are in milliseconds
1383
+ - Returns paginated results with total count information
1384
+
1385
+ **Example use cases:**
1386
+ 1. User says "Show me my active BTC orders" → Use symbol="BTC-INR", status="ACTIVE"
1387
+ 2. User says "Get all my filled ETH orders" → Use symbol="ETH-INR", status="FILLED"
1388
+ 3. User wants to see order history with pagination → Use currentPage and pageSize parameters
1389
+
1390
+ **Example request:**
1391
+ {
1392
+ "symbol": "BTC-INR",
1393
+ "status": "ACTIVE",
1394
+ "currentPage": 1,
1395
+ "pageSize": 20
1396
+ }
1397
+
1398
+ **Example response:**
1399
+ Returns paginated order list with details including orderId, symbol, side, type, amount, price, status, filled amount, fees, and timestamps.
1400
+
1401
+ Endpoint: GET /api/v2/ex/orders`, {
1402
+ symbol: symbolSchema.describe(`Trading pair symbol in format BASE-QUOTE (required).
1403
+ Examples: "BTC-INR", "ETH-INR", "BTC-USDT".`),
1404
+ status: z.enum(["ACTIVE", "FILLED", "CANCELLED"]).optional().describe(`Order status filter (optional).
1405
+ - "ACTIVE": Open/pending orders
1406
+ - "FILLED": Completed orders
1407
+ - "CANCELLED": Cancelled orders
1408
+ Default: "ACTIVE" if not specified.`),
1409
+ currentPage: z.number().int().positive().optional().describe(`Current page number for pagination (optional).
1410
+ Must be a positive integer. Default: 1.`),
1411
+ pageSize: z.number().int().positive().optional().describe(`Number of records per page (optional).
1412
+ Must be a positive integer. Default: 20.`),
1413
+ startTime: z.number().int().positive().optional().describe(`Start time filter in milliseconds (optional).
1414
+ Unix timestamp in milliseconds. Only returns orders created after this time.`),
1415
+ endTime: z.number().int().positive().optional().describe(`End time filter in milliseconds (optional).
1416
+ Unix timestamp in milliseconds. Only returns orders created before this time.`),
1417
+ }, withLogging("zebpay_spot_getOrders", config.logLevel, async ({ symbol, status, currentPage, pageSize, startTime, endTime }) => {
1418
+ const correlationId = generateCorrelationId();
1419
+ const startTimeMs = Date.now();
1420
+ if (!spot) {
1421
+ metricsCollector.record("zebpay_spot_getOrders", Date.now() - startTimeMs, false, "missing_credentials");
1422
+ throw createInvalidParamsError("API credentials are required for this operation. Please provide ZEBPAY_API_KEY and ZEBPAY_API_SECRET.", {});
1423
+ }
1424
+ try {
1425
+ validateSymbol(symbol);
1426
+ }
1427
+ catch (error) {
1428
+ metricsCollector.record("zebpay_spot_getOrders", Date.now() - startTimeMs, false, "validation_error");
1429
+ throw error;
1430
+ }
1431
+ const params = {
1432
+ symbol: symbol.trim().toUpperCase(),
1433
+ };
1434
+ if (status) {
1435
+ params.status = status;
1436
+ }
1437
+ if (currentPage !== undefined) {
1438
+ params.currentPage = currentPage;
1439
+ }
1440
+ if (pageSize !== undefined) {
1441
+ params.pageSize = pageSize;
1442
+ }
1443
+ if (startTime !== undefined) {
1444
+ params.startTime = startTime;
1445
+ }
1446
+ if (endTime !== undefined) {
1447
+ params.endTime = endTime;
1448
+ }
1449
+ try {
1450
+ const result = await spot.getOrders(params);
1451
+ const durationMs = Date.now() - startTimeMs;
1452
+ metricsCollector.record("zebpay_spot_getOrders", durationMs, true);
1453
+ return formatResponse(result, {
1454
+ correlationId,
1455
+ executionTimeMs: durationMs,
1456
+ });
1457
+ }
1458
+ catch (error) {
1459
+ const durationMs = Date.now() - startTimeMs;
1460
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
1461
+ metricsCollector.record("zebpay_spot_getOrders", durationMs, false, errorType);
1462
+ throw error;
1463
+ }
1464
+ }));
1465
+ console.error("Registered tool: zebpay_spot_getOrders");
1466
+ }
1467
+ catch (error) {
1468
+ console.error("Error registering zebpay_spot_getOrders:", error);
1469
+ }
1470
+ try {
1471
+ server.tool("zebpay_spot_getOrderById", `Get order details by order ID.
1472
+
1473
+ Endpoint: GET /api/v2/ex/order?orderId=ID&timestamp=...`, {
1474
+ orderId: z.string().min(1).describe(`Order ID as a non-empty string.`),
1475
+ }, withLogging("zebpay_spot_getOrderById", config.logLevel, async ({ orderId }) => {
1476
+ const correlationId = generateCorrelationId();
1477
+ const startTime = Date.now();
1478
+ if (!spot) {
1479
+ metricsCollector.record("zebpay_spot_getOrderById", Date.now() - startTime, false, "missing_credentials");
1480
+ throw createInvalidParamsError("API credentials are required for this operation. Please provide ZEBPAY_API_KEY and ZEBPAY_API_SECRET.", {});
1481
+ }
1482
+ if (!orderId || typeof orderId !== "string" || orderId.trim().length === 0) {
1483
+ metricsCollector.record("zebpay_spot_getOrderById", Date.now() - startTime, false, "validation_error");
1484
+ throw createInvalidParamsError("Invalid orderId: must be a non-empty string.", { orderId });
1485
+ }
1486
+ try {
1487
+ const result = await spot.getOrderById(orderId.trim());
1488
+ const durationMs = Date.now() - startTime;
1489
+ metricsCollector.record("zebpay_spot_getOrderById", durationMs, true);
1490
+ return formatOrderResponse(result, {
1491
+ correlationId,
1492
+ executionTimeMs: durationMs,
1493
+ });
1494
+ }
1495
+ catch (error) {
1496
+ const durationMs = Date.now() - startTime;
1497
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
1498
+ metricsCollector.record("zebpay_spot_getOrderById", durationMs, false, errorType);
1499
+ throw error;
1500
+ }
1501
+ }));
1502
+ console.error("Registered tool: zebpay_spot_getOrderById");
1503
+ }
1504
+ catch (error) {
1505
+ console.error("Error registering zebpay_spot_getOrderById:", error);
1506
+ }
1507
+ try {
1508
+ server.tool("zebpay_spot_cancelOrderById", `Cancel a specific order by ID.
1509
+
1510
+ Endpoint: DELETE /api/v2/ex/order?orderId=ID&timestamp=...`, {
1511
+ orderId: z.string().min(1).describe(`Order ID as a non-empty string.`),
1512
+ }, withLogging("zebpay_spot_cancelOrderById", config.logLevel, async ({ orderId }) => {
1513
+ const correlationId = generateCorrelationId();
1514
+ const startTime = Date.now();
1515
+ if (!spot) {
1516
+ metricsCollector.record("zebpay_spot_cancelOrderById", Date.now() - startTime, false, "missing_credentials");
1517
+ throw createInvalidParamsError("API credentials are required for this operation. Please provide ZEBPAY_API_KEY and ZEBPAY_API_SECRET.", {});
1518
+ }
1519
+ if (!orderId || typeof orderId !== "string" || orderId.trim().length === 0) {
1520
+ metricsCollector.record("zebpay_spot_cancelOrderById", Date.now() - startTime, false, "validation_error");
1521
+ throw createInvalidParamsError("Invalid orderId: must be a non-empty string.", { orderId });
1522
+ }
1523
+ try {
1524
+ const result = await spot.cancelOrderById(orderId.trim());
1525
+ const durationMs = Date.now() - startTime;
1526
+ metricsCollector.record("zebpay_spot_cancelOrderById", durationMs, true);
1527
+ return formatResponse(result, {
1528
+ correlationId,
1529
+ executionTimeMs: durationMs,
1530
+ });
1531
+ }
1532
+ catch (error) {
1533
+ const durationMs = Date.now() - startTime;
1534
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
1535
+ metricsCollector.record("zebpay_spot_cancelOrderById", durationMs, false, errorType);
1536
+ throw error;
1537
+ }
1538
+ }));
1539
+ console.error("Registered tool: zebpay_spot_cancelOrderById");
1540
+ }
1541
+ catch (error) {
1542
+ console.error("Error registering zebpay_spot_cancelOrderById:", error);
1543
+ }
1544
+ }
1545
+ /**
1546
+ * Register ONLY public Zebpay tools for market data.
1547
+ * Use this for HTTP streamable transport where you want a public-only server.
1548
+ */
1549
+ export function registerPublicToolsOnly(server, publicClient, config) {
1550
+ console.error("Registering public market-data tools...");
1551
+ try {
1552
+ server.tool("zebpay_public_getAllTickers", `Get ticker/price information for all trading pairs on Zebpay exchange.
1553
+
1554
+ This tool retrieves the current market price, 24h statistics, and trading volume for all available trading pairs. No authentication required.
1555
+
1556
+ **When to use this tool:**
1557
+ - User asks to see all market prices
1558
+ - User wants to check prices for multiple trading pairs
1559
+ - User says "Show me all prices" or "List all tickers"
1560
+ - User wants an overview of all market data
1561
+
1562
+ **Important notes:**
1563
+ - Returns ticker data for all trading pairs including last price, bid/ask, 24h high/low, volume
1564
+ - Uses endpoint: api/v2/market/allTickers
1565
+ - No authentication required - this is public market data
1566
+
1567
+ **Example use cases:**
1568
+ 1. User says "Show me all prices" → Call without parameters
1569
+ 2. User wants market overview → Use this tool
1570
+ 3. User asks "List all tickers" → Use this tool
1571
+
1572
+ **Example request:**
1573
+ {}
1574
+
1575
+ **Example response:**
1576
+ Returns ticker data for all trading pairs with current price, 24h statistics, trading volume, and price changes.
1577
+
1578
+ **Rate Limits:**
1579
+ - Maximum 20 requests per second for public endpoints
1580
+ - Burst limit: 40 requests
1581
+ - Rate limit headers (X-RateLimit-*) are included in responses
1582
+ - Public endpoints have higher rate limits than authenticated endpoints
1583
+
1584
+ **Related Tools:**
1585
+ - Use zebpay_public_getTicker for a specific trading pair
1586
+ - Use zebpay_public_getOrderBook to see market depth
1587
+ - Access all tickers as a resource via zebpay://market/tickers`, {}, withLogging("zebpay_public_getAllTickers", config.logLevel, async () => {
1588
+ const correlationId = generateCorrelationId();
1589
+ const startTime = Date.now();
1590
+ try {
1591
+ const result = await publicClient.getAllTickers();
1592
+ const durationMs = Date.now() - startTime;
1593
+ metricsCollector.record("zebpay_public_getAllTickers", durationMs, true);
1594
+ return formatResponse(result, {
1595
+ toolName: "zebpay_public_getAllTickers",
1596
+ correlationId,
1597
+ executionTimeMs: durationMs,
1598
+ });
1599
+ }
1600
+ catch (error) {
1601
+ const durationMs = Date.now() - startTime;
1602
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
1603
+ metricsCollector.record("zebpay_public_getAllTickers", durationMs, false, errorType);
1604
+ throw error;
1605
+ }
1606
+ }));
1607
+ console.error("Registered tool: zebpay_public_getAllTickers");
1608
+ }
1609
+ catch (error) {
1610
+ console.error("Error registering public_getAllTickers:", error);
1611
+ }
1612
+ try {
1613
+ server.tool("zebpay_public_getTicker", `Get ticker/price information for a specific trading pair on Zebpay exchange.
1614
+
1615
+ This tool retrieves the current market price, 24h statistics, and trading volume for a specific trading pair. No authentication required.
1616
+
1617
+ **When to use this tool:**
1618
+ - User asks about current price of a specific cryptocurrency
1619
+ - User wants to check market price or price changes for a symbol
1620
+ - User asks "What's the price of BTC?" or "Show me ETH price"
1621
+ - User wants ticker data for a specific trading pair
1622
+
1623
+ **Important notes:**
1624
+ - Returns ticker data including last price, bid/ask, 24h high/low, volume
1625
+ - Requires a specific symbol parameter
1626
+ - Uses endpoint: api/v2/market/ticker?symbol={symbol}
1627
+ - No authentication required - this is public market data
1628
+
1629
+ **Example use cases:**
1630
+ 1. User says "What's the price of Bitcoin?" → Use symbol="BTC-INR"
1631
+ 2. User asks "How much is ETH?" → Use symbol="ETH-INR"
1632
+ 3. User wants BTC price → Use symbol="BTC-INR"
1633
+
1634
+ **Example request:**
1635
+ {
1636
+ "symbol": "BTC-INR"
1637
+ }
1638
+
1639
+ **Example response:**
1640
+ Returns ticker data with current price, 24h statistics, trading volume, and price changes for the specified symbol.
1641
+
1642
+ **Rate Limits:**
1643
+ - Maximum 20 requests per second for public endpoints
1644
+ - Burst limit: 40 requests
1645
+ - Rate limit headers (X-RateLimit-*) are included in responses
1646
+ - Public endpoints have higher rate limits than authenticated endpoints
1647
+
1648
+ **Related Tools:**
1649
+ - Use zebpay_public_getAllTickers to get prices for all pairs at once
1650
+ - Use zebpay_public_getOrderBook to see market depth and liquidity
1651
+ - Use zebpay_public_getTrades to see recent trading activity
1652
+ - Use before zebpay_spot_placeMarketOrder to check current market price`, {
1653
+ symbol: symbolSchema.describe(`Trading pair symbol in format BASE-QUOTE (required).
1654
+ Examples: "BTC-INR" (Bitcoin/Indian Rupees), "ETH-INR" (Ethereum/Indian Rupees), "BTC-USDT" (Bitcoin/Tether).
1655
+ The symbol determines which market's ticker to retrieve.
1656
+ Always use uppercase currency codes separated by a hyphen.`),
1657
+ }, withLogging("zebpay_public_getTicker", config.logLevel, async ({ symbol }) => {
1658
+ const correlationId = generateCorrelationId();
1659
+ const startTime = Date.now();
1660
+ try {
1661
+ validateSymbol(symbol);
1662
+ }
1663
+ catch (error) {
1664
+ metricsCollector.record("zebpay_public_getTicker", Date.now() - startTime, false, "validation_error");
1665
+ throw error;
1666
+ }
1667
+ try {
1668
+ const result = await publicClient.getTicker({ symbol: symbol.trim().toUpperCase() });
1669
+ const durationMs = Date.now() - startTime;
1670
+ metricsCollector.record("zebpay_public_getTicker", durationMs, true);
1671
+ return formatTickerResponse(result, {
1672
+ correlationId,
1673
+ executionTimeMs: durationMs,
1674
+ });
1675
+ }
1676
+ catch (error) {
1677
+ const durationMs = Date.now() - startTime;
1678
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
1679
+ metricsCollector.record("zebpay_public_getTicker", durationMs, false, errorType);
1680
+ if (error && typeof error === "object" && "code" in error) {
1681
+ throw error;
1682
+ }
1683
+ throw createInternalError(`Failed to get ticker: ${error instanceof Error ? error.message : String(error)}`, { symbol });
1684
+ }
1685
+ }));
1686
+ console.error("Registered tool: zebpay_public_getTicker");
1687
+ }
1688
+ catch (error) {
1689
+ console.error("Error registering public_getTicker:", error);
1690
+ }
1691
+ try {
1692
+ server.tool("zebpay_public_getOrderBook", `Get order book (market depth) for a trading pair on Zebpay exchange.
1693
+
1694
+ This tool retrieves the current buy and sell orders (bids and asks) for a specific trading pair, showing market depth and liquidity. No authentication required.
1695
+
1696
+ **When to use this tool:**
1697
+ - User asks about order book or market depth
1698
+ - User wants to see buy/sell orders for a trading pair
1699
+ - User asks "Show me the order book for BTC" or "What's the market depth?"
1700
+ - Before placing a large order to check liquidity
1701
+
1702
+ **Important notes:**
1703
+ - Shows bids (buy orders) and asks (sell orders) with prices and quantities
1704
+ - Can limit the depth using the limit parameter
1705
+ - Uses endpoint: api/v2/market/orderbook?symbol={symbol}&limit={limit}
1706
+ - No authentication required - this is public market data
1707
+
1708
+ **Example use cases:**
1709
+ 1. User says "Show me order book for BTC" → Use symbol="BTC-INR"
1710
+ 2. User wants to check market depth → Use symbol with optional limit
1711
+ 3. User asks "What are the current buy and sell orders?" → Use symbol parameter
1712
+
1713
+ **Example request:**
1714
+ {
1715
+ "symbol": "BTC-INR",
1716
+ "limit": 10
1717
+ }
1718
+
1719
+ **Example response:**
1720
+ Returns order book data with bids (buy orders) and asks (sell orders), showing price levels and quantities at each level.
1721
+
1722
+ **Rate Limits:**
1723
+ - Maximum 20 requests per second for public endpoints
1724
+ - Burst limit: 40 requests
1725
+ - Rate limit headers (X-RateLimit-*) are included in responses
1726
+ - Lower limit values return faster responses
1727
+
1728
+ **Related Tools:**
1729
+ - Use zebpay_public_getTicker to get current price summary
1730
+ - Use zebpay_public_getOrderBookTicker for lightweight best bid/ask prices
1731
+ - Use zebpay_public_getTrades to see recent executed trades
1732
+ - Use before placing large orders to assess market liquidity`, {
1733
+ symbol: symbolSchema.describe(`Trading pair symbol in format BASE-QUOTE (required).
1734
+ Examples: "BTC-INR" (Bitcoin/Indian Rupees), "ETH-INR" (Ethereum/Indian Rupees).
1735
+ The symbol determines which market's order book to retrieve.
1736
+ Always use uppercase currency codes separated by a hyphen.`),
1737
+ limit: z.number().int().positive().optional().describe(`Optional limit for the number of price levels to return (positive integer).
1738
+ **Default behavior:** If not provided, returns default depth (usually 100 levels).
1739
+ **Performance:** Lower limits return fewer levels but provide faster responses.
1740
+ **Examples:** 10 (top 10 bids/asks), 20 (top 20 bids/asks), 100 (full depth).
1741
+ **Use case:** Use lower limits for quick overview, higher limits for detailed market depth analysis.`),
1742
+ }, withLogging("zebpay_public_getOrderBook", config.logLevel, async ({ symbol, limit }) => {
1743
+ const correlationId = generateCorrelationId();
1744
+ const startTime = Date.now();
1745
+ try {
1746
+ validateSymbol(symbol);
1747
+ }
1748
+ catch (error) {
1749
+ metricsCollector.record("zebpay_public_getOrderBook", Date.now() - startTime, false, "validation_error");
1750
+ throw error;
1751
+ }
1752
+ try {
1753
+ const result = await publicClient.getOrderBook({ symbol: symbol.trim().toUpperCase(), limit });
1754
+ const durationMs = Date.now() - startTime;
1755
+ metricsCollector.record("zebpay_public_getOrderBook", durationMs, true);
1756
+ return formatOrderBookResponse(result, {
1757
+ toolName: "zebpay_public_getOrderBook",
1758
+ correlationId,
1759
+ executionTimeMs: durationMs,
1760
+ });
1761
+ }
1762
+ catch (error) {
1763
+ const durationMs = Date.now() - startTime;
1764
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
1765
+ metricsCollector.record("zebpay_public_getOrderBook", durationMs, false, errorType);
1766
+ if (error && typeof error === "object" && "code" in error) {
1767
+ throw error;
1768
+ }
1769
+ throw createInternalError(`Failed to get order book: ${error instanceof Error ? error.message : String(error)}`, { symbol, limit });
1770
+ }
1771
+ }));
1772
+ console.error("Registered tool: zebpay_public_getOrderBook");
1773
+ }
1774
+ catch (error) {
1775
+ console.error("Error registering public_getOrderBook:", error);
1776
+ }
1777
+ try {
1778
+ server.tool("zebpay_public_getOrderBookTicker", `Get order book ticker for a trading pair on Zebpay exchange.
1779
+
1780
+ This tool retrieves the best bid and ask prices (ticker) from the order book for a specific trading pair. This is a lightweight endpoint that returns only the top-of-book prices without the full order book depth. No authentication required.
1781
+
1782
+ **When to use this tool:**
1783
+ - User asks about best bid/ask prices
1784
+ - User wants to see the current best buy and sell prices
1785
+ - User asks "What's the best bid and ask for BTC?" or "Show me the order book ticker"
1786
+ - User wants a quick price check without full order book data
1787
+
1788
+ **Important notes:**
1789
+ - Returns best bid (highest buy order) and best ask (lowest sell order) prices
1790
+ - Lightweight endpoint - faster than full order book
1791
+ - Uses endpoint: api/v2/market/orderbook/ticker?symbol={symbol}
1792
+ - No authentication required - this is public market data
1793
+
1794
+ **Example use cases:**
1795
+ 1. User says "Show me best bid/ask for BTC" → Use symbol="BTC-INR"
1796
+ 2. User wants quick price check → Use this tool instead of full order book
1797
+ 3. User asks "What are the current best prices?" → Use symbol parameter
1798
+
1799
+ **Example request:**
1800
+ {
1801
+ "symbol": "BTC-INR"
1802
+ }
1803
+
1804
+ **Example response:**
1805
+ Returns order book ticker data with best bid price, best ask price, and related market information.
1806
+
1807
+ **Rate Limits:**
1808
+ - Maximum 20 requests per second for public endpoints
1809
+ - Burst limit: 40 requests
1810
+ - Rate limit headers (X-RateLimit-*) are included in responses
1811
+ - This is a lightweight endpoint, faster than full order book
1812
+
1813
+ **Related Tools:**
1814
+ - Use zebpay_public_getOrderBook for full market depth
1815
+ - Use zebpay_public_getTicker for comprehensive price information
1816
+ - Use for quick price checks before trading`, {
1817
+ symbol: symbolSchema.describe(`Trading pair symbol in format BASE-QUOTE (required).
1818
+ Examples: "BTC-INR" (Bitcoin/Indian Rupees), "ETH-INR" (Ethereum/Indian Rupees).
1819
+ The symbol determines which market's order book ticker to retrieve.
1820
+ Always use uppercase currency codes separated by a hyphen.`),
1821
+ }, withLogging("zebpay_public_getOrderBookTicker", config.logLevel, async ({ symbol }) => {
1822
+ const correlationId = generateCorrelationId();
1823
+ const startTime = Date.now();
1824
+ try {
1825
+ validateSymbol(symbol);
1826
+ }
1827
+ catch (error) {
1828
+ metricsCollector.record("zebpay_public_getOrderBookTicker", Date.now() - startTime, false, "validation_error");
1829
+ throw error;
1830
+ }
1831
+ try {
1832
+ const result = await publicClient.getOrderBookTicker({ symbol: symbol.trim().toUpperCase() });
1833
+ const durationMs = Date.now() - startTime;
1834
+ metricsCollector.record("zebpay_public_getOrderBookTicker", durationMs, true);
1835
+ return formatResponse(result, {
1836
+ toolName: "zebpay_public_getOrderBookTicker",
1837
+ correlationId,
1838
+ executionTimeMs: durationMs,
1839
+ });
1840
+ }
1841
+ catch (error) {
1842
+ const durationMs = Date.now() - startTime;
1843
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
1844
+ metricsCollector.record("zebpay_public_getOrderBookTicker", durationMs, false, errorType);
1845
+ if (error && typeof error === "object" && "code" in error) {
1846
+ throw error;
1847
+ }
1848
+ throw createInternalError(`Failed to get order book ticker: ${error instanceof Error ? error.message : String(error)}`, { symbol });
1849
+ }
1850
+ }));
1851
+ console.error("Registered tool: zebpay_public_getOrderBookTicker");
1852
+ }
1853
+ catch (error) {
1854
+ console.error("Error registering public_getOrderBookTicker:", error);
1855
+ }
1856
+ try {
1857
+ server.tool("zebpay_public_getTrades", `Get recent trades/transactions for a trading pair on Zebpay exchange.
1858
+
1859
+ This tool retrieves the most recent trades executed on the exchange for a specific trading pair, showing price, quantity, and time. Supports pagination for accessing older trades. No authentication required.
1860
+
1861
+ **When to use this tool:**
1862
+ - User asks about recent trades or transaction history
1863
+ - User wants to see recent market activity
1864
+ - User asks "Show me recent BTC trades" or "What trades happened?"
1865
+ - User wants to analyze trading activity
1866
+ - User wants to paginate through historical trades
1867
+
1868
+ **Important notes:**
1869
+ - Shows recent trades with price, quantity, side (buy/sell), and timestamp
1870
+ - Can limit the number of trades returned per page
1871
+ - Supports pagination with the page parameter
1872
+ - Uses endpoint: api/v2/market/trades?symbol={symbol}&limit={limit}&page={page}
1873
+ - No authentication required - this is public market data
1874
+
1875
+ **Example use cases:**
1876
+ 1. User says "Show me recent BTC trades" → Use symbol="BTC-INR"
1877
+ 2. User wants to see market activity → Use symbol with optional limit
1878
+ 3. User asks "What trades happened recently?" → Use symbol parameter
1879
+ 4. User wants page 2 of trades → Use symbol="BTC-INR", limit=50, page=2
1880
+
1881
+ **Example request:**
1882
+ {
1883
+ "symbol": "BTC-INR",
1884
+ "limit": 50,
1885
+ "page": 1
1886
+ }
1887
+
1888
+ **Example response:**
1889
+ Returns array of recent trades with price, quantity, side (buy/sell), and timestamp for each trade.
1890
+
1891
+ **Rate Limits:**
1892
+ - Maximum 20 requests per second for public endpoints
1893
+ - Burst limit: 40 requests
1894
+ - Rate limit headers (X-RateLimit-*) are included in responses
1895
+ - Use pagination (page parameter) to access historical trades efficiently
1896
+
1897
+ **Related Tools:**
1898
+ - Use zebpay_public_getTicker to get current price summary
1899
+ - Use zebpay_public_getOrderBook to see pending orders
1900
+ - Use zebpay_public_getKlines for historical price analysis`, {
1901
+ symbol: symbolSchema.describe(`Trading pair symbol in format BASE-QUOTE (required).
1902
+ Examples: "BTC-INR" (Bitcoin/Indian Rupees), "ETH-INR" (Ethereum/Indian Rupees).
1903
+ The symbol determines which market's trades to retrieve.
1904
+ Always use uppercase currency codes separated by a hyphen.`),
1905
+ limit: z.number().int().positive().optional().describe(`Optional limit for the number of recent trades to return per page (positive integer).
1906
+ **Default behavior:** If not provided, returns default number (usually 100 trades).
1907
+ **Performance:** Lower limits return fewer trades but provide faster responses.
1908
+ **Examples:** 10 (last 10 trades), 50 (last 50 trades), 100 (last 100 trades).
1909
+ **Use case:** Combine with page parameter for pagination. Use lower limits for quick market activity overview.`),
1910
+ page: z.number().int().positive().optional().describe(`Optional page number for pagination (positive integer, starts from 1).
1911
+ **Default behavior:** If not provided, returns the first page of results (page 1).
1912
+ **Pagination:** Use this to access older trades beyond the first page.
1913
+ **Examples:** 1 (first page), 2 (second page), 3 (third page).
1914
+ **Use case:** Combine with limit parameter to control how many trades per page. Use page=1 for most recent trades, higher page numbers for historical data.`),
1915
+ }, withLogging("zebpay_public_getTrades", config.logLevel, async ({ symbol, limit, page }) => {
1916
+ const correlationId = generateCorrelationId();
1917
+ const startTime = Date.now();
1918
+ try {
1919
+ validateSymbol(symbol);
1920
+ }
1921
+ catch (error) {
1922
+ metricsCollector.record("zebpay_public_getTrades", Date.now() - startTime, false, "validation_error");
1923
+ throw error;
1924
+ }
1925
+ try {
1926
+ const result = await publicClient.getTrades({ symbol: symbol.trim().toUpperCase(), limit, page });
1927
+ const durationMs = Date.now() - startTime;
1928
+ metricsCollector.record("zebpay_public_getTrades", durationMs, true);
1929
+ return formatTradesResponse(result, {
1930
+ toolName: "zebpay_public_getTrades",
1931
+ correlationId,
1932
+ executionTimeMs: durationMs,
1933
+ });
1934
+ }
1935
+ catch (error) {
1936
+ const durationMs = Date.now() - startTime;
1937
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
1938
+ metricsCollector.record("zebpay_public_getTrades", durationMs, false, errorType);
1939
+ if (error && typeof error === "object" && "code" in error) {
1940
+ throw error;
1941
+ }
1942
+ throw createInternalError(`Failed to get trades: ${error instanceof Error ? error.message : String(error)}`, { symbol, limit, page });
1943
+ }
1944
+ }));
1945
+ console.error("Registered tool: zebpay_public_getTrades");
1946
+ }
1947
+ catch (error) {
1948
+ console.error("Error registering public_getTrades:", error);
1949
+ }
1950
+ try {
1951
+ server.tool("zebpay_public_getKlines", `Get candlestick/K-line data for a trading pair on Zebpay exchange.
1952
+
1953
+ This tool retrieves historical price data in candlestick format (OHLCV: Open, High, Low, Close, Volume) for charting and analysis. Requires a time range to be specified. Supports optional filtering by category. No authentication required.
1954
+
1955
+ **When to use this tool:**
1956
+ - User asks about price history or charts
1957
+ - User wants to analyze price trends over time
1958
+ - User asks "Show me BTC price history" or "What was the price last week?"
1959
+ - User wants candlestick data for technical analysis
1960
+ - User wants spot or futures market data
1961
+
1962
+ **Important notes:**
1963
+ - Returns OHLCV (Open, High, Low, Close, Volume) data
1964
+ - Supports different time intervals (1m, 5m, 15m, 1h, 1d, etc.)
1965
+ - Requires startTime and endTime to specify the time range (in seconds since epoch)
1966
+ - Supports optional category parameter to specify market type (e.g., "spot")
1967
+ - Uses endpoint: api/v2/market/klines?symbol={symbol}&interval={interval}&startTime={startTime}&endTime={endTime}&category={category}
1968
+ - No authentication required - this is public market data
1969
+
1970
+ **Example use cases:**
1971
+ 1. User says "Show me BTC price history" → Use symbol="BTC-INR", interval="1h", startTime and endTime
1972
+ 2. User wants daily charts → Use interval="1d" with time range
1973
+ 3. User asks "What was the price last week?" → Use interval="1d" with time range
1974
+ 4. User wants spot market data → Use category="spot"
1975
+
1976
+ **Example request:**
1977
+ {
1978
+ "symbol": "BTC-INR",
1979
+ "interval": "15m",
1980
+ "startTime": 1756634020,
1981
+ "endTime": 1756740279,
1982
+ "category": "spot"
1983
+ }
1984
+
1985
+ **Example response:**
1986
+ Returns array of candlestick data with open, high, low, close prices and volume for each time period.
1987
+
1988
+ **Rate Limits:**
1989
+ - Maximum 20 requests per second for public endpoints
1990
+ - Burst limit: 40 requests
1991
+ - Rate limit headers (X-RateLimit-*) are included in responses
1992
+ - Larger time ranges may require multiple requests with pagination
1993
+
1994
+ **Related Tools:**
1995
+ - Use zebpay_public_getTicker for current price information
1996
+ - Use zebpay_public_getTrades to see recent trading activity
1997
+ - Use zebpay_public_getOrderBook to analyze current market depth
1998
+ - Combine with zebpay_public_getTicker for comprehensive market analysis`, {
1999
+ symbol: symbolSchema.describe(`Trading pair symbol in format BASE-QUOTE (required).
2000
+ Examples: "BTC-INR" (Bitcoin/Indian Rupees), "ETH-INR" (Ethereum/Indian Rupees).
2001
+ The symbol determines which market's data to retrieve.
2002
+ Always use uppercase currency codes separated by a hyphen.`),
2003
+ interval: z.string().min(1).describe(`Time interval for each candlestick (required).
2004
+ Common intervals: "1m" (1 minute), "5m" (5 minutes), "15m" (15 minutes), "1h" (1 hour), "4h" (4 hours), "1d" (1 day), "1w" (1 week).
2005
+ The interval determines the granularity of the price data.
2006
+ Examples: "1m" for minute-by-minute data, "1d" for daily data.`),
2007
+ startTime: z.number().describe(`Start timestamp in seconds since epoch (required).
2008
+ Returns candlesticks starting from this time.
2009
+ Use with endTime to get data for a specific time range.
2010
+ Example: 1756634020`),
2011
+ endTime: z.number().describe(`End timestamp in seconds since epoch (required).
2012
+ Returns candlesticks up to this time.
2013
+ Use with startTime to get data for a specific time range.
2014
+ Example: 1756740279`),
2015
+ limit: z.number().int().positive().optional().describe(`Optional limit for the number of candlesticks to return (positive integer).
2016
+ **Default behavior:** If not provided, returns default number (usually 500 candlesticks).
2017
+ **Performance:** Lower limits return fewer data points but provide faster responses.
2018
+ **Examples:** 100 (last 100 candles), 500 (last 500 candles).
2019
+ **Use case:** Use lower limits for quick price history overview, higher limits for detailed technical analysis. Consider time range (startTime/endTime) when setting limit.`),
2020
+ category: z.string().optional().describe(`Optional category parameter to specify market type (string).
2021
+ **Default behavior:** If not provided, returns default market data.
2022
+ **Common values:** "spot" (spot trading market).
2023
+ **Use case:** Use this to filter data by market category. For spot trading analysis, use category="spot".`),
2024
+ }, withLogging("zebpay_public_getKlines", config.logLevel, async ({ symbol, interval, limit, startTime, endTime, category }) => {
2025
+ const correlationId = generateCorrelationId();
2026
+ const executionStartTime = Date.now();
2027
+ try {
2028
+ validateSymbol(symbol);
2029
+ }
2030
+ catch (error) {
2031
+ metricsCollector.record("zebpay_public_getKlines", Date.now() - executionStartTime, false, "validation_error");
2032
+ throw error;
2033
+ }
2034
+ try {
2035
+ const result = await publicClient.getKlines({
2036
+ symbol: symbol.trim().toUpperCase(),
2037
+ interval,
2038
+ startTime,
2039
+ endTime,
2040
+ limit,
2041
+ category,
2042
+ });
2043
+ const durationMs = Date.now() - executionStartTime;
2044
+ metricsCollector.record("zebpay_public_getKlines", durationMs, true);
2045
+ return formatKlinesResponse(result, {
2046
+ toolName: "zebpay_public_getKlines",
2047
+ correlationId,
2048
+ executionTimeMs: durationMs,
2049
+ });
2050
+ }
2051
+ catch (error) {
2052
+ const durationMs = Date.now() - executionStartTime;
2053
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
2054
+ metricsCollector.record("zebpay_public_getKlines", durationMs, false, errorType);
2055
+ if (error && typeof error === "object" && "code" in error) {
2056
+ throw error;
2057
+ }
2058
+ throw createInternalError(`Failed to get klines: ${error instanceof Error ? error.message : String(error)}`, { symbol, interval, startTime, endTime });
2059
+ }
2060
+ }));
2061
+ console.error("Registered tool: zebpay_public_getKlines");
2062
+ }
2063
+ catch (error) {
2064
+ console.error("Error registering public_getKlines:", error);
2065
+ }
2066
+ try {
2067
+ server.tool("zebpay_public_getExchangeInfo", `Get exchange information including trading rules, symbols, and filters.
2068
+
2069
+ This tool retrieves general information about the Zebpay exchange, including available trading pairs, trading rules, order limits, and other exchange metadata. No authentication required.
2070
+
2071
+ **When to use this tool:**
2072
+ - User asks about available trading pairs
2073
+ - User wants to know exchange rules or limits
2074
+ - User asks "What pairs can I trade?" or "What are the trading rules?"
2075
+ - User needs to check symbol information before trading
2076
+
2077
+ **Important notes:**
2078
+ - Returns exchange-wide information including all trading pairs
2079
+ - Includes trading rules, filters, and limits
2080
+ - No authentication required - this is public exchange information
2081
+
2082
+ **Example use cases:**
2083
+ 1. User says "What pairs are available?" → Call without parameters
2084
+ 2. User wants to check trading rules → Call to get exchange info
2085
+ 3. User asks "What can I trade?" → Use this tool
2086
+
2087
+ **Example request:**
2088
+ {}
2089
+
2090
+ **Example response:**
2091
+ Returns exchange information including available symbols, trading rules, order limits, and other exchange metadata.
2092
+
2093
+ **Rate Limits:**
2094
+ - Maximum 20 requests per second for public endpoints
2095
+ - Burst limit: 40 requests
2096
+ - Rate limit headers (X-RateLimit-*) are included in responses
2097
+ - This data changes infrequently, consider caching results
2098
+
2099
+ **Related Tools:**
2100
+ - Use zebpay_public_getCurrencies to see supported currencies
2101
+ - Use zebpay_public_getAllTickers to see all available trading pairs
2102
+ - Access exchange info as a resource via zebpay://exchange/info`, {}, withLogging("zebpay_public_getExchangeInfo", config.logLevel, async () => {
2103
+ const correlationId = generateCorrelationId();
2104
+ const startTime = Date.now();
2105
+ try {
2106
+ const result = await publicClient.getExchangeInfo();
2107
+ const durationMs = Date.now() - startTime;
2108
+ metricsCollector.record("zebpay_public_getExchangeInfo", durationMs, true);
2109
+ return formatResponse(result, {
2110
+ toolName: "zebpay_public_getExchangeInfo",
2111
+ correlationId,
2112
+ executionTimeMs: durationMs,
2113
+ });
2114
+ }
2115
+ catch (error) {
2116
+ const durationMs = Date.now() - startTime;
2117
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
2118
+ metricsCollector.record("zebpay_public_getExchangeInfo", durationMs, false, errorType);
2119
+ if (error && typeof error === "object" && "code" in error) {
2120
+ throw error;
2121
+ }
2122
+ throw createInternalError(`Failed to get exchange info: ${error instanceof Error ? error.message : String(error)}`, {});
2123
+ }
2124
+ }));
2125
+ console.error("Registered tool: zebpay_public_getExchangeInfo");
2126
+ }
2127
+ catch (error) {
2128
+ console.error("Error registering public_getExchangeInfo:", error);
2129
+ }
2130
+ try {
2131
+ server.tool("zebpay_public_getCurrencies", `Get list of supported currencies and tokens on Zebpay exchange.
2132
+
2133
+ This tool retrieves a comprehensive list of all supported currencies and tokens, including their details such as precision, type (crypto/fiat), withdrawal/deposit settings, supported blockchain chains, fees, and limits. No authentication required.
2134
+
2135
+ **When to use this tool:**
2136
+ - User asks about supported currencies or tokens
2137
+ - User wants to see what currencies are available
2138
+ - User asks "What currencies does Zebpay support?" or "List all currencies"
2139
+ - User needs to check withdrawal/deposit limits or fees
2140
+ - User wants to verify if a specific currency is supported
2141
+ - User needs information about supported blockchain chains for a currency
2142
+
2143
+ **Important notes:**
2144
+ - Returns detailed information about each currency including:
2145
+ - Currency code, name, and full name
2146
+ - Precision and type (crypto/fiat)
2147
+ - Withdrawal and deposit settings
2148
+ - Supported blockchain chains with fees and limits
2149
+ - Contract addresses for tokens
2150
+ - Address validation regex patterns
2151
+ - Uses endpoint: api/v2/ex/currencies
2152
+ - No authentication required - this is public exchange information
2153
+
2154
+ **Example use cases:**
2155
+ 1. User says "What currencies are supported?" → Call without parameters
2156
+ 2. User wants to check BTC details → Use this tool to find BTC information
2157
+ 3. User asks "Can I deposit ETH?" → Use this tool to check ETH deposit settings
2158
+ 4. User wants withdrawal fees → Use this tool to see fees for each chain
2159
+ 5. User asks "What chains support USDT?" → Use this tool to see USDT chain details
2160
+
2161
+ **Example request:**
2162
+ {}
2163
+
2164
+ **Example response:**
2165
+ Returns array of currency objects with details including:
2166
+ - currency: Currency code (e.g., "BTC")
2167
+ - name: Currency name
2168
+ - fullName: Full currency name (e.g., "Bitcoin")
2169
+ - precision: Decimal precision
2170
+ - type: Currency type (e.g., "crypto")
2171
+ - isDebitEnabled: Whether debit is enabled
2172
+ - chains: Array of supported blockchain chains with:
2173
+ - chainName: Name of the blockchain
2174
+ - withdrawalMinSize: Minimum withdrawal amount
2175
+ - depositMinSize: Minimum deposit amount
2176
+ - withdrawalFee: Fee for withdrawals
2177
+ - isWithdrawEnabled: Whether withdrawals are enabled
2178
+ - isDepositEnabled: Whether deposits are enabled
2179
+ - contractAddress: Contract address (for tokens)
2180
+ - withdrawPrecision: Withdrawal precision
2181
+ - maxWithdraw: Maximum withdrawal amount
2182
+ - maxDeposit: Maximum deposit amount
2183
+ - needTag: Whether tag/memo is required
2184
+ - chainId: Chain identifier
2185
+ - AddressRegex: Address validation regex pattern
2186
+
2187
+ **Rate Limits:**
2188
+ - Maximum 20 requests per second for public endpoints
2189
+ - Burst limit: 40 requests
2190
+ - Rate limit headers (X-RateLimit-*) are included in responses
2191
+ - This data changes infrequently, consider caching results
2192
+
2193
+ **Related Tools:**
2194
+ - Use zebpay_public_getExchangeInfo to see trading pairs and rules
2195
+ - Access currencies as a resource via zebpay://currencies`, {}, withLogging("zebpay_public_getCurrencies", config.logLevel, async () => {
2196
+ const correlationId = generateCorrelationId();
2197
+ const startTime = Date.now();
2198
+ try {
2199
+ const result = await publicClient.getCurrencies();
2200
+ const durationMs = Date.now() - startTime;
2201
+ metricsCollector.record("zebpay_public_getCurrencies", durationMs, true);
2202
+ return formatResponse(result, {
2203
+ toolName: "zebpay_public_getCurrencies",
2204
+ correlationId,
2205
+ executionTimeMs: durationMs,
2206
+ });
2207
+ }
2208
+ catch (error) {
2209
+ const durationMs = Date.now() - startTime;
2210
+ const errorType = error && typeof error === "object" && "code" in error ? String(error.code) : "unknown";
2211
+ metricsCollector.record("zebpay_public_getCurrencies", durationMs, false, errorType);
2212
+ if (error && typeof error === "object" && "code" in error) {
2213
+ throw error;
2214
+ }
2215
+ throw createInternalError(`Failed to get currencies: ${error instanceof Error ? error.message : String(error)}`, {});
2216
+ }
2217
+ }));
2218
+ console.error("Registered tool: zebpay_public_getCurrencies");
2219
+ }
2220
+ catch (error) {
2221
+ console.error("Error registering public_getCurrencies:", error);
2222
+ }
2223
+ console.error("✓ Public-only tool registration complete.");
2224
+ }
2225
+ //# sourceMappingURL=tools_spot.js.map