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