@guiie/buda-mcp 1.4.2 → 1.5.1

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 (113) hide show
  1. package/.cursor/rules/marketplace-docs-sync.mdc +32 -0
  2. package/CHANGELOG.md +79 -0
  3. package/PUBLISH_CHECKLIST.md +40 -88
  4. package/README.md +446 -78
  5. package/dist/cache.d.ts +1 -0
  6. package/dist/cache.d.ts.map +1 -1
  7. package/dist/cache.js +1 -0
  8. package/dist/client.d.ts +2 -0
  9. package/dist/client.d.ts.map +1 -1
  10. package/dist/client.js +18 -1
  11. package/dist/http.js +97 -6
  12. package/dist/index.js +42 -3
  13. package/dist/tools/account.d.ts +19 -0
  14. package/dist/tools/account.d.ts.map +1 -0
  15. package/dist/tools/account.js +49 -0
  16. package/dist/tools/balance.d.ts +29 -0
  17. package/dist/tools/balance.d.ts.map +1 -0
  18. package/dist/tools/balance.js +72 -0
  19. package/dist/tools/banks.d.ts +28 -0
  20. package/dist/tools/banks.d.ts.map +1 -0
  21. package/dist/tools/banks.js +68 -0
  22. package/dist/tools/batch_orders.d.ts +82 -0
  23. package/dist/tools/batch_orders.d.ts.map +1 -0
  24. package/dist/tools/batch_orders.js +188 -0
  25. package/dist/tools/cancel_all_orders.d.ts +34 -0
  26. package/dist/tools/cancel_all_orders.d.ts.map +1 -0
  27. package/dist/tools/cancel_all_orders.js +89 -0
  28. package/dist/tools/cancel_order.js +1 -1
  29. package/dist/tools/cancel_order_by_client_id.d.ts +34 -0
  30. package/dist/tools/cancel_order_by_client_id.d.ts.map +1 -0
  31. package/dist/tools/cancel_order_by_client_id.js +102 -0
  32. package/dist/tools/dead_mans_switch.d.ts +1 -1
  33. package/dist/tools/dead_mans_switch.d.ts.map +1 -1
  34. package/dist/tools/dead_mans_switch.js +33 -3
  35. package/dist/tools/deposits.d.ts +83 -0
  36. package/dist/tools/deposits.d.ts.map +1 -0
  37. package/dist/tools/deposits.js +174 -0
  38. package/dist/tools/fees.d.ts +34 -0
  39. package/dist/tools/fees.d.ts.map +1 -0
  40. package/dist/tools/fees.js +72 -0
  41. package/dist/tools/lightning.d.ts +68 -0
  42. package/dist/tools/lightning.d.ts.map +1 -0
  43. package/dist/tools/lightning.js +185 -0
  44. package/dist/tools/order_lookup.d.ts +50 -0
  45. package/dist/tools/order_lookup.d.ts.map +1 -0
  46. package/dist/tools/order_lookup.js +112 -0
  47. package/dist/tools/place_order.d.ts +30 -0
  48. package/dist/tools/place_order.d.ts.map +1 -1
  49. package/dist/tools/place_order.js +131 -2
  50. package/dist/tools/quotation.d.ts +44 -0
  51. package/dist/tools/quotation.d.ts.map +1 -0
  52. package/dist/tools/quotation.js +99 -0
  53. package/dist/tools/receive_addresses.d.ts +83 -0
  54. package/dist/tools/receive_addresses.d.ts.map +1 -0
  55. package/dist/tools/receive_addresses.js +185 -0
  56. package/dist/tools/remittance_recipients.d.ts +54 -0
  57. package/dist/tools/remittance_recipients.d.ts.map +1 -0
  58. package/dist/tools/remittance_recipients.js +106 -0
  59. package/dist/tools/remittances.d.ts +120 -0
  60. package/dist/tools/remittances.d.ts.map +1 -0
  61. package/dist/tools/remittances.js +261 -0
  62. package/dist/tools/simulate_order.d.ts.map +1 -1
  63. package/dist/tools/simulate_order.js +2 -1
  64. package/dist/tools/technical_indicators.d.ts.map +1 -1
  65. package/dist/tools/technical_indicators.js +2 -1
  66. package/dist/tools/withdrawals.d.ts +93 -0
  67. package/dist/tools/withdrawals.d.ts.map +1 -0
  68. package/dist/tools/withdrawals.js +225 -0
  69. package/dist/types.d.ts +155 -0
  70. package/dist/types.d.ts.map +1 -1
  71. package/dist/utils.d.ts.map +1 -1
  72. package/dist/utils.js +4 -1
  73. package/dist/validation.d.ts +11 -0
  74. package/dist/validation.d.ts.map +1 -1
  75. package/dist/validation.js +38 -0
  76. package/dist/version.d.ts.map +1 -1
  77. package/dist/version.js +8 -1
  78. package/marketplace/README.md +1 -1
  79. package/marketplace/claude-listing.md +101 -2
  80. package/marketplace/gemini-tools.json +478 -1
  81. package/marketplace/openapi.yaml +160 -1
  82. package/package.json +2 -1
  83. package/server.json +2 -2
  84. package/src/cache.ts +1 -0
  85. package/src/client.ts +23 -1
  86. package/src/http.ts +105 -6
  87. package/src/index.ts +40 -3
  88. package/src/tools/account.ts +66 -0
  89. package/src/tools/balance.ts +94 -0
  90. package/src/tools/banks.ts +94 -0
  91. package/src/tools/batch_orders.ts +238 -0
  92. package/src/tools/cancel_all_orders.ts +117 -0
  93. package/src/tools/cancel_order.ts +1 -1
  94. package/src/tools/cancel_order_by_client_id.ts +132 -0
  95. package/src/tools/dead_mans_switch.ts +39 -3
  96. package/src/tools/deposits.ts +230 -0
  97. package/src/tools/fees.ts +91 -0
  98. package/src/tools/lightning.ts +247 -0
  99. package/src/tools/order_lookup.ts +139 -0
  100. package/src/tools/place_order.ts +151 -2
  101. package/src/tools/quotation.ts +124 -0
  102. package/src/tools/receive_addresses.ts +242 -0
  103. package/src/tools/remittance_recipients.ts +139 -0
  104. package/src/tools/remittances.ts +325 -0
  105. package/src/tools/simulate_order.ts +1 -0
  106. package/src/tools/technical_indicators.ts +2 -1
  107. package/src/tools/withdrawals.ts +287 -0
  108. package/src/types.ts +210 -0
  109. package/src/utils.ts +3 -1
  110. package/src/validation.ts +45 -0
  111. package/src/version.ts +11 -3
  112. package/test/run-all.ts +16 -0
  113. package/test/unit.ts +2149 -1
@@ -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
+ }
@@ -0,0 +1,247 @@
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 { LightningWithdrawalResponse, LightningInvoiceResponse } from "../types.js";
6
+
7
+ export const lightningWithdrawalToolSchema = {
8
+ name: "lightning_withdrawal",
9
+ description:
10
+ "Pay a Bitcoin Lightning Network invoice from your Buda.com LN-BTC reserve. " +
11
+ "IMPORTANT: Funds leave the account immediately on success. " +
12
+ "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
+ invoice: {
18
+ type: "string",
19
+ description: "BOLT-11 Lightning invoice string (starts with 'lnbc', 'lntb', etc.).",
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 paying.",
26
+ },
27
+ },
28
+ required: ["invoice", "confirmation_token"],
29
+ },
30
+ };
31
+
32
+ export const createLightningInvoiceToolSchema = {
33
+ name: "create_lightning_invoice",
34
+ description:
35
+ "Create a Bitcoin Lightning Network invoice on Buda.com to receive a payment. " +
36
+ "No funds leave the account — no confirmation required. " +
37
+ "Requires BUDA_API_KEY and BUDA_API_SECRET.",
38
+ inputSchema: {
39
+ type: "object" as const,
40
+ properties: {
41
+ amount_satoshis: {
42
+ type: "number",
43
+ description: "Invoice amount in satoshis (positive integer).",
44
+ },
45
+ description: {
46
+ type: "string",
47
+ description: "Optional payment description (max 140 characters).",
48
+ },
49
+ expiry_seconds: {
50
+ type: "number",
51
+ description: "Invoice expiry in seconds (60–86400, default: 3600).",
52
+ },
53
+ },
54
+ required: ["amount_satoshis"],
55
+ },
56
+ };
57
+
58
+ type LightningWithdrawalArgs = {
59
+ invoice: string;
60
+ confirmation_token: string;
61
+ };
62
+
63
+ type CreateLightningInvoiceArgs = {
64
+ amount_satoshis: number;
65
+ description?: string;
66
+ expiry_seconds?: number;
67
+ };
68
+
69
+ export async function handleLightningWithdrawal(
70
+ args: LightningWithdrawalArgs,
71
+ client: BudaClient,
72
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
73
+ const { invoice, confirmation_token } = args;
74
+
75
+ if (confirmation_token !== "CONFIRM") {
76
+ const preview = invoice.length > 20 ? invoice.substring(0, 20) + "..." : invoice;
77
+ return {
78
+ content: [
79
+ {
80
+ type: "text",
81
+ text: JSON.stringify({
82
+ error:
83
+ "Lightning withdrawal not executed. confirmation_token must equal 'CONFIRM' to execute. " +
84
+ "Review the invoice and set confirmation_token='CONFIRM' to proceed.",
85
+ code: "CONFIRMATION_REQUIRED",
86
+ preview: { invoice_preview: preview },
87
+ }),
88
+ },
89
+ ],
90
+ isError: true,
91
+ };
92
+ }
93
+
94
+ const BOLT11_RE = /^ln(bc|tb|bcrt)\d/i;
95
+ if (!BOLT11_RE.test(invoice)) {
96
+ return {
97
+ content: [{
98
+ type: "text",
99
+ text: JSON.stringify({
100
+ error:
101
+ "Invalid Lightning invoice format. " +
102
+ "Expected a BOLT-11 string starting with 'lnbc', 'lntb', or 'lnbcrt'.",
103
+ code: "INVALID_INVOICE",
104
+ }),
105
+ }],
106
+ isError: true,
107
+ };
108
+ }
109
+
110
+ try {
111
+ const data = await client.post<LightningWithdrawalResponse>(
112
+ `/reserves/ln-btc/withdrawals`,
113
+ { invoice },
114
+ );
115
+
116
+ const lw = data.lightning_withdrawal;
117
+ const amount = flattenAmount(lw.amount);
118
+ const fee = flattenAmount(lw.fee);
119
+
120
+ return {
121
+ content: [
122
+ {
123
+ type: "text",
124
+ text: JSON.stringify(
125
+ {
126
+ id: lw.id,
127
+ state: lw.state,
128
+ amount: amount.value,
129
+ amount_currency: amount.currency,
130
+ fee: fee.value,
131
+ fee_currency: fee.currency,
132
+ payment_hash: lw.payment_hash,
133
+ created_at: lw.created_at,
134
+ },
135
+ null,
136
+ 2,
137
+ ),
138
+ },
139
+ ],
140
+ };
141
+ } catch (err) {
142
+ const msg =
143
+ err instanceof BudaApiError
144
+ ? { error: err.message, code: err.status, path: err.path }
145
+ : { error: String(err), code: "UNKNOWN" };
146
+ return {
147
+ content: [{ type: "text", text: JSON.stringify(msg) }],
148
+ isError: true,
149
+ };
150
+ }
151
+ }
152
+
153
+ export async function handleCreateLightningInvoice(
154
+ args: CreateLightningInvoiceArgs,
155
+ client: BudaClient,
156
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
157
+ const { amount_satoshis, description, expiry_seconds } = args;
158
+
159
+ try {
160
+ const invoicePayload: Record<string, unknown> = { amount: amount_satoshis };
161
+ if (description !== undefined) invoicePayload.description = description;
162
+ if (expiry_seconds !== undefined) invoicePayload.expiry = expiry_seconds;
163
+
164
+ const data = await client.post<LightningInvoiceResponse>(
165
+ `/lightning_network_invoices`,
166
+ { lightning_network_invoice: invoicePayload },
167
+ );
168
+
169
+ const inv = data.lightning_network_invoice;
170
+ const amount = flattenAmount(inv.amount);
171
+
172
+ return {
173
+ content: [
174
+ {
175
+ type: "text",
176
+ text: JSON.stringify(
177
+ {
178
+ id: inv.id,
179
+ payment_request: inv.payment_request,
180
+ amount_satoshis: amount.value,
181
+ description: inv.description,
182
+ expires_at: inv.expires_at,
183
+ state: inv.state,
184
+ created_at: inv.created_at,
185
+ },
186
+ null,
187
+ 2,
188
+ ),
189
+ },
190
+ ],
191
+ };
192
+ } catch (err) {
193
+ const msg =
194
+ err instanceof BudaApiError
195
+ ? { error: err.message, code: err.status, path: err.path }
196
+ : { error: String(err), code: "UNKNOWN" };
197
+ return {
198
+ content: [{ type: "text", text: JSON.stringify(msg) }],
199
+ isError: true,
200
+ };
201
+ }
202
+ }
203
+
204
+ export function register(server: McpServer, client: BudaClient): void {
205
+ server.tool(
206
+ lightningWithdrawalToolSchema.name,
207
+ lightningWithdrawalToolSchema.description,
208
+ {
209
+ invoice: z
210
+ .string()
211
+ .min(50)
212
+ .describe("BOLT-11 Lightning invoice string (starts with 'lnbc', 'lntb', etc.)."),
213
+ confirmation_token: z
214
+ .string()
215
+ .describe(
216
+ "Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to execute. " +
217
+ "Any other value will reject the request without paying.",
218
+ ),
219
+ },
220
+ (args) => handleLightningWithdrawal(args, client),
221
+ );
222
+
223
+ server.tool(
224
+ createLightningInvoiceToolSchema.name,
225
+ createLightningInvoiceToolSchema.description,
226
+ {
227
+ amount_satoshis: z
228
+ .number()
229
+ .int()
230
+ .positive()
231
+ .describe("Invoice amount in satoshis (positive integer)."),
232
+ description: z
233
+ .string()
234
+ .max(140)
235
+ .optional()
236
+ .describe("Optional payment description (max 140 characters)."),
237
+ expiry_seconds: z
238
+ .number()
239
+ .int()
240
+ .min(60)
241
+ .max(86400)
242
+ .optional()
243
+ .describe("Invoice expiry in seconds (60–86400, default: 3600)."),
244
+ },
245
+ (args) => handleCreateLightningInvoice(args, client),
246
+ );
247
+ }
@@ -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 { flattenAmount } from "../utils.js";
5
+ import type { OrderResponse, Order } from "../types.js";
6
+
7
+ export const getOrderToolSchema = {
8
+ name: "get_order",
9
+ description:
10
+ "Returns a single order by its numeric ID on Buda.com. " +
11
+ "Fetches full detail including state, amounts, fees, and timestamps. " +
12
+ "All monetary amounts are floats with separate _currency fields. " +
13
+ "Requires BUDA_API_KEY and BUDA_API_SECRET. " +
14
+ "Example: 'Show me the details of order 987654.'",
15
+ inputSchema: {
16
+ type: "object" as const,
17
+ properties: {
18
+ order_id: {
19
+ type: "number",
20
+ description: "The numeric ID of the order.",
21
+ },
22
+ },
23
+ required: ["order_id"],
24
+ },
25
+ };
26
+
27
+ export const getOrderByClientIdToolSchema = {
28
+ name: "get_order_by_client_id",
29
+ description:
30
+ "Returns an order by the client-assigned ID you set at placement on Buda.com. " +
31
+ "All monetary amounts are floats with separate _currency fields. " +
32
+ "Requires BUDA_API_KEY and BUDA_API_SECRET. " +
33
+ "Example: 'Find my order with client ID my-bot-order-42.'",
34
+ inputSchema: {
35
+ type: "object" as const,
36
+ properties: {
37
+ client_id: {
38
+ type: "string",
39
+ description: "The client ID string assigned when placing the order.",
40
+ },
41
+ },
42
+ required: ["client_id"],
43
+ },
44
+ };
45
+
46
+ function normalizeOrder(o: Order) {
47
+ const amount = flattenAmount(o.amount);
48
+ const originalAmount = flattenAmount(o.original_amount);
49
+ const tradedAmount = flattenAmount(o.traded_amount);
50
+ const totalExchanged = flattenAmount(o.total_exchanged);
51
+ const paidFee = flattenAmount(o.paid_fee);
52
+ const limitPrice = o.limit ? flattenAmount(o.limit) : null;
53
+
54
+ return {
55
+ id: o.id,
56
+ type: o.type,
57
+ state: o.state,
58
+ created_at: o.created_at,
59
+ market_id: o.market_id,
60
+ fee_currency: o.fee_currency,
61
+ price_type: o.price_type,
62
+ order_type: o.order_type,
63
+ client_id: o.client_id,
64
+ limit_price: limitPrice ? limitPrice.value : null,
65
+ limit_price_currency: limitPrice ? limitPrice.currency : null,
66
+ amount: amount.value,
67
+ amount_currency: amount.currency,
68
+ original_amount: originalAmount.value,
69
+ original_amount_currency: originalAmount.currency,
70
+ traded_amount: tradedAmount.value,
71
+ traded_amount_currency: tradedAmount.currency,
72
+ total_exchanged: totalExchanged.value,
73
+ total_exchanged_currency: totalExchanged.currency,
74
+ paid_fee: paidFee.value,
75
+ paid_fee_currency: paidFee.currency,
76
+ };
77
+ }
78
+
79
+ export async function handleGetOrder(
80
+ args: { order_id: number },
81
+ client: BudaClient,
82
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
83
+ try {
84
+ const data = await client.get<OrderResponse>(`/orders/${args.order_id}`);
85
+ return {
86
+ content: [{ type: "text", text: JSON.stringify(normalizeOrder(data.order), null, 2) }],
87
+ };
88
+ } catch (err) {
89
+ const msg =
90
+ err instanceof BudaApiError
91
+ ? { error: err.message, code: err.status, path: err.path }
92
+ : { error: String(err), code: "UNKNOWN" };
93
+ return {
94
+ content: [{ type: "text", text: JSON.stringify(msg) }],
95
+ isError: true,
96
+ };
97
+ }
98
+ }
99
+
100
+ export async function handleGetOrderByClientId(
101
+ args: { client_id: string },
102
+ client: BudaClient,
103
+ ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
104
+ try {
105
+ const data = await client.get<OrderResponse>(`/orders/by-client-id/${encodeURIComponent(args.client_id)}`);
106
+ return {
107
+ content: [{ type: "text", text: JSON.stringify(normalizeOrder(data.order), null, 2) }],
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 function register(server: McpServer, client: BudaClient): void {
122
+ server.tool(
123
+ getOrderToolSchema.name,
124
+ getOrderToolSchema.description,
125
+ {
126
+ order_id: z.number().int().positive().describe("The numeric ID of the order."),
127
+ },
128
+ (args) => handleGetOrder(args, client),
129
+ );
130
+
131
+ server.tool(
132
+ getOrderByClientIdToolSchema.name,
133
+ getOrderByClientIdToolSchema.description,
134
+ {
135
+ client_id: z.string().min(1).describe("The client ID string assigned when placing the order."),
136
+ },
137
+ (args) => handleGetOrderByClientId(args, client),
138
+ );
139
+ }