@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.
- package/CHANGELOG.md +61 -0
- package/PUBLISH_CHECKLIST.md +56 -36
- package/dist/audit.d.ts +21 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +14 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +6 -1
- package/dist/http.js +47 -15
- package/dist/tools/account.js +1 -1
- package/dist/tools/arbitrage.js +1 -1
- package/dist/tools/balance.js +1 -1
- package/dist/tools/balances.js +1 -1
- package/dist/tools/banks.js +1 -1
- package/dist/tools/batch_orders.d.ts +2 -2
- package/dist/tools/batch_orders.d.ts.map +1 -1
- package/dist/tools/batch_orders.js +14 -4
- package/dist/tools/cancel_all_orders.d.ts +2 -2
- package/dist/tools/cancel_all_orders.d.ts.map +1 -1
- package/dist/tools/cancel_all_orders.js +12 -15
- package/dist/tools/cancel_order.d.ts +2 -2
- package/dist/tools/cancel_order.d.ts.map +1 -1
- package/dist/tools/cancel_order.js +11 -11
- package/dist/tools/cancel_order_by_client_id.d.ts +2 -2
- package/dist/tools/cancel_order_by_client_id.d.ts.map +1 -1
- package/dist/tools/cancel_order_by_client_id.js +11 -11
- package/dist/tools/compare_markets.d.ts +9 -0
- package/dist/tools/compare_markets.d.ts.map +1 -1
- package/dist/tools/compare_markets.js +63 -53
- package/dist/tools/dead_mans_switch.d.ts +1 -1
- package/dist/tools/dead_mans_switch.d.ts.map +1 -1
- package/dist/tools/dead_mans_switch.js +35 -3
- package/dist/tools/deposits.js +2 -2
- package/dist/tools/fees.js +1 -1
- package/dist/tools/lightning.d.ts +2 -2
- package/dist/tools/lightning.d.ts.map +1 -1
- package/dist/tools/lightning.js +13 -11
- package/dist/tools/market_sentiment.js +1 -1
- package/dist/tools/market_summary.js +1 -1
- package/dist/tools/markets.js +1 -1
- package/dist/tools/order_lookup.js +2 -2
- package/dist/tools/orderbook.js +1 -1
- package/dist/tools/orders.js +1 -1
- package/dist/tools/place_order.d.ts +2 -2
- package/dist/tools/place_order.d.ts.map +1 -1
- package/dist/tools/place_order.js +24 -6
- package/dist/tools/price_history.js +1 -1
- package/dist/tools/quotation.js +1 -1
- package/dist/tools/receive_addresses.d.ts +2 -2
- package/dist/tools/receive_addresses.d.ts.map +1 -1
- package/dist/tools/receive_addresses.js +13 -13
- package/dist/tools/remittance_recipients.js +2 -2
- package/dist/tools/remittances.d.ts +3 -3
- package/dist/tools/remittances.d.ts.map +1 -1
- package/dist/tools/remittances.js +22 -23
- package/dist/tools/simulate_order.js +1 -1
- package/dist/tools/spread.js +1 -1
- package/dist/tools/technical_indicators.js +1 -1
- package/dist/tools/ticker.js +1 -1
- package/dist/tools/trades.js +1 -1
- package/dist/tools/volume.js +1 -1
- package/dist/tools/withdrawals.d.ts +2 -2
- package/dist/tools/withdrawals.d.ts.map +1 -1
- package/dist/tools/withdrawals.js +12 -12
- package/dist/utils.d.ts +10 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +25 -0
- package/package.json +2 -1
- package/server.json +2 -2
- package/src/audit.ts +24 -0
- package/src/client.ts +9 -1
- package/src/http.ts +50 -15
- package/src/tools/account.ts +1 -1
- package/src/tools/arbitrage.ts +1 -1
- package/src/tools/balance.ts +1 -1
- package/src/tools/balances.ts +1 -1
- package/src/tools/banks.ts +1 -1
- package/src/tools/batch_orders.ts +18 -3
- package/src/tools/cancel_all_orders.ts +16 -14
- package/src/tools/cancel_order.ts +15 -10
- package/src/tools/cancel_order_by_client_id.ts +15 -10
- package/src/tools/compare_markets.ts +78 -61
- package/src/tools/dead_mans_switch.ts +37 -2
- package/src/tools/deposits.ts +2 -2
- package/src/tools/fees.ts +1 -1
- package/src/tools/lightning.ts +18 -11
- package/src/tools/market_sentiment.ts +1 -1
- package/src/tools/market_summary.ts +1 -1
- package/src/tools/markets.ts +1 -1
- package/src/tools/order_lookup.ts +2 -2
- package/src/tools/orderbook.ts +1 -1
- package/src/tools/orders.ts +1 -1
- package/src/tools/place_order.ts +30 -7
- package/src/tools/price_history.ts +1 -1
- package/src/tools/quotation.ts +1 -1
- package/src/tools/receive_addresses.ts +17 -12
- package/src/tools/remittance_recipients.ts +2 -2
- package/src/tools/remittances.ts +26 -21
- package/src/tools/simulate_order.ts +1 -1
- package/src/tools/spread.ts +1 -1
- package/src/tools/technical_indicators.ts +1 -1
- package/src/tools/ticker.ts +1 -1
- package/src/tools/trades.ts +1 -1
- package/src/tools/volume.ts +1 -1
- package/src/tools/withdrawals.ts +16 -11
- package/src/utils.ts +33 -0
- package/test/unit.ts +362 -4
package/src/tools/fees.ts
CHANGED
|
@@ -69,7 +69,7 @@ export async function handleGetNetworkFees(
|
|
|
69
69
|
} catch (err) {
|
|
70
70
|
const msg =
|
|
71
71
|
err instanceof BudaApiError
|
|
72
|
-
? { error: err.message, code: err.status
|
|
72
|
+
? { error: err.message, code: err.status }
|
|
73
73
|
: { error: String(err), code: "UNKNOWN" };
|
|
74
74
|
return {
|
|
75
75
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
package/src/tools/lightning.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { BudaClient, BudaApiError } from "../client.js";
|
|
4
4
|
import { flattenAmount } from "../utils.js";
|
|
5
|
+
import { logAudit } from "../audit.js";
|
|
5
6
|
import type { LightningWithdrawalResponse, LightningInvoiceResponse } from "../types.js";
|
|
6
7
|
|
|
7
8
|
export const lightningWithdrawalToolSchema = {
|
|
@@ -69,6 +70,7 @@ type CreateLightningInvoiceArgs = {
|
|
|
69
70
|
export async function handleLightningWithdrawal(
|
|
70
71
|
args: LightningWithdrawalArgs,
|
|
71
72
|
client: BudaClient,
|
|
73
|
+
transport: "http" | "stdio" = "stdio",
|
|
72
74
|
): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
|
|
73
75
|
const { invoice, confirmation_token } = args;
|
|
74
76
|
|
|
@@ -91,7 +93,7 @@ export async function handleLightningWithdrawal(
|
|
|
91
93
|
};
|
|
92
94
|
}
|
|
93
95
|
|
|
94
|
-
const BOLT11_RE = /^ln(bc|tb|bcrt)\d
|
|
96
|
+
const BOLT11_RE = /^ln(bc|tb|bcrt)\d*[munp]?1[a-z0-9]{20,}$/i;
|
|
95
97
|
if (!BOLT11_RE.test(invoice)) {
|
|
96
98
|
return {
|
|
97
99
|
content: [{
|
|
@@ -117,10 +119,10 @@ export async function handleLightningWithdrawal(
|
|
|
117
119
|
const amount = flattenAmount(lw.amount);
|
|
118
120
|
const fee = flattenAmount(lw.fee);
|
|
119
121
|
|
|
120
|
-
|
|
122
|
+
const result = {
|
|
121
123
|
content: [
|
|
122
124
|
{
|
|
123
|
-
type: "text",
|
|
125
|
+
type: "text" as const,
|
|
124
126
|
text: JSON.stringify(
|
|
125
127
|
{
|
|
126
128
|
id: lw.id,
|
|
@@ -138,15 +140,16 @@ export async function handleLightningWithdrawal(
|
|
|
138
140
|
},
|
|
139
141
|
],
|
|
140
142
|
};
|
|
143
|
+
logAudit({ ts: new Date().toISOString(), tool: "lightning_withdrawal", transport, args_summary: {}, success: true });
|
|
144
|
+
return result;
|
|
141
145
|
} catch (err) {
|
|
142
146
|
const msg =
|
|
143
147
|
err instanceof BudaApiError
|
|
144
|
-
? { error: err.message, code: err.status
|
|
148
|
+
? { error: err.message, code: err.status }
|
|
145
149
|
: { error: String(err), code: "UNKNOWN" };
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
};
|
|
150
|
+
const result = { content: [{ type: "text" as const, text: JSON.stringify(msg) }], isError: true as const };
|
|
151
|
+
logAudit({ ts: new Date().toISOString(), tool: "lightning_withdrawal", transport, args_summary: {}, success: false, error_code: msg.code });
|
|
152
|
+
return result;
|
|
150
153
|
}
|
|
151
154
|
}
|
|
152
155
|
|
|
@@ -192,7 +195,7 @@ export async function handleCreateLightningInvoice(
|
|
|
192
195
|
} catch (err) {
|
|
193
196
|
const msg =
|
|
194
197
|
err instanceof BudaApiError
|
|
195
|
-
? { error: err.message, code: err.status
|
|
198
|
+
? { error: err.message, code: err.status }
|
|
196
199
|
: { error: String(err), code: "UNKNOWN" };
|
|
197
200
|
return {
|
|
198
201
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
@@ -201,7 +204,11 @@ export async function handleCreateLightningInvoice(
|
|
|
201
204
|
}
|
|
202
205
|
}
|
|
203
206
|
|
|
204
|
-
export function register(
|
|
207
|
+
export function register(
|
|
208
|
+
server: McpServer,
|
|
209
|
+
client: BudaClient,
|
|
210
|
+
transport: "http" | "stdio" = "stdio",
|
|
211
|
+
): void {
|
|
205
212
|
server.tool(
|
|
206
213
|
lightningWithdrawalToolSchema.name,
|
|
207
214
|
lightningWithdrawalToolSchema.description,
|
|
@@ -217,7 +224,7 @@ export function register(server: McpServer, client: BudaClient): void {
|
|
|
217
224
|
"Any other value will reject the request without paying.",
|
|
218
225
|
),
|
|
219
226
|
},
|
|
220
|
-
(args) => handleLightningWithdrawal(args, client),
|
|
227
|
+
(args) => handleLightningWithdrawal(args, client, transport),
|
|
221
228
|
);
|
|
222
229
|
|
|
223
230
|
server.tool(
|
|
@@ -118,7 +118,7 @@ export async function handleMarketSentiment(
|
|
|
118
118
|
} catch (err) {
|
|
119
119
|
const msg =
|
|
120
120
|
err instanceof BudaApiError
|
|
121
|
-
? { error: err.message, code: err.status
|
|
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) }],
|
|
@@ -101,7 +101,7 @@ export async function handleMarketSummary(
|
|
|
101
101
|
} catch (err) {
|
|
102
102
|
const msg =
|
|
103
103
|
err instanceof BudaApiError
|
|
104
|
-
? { error: err.message, code: err.status
|
|
104
|
+
? { error: err.message, code: err.status }
|
|
105
105
|
: { error: String(err), code: "UNKNOWN" };
|
|
106
106
|
return {
|
|
107
107
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
package/src/tools/markets.ts
CHANGED
|
@@ -68,7 +68,7 @@ export function register(server: McpServer, client: BudaClient, cache: MemoryCac
|
|
|
68
68
|
} catch (err) {
|
|
69
69
|
const msg =
|
|
70
70
|
err instanceof BudaApiError
|
|
71
|
-
? { error: err.message, code: err.status
|
|
71
|
+
? { error: err.message, code: err.status }
|
|
72
72
|
: { error: String(err), code: "UNKNOWN" };
|
|
73
73
|
return {
|
|
74
74
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
@@ -88,7 +88,7 @@ export async function handleGetOrder(
|
|
|
88
88
|
} catch (err) {
|
|
89
89
|
const msg =
|
|
90
90
|
err instanceof BudaApiError
|
|
91
|
-
? { error: err.message, code: err.status
|
|
91
|
+
? { error: err.message, code: err.status }
|
|
92
92
|
: { error: String(err), code: "UNKNOWN" };
|
|
93
93
|
return {
|
|
94
94
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
@@ -109,7 +109,7 @@ export async function handleGetOrderByClientId(
|
|
|
109
109
|
} catch (err) {
|
|
110
110
|
const msg =
|
|
111
111
|
err instanceof BudaApiError
|
|
112
|
-
? { error: err.message, code: err.status
|
|
112
|
+
? { error: err.message, code: err.status }
|
|
113
113
|
: { error: String(err), code: "UNKNOWN" };
|
|
114
114
|
return {
|
|
115
115
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
package/src/tools/orderbook.ts
CHANGED
|
@@ -83,7 +83,7 @@ export function register(server: McpServer, client: BudaClient, cache: MemoryCac
|
|
|
83
83
|
} catch (err) {
|
|
84
84
|
const msg =
|
|
85
85
|
err instanceof BudaApiError
|
|
86
|
-
? { error: err.message, code: err.status
|
|
86
|
+
? { error: err.message, code: err.status }
|
|
87
87
|
: { error: String(err), code: "UNKNOWN" };
|
|
88
88
|
return {
|
|
89
89
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
package/src/tools/orders.ts
CHANGED
|
@@ -135,7 +135,7 @@ export function register(server: McpServer, client: BudaClient): void {
|
|
|
135
135
|
} catch (err) {
|
|
136
136
|
const msg =
|
|
137
137
|
err instanceof BudaApiError
|
|
138
|
-
? { error: err.message, code: err.status
|
|
138
|
+
? { error: err.message, code: err.status }
|
|
139
139
|
: { error: String(err), code: "UNKNOWN" };
|
|
140
140
|
return {
|
|
141
141
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
package/src/tools/place_order.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { BudaClient, BudaApiError } from "../client.js";
|
|
4
4
|
import { validateMarketId } from "../validation.js";
|
|
5
|
+
import { logAudit } from "../audit.js";
|
|
5
6
|
import type { OrderResponse } from "../types.js";
|
|
6
7
|
|
|
7
8
|
export const toolSchema = {
|
|
@@ -91,6 +92,7 @@ type PlaceOrderArgs = {
|
|
|
91
92
|
export async function handlePlaceOrder(
|
|
92
93
|
args: PlaceOrderArgs,
|
|
93
94
|
client: BudaClient,
|
|
95
|
+
transport: "http" | "stdio" = "stdio",
|
|
94
96
|
): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
|
|
95
97
|
const {
|
|
96
98
|
market_id,
|
|
@@ -243,22 +245,43 @@ export async function handlePlaceOrder(
|
|
|
243
245
|
payload,
|
|
244
246
|
);
|
|
245
247
|
|
|
246
|
-
|
|
247
|
-
content: [{ type: "text", text: JSON.stringify(data.order, null, 2) }],
|
|
248
|
+
const result = {
|
|
249
|
+
content: [{ type: "text" as const, text: JSON.stringify(data.order, null, 2) }],
|
|
248
250
|
};
|
|
251
|
+
logAudit({
|
|
252
|
+
ts: new Date().toISOString(),
|
|
253
|
+
tool: "place_order",
|
|
254
|
+
transport,
|
|
255
|
+
args_summary: { market_id, type, price_type, amount },
|
|
256
|
+
success: true,
|
|
257
|
+
});
|
|
258
|
+
return result;
|
|
249
259
|
} catch (err) {
|
|
250
260
|
const msg =
|
|
251
261
|
err instanceof BudaApiError
|
|
252
|
-
? { error: err.message, code: err.status
|
|
262
|
+
? { error: err.message, code: err.status }
|
|
253
263
|
: { error: String(err), code: "UNKNOWN" };
|
|
254
|
-
|
|
255
|
-
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
264
|
+
const result = {
|
|
265
|
+
content: [{ type: "text" as const, text: JSON.stringify(msg) }],
|
|
256
266
|
isError: true,
|
|
257
267
|
};
|
|
268
|
+
logAudit({
|
|
269
|
+
ts: new Date().toISOString(),
|
|
270
|
+
tool: "place_order",
|
|
271
|
+
transport,
|
|
272
|
+
args_summary: { market_id, type, price_type, amount },
|
|
273
|
+
success: false,
|
|
274
|
+
error_code: msg.code,
|
|
275
|
+
});
|
|
276
|
+
return result;
|
|
258
277
|
}
|
|
259
278
|
}
|
|
260
279
|
|
|
261
|
-
export function register(
|
|
280
|
+
export function register(
|
|
281
|
+
server: McpServer,
|
|
282
|
+
client: BudaClient,
|
|
283
|
+
transport: "http" | "stdio" = "stdio",
|
|
284
|
+
): void {
|
|
262
285
|
server.tool(
|
|
263
286
|
toolSchema.name,
|
|
264
287
|
toolSchema.description,
|
|
@@ -316,6 +339,6 @@ export function register(server: McpServer, client: BudaClient): void {
|
|
|
316
339
|
"Any other value will reject the request without placing an order.",
|
|
317
340
|
),
|
|
318
341
|
},
|
|
319
|
-
(args) => handlePlaceOrder(args, client),
|
|
342
|
+
(args) => handlePlaceOrder(args, client, transport),
|
|
320
343
|
);
|
|
321
344
|
}
|
|
@@ -108,7 +108,7 @@ export function register(server: McpServer, client: BudaClient, _cache: MemoryCa
|
|
|
108
108
|
} catch (err) {
|
|
109
109
|
const msg =
|
|
110
110
|
err instanceof BudaApiError
|
|
111
|
-
? { error: err.message, code: err.status
|
|
111
|
+
? { error: err.message, code: err.status }
|
|
112
112
|
: { error: String(err), code: "UNKNOWN" };
|
|
113
113
|
return {
|
|
114
114
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
package/src/tools/quotation.ts
CHANGED
|
@@ -100,7 +100,7 @@ export async function handleGetRealQuotation(
|
|
|
100
100
|
} catch (err) {
|
|
101
101
|
const msg =
|
|
102
102
|
err instanceof BudaApiError
|
|
103
|
-
? { error: err.message, code: err.status
|
|
103
|
+
? { error: err.message, code: err.status }
|
|
104
104
|
: { error: String(err), code: "UNKNOWN" };
|
|
105
105
|
return {
|
|
106
106
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
@@ -2,6 +2,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { BudaClient, BudaApiError } from "../client.js";
|
|
4
4
|
import { validateCurrency } from "../validation.js";
|
|
5
|
+
import { logAudit } from "../audit.js";
|
|
5
6
|
import type { ReceiveAddressesResponse, SingleReceiveAddressResponse, ReceiveAddress } from "../types.js";
|
|
6
7
|
|
|
7
8
|
export const createReceiveAddressToolSchema = {
|
|
@@ -114,7 +115,7 @@ export async function handleListReceiveAddresses(
|
|
|
114
115
|
} catch (err) {
|
|
115
116
|
const msg =
|
|
116
117
|
err instanceof BudaApiError
|
|
117
|
-
? { error: err.message, code: err.status
|
|
118
|
+
? { error: err.message, code: err.status }
|
|
118
119
|
: { error: String(err), code: "UNKNOWN" };
|
|
119
120
|
return {
|
|
120
121
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
@@ -147,7 +148,7 @@ export async function handleGetReceiveAddress(
|
|
|
147
148
|
} catch (err) {
|
|
148
149
|
const msg =
|
|
149
150
|
err instanceof BudaApiError
|
|
150
|
-
? { error: err.message, code: err.status
|
|
151
|
+
? { error: err.message, code: err.status }
|
|
151
152
|
: { error: String(err), code: "UNKNOWN" };
|
|
152
153
|
return {
|
|
153
154
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
@@ -159,6 +160,7 @@ export async function handleGetReceiveAddress(
|
|
|
159
160
|
export async function handleCreateReceiveAddress(
|
|
160
161
|
args: { currency: string; confirmation_token: string },
|
|
161
162
|
client: BudaClient,
|
|
163
|
+
transport: "http" | "stdio" = "stdio",
|
|
162
164
|
): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
|
|
163
165
|
const { currency, confirmation_token } = args;
|
|
164
166
|
|
|
@@ -193,22 +195,25 @@ export async function handleCreateReceiveAddress(
|
|
|
193
195
|
`/currencies/${currency.toUpperCase()}/receive_addresses`,
|
|
194
196
|
{},
|
|
195
197
|
);
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
198
|
+
const result = { content: [{ type: "text" as const, text: JSON.stringify(normalizeAddress(data.receive_address), null, 2) }] };
|
|
199
|
+
logAudit({ ts: new Date().toISOString(), tool: "create_receive_address", transport, args_summary: { currency }, success: true });
|
|
200
|
+
return result;
|
|
199
201
|
} catch (err) {
|
|
200
202
|
const msg =
|
|
201
203
|
err instanceof BudaApiError
|
|
202
|
-
? { error: err.message, code: err.status
|
|
204
|
+
? { error: err.message, code: err.status }
|
|
203
205
|
: { error: String(err), code: "UNKNOWN" };
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
};
|
|
206
|
+
const result = { content: [{ type: "text" as const, text: JSON.stringify(msg) }], isError: true as const };
|
|
207
|
+
logAudit({ ts: new Date().toISOString(), tool: "create_receive_address", transport, args_summary: { currency }, success: false, error_code: msg.code });
|
|
208
|
+
return result;
|
|
208
209
|
}
|
|
209
210
|
}
|
|
210
211
|
|
|
211
|
-
export function register(
|
|
212
|
+
export function register(
|
|
213
|
+
server: McpServer,
|
|
214
|
+
client: BudaClient,
|
|
215
|
+
transport: "http" | "stdio" = "stdio",
|
|
216
|
+
): void {
|
|
212
217
|
server.tool(
|
|
213
218
|
listReceiveAddressesToolSchema.name,
|
|
214
219
|
listReceiveAddressesToolSchema.description,
|
|
@@ -237,6 +242,6 @@ export function register(server: McpServer, client: BudaClient): void {
|
|
|
237
242
|
.string()
|
|
238
243
|
.describe("Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to generate a new address."),
|
|
239
244
|
},
|
|
240
|
-
(args) => handleCreateReceiveAddress(args, client),
|
|
245
|
+
(args) => handleCreateReceiveAddress(args, client, transport),
|
|
241
246
|
);
|
|
242
247
|
}
|
|
@@ -87,7 +87,7 @@ export async function handleListRemittanceRecipients(
|
|
|
87
87
|
} catch (err) {
|
|
88
88
|
const msg =
|
|
89
89
|
err instanceof BudaApiError
|
|
90
|
-
? { error: err.message, code: err.status
|
|
90
|
+
? { error: err.message, code: err.status }
|
|
91
91
|
: { error: String(err), code: "UNKNOWN" };
|
|
92
92
|
return {
|
|
93
93
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
@@ -108,7 +108,7 @@ export async function handleGetRemittanceRecipient(
|
|
|
108
108
|
} catch (err) {
|
|
109
109
|
const msg =
|
|
110
110
|
err instanceof BudaApiError
|
|
111
|
-
? { error: err.message, code: err.status
|
|
111
|
+
? { error: err.message, code: err.status }
|
|
112
112
|
: { error: String(err), code: "UNKNOWN" };
|
|
113
113
|
return {
|
|
114
114
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
package/src/tools/remittances.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import { BudaClient, BudaApiError } from "../client.js";
|
|
4
4
|
import { flattenAmount } from "../utils.js";
|
|
5
5
|
import { validateCurrency } from "../validation.js";
|
|
6
|
+
import { logAudit } from "../audit.js";
|
|
6
7
|
import type { RemittancesResponse, SingleRemittanceResponse, Remittance } from "../types.js";
|
|
7
8
|
|
|
8
9
|
export const listRemittancesToolSchema = {
|
|
@@ -152,7 +153,7 @@ export async function handleListRemittances(
|
|
|
152
153
|
} catch (err) {
|
|
153
154
|
const msg =
|
|
154
155
|
err instanceof BudaApiError
|
|
155
|
-
? { error: err.message, code: err.status
|
|
156
|
+
? { error: err.message, code: err.status }
|
|
156
157
|
: { error: String(err), code: "UNKNOWN" };
|
|
157
158
|
return {
|
|
158
159
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
@@ -173,7 +174,7 @@ export async function handleGetRemittance(
|
|
|
173
174
|
} catch (err) {
|
|
174
175
|
const msg =
|
|
175
176
|
err instanceof BudaApiError
|
|
176
|
-
? { error: err.message, code: err.status
|
|
177
|
+
? { error: err.message, code: err.status }
|
|
177
178
|
: { error: String(err), code: "UNKNOWN" };
|
|
178
179
|
return {
|
|
179
180
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
@@ -185,6 +186,7 @@ export async function handleGetRemittance(
|
|
|
185
186
|
export async function handleQuoteRemittance(
|
|
186
187
|
args: { currency: string; amount: number; recipient_id: number; confirmation_token: string },
|
|
187
188
|
client: BudaClient,
|
|
189
|
+
transport: "http" | "stdio" = "stdio",
|
|
188
190
|
): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
|
|
189
191
|
const { currency, amount, recipient_id, confirmation_token } = args;
|
|
190
192
|
|
|
@@ -222,24 +224,24 @@ export async function handleQuoteRemittance(
|
|
|
222
224
|
recipient_id,
|
|
223
225
|
},
|
|
224
226
|
});
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
227
|
+
const result = { content: [{ type: "text" as const, text: JSON.stringify(normalizeRemittance(data.remittance), null, 2) }] };
|
|
228
|
+
logAudit({ ts: new Date().toISOString(), tool: "quote_remittance", transport, args_summary: { currency, amount, recipient_id }, success: true });
|
|
229
|
+
return result;
|
|
228
230
|
} catch (err) {
|
|
229
231
|
const msg =
|
|
230
232
|
err instanceof BudaApiError
|
|
231
|
-
? { error: err.message, code: err.status
|
|
233
|
+
? { error: err.message, code: err.status }
|
|
232
234
|
: { error: String(err), code: "UNKNOWN" };
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
};
|
|
235
|
+
const result = { content: [{ type: "text" as const, text: JSON.stringify(msg) }], isError: true as const };
|
|
236
|
+
logAudit({ ts: new Date().toISOString(), tool: "quote_remittance", transport, args_summary: { currency, amount, recipient_id }, success: false, error_code: msg.code });
|
|
237
|
+
return result;
|
|
237
238
|
}
|
|
238
239
|
}
|
|
239
240
|
|
|
240
241
|
export async function handleAcceptRemittanceQuote(
|
|
241
242
|
args: { id: number; confirmation_token: string },
|
|
242
243
|
client: BudaClient,
|
|
244
|
+
transport: "http" | "stdio" = "stdio",
|
|
243
245
|
): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
|
|
244
246
|
const { id, confirmation_token } = args;
|
|
245
247
|
|
|
@@ -264,22 +266,25 @@ export async function handleAcceptRemittanceQuote(
|
|
|
264
266
|
const data = await client.put<SingleRemittanceResponse>(`/remittances/${id}`, {
|
|
265
267
|
remittance: { state: "confirming" },
|
|
266
268
|
});
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
269
|
+
const result = { content: [{ type: "text" as const, text: JSON.stringify(normalizeRemittance(data.remittance), null, 2) }] };
|
|
270
|
+
logAudit({ ts: new Date().toISOString(), tool: "accept_remittance_quote", transport, args_summary: { remittance_id: id }, success: true });
|
|
271
|
+
return result;
|
|
270
272
|
} catch (err) {
|
|
271
273
|
const msg =
|
|
272
274
|
err instanceof BudaApiError
|
|
273
|
-
? { error: err.message, code: err.status
|
|
275
|
+
? { error: err.message, code: err.status }
|
|
274
276
|
: { error: String(err), code: "UNKNOWN" };
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
};
|
|
277
|
+
const result = { content: [{ type: "text" as const, text: JSON.stringify(msg) }], isError: true as const };
|
|
278
|
+
logAudit({ ts: new Date().toISOString(), tool: "accept_remittance_quote", transport, args_summary: { remittance_id: id }, success: false, error_code: msg.code });
|
|
279
|
+
return result;
|
|
279
280
|
}
|
|
280
281
|
}
|
|
281
282
|
|
|
282
|
-
export function register(
|
|
283
|
+
export function register(
|
|
284
|
+
server: McpServer,
|
|
285
|
+
client: BudaClient,
|
|
286
|
+
transport: "http" | "stdio" = "stdio",
|
|
287
|
+
): void {
|
|
283
288
|
server.tool(
|
|
284
289
|
listRemittancesToolSchema.name,
|
|
285
290
|
listRemittancesToolSchema.description,
|
|
@@ -310,7 +315,7 @@ export function register(server: McpServer, client: BudaClient): void {
|
|
|
310
315
|
.string()
|
|
311
316
|
.describe("Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to create the quote."),
|
|
312
317
|
},
|
|
313
|
-
(args) => handleQuoteRemittance(args, client),
|
|
318
|
+
(args) => handleQuoteRemittance(args, client, transport),
|
|
314
319
|
);
|
|
315
320
|
|
|
316
321
|
server.tool(
|
|
@@ -320,6 +325,6 @@ export function register(server: McpServer, client: BudaClient): void {
|
|
|
320
325
|
id: z.number().int().positive().describe("The numeric ID of the remittance quote to accept."),
|
|
321
326
|
confirmation_token: z.string().describe("Must be 'CONFIRM' to proceed. Any other value aborts."),
|
|
322
327
|
},
|
|
323
|
-
(args) => handleAcceptRemittanceQuote(args, client),
|
|
328
|
+
(args) => handleAcceptRemittanceQuote(args, client, transport),
|
|
324
329
|
);
|
|
325
330
|
}
|
|
@@ -148,7 +148,7 @@ export async function handleSimulateOrder(
|
|
|
148
148
|
} catch (err) {
|
|
149
149
|
const msg =
|
|
150
150
|
err instanceof BudaApiError
|
|
151
|
-
? { error: err.message, code: err.status
|
|
151
|
+
? { error: err.message, code: err.status }
|
|
152
152
|
: { error: String(err), code: "UNKNOWN" };
|
|
153
153
|
return {
|
|
154
154
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
package/src/tools/spread.ts
CHANGED
|
@@ -86,7 +86,7 @@ export function register(server: McpServer, client: BudaClient, cache: MemoryCac
|
|
|
86
86
|
} catch (err) {
|
|
87
87
|
const msg =
|
|
88
88
|
err instanceof BudaApiError
|
|
89
|
-
? { error: err.message, code: err.status
|
|
89
|
+
? { error: err.message, code: err.status }
|
|
90
90
|
: { error: String(err), code: "UNKNOWN" };
|
|
91
91
|
return {
|
|
92
92
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
@@ -247,7 +247,7 @@ export async function handleTechnicalIndicators(
|
|
|
247
247
|
} catch (err) {
|
|
248
248
|
const msg =
|
|
249
249
|
err instanceof BudaApiError
|
|
250
|
-
? { error: err.message, code: err.status
|
|
250
|
+
? { error: err.message, code: err.status }
|
|
251
251
|
: { error: String(err), code: "UNKNOWN" };
|
|
252
252
|
return {
|
|
253
253
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
package/src/tools/ticker.ts
CHANGED
|
@@ -77,7 +77,7 @@ export function register(server: McpServer, client: BudaClient, cache: MemoryCac
|
|
|
77
77
|
} catch (err) {
|
|
78
78
|
const msg =
|
|
79
79
|
err instanceof BudaApiError
|
|
80
|
-
? { error: err.message, code: err.status
|
|
80
|
+
? { error: err.message, code: err.status }
|
|
81
81
|
: { error: String(err), code: "UNKNOWN" };
|
|
82
82
|
return {
|
|
83
83
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
package/src/tools/trades.ts
CHANGED
|
@@ -94,7 +94,7 @@ export function register(server: McpServer, client: BudaClient, _cache: MemoryCa
|
|
|
94
94
|
} catch (err) {
|
|
95
95
|
const msg =
|
|
96
96
|
err instanceof BudaApiError
|
|
97
|
-
? { error: err.message, code: err.status
|
|
97
|
+
? { error: err.message, code: err.status }
|
|
98
98
|
: { error: String(err), code: "UNKNOWN" };
|
|
99
99
|
return {
|
|
100
100
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
package/src/tools/volume.ts
CHANGED
|
@@ -71,7 +71,7 @@ export function register(server: McpServer, client: BudaClient, _cache: MemoryCa
|
|
|
71
71
|
} catch (err) {
|
|
72
72
|
const msg =
|
|
73
73
|
err instanceof BudaApiError
|
|
74
|
-
? { error: err.message, code: err.status
|
|
74
|
+
? { error: err.message, code: err.status }
|
|
75
75
|
: { error: String(err), code: "UNKNOWN" };
|
|
76
76
|
return {
|
|
77
77
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
package/src/tools/withdrawals.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import { BudaClient, BudaApiError } from "../client.js";
|
|
4
4
|
import { validateCurrency, validateCryptoAddress } from "../validation.js";
|
|
5
5
|
import { flattenAmount } from "../utils.js";
|
|
6
|
+
import { logAudit } from "../audit.js";
|
|
6
7
|
import type { WithdrawalsResponse, SingleWithdrawalResponse, Withdrawal } from "../types.js";
|
|
7
8
|
|
|
8
9
|
export const getWithdrawalHistoryToolSchema = {
|
|
@@ -106,7 +107,7 @@ export async function handleGetWithdrawalHistory(
|
|
|
106
107
|
} catch (err) {
|
|
107
108
|
const msg =
|
|
108
109
|
err instanceof BudaApiError
|
|
109
|
-
? { error: err.message, code: err.status
|
|
110
|
+
? { error: err.message, code: err.status }
|
|
110
111
|
: { error: String(err), code: "UNKNOWN" };
|
|
111
112
|
return {
|
|
112
113
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
@@ -152,6 +153,7 @@ type CreateWithdrawalArgs = {
|
|
|
152
153
|
export async function handleCreateWithdrawal(
|
|
153
154
|
args: CreateWithdrawalArgs,
|
|
154
155
|
client: BudaClient,
|
|
156
|
+
transport: "http" | "stdio" = "stdio",
|
|
155
157
|
): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
|
|
156
158
|
const { currency, amount, address, network, bank_account_id, confirmation_token } = args;
|
|
157
159
|
|
|
@@ -238,22 +240,25 @@ export async function handleCreateWithdrawal(
|
|
|
238
240
|
payload,
|
|
239
241
|
);
|
|
240
242
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
243
|
+
const result = { content: [{ type: "text" as const, text: JSON.stringify(normalizeWithdrawal(data.withdrawal), null, 2) }] };
|
|
244
|
+
logAudit({ ts: new Date().toISOString(), tool: "create_withdrawal", transport, args_summary: { currency, amount, type: hasAddress ? "crypto" : "fiat" }, success: true });
|
|
245
|
+
return result;
|
|
244
246
|
} catch (err) {
|
|
245
247
|
const msg =
|
|
246
248
|
err instanceof BudaApiError
|
|
247
|
-
? { error: err.message, code: err.status
|
|
249
|
+
? { error: err.message, code: err.status }
|
|
248
250
|
: { error: String(err), code: "UNKNOWN" };
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
};
|
|
251
|
+
const result = { content: [{ type: "text" as const, text: JSON.stringify(msg) }], isError: true as const };
|
|
252
|
+
logAudit({ ts: new Date().toISOString(), tool: "create_withdrawal", transport, args_summary: { currency, amount, type: hasAddress ? "crypto" : "fiat" }, success: false, error_code: msg.code });
|
|
253
|
+
return result;
|
|
253
254
|
}
|
|
254
255
|
}
|
|
255
256
|
|
|
256
|
-
export function register(
|
|
257
|
+
export function register(
|
|
258
|
+
server: McpServer,
|
|
259
|
+
client: BudaClient,
|
|
260
|
+
transport: "http" | "stdio" = "stdio",
|
|
261
|
+
): void {
|
|
257
262
|
server.tool(
|
|
258
263
|
getWithdrawalHistoryToolSchema.name,
|
|
259
264
|
getWithdrawalHistoryToolSchema.description,
|
|
@@ -282,6 +287,6 @@ export function register(server: McpServer, client: BudaClient): void {
|
|
|
282
287
|
.string()
|
|
283
288
|
.describe("Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to execute."),
|
|
284
289
|
},
|
|
285
|
-
(args) => handleCreateWithdrawal(args, client),
|
|
290
|
+
(args) => handleCreateWithdrawal(args, client, transport),
|
|
286
291
|
);
|
|
287
292
|
}
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
|
+
import { timingSafeEqual } from "crypto";
|
|
1
2
|
import type { Amount, OhlcvCandle } from "./types.js";
|
|
2
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Constant-time string comparison to prevent timing attacks on bearer tokens.
|
|
6
|
+
*/
|
|
7
|
+
export function safeTokenEqual(a: string, b: string): boolean {
|
|
8
|
+
const aBuf = Buffer.from(a);
|
|
9
|
+
const bBuf = Buffer.from(b);
|
|
10
|
+
if (aBuf.length !== bBuf.length) return false;
|
|
11
|
+
return timingSafeEqual(aBuf, bBuf);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parses a raw string (from an environment variable) as an integer within [min, max].
|
|
16
|
+
* Returns the fallback when raw is undefined.
|
|
17
|
+
* Throws a descriptive Error if the value is non-numeric or out of range.
|
|
18
|
+
*/
|
|
19
|
+
export function parseEnvInt(
|
|
20
|
+
raw: string | undefined,
|
|
21
|
+
fallback: number,
|
|
22
|
+
min: number,
|
|
23
|
+
max: number,
|
|
24
|
+
name: string,
|
|
25
|
+
): number {
|
|
26
|
+
if (raw === undefined) return fallback;
|
|
27
|
+
const n = parseInt(raw, 10);
|
|
28
|
+
if (isNaN(n) || n < min || n > max) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`[buda-mcp] Invalid ${name} "${raw}". Must be an integer between ${min} and ${max}.`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return n;
|
|
34
|
+
}
|
|
35
|
+
|
|
3
36
|
/**
|
|
4
37
|
* Flattens a Buda API Amount tuple [value_string, currency] into a typed object.
|
|
5
38
|
* All numeric strings are cast to float via parseFloat.
|