@guiie/buda-mcp 1.0.0 → 1.1.1

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.
Files changed (77) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/PUBLISH_CHECKLIST.md +192 -0
  3. package/README.md +308 -70
  4. package/dist/cache.d.ts +13 -0
  5. package/dist/cache.d.ts.map +1 -0
  6. package/dist/cache.js +25 -0
  7. package/dist/client.d.ts +9 -8
  8. package/dist/client.d.ts.map +1 -1
  9. package/dist/client.js +95 -12
  10. package/dist/http.d.ts +2 -0
  11. package/dist/http.d.ts.map +1 -0
  12. package/dist/http.js +262 -0
  13. package/dist/index.js +53 -9
  14. package/dist/tools/balances.d.ts +4 -0
  15. package/dist/tools/balances.d.ts.map +1 -0
  16. package/dist/tools/balances.js +23 -0
  17. package/dist/tools/cancel_order.d.ts +4 -0
  18. package/dist/tools/cancel_order.d.ts.map +1 -0
  19. package/dist/tools/cancel_order.js +53 -0
  20. package/dist/tools/compare_markets.d.ts +5 -0
  21. package/dist/tools/compare_markets.d.ts.map +1 -0
  22. package/dist/tools/compare_markets.js +65 -0
  23. package/dist/tools/markets.d.ts +2 -1
  24. package/dist/tools/markets.d.ts.map +1 -1
  25. package/dist/tools/markets.js +22 -8
  26. package/dist/tools/orderbook.d.ts +2 -1
  27. package/dist/tools/orderbook.d.ts.map +1 -1
  28. package/dist/tools/orderbook.js +27 -13
  29. package/dist/tools/orders.d.ts +4 -0
  30. package/dist/tools/orders.d.ts.map +1 -0
  31. package/dist/tools/orders.js +57 -0
  32. package/dist/tools/place_order.d.ts +4 -0
  33. package/dist/tools/place_order.d.ts.map +1 -0
  34. package/dist/tools/place_order.js +88 -0
  35. package/dist/tools/price_history.d.ts +5 -0
  36. package/dist/tools/price_history.d.ts.map +1 -0
  37. package/dist/tools/price_history.js +97 -0
  38. package/dist/tools/spread.d.ts +5 -0
  39. package/dist/tools/spread.d.ts.map +1 -0
  40. package/dist/tools/spread.js +55 -0
  41. package/dist/tools/ticker.d.ts +2 -1
  42. package/dist/tools/ticker.d.ts.map +1 -1
  43. package/dist/tools/ticker.js +19 -5
  44. package/dist/tools/trades.d.ts +2 -1
  45. package/dist/tools/trades.d.ts.map +1 -1
  46. package/dist/tools/trades.js +22 -10
  47. package/dist/tools/volume.d.ts +2 -1
  48. package/dist/tools/volume.d.ts.map +1 -1
  49. package/dist/tools/volume.js +17 -5
  50. package/dist/types.d.ts +42 -1
  51. package/dist/types.d.ts.map +1 -1
  52. package/marketplace/claude-listing.md +72 -4
  53. package/marketplace/cursor-mcp.json +1 -1
  54. package/marketplace/gemini-tools.json +51 -1
  55. package/marketplace/openapi.yaml +203 -3
  56. package/package.json +6 -2
  57. package/railway.json +12 -0
  58. package/server.json +3 -3
  59. package/src/cache.ts +34 -0
  60. package/src/client.ts +107 -12
  61. package/src/http.ts +305 -0
  62. package/src/index.ts +77 -9
  63. package/src/tools/balances.ts +30 -0
  64. package/src/tools/cancel_order.ts +65 -0
  65. package/src/tools/compare_markets.ts +82 -0
  66. package/src/tools/markets.ts +30 -11
  67. package/src/tools/orderbook.ts +31 -16
  68. package/src/tools/orders.ts +68 -0
  69. package/src/tools/place_order.ts +107 -0
  70. package/src/tools/price_history.ts +124 -0
  71. package/src/tools/spread.ts +71 -0
  72. package/src/tools/ticker.ts +23 -8
  73. package/src/tools/trades.ts +24 -12
  74. package/src/tools/volume.ts +20 -8
  75. package/src/types.ts +52 -1
  76. package/test/run-all.ts +122 -1
  77. package/tsconfig.json +1 -1
@@ -1,7 +1,7 @@
1
1
  # Buda.com Market Data
2
2
 
3
3
  **Category:** Finance / Cryptocurrency
4
- **Auth:** None required
4
+ **Auth:** Optional (public mode requires no key; authenticated mode needs `BUDA_API_KEY` + `BUDA_API_SECRET`)
5
5
  **Transport:** stdio
6
6
  **npm:** `@guiie/buda-mcp`
7
7
  **Registry name:** `io.github.gtorreal/buda-mcp`
@@ -10,14 +10,16 @@
10
10
 
11
11
  ## Description
12
12
 
13
- Real-time market data from [Buda.com](https://www.buda.com/), the leading cryptocurrency exchange operating in Chile, Colombia, and Peru. All data is sourced from Buda's public REST API v2 — no API key required.
13
+ Real-time market data from [Buda.com](https://www.buda.com/), the leading cryptocurrency exchange operating in Chile, Colombia, and Peru. All public data is sourced from Buda's public REST API v2 — no API key required.
14
14
 
15
- Use this server to query live prices, order books, trade history, and volume for all BTC, ETH, and altcoin markets quoted in CLP, COP, and PEN.
15
+ Use this server to query live prices, spreads, order books, OHLCV candles, trade history, and volume for all BTC, ETH, and altcoin markets quoted in CLP, COP, PEN, and USDC. Optional API credentials unlock account tools for balances and order management.
16
16
 
17
17
  ---
18
18
 
19
19
  ## Tools
20
20
 
21
+ ### Public tools (no credentials required)
22
+
21
23
  ### `get_markets`
22
24
  List all available trading pairs on Buda.com, or retrieve details for a specific market.
23
25
  Returns: base/quote currencies, trading fees, minimum order amounts, fee discount tiers.
@@ -39,6 +41,46 @@ Recent trade history. Each entry: `[timestamp_ms, amount, price, direction]`.
39
41
  24h and 7-day transacted volume broken down by buy (bid) and sell (ask) side.
40
42
  **Parameters:** `market_id` *(required)*.
41
43
 
44
+ ### `get_spread`
45
+ Bid/ask spread for a market: absolute spread and spread as a percentage of the ask price.
46
+ **Parameters:** `market_id` *(required)*.
47
+
48
+ ### `compare_markets`
49
+ Side-by-side ticker data for all trading pairs of a given base currency across all supported quote currencies.
50
+ **Parameters:** `base_currency` *(required)* — e.g. `BTC`, `ETH`, `XRP`.
51
+
52
+ ### `get_price_history`
53
+ OHLCV (open/high/low/close/volume) candles derived from recent trade history. Supports `1h`, `4h`, and `1d` periods.
54
+ **Parameters:** `market_id` *(required)*, `period` *(optional: `1h`/`4h`/`1d`, default `1h`)*, `limit` *(optional, max 100 trades)*.
55
+
56
+ ### Authenticated tools (require `BUDA_API_KEY` + `BUDA_API_SECRET`)
57
+
58
+ > **Important:** Authenticated instances must run locally only — never expose a server with API credentials publicly.
59
+
60
+ ### `get_balances`
61
+ All currency balances: total, available, frozen, and pending withdrawal amounts.
62
+
63
+ ### `get_orders`
64
+ Orders for a given market, filterable by state (`pending`, `active`, `traded`, `canceled`).
65
+ **Parameters:** `market_id` *(required)*, `state` *(optional)*, `per` *(optional)*, `page` *(optional)*.
66
+
67
+ ### `place_order`
68
+ Place a limit or market order. Requires `confirmation_token="CONFIRM"` to prevent accidental execution.
69
+ **Parameters:** `market_id`, `type` (Bid/Ask), `price_type` (limit/market), `amount`, `limit_price` *(for limit orders)*, `confirmation_token`.
70
+
71
+ ### `cancel_order`
72
+ Cancel an open order by ID. Requires `confirmation_token="CONFIRM"`.
73
+ **Parameters:** `order_id`, `confirmation_token`.
74
+
75
+ ---
76
+
77
+ ## MCP Resources
78
+
79
+ | URI | Description |
80
+ |-----|-------------|
81
+ | `buda://markets` | JSON list of all Buda.com markets |
82
+ | `buda://ticker/{market}` | JSON ticker for a specific market |
83
+
42
84
  ---
43
85
 
44
86
  ## Example prompts
@@ -48,11 +90,20 @@ Recent trade history. Each entry: `[timestamp_ms, amount, price, direction]`.
48
90
  - *"How much ETH was traded on Buda in the last 7 days?"*
49
91
  - *"List all markets available on Buda.com."*
50
92
  - *"What's the spread on BTC-COP right now?"*
93
+ - *"Compare the Bitcoin price across all Buda markets."*
94
+ - *"Show me hourly BTC-CLP candles."*
95
+ - *"What's my available BTC balance?"* *(authenticated)*
96
+ - *"Show my open orders on BTC-CLP."* *(authenticated)*
51
97
 
52
98
  ---
53
99
 
54
100
  ## Installation
55
101
 
102
+ **Quick start (npx)**
103
+ ```bash
104
+ npx @guiie/buda-mcp
105
+ ```
106
+
56
107
  **Claude Code (claude CLI)**
57
108
  ```bash
58
109
  claude mcp add buda-mcp -- npx -y @guiie/buda-mcp
@@ -70,11 +121,27 @@ claude mcp add buda-mcp -- npx -y @guiie/buda-mcp
70
121
  }
71
122
  ```
72
123
 
124
+ **With authentication**
125
+ ```json
126
+ {
127
+ "mcpServers": {
128
+ "buda-mcp": {
129
+ "command": "npx",
130
+ "args": ["-y", "@guiie/buda-mcp"],
131
+ "env": {
132
+ "BUDA_API_KEY": "your_api_key",
133
+ "BUDA_API_SECRET": "your_api_secret"
134
+ }
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
73
140
  **From source**
74
141
  ```bash
75
142
  git clone https://github.com/gtorreal/buda-mcp.git
76
143
  cd buda-mcp && npm install && npm run build
77
- # Then point to: node /absolute/path/buda-mcp/dist/index.js
144
+ node dist/index.js
78
145
  ```
79
146
 
80
147
  ---
@@ -86,6 +153,7 @@ cd buda-mcp && npm install && npm run build
86
153
  | CLP | Chile | BTC-CLP, ETH-CLP, XRP-CLP |
87
154
  | COP | Colombia | BTC-COP, ETH-COP, XRP-COP |
88
155
  | PEN | Peru | BTC-PEN, ETH-PEN |
156
+ | USDC | USD-pegged | BTC-USDC, ETH-USDC |
89
157
  | BTC | Cross | ETH-BTC, XRP-BTC, BCH-BTC |
90
158
 
91
159
  ---
@@ -3,7 +3,7 @@
3
3
  "buda-mcp": {
4
4
  "command": "npx",
5
5
  "args": ["-y", "@guiie/buda-mcp"],
6
- "description": "Buda.com cryptocurrency exchange — real-time market data for Chile, Colombia, and Peru"
6
+ "description": "Buda.com cryptocurrency exchange — real-time market data, spreads, OHLCV history, and account tools for Chile, Colombia, and Peru"
7
7
  }
8
8
  }
9
9
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "_comment": "Gemini function declarations for buda-mcp. Pass this array as the `tools[0].functionDeclarations` field when calling the Gemini API. See: https://ai.google.dev/gemini-api/docs/function-calling",
2
+ "_comment": "Gemini function declarations for buda-mcp v1.1.0. Pass this array as the `tools[0].functionDeclarations` field when calling the Gemini API. See: https://ai.google.dev/gemini-api/docs/function-calling",
3
3
  "functionDeclarations": [
4
4
  {
5
5
  "name": "get_markets",
@@ -82,6 +82,56 @@
82
82
  },
83
83
  "required": ["market_id"]
84
84
  }
85
+ },
86
+ {
87
+ "name": "get_spread",
88
+ "description": "Calculate the bid/ask spread for a Buda.com market. Returns the best bid price, best ask price, absolute spread, and spread as a percentage of the ask price. Useful for assessing market liquidity.",
89
+ "parameters": {
90
+ "type": "OBJECT",
91
+ "properties": {
92
+ "market_id": {
93
+ "type": "STRING",
94
+ "description": "Market identifier such as 'BTC-CLP', 'ETH-COP', or 'ETH-BTC'. Case-insensitive."
95
+ }
96
+ },
97
+ "required": ["market_id"]
98
+ }
99
+ },
100
+ {
101
+ "name": "compare_markets",
102
+ "description": "Compare ticker data side-by-side for all trading pairs of a given base currency across Buda.com's supported quote currencies (CLP, COP, PEN, USDC, BTC, ETH). For example, 'BTC' returns data for BTC-CLP, BTC-COP, BTC-PEN, and BTC-USDC.",
103
+ "parameters": {
104
+ "type": "OBJECT",
105
+ "properties": {
106
+ "base_currency": {
107
+ "type": "STRING",
108
+ "description": "Base currency to compare across all available markets. Examples: 'BTC', 'ETH', 'XRP'. Case-insensitive."
109
+ }
110
+ },
111
+ "required": ["base_currency"]
112
+ }
113
+ },
114
+ {
115
+ "name": "get_price_history",
116
+ "description": "Get OHLCV (open/high/low/close/volume) price history for a Buda.com market, derived from recent trade history. Candles are aggregated client-side from up to 100 raw trades. Supports 1h, 4h, and 1d candle periods.",
117
+ "parameters": {
118
+ "type": "OBJECT",
119
+ "properties": {
120
+ "market_id": {
121
+ "type": "STRING",
122
+ "description": "Market identifier such as 'BTC-CLP' or 'ETH-BTC'. Case-insensitive."
123
+ },
124
+ "period": {
125
+ "type": "STRING",
126
+ "description": "Candle period: '1h' (1 hour), '4h' (4 hours), or '1d' (1 day). Default is '1h'."
127
+ },
128
+ "limit": {
129
+ "type": "INTEGER",
130
+ "description": "Number of raw trades to fetch before aggregation. Default is 100, maximum is 100."
131
+ }
132
+ },
133
+ "required": ["market_id"]
134
+ }
85
135
  }
86
136
  ]
87
137
  }
@@ -4,14 +4,14 @@ info:
4
4
  description: |
5
5
  Real-time cryptocurrency market data from Buda.com — the leading exchange
6
6
  in Chile (CLP), Colombia (COP), and Peru (PEN).
7
- Provides live prices, order books, trade history, and volume for all markets.
8
- No authentication required.
7
+ Provides live prices, spreads, OHLCV history, order books, trade history, and volume for all markets.
8
+ No authentication required for public tools.
9
9
 
10
10
  **Note:** This spec describes a deployed HTTP wrapper around the buda-mcp
11
11
  stdio server. Deploy locally with mcp-proxy:
12
12
  mcp-proxy --port 8000 -- npx -y @guiie/buda-mcp
13
13
  Or point `servers[0].url` at your hosted instance.
14
- version: 1.0.0
14
+ version: 1.1.0
15
15
  contact:
16
16
  url: https://github.com/gtorreal/buda-mcp
17
17
 
@@ -175,6 +175,99 @@ paths:
175
175
  "404":
176
176
  $ref: "#/components/responses/NotFound"
177
177
 
178
+ /get_spread:
179
+ get:
180
+ operationId: getSpread
181
+ summary: Bid/ask spread for a market
182
+ description: |
183
+ Returns the best bid price, best ask price, absolute spread, and spread as a percentage
184
+ of the ask price. Useful for assessing market liquidity.
185
+ parameters:
186
+ - name: market_id
187
+ in: query
188
+ required: true
189
+ description: Market identifier (e.g. "BTC-CLP"). Case-insensitive.
190
+ schema:
191
+ type: string
192
+ example: BTC-CLP
193
+ responses:
194
+ "200":
195
+ description: Spread data
196
+ content:
197
+ application/json:
198
+ schema:
199
+ $ref: "#/components/schemas/SpreadResponse"
200
+ "404":
201
+ $ref: "#/components/responses/NotFound"
202
+
203
+ /compare_markets:
204
+ get:
205
+ operationId: compareMarkets
206
+ summary: Compare all pairs of a base currency side by side
207
+ description: |
208
+ Returns ticker data for all trading pairs of a given base currency across all supported
209
+ quote currencies (CLP, COP, PEN, USDC, BTC, ETH). For example, base_currency=BTC returns
210
+ data for BTC-CLP, BTC-COP, BTC-PEN, and BTC-USDC.
211
+ parameters:
212
+ - name: base_currency
213
+ in: query
214
+ required: true
215
+ description: Base currency to compare (e.g. "BTC", "ETH"). Case-insensitive.
216
+ schema:
217
+ type: string
218
+ example: BTC
219
+ responses:
220
+ "200":
221
+ description: Comparison of all pairs for the given base currency
222
+ content:
223
+ application/json:
224
+ schema:
225
+ $ref: "#/components/schemas/CompareMarketsResponse"
226
+ "404":
227
+ $ref: "#/components/responses/NotFound"
228
+
229
+ /get_price_history:
230
+ get:
231
+ operationId: getPriceHistory
232
+ summary: OHLCV price history derived from trade history
233
+ description: |
234
+ Returns OHLCV (open/high/low/close/volume) candles derived from recent trade history.
235
+ Buda.com has no native candlestick endpoint; candles are aggregated client-side from
236
+ up to 100 raw trades. Supports 1h, 4h, and 1d candle periods.
237
+ parameters:
238
+ - name: market_id
239
+ in: query
240
+ required: true
241
+ description: Market identifier (e.g. "BTC-CLP"). Case-insensitive.
242
+ schema:
243
+ type: string
244
+ example: BTC-CLP
245
+ - name: period
246
+ in: query
247
+ required: false
248
+ description: Candle period. One of "1h", "4h", "1d". Default is "1h".
249
+ schema:
250
+ type: string
251
+ enum: ["1h", "4h", "1d"]
252
+ default: "1h"
253
+ - name: limit
254
+ in: query
255
+ required: false
256
+ description: Number of raw trades to fetch before aggregation (default 100, max 100).
257
+ schema:
258
+ type: integer
259
+ minimum: 1
260
+ maximum: 100
261
+ responses:
262
+ "200":
263
+ description: OHLCV candles
264
+ content:
265
+ application/json:
266
+ schema:
267
+ $ref: "#/components/schemas/PriceHistoryResponse"
268
+ "404":
269
+ $ref: "#/components/responses/NotFound"
270
+
178
271
  components:
179
272
  schemas:
180
273
  Amount:
@@ -334,11 +427,118 @@ components:
334
427
  bid_volume_7d:
335
428
  $ref: "#/components/schemas/Amount"
336
429
 
430
+ SpreadResponse:
431
+ type: object
432
+ properties:
433
+ market_id:
434
+ type: string
435
+ example: BTC-CLP
436
+ currency:
437
+ type: string
438
+ example: CLP
439
+ best_bid:
440
+ type: string
441
+ example: "65221128.0"
442
+ best_ask:
443
+ type: string
444
+ example: "65727568.0"
445
+ spread_absolute:
446
+ type: string
447
+ example: "506440.00"
448
+ spread_percentage:
449
+ type: string
450
+ example: "0.7705%"
451
+ last_price:
452
+ type: string
453
+ example: "65221078.43"
454
+
455
+ CompareMarketsResponse:
456
+ type: object
457
+ properties:
458
+ base_currency:
459
+ type: string
460
+ example: BTC
461
+ markets:
462
+ type: array
463
+ items:
464
+ type: object
465
+ properties:
466
+ market_id:
467
+ type: string
468
+ example: BTC-CLP
469
+ last_price:
470
+ type: string
471
+ example: "65221078.43"
472
+ currency:
473
+ type: string
474
+ example: CLP
475
+ best_bid:
476
+ type: string
477
+ best_ask:
478
+ type: string
479
+ volume_24h:
480
+ type: string
481
+ price_change_24h:
482
+ type: string
483
+ example: "0.30%"
484
+ price_change_7d:
485
+ type: string
486
+ example: "-1.20%"
487
+
488
+ OhlcvCandle:
489
+ type: object
490
+ properties:
491
+ time:
492
+ type: string
493
+ format: date-time
494
+ example: "2026-04-10T12:00:00.000Z"
495
+ open:
496
+ type: string
497
+ example: "65221078.43"
498
+ high:
499
+ type: string
500
+ example: "65975364.0"
501
+ low:
502
+ type: string
503
+ example: "65164404.87"
504
+ close:
505
+ type: string
506
+ example: "65386647.0"
507
+ volume:
508
+ type: string
509
+ example: "0.52341200"
510
+ trade_count:
511
+ type: integer
512
+ example: 12
513
+
514
+ PriceHistoryResponse:
515
+ type: object
516
+ properties:
517
+ market_id:
518
+ type: string
519
+ example: BTC-CLP
520
+ period:
521
+ type: string
522
+ example: "1h"
523
+ candle_count:
524
+ type: integer
525
+ example: 6
526
+ note:
527
+ type: string
528
+ candles:
529
+ type: array
530
+ items:
531
+ $ref: "#/components/schemas/OhlcvCandle"
532
+
337
533
  Error:
338
534
  type: object
339
535
  properties:
340
536
  error:
341
537
  type: string
538
+ code:
539
+ oneOf:
540
+ - type: string
541
+ - type: integer
342
542
 
343
543
  responses:
344
544
  NotFound:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guiie/buda-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "mcpName": "io.github.gtorreal/buda-mcp",
5
5
  "description": "MCP server for Buda.com's public cryptocurrency exchange API (Chile, Colombia, Peru)",
6
6
  "type": "module",
@@ -10,7 +10,9 @@
10
10
  },
11
11
  "scripts": {
12
12
  "build": "tsc",
13
- "start": "node dist/index.js",
13
+ "start": "node dist/http.js",
14
+ "start:stdio": "node dist/index.js",
15
+ "dev:http": "tsx src/http.ts",
14
16
  "dev": "tsx src/index.ts",
15
17
  "test": "tsx test/run-all.ts"
16
18
  },
@@ -43,9 +45,11 @@
43
45
  "author": "Guille <guillermo@buda.com>",
44
46
  "dependencies": {
45
47
  "@modelcontextprotocol/sdk": "^1.29.0",
48
+ "express": "^5.2.1",
46
49
  "zod": "^4.3.6"
47
50
  },
48
51
  "devDependencies": {
52
+ "@types/express": "^5.0.6",
49
53
  "@types/node": "^25.6.0",
50
54
  "tsx": "^4.21.0",
51
55
  "typescript": "^6.0.2"
package/railway.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "https://railway.com/railway.schema.json",
3
+ "build": {
4
+ "builder": "NIXPACKS"
5
+ },
6
+ "deploy": {
7
+ "startCommand": "npm start",
8
+ "healthcheckPath": "/health",
9
+ "restartPolicyType": "ON_FAILURE",
10
+ "restartPolicyMaxRetries": 3
11
+ }
12
+ }
package/server.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.gtorreal/buda-mcp",
4
- "description": "Real-time cryptocurrency market data from Buda.com — prices, order books, trades, and volume for markets in Chile (CLP), Colombia (COP), and Peru (PEN). No API key required.",
4
+ "description": "Buda.com market data: prices, spreads, OHLCV, order books, trades, and volume for CLP, COP, PEN.",
5
5
  "repository": {
6
6
  "url": "https://github.com/gtorreal/buda-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "1.0.0",
9
+ "version": "1.1.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@guiie/buda-mcp",
14
- "version": "1.0.0",
14
+ "version": "1.1.1",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  }
package/src/cache.ts ADDED
@@ -0,0 +1,34 @@
1
+ interface CacheEntry<T> {
2
+ data: T;
3
+ expiry: number;
4
+ }
5
+
6
+ export class MemoryCache {
7
+ private store = new Map<string, CacheEntry<unknown>>();
8
+
9
+ async getOrFetch<T>(key: string, ttlMs: number, fetcher: () => Promise<T>): Promise<T> {
10
+ const entry = this.store.get(key) as CacheEntry<T> | undefined;
11
+ if (entry && Date.now() < entry.expiry) {
12
+ return entry.data;
13
+ }
14
+ const data = await fetcher();
15
+ this.store.set(key, { data, expiry: Date.now() + ttlMs });
16
+ return data;
17
+ }
18
+
19
+ invalidate(key: string): void {
20
+ this.store.delete(key);
21
+ }
22
+
23
+ clear(): void {
24
+ this.store.clear();
25
+ }
26
+ }
27
+
28
+ export const CACHE_TTL = {
29
+ MARKETS: 60_000,
30
+ TICKER: 5_000,
31
+ ORDERBOOK: 3_000,
32
+ } as const;
33
+
34
+ export const cache = new MemoryCache();
package/src/client.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { createHmac } from "crypto";
2
+
1
3
  const BASE_URL = "https://www.buda.com/api/v2";
2
4
 
3
5
  export class BudaApiError extends Error {
@@ -13,18 +15,47 @@ export class BudaApiError extends Error {
13
15
 
14
16
  export class BudaClient {
15
17
  private readonly baseUrl: string;
18
+ private readonly apiKey: string | undefined;
19
+ private readonly apiSecret: string | undefined;
16
20
 
17
- constructor(baseUrl: string = BASE_URL) {
21
+ constructor(
22
+ baseUrl: string = BASE_URL,
23
+ apiKey?: string,
24
+ apiSecret?: string,
25
+ ) {
18
26
  this.baseUrl = baseUrl;
27
+ this.apiKey = apiKey;
28
+ this.apiSecret = apiSecret;
29
+ }
30
+
31
+ hasAuth(): boolean {
32
+ return Boolean(this.apiKey && this.apiSecret);
33
+ }
34
+
35
+ private nonce(): string {
36
+ return String(Math.floor(Date.now() * 1000));
37
+ }
38
+
39
+ private sign(method: string, pathWithQuery: string, body: string, nonce: string): string {
40
+ const encodedBody = body ? Buffer.from(body).toString("base64") : "";
41
+ const parts = [method, pathWithQuery];
42
+ if (encodedBody) parts.push(encodedBody);
43
+ parts.push(nonce);
44
+ const msg = parts.join(" ");
45
+ return createHmac("sha384", this.apiSecret!).update(msg).digest("hex");
46
+ }
47
+
48
+ private authHeaders(method: string, path: string, body?: string): Record<string, string> {
49
+ if (!this.hasAuth()) return {};
50
+ const nonce = this.nonce();
51
+ const signature = this.sign(method, path, body ?? "", nonce);
52
+ return {
53
+ "X-SBTC-APIKEY": this.apiKey!,
54
+ "X-SBTC-NONCE": nonce,
55
+ "X-SBTC-SIGNATURE": signature,
56
+ };
19
57
  }
20
58
 
21
- /**
22
- * Perform an authenticated GET request.
23
- *
24
- * Private endpoints (balances, orders, etc.) require HMAC-SHA2 signing.
25
- * To add auth later, extend this method with the `apiKey` + `apiSecret`
26
- * constructor params and sign the nonce/path headers here before fetching.
27
- */
28
59
  async get<T>(path: string, params?: Record<string, string | number>): Promise<T> {
29
60
  const url = new URL(`${this.baseUrl}${path}.json`);
30
61
 
@@ -34,11 +65,75 @@ export class BudaClient {
34
65
  }
35
66
  }
36
67
 
68
+ const urlPath = url.pathname + url.search;
69
+ const headers: Record<string, string> = {
70
+ Accept: "application/json",
71
+ "User-Agent": "buda-mcp/1.1.0",
72
+ ...this.authHeaders("GET", urlPath),
73
+ };
74
+
75
+ const response = await fetch(url.toString(), { headers });
76
+
77
+ if (!response.ok) {
78
+ let detail = response.statusText;
79
+ try {
80
+ const body = (await response.json()) as { message?: string };
81
+ if (body.message) detail = body.message;
82
+ } catch {
83
+ // ignore parse error, use statusText
84
+ }
85
+ throw new BudaApiError(response.status, path, `Buda API ${response.status}: ${detail}`);
86
+ }
87
+
88
+ return response.json() as Promise<T>;
89
+ }
90
+
91
+ async post<T>(path: string, payload: unknown): Promise<T> {
92
+ const url = new URL(`${this.baseUrl}${path}.json`);
93
+ const bodyStr = JSON.stringify(payload);
94
+ const urlPath = url.pathname + url.search;
95
+ const headers: Record<string, string> = {
96
+ Accept: "application/json",
97
+ "Content-Type": "application/json",
98
+ "User-Agent": "buda-mcp/1.1.0",
99
+ ...this.authHeaders("POST", urlPath, bodyStr),
100
+ };
101
+
102
+ const response = await fetch(url.toString(), {
103
+ method: "POST",
104
+ headers,
105
+ body: bodyStr,
106
+ });
107
+
108
+ if (!response.ok) {
109
+ let detail = response.statusText;
110
+ try {
111
+ const body = (await response.json()) as { message?: string };
112
+ if (body.message) detail = body.message;
113
+ } catch {
114
+ // ignore parse error, use statusText
115
+ }
116
+ throw new BudaApiError(response.status, path, `Buda API ${response.status}: ${detail}`);
117
+ }
118
+
119
+ return response.json() as Promise<T>;
120
+ }
121
+
122
+ async put<T>(path: string, payload: unknown): Promise<T> {
123
+ const url = new URL(`${this.baseUrl}${path}.json`);
124
+ const bodyStr = JSON.stringify(payload);
125
+ const urlPath = url.pathname + url.search;
126
+ const headers: Record<string, string> = {
127
+ Accept: "application/json",
128
+ "Content-Type": "application/json",
129
+ "User-Agent": "buda-mcp/1.1.0",
130
+ ...this.authHeaders("PUT", urlPath, bodyStr),
131
+ };
132
+
37
133
  const response = await fetch(url.toString(), {
38
- headers: {
39
- Accept: "application/json",
40
- "User-Agent": "buda-mcp/1.0.0",
41
- },
134
+ method: "PUT",
135
+ headers,
136
+ body: bodyStr,
42
137
  });
43
138
 
44
139
  if (!response.ok) {