@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.
- package/.env.example +14 -0
- package/README.md +223 -0
- package/dist/__tests__/errors.test.d.ts +5 -0
- package/dist/__tests__/errors.test.js +147 -0
- package/dist/__tests__/errors.test.js.map +1 -0
- package/dist/__tests__/prompts.test.d.ts +1 -0
- package/dist/__tests__/prompts.test.js +73 -0
- package/dist/__tests__/prompts.test.js.map +1 -0
- package/dist/__tests__/resources.test.d.ts +1 -0
- package/dist/__tests__/resources.test.js +79 -0
- package/dist/__tests__/resources.test.js.map +1 -0
- package/dist/__tests__/validation.test.d.ts +15 -0
- package/dist/__tests__/validation.test.js +64 -0
- package/dist/__tests__/validation.test.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.js +81 -0
- package/dist/config.js.map +1 -0
- package/dist/http/httpClient.d.ts +40 -0
- package/dist/http/httpClient.js +341 -0
- package/dist/http/httpClient.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/errors.d.ts +21 -0
- package/dist/mcp/errors.js +214 -0
- package/dist/mcp/errors.js.map +1 -0
- package/dist/mcp/logging.d.ts +21 -0
- package/dist/mcp/logging.js +241 -0
- package/dist/mcp/logging.js.map +1 -0
- package/dist/mcp/prompts.d.ts +9 -0
- package/dist/mcp/prompts.js +165 -0
- package/dist/mcp/prompts.js.map +1 -0
- package/dist/mcp/resources.d.ts +9 -0
- package/dist/mcp/resources.js +125 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/tools_futures.d.ts +5 -0
- package/dist/mcp/tools_futures.js +694 -0
- package/dist/mcp/tools_futures.js.map +1 -0
- package/dist/mcp/tools_spot.d.ts +11 -0
- package/dist/mcp/tools_spot.js +2225 -0
- package/dist/mcp/tools_spot.js.map +1 -0
- package/dist/private/FuturesClient.d.ts +57 -0
- package/dist/private/FuturesClient.js +181 -0
- package/dist/private/FuturesClient.js.map +1 -0
- package/dist/private/SpotClient.d.ts +44 -0
- package/dist/private/SpotClient.js +201 -0
- package/dist/private/SpotClient.js.map +1 -0
- package/dist/private/ZebpayAPI.d.ts +19 -0
- package/dist/private/ZebpayAPI.js +172 -0
- package/dist/private/ZebpayAPI.js.map +1 -0
- package/dist/public/PublicClient.d.ts +79 -0
- package/dist/public/PublicClient.js +283 -0
- package/dist/public/PublicClient.js.map +1 -0
- package/dist/public/PublicFuturesClient.d.ts +27 -0
- package/dist/public/PublicFuturesClient.js +187 -0
- package/dist/public/PublicFuturesClient.js.map +1 -0
- package/dist/security/credentials.d.ts +42 -0
- package/dist/security/credentials.js +80 -0
- package/dist/security/credentials.js.map +1 -0
- package/dist/security/signing.d.ts +33 -0
- package/dist/security/signing.js +56 -0
- package/dist/security/signing.js.map +1 -0
- package/dist/types/responses.d.ts +130 -0
- package/dist/types/responses.js +6 -0
- package/dist/types/responses.js.map +1 -0
- package/dist/utils/cache.d.ts +29 -0
- package/dist/utils/cache.js +72 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/fileLogger.d.ts +10 -0
- package/dist/utils/fileLogger.js +81 -0
- package/dist/utils/fileLogger.js.map +1 -0
- package/dist/utils/metrics.d.ts +35 -0
- package/dist/utils/metrics.js +94 -0
- package/dist/utils/metrics.js.map +1 -0
- package/dist/utils/responseFormatter.d.ts +93 -0
- package/dist/utils/responseFormatter.js +268 -0
- package/dist/utils/responseFormatter.js.map +1 -0
- package/dist/validation/schemas.d.ts +70 -0
- package/dist/validation/schemas.js +48 -0
- package/dist/validation/schemas.js.map +1 -0
- package/dist/validation/validators.d.ts +28 -0
- package/dist/validation/validators.js +129 -0
- package/dist/validation/validators.js.map +1 -0
- package/docs/LOGGING.md +371 -0
- package/docs/zebpay-ai-trading-beginner.png +0 -0
- package/mcp-config.json.example +20 -0
- package/package.json +54 -0
- package/scripts/README.md +103 -0
- package/scripts/clear-logs.js +52 -0
- package/scripts/log-stats.js +264 -0
- package/scripts/log-viewer.js +288 -0
- package/server.json +31 -0
- package/src/__tests__/errors.test.ts +180 -0
- package/src/__tests__/prompts.test.ts +89 -0
- package/src/__tests__/resources.test.ts +95 -0
- package/src/__tests__/validation.test.ts +88 -0
- package/src/config.ts +108 -0
- package/src/http/httpClient.ts +398 -0
- package/src/index.ts +71 -0
- package/src/mcp/errors.ts +262 -0
- package/src/mcp/logging.ts +284 -0
- package/src/mcp/prompts.ts +206 -0
- package/src/mcp/resources.ts +163 -0
- package/src/mcp/tools_futures.ts +874 -0
- package/src/mcp/tools_spot.ts +2702 -0
- package/src/private/FuturesClient.ts +189 -0
- package/src/private/SpotClient.ts +250 -0
- package/src/private/ZebpayAPI.ts +205 -0
- package/src/public/PublicClient.ts +381 -0
- package/src/public/PublicFuturesClient.ts +228 -0
- package/src/security/credentials.ts +114 -0
- package/src/security/signing.ts +98 -0
- package/src/types/responses.ts +146 -0
- package/src/utils/cache.ts +90 -0
- package/src/utils/fileLogger.ts +88 -0
- package/src/utils/metrics.ts +135 -0
- package/src/utils/responseFormatter.ts +361 -0
- package/src/validation/schemas.ts +66 -0
- package/src/validation/validators.ts +189 -0
- package/tsconfig.json +21 -0
package/.env.example
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
ZEBPAY_API_KEY=
|
|
2
|
+
ZEBPAY_API_SECRET=
|
|
3
|
+
|
|
4
|
+
# Required runtime configuration (no fallback defaults in code)
|
|
5
|
+
ZEBPAY_SPOT_BASE_URL=https://www.zebapi.com/api/v2
|
|
6
|
+
ZEBPAY_FUTURES_BASE_URL=https://futures-api.zebpay.com/api/v1
|
|
7
|
+
ZEBPAY_MARKET_BASE_URL=https://www.zebapi.com/api/v1/market
|
|
8
|
+
|
|
9
|
+
MCP_TRANSPORTS=stdio
|
|
10
|
+
LOG_LEVEL=info
|
|
11
|
+
HTTP_TIMEOUT_MS=15000
|
|
12
|
+
HTTP_RETRY_COUNT=2
|
|
13
|
+
|
|
14
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# Zebpay MCP Server
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Production-ready TypeScript **MCP (Model Context Protocol)** server for Zebpay spot and futures APIs, using **stdio transport only**.
|
|
6
|
+
Model Context Protocol (MCP) is a standard way for AI assistants to securely call tools and data sources.
|
|
7
|
+
It is designed for MCP clients like Cursor, Claude Desktop, and MCP Inspector that connect through local process execution.
|
|
8
|
+
The server provides both market-data and trading workflows with strict validation, safe error handling, and developer-friendly observability.
|
|
9
|
+
|
|
10
|
+
## What This Server Provides
|
|
11
|
+
|
|
12
|
+
- Public market data tools (spot + futures)
|
|
13
|
+
- Private trading/account tools (requires API credentials)
|
|
14
|
+
- MCP resources and prompts for agent workflows
|
|
15
|
+
- Request validation via Zod
|
|
16
|
+
- Structured error handling and optional file logging
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- Stdio-only MCP server for secure local integration with MCP clients
|
|
21
|
+
- Spot and futures API coverage for both public and authenticated operations
|
|
22
|
+
- HMAC-based authenticated request flow for private endpoints
|
|
23
|
+
- Input validation and normalized MCP error responses
|
|
24
|
+
- Outbound request retries and timeout controls
|
|
25
|
+
- Optional structured file logging for debugging and auditability
|
|
26
|
+
|
|
27
|
+
## Supported Tools
|
|
28
|
+
|
|
29
|
+
### Spot (Public + Private)
|
|
30
|
+
|
|
31
|
+
- Market data: tickers, order book, trades, klines, exchange info, currencies
|
|
32
|
+
- Trading: place market/limit orders, cancel orders, cancel by symbol, cancel all
|
|
33
|
+
- Account: balances, open orders, order history, trade history, exchange fee
|
|
34
|
+
- Example tool names:
|
|
35
|
+
- `zebpay_public_getTicker`
|
|
36
|
+
- `zebpay_public_getOrderBook`
|
|
37
|
+
- `zebpay_spot_placeMarketOrder`
|
|
38
|
+
- `zebpay_spot_placeLimitOrder`
|
|
39
|
+
- `zebpay_spot_getBalance`
|
|
40
|
+
|
|
41
|
+
### Futures (Public + Private)
|
|
42
|
+
|
|
43
|
+
- Market data: health status, markets, market info, order book, 24h ticker, aggregate trades
|
|
44
|
+
- Trading/account: wallet balance, place order, add/reduce margin, open orders, positions
|
|
45
|
+
- History: order history, linked orders, trade history, transaction history
|
|
46
|
+
- Example tool names:
|
|
47
|
+
- `zebpay_futures_public_getMarkets`
|
|
48
|
+
- `zebpay_futures_public_getOrderBook`
|
|
49
|
+
- `zebpay_futures_placeOrder`
|
|
50
|
+
- `zebpay_futures_getWalletBalance`
|
|
51
|
+
- `zebpay_futures_getPositions`
|
|
52
|
+
|
|
53
|
+
## Tech Stack
|
|
54
|
+
|
|
55
|
+
- Node.js (ESM)
|
|
56
|
+
- TypeScript
|
|
57
|
+
- `@modelcontextprotocol/sdk`
|
|
58
|
+
- `undici`
|
|
59
|
+
- `zod`
|
|
60
|
+
|
|
61
|
+
## Project Structure
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
src/
|
|
65
|
+
├── index.ts # Stdio MCP server entrypoint
|
|
66
|
+
├── mcp/ # Tools, resources, prompts, MCP errors/logging
|
|
67
|
+
├── private/ # Authenticated Zebpay clients
|
|
68
|
+
├── public/ # Public market-data clients
|
|
69
|
+
├── security/ # Credentials + signing helpers
|
|
70
|
+
├── http/ # Shared outbound HTTP client to Zebpay APIs
|
|
71
|
+
├── validation/ # Schemas and validation helpers
|
|
72
|
+
├── utils/ # Caching, metrics, formatting, file logging
|
|
73
|
+
└── types/ # Shared response types
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Prerequisites
|
|
77
|
+
|
|
78
|
+
- Node.js 20+
|
|
79
|
+
- npm 9+
|
|
80
|
+
- Zebpay API credentials (for private tools)
|
|
81
|
+
- Create your API key/secret from the official ZebPay API portal: `https://api.zebpay.com/`
|
|
82
|
+
|
|
83
|
+
## Creating a ZebPay API Key
|
|
84
|
+
|
|
85
|
+
Use the steps below to generate API credentials for this MCP server:
|
|
86
|
+
|
|
87
|
+
1. Open the ZebPay API portal: `https://api.zebpay.com/`
|
|
88
|
+
2. Sign in to your ZebPay account.
|
|
89
|
+
3. Go to **API Trading** and click **Create New API Key**.
|
|
90
|
+
4. Enter key details:
|
|
91
|
+
- key name (for example: `zebpay-mcp-local`)
|
|
92
|
+
- required permissions (read-only for market/account checks, trading permissions only if needed)
|
|
93
|
+
- optional IP whitelist (recommended for better security)
|
|
94
|
+
5. Complete OTP verification.
|
|
95
|
+
6. Copy and save:
|
|
96
|
+
- API Key
|
|
97
|
+
- Secret Key (shown once)
|
|
98
|
+
|
|
99
|
+
Then add them to your local `.env` file:
|
|
100
|
+
|
|
101
|
+
```env
|
|
102
|
+
ZEBPAY_API_KEY=your_api_key
|
|
103
|
+
ZEBPAY_API_SECRET=your_api_secret
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Security notes:
|
|
107
|
+
|
|
108
|
+
- Never commit API keys/secrets to git.
|
|
109
|
+
- Prefer least-privilege API permissions.
|
|
110
|
+
- Rotate keys immediately if exposed.
|
|
111
|
+
|
|
112
|
+
## Setup
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
git clone <your-repo-url>
|
|
116
|
+
cd zebpay-mcp-server
|
|
117
|
+
npm install
|
|
118
|
+
cp env.example .env
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Update `.env` (all values are read from environment; there are no fallback defaults in code):
|
|
122
|
+
|
|
123
|
+
```env
|
|
124
|
+
ZEBPAY_API_KEY=your_api_key # Use the API key from the step above
|
|
125
|
+
ZEBPAY_API_SECRET=your_api_secret # Use the Secret key from the step above
|
|
126
|
+
ZEBPAY_SPOT_BASE_URL=https://sapi.zebpay.com/api/v2
|
|
127
|
+
ZEBPAY_FUTURES_BASE_URL=https://futuresbe.zebpay.com/api/v1
|
|
128
|
+
ZEBPAY_MARKET_BASE_URL=https://www.zebapi.com/api/v1/market
|
|
129
|
+
MCP_TRANSPORTS=stdio
|
|
130
|
+
LOG_LEVEL=info
|
|
131
|
+
HTTP_TIMEOUT_MS=15000
|
|
132
|
+
HTTP_RETRY_COUNT=2
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Build and Run
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npm run build
|
|
139
|
+
npm start
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Optional logging to file:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npm run start:log
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## MCP Client Configuration (Stdio)
|
|
149
|
+
|
|
150
|
+
Example MCP client config:
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"mcpServers": {
|
|
155
|
+
"zebpay": {
|
|
156
|
+
"command": "node",
|
|
157
|
+
"args": [
|
|
158
|
+
"/absolute/path/to/zebpay-mcp-server/dist/index.js"
|
|
159
|
+
],
|
|
160
|
+
"env": {
|
|
161
|
+
"ZEBPAY_API_KEY": "YOUR_API_KEY_HERE",
|
|
162
|
+
"ZEBPAY_API_SECRET": "YOUR_API_SECRET_HERE",
|
|
163
|
+
"LOG_LEVEL": "info"
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
[<img src="https://cursor.com/deeplink/mcp-install-dark.svg" alt="Install in Cursor">](https://cursor.com/en/install-mcp?name=zebpay&config=eyJjb21tYW5kIjoibm9kZSIsImFyZ3MiOlsiL2Fic29sdXRlL3BhdGgvdG8vemVicGF5LW1jcC1zZXJ2ZXIvZGlzdC9pbmRleC5qcyJdLCJlbnYiOnsiWkVCUEFZX0FQSV9LRVkiOiJZT1VSX0FQSV9LRVlfSEVSRSIsIlpFQlBBWV9BUElfU0VDUkVUIjoiWU9VUl9BUElfU0VDUkVUX0hFUkUiLCJMT0dfTEVWRUwiOiJpbmZvIn19)
|
|
170
|
+
|
|
171
|
+
Also see: `mcp-config.json.example`.
|
|
172
|
+
|
|
173
|
+
## Environment Variables
|
|
174
|
+
|
|
175
|
+
| Variable | Required | Description | Example value |
|
|
176
|
+
| ------------------------- | -------- | ---------------------------------------------- | --------------------------------------- |
|
|
177
|
+
| `ZEBPAY_API_KEY` | Yes | Zebpay API key (required for private tools) | `YOUR_API_KEY_HERE` |
|
|
178
|
+
| `ZEBPAY_API_SECRET` | Yes | Zebpay API secret (required for private tools) | `YOUR_API_SECRET_HERE` |
|
|
179
|
+
| `MCP_TRANSPORTS` | Yes | Must be `stdio` | `stdio` |
|
|
180
|
+
| `ZEBPAY_SPOT_BASE_URL` | Yes | Spot API base URL | `https://www.zebapi.com/api/v2` |
|
|
181
|
+
| `ZEBPAY_FUTURES_BASE_URL` | Yes | Futures API base URL | `https://futures-api.zebpay.com/api/v1` |
|
|
182
|
+
| `ZEBPAY_MARKET_BASE_URL` | Yes | Market API base URL | `https://www.zebapi.com/api/v1/market` |
|
|
183
|
+
| `LOG_LEVEL` | Yes | `debug`, `info`, `warn`, `error` | `info` |
|
|
184
|
+
| `LOG_FILE` | No | File path for logs | `logs/mcp-server.log` |
|
|
185
|
+
| `HTTP_TIMEOUT_MS` | Yes | Outbound request timeout (ms) | `15000` |
|
|
186
|
+
| `HTTP_RETRY_COUNT` | Yes | Retry count for outbound requests | `2` |
|
|
187
|
+
|
|
188
|
+
## Developer Commands
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
npm run build
|
|
192
|
+
npm test
|
|
193
|
+
npm run test:watch
|
|
194
|
+
npm run logs
|
|
195
|
+
npm run logs:watch
|
|
196
|
+
npm run logs:errors
|
|
197
|
+
npm run logs:stats
|
|
198
|
+
npm run logs:clear
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Logging
|
|
202
|
+
|
|
203
|
+
- Log tools and examples: `scripts/README.md`
|
|
204
|
+
- Full logging docs: `docs/LOGGING.md`
|
|
205
|
+
- Keep logs out of git (`logs/` is ignored)
|
|
206
|
+
|
|
207
|
+
## Issues
|
|
208
|
+
|
|
209
|
+
Found a bug or want to request a feature?
|
|
210
|
+
|
|
211
|
+
- Create an issue from the GitHub **Issues** tab for this repository.
|
|
212
|
+
- You can also use direct links after replacing `<your-org>` and `<your-repo>`:
|
|
213
|
+
- Bug report: `https://github.com/<your-org>/<your-repo>/issues/new?template=bug_report.md`
|
|
214
|
+
- Feature request: `https://github.com/<your-org>/<your-repo>/issues/new?template=feature_request.md`
|
|
215
|
+
- Include these details for faster triage:
|
|
216
|
+
- MCP client used (Cursor/Claude Desktop/other)
|
|
217
|
+
- Node.js version and OS
|
|
218
|
+
- Minimal reproduction steps
|
|
219
|
+
- Relevant logs (redact secrets)
|
|
220
|
+
|
|
221
|
+
## License
|
|
222
|
+
|
|
223
|
+
MIT
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Error handling tests for MCP error utilities.
|
|
3
|
+
*/
|
|
4
|
+
import { createInvalidParamsError, createInternalError, convertHttpErrorToMcpError } from "../mcp/errors.js";
|
|
5
|
+
import { ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
// Basic test suite (requires jest to be installed)
|
|
7
|
+
describe("MCP Error Handling", () => {
|
|
8
|
+
describe("createInvalidParamsError", () => {
|
|
9
|
+
it("should create an InvalidParams error with message", () => {
|
|
10
|
+
const error = createInvalidParamsError("Invalid symbol format");
|
|
11
|
+
if (!(error instanceof Error)) {
|
|
12
|
+
throw new Error("Expected error to be instance of Error");
|
|
13
|
+
}
|
|
14
|
+
if (error.code !== ErrorCode.InvalidParams) {
|
|
15
|
+
throw new Error(`Expected error code ${ErrorCode.InvalidParams}, got ${error.code}`);
|
|
16
|
+
}
|
|
17
|
+
// MCP error messages include code prefix: "MCP error -32602: <message>"
|
|
18
|
+
if (!error.message.includes("Invalid symbol format")) {
|
|
19
|
+
throw new Error(`Expected error message to contain "Invalid symbol format", got "${error.message}"`);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
it("should include data when provided", () => {
|
|
23
|
+
const data = { symbol: "BTC-INR" };
|
|
24
|
+
const error = createInvalidParamsError("Invalid symbol", data);
|
|
25
|
+
if (JSON.stringify(error.data) !== JSON.stringify(data)) {
|
|
26
|
+
throw new Error(`Expected error data to match ${JSON.stringify(data)}`);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe("createInternalError", () => {
|
|
31
|
+
it("should create an InternalError with message", () => {
|
|
32
|
+
const error = createInternalError("Server error occurred");
|
|
33
|
+
if (!(error instanceof Error)) {
|
|
34
|
+
throw new Error("Expected error to be instance of Error");
|
|
35
|
+
}
|
|
36
|
+
if (error.code !== ErrorCode.InternalError) {
|
|
37
|
+
throw new Error(`Expected error code ${ErrorCode.InternalError}, got ${error.code}`);
|
|
38
|
+
}
|
|
39
|
+
// MCP error messages include code prefix: "MCP error -32603: <message>"
|
|
40
|
+
if (!error.message.includes("Server error occurred")) {
|
|
41
|
+
throw new Error(`Expected error message to contain "Server error occurred", got "${error.message}"`);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
it("should include data when provided", () => {
|
|
45
|
+
const data = { status: 500 };
|
|
46
|
+
const error = createInternalError("Server error", data);
|
|
47
|
+
if (JSON.stringify(error.data) !== JSON.stringify(data)) {
|
|
48
|
+
throw new Error(`Expected error data to match ${JSON.stringify(data)}`);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe("convertHttpErrorToMcpError", () => {
|
|
53
|
+
it("should convert 400 to InvalidParams", () => {
|
|
54
|
+
const error = convertHttpErrorToMcpError(400, "Bad request", {});
|
|
55
|
+
if (error.code !== ErrorCode.InvalidParams) {
|
|
56
|
+
throw new Error(`Expected InvalidParams, got ${error.code}`);
|
|
57
|
+
}
|
|
58
|
+
// The message should contain the original message or a transformed version
|
|
59
|
+
if (!error.message.toLowerCase().includes("bad request") && !error.message.toLowerCase().includes("invalid")) {
|
|
60
|
+
throw new Error(`Expected message to contain "bad request" or "invalid", got "${error.message}"`);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
it("should convert 401 to InvalidParams with auth message", () => {
|
|
64
|
+
const error = convertHttpErrorToMcpError(401, "Unauthorized", {});
|
|
65
|
+
if (error.code !== ErrorCode.InvalidParams) {
|
|
66
|
+
throw new Error(`Expected InvalidParams, got ${error.code}`);
|
|
67
|
+
}
|
|
68
|
+
if (!error.message.toLowerCase().includes("credential")) {
|
|
69
|
+
throw new Error(`Expected message to contain "credential", got "${error.message}"`);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
it("should convert 403 to InvalidParams with auth message", () => {
|
|
73
|
+
const error = convertHttpErrorToMcpError(403, "Forbidden", {});
|
|
74
|
+
if (error.code !== ErrorCode.InvalidParams) {
|
|
75
|
+
throw new Error(`Expected InvalidParams, got ${error.code}`);
|
|
76
|
+
}
|
|
77
|
+
if (!error.message.toLowerCase().includes("credential")) {
|
|
78
|
+
throw new Error(`Expected message to contain "credential", got "${error.message}"`);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
it("should convert 404 to InvalidParams", () => {
|
|
82
|
+
const error = convertHttpErrorToMcpError(404, "Not found", {});
|
|
83
|
+
if (error.code !== ErrorCode.InvalidParams) {
|
|
84
|
+
throw new Error(`Expected InvalidParams, got ${error.code}`);
|
|
85
|
+
}
|
|
86
|
+
if (!error.message.toLowerCase().includes("not found")) {
|
|
87
|
+
throw new Error(`Expected message to contain "not found", got "${error.message}"`);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
it("should convert 429 to InternalError with retryable flag", () => {
|
|
91
|
+
const error = convertHttpErrorToMcpError(429, "Rate limit", {});
|
|
92
|
+
if (error.code !== ErrorCode.InternalError) {
|
|
93
|
+
throw new Error(`Expected InternalError, got ${error.code}`);
|
|
94
|
+
}
|
|
95
|
+
if (!error.message.toLowerCase().includes("rate limit")) {
|
|
96
|
+
throw new Error(`Expected message to contain "rate limit", got "${error.message}"`);
|
|
97
|
+
}
|
|
98
|
+
const data = error.data;
|
|
99
|
+
if (!data?.retryable) {
|
|
100
|
+
throw new Error("Expected retryable flag to be true");
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
it("should convert 500 to InternalError with retryable flag", () => {
|
|
104
|
+
const error = convertHttpErrorToMcpError(500, "Server error", {});
|
|
105
|
+
if (error.code !== ErrorCode.InternalError) {
|
|
106
|
+
throw new Error(`Expected InternalError, got ${error.code}`);
|
|
107
|
+
}
|
|
108
|
+
if (!error.message.toLowerCase().includes("server error")) {
|
|
109
|
+
throw new Error(`Expected message to contain "server error", got "${error.message}"`);
|
|
110
|
+
}
|
|
111
|
+
const data = error.data;
|
|
112
|
+
if (!data?.retryable) {
|
|
113
|
+
throw new Error("Expected retryable flag to be true");
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
it("should convert network error (status 0) to InternalError", () => {
|
|
117
|
+
const error = convertHttpErrorToMcpError(0, "Network error", {});
|
|
118
|
+
if (error.code !== ErrorCode.InternalError) {
|
|
119
|
+
throw new Error(`Expected InternalError, got ${error.code}`);
|
|
120
|
+
}
|
|
121
|
+
if (!error.message.toLowerCase().includes("network")) {
|
|
122
|
+
throw new Error(`Expected message to contain "network", got "${error.message}"`);
|
|
123
|
+
}
|
|
124
|
+
const data = error.data;
|
|
125
|
+
if (!data?.retryable) {
|
|
126
|
+
throw new Error("Expected retryable flag to be true");
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
it("should handle generic HTTP error messages", () => {
|
|
130
|
+
const error = convertHttpErrorToMcpError(400, "HTTP 400", {});
|
|
131
|
+
if (error.code !== ErrorCode.InvalidParams) {
|
|
132
|
+
throw new Error(`Expected InvalidParams, got ${error.code}`);
|
|
133
|
+
}
|
|
134
|
+
if (error.message === "HTTP 400") {
|
|
135
|
+
throw new Error("Expected message to be transformed, not generic HTTP 400");
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
it("should preserve detailed error messages", () => {
|
|
139
|
+
const detailedMessage = "Symbol BTC-INR not found in exchange";
|
|
140
|
+
const error = convertHttpErrorToMcpError(404, detailedMessage, {});
|
|
141
|
+
if (!error.message.includes(detailedMessage)) {
|
|
142
|
+
throw new Error(`Expected message to contain "${detailedMessage}", got "${error.message}"`);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
//# sourceMappingURL=errors.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.test.js","sourceRoot":"","sources":["../../src/__tests__/errors.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Tests for MCP prompts registration and functionality.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, beforeEach, afterEach, jest } from "@jest/globals";
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import { registerPrompts } from "../mcp/prompts.js";
|
|
7
|
+
describe("MCP Prompts", () => {
|
|
8
|
+
let mockServer;
|
|
9
|
+
let mockPublicClient;
|
|
10
|
+
let mockSpotClient;
|
|
11
|
+
let mockConfig;
|
|
12
|
+
let originalConsoleError;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
// Suppress console.error during tests
|
|
15
|
+
originalConsoleError = console.error;
|
|
16
|
+
console.error = jest.fn();
|
|
17
|
+
// Create mock server
|
|
18
|
+
mockServer = new McpServer({
|
|
19
|
+
name: "test-server",
|
|
20
|
+
version: "1.0.0",
|
|
21
|
+
});
|
|
22
|
+
// Create mock public client
|
|
23
|
+
mockPublicClient = {};
|
|
24
|
+
// Create mock spot client
|
|
25
|
+
mockSpotClient = {};
|
|
26
|
+
// Create mock config
|
|
27
|
+
mockConfig = {
|
|
28
|
+
spotBaseUrl: "https://test.api",
|
|
29
|
+
futuresBaseUrl: "https://test.futures.api",
|
|
30
|
+
marketBaseUrl: "https://test.market.api",
|
|
31
|
+
transports: ["stdio"],
|
|
32
|
+
logLevel: "info",
|
|
33
|
+
signingHeaders: {
|
|
34
|
+
apiKeyHeader: "X-AUTH-APIKEY",
|
|
35
|
+
signatureHeader: "X-AUTH-SIGNATURE",
|
|
36
|
+
timestampHeader: "",
|
|
37
|
+
},
|
|
38
|
+
timeoutMs: 15000,
|
|
39
|
+
retryCount: 2,
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
// Restore console.error after tests
|
|
44
|
+
console.error = originalConsoleError;
|
|
45
|
+
});
|
|
46
|
+
it("should register check-balance-before-trade prompt when spot client is available", () => {
|
|
47
|
+
expect(() => {
|
|
48
|
+
registerPrompts(mockServer, mockSpotClient, mockPublicClient, mockConfig);
|
|
49
|
+
}).not.toThrow();
|
|
50
|
+
});
|
|
51
|
+
it("should register analyze-market prompt", () => {
|
|
52
|
+
expect(() => {
|
|
53
|
+
registerPrompts(mockServer, null, mockPublicClient, mockConfig);
|
|
54
|
+
}).not.toThrow();
|
|
55
|
+
});
|
|
56
|
+
it("should register compare-trading-pairs prompt", () => {
|
|
57
|
+
expect(() => {
|
|
58
|
+
registerPrompts(mockServer, null, mockPublicClient, mockConfig);
|
|
59
|
+
}).not.toThrow();
|
|
60
|
+
});
|
|
61
|
+
it("should register get-exchange-info prompt", () => {
|
|
62
|
+
expect(() => {
|
|
63
|
+
registerPrompts(mockServer, null, mockPublicClient, mockConfig);
|
|
64
|
+
}).not.toThrow();
|
|
65
|
+
});
|
|
66
|
+
it("should not register authenticated prompts when spot client is null", () => {
|
|
67
|
+
// Public prompts should still register successfully without spot client
|
|
68
|
+
expect(() => {
|
|
69
|
+
registerPrompts(mockServer, null, mockPublicClient, mockConfig);
|
|
70
|
+
}).not.toThrow();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=prompts.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.test.js","sourceRoot":"","sources":["../../src/__tests__/prompts.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Tests for MCP resources registration and functionality.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, beforeEach, afterEach, jest } from "@jest/globals";
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import { registerResources } from "../mcp/resources.js";
|
|
7
|
+
describe("MCP Resources", () => {
|
|
8
|
+
let mockServer;
|
|
9
|
+
let mockPublicClient;
|
|
10
|
+
let mockSpotClient;
|
|
11
|
+
let mockConfig;
|
|
12
|
+
let originalConsoleError;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
// Suppress console.error during tests
|
|
15
|
+
originalConsoleError = console.error;
|
|
16
|
+
console.error = jest.fn();
|
|
17
|
+
// Create mock server
|
|
18
|
+
mockServer = new McpServer({
|
|
19
|
+
name: "test-server",
|
|
20
|
+
version: "1.0.0",
|
|
21
|
+
});
|
|
22
|
+
// Create mock public client
|
|
23
|
+
mockPublicClient = {
|
|
24
|
+
getExchangeInfo: jest.fn().mockResolvedValue({ symbols: [] }),
|
|
25
|
+
getCurrencies: jest.fn().mockResolvedValue({ currencies: [] }),
|
|
26
|
+
getAllTickers: jest.fn().mockResolvedValue({ tickers: [] }),
|
|
27
|
+
};
|
|
28
|
+
// Create mock spot client
|
|
29
|
+
mockSpotClient = {
|
|
30
|
+
getBalance: jest.fn().mockResolvedValue({ balances: [] }),
|
|
31
|
+
};
|
|
32
|
+
// Create mock config
|
|
33
|
+
mockConfig = {
|
|
34
|
+
spotBaseUrl: "https://test.api",
|
|
35
|
+
futuresBaseUrl: "https://test.futures.api",
|
|
36
|
+
marketBaseUrl: "https://test.market.api",
|
|
37
|
+
transports: ["stdio"],
|
|
38
|
+
logLevel: "info",
|
|
39
|
+
signingHeaders: {
|
|
40
|
+
apiKeyHeader: "X-AUTH-APIKEY",
|
|
41
|
+
signatureHeader: "X-AUTH-SIGNATURE",
|
|
42
|
+
timestampHeader: "",
|
|
43
|
+
},
|
|
44
|
+
timeoutMs: 15000,
|
|
45
|
+
retryCount: 2,
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
afterEach(() => {
|
|
49
|
+
// Restore console.error after tests
|
|
50
|
+
console.error = originalConsoleError;
|
|
51
|
+
});
|
|
52
|
+
it("should register exchange-info resource", () => {
|
|
53
|
+
expect(() => {
|
|
54
|
+
registerResources(mockServer, mockPublicClient, null, mockConfig);
|
|
55
|
+
}).not.toThrow();
|
|
56
|
+
});
|
|
57
|
+
it("should register currencies resource", () => {
|
|
58
|
+
expect(() => {
|
|
59
|
+
registerResources(mockServer, mockPublicClient, null, mockConfig);
|
|
60
|
+
}).not.toThrow();
|
|
61
|
+
});
|
|
62
|
+
it("should register all-tickers resource", () => {
|
|
63
|
+
expect(() => {
|
|
64
|
+
registerResources(mockServer, mockPublicClient, null, mockConfig);
|
|
65
|
+
}).not.toThrow();
|
|
66
|
+
});
|
|
67
|
+
it("should register balance resource when spot client is available", () => {
|
|
68
|
+
expect(() => {
|
|
69
|
+
registerResources(mockServer, mockPublicClient, mockSpotClient, mockConfig);
|
|
70
|
+
}).not.toThrow();
|
|
71
|
+
});
|
|
72
|
+
it("should not register balance resource when spot client is null", () => {
|
|
73
|
+
// Resources should still register successfully without spot client
|
|
74
|
+
expect(() => {
|
|
75
|
+
registerResources(mockServer, mockPublicClient, null, mockConfig);
|
|
76
|
+
}).not.toThrow();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=resources.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.test.js","sourceRoot":"","sources":["../../src/__tests__/resources.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
function describe(name: string, fn: () => void): void;
|
|
3
|
+
function it(name: string, fn: () => void): void;
|
|
4
|
+
function expect(fn: () => void): {
|
|
5
|
+
not: {
|
|
6
|
+
toThrow: () => void;
|
|
7
|
+
};
|
|
8
|
+
toThrow: () => void;
|
|
9
|
+
};
|
|
10
|
+
function expect<T>(value: T): {
|
|
11
|
+
toBeInstanceOf: (constructor: new (...args: any[]) => T) => void;
|
|
12
|
+
toBe: (expected: T) => void;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Basic test setup for ZebPay MCP Server
|
|
3
|
+
Note: Install jest dependencies first: npm install --save-dev jest @types/jest ts-jest
|
|
4
|
+
Then run: npm test
|
|
5
|
+
*/
|
|
6
|
+
import { validateSymbol, validateQuantity } from "../validation/validators.js";
|
|
7
|
+
import { createInvalidParamsError } from "../mcp/errors.js";
|
|
8
|
+
// Basic test suite (requires jest to be installed)
|
|
9
|
+
describe("Validation", () => {
|
|
10
|
+
describe("validateSymbol", () => {
|
|
11
|
+
it("should accept valid symbols", () => {
|
|
12
|
+
expect(() => validateSymbol("BTC-INR")).not.toThrow();
|
|
13
|
+
expect(() => validateSymbol("ETH-INR")).not.toThrow();
|
|
14
|
+
expect(() => validateSymbol("BTC-USDT")).not.toThrow();
|
|
15
|
+
});
|
|
16
|
+
it("should reject invalid symbols", () => {
|
|
17
|
+
expect(() => validateSymbol("BTCINR")).toThrow();
|
|
18
|
+
expect(() => validateSymbol("BTC/INR")).toThrow();
|
|
19
|
+
// Note: "btc-inr" is converted to uppercase and becomes valid "BTC-INR"
|
|
20
|
+
// So we test with a symbol that's invalid even after conversion
|
|
21
|
+
expect(() => validateSymbol("")).toThrow();
|
|
22
|
+
expect(() => validateSymbol("BTC-INR-EXTRA")).toThrow(); // Too many parts
|
|
23
|
+
});
|
|
24
|
+
it("should accept lowercase symbols (converts to uppercase)", () => {
|
|
25
|
+
// The function converts to uppercase, so lowercase should work
|
|
26
|
+
expect(() => validateSymbol("btc-inr")).not.toThrow();
|
|
27
|
+
expect(() => validateSymbol("eth-inr")).not.toThrow();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe("validateQuantity", () => {
|
|
31
|
+
it("should accept valid quantities", () => {
|
|
32
|
+
expect(() => validateQuantity("0.001")).not.toThrow();
|
|
33
|
+
expect(() => validateQuantity("100")).not.toThrow();
|
|
34
|
+
expect(() => validateQuantity("0.5")).not.toThrow();
|
|
35
|
+
});
|
|
36
|
+
it("should reject invalid quantities", () => {
|
|
37
|
+
expect(() => validateQuantity("")).toThrow();
|
|
38
|
+
expect(() => validateQuantity("0")).toThrow();
|
|
39
|
+
expect(() => validateQuantity("-1")).toThrow();
|
|
40
|
+
expect(() => validateQuantity("abc")).toThrow();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe("Error Handling", () => {
|
|
45
|
+
it("should create invalid params error with correct code", () => {
|
|
46
|
+
const error = createInvalidParamsError("Test error");
|
|
47
|
+
// Note: These assertions require jest to be installed
|
|
48
|
+
// expect(error).toBeInstanceOf(Error);
|
|
49
|
+
// expect(error.code).toBe(-32602);
|
|
50
|
+
// expect(error.message).toBe("Test error");
|
|
51
|
+
// Basic validation without jest
|
|
52
|
+
if (!(error instanceof Error)) {
|
|
53
|
+
throw new Error("Expected error to be instance of Error");
|
|
54
|
+
}
|
|
55
|
+
if (error.code !== -32602) {
|
|
56
|
+
throw new Error(`Expected error code -32602, got ${error.code}`);
|
|
57
|
+
}
|
|
58
|
+
// MCP error messages include code prefix: "MCP error -32602: <message>"
|
|
59
|
+
if (!error.message.includes("Test error")) {
|
|
60
|
+
throw new Error(`Expected error message to contain "Test error", got "${error.message}"`);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
//# sourceMappingURL=validation.test.js.map
|