@guiie/buda-mcp 1.4.2 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/cache.d.ts +1 -0
  3. package/dist/cache.d.ts.map +1 -1
  4. package/dist/cache.js +1 -0
  5. package/dist/client.d.ts +1 -0
  6. package/dist/client.d.ts.map +1 -1
  7. package/dist/client.js +16 -0
  8. package/dist/http.js +55 -0
  9. package/dist/index.js +30 -0
  10. package/dist/tools/account.d.ts +19 -0
  11. package/dist/tools/account.d.ts.map +1 -0
  12. package/dist/tools/account.js +49 -0
  13. package/dist/tools/balance.d.ts +29 -0
  14. package/dist/tools/balance.d.ts.map +1 -0
  15. package/dist/tools/balance.js +72 -0
  16. package/dist/tools/banks.d.ts +28 -0
  17. package/dist/tools/banks.d.ts.map +1 -0
  18. package/dist/tools/banks.js +68 -0
  19. package/dist/tools/batch_orders.d.ts +77 -0
  20. package/dist/tools/batch_orders.d.ts.map +1 -0
  21. package/dist/tools/batch_orders.js +154 -0
  22. package/dist/tools/cancel_all_orders.d.ts +34 -0
  23. package/dist/tools/cancel_all_orders.d.ts.map +1 -0
  24. package/dist/tools/cancel_all_orders.js +89 -0
  25. package/dist/tools/cancel_order_by_client_id.d.ts +34 -0
  26. package/dist/tools/cancel_order_by_client_id.d.ts.map +1 -0
  27. package/dist/tools/cancel_order_by_client_id.js +102 -0
  28. package/dist/tools/deposits.d.ts +83 -0
  29. package/dist/tools/deposits.d.ts.map +1 -0
  30. package/dist/tools/deposits.js +174 -0
  31. package/dist/tools/fees.d.ts +34 -0
  32. package/dist/tools/fees.d.ts.map +1 -0
  33. package/dist/tools/fees.js +72 -0
  34. package/dist/tools/lightning.d.ts +68 -0
  35. package/dist/tools/lightning.d.ts.map +1 -0
  36. package/dist/tools/lightning.js +171 -0
  37. package/dist/tools/order_lookup.d.ts +50 -0
  38. package/dist/tools/order_lookup.d.ts.map +1 -0
  39. package/dist/tools/order_lookup.js +112 -0
  40. package/dist/tools/place_order.d.ts +30 -0
  41. package/dist/tools/place_order.d.ts.map +1 -1
  42. package/dist/tools/place_order.js +100 -2
  43. package/dist/tools/quotation.d.ts +44 -0
  44. package/dist/tools/quotation.d.ts.map +1 -0
  45. package/dist/tools/quotation.js +99 -0
  46. package/dist/tools/receive_addresses.d.ts +78 -0
  47. package/dist/tools/receive_addresses.d.ts.map +1 -0
  48. package/dist/tools/receive_addresses.js +161 -0
  49. package/dist/tools/remittance_recipients.d.ts +54 -0
  50. package/dist/tools/remittance_recipients.d.ts.map +1 -0
  51. package/dist/tools/remittance_recipients.js +106 -0
  52. package/dist/tools/remittances.d.ts +115 -0
  53. package/dist/tools/remittances.d.ts.map +1 -0
  54. package/dist/tools/remittances.js +237 -0
  55. package/dist/tools/simulate_order.d.ts.map +1 -1
  56. package/dist/tools/simulate_order.js +2 -1
  57. package/dist/tools/withdrawals.d.ts +93 -0
  58. package/dist/tools/withdrawals.d.ts.map +1 -0
  59. package/dist/tools/withdrawals.js +215 -0
  60. package/dist/types.d.ts +155 -0
  61. package/dist/types.d.ts.map +1 -1
  62. package/dist/validation.d.ts +5 -0
  63. package/dist/validation.d.ts.map +1 -1
  64. package/dist/validation.js +12 -0
  65. package/marketplace/README.md +1 -1
  66. package/marketplace/claude-listing.md +30 -2
  67. package/marketplace/gemini-tools.json +155 -1
  68. package/marketplace/openapi.yaml +1 -1
  69. package/package.json +1 -1
  70. package/server.json +2 -2
  71. package/src/cache.ts +1 -0
  72. package/src/client.ts +20 -0
  73. package/src/http.ts +55 -0
  74. package/src/index.ts +30 -0
  75. package/src/tools/account.ts +66 -0
  76. package/src/tools/balance.ts +94 -0
  77. package/src/tools/banks.ts +94 -0
  78. package/src/tools/batch_orders.ts +199 -0
  79. package/src/tools/cancel_all_orders.ts +117 -0
  80. package/src/tools/cancel_order_by_client_id.ts +132 -0
  81. package/src/tools/deposits.ts +230 -0
  82. package/src/tools/fees.ts +91 -0
  83. package/src/tools/lightning.ts +231 -0
  84. package/src/tools/order_lookup.ts +139 -0
  85. package/src/tools/place_order.ts +119 -2
  86. package/src/tools/quotation.ts +124 -0
  87. package/src/tools/receive_addresses.ts +216 -0
  88. package/src/tools/remittance_recipients.ts +139 -0
  89. package/src/tools/remittances.ts +299 -0
  90. package/src/tools/simulate_order.ts +1 -0
  91. package/src/tools/withdrawals.ts +276 -0
  92. package/src/types.ts +210 -0
  93. package/src/validation.ts +16 -0
  94. package/test/run-all.ts +16 -0
  95. package/test/unit.ts +1905 -0
@@ -0,0 +1,117 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { BudaClient, BudaApiError } from "../client.js";
4
+ import { validateMarketId } from "../validation.js";
5
+ import type { CancelAllOrdersResponse } from "../types.js";
6
+
7
+ export const toolSchema = {
8
+ name: "cancel_all_orders",
9
+ description:
10
+ "Cancel all open orders on Buda.com, optionally filtered by a specific market. " +
11
+ "Pass market_id='*' to cancel across all markets, or a specific market ID (e.g. 'BTC-CLP'). " +
12
+ "IMPORTANT: This action is irreversible. Pass confirmation_token='CONFIRM' to execute. " +
13
+ "Requires BUDA_API_KEY and BUDA_API_SECRET.",
14
+ inputSchema: {
15
+ type: "object" as const,
16
+ properties: {
17
+ market_id: {
18
+ type: "string",
19
+ description: "Market ID (e.g. 'BTC-CLP') or '*' to cancel orders across all markets.",
20
+ },
21
+ confirmation_token: {
22
+ type: "string",
23
+ description:
24
+ "Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to execute. " +
25
+ "Any other value will reject the request without canceling.",
26
+ },
27
+ },
28
+ required: ["market_id", "confirmation_token"],
29
+ },
30
+ };
31
+
32
+ type CancelAllOrdersArgs = {
33
+ market_id: string;
34
+ confirmation_token: string;
35
+ };
36
+
37
+ export async function handleCancelAllOrders(
38
+ args: CancelAllOrdersArgs,
39
+ client: BudaClient,
40
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
41
+ const { market_id, confirmation_token } = args;
42
+
43
+ if (confirmation_token !== "CONFIRM") {
44
+ return {
45
+ content: [
46
+ {
47
+ type: "text",
48
+ text: JSON.stringify({
49
+ error:
50
+ "Orders not canceled. confirmation_token must equal 'CONFIRM' to execute. " +
51
+ "Review and set confirmation_token='CONFIRM' to proceed.",
52
+ code: "CONFIRMATION_REQUIRED",
53
+ preview: { market_id },
54
+ }),
55
+ },
56
+ ],
57
+ isError: true,
58
+ };
59
+ }
60
+
61
+ if (market_id !== "*") {
62
+ const validationError = validateMarketId(market_id);
63
+ if (validationError) {
64
+ return {
65
+ content: [
66
+ { type: "text", text: JSON.stringify({ error: validationError, code: "INVALID_MARKET_ID" }) },
67
+ ],
68
+ isError: true,
69
+ };
70
+ }
71
+ }
72
+
73
+ try {
74
+ const params =
75
+ market_id !== "*" ? { market_id: market_id.toLowerCase() } : undefined;
76
+
77
+ const data = await client.delete<CancelAllOrdersResponse>(`/orders`, params);
78
+
79
+ return {
80
+ content: [
81
+ {
82
+ type: "text",
83
+ text: JSON.stringify({ canceled_count: data.canceled_count, market_id }),
84
+ },
85
+ ],
86
+ };
87
+ } catch (err) {
88
+ const msg =
89
+ err instanceof BudaApiError
90
+ ? { error: err.message, code: err.status, path: err.path }
91
+ : { error: String(err), code: "UNKNOWN" };
92
+ return {
93
+ content: [{ type: "text", text: JSON.stringify(msg) }],
94
+ isError: true,
95
+ };
96
+ }
97
+ }
98
+
99
+ export function register(server: McpServer, client: BudaClient): void {
100
+ server.tool(
101
+ toolSchema.name,
102
+ toolSchema.description,
103
+ {
104
+ market_id: z
105
+ .string()
106
+ .min(1)
107
+ .describe("Market ID (e.g. 'BTC-CLP') or '*' to cancel orders across all markets."),
108
+ confirmation_token: z
109
+ .string()
110
+ .describe(
111
+ "Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to execute. " +
112
+ "Any other value will reject the request without canceling.",
113
+ ),
114
+ },
115
+ (args) => handleCancelAllOrders(args, client),
116
+ );
117
+ }
@@ -0,0 +1,132 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { BudaClient, BudaApiError } from "../client.js";
4
+ import { flattenAmount } from "../utils.js";
5
+ import type { OrderResponse, Order } from "../types.js";
6
+
7
+ export const toolSchema = {
8
+ name: "cancel_order_by_client_id",
9
+ description:
10
+ "Cancel an open order by its client-assigned ID on Buda.com. " +
11
+ "IMPORTANT: Pass confirmation_token='CONFIRM' to execute. " +
12
+ "Requires BUDA_API_KEY and BUDA_API_SECRET.",
13
+ inputSchema: {
14
+ type: "object" as const,
15
+ properties: {
16
+ client_id: {
17
+ type: "string",
18
+ description: "The client ID string assigned when placing the order.",
19
+ },
20
+ confirmation_token: {
21
+ type: "string",
22
+ description:
23
+ "Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to execute. " +
24
+ "Any other value will reject the request without canceling.",
25
+ },
26
+ },
27
+ required: ["client_id", "confirmation_token"],
28
+ },
29
+ };
30
+
31
+ type CancelOrderByClientIdArgs = {
32
+ client_id: string;
33
+ confirmation_token: string;
34
+ };
35
+
36
+ function normalizeOrder(o: Order) {
37
+ const amount = flattenAmount(o.amount);
38
+ const originalAmount = flattenAmount(o.original_amount);
39
+ const tradedAmount = flattenAmount(o.traded_amount);
40
+ const totalExchanged = flattenAmount(o.total_exchanged);
41
+ const paidFee = flattenAmount(o.paid_fee);
42
+ const limitPrice = o.limit ? flattenAmount(o.limit) : null;
43
+
44
+ return {
45
+ id: o.id,
46
+ type: o.type,
47
+ state: o.state,
48
+ created_at: o.created_at,
49
+ market_id: o.market_id,
50
+ fee_currency: o.fee_currency,
51
+ price_type: o.price_type,
52
+ order_type: o.order_type,
53
+ client_id: o.client_id,
54
+ limit_price: limitPrice ? limitPrice.value : null,
55
+ limit_price_currency: limitPrice ? limitPrice.currency : null,
56
+ amount: amount.value,
57
+ amount_currency: amount.currency,
58
+ original_amount: originalAmount.value,
59
+ original_amount_currency: originalAmount.currency,
60
+ traded_amount: tradedAmount.value,
61
+ traded_amount_currency: tradedAmount.currency,
62
+ total_exchanged: totalExchanged.value,
63
+ total_exchanged_currency: totalExchanged.currency,
64
+ paid_fee: paidFee.value,
65
+ paid_fee_currency: paidFee.currency,
66
+ };
67
+ }
68
+
69
+ export async function handleCancelOrderByClientId(
70
+ args: CancelOrderByClientIdArgs,
71
+ client: BudaClient,
72
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
73
+ const { client_id, confirmation_token } = args;
74
+
75
+ if (confirmation_token !== "CONFIRM") {
76
+ return {
77
+ content: [
78
+ {
79
+ type: "text",
80
+ text: JSON.stringify({
81
+ error:
82
+ "Order not canceled. confirmation_token must equal 'CONFIRM' to execute. " +
83
+ "Verify the client ID and set confirmation_token='CONFIRM' to proceed.",
84
+ code: "CONFIRMATION_REQUIRED",
85
+ client_id,
86
+ }),
87
+ },
88
+ ],
89
+ isError: true,
90
+ };
91
+ }
92
+
93
+ try {
94
+ const data = await client.put<OrderResponse>(
95
+ `/orders/by-client-id/${encodeURIComponent(client_id)}`,
96
+ { order: { state: "canceling" } },
97
+ );
98
+
99
+ return {
100
+ content: [{ type: "text", text: JSON.stringify(normalizeOrder(data.order), null, 2) }],
101
+ };
102
+ } catch (err) {
103
+ const msg =
104
+ err instanceof BudaApiError
105
+ ? { error: err.message, code: err.status, path: err.path }
106
+ : { error: String(err), code: "UNKNOWN" };
107
+ return {
108
+ content: [{ type: "text", text: JSON.stringify(msg) }],
109
+ isError: true,
110
+ };
111
+ }
112
+ }
113
+
114
+ export function register(server: McpServer, client: BudaClient): void {
115
+ server.tool(
116
+ toolSchema.name,
117
+ toolSchema.description,
118
+ {
119
+ client_id: z
120
+ .string()
121
+ .min(1)
122
+ .describe("The client ID string assigned when placing the order."),
123
+ confirmation_token: z
124
+ .string()
125
+ .describe(
126
+ "Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to execute. " +
127
+ "Any other value will reject the request without canceling.",
128
+ ),
129
+ },
130
+ (args) => handleCancelOrderByClientId(args, client),
131
+ );
132
+ }
@@ -0,0 +1,230 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { BudaClient, BudaApiError } from "../client.js";
4
+ import { validateCurrency } from "../validation.js";
5
+ import { flattenAmount } from "../utils.js";
6
+ import type { DepositsResponse, SingleDepositResponse, Deposit } from "../types.js";
7
+
8
+ export const getDepositHistoryToolSchema = {
9
+ name: "get_deposit_history",
10
+ description:
11
+ "Returns deposit history for a currency on the authenticated Buda.com account. " +
12
+ "Supports state filtering and pagination. All amounts are floats with separate _currency fields. " +
13
+ "Requires BUDA_API_KEY and BUDA_API_SECRET. " +
14
+ "Example: 'Show my last 10 BTC deposits.'",
15
+ inputSchema: {
16
+ type: "object" as const,
17
+ properties: {
18
+ currency: {
19
+ type: "string",
20
+ description: "Currency code (e.g. 'BTC', 'CLP').",
21
+ },
22
+ state: {
23
+ type: "string",
24
+ description: "Filter by state: 'pending_info', 'pending', 'confirmed', 'anulled', 'retained'.",
25
+ },
26
+ per: {
27
+ type: "number",
28
+ description: "Results per page (default: 20, max: 300).",
29
+ },
30
+ page: {
31
+ type: "number",
32
+ description: "Page number (default: 1).",
33
+ },
34
+ },
35
+ required: ["currency"],
36
+ },
37
+ };
38
+
39
+ type GetDepositHistoryArgs = {
40
+ currency: string;
41
+ state?: "pending_info" | "pending" | "confirmed" | "anulled" | "retained";
42
+ per?: number;
43
+ page?: number;
44
+ };
45
+
46
+ function normalizeDeposit(d: Deposit) {
47
+ const amount = flattenAmount(d.amount);
48
+ const fee = flattenAmount(d.fee);
49
+ return {
50
+ id: d.id,
51
+ state: d.state,
52
+ currency: d.currency,
53
+ amount: amount.value,
54
+ amount_currency: amount.currency,
55
+ fee: fee.value,
56
+ fee_currency: fee.currency,
57
+ created_at: d.created_at,
58
+ updated_at: d.updated_at,
59
+ transfer_account_id: d.transfer_account_id,
60
+ transaction_hash: d.transaction_hash,
61
+ };
62
+ }
63
+
64
+ export async function handleGetDepositHistory(
65
+ args: GetDepositHistoryArgs,
66
+ client: BudaClient,
67
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
68
+ const { currency, state, per, page } = args;
69
+
70
+ const validationError = validateCurrency(currency);
71
+ if (validationError) {
72
+ return {
73
+ content: [{ type: "text", text: JSON.stringify({ error: validationError, code: "INVALID_CURRENCY" }) }],
74
+ isError: true,
75
+ };
76
+ }
77
+
78
+ try {
79
+ const params: Record<string, string | number> = {};
80
+ if (state) params.state = state;
81
+ if (per !== undefined) params.per = per;
82
+ if (page !== undefined) params.page = page;
83
+
84
+ const data = await client.get<DepositsResponse>(
85
+ `/currencies/${currency.toUpperCase()}/deposits`,
86
+ Object.keys(params).length > 0 ? params : undefined,
87
+ );
88
+
89
+ return {
90
+ content: [
91
+ {
92
+ type: "text",
93
+ text: JSON.stringify(
94
+ {
95
+ deposits: data.deposits.map(normalizeDeposit),
96
+ meta: data.meta,
97
+ },
98
+ null,
99
+ 2,
100
+ ),
101
+ },
102
+ ],
103
+ };
104
+ } catch (err) {
105
+ const msg =
106
+ err instanceof BudaApiError
107
+ ? { error: err.message, code: err.status, path: err.path }
108
+ : { error: String(err), code: "UNKNOWN" };
109
+ return {
110
+ content: [{ type: "text", text: JSON.stringify(msg) }],
111
+ isError: true,
112
+ };
113
+ }
114
+ }
115
+
116
+ export const createFiatDepositToolSchema = {
117
+ name: "create_fiat_deposit",
118
+ description:
119
+ "Record a fiat deposit on Buda.com. " +
120
+ "IMPORTANT: Calling this twice creates duplicate records — the confirmation guard is critical. " +
121
+ "Pass confirmation_token='CONFIRM' to execute. " +
122
+ "Requires BUDA_API_KEY and BUDA_API_SECRET.",
123
+ inputSchema: {
124
+ type: "object" as const,
125
+ properties: {
126
+ currency: { type: "string", description: "Fiat currency code (e.g. 'CLP', 'COP', 'PEN')." },
127
+ amount: { type: "number", description: "Deposit amount." },
128
+ bank: { type: "string", description: "Bank name or identifier for the deposit source." },
129
+ confirmation_token: {
130
+ type: "string",
131
+ description: "Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to execute.",
132
+ },
133
+ },
134
+ required: ["currency", "amount", "confirmation_token"],
135
+ },
136
+ };
137
+
138
+ type CreateFiatDepositArgs = {
139
+ currency: string;
140
+ amount: number;
141
+ bank?: string;
142
+ confirmation_token: string;
143
+ };
144
+
145
+ export async function handleCreateFiatDeposit(
146
+ args: CreateFiatDepositArgs,
147
+ client: BudaClient,
148
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
149
+ const { currency, amount, bank, confirmation_token } = args;
150
+
151
+ if (confirmation_token !== "CONFIRM") {
152
+ return {
153
+ content: [
154
+ {
155
+ type: "text",
156
+ text: JSON.stringify({
157
+ error:
158
+ "Deposit not created. confirmation_token must equal 'CONFIRM' to execute. " +
159
+ "Review the details and set confirmation_token='CONFIRM' to proceed.",
160
+ code: "CONFIRMATION_REQUIRED",
161
+ preview: { currency, amount, bank },
162
+ }),
163
+ },
164
+ ],
165
+ isError: true,
166
+ };
167
+ }
168
+
169
+ const validationError = validateCurrency(currency);
170
+ if (validationError) {
171
+ return {
172
+ content: [{ type: "text", text: JSON.stringify({ error: validationError, code: "INVALID_CURRENCY" }) }],
173
+ isError: true,
174
+ };
175
+ }
176
+
177
+ try {
178
+ const payload: Record<string, unknown> = { amount: String(amount) };
179
+ if (bank) payload.bank = bank;
180
+
181
+ const data = await client.post<SingleDepositResponse>(
182
+ `/currencies/${currency.toUpperCase()}/deposits`,
183
+ payload,
184
+ );
185
+
186
+ return {
187
+ content: [{ type: "text", text: JSON.stringify(normalizeDeposit(data.deposit), null, 2) }],
188
+ };
189
+ } catch (err) {
190
+ const msg =
191
+ err instanceof BudaApiError
192
+ ? { error: err.message, code: err.status, path: err.path }
193
+ : { error: String(err), code: "UNKNOWN" };
194
+ return {
195
+ content: [{ type: "text", text: JSON.stringify(msg) }],
196
+ isError: true,
197
+ };
198
+ }
199
+ }
200
+
201
+ export function register(server: McpServer, client: BudaClient): void {
202
+ server.tool(
203
+ getDepositHistoryToolSchema.name,
204
+ getDepositHistoryToolSchema.description,
205
+ {
206
+ currency: z.string().min(2).max(10).describe("Currency code (e.g. 'BTC', 'CLP')."),
207
+ state: z
208
+ .enum(["pending_info", "pending", "confirmed", "anulled", "retained"])
209
+ .optional()
210
+ .describe("Filter by state."),
211
+ per: z.number().int().min(1).max(300).optional().describe("Results per page (default: 20, max: 300)."),
212
+ page: z.number().int().min(1).optional().describe("Page number (default: 1)."),
213
+ },
214
+ (args) => handleGetDepositHistory(args, client),
215
+ );
216
+
217
+ server.tool(
218
+ createFiatDepositToolSchema.name,
219
+ createFiatDepositToolSchema.description,
220
+ {
221
+ currency: z.string().min(2).max(10).describe("Fiat currency code (e.g. 'CLP', 'COP', 'PEN')."),
222
+ amount: z.number().positive().describe("Deposit amount."),
223
+ bank: z.string().optional().describe("Bank name or identifier for the deposit source."),
224
+ confirmation_token: z
225
+ .string()
226
+ .describe("Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to execute."),
227
+ },
228
+ (args) => handleCreateFiatDeposit(args, client),
229
+ );
230
+ }
@@ -0,0 +1,91 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { BudaClient, BudaApiError } from "../client.js";
4
+ import { validateCurrency } from "../validation.js";
5
+ import { flattenAmount } from "../utils.js";
6
+ import type { FeesResponse } from "../types.js";
7
+
8
+ export const toolSchema = {
9
+ name: "get_network_fees",
10
+ description:
11
+ "Returns the deposit or withdrawal network fee schedule for a currency on Buda.com. " +
12
+ "Useful before initiating a transfer to preview costs. " +
13
+ "Requires BUDA_API_KEY and BUDA_API_SECRET. " +
14
+ "Example: 'What are the withdrawal fees for BTC?'",
15
+ inputSchema: {
16
+ type: "object" as const,
17
+ properties: {
18
+ currency: {
19
+ type: "string",
20
+ description: "Currency code (e.g. 'BTC', 'ETH', 'CLP').",
21
+ },
22
+ type: {
23
+ type: "string",
24
+ description: "Fee direction: 'deposit' or 'withdrawal'.",
25
+ },
26
+ },
27
+ required: ["currency", "type"],
28
+ },
29
+ };
30
+
31
+ type GetNetworkFeesArgs = { currency: string; type: "deposit" | "withdrawal" };
32
+
33
+ export async function handleGetNetworkFees(
34
+ args: GetNetworkFeesArgs,
35
+ client: BudaClient,
36
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
37
+ const { currency, type } = args;
38
+
39
+ const validationError = validateCurrency(currency);
40
+ if (validationError) {
41
+ return {
42
+ content: [{ type: "text", text: JSON.stringify({ error: validationError, code: "INVALID_CURRENCY" }) }],
43
+ isError: true,
44
+ };
45
+ }
46
+
47
+ try {
48
+ const data = await client.get<FeesResponse>(`/currencies/${currency.toUpperCase()}/fees/${type}`);
49
+
50
+ const fees = data.fees.map((f) => {
51
+ const baseFee = flattenAmount(f.base_fee);
52
+ return {
53
+ name: f.name,
54
+ fee_type: f.fee_type,
55
+ base_fee: baseFee.value,
56
+ base_fee_currency: baseFee.currency,
57
+ percent: f.percent !== null ? parseFloat(f.percent) : null,
58
+ };
59
+ });
60
+
61
+ return {
62
+ content: [
63
+ {
64
+ type: "text",
65
+ text: JSON.stringify({ currency: currency.toUpperCase(), type, fees }, null, 2),
66
+ },
67
+ ],
68
+ };
69
+ } catch (err) {
70
+ const msg =
71
+ err instanceof BudaApiError
72
+ ? { error: err.message, code: err.status, path: err.path }
73
+ : { error: String(err), code: "UNKNOWN" };
74
+ return {
75
+ content: [{ type: "text", text: JSON.stringify(msg) }],
76
+ isError: true,
77
+ };
78
+ }
79
+ }
80
+
81
+ export function register(server: McpServer, client: BudaClient): void {
82
+ server.tool(
83
+ toolSchema.name,
84
+ toolSchema.description,
85
+ {
86
+ currency: z.string().min(2).max(10).describe("Currency code (e.g. 'BTC', 'ETH', 'CLP')."),
87
+ type: z.enum(["deposit", "withdrawal"]).describe("Fee direction: 'deposit' or 'withdrawal'."),
88
+ },
89
+ (args) => handleGetNetworkFees(args, client),
90
+ );
91
+ }