@guiie/buda-mcp 1.5.1 → 1.5.3

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 (106) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/PUBLISH_CHECKLIST.md +56 -36
  3. package/dist/audit.d.ts +21 -0
  4. package/dist/audit.d.ts.map +1 -0
  5. package/dist/audit.js +14 -0
  6. package/dist/client.d.ts.map +1 -1
  7. package/dist/client.js +6 -1
  8. package/dist/http.js +47 -15
  9. package/dist/tools/account.js +1 -1
  10. package/dist/tools/arbitrage.js +1 -1
  11. package/dist/tools/balance.js +1 -1
  12. package/dist/tools/balances.js +1 -1
  13. package/dist/tools/banks.js +1 -1
  14. package/dist/tools/batch_orders.d.ts +2 -2
  15. package/dist/tools/batch_orders.d.ts.map +1 -1
  16. package/dist/tools/batch_orders.js +14 -4
  17. package/dist/tools/cancel_all_orders.d.ts +2 -2
  18. package/dist/tools/cancel_all_orders.d.ts.map +1 -1
  19. package/dist/tools/cancel_all_orders.js +12 -15
  20. package/dist/tools/cancel_order.d.ts +2 -2
  21. package/dist/tools/cancel_order.d.ts.map +1 -1
  22. package/dist/tools/cancel_order.js +11 -11
  23. package/dist/tools/cancel_order_by_client_id.d.ts +2 -2
  24. package/dist/tools/cancel_order_by_client_id.d.ts.map +1 -1
  25. package/dist/tools/cancel_order_by_client_id.js +11 -11
  26. package/dist/tools/compare_markets.d.ts +9 -0
  27. package/dist/tools/compare_markets.d.ts.map +1 -1
  28. package/dist/tools/compare_markets.js +63 -53
  29. package/dist/tools/dead_mans_switch.d.ts +1 -1
  30. package/dist/tools/dead_mans_switch.d.ts.map +1 -1
  31. package/dist/tools/dead_mans_switch.js +35 -3
  32. package/dist/tools/deposits.js +2 -2
  33. package/dist/tools/fees.js +1 -1
  34. package/dist/tools/lightning.d.ts +2 -2
  35. package/dist/tools/lightning.d.ts.map +1 -1
  36. package/dist/tools/lightning.js +13 -11
  37. package/dist/tools/market_sentiment.js +1 -1
  38. package/dist/tools/market_summary.js +1 -1
  39. package/dist/tools/markets.js +1 -1
  40. package/dist/tools/order_lookup.js +2 -2
  41. package/dist/tools/orderbook.js +1 -1
  42. package/dist/tools/orders.js +1 -1
  43. package/dist/tools/place_order.d.ts +2 -2
  44. package/dist/tools/place_order.d.ts.map +1 -1
  45. package/dist/tools/place_order.js +24 -6
  46. package/dist/tools/price_history.js +1 -1
  47. package/dist/tools/quotation.js +1 -1
  48. package/dist/tools/receive_addresses.d.ts +2 -2
  49. package/dist/tools/receive_addresses.d.ts.map +1 -1
  50. package/dist/tools/receive_addresses.js +13 -13
  51. package/dist/tools/remittance_recipients.js +2 -2
  52. package/dist/tools/remittances.d.ts +3 -3
  53. package/dist/tools/remittances.d.ts.map +1 -1
  54. package/dist/tools/remittances.js +22 -23
  55. package/dist/tools/simulate_order.js +1 -1
  56. package/dist/tools/spread.js +1 -1
  57. package/dist/tools/technical_indicators.js +1 -1
  58. package/dist/tools/ticker.js +1 -1
  59. package/dist/tools/trades.js +1 -1
  60. package/dist/tools/volume.js +1 -1
  61. package/dist/tools/withdrawals.d.ts +2 -2
  62. package/dist/tools/withdrawals.d.ts.map +1 -1
  63. package/dist/tools/withdrawals.js +12 -12
  64. package/dist/utils.d.ts +10 -0
  65. package/dist/utils.d.ts.map +1 -1
  66. package/dist/utils.js +25 -0
  67. package/package.json +2 -1
  68. package/server.json +2 -2
  69. package/src/audit.ts +24 -0
  70. package/src/client.ts +9 -1
  71. package/src/http.ts +50 -15
  72. package/src/tools/account.ts +1 -1
  73. package/src/tools/arbitrage.ts +1 -1
  74. package/src/tools/balance.ts +1 -1
  75. package/src/tools/balances.ts +1 -1
  76. package/src/tools/banks.ts +1 -1
  77. package/src/tools/batch_orders.ts +18 -3
  78. package/src/tools/cancel_all_orders.ts +16 -14
  79. package/src/tools/cancel_order.ts +15 -10
  80. package/src/tools/cancel_order_by_client_id.ts +15 -10
  81. package/src/tools/compare_markets.ts +78 -61
  82. package/src/tools/dead_mans_switch.ts +37 -2
  83. package/src/tools/deposits.ts +2 -2
  84. package/src/tools/fees.ts +1 -1
  85. package/src/tools/lightning.ts +18 -11
  86. package/src/tools/market_sentiment.ts +1 -1
  87. package/src/tools/market_summary.ts +1 -1
  88. package/src/tools/markets.ts +1 -1
  89. package/src/tools/order_lookup.ts +2 -2
  90. package/src/tools/orderbook.ts +1 -1
  91. package/src/tools/orders.ts +1 -1
  92. package/src/tools/place_order.ts +30 -7
  93. package/src/tools/price_history.ts +1 -1
  94. package/src/tools/quotation.ts +1 -1
  95. package/src/tools/receive_addresses.ts +17 -12
  96. package/src/tools/remittance_recipients.ts +2 -2
  97. package/src/tools/remittances.ts +26 -21
  98. package/src/tools/simulate_order.ts +1 -1
  99. package/src/tools/spread.ts +1 -1
  100. package/src/tools/technical_indicators.ts +1 -1
  101. package/src/tools/ticker.ts +1 -1
  102. package/src/tools/trades.ts +1 -1
  103. package/src/tools/volume.ts +1 -1
  104. package/src/tools/withdrawals.ts +16 -11
  105. package/src/utils.ts +33 -0
  106. package/test/unit.ts +362 -4
package/CHANGELOG.md CHANGED
@@ -11,6 +11,67 @@ This project uses [Semantic Versioning](https://semver.org/).
11
11
 
12
12
  ---
13
13
 
14
+ ## [1.5.3] – 2026-04-11
15
+
16
+ ### Security
17
+
18
+ - **Upstream API errors no longer forwarded to MCP clients** — `BudaClient.handleResponse` now logs the full Buda API error detail (status, path, message) to `process.stderr` as structured JSON and returns only a generic message to the MCP caller (e.g. `"Buda API error 404 on /path."`). Previously, raw upstream error messages including potential internal details were forwarded directly to clients.
19
+
20
+ - **Audit log transport field corrected for HTTP** — nine destructive tool handlers (`place_order`, `cancel_order`, `cancel_all_orders`, `cancel_order_by_client_id`, `place_batch_orders`, `create_withdrawal`, `lightning_withdrawal`, `create_receive_address`, `quote_remittance`, `accept_remittance_quote`) now correctly log `transport: "http"` when invoked via the HTTP server. Previously their `register()` functions defaulted to `"stdio"`, making all HTTP audit events appear as stdio traffic.
21
+
22
+ - **HTTP security headers via `helmet`** — Express HTTP server now applies `helmet()` as the first middleware, adding `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`, `X-DNS-Prefetch-Control`, `X-Download-Options`, and removing `X-Powered-By`.
23
+
24
+ - **Request body size limit** — `express.json()` now enforces an explicit `limit: "10kb"` on the `/mcp` endpoint, reducing the memory/CPU surface for oversized body attacks in combination with the existing rate limiter.
25
+
26
+ - **Rate limiting extended to `/health` and `/.well-known/mcp/server-card.json`** — a `staticRateLimiter` (60 req/min) now protects these endpoints, which previously had no throttling. Sufficient for all legitimate uptime monitors and Smithery discovery.
27
+
28
+ - **`trust proxy` topology documented** — added inline comment to `app.set("trust proxy", 1)` explaining the single-hop assumption (Railway), the impact on `req.ip` and `express-rate-limit` client IP detection, and the action required if an additional proxy layer is added.
29
+
30
+ ### Pending (manual)
31
+
32
+ - **CI binary pinning** — `publish.yml` should pin `mcp-publisher` to a fixed version with SHA256 verification instead of downloading `releases/latest`. Target version: `v1.5.0`, SHA256: `79bbb73ba048c5906034f73ef6286d7763bd53cf368ea0b358fc593ed360cbd5`. See `PUBLISH_CHECKLIST.md` for the exact step.
33
+
34
+ ### Added
35
+
36
+ - `helmet` dependency (v8.x) — HTTP security headers middleware.
37
+
38
+ ---
39
+
40
+ ## [1.5.2] – 2026-04-11
41
+
42
+ ### Security
43
+
44
+ - **Trust proxy configured for Railway** — added `app.set("trust proxy", 1)` to Express before any middleware. Without this, `express-rate-limit` saw the proxy IP for every request instead of the real client IP, making per-IP rate limiting effectively useless in the Railway deployment.
45
+
46
+ - **Constant-time bearer token comparison** — `mcpAuthMiddleware` now uses `crypto.timingSafeEqual` via a new `safeTokenEqual()` helper (exported from `src/utils.ts`) instead of plain string equality, eliminating the theoretical timing side-channel on the `MCP_AUTH_TOKEN` comparison.
47
+
48
+ - **PORT and MCP_RATE_LIMIT startup validation** — both environment variables are now parsed through a new `parseEnvInt(raw, fallback, min, max, name)` helper that throws a descriptive error and exits on `NaN` or out-of-range values, preventing silent misconfigurations (e.g. `MCP_RATE_LIMIT=abc` previously resolved to `NaN` and could disable the rate limiter).
49
+
50
+ - **MCP_AUTH_TOKEN entropy warning** — server now emits a `console.warn` at startup if `MCP_AUTH_TOKEN` is set but shorter than 32 characters, nudging operators toward adequately random secrets.
51
+
52
+ - **Dead man's switch fully isolated to stdio transport** — `renew_cancel_timer` and `disarm_cancel_timer` now also return `TRANSPORT_NOT_SUPPORTED` on HTTP transport (previously only `schedule_cancel_all` was blocked). An attacker with HTTP access could previously disarm or renew a timer armed via the stdio process, since both share the same module-level `timers` Map.
53
+
54
+ - **Input validation in `compare_markets`** — `base_currency` is now validated with `validateCurrency()` before fetching tickers, consistent with all other tools that accept a currency parameter. Arbitrary-length strings no longer reach the cache or API.
55
+
56
+ - **BOLT-11 invoice regex strengthened** — regex updated from `/^ln(bc|tb|bcrt)\d/i` to `/^ln(bc|tb|bcrt)\d*[munp]?1[a-z0-9]{20,}$/i`. The new pattern requires the bech32 separator `1`, at least 20 characters of bech32 data after it, and anchors at `$` — rejecting malformed strings that happen to start with the right prefix.
57
+
58
+ - **API path redaction from error responses** — removed the `path` field from all `BudaApiError` catch blocks across 31 tool handlers. The field was included in MCP tool responses, leaking internal API endpoint patterns (e.g. `/currencies/BTC/withdrawals`) to clients. The `path` property still exists on `BudaApiError` for internal use in audit logs.
59
+
60
+ - **Structured audit logging for destructive operations** — new `src/audit.ts` module with `logAudit(event: AuditEvent)` writes newline-delimited JSON to `process.stderr` for all 11 handlers with financial side-effects: `place_order`, `cancel_order`, `cancel_all_orders`, `cancel_order_by_client_id`, `place_batch_orders`, `create_withdrawal`, `lightning_withdrawal`, `create_receive_address`, `quote_remittance`, `accept_remittance_quote`, `schedule_cancel_all`. Audit events include `ts`, `tool`, `transport`, `args_summary` (sanitized — never includes `confirmation_token`, `invoice`, or `address`), `success`, and `error_code`. Each handler exposes an optional `transport` parameter (default `"stdio"`) for future HTTP-aware logging.
61
+
62
+ ### Added
63
+
64
+ - **`safeTokenEqual(a, b)` utility** — exported from `src/utils.ts`; constant-time string comparison using `crypto.timingSafeEqual`. Usable by any future code that compares secrets.
65
+ - **`parseEnvInt(raw, fallback, min, max, name)` utility** — exported from `src/utils.ts`; safe environment variable integer parsing with range validation. Used for `PORT` and `MCP_RATE_LIMIT` at startup.
66
+ - **`handleCompareMarkets` exported handler** — `compare_markets.ts` logic extracted from the inline registration closure into a named, exported function for unit testability.
67
+
68
+ ### Tests
69
+
70
+ - **+28 unit tests** covering all new security behaviors: `safeTokenEqual` (5 cases), `parseEnvInt` (6 cases), `handleCompareMarkets` validateCurrency guard (4 cases), improved BOLT-11 regex (3 cases), DMS HTTP transport guard for renew and disarm (4 cases), `logAudit` output format and secret redaction (3 cases), audit integration with `handlePlaceOrder` (1 case), `path` field absence in error responses (2 cases).
71
+ - **Updated 3 existing test fixtures** — replaced placeholder invoice string `"lnbc1000u1ptest..."` (which contained dots — invalid bech32) with a well-formed test value that satisfies the improved BOLT-11 regex.
72
+
73
+ ---
74
+
14
75
  ## [1.5.1] – 2026-04-11
15
76
 
16
77
  ### Security
@@ -1,6 +1,6 @@
1
- # Publish Checklist — buda-mcp v1.5.1
1
+ # Publish Checklist — buda-mcp v1.5.3
2
2
 
3
- Steps to publish `v1.5.1` to npm, the MCP registry, and notify community directories.
3
+ Steps to publish `v1.5.3` to npm, the MCP registry, and notify community directories.
4
4
 
5
5
  ---
6
6
 
@@ -8,7 +8,7 @@ Steps to publish `v1.5.1` to npm, the MCP registry, and notify community directo
8
8
 
9
9
  ```bash
10
10
  # Confirm version
11
- node -e "console.log(require('./package.json').version)" # should print 1.5.1
11
+ node -e "console.log(require('./package.json').version)" # should print 1.5.3
12
12
 
13
13
  # Build and test
14
14
  npm run build
@@ -37,9 +37,9 @@ Verify: https://www.npmjs.com/package/@guiie/buda-mcp
37
37
 
38
38
  ## 3. GitHub release
39
39
 
40
- Tag and release already created via `gh release create v1.5.1`. Verify at:
40
+ Tag and release already created via `gh release create v1.5.3`. Verify at:
41
41
 
42
- https://github.com/gtorreal/buda-mcp/releases/tag/v1.5.1
42
+ https://github.com/gtorreal/buda-mcp/releases/tag/v1.5.3
43
43
 
44
44
  ---
45
45
 
@@ -64,20 +64,18 @@ Verify: https://smithery.ai/server/@guiie/buda-mcp
64
64
  **Email/message template:**
65
65
 
66
66
  ```
67
- Subject: [Update] buda-mcp v1.5.1 — Security hardening release
67
+ Subject: [Update] buda-mcp v1.5.3 — Security hardening (third pass)
68
68
 
69
69
  Hi mcp.so team,
70
70
 
71
- I've released v1.5.1 of buda-mcp (@guiie/buda-mcp on npm).
71
+ I've released v1.5.3 of buda-mcp (@guiie/buda-mcp on npm).
72
72
 
73
73
  Key changes (security hardening, no new tools):
74
- - HTTP startup guard: server exits if credentials are set without MCP_AUTH_TOKEN
75
- - Rate limiting: 120 req/min per IP on /mcp (configurable via MCP_RATE_LIMIT)
76
- - Crypto address validation in create_withdrawal (BTC, ETH, USDC, USDT, LTC, BCH, XRP)
77
- - BOLT-11 invoice format validation in lightning_withdrawal
78
- - Dead man's switch blocked on HTTP transport (process restarts drop timers)
79
- - place_batch_orders: optional max_notional spending cap
80
- - 156 unit tests (was 136)
74
+ - Upstream API errors no longer forwarded to MCP clients (generic messages only, detail logged server-side)
75
+ - Audit log transport field corrected for HTTP (9 handlers previously showed "stdio" for HTTP traffic)
76
+ - HTTP security headers via helmet (X-Content-Type-Options, X-Frame-Options, Referrer-Policy, etc.)
77
+ - Request body size limit enforced (10kb) on /mcp endpoint
78
+ - Rate limiting extended to /health and /.well-known/mcp/server-card.json endpoints
81
79
 
82
80
  Links:
83
81
  - npm: https://www.npmjs.com/package/@guiie/buda-mcp
@@ -96,24 +94,23 @@ Thank you!
96
94
  **Message template:**
97
95
 
98
96
  ```
99
- Subject: [Update] buda-mcp v1.5.1
97
+ Subject: [Update] buda-mcp v1.5.3
100
98
 
101
99
  Hi Glama team,
102
100
 
103
- buda-mcp has been updated to v1.5.1.
101
+ buda-mcp has been updated to v1.5.3.
104
102
 
105
103
  Package: @guiie/buda-mcp (npm)
106
104
  Registry: io.github.gtorreal/buda-mcp (MCP Registry)
107
- Version: 1.5.1
105
+ Version: 1.5.3
108
106
 
109
- Changes (security hardening):
110
- - HTTP startup guard for missing MCP_AUTH_TOKEN
111
- - Rate limiting on /mcp (120 req/min per IP)
112
- - Crypto address format validation in create_withdrawal
113
- - BOLT-11 invoice validation in lightning_withdrawal
114
- - Dead man's switch blocked on HTTP transport
115
- - place_batch_orders: optional max_notional cap
116
- - 156 unit tests
107
+ Changes (security hardening, third pass):
108
+ - Upstream API errors no longer forwarded to MCP clients
109
+ - Audit log transport field corrected for HTTP (9 handlers)
110
+ - HTTP security headers via helmet
111
+ - Request body size limit (10kb) on /mcp endpoint
112
+ - Rate limiting on /health and server-card endpoints
113
+ - 184 unit tests
117
114
 
118
115
  Quick start:
119
116
  npx @guiie/buda-mcp
@@ -128,22 +125,45 @@ Thank you!
128
125
 
129
126
  ## 8. Post-publish verification
130
127
 
131
- - [ ] `npx @guiie/buda-mcp@1.5.1` starts successfully
132
- - [ ] `npm info @guiie/buda-mcp version` returns `1.5.1`
133
- - [ ] GitHub release tag `v1.5.1` is visible
134
- - [ ] MCP Registry entry reflects v1.5.1
128
+ - [ ] `npx @guiie/buda-mcp@1.5.3` starts successfully
129
+ - [ ] `npm info @guiie/buda-mcp version` returns `1.5.3`
130
+ - [ ] GitHub release tag `v1.5.3` is visible
131
+ - [ ] MCP Registry entry reflects v1.5.3
135
132
  - [ ] Smithery server card lists all tools
136
- - [ ] `GET /health` returns `"version":"1.5.1"` on Railway deployment
137
- - [ ] HTTP server exits if `BUDA_API_KEY` set but `MCP_AUTH_TOKEN` is absent
138
- - [ ] `create_withdrawal` rejects a truncated BTC address with `INVALID_ADDRESS`
139
- - [ ] `lightning_withdrawal` rejects a non-BOLT11 string with `INVALID_INVOICE`
140
- - [ ] `place_batch_orders` with `max_notional` rejects over-cap batch before API call
141
- - [ ] `schedule_cancel_all` via HTTP returns `TRANSPORT_NOT_SUPPORTED`
133
+ - [ ] `GET /health` returns `"version":"1.5.3"` on Railway deployment
134
+ - [ ] `GET /health` responds with `X-Content-Type-Options: nosniff` header (helmet active)
135
+ - [ ] `GET /health` rate-limited at 60 req/min
136
+ - [ ] Error responses from the MCP server show generic message (not raw Buda API detail)
137
+ - [ ] Audit log shows `"transport":"http"` for HTTP-triggered destructive tools
138
+ - [ ] Pending: manually apply CI binary pinning to `publish.yml` (see CHANGELOG v1.5.3)
142
139
  - [ ] mcp.so listing updated
143
140
  - [ ] Glama.ai listing updated
144
141
 
145
142
  ---
146
143
 
144
+ ---
145
+
146
+ ## 9. Pending manual fix — CI binary pinning
147
+
148
+ Edit `.github/workflows/publish.yml`, replace the `Install mcp-publisher` step with:
149
+
150
+ ```yaml
151
+ - name: Install mcp-publisher
152
+ env:
153
+ MCP_PUBLISHER_VERSION: "v1.5.0"
154
+ MCP_PUBLISHER_SHA256: "79bbb73ba048c5906034f73ef6286d7763bd53cf368ea0b358fc593ed360cbd5"
155
+ run: |
156
+ curl -fsSL "https://github.com/modelcontextprotocol/registry/releases/download/${MCP_PUBLISHER_VERSION}/mcp-publisher_linux_amd64.tar.gz" \
157
+ -o mcp-publisher.tar.gz
158
+ echo "${MCP_PUBLISHER_SHA256} mcp-publisher.tar.gz" | sha256sum --check
159
+ tar xz -f mcp-publisher.tar.gz mcp-publisher
160
+ sudo mv mcp-publisher /usr/local/bin/
161
+ ```
162
+
163
+ SHA256 verified against GitHub release `v1.5.0` on 2026-04-11. Update both values when bumping `mcp-publisher`.
164
+
165
+ ---
166
+
147
167
  ## ARCHIVED: previous checklists
148
168
 
149
- See git tags `v1.5.0`, `v1.4.0`, `v1.4.1`, `v1.4.2` for previous release notes and verification steps.
169
+ See git tags `v1.5.0`, `v1.5.1`, `v1.4.0`, `v1.4.1`, `v1.4.2` for previous release notes and verification steps.
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Structured audit logging for destructive MCP tool calls.
3
+ *
4
+ * Writes newline-delimited JSON to stderr so it never pollutes the stdio MCP transport
5
+ * and is captured by Railway / any log aggregator attached to the process.
6
+ *
7
+ * Rules for args_summary:
8
+ * - Include: market_id, currency, price_type, type, amount ranges
9
+ * - NEVER include: confirmation_token, invoice, address, bank_account_id
10
+ */
11
+ export interface AuditEvent {
12
+ ts: string;
13
+ tool: string;
14
+ transport: "http" | "stdio";
15
+ ip?: string;
16
+ args_summary: Record<string, unknown>;
17
+ success: boolean;
18
+ error_code?: string | number;
19
+ }
20
+ export declare function logAudit(event: AuditEvent): void;
21
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC9B;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAEhD"}
package/dist/audit.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Structured audit logging for destructive MCP tool calls.
3
+ *
4
+ * Writes newline-delimited JSON to stderr so it never pollutes the stdio MCP transport
5
+ * and is captured by Railway / any log aggregator attached to the process.
6
+ *
7
+ * Rules for args_summary:
8
+ * - Include: market_id, currency, price_type, type, amount ranges
9
+ * - NEVER include: confirmation_token, invoice, address, bank_account_id
10
+ */
11
+ export function logAudit(event) {
12
+ process.stderr.write(JSON.stringify({ audit: true, ...event }) + "\n");
13
+ }
14
+ //# sourceMappingURL=audit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAKA,qBAAa,YAAa,SAAQ,KAAK;aAEnB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM;aAEZ,YAAY,CAAC,EAAE,MAAM;gBAHrB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM,EACC,YAAY,CAAC,EAAE,MAAM,YAAA;CAKxC;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;gBAG7C,OAAO,GAAE,MAAiB,EAC1B,MAAM,CAAC,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM;IAOpB,OAAO,IAAI,OAAO;IAIlB,OAAO,CAAC,aAAa,CAAK;IAE1B,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,IAAI;IASZ,OAAO,CAAC,WAAW;IAWnB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAOzB;;;;OAIG;YACW,cAAc;YA2Bd,cAAc;IActB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAoB1E,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAmBnD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAmBlD,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAmBpF"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAKA,qBAAa,YAAa,SAAQ,KAAK;aAEnB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM;aAEZ,YAAY,CAAC,EAAE,MAAM;gBAHrB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM,EACC,YAAY,CAAC,EAAE,MAAM,YAAA;CAKxC;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;gBAG7C,OAAO,GAAE,MAAiB,EAC1B,MAAM,CAAC,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM;IAOpB,OAAO,IAAI,OAAO;IAIlB,OAAO,CAAC,aAAa,CAAK;IAE1B,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,IAAI;IASZ,OAAO,CAAC,WAAW;IAWnB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAOzB;;;;OAIG;YACW,cAAc;YA2Bd,cAAc;IAsBtB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAoB1E,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAmBnD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAmBlD,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAmBpF"}
package/dist/client.js CHANGED
@@ -90,7 +90,12 @@ export class BudaClient {
90
90
  catch {
91
91
  // ignore parse error, use statusText
92
92
  }
93
- throw new BudaApiError(response.status, path, `Buda API ${response.status}: ${detail}`);
93
+ // Log full upstream detail server-side only — never forward to MCP caller
94
+ process.stderr.write(JSON.stringify({ buda_api_error: true, status: response.status, path, detail }) + "\n");
95
+ const clientMsg = response.status === 429
96
+ ? `Rate limit exceeded on ${path}. Retry later.`
97
+ : `Buda API error ${response.status} on ${path}.`;
98
+ throw new BudaApiError(response.status, path, clientMsg);
94
99
  }
95
100
  return response.json();
96
101
  }
package/dist/http.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import express from "express";
2
+ import helmet from "helmet";
2
3
  import rateLimit from "express-rate-limit";
3
4
  import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
4
5
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
5
6
  import { BudaClient } from "./client.js";
6
7
  import { MemoryCache, CACHE_TTL } from "./cache.js";
8
+ import { safeTokenEqual, parseEnvInt } from "./utils.js";
7
9
  import { VERSION } from "./version.js";
8
10
  import { validateMarketId } from "./validation.js";
9
11
  import * as markets from "./tools/markets.js";
@@ -41,7 +43,14 @@ import * as cancelOrderByClientId from "./tools/cancel_order_by_client_id.js";
41
43
  import * as batchOrders from "./tools/batch_orders.js";
42
44
  import * as lightning from "./tools/lightning.js";
43
45
  import { handleMarketSummary } from "./tools/market_summary.js";
44
- const PORT = parseInt(process.env.PORT ?? "3000", 10);
46
+ let PORT;
47
+ try {
48
+ PORT = parseEnvInt(process.env.PORT, 3000, 1, 65535, "PORT");
49
+ }
50
+ catch (err) {
51
+ console.error(err instanceof Error ? err.message : String(err));
52
+ process.exit(1);
53
+ }
45
54
  const client = new BudaClient(undefined, process.env.BUDA_API_KEY, process.env.BUDA_API_SECRET);
46
55
  const authEnabled = client.hasAuth();
47
56
  // Schemas for the Smithery server-card — assembled from the same definitions used in register().
@@ -119,22 +128,22 @@ function createServer() {
119
128
  if (authEnabled) {
120
129
  balances.register(server, client);
121
130
  orders.register(server, client);
122
- placeOrder.register(server, client);
123
- cancelOrder.register(server, client);
131
+ placeOrder.register(server, client, "http");
132
+ cancelOrder.register(server, client, "http");
124
133
  deadMansSwitch.register(server, client, "http");
125
134
  account.register(server, client);
126
135
  balance.register(server, client);
127
136
  orderLookup.register(server, client);
128
137
  networkFees.register(server, client);
129
138
  deposits.register(server, client);
130
- withdrawals.register(server, client);
131
- receiveAddresses.register(server, client);
132
- remittances.register(server, client);
139
+ withdrawals.register(server, client, "http");
140
+ receiveAddresses.register(server, client, "http");
141
+ remittances.register(server, client, "http");
133
142
  remittanceRecipients.register(server, client);
134
- cancelAllOrders.register(server, client);
135
- cancelOrderByClientId.register(server, client);
136
- batchOrders.register(server, client);
137
- lightning.register(server, client);
143
+ cancelAllOrders.register(server, client, "http");
144
+ cancelOrderByClientId.register(server, client, "http");
145
+ batchOrders.register(server, client, "http");
146
+ lightning.register(server, client, "http");
138
147
  }
139
148
  // MCP Resources
140
149
  server.resource("buda-markets", "buda://markets", async (uri) => {
@@ -187,7 +196,12 @@ function createServer() {
187
196
  return server;
188
197
  }
189
198
  const app = express();
190
- app.use(express.json());
199
+ app.use(helmet());
200
+ // trust proxy: 1 = trust exactly one hop (Railway's reverse proxy).
201
+ // If Cloudflare or another proxy is added in front, increment this value.
202
+ // Affects: req.ip and express-rate-limit client IP detection.
203
+ app.set("trust proxy", 1);
204
+ app.use(express.json({ limit: "10kb" }));
191
205
  const MCP_AUTH_TOKEN = process.env.MCP_AUTH_TOKEN;
192
206
  if (authEnabled && !MCP_AUTH_TOKEN) {
193
207
  console.error("[buda-mcp] FATAL: BUDA_API_KEY/BUDA_API_SECRET are set but MCP_AUTH_TOKEN is not.\n" +
@@ -195,27 +209,45 @@ if (authEnabled && !MCP_AUTH_TOKEN) {
195
209
  " Set MCP_AUTH_TOKEN to a long random secret, or run in stdio mode instead.");
196
210
  process.exit(1);
197
211
  }
212
+ if (MCP_AUTH_TOKEN && MCP_AUTH_TOKEN.length < 32) {
213
+ console.warn("[buda-mcp] WARNING: MCP_AUTH_TOKEN has fewer than 32 characters. Use a longer random secret.");
214
+ }
215
+ let rateLimitMax;
216
+ try {
217
+ rateLimitMax = parseEnvInt(process.env.MCP_RATE_LIMIT, 120, 1, 10_000, "MCP_RATE_LIMIT");
218
+ }
219
+ catch (err) {
220
+ console.error(err instanceof Error ? err.message : String(err));
221
+ process.exit(1);
222
+ }
198
223
  const mcpRateLimiter = rateLimit({
199
224
  windowMs: 60_000,
200
- max: parseInt(process.env.MCP_RATE_LIMIT ?? "120", 10),
225
+ max: rateLimitMax,
201
226
  standardHeaders: true,
202
227
  legacyHeaders: false,
203
228
  message: { error: "Too many requests. Retry after 60 seconds.", code: "RATE_LIMITED" },
204
229
  });
230
+ const staticRateLimiter = rateLimit({
231
+ windowMs: 60_000,
232
+ max: 60,
233
+ standardHeaders: true,
234
+ legacyHeaders: false,
235
+ message: { error: "Too many requests.", code: "RATE_LIMITED" },
236
+ });
205
237
  function mcpAuthMiddleware(req, res, next) {
206
238
  if (!MCP_AUTH_TOKEN) {
207
239
  next();
208
240
  return;
209
241
  }
210
242
  const auth = req.headers.authorization ?? "";
211
- if (auth !== `Bearer ${MCP_AUTH_TOKEN}`) {
243
+ if (!safeTokenEqual(auth, `Bearer ${MCP_AUTH_TOKEN}`)) {
212
244
  res.status(401).json({ error: "Unauthorized" });
213
245
  return;
214
246
  }
215
247
  next();
216
248
  }
217
249
  // Health check for Railway / uptime monitors
218
- app.get("/health", (_req, res) => {
250
+ app.get("/health", staticRateLimiter, (_req, res) => {
219
251
  res.json({
220
252
  status: "ok",
221
253
  server: "buda-mcp",
@@ -225,7 +257,7 @@ app.get("/health", (_req, res) => {
225
257
  });
226
258
  // Smithery static server card — assembled programmatically from tool definitions.
227
259
  // Adding a new tool only requires exporting its toolSchema; this handler needs no changes.
228
- app.get("/.well-known/mcp/server-card.json", (_req, res) => {
260
+ app.get("/.well-known/mcp/server-card.json", staticRateLimiter, (_req, res) => {
229
261
  res.json({
230
262
  serverInfo: { name: "buda-mcp", version: VERSION },
231
263
  authentication: { required: authEnabled },
@@ -35,7 +35,7 @@ export async function handleGetAccountInfo(client) {
35
35
  }
36
36
  catch (err) {
37
37
  const msg = err instanceof BudaApiError
38
- ? { error: err.message, code: err.status, path: err.path }
38
+ ? { error: err.message, code: err.status }
39
39
  : { error: String(err), code: "UNKNOWN" };
40
40
  return {
41
41
  content: [{ type: "text", text: JSON.stringify(msg) }],
@@ -118,7 +118,7 @@ export async function handleArbitrageOpportunities({ base_currency, threshold_pc
118
118
  }
119
119
  catch (err) {
120
120
  const msg = err instanceof BudaApiError
121
- ? { error: err.message, code: err.status, path: err.path }
121
+ ? { error: err.message, code: err.status }
122
122
  : { error: String(err), code: "UNKNOWN" };
123
123
  return {
124
124
  content: [{ type: "text", text: JSON.stringify(msg) }],
@@ -56,7 +56,7 @@ export async function handleGetBalance(args, client) {
56
56
  }
57
57
  catch (err) {
58
58
  const msg = err instanceof BudaApiError
59
- ? { error: err.message, code: err.status, path: err.path }
59
+ ? { error: err.message, code: err.status }
60
60
  : { error: String(err), code: "UNKNOWN" };
61
61
  return {
62
62
  content: [{ type: "text", text: JSON.stringify(msg) }],
@@ -39,7 +39,7 @@ export function register(server, client) {
39
39
  }
40
40
  catch (err) {
41
41
  const msg = err instanceof BudaApiError
42
- ? { error: err.message, code: err.status, path: err.path }
42
+ ? { error: err.message, code: err.status }
43
43
  : { error: String(err), code: "UNKNOWN" };
44
44
  return {
45
45
  content: [{ type: "text", text: JSON.stringify(msg) }],
@@ -52,7 +52,7 @@ export async function handleGetAvailableBanks(args, client, cache) {
52
52
  };
53
53
  }
54
54
  const msg = err instanceof BudaApiError
55
- ? { error: err.message, code: err.status, path: err.path }
55
+ ? { error: err.message, code: err.status }
56
56
  : { error: String(err), code: "UNKNOWN" };
57
57
  return {
58
58
  content: [{ type: "text", text: JSON.stringify(msg) }],
@@ -70,13 +70,13 @@ type BatchOrdersArgs = {
70
70
  max_notional?: number;
71
71
  confirmation_token: string;
72
72
  };
73
- export declare function handlePlaceBatchOrders(args: BatchOrdersArgs, client: BudaClient): Promise<{
73
+ export declare function handlePlaceBatchOrders(args: BatchOrdersArgs, client: BudaClient, transport?: "http" | "stdio"): Promise<{
74
74
  content: Array<{
75
75
  type: "text";
76
76
  text: string;
77
77
  }>;
78
78
  isError?: boolean;
79
79
  }>;
80
- export declare function register(server: McpServer, client: BudaClient): void;
80
+ export declare function register(server: McpServer, client: BudaClient, transport?: "http" | "stdio"): void;
81
81
  export {};
82
82
  //# sourceMappingURL=batch_orders.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"batch_orders.d.ts","sourceRoot":"","sources":["../../src/tools/batch_orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAIxD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CtB,CAAC;AAEF,QAAA,MAAM,UAAU;;;;;;;;;;;;iBAMd,CAAC;AAEH,KAAK,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAWnD,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,eAAe,EACrB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAkIhF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CA2BpE"}
1
+ {"version":3,"file":"batch_orders.d.ts","sourceRoot":"","sources":["../../src/tools/batch_orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAKxD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CtB,CAAC;AAEF,QAAA,MAAM,UAAU;;;;;;;;;;;;iBAMd,CAAC;AAEH,KAAK,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAWnD,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,eAAe,EACrB,MAAM,EAAE,UAAU,EAClB,SAAS,GAAE,MAAM,GAAG,OAAiB,GACpC,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CA2IhF;AAED,wBAAgB,QAAQ,CACtB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,UAAU,EAClB,SAAS,GAAE,MAAM,GAAG,OAAiB,GACpC,IAAI,CA2BN"}
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { BudaApiError } from "../client.js";
3
3
  import { validateMarketId } from "../validation.js";
4
+ import { logAudit } from "../audit.js";
4
5
  export const toolSchema = {
5
6
  name: "place_batch_orders",
6
7
  description: "Place multiple orders sequentially on Buda.com (up to 20). " +
@@ -48,7 +49,7 @@ const orderShape = z.object({
48
49
  amount: z.number().positive(),
49
50
  limit_price: z.number().positive().optional(),
50
51
  });
51
- export async function handlePlaceBatchOrders(args, client) {
52
+ export async function handlePlaceBatchOrders(args, client, transport = "stdio") {
52
53
  const { orders, max_notional, confirmation_token } = args;
53
54
  if (confirmation_token !== "CONFIRM") {
54
55
  return {
@@ -161,12 +162,21 @@ export async function handlePlaceBatchOrders(args, client) {
161
162
  if (failed > 0 && succeeded > 0) {
162
163
  response.warning = "Some orders failed. Already-placed orders were NOT rolled back.";
163
164
  }
165
+ const isError = failed > 0 && succeeded === 0 ? true : undefined;
166
+ logAudit({
167
+ ts: new Date().toISOString(),
168
+ tool: "place_batch_orders",
169
+ transport,
170
+ args_summary: { order_count: orders.length, succeeded, failed },
171
+ success: !isError,
172
+ error_code: isError ? "PARTIAL_OR_FULL_FAILURE" : undefined,
173
+ });
164
174
  return {
165
175
  content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
166
- isError: failed > 0 && succeeded === 0 ? true : undefined,
176
+ isError,
167
177
  };
168
178
  }
169
- export function register(server, client) {
179
+ export function register(server, client, transport = "stdio") {
170
180
  server.tool(toolSchema.name, toolSchema.description, {
171
181
  orders: z
172
182
  .array(orderShape)
@@ -183,6 +193,6 @@ export function register(server, client) {
183
193
  confirmation_token: z
184
194
  .string()
185
195
  .describe("Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to execute."),
186
- }, (args) => handlePlaceBatchOrders(args, client));
196
+ }, (args) => handlePlaceBatchOrders(args, client, transport));
187
197
  }
188
198
  //# sourceMappingURL=batch_orders.js.map
@@ -22,13 +22,13 @@ type CancelAllOrdersArgs = {
22
22
  market_id: string;
23
23
  confirmation_token: string;
24
24
  };
25
- export declare function handleCancelAllOrders(args: CancelAllOrdersArgs, client: BudaClient): Promise<{
25
+ export declare function handleCancelAllOrders(args: CancelAllOrdersArgs, client: BudaClient, transport?: "http" | "stdio"): Promise<{
26
26
  content: Array<{
27
27
  type: "text";
28
28
  text: string;
29
29
  }>;
30
30
  isError?: boolean;
31
31
  }>;
32
- export declare function register(server: McpServer, client: BudaClient): void;
32
+ export declare function register(server: McpServer, client: BudaClient, transport?: "http" | "stdio"): void;
33
33
  export {};
34
34
  //# sourceMappingURL=cancel_all_orders.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cancel_all_orders.d.ts","sourceRoot":"","sources":["../../src/tools/cancel_all_orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAIxD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;CAuBtB,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAyDhF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAkBpE"}
1
+ {"version":3,"file":"cancel_all_orders.d.ts","sourceRoot":"","sources":["../../src/tools/cancel_all_orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAKxD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;CAuBtB,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,UAAU,EAClB,SAAS,GAAE,MAAM,GAAG,OAAiB,GACpC,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAqDhF;AAED,wBAAgB,QAAQ,CACtB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,UAAU,EAClB,SAAS,GAAE,MAAM,GAAG,OAAiB,GACpC,IAAI,CAkBN"}
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { BudaApiError } from "../client.js";
3
3
  import { validateMarketId } from "../validation.js";
4
+ import { logAudit } from "../audit.js";
4
5
  export const toolSchema = {
5
6
  name: "cancel_all_orders",
6
7
  description: "Cancel all open orders on Buda.com, optionally filtered by a specific market. " +
@@ -23,7 +24,7 @@ export const toolSchema = {
23
24
  required: ["market_id", "confirmation_token"],
24
25
  },
25
26
  };
26
- export async function handleCancelAllOrders(args, client) {
27
+ export async function handleCancelAllOrders(args, client, transport = "stdio") {
27
28
  const { market_id, confirmation_token } = args;
28
29
  if (confirmation_token !== "CONFIRM") {
29
30
  return {
@@ -55,26 +56,22 @@ export async function handleCancelAllOrders(args, client) {
55
56
  try {
56
57
  const params = market_id !== "*" ? { market_id: market_id.toLowerCase() } : undefined;
57
58
  const data = await client.delete(`/orders`, params);
58
- return {
59
- content: [
60
- {
61
- type: "text",
62
- text: JSON.stringify({ canceled_count: data.canceled_count, market_id }),
63
- },
64
- ],
59
+ const result = {
60
+ content: [{ type: "text", text: JSON.stringify({ canceled_count: data.canceled_count, market_id }) }],
65
61
  };
62
+ logAudit({ ts: new Date().toISOString(), tool: "cancel_all_orders", transport, args_summary: { market_id }, success: true });
63
+ return result;
66
64
  }
67
65
  catch (err) {
68
66
  const msg = err instanceof BudaApiError
69
- ? { error: err.message, code: err.status, path: err.path }
67
+ ? { error: err.message, code: err.status }
70
68
  : { error: String(err), code: "UNKNOWN" };
71
- return {
72
- content: [{ type: "text", text: JSON.stringify(msg) }],
73
- isError: true,
74
- };
69
+ const result = { content: [{ type: "text", text: JSON.stringify(msg) }], isError: true };
70
+ logAudit({ ts: new Date().toISOString(), tool: "cancel_all_orders", transport, args_summary: { market_id }, success: false, error_code: msg.code });
71
+ return result;
75
72
  }
76
73
  }
77
- export function register(server, client) {
74
+ export function register(server, client, transport = "stdio") {
78
75
  server.tool(toolSchema.name, toolSchema.description, {
79
76
  market_id: z
80
77
  .string()
@@ -84,6 +81,6 @@ export function register(server, client) {
84
81
  .string()
85
82
  .describe("Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to execute. " +
86
83
  "Any other value will reject the request without canceling."),
87
- }, (args) => handleCancelAllOrders(args, client));
84
+ }, (args) => handleCancelAllOrders(args, client, transport));
88
85
  }
89
86
  //# sourceMappingURL=cancel_all_orders.js.map