@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,216 @@
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 type { ReceiveAddressesResponse, SingleReceiveAddressResponse, ReceiveAddress } from "../types.js";
6
+
7
+ export const createReceiveAddressToolSchema = {
8
+ name: "create_receive_address",
9
+ description:
10
+ "Generates a new receive address for a crypto currency. " +
11
+ "Creates a new blockchain deposit address for the given currency. " +
12
+ "Each call generates a distinct address. Not idempotent. " +
13
+ "Only applicable to crypto currencies (BTC, ETH, etc.). " +
14
+ "Requires BUDA_API_KEY and BUDA_API_SECRET. " +
15
+ "Example: 'Give me a fresh Bitcoin deposit address.'",
16
+ inputSchema: {
17
+ type: "object" as const,
18
+ properties: {
19
+ currency: {
20
+ type: "string",
21
+ description: "Currency code (e.g. 'BTC', 'ETH').",
22
+ },
23
+ },
24
+ required: ["currency"],
25
+ },
26
+ };
27
+
28
+ export const listReceiveAddressesToolSchema = {
29
+ name: "list_receive_addresses",
30
+ description:
31
+ "Lists all receive (deposit) addresses for a crypto currency on the authenticated Buda.com account. " +
32
+ "Returns an empty array if no addresses have been created yet. " +
33
+ "Requires BUDA_API_KEY and BUDA_API_SECRET. " +
34
+ "Example: 'What are my Bitcoin deposit addresses?'",
35
+ inputSchema: {
36
+ type: "object" as const,
37
+ properties: {
38
+ currency: {
39
+ type: "string",
40
+ description: "Currency code (e.g. 'BTC', 'ETH').",
41
+ },
42
+ },
43
+ required: ["currency"],
44
+ },
45
+ };
46
+
47
+ export const getReceiveAddressToolSchema = {
48
+ name: "get_receive_address",
49
+ description:
50
+ "Returns a single receive address by its ID for a given currency on Buda.com. " +
51
+ "Requires BUDA_API_KEY and BUDA_API_SECRET. " +
52
+ "Example: 'Get the details of my BTC receive address ID 42.'",
53
+ inputSchema: {
54
+ type: "object" as const,
55
+ properties: {
56
+ currency: {
57
+ type: "string",
58
+ description: "Currency code (e.g. 'BTC', 'ETH').",
59
+ },
60
+ id: {
61
+ type: "number",
62
+ description: "The numeric ID of the receive address.",
63
+ },
64
+ },
65
+ required: ["currency", "id"],
66
+ },
67
+ };
68
+
69
+ function normalizeAddress(a: ReceiveAddress) {
70
+ return {
71
+ id: a.id,
72
+ address: a.address,
73
+ currency: a.currency,
74
+ created_at: a.created_at,
75
+ label: a.label ?? null,
76
+ };
77
+ }
78
+
79
+ export async function handleListReceiveAddresses(
80
+ args: { currency: string },
81
+ client: BudaClient,
82
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
83
+ const { currency } = args;
84
+
85
+ const validationError = validateCurrency(currency);
86
+ if (validationError) {
87
+ return {
88
+ content: [{ type: "text", text: JSON.stringify({ error: validationError, code: "INVALID_CURRENCY" }) }],
89
+ isError: true,
90
+ };
91
+ }
92
+
93
+ try {
94
+ const data = await client.get<ReceiveAddressesResponse>(
95
+ `/currencies/${currency.toUpperCase()}/receive_addresses`,
96
+ );
97
+ return {
98
+ content: [
99
+ {
100
+ type: "text",
101
+ text: JSON.stringify(
102
+ { receive_addresses: data.receive_addresses.map(normalizeAddress) },
103
+ null,
104
+ 2,
105
+ ),
106
+ },
107
+ ],
108
+ };
109
+ } catch (err) {
110
+ const msg =
111
+ err instanceof BudaApiError
112
+ ? { error: err.message, code: err.status, path: err.path }
113
+ : { error: String(err), code: "UNKNOWN" };
114
+ return {
115
+ content: [{ type: "text", text: JSON.stringify(msg) }],
116
+ isError: true,
117
+ };
118
+ }
119
+ }
120
+
121
+ export async function handleGetReceiveAddress(
122
+ args: { currency: string; id: number },
123
+ client: BudaClient,
124
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
125
+ const { currency, id } = args;
126
+
127
+ const validationError = validateCurrency(currency);
128
+ if (validationError) {
129
+ return {
130
+ content: [{ type: "text", text: JSON.stringify({ error: validationError, code: "INVALID_CURRENCY" }) }],
131
+ isError: true,
132
+ };
133
+ }
134
+
135
+ try {
136
+ const data = await client.get<SingleReceiveAddressResponse>(
137
+ `/currencies/${currency.toUpperCase()}/receive_addresses/${id}`,
138
+ );
139
+ return {
140
+ content: [{ type: "text", text: JSON.stringify(normalizeAddress(data.receive_address), null, 2) }],
141
+ };
142
+ } catch (err) {
143
+ const msg =
144
+ err instanceof BudaApiError
145
+ ? { error: err.message, code: err.status, path: err.path }
146
+ : { error: String(err), code: "UNKNOWN" };
147
+ return {
148
+ content: [{ type: "text", text: JSON.stringify(msg) }],
149
+ isError: true,
150
+ };
151
+ }
152
+ }
153
+
154
+ export async function handleCreateReceiveAddress(
155
+ args: { currency: string },
156
+ client: BudaClient,
157
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
158
+ const { currency } = args;
159
+
160
+ const validationError = validateCurrency(currency);
161
+ if (validationError) {
162
+ return {
163
+ content: [{ type: "text", text: JSON.stringify({ error: validationError, code: "INVALID_CURRENCY" }) }],
164
+ isError: true,
165
+ };
166
+ }
167
+
168
+ try {
169
+ const data = await client.post<SingleReceiveAddressResponse>(
170
+ `/currencies/${currency.toUpperCase()}/receive_addresses`,
171
+ {},
172
+ );
173
+ return {
174
+ content: [{ type: "text", text: JSON.stringify(normalizeAddress(data.receive_address), null, 2) }],
175
+ };
176
+ } catch (err) {
177
+ const msg =
178
+ err instanceof BudaApiError
179
+ ? { error: err.message, code: err.status, path: err.path }
180
+ : { error: String(err), code: "UNKNOWN" };
181
+ return {
182
+ content: [{ type: "text", text: JSON.stringify(msg) }],
183
+ isError: true,
184
+ };
185
+ }
186
+ }
187
+
188
+ export function register(server: McpServer, client: BudaClient): void {
189
+ server.tool(
190
+ listReceiveAddressesToolSchema.name,
191
+ listReceiveAddressesToolSchema.description,
192
+ {
193
+ currency: z.string().min(2).max(10).describe("Currency code (e.g. 'BTC', 'ETH')."),
194
+ },
195
+ (args) => handleListReceiveAddresses(args, client),
196
+ );
197
+
198
+ server.tool(
199
+ getReceiveAddressToolSchema.name,
200
+ getReceiveAddressToolSchema.description,
201
+ {
202
+ currency: z.string().min(2).max(10).describe("Currency code (e.g. 'BTC', 'ETH')."),
203
+ id: z.number().int().positive().describe("The numeric ID of the receive address."),
204
+ },
205
+ (args) => handleGetReceiveAddress(args, client),
206
+ );
207
+
208
+ server.tool(
209
+ createReceiveAddressToolSchema.name,
210
+ createReceiveAddressToolSchema.description,
211
+ {
212
+ currency: z.string().min(2).max(10).describe("Currency code (e.g. 'BTC', 'ETH')."),
213
+ },
214
+ (args) => handleCreateReceiveAddress(args, client),
215
+ );
216
+ }
@@ -0,0 +1,139 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { BudaClient, BudaApiError } from "../client.js";
4
+ import type { RemittanceRecipientsResponse, SingleRemittanceRecipientResponse, RemittanceRecipient } from "../types.js";
5
+
6
+ export const listToolSchema = {
7
+ name: "list_remittance_recipients",
8
+ description:
9
+ "Lists all saved remittance recipients (bank accounts) for the authenticated Buda.com account. " +
10
+ "Supports pagination. " +
11
+ "Requires BUDA_API_KEY and BUDA_API_SECRET. " +
12
+ "Example: 'Who are my saved remittance recipients?'",
13
+ inputSchema: {
14
+ type: "object" as const,
15
+ properties: {
16
+ per: {
17
+ type: "number",
18
+ description: "Results per page (default: 20, max: 300).",
19
+ },
20
+ page: {
21
+ type: "number",
22
+ description: "Page number (default: 1).",
23
+ },
24
+ },
25
+ },
26
+ };
27
+
28
+ export const getToolSchema = {
29
+ name: "get_remittance_recipient",
30
+ description:
31
+ "Returns a single saved remittance recipient by its ID on Buda.com. " +
32
+ "Fetches saved bank details for one recipient. " +
33
+ "Requires BUDA_API_KEY and BUDA_API_SECRET. " +
34
+ "Example: 'Show remittance recipient ID 5.'",
35
+ inputSchema: {
36
+ type: "object" as const,
37
+ properties: {
38
+ id: {
39
+ type: "number",
40
+ description: "The numeric ID of the remittance recipient.",
41
+ },
42
+ },
43
+ required: ["id"],
44
+ },
45
+ };
46
+
47
+ function normalizeRecipient(r: RemittanceRecipient) {
48
+ return {
49
+ id: r.id,
50
+ name: r.name,
51
+ bank: r.bank,
52
+ account_number: r.account_number,
53
+ currency: r.currency,
54
+ country: r.country ?? null,
55
+ };
56
+ }
57
+
58
+ export async function handleListRemittanceRecipients(
59
+ args: { per?: number; page?: number },
60
+ client: BudaClient,
61
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
62
+ try {
63
+ const params: Record<string, string | number> = {};
64
+ if (args.per !== undefined) params.per = args.per;
65
+ if (args.page !== undefined) params.page = args.page;
66
+
67
+ const data = await client.get<RemittanceRecipientsResponse>(
68
+ "/remittance_recipients",
69
+ Object.keys(params).length > 0 ? params : undefined,
70
+ );
71
+
72
+ return {
73
+ content: [
74
+ {
75
+ type: "text",
76
+ text: JSON.stringify(
77
+ {
78
+ remittance_recipients: data.remittance_recipients.map(normalizeRecipient),
79
+ meta: data.meta,
80
+ },
81
+ null,
82
+ 2,
83
+ ),
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 async function handleGetRemittanceRecipient(
100
+ args: { id: number },
101
+ client: BudaClient,
102
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
103
+ try {
104
+ const data = await client.get<SingleRemittanceRecipientResponse>(`/remittance_recipients/${args.id}`);
105
+ return {
106
+ content: [{ type: "text", text: JSON.stringify(normalizeRecipient(data.remittance_recipient), null, 2) }],
107
+ };
108
+ } catch (err) {
109
+ const msg =
110
+ err instanceof BudaApiError
111
+ ? { error: err.message, code: err.status, path: err.path }
112
+ : { error: String(err), code: "UNKNOWN" };
113
+ return {
114
+ content: [{ type: "text", text: JSON.stringify(msg) }],
115
+ isError: true,
116
+ };
117
+ }
118
+ }
119
+
120
+ export function register(server: McpServer, client: BudaClient): void {
121
+ server.tool(
122
+ listToolSchema.name,
123
+ listToolSchema.description,
124
+ {
125
+ per: z.number().int().min(1).max(300).optional().describe("Results per page (default: 20, max: 300)."),
126
+ page: z.number().int().min(1).optional().describe("Page number (default: 1)."),
127
+ },
128
+ (args) => handleListRemittanceRecipients(args, client),
129
+ );
130
+
131
+ server.tool(
132
+ getToolSchema.name,
133
+ getToolSchema.description,
134
+ {
135
+ id: z.number().int().positive().describe("The numeric ID of the remittance recipient."),
136
+ },
137
+ (args) => handleGetRemittanceRecipient(args, client),
138
+ );
139
+ }
@@ -0,0 +1,299 @@
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 { validateCurrency } from "../validation.js";
6
+ import type { RemittancesResponse, SingleRemittanceResponse, Remittance } from "../types.js";
7
+
8
+ export const listRemittancesToolSchema = {
9
+ name: "list_remittances",
10
+ description:
11
+ "Returns all fiat remittance transfers for the authenticated Buda.com account. " +
12
+ "Supports pagination. All amounts are floats with separate _currency fields. " +
13
+ "Requires BUDA_API_KEY and BUDA_API_SECRET. " +
14
+ "Example: 'List my recent remittances.'",
15
+ inputSchema: {
16
+ type: "object" as const,
17
+ properties: {
18
+ per: {
19
+ type: "number",
20
+ description: "Results per page (default: 20, max: 300).",
21
+ },
22
+ page: {
23
+ type: "number",
24
+ description: "Page number (default: 1).",
25
+ },
26
+ },
27
+ },
28
+ };
29
+
30
+ export const quoteRemittanceToolSchema = {
31
+ name: "quote_remittance",
32
+ description:
33
+ "Creates a time-limited remittance quote without committing funds. " +
34
+ "Requests a price quote for a fiat remittance to a saved recipient. " +
35
+ "Returns a remittance object in 'quoted' state with an expiry timestamp. " +
36
+ "NOT idempotent — creates a new remittance record each call. " +
37
+ "To execute, call accept_remittance_quote with the returned ID before it expires. " +
38
+ "Requires BUDA_API_KEY and BUDA_API_SECRET. " +
39
+ "Example: 'Get a remittance quote to send 100000 CLP to recipient 5.'",
40
+ inputSchema: {
41
+ type: "object" as const,
42
+ properties: {
43
+ currency: {
44
+ type: "string",
45
+ description: "Fiat currency code (e.g. 'CLP', 'COP').",
46
+ },
47
+ amount: {
48
+ type: "number",
49
+ description: "Amount to remit (positive number).",
50
+ },
51
+ recipient_id: {
52
+ type: "number",
53
+ description: "ID of the saved remittance recipient.",
54
+ },
55
+ },
56
+ required: ["currency", "amount", "recipient_id"],
57
+ },
58
+ };
59
+
60
+ export const acceptRemittanceQuoteToolSchema = {
61
+ name: "accept_remittance_quote",
62
+ description:
63
+ "Accepts and executes a pending remittance quote. " +
64
+ "Commits a previously quoted remittance, triggering a real fiat transfer. " +
65
+ "IRREVERSIBLE once the transfer is initiated. " +
66
+ "You must pass confirmation_token='CONFIRM' to proceed. " +
67
+ "Requires BUDA_API_KEY and BUDA_API_SECRET. " +
68
+ "Example: \"Accept remittance quote 77 — set confirmation_token='CONFIRM'.\"",
69
+ inputSchema: {
70
+ type: "object" as const,
71
+ properties: {
72
+ id: {
73
+ type: "number",
74
+ description: "The numeric ID of the remittance quote to accept.",
75
+ },
76
+ confirmation_token: {
77
+ type: "string",
78
+ description: "Must be 'CONFIRM' to proceed. Any other value aborts.",
79
+ },
80
+ },
81
+ required: ["id", "confirmation_token"],
82
+ },
83
+ };
84
+
85
+ export const getRemittanceToolSchema = {
86
+ name: "get_remittance",
87
+ description:
88
+ "Returns a single remittance by its ID on Buda.com. " +
89
+ "Fetches current state and details. " +
90
+ "Requires BUDA_API_KEY and BUDA_API_SECRET. " +
91
+ "Example: 'What is the status of remittance 77?'",
92
+ inputSchema: {
93
+ type: "object" as const,
94
+ properties: {
95
+ id: {
96
+ type: "number",
97
+ description: "The numeric ID of the remittance.",
98
+ },
99
+ },
100
+ required: ["id"],
101
+ },
102
+ };
103
+
104
+ function normalizeRemittance(r: Remittance) {
105
+ const amount = flattenAmount(r.amount);
106
+ return {
107
+ id: r.id,
108
+ state: r.state,
109
+ currency: r.currency,
110
+ amount: amount.value,
111
+ amount_currency: amount.currency,
112
+ recipient_id: r.recipient_id,
113
+ created_at: r.created_at,
114
+ expires_at: r.expires_at ?? null,
115
+ };
116
+ }
117
+
118
+ export async function handleListRemittances(
119
+ args: { per?: number; page?: number },
120
+ client: BudaClient,
121
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
122
+ try {
123
+ const params: Record<string, string | number> = {};
124
+ if (args.per !== undefined) params.per = args.per;
125
+ if (args.page !== undefined) params.page = args.page;
126
+
127
+ const data = await client.get<RemittancesResponse>(
128
+ "/remittances",
129
+ Object.keys(params).length > 0 ? params : undefined,
130
+ );
131
+
132
+ return {
133
+ content: [
134
+ {
135
+ type: "text",
136
+ text: JSON.stringify(
137
+ {
138
+ remittances: data.remittances.map(normalizeRemittance),
139
+ meta: data.meta,
140
+ },
141
+ null,
142
+ 2,
143
+ ),
144
+ },
145
+ ],
146
+ };
147
+ } catch (err) {
148
+ const msg =
149
+ err instanceof BudaApiError
150
+ ? { error: err.message, code: err.status, path: err.path }
151
+ : { error: String(err), code: "UNKNOWN" };
152
+ return {
153
+ content: [{ type: "text", text: JSON.stringify(msg) }],
154
+ isError: true,
155
+ };
156
+ }
157
+ }
158
+
159
+ export async function handleGetRemittance(
160
+ args: { id: number },
161
+ client: BudaClient,
162
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
163
+ try {
164
+ const data = await client.get<SingleRemittanceResponse>(`/remittances/${args.id}`);
165
+ return {
166
+ content: [{ type: "text", text: JSON.stringify(normalizeRemittance(data.remittance), null, 2) }],
167
+ };
168
+ } catch (err) {
169
+ const msg =
170
+ err instanceof BudaApiError
171
+ ? { error: err.message, code: err.status, path: err.path }
172
+ : { error: String(err), code: "UNKNOWN" };
173
+ return {
174
+ content: [{ type: "text", text: JSON.stringify(msg) }],
175
+ isError: true,
176
+ };
177
+ }
178
+ }
179
+
180
+ export async function handleQuoteRemittance(
181
+ args: { currency: string; amount: number; recipient_id: number },
182
+ client: BudaClient,
183
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
184
+ const { currency, amount, recipient_id } = args;
185
+
186
+ const validationError = validateCurrency(currency);
187
+ if (validationError) {
188
+ return {
189
+ content: [{ type: "text", text: JSON.stringify({ error: validationError, code: "INVALID_CURRENCY" }) }],
190
+ isError: true,
191
+ };
192
+ }
193
+
194
+ try {
195
+ const data = await client.post<SingleRemittanceResponse>("/remittances", {
196
+ remittance: {
197
+ currency: currency.toUpperCase(),
198
+ amount: String(amount),
199
+ recipient_id,
200
+ },
201
+ });
202
+ return {
203
+ content: [{ type: "text", text: JSON.stringify(normalizeRemittance(data.remittance), null, 2) }],
204
+ };
205
+ } catch (err) {
206
+ const msg =
207
+ err instanceof BudaApiError
208
+ ? { error: err.message, code: err.status, path: err.path }
209
+ : { error: String(err), code: "UNKNOWN" };
210
+ return {
211
+ content: [{ type: "text", text: JSON.stringify(msg) }],
212
+ isError: true,
213
+ };
214
+ }
215
+ }
216
+
217
+ export async function handleAcceptRemittanceQuote(
218
+ args: { id: number; confirmation_token: string },
219
+ client: BudaClient,
220
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
221
+ const { id, confirmation_token } = args;
222
+
223
+ if (confirmation_token !== "CONFIRM") {
224
+ return {
225
+ content: [
226
+ {
227
+ type: "text",
228
+ text: JSON.stringify({
229
+ error:
230
+ "Accepting a remittance quote is irreversible. Pass confirmation_token='CONFIRM' to proceed.",
231
+ code: "CONFIRMATION_REQUIRED",
232
+ remittance_id: id,
233
+ }),
234
+ },
235
+ ],
236
+ isError: true,
237
+ };
238
+ }
239
+
240
+ try {
241
+ const data = await client.put<SingleRemittanceResponse>(`/remittances/${id}`, {
242
+ remittance: { state: "confirming" },
243
+ });
244
+ return {
245
+ content: [{ type: "text", text: JSON.stringify(normalizeRemittance(data.remittance), null, 2) }],
246
+ };
247
+ } catch (err) {
248
+ const msg =
249
+ err instanceof BudaApiError
250
+ ? { error: err.message, code: err.status, path: err.path }
251
+ : { error: String(err), code: "UNKNOWN" };
252
+ return {
253
+ content: [{ type: "text", text: JSON.stringify(msg) }],
254
+ isError: true,
255
+ };
256
+ }
257
+ }
258
+
259
+ export function register(server: McpServer, client: BudaClient): void {
260
+ server.tool(
261
+ listRemittancesToolSchema.name,
262
+ listRemittancesToolSchema.description,
263
+ {
264
+ per: z.number().int().min(1).max(300).optional().describe("Results per page (default: 20, max: 300)."),
265
+ page: z.number().int().min(1).optional().describe("Page number (default: 1)."),
266
+ },
267
+ (args) => handleListRemittances(args, client),
268
+ );
269
+
270
+ server.tool(
271
+ getRemittanceToolSchema.name,
272
+ getRemittanceToolSchema.description,
273
+ {
274
+ id: z.number().int().positive().describe("The numeric ID of the remittance."),
275
+ },
276
+ (args) => handleGetRemittance(args, client),
277
+ );
278
+
279
+ server.tool(
280
+ quoteRemittanceToolSchema.name,
281
+ quoteRemittanceToolSchema.description,
282
+ {
283
+ currency: z.string().min(2).max(10).describe("Fiat currency code (e.g. 'CLP', 'COP')."),
284
+ amount: z.number().positive().describe("Amount to remit (positive number)."),
285
+ recipient_id: z.number().int().positive().describe("ID of the saved remittance recipient."),
286
+ },
287
+ (args) => handleQuoteRemittance(args, client),
288
+ );
289
+
290
+ server.tool(
291
+ acceptRemittanceQuoteToolSchema.name,
292
+ acceptRemittanceQuoteToolSchema.description,
293
+ {
294
+ id: z.number().int().positive().describe("The numeric ID of the remittance quote to accept."),
295
+ confirmation_token: z.string().describe("Must be 'CONFIRM' to proceed. Any other value aborts."),
296
+ },
297
+ (args) => handleAcceptRemittanceQuote(args, client),
298
+ );
299
+ }
@@ -8,6 +8,7 @@ import type { TickerResponse, MarketResponse } from "../types.js";
8
8
  export const toolSchema = {
9
9
  name: "simulate_order",
10
10
  description:
11
+ "[DEPRECATED: prefer get_real_quotation for server-side accurate quotes] " +
11
12
  "Simulates a buy or sell order on Buda.com using live ticker data — no order is placed. " +
12
13
  "Returns estimated fill price, fee, total cost, and slippage vs mid-price. " +
13
14
  "Omit 'price' for a market order simulation; supply 'price' for a limit order simulation. " +