@guiie/buda-mcp 1.5.0 → 1.5.2

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 (123) hide show
  1. package/.cursor/rules/marketplace-docs-sync.mdc +32 -0
  2. package/CHANGELOG.md +75 -0
  3. package/PUBLISH_CHECKLIST.md +48 -89
  4. package/README.md +446 -78
  5. package/dist/audit.d.ts +21 -0
  6. package/dist/audit.d.ts.map +1 -0
  7. package/dist/audit.js +14 -0
  8. package/dist/client.d.ts +1 -0
  9. package/dist/client.d.ts.map +1 -1
  10. package/dist/client.js +2 -1
  11. package/dist/http.js +65 -7
  12. package/dist/index.js +12 -3
  13. package/dist/tools/account.js +1 -1
  14. package/dist/tools/arbitrage.js +1 -1
  15. package/dist/tools/balance.js +1 -1
  16. package/dist/tools/balances.js +1 -1
  17. package/dist/tools/banks.js +1 -1
  18. package/dist/tools/batch_orders.d.ts +6 -1
  19. package/dist/tools/batch_orders.d.ts.map +1 -1
  20. package/dist/tools/batch_orders.js +47 -3
  21. package/dist/tools/cancel_all_orders.d.ts +1 -1
  22. package/dist/tools/cancel_all_orders.d.ts.map +1 -1
  23. package/dist/tools/cancel_all_orders.js +10 -13
  24. package/dist/tools/cancel_order.d.ts +1 -1
  25. package/dist/tools/cancel_order.d.ts.map +1 -1
  26. package/dist/tools/cancel_order.js +10 -10
  27. package/dist/tools/cancel_order_by_client_id.d.ts +1 -1
  28. package/dist/tools/cancel_order_by_client_id.d.ts.map +1 -1
  29. package/dist/tools/cancel_order_by_client_id.js +9 -9
  30. package/dist/tools/compare_markets.d.ts +9 -0
  31. package/dist/tools/compare_markets.d.ts.map +1 -1
  32. package/dist/tools/compare_markets.js +63 -53
  33. package/dist/tools/dead_mans_switch.d.ts +2 -2
  34. package/dist/tools/dead_mans_switch.d.ts.map +1 -1
  35. package/dist/tools/dead_mans_switch.js +68 -6
  36. package/dist/tools/deposits.js +2 -2
  37. package/dist/tools/fees.js +1 -1
  38. package/dist/tools/lightning.d.ts +1 -1
  39. package/dist/tools/lightning.d.ts.map +1 -1
  40. package/dist/tools/lightning.js +25 -9
  41. package/dist/tools/market_sentiment.js +1 -1
  42. package/dist/tools/market_summary.js +1 -1
  43. package/dist/tools/markets.js +1 -1
  44. package/dist/tools/order_lookup.js +2 -2
  45. package/dist/tools/orderbook.js +1 -1
  46. package/dist/tools/orders.js +1 -1
  47. package/dist/tools/place_order.d.ts +1 -1
  48. package/dist/tools/place_order.d.ts.map +1 -1
  49. package/dist/tools/place_order.js +53 -4
  50. package/dist/tools/price_history.js +1 -1
  51. package/dist/tools/quotation.js +1 -1
  52. package/dist/tools/receive_addresses.d.ts +6 -1
  53. package/dist/tools/receive_addresses.d.ts.map +1 -1
  54. package/dist/tools/receive_addresses.js +37 -13
  55. package/dist/tools/remittance_recipients.js +2 -2
  56. package/dist/tools/remittances.d.ts +7 -2
  57. package/dist/tools/remittances.d.ts.map +1 -1
  58. package/dist/tools/remittances.js +46 -23
  59. package/dist/tools/simulate_order.js +1 -1
  60. package/dist/tools/spread.js +1 -1
  61. package/dist/tools/technical_indicators.d.ts.map +1 -1
  62. package/dist/tools/technical_indicators.js +3 -2
  63. package/dist/tools/ticker.js +1 -1
  64. package/dist/tools/trades.js +1 -1
  65. package/dist/tools/volume.js +1 -1
  66. package/dist/tools/withdrawals.d.ts +1 -1
  67. package/dist/tools/withdrawals.d.ts.map +1 -1
  68. package/dist/tools/withdrawals.js +21 -11
  69. package/dist/utils.d.ts +10 -0
  70. package/dist/utils.d.ts.map +1 -1
  71. package/dist/utils.js +29 -1
  72. package/dist/validation.d.ts +6 -0
  73. package/dist/validation.d.ts.map +1 -1
  74. package/dist/validation.js +26 -0
  75. package/dist/version.d.ts.map +1 -1
  76. package/dist/version.js +8 -1
  77. package/marketplace/README.md +1 -1
  78. package/marketplace/claude-listing.md +75 -4
  79. package/marketplace/gemini-tools.json +325 -2
  80. package/marketplace/openapi.yaml +160 -1
  81. package/package.json +2 -1
  82. package/server.json +2 -2
  83. package/src/audit.ts +24 -0
  84. package/src/client.ts +3 -1
  85. package/src/http.ts +75 -7
  86. package/src/index.ts +10 -3
  87. package/src/tools/account.ts +1 -1
  88. package/src/tools/arbitrage.ts +1 -1
  89. package/src/tools/balance.ts +1 -1
  90. package/src/tools/balances.ts +1 -1
  91. package/src/tools/banks.ts +1 -1
  92. package/src/tools/batch_orders.ts +52 -2
  93. package/src/tools/cancel_all_orders.ts +10 -12
  94. package/src/tools/cancel_order.ts +10 -9
  95. package/src/tools/cancel_order_by_client_id.ts +9 -8
  96. package/src/tools/compare_markets.ts +78 -61
  97. package/src/tools/dead_mans_switch.ts +76 -5
  98. package/src/tools/deposits.ts +2 -2
  99. package/src/tools/fees.ts +1 -1
  100. package/src/tools/lightning.ts +28 -9
  101. package/src/tools/market_sentiment.ts +1 -1
  102. package/src/tools/market_summary.ts +1 -1
  103. package/src/tools/markets.ts +1 -1
  104. package/src/tools/order_lookup.ts +2 -2
  105. package/src/tools/orderbook.ts +1 -1
  106. package/src/tools/orders.ts +1 -1
  107. package/src/tools/place_order.ts +56 -5
  108. package/src/tools/price_history.ts +1 -1
  109. package/src/tools/quotation.ts +1 -1
  110. package/src/tools/receive_addresses.ts +40 -13
  111. package/src/tools/remittance_recipients.ts +2 -2
  112. package/src/tools/remittances.ts +49 -22
  113. package/src/tools/simulate_order.ts +1 -1
  114. package/src/tools/spread.ts +1 -1
  115. package/src/tools/technical_indicators.ts +3 -2
  116. package/src/tools/ticker.ts +1 -1
  117. package/src/tools/trades.ts +1 -1
  118. package/src/tools/volume.ts +1 -1
  119. package/src/tools/withdrawals.ts +22 -10
  120. package/src/utils.ts +36 -1
  121. package/src/validation.ts +29 -0
  122. package/src/version.ts +11 -3
  123. package/test/unit.ts +623 -22
@@ -1,11 +1,13 @@
1
1
  import { z } from "zod";
2
2
  import { BudaApiError } from "../client.js";
3
3
  import { validateCurrency } from "../validation.js";
4
+ import { logAudit } from "../audit.js";
4
5
  export const createReceiveAddressToolSchema = {
5
6
  name: "create_receive_address",
6
7
  description: "Generates a new receive address for a crypto currency. " +
7
8
  "Creates a new blockchain deposit address for the given currency. " +
8
9
  "Each call generates a distinct address. Not idempotent. " +
10
+ "IMPORTANT: Pass confirmation_token='CONFIRM' to execute. " +
9
11
  "Only applicable to crypto currencies (BTC, ETH, etc.). " +
10
12
  "Requires BUDA_API_KEY and BUDA_API_SECRET. " +
11
13
  "Example: 'Give me a fresh Bitcoin deposit address.'",
@@ -16,8 +18,12 @@ export const createReceiveAddressToolSchema = {
16
18
  type: "string",
17
19
  description: "Currency code (e.g. 'BTC', 'ETH').",
18
20
  },
21
+ confirmation_token: {
22
+ type: "string",
23
+ description: "Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to generate a new address.",
24
+ },
19
25
  },
20
- required: ["currency"],
26
+ required: ["currency", "confirmation_token"],
21
27
  },
22
28
  };
23
29
  export const listReceiveAddressesToolSchema = {
@@ -88,7 +94,7 @@ export async function handleListReceiveAddresses(args, client) {
88
94
  }
89
95
  catch (err) {
90
96
  const msg = err instanceof BudaApiError
91
- ? { error: err.message, code: err.status, path: err.path }
97
+ ? { error: err.message, code: err.status }
92
98
  : { error: String(err), code: "UNKNOWN" };
93
99
  return {
94
100
  content: [{ type: "text", text: JSON.stringify(msg) }],
@@ -113,7 +119,7 @@ export async function handleGetReceiveAddress(args, client) {
113
119
  }
114
120
  catch (err) {
115
121
  const msg = err instanceof BudaApiError
116
- ? { error: err.message, code: err.status, path: err.path }
122
+ ? { error: err.message, code: err.status }
117
123
  : { error: String(err), code: "UNKNOWN" };
118
124
  return {
119
125
  content: [{ type: "text", text: JSON.stringify(msg) }],
@@ -121,8 +127,24 @@ export async function handleGetReceiveAddress(args, client) {
121
127
  };
122
128
  }
123
129
  }
124
- export async function handleCreateReceiveAddress(args, client) {
125
- const { currency } = args;
130
+ export async function handleCreateReceiveAddress(args, client, transport = "stdio") {
131
+ const { currency, confirmation_token } = args;
132
+ if (confirmation_token !== "CONFIRM") {
133
+ return {
134
+ content: [
135
+ {
136
+ type: "text",
137
+ text: JSON.stringify({
138
+ error: "Address not generated. confirmation_token must equal 'CONFIRM' to execute. " +
139
+ "Each call creates a distinct address — review and set confirmation_token='CONFIRM' to proceed.",
140
+ code: "CONFIRMATION_REQUIRED",
141
+ preview: { currency },
142
+ }),
143
+ },
144
+ ],
145
+ isError: true,
146
+ };
147
+ }
126
148
  const validationError = validateCurrency(currency);
127
149
  if (validationError) {
128
150
  return {
@@ -132,18 +154,17 @@ export async function handleCreateReceiveAddress(args, client) {
132
154
  }
133
155
  try {
134
156
  const data = await client.post(`/currencies/${currency.toUpperCase()}/receive_addresses`, {});
135
- return {
136
- content: [{ type: "text", text: JSON.stringify(normalizeAddress(data.receive_address), null, 2) }],
137
- };
157
+ const result = { content: [{ type: "text", text: JSON.stringify(normalizeAddress(data.receive_address), null, 2) }] };
158
+ logAudit({ ts: new Date().toISOString(), tool: "create_receive_address", transport, args_summary: { currency }, success: true });
159
+ return result;
138
160
  }
139
161
  catch (err) {
140
162
  const msg = err instanceof BudaApiError
141
- ? { error: err.message, code: err.status, path: err.path }
163
+ ? { error: err.message, code: err.status }
142
164
  : { error: String(err), code: "UNKNOWN" };
143
- return {
144
- content: [{ type: "text", text: JSON.stringify(msg) }],
145
- isError: true,
146
- };
165
+ const result = { content: [{ type: "text", text: JSON.stringify(msg) }], isError: true };
166
+ logAudit({ ts: new Date().toISOString(), tool: "create_receive_address", transport, args_summary: { currency }, success: false, error_code: msg.code });
167
+ return result;
147
168
  }
148
169
  }
149
170
  export function register(server, client) {
@@ -156,6 +177,9 @@ export function register(server, client) {
156
177
  }, (args) => handleGetReceiveAddress(args, client));
157
178
  server.tool(createReceiveAddressToolSchema.name, createReceiveAddressToolSchema.description, {
158
179
  currency: z.string().min(2).max(10).describe("Currency code (e.g. 'BTC', 'ETH')."),
180
+ confirmation_token: z
181
+ .string()
182
+ .describe("Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to generate a new address."),
159
183
  }, (args) => handleCreateReceiveAddress(args, client));
160
184
  }
161
185
  //# sourceMappingURL=receive_addresses.js.map
@@ -69,7 +69,7 @@ export async function handleListRemittanceRecipients(args, client) {
69
69
  }
70
70
  catch (err) {
71
71
  const msg = err instanceof BudaApiError
72
- ? { error: err.message, code: err.status, path: err.path }
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) }],
@@ -86,7 +86,7 @@ export async function handleGetRemittanceRecipient(args, client) {
86
86
  }
87
87
  catch (err) {
88
88
  const msg = err instanceof BudaApiError
89
- ? { error: err.message, code: err.status, path: err.path }
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) }],
@@ -35,6 +35,10 @@ export declare const quoteRemittanceToolSchema: {
35
35
  type: string;
36
36
  description: string;
37
37
  };
38
+ confirmation_token: {
39
+ type: string;
40
+ description: string;
41
+ };
38
42
  };
39
43
  required: string[];
40
44
  };
@@ -94,7 +98,8 @@ export declare function handleQuoteRemittance(args: {
94
98
  currency: string;
95
99
  amount: number;
96
100
  recipient_id: number;
97
- }, client: BudaClient): Promise<{
101
+ confirmation_token: string;
102
+ }, client: BudaClient, transport?: "http" | "stdio"): Promise<{
98
103
  content: Array<{
99
104
  type: "text";
100
105
  text: string;
@@ -104,7 +109,7 @@ export declare function handleQuoteRemittance(args: {
104
109
  export declare function handleAcceptRemittanceQuote(args: {
105
110
  id: number;
106
111
  confirmation_token: string;
107
- }, client: BudaClient): Promise<{
112
+ }, client: BudaClient, transport?: "http" | "stdio"): Promise<{
108
113
  content: Array<{
109
114
  type: "text";
110
115
  text: string;
@@ -1 +1 @@
1
- {"version":3,"file":"remittances.d.ts","sourceRoot":"","sources":["../../src/tools/remittances.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAKxD,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;CAoBrC,CAAC;AAEF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;CA4BrC,CAAC;AAEF,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;CAuB3C,CAAC;AAEF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;CAiBnC,CAAC;AAgBF,wBAAsB,qBAAqB,CACzC,IAAI,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EACrC,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,CAoChF;AAED,wBAAsB,mBAAmB,CACvC,IAAI,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EACpB,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,CAgBhF;AAED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,EAChE,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,CAgChF;AAED,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,EAChD,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,CAqChF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAwCpE"}
1
+ {"version":3,"file":"remittances.d.ts","sourceRoot":"","sources":["../../src/tools/remittances.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAMxD,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;CAoBrC,CAAC;AAEF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;CAiCrC,CAAC;AAEF,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;CAuB3C,CAAC;AAEF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;CAiBnC,CAAC;AAgBF,wBAAsB,qBAAqB,CACzC,IAAI,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EACrC,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,CAoChF;AAED,wBAAsB,mBAAmB,CACvC,IAAI,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EACpB,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,CAgBhF;AAED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,EAC5F,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,CAiDhF;AAED,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,EAChD,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,CAoChF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CA2CpE"}
@@ -2,6 +2,7 @@ import { z } from "zod";
2
2
  import { BudaApiError } from "../client.js";
3
3
  import { flattenAmount } from "../utils.js";
4
4
  import { validateCurrency } from "../validation.js";
5
+ import { logAudit } from "../audit.js";
5
6
  export const listRemittancesToolSchema = {
6
7
  name: "list_remittances",
7
8
  description: "Returns all fiat remittance transfers for the authenticated Buda.com account. " +
@@ -28,7 +29,8 @@ export const quoteRemittanceToolSchema = {
28
29
  "Requests a price quote for a fiat remittance to a saved recipient. " +
29
30
  "Returns a remittance object in 'quoted' state with an expiry timestamp. " +
30
31
  "NOT idempotent — creates a new remittance record each call. " +
31
- "To execute, call accept_remittance_quote with the returned ID before it expires. " +
32
+ "IMPORTANT: Pass confirmation_token='CONFIRM' to execute. " +
33
+ "To execute the transfer, call accept_remittance_quote with the returned ID before it expires. " +
32
34
  "Requires BUDA_API_KEY and BUDA_API_SECRET. " +
33
35
  "Example: 'Get a remittance quote to send 100000 CLP to recipient 5.'",
34
36
  inputSchema: {
@@ -46,8 +48,12 @@ export const quoteRemittanceToolSchema = {
46
48
  type: "number",
47
49
  description: "ID of the saved remittance recipient.",
48
50
  },
51
+ confirmation_token: {
52
+ type: "string",
53
+ description: "Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to create the quote.",
54
+ },
49
55
  },
50
- required: ["currency", "amount", "recipient_id"],
56
+ required: ["currency", "amount", "recipient_id", "confirmation_token"],
51
57
  },
52
58
  };
53
59
  export const acceptRemittanceQuoteToolSchema = {
@@ -125,7 +131,7 @@ export async function handleListRemittances(args, client) {
125
131
  }
126
132
  catch (err) {
127
133
  const msg = err instanceof BudaApiError
128
- ? { error: err.message, code: err.status, path: err.path }
134
+ ? { error: err.message, code: err.status }
129
135
  : { error: String(err), code: "UNKNOWN" };
130
136
  return {
131
137
  content: [{ type: "text", text: JSON.stringify(msg) }],
@@ -142,7 +148,7 @@ export async function handleGetRemittance(args, client) {
142
148
  }
143
149
  catch (err) {
144
150
  const msg = err instanceof BudaApiError
145
- ? { error: err.message, code: err.status, path: err.path }
151
+ ? { error: err.message, code: err.status }
146
152
  : { error: String(err), code: "UNKNOWN" };
147
153
  return {
148
154
  content: [{ type: "text", text: JSON.stringify(msg) }],
@@ -150,8 +156,24 @@ export async function handleGetRemittance(args, client) {
150
156
  };
151
157
  }
152
158
  }
153
- export async function handleQuoteRemittance(args, client) {
154
- const { currency, amount, recipient_id } = args;
159
+ export async function handleQuoteRemittance(args, client, transport = "stdio") {
160
+ const { currency, amount, recipient_id, confirmation_token } = args;
161
+ if (confirmation_token !== "CONFIRM") {
162
+ return {
163
+ content: [
164
+ {
165
+ type: "text",
166
+ text: JSON.stringify({
167
+ error: "Remittance quote not created. confirmation_token must equal 'CONFIRM' to execute. " +
168
+ "Review the details and set confirmation_token='CONFIRM' to proceed.",
169
+ code: "CONFIRMATION_REQUIRED",
170
+ preview: { currency, amount, recipient_id },
171
+ }),
172
+ },
173
+ ],
174
+ isError: true,
175
+ };
176
+ }
155
177
  const validationError = validateCurrency(currency);
156
178
  if (validationError) {
157
179
  return {
@@ -167,21 +189,20 @@ export async function handleQuoteRemittance(args, client) {
167
189
  recipient_id,
168
190
  },
169
191
  });
170
- return {
171
- content: [{ type: "text", text: JSON.stringify(normalizeRemittance(data.remittance), null, 2) }],
172
- };
192
+ const result = { content: [{ type: "text", text: JSON.stringify(normalizeRemittance(data.remittance), null, 2) }] };
193
+ logAudit({ ts: new Date().toISOString(), tool: "quote_remittance", transport, args_summary: { currency, amount, recipient_id }, success: true });
194
+ return result;
173
195
  }
174
196
  catch (err) {
175
197
  const msg = err instanceof BudaApiError
176
- ? { error: err.message, code: err.status, path: err.path }
198
+ ? { error: err.message, code: err.status }
177
199
  : { error: String(err), code: "UNKNOWN" };
178
- return {
179
- content: [{ type: "text", text: JSON.stringify(msg) }],
180
- isError: true,
181
- };
200
+ const result = { content: [{ type: "text", text: JSON.stringify(msg) }], isError: true };
201
+ logAudit({ ts: new Date().toISOString(), tool: "quote_remittance", transport, args_summary: { currency, amount, recipient_id }, success: false, error_code: msg.code });
202
+ return result;
182
203
  }
183
204
  }
184
- export async function handleAcceptRemittanceQuote(args, client) {
205
+ export async function handleAcceptRemittanceQuote(args, client, transport = "stdio") {
185
206
  const { id, confirmation_token } = args;
186
207
  if (confirmation_token !== "CONFIRM") {
187
208
  return {
@@ -202,18 +223,17 @@ export async function handleAcceptRemittanceQuote(args, client) {
202
223
  const data = await client.put(`/remittances/${id}`, {
203
224
  remittance: { state: "confirming" },
204
225
  });
205
- return {
206
- content: [{ type: "text", text: JSON.stringify(normalizeRemittance(data.remittance), null, 2) }],
207
- };
226
+ const result = { content: [{ type: "text", text: JSON.stringify(normalizeRemittance(data.remittance), null, 2) }] };
227
+ logAudit({ ts: new Date().toISOString(), tool: "accept_remittance_quote", transport, args_summary: { remittance_id: id }, success: true });
228
+ return result;
208
229
  }
209
230
  catch (err) {
210
231
  const msg = err instanceof BudaApiError
211
- ? { error: err.message, code: err.status, path: err.path }
232
+ ? { error: err.message, code: err.status }
212
233
  : { error: String(err), code: "UNKNOWN" };
213
- return {
214
- content: [{ type: "text", text: JSON.stringify(msg) }],
215
- isError: true,
216
- };
234
+ const result = { content: [{ type: "text", text: JSON.stringify(msg) }], isError: true };
235
+ logAudit({ ts: new Date().toISOString(), tool: "accept_remittance_quote", transport, args_summary: { remittance_id: id }, success: false, error_code: msg.code });
236
+ return result;
217
237
  }
218
238
  }
219
239
  export function register(server, client) {
@@ -228,6 +248,9 @@ export function register(server, client) {
228
248
  currency: z.string().min(2).max(10).describe("Fiat currency code (e.g. 'CLP', 'COP')."),
229
249
  amount: z.number().positive().describe("Amount to remit (positive number)."),
230
250
  recipient_id: z.number().int().positive().describe("ID of the saved remittance recipient."),
251
+ confirmation_token: z
252
+ .string()
253
+ .describe("Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to create the quote."),
231
254
  }, (args) => handleQuoteRemittance(args, client));
232
255
  server.tool(acceptRemittanceQuoteToolSchema.name, acceptRemittanceQuoteToolSchema.description, {
233
256
  id: z.number().int().positive().describe("The numeric ID of the remittance quote to accept."),
@@ -110,7 +110,7 @@ export async function handleSimulateOrder(args, client, cache) {
110
110
  }
111
111
  catch (err) {
112
112
  const msg = err instanceof BudaApiError
113
- ? { error: err.message, code: err.status, path: err.path }
113
+ ? { error: err.message, code: err.status }
114
114
  : { error: String(err), code: "UNKNOWN" };
115
115
  return {
116
116
  content: [{ type: "text", text: JSON.stringify(msg) }],
@@ -67,7 +67,7 @@ export function register(server, client, cache) {
67
67
  }
68
68
  catch (err) {
69
69
  const msg = err instanceof BudaApiError
70
- ? { error: err.message, code: err.status, path: err.path }
70
+ ? { error: err.message, code: err.status }
71
71
  : { error: String(err), code: "UNKNOWN" };
72
72
  return {
73
73
  content: [{ type: "text", text: JSON.stringify(msg) }],
@@ -1 +1 @@
1
- {"version":3,"file":"technical_indicators.d.ts","sourceRoot":"","sources":["../../src/tools/technical_indicators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAKxD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;CA8BtB,CAAC;AAsGF,KAAK,uBAAuB,GAAG;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,uBAAuB,EAC7B,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,CA2GhF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAyBpE"}
1
+ {"version":3,"file":"technical_indicators.d.ts","sourceRoot":"","sources":["../../src/tools/technical_indicators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAKxD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;CA8BtB,CAAC;AAsGF,KAAK,uBAAuB,GAAG;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,uBAAuB,EAC7B,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,CA4GhF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAyBpE"}
@@ -146,7 +146,7 @@ export async function handleTechnicalIndicators(args, client) {
146
146
  const macdResult = macd(closes, 12, 26, 9);
147
147
  const bbResult = bollingerBands(closes, 20, 2);
148
148
  const sma20 = parseFloat(sma(closes, 20).toFixed(2));
149
- const sma50 = parseFloat(sma(closes, 50).toFixed(2));
149
+ const sma50 = closes.length >= 50 ? parseFloat(sma(closes, 50).toFixed(2)) : null;
150
150
  const lastClose = closes[closes.length - 1];
151
151
  // Signal interpretations
152
152
  const rsiSignal = rsiValue !== null && rsiValue > 70
@@ -180,6 +180,7 @@ export async function handleTechnicalIndicators(args, client) {
180
180
  bollinger_bands: bbResult,
181
181
  sma_20: sma20,
182
182
  sma_50: sma50,
183
+ sma_50_warning: sma50 === null ? `insufficient data (need 50 candles, have ${closes.length})` : undefined,
183
184
  },
184
185
  signals: {
185
186
  rsi_signal: rsiSignal,
@@ -194,7 +195,7 @@ export async function handleTechnicalIndicators(args, client) {
194
195
  }
195
196
  catch (err) {
196
197
  const msg = err instanceof BudaApiError
197
- ? { error: err.message, code: err.status, path: err.path }
198
+ ? { error: err.message, code: err.status }
198
199
  : { error: String(err), code: "UNKNOWN" };
199
200
  return {
200
201
  content: [{ type: "text", text: JSON.stringify(msg) }],
@@ -60,7 +60,7 @@ export function register(server, client, cache) {
60
60
  }
61
61
  catch (err) {
62
62
  const msg = err instanceof BudaApiError
63
- ? { error: err.message, code: err.status, path: err.path }
63
+ ? { error: err.message, code: err.status }
64
64
  : { error: String(err), code: "UNKNOWN" };
65
65
  return {
66
66
  content: [{ type: "text", text: JSON.stringify(msg) }],
@@ -76,7 +76,7 @@ export function register(server, client, _cache) {
76
76
  }
77
77
  catch (err) {
78
78
  const msg = err instanceof BudaApiError
79
- ? { error: err.message, code: err.status, path: err.path }
79
+ ? { error: err.message, code: err.status }
80
80
  : { error: String(err), code: "UNKNOWN" };
81
81
  return {
82
82
  content: [{ type: "text", text: JSON.stringify(msg) }],
@@ -55,7 +55,7 @@ export function register(server, client, _cache) {
55
55
  }
56
56
  catch (err) {
57
57
  const msg = err instanceof BudaApiError
58
- ? { error: err.message, code: err.status, path: err.path }
58
+ ? { error: err.message, code: err.status }
59
59
  : { error: String(err), code: "UNKNOWN" };
60
60
  return {
61
61
  content: [{ type: "text", text: JSON.stringify(msg) }],
@@ -81,7 +81,7 @@ type CreateWithdrawalArgs = {
81
81
  bank_account_id?: number;
82
82
  confirmation_token: string;
83
83
  };
84
- export declare function handleCreateWithdrawal(args: CreateWithdrawalArgs, client: BudaClient): Promise<{
84
+ export declare function handleCreateWithdrawal(args: CreateWithdrawalArgs, client: BudaClient, transport?: "http" | "stdio"): Promise<{
85
85
  content: Array<{
86
86
  type: "text";
87
87
  text: string;
@@ -1 +1 @@
1
- {"version":3,"file":"withdrawals.d.ts","sourceRoot":"","sources":["../../src/tools/withdrawals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAKxD,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;CA8B1C,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,mBAAmB,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,CAAC;IAC/E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAqBF,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,wBAAwB,EAC9B,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,CA+ChF;AAED,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsBtC,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,oBAAoB,EAC1B,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,CAyFhF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CA+BpE"}
1
+ {"version":3,"file":"withdrawals.d.ts","sourceRoot":"","sources":["../../src/tools/withdrawals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAMxD,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;CA8B1C,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,mBAAmB,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,CAAC;IAC/E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAqBF,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,wBAAwB,EAC9B,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,CA+ChF;AAED,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuBtC,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,oBAAoB,EAC1B,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,CAkGhF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CA+BpE"}
@@ -1,7 +1,8 @@
1
1
  import { z } from "zod";
2
2
  import { BudaApiError } from "../client.js";
3
- import { validateCurrency } from "../validation.js";
3
+ import { validateCurrency, validateCryptoAddress } from "../validation.js";
4
4
  import { flattenAmount } from "../utils.js";
5
+ import { logAudit } from "../audit.js";
5
6
  export const getWithdrawalHistoryToolSchema = {
6
7
  name: "get_withdrawal_history",
7
8
  description: "Returns withdrawal history for a currency on the authenticated Buda.com account. " +
@@ -81,7 +82,7 @@ export async function handleGetWithdrawalHistory(args, client) {
81
82
  }
82
83
  catch (err) {
83
84
  const msg = err instanceof BudaApiError
84
- ? { error: err.message, code: err.status, path: err.path }
85
+ ? { error: err.message, code: err.status }
85
86
  : { error: String(err), code: "UNKNOWN" };
86
87
  return {
87
88
  content: [{ type: "text", text: JSON.stringify(msg) }],
@@ -93,6 +94,7 @@ export const createWithdrawalToolSchema = {
93
94
  name: "create_withdrawal",
94
95
  description: "Create a withdrawal on Buda.com. Supports both crypto (address) and fiat (bank_account_id) withdrawals. " +
95
96
  "Exactly one of address or bank_account_id must be provided. " +
97
+ "WARNING: Crypto withdrawals are irreversible — verify the destination address carefully before confirming. " +
96
98
  "IMPORTANT: Pass confirmation_token='CONFIRM' to execute. " +
97
99
  "Requires BUDA_API_KEY and BUDA_API_SECRET.",
98
100
  inputSchema: {
@@ -111,7 +113,7 @@ export const createWithdrawalToolSchema = {
111
113
  required: ["currency", "amount", "confirmation_token"],
112
114
  },
113
115
  };
114
- export async function handleCreateWithdrawal(args, client) {
116
+ export async function handleCreateWithdrawal(args, client, transport = "stdio") {
115
117
  const { currency, amount, address, network, bank_account_id, confirmation_token } = args;
116
118
  if (confirmation_token !== "CONFIRM") {
117
119
  return {
@@ -166,6 +168,15 @@ export async function handleCreateWithdrawal(args, client) {
166
168
  isError: true,
167
169
  };
168
170
  }
171
+ if (hasAddress) {
172
+ const addrError = validateCryptoAddress(address, currency);
173
+ if (addrError) {
174
+ return {
175
+ content: [{ type: "text", text: JSON.stringify({ error: addrError, code: "INVALID_ADDRESS" }) }],
176
+ isError: true,
177
+ };
178
+ }
179
+ }
169
180
  try {
170
181
  const payload = { amount: String(amount) };
171
182
  if (hasAddress) {
@@ -177,18 +188,17 @@ export async function handleCreateWithdrawal(args, client) {
177
188
  payload.bank_account_id = bank_account_id;
178
189
  }
179
190
  const data = await client.post(`/currencies/${currency.toUpperCase()}/withdrawals`, payload);
180
- return {
181
- content: [{ type: "text", text: JSON.stringify(normalizeWithdrawal(data.withdrawal), null, 2) }],
182
- };
191
+ const result = { content: [{ type: "text", text: JSON.stringify(normalizeWithdrawal(data.withdrawal), null, 2) }] };
192
+ logAudit({ ts: new Date().toISOString(), tool: "create_withdrawal", transport, args_summary: { currency, amount, type: hasAddress ? "crypto" : "fiat" }, success: true });
193
+ return result;
183
194
  }
184
195
  catch (err) {
185
196
  const msg = err instanceof BudaApiError
186
- ? { error: err.message, code: err.status, path: err.path }
197
+ ? { error: err.message, code: err.status }
187
198
  : { error: String(err), code: "UNKNOWN" };
188
- return {
189
- content: [{ type: "text", text: JSON.stringify(msg) }],
190
- isError: true,
191
- };
199
+ const result = { content: [{ type: "text", text: JSON.stringify(msg) }], isError: true };
200
+ logAudit({ ts: new Date().toISOString(), tool: "create_withdrawal", transport, args_summary: { currency, amount, type: hasAddress ? "crypto" : "fiat" }, success: false, error_code: msg.code });
201
+ return result;
192
202
  }
193
203
  }
194
204
  export function register(server, client) {
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,14 @@
1
1
  import type { Amount, OhlcvCandle } from "./types.js";
2
+ /**
3
+ * Constant-time string comparison to prevent timing attacks on bearer tokens.
4
+ */
5
+ export declare function safeTokenEqual(a: string, b: string): boolean;
6
+ /**
7
+ * Parses a raw string (from an environment variable) as an integer within [min, max].
8
+ * Returns the fallback when raw is undefined.
9
+ * Throws a descriptive Error if the value is non-numeric or out of range.
10
+ */
11
+ export declare function parseEnvInt(raw: string | undefined, fallback: number, min: number, max: number, name: string): number;
2
12
  /**
3
13
  * Flattens a Buda API Amount tuple [value_string, currency] into a typed object.
4
14
  * All numeric strings are cast to float via parseFloat.
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEtD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAEjF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAI/E;AAWD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,EAC3C,MAAM,EAAE,MAAM,GACb,WAAW,EAAE,CAoCf"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEtD;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAK5D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,GACX,MAAM,CASR;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAIjF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAI/E;AAWD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,EAC3C,MAAM,EAAE,MAAM,GACb,WAAW,EAAE,CAoCf"}
package/dist/utils.js CHANGED
@@ -1,9 +1,37 @@
1
+ import { timingSafeEqual } from "crypto";
2
+ /**
3
+ * Constant-time string comparison to prevent timing attacks on bearer tokens.
4
+ */
5
+ export function safeTokenEqual(a, b) {
6
+ const aBuf = Buffer.from(a);
7
+ const bBuf = Buffer.from(b);
8
+ if (aBuf.length !== bBuf.length)
9
+ return false;
10
+ return timingSafeEqual(aBuf, bBuf);
11
+ }
12
+ /**
13
+ * Parses a raw string (from an environment variable) as an integer within [min, max].
14
+ * Returns the fallback when raw is undefined.
15
+ * Throws a descriptive Error if the value is non-numeric or out of range.
16
+ */
17
+ export function parseEnvInt(raw, fallback, min, max, name) {
18
+ if (raw === undefined)
19
+ return fallback;
20
+ const n = parseInt(raw, 10);
21
+ if (isNaN(n) || n < min || n > max) {
22
+ throw new Error(`[buda-mcp] Invalid ${name} "${raw}". Must be an integer between ${min} and ${max}.`);
23
+ }
24
+ return n;
25
+ }
1
26
  /**
2
27
  * Flattens a Buda API Amount tuple [value_string, currency] into a typed object.
3
28
  * All numeric strings are cast to float via parseFloat.
4
29
  */
5
30
  export function flattenAmount(amount) {
6
- return { value: parseFloat(amount[0]), currency: amount[1] };
31
+ const value = parseFloat(amount[0]);
32
+ if (isNaN(value))
33
+ throw new Error(`Invalid amount value: "${amount[0]}"`);
34
+ return { value, currency: amount[1] };
7
35
  }
8
36
  /**
9
37
  * Returns a liquidity rating based on the bid/ask spread percentage.
@@ -8,4 +8,10 @@ export declare function validateMarketId(id: string): string | null;
8
8
  * Returns an error message string if invalid, or null if valid.
9
9
  */
10
10
  export declare function validateCurrency(id: string): string | null;
11
+ /**
12
+ * Validates a crypto withdrawal address against known per-currency formats.
13
+ * Returns an error message string if the address is invalid, or null if valid
14
+ * (including null for unknown currencies, where the exchange is the last line of defence).
15
+ */
16
+ export declare function validateCryptoAddress(address: string, currency: string): string | null;
11
17
  //# sourceMappingURL=validation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS1D;AAID;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ1D"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS1D;AAID;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ1D;AAcD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUtF"}
@@ -23,4 +23,30 @@ export function validateCurrency(id) {
23
23
  }
24
24
  return null;
25
25
  }
26
+ // Per-currency address format rules.
27
+ // Unknown currencies pass through (undefined rule) — the exchange validates those.
28
+ const ADDRESS_RULES = {
29
+ BTC: /^(bc1[a-z0-9]{6,87}|[13][a-zA-HJ-NP-Z0-9]{25,34})$/,
30
+ ETH: /^0x[0-9a-fA-F]{40}$/,
31
+ USDC: /^0x[0-9a-fA-F]{40}$/,
32
+ USDT: /^0x[0-9a-fA-F]{40}$/,
33
+ LTC: /^(ltc1[a-z0-9]{6,87}|[LM3][a-zA-HJ-NP-Z0-9]{25,34})$/,
34
+ BCH: /^(bitcoincash:)?[qp][a-z0-9]{41}$/,
35
+ XRP: /^r[1-9A-HJ-NP-Za-km-z]{24,33}$/,
36
+ };
37
+ /**
38
+ * Validates a crypto withdrawal address against known per-currency formats.
39
+ * Returns an error message string if the address is invalid, or null if valid
40
+ * (including null for unknown currencies, where the exchange is the last line of defence).
41
+ */
42
+ export function validateCryptoAddress(address, currency) {
43
+ const rule = ADDRESS_RULES[currency.toUpperCase()];
44
+ if (!rule)
45
+ return null;
46
+ if (!rule.test(address)) {
47
+ return (`Invalid ${currency.toUpperCase()} address format. ` +
48
+ `Double-check the destination address — crypto withdrawals are irreversible.`);
49
+ }
50
+ return null;
51
+ }
26
52
  //# sourceMappingURL=validation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,OAAO,EAAE,MAEb,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,OAAO,EAAE,MAAiB,CAAC"}
package/dist/version.js CHANGED
@@ -2,5 +2,12 @@ import { readFileSync } from "fs";
2
2
  import { fileURLToPath } from "url";
3
3
  import { dirname, join } from "path";
4
4
  const _dir = dirname(fileURLToPath(import.meta.url));
5
- export const VERSION = JSON.parse(readFileSync(join(_dir, "../package.json"), "utf8")).version;
5
+ let _version = "unknown";
6
+ try {
7
+ _version = JSON.parse(readFileSync(join(_dir, "../package.json"), "utf8")).version;
8
+ }
9
+ catch {
10
+ // package.json not found in deployment — use fallback
11
+ }
12
+ export const VERSION = _version;
6
13
  //# sourceMappingURL=version.js.map
@@ -1,4 +1,4 @@
1
- # Marketplace Submission Assets — v1.5.0
1
+ # Marketplace Submission Assets — v1.5.1
2
2
 
3
3
  Ready-to-use assets for submitting buda-mcp to every major AI marketplace.
4
4
  Replace `gtorreal` / `gtorreal` with your actual handles before submitting.