@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.
- package/.cursor/rules/marketplace-docs-sync.mdc +32 -0
- package/CHANGELOG.md +79 -0
- package/PUBLISH_CHECKLIST.md +40 -88
- package/README.md +446 -78
- package/dist/cache.d.ts +1 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +1 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +18 -1
- package/dist/http.js +97 -6
- package/dist/index.js +42 -3
- package/dist/tools/account.d.ts +19 -0
- package/dist/tools/account.d.ts.map +1 -0
- package/dist/tools/account.js +49 -0
- package/dist/tools/balance.d.ts +29 -0
- package/dist/tools/balance.d.ts.map +1 -0
- package/dist/tools/balance.js +72 -0
- package/dist/tools/banks.d.ts +28 -0
- package/dist/tools/banks.d.ts.map +1 -0
- package/dist/tools/banks.js +68 -0
- package/dist/tools/batch_orders.d.ts +82 -0
- package/dist/tools/batch_orders.d.ts.map +1 -0
- package/dist/tools/batch_orders.js +188 -0
- package/dist/tools/cancel_all_orders.d.ts +34 -0
- package/dist/tools/cancel_all_orders.d.ts.map +1 -0
- package/dist/tools/cancel_all_orders.js +89 -0
- package/dist/tools/cancel_order.js +1 -1
- package/dist/tools/cancel_order_by_client_id.d.ts +34 -0
- package/dist/tools/cancel_order_by_client_id.d.ts.map +1 -0
- package/dist/tools/cancel_order_by_client_id.js +102 -0
- package/dist/tools/dead_mans_switch.d.ts +1 -1
- package/dist/tools/dead_mans_switch.d.ts.map +1 -1
- package/dist/tools/dead_mans_switch.js +33 -3
- package/dist/tools/deposits.d.ts +83 -0
- package/dist/tools/deposits.d.ts.map +1 -0
- package/dist/tools/deposits.js +174 -0
- package/dist/tools/fees.d.ts +34 -0
- package/dist/tools/fees.d.ts.map +1 -0
- package/dist/tools/fees.js +72 -0
- package/dist/tools/lightning.d.ts +68 -0
- package/dist/tools/lightning.d.ts.map +1 -0
- package/dist/tools/lightning.js +185 -0
- package/dist/tools/order_lookup.d.ts +50 -0
- package/dist/tools/order_lookup.d.ts.map +1 -0
- package/dist/tools/order_lookup.js +112 -0
- package/dist/tools/place_order.d.ts +30 -0
- package/dist/tools/place_order.d.ts.map +1 -1
- package/dist/tools/place_order.js +131 -2
- package/dist/tools/quotation.d.ts +44 -0
- package/dist/tools/quotation.d.ts.map +1 -0
- package/dist/tools/quotation.js +99 -0
- package/dist/tools/receive_addresses.d.ts +83 -0
- package/dist/tools/receive_addresses.d.ts.map +1 -0
- package/dist/tools/receive_addresses.js +185 -0
- package/dist/tools/remittance_recipients.d.ts +54 -0
- package/dist/tools/remittance_recipients.d.ts.map +1 -0
- package/dist/tools/remittance_recipients.js +106 -0
- package/dist/tools/remittances.d.ts +120 -0
- package/dist/tools/remittances.d.ts.map +1 -0
- package/dist/tools/remittances.js +261 -0
- package/dist/tools/simulate_order.d.ts.map +1 -1
- package/dist/tools/simulate_order.js +2 -1
- package/dist/tools/technical_indicators.d.ts.map +1 -1
- package/dist/tools/technical_indicators.js +2 -1
- package/dist/tools/withdrawals.d.ts +93 -0
- package/dist/tools/withdrawals.d.ts.map +1 -0
- package/dist/tools/withdrawals.js +225 -0
- package/dist/types.d.ts +155 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +4 -1
- package/dist/validation.d.ts +11 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +38 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +8 -1
- package/marketplace/README.md +1 -1
- package/marketplace/claude-listing.md +101 -2
- package/marketplace/gemini-tools.json +478 -1
- package/marketplace/openapi.yaml +160 -1
- package/package.json +2 -1
- package/server.json +2 -2
- package/src/cache.ts +1 -0
- package/src/client.ts +23 -1
- package/src/http.ts +105 -6
- package/src/index.ts +40 -3
- package/src/tools/account.ts +66 -0
- package/src/tools/balance.ts +94 -0
- package/src/tools/banks.ts +94 -0
- package/src/tools/batch_orders.ts +238 -0
- package/src/tools/cancel_all_orders.ts +117 -0
- package/src/tools/cancel_order.ts +1 -1
- package/src/tools/cancel_order_by_client_id.ts +132 -0
- package/src/tools/dead_mans_switch.ts +39 -3
- package/src/tools/deposits.ts +230 -0
- package/src/tools/fees.ts +91 -0
- package/src/tools/lightning.ts +247 -0
- package/src/tools/order_lookup.ts +139 -0
- package/src/tools/place_order.ts +151 -2
- package/src/tools/quotation.ts +124 -0
- package/src/tools/receive_addresses.ts +242 -0
- package/src/tools/remittance_recipients.ts +139 -0
- package/src/tools/remittances.ts +325 -0
- package/src/tools/simulate_order.ts +1 -0
- package/src/tools/technical_indicators.ts +2 -1
- package/src/tools/withdrawals.ts +287 -0
- package/src/types.ts +210 -0
- package/src/utils.ts +3 -1
- package/src/validation.ts +45 -0
- package/src/version.ts +11 -3
- package/test/run-all.ts +16 -0
- package/test/unit.ts +2149 -1
package/dist/cache.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAKA,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,QAAQ,CAAuC;IAEjD,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAyBtF,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI7B,KAAK,IAAI,IAAI;CAGd;AAED,eAAO,MAAM,SAAS
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAKA,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,QAAQ,CAAuC;IAEjD,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAyBtF,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI7B,KAAK,IAAI,IAAI;CAGd;AAED,eAAO,MAAM,SAAS;;;;;CAKZ,CAAC;AAEX,eAAO,MAAM,KAAK,aAAoB,CAAC"}
|
package/dist/cache.js
CHANGED
package/dist/client.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export declare class BudaClient {
|
|
|
10
10
|
private readonly apiSecret;
|
|
11
11
|
constructor(baseUrl?: string, apiKey?: string, apiSecret?: string);
|
|
12
12
|
hasAuth(): boolean;
|
|
13
|
+
private _nonceCounter;
|
|
13
14
|
private nonce;
|
|
14
15
|
private sign;
|
|
15
16
|
private authHeaders;
|
|
@@ -29,5 +30,6 @@ export declare class BudaClient {
|
|
|
29
30
|
get<T>(path: string, params?: Record<string, string | number>): Promise<T>;
|
|
30
31
|
post<T>(path: string, payload: unknown): Promise<T>;
|
|
31
32
|
put<T>(path: string, payload: unknown): Promise<T>;
|
|
33
|
+
delete<T>(path: string, params?: Record<string, string | number>): Promise<T>;
|
|
32
34
|
}
|
|
33
35
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAKA,qBAAa,YAAa,SAAQ,KAAK;aAEnB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM;aAEZ,YAAY,CAAC,EAAE,MAAM;gBAHrB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM,EACC,YAAY,CAAC,EAAE,MAAM,YAAA;CAKxC;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;gBAG7C,OAAO,GAAE,MAAiB,EAC1B,MAAM,CAAC,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM;IAOpB,OAAO,IAAI,OAAO;IAIlB,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,IAAI;IASZ,OAAO,CAAC,WAAW;IAWnB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAOzB;;;;OAIG;YACW,cAAc;YA2Bd,cAAc;IActB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAoB1E,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAmBnD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAKA,qBAAa,YAAa,SAAQ,KAAK;aAEnB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM;aAEZ,YAAY,CAAC,EAAE,MAAM;gBAHrB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM,EACC,YAAY,CAAC,EAAE,MAAM,YAAA;CAKxC;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;gBAG7C,OAAO,GAAE,MAAiB,EAC1B,MAAM,CAAC,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM;IAOpB,OAAO,IAAI,OAAO;IAIlB,OAAO,CAAC,aAAa,CAAK;IAE1B,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,IAAI;IASZ,OAAO,CAAC,WAAW;IAWnB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAOzB;;;;OAIG;YACW,cAAc;YA2Bd,cAAc;IActB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAoB1E,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAmBnD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAmBlD,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAmBpF"}
|
package/dist/client.js
CHANGED
|
@@ -25,8 +25,9 @@ export class BudaClient {
|
|
|
25
25
|
hasAuth() {
|
|
26
26
|
return Boolean(this.apiKey && this.apiSecret);
|
|
27
27
|
}
|
|
28
|
+
_nonceCounter = 0;
|
|
28
29
|
nonce() {
|
|
29
|
-
return String(
|
|
30
|
+
return String(Date.now() * 1000 + (this._nonceCounter++ % 1000));
|
|
30
31
|
}
|
|
31
32
|
sign(method, pathWithQuery, body, nonce) {
|
|
32
33
|
const encodedBody = body ? Buffer.from(body).toString("base64") : "";
|
|
@@ -135,5 +136,21 @@ export class BudaClient {
|
|
|
135
136
|
const response = await this.fetchWithRetry(url, { method: "PUT", headers, body: bodyStr }, path);
|
|
136
137
|
return this.handleResponse(response, path);
|
|
137
138
|
}
|
|
139
|
+
async delete(path, params) {
|
|
140
|
+
const url = new URL(`${this.baseUrl}${path}.json`);
|
|
141
|
+
if (params) {
|
|
142
|
+
for (const [key, value] of Object.entries(params)) {
|
|
143
|
+
url.searchParams.set(key, String(value));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const urlPath = url.pathname + url.search;
|
|
147
|
+
const headers = {
|
|
148
|
+
Accept: "application/json",
|
|
149
|
+
"User-Agent": `buda-mcp/${VERSION}`,
|
|
150
|
+
...this.authHeaders("DELETE", urlPath),
|
|
151
|
+
};
|
|
152
|
+
const response = await this.fetchWithRetry(url, { method: "DELETE", headers }, path);
|
|
153
|
+
return this.handleResponse(response, path);
|
|
154
|
+
}
|
|
138
155
|
}
|
|
139
156
|
//# sourceMappingURL=client.js.map
|
package/dist/http.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import express from "express";
|
|
2
|
+
import rateLimit from "express-rate-limit";
|
|
2
3
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
4
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4
5
|
import { BudaClient } from "./client.js";
|
|
5
6
|
import { MemoryCache, CACHE_TTL } from "./cache.js";
|
|
6
7
|
import { VERSION } from "./version.js";
|
|
8
|
+
import { validateMarketId } from "./validation.js";
|
|
7
9
|
import * as markets from "./tools/markets.js";
|
|
8
10
|
import * as ticker from "./tools/ticker.js";
|
|
9
11
|
import * as orderbook from "./tools/orderbook.js";
|
|
@@ -23,6 +25,21 @@ import * as positionSize from "./tools/calculate_position_size.js";
|
|
|
23
25
|
import * as marketSentiment from "./tools/market_sentiment.js";
|
|
24
26
|
import * as technicalIndicators from "./tools/technical_indicators.js";
|
|
25
27
|
import * as deadMansSwitch from "./tools/dead_mans_switch.js";
|
|
28
|
+
import * as banks from "./tools/banks.js";
|
|
29
|
+
import * as account from "./tools/account.js";
|
|
30
|
+
import * as balance from "./tools/balance.js";
|
|
31
|
+
import * as orderLookup from "./tools/order_lookup.js";
|
|
32
|
+
import * as networkFees from "./tools/fees.js";
|
|
33
|
+
import * as deposits from "./tools/deposits.js";
|
|
34
|
+
import * as withdrawals from "./tools/withdrawals.js";
|
|
35
|
+
import * as receiveAddresses from "./tools/receive_addresses.js";
|
|
36
|
+
import * as remittances from "./tools/remittances.js";
|
|
37
|
+
import * as remittanceRecipients from "./tools/remittance_recipients.js";
|
|
38
|
+
import * as quotation from "./tools/quotation.js";
|
|
39
|
+
import * as cancelAllOrders from "./tools/cancel_all_orders.js";
|
|
40
|
+
import * as cancelOrderByClientId from "./tools/cancel_order_by_client_id.js";
|
|
41
|
+
import * as batchOrders from "./tools/batch_orders.js";
|
|
42
|
+
import * as lightning from "./tools/lightning.js";
|
|
26
43
|
import { handleMarketSummary } from "./tools/market_summary.js";
|
|
27
44
|
const PORT = parseInt(process.env.PORT ?? "3000", 10);
|
|
28
45
|
const client = new BudaClient(undefined, process.env.BUDA_API_KEY, process.env.BUDA_API_SECRET);
|
|
@@ -41,9 +58,11 @@ const PUBLIC_TOOL_SCHEMAS = [
|
|
|
41
58
|
arbitrage.toolSchema,
|
|
42
59
|
marketSummary.toolSchema,
|
|
43
60
|
simulateOrder.toolSchema,
|
|
61
|
+
quotation.toolSchema,
|
|
44
62
|
positionSize.toolSchema,
|
|
45
63
|
marketSentiment.toolSchema,
|
|
46
64
|
technicalIndicators.toolSchema,
|
|
65
|
+
banks.toolSchema,
|
|
47
66
|
];
|
|
48
67
|
const AUTH_TOOL_SCHEMAS = [
|
|
49
68
|
balances.toolSchema,
|
|
@@ -53,6 +72,29 @@ const AUTH_TOOL_SCHEMAS = [
|
|
|
53
72
|
deadMansSwitch.toolSchema,
|
|
54
73
|
deadMansSwitch.renewToolSchema,
|
|
55
74
|
deadMansSwitch.disarmToolSchema,
|
|
75
|
+
account.toolSchema,
|
|
76
|
+
balance.toolSchema,
|
|
77
|
+
orderLookup.getOrderToolSchema,
|
|
78
|
+
orderLookup.getOrderByClientIdToolSchema,
|
|
79
|
+
networkFees.toolSchema,
|
|
80
|
+
deposits.getDepositHistoryToolSchema,
|
|
81
|
+
withdrawals.getWithdrawalHistoryToolSchema,
|
|
82
|
+
receiveAddresses.listReceiveAddressesToolSchema,
|
|
83
|
+
receiveAddresses.getReceiveAddressToolSchema,
|
|
84
|
+
remittances.listRemittancesToolSchema,
|
|
85
|
+
remittances.getRemittanceToolSchema,
|
|
86
|
+
remittances.quoteRemittanceToolSchema,
|
|
87
|
+
remittances.acceptRemittanceQuoteToolSchema,
|
|
88
|
+
remittanceRecipients.listToolSchema,
|
|
89
|
+
remittanceRecipients.getToolSchema,
|
|
90
|
+
receiveAddresses.createReceiveAddressToolSchema,
|
|
91
|
+
cancelAllOrders.toolSchema,
|
|
92
|
+
cancelOrderByClientId.toolSchema,
|
|
93
|
+
batchOrders.toolSchema,
|
|
94
|
+
withdrawals.createWithdrawalToolSchema,
|
|
95
|
+
deposits.createFiatDepositToolSchema,
|
|
96
|
+
lightning.lightningWithdrawalToolSchema,
|
|
97
|
+
lightning.createLightningInvoiceToolSchema,
|
|
56
98
|
];
|
|
57
99
|
function createServer() {
|
|
58
100
|
const server = new McpServer({ name: "buda-mcp", version: VERSION });
|
|
@@ -69,15 +111,30 @@ function createServer() {
|
|
|
69
111
|
arbitrage.register(server, client, reqCache);
|
|
70
112
|
marketSummary.register(server, client, reqCache);
|
|
71
113
|
simulateOrder.register(server, client, reqCache);
|
|
114
|
+
quotation.register(server, client);
|
|
72
115
|
positionSize.register(server);
|
|
73
116
|
marketSentiment.register(server, client, reqCache);
|
|
74
117
|
technicalIndicators.register(server, client);
|
|
118
|
+
banks.register(server, client, reqCache);
|
|
75
119
|
if (authEnabled) {
|
|
76
120
|
balances.register(server, client);
|
|
77
121
|
orders.register(server, client);
|
|
78
122
|
placeOrder.register(server, client);
|
|
79
123
|
cancelOrder.register(server, client);
|
|
80
|
-
deadMansSwitch.register(server, client);
|
|
124
|
+
deadMansSwitch.register(server, client, "http");
|
|
125
|
+
account.register(server, client);
|
|
126
|
+
balance.register(server, client);
|
|
127
|
+
orderLookup.register(server, client);
|
|
128
|
+
networkFees.register(server, client);
|
|
129
|
+
deposits.register(server, client);
|
|
130
|
+
withdrawals.register(server, client);
|
|
131
|
+
receiveAddresses.register(server, client);
|
|
132
|
+
remittances.register(server, client);
|
|
133
|
+
remittanceRecipients.register(server, client);
|
|
134
|
+
cancelAllOrders.register(server, client);
|
|
135
|
+
cancelOrderByClientId.register(server, client);
|
|
136
|
+
batchOrders.register(server, client);
|
|
137
|
+
lightning.register(server, client);
|
|
81
138
|
}
|
|
82
139
|
// MCP Resources
|
|
83
140
|
server.resource("buda-markets", "buda://markets", async (uri) => {
|
|
@@ -93,7 +150,11 @@ function createServer() {
|
|
|
93
150
|
};
|
|
94
151
|
});
|
|
95
152
|
server.resource("buda-ticker", new ResourceTemplate("buda://ticker/{market}", { list: undefined }), async (uri, params) => {
|
|
96
|
-
const
|
|
153
|
+
const raw = params.market;
|
|
154
|
+
const validationError = validateMarketId(raw);
|
|
155
|
+
if (validationError)
|
|
156
|
+
throw new Error(validationError);
|
|
157
|
+
const marketId = raw.toLowerCase();
|
|
97
158
|
const data = await reqCache.getOrFetch(`ticker:${marketId}`, CACHE_TTL.TICKER, () => client.get(`/markets/${marketId}/ticker`));
|
|
98
159
|
return {
|
|
99
160
|
contents: [
|
|
@@ -106,9 +167,13 @@ function createServer() {
|
|
|
106
167
|
};
|
|
107
168
|
});
|
|
108
169
|
server.resource("buda-summary", new ResourceTemplate("buda://summary/{market}", { list: undefined }), async (uri, params) => {
|
|
109
|
-
const
|
|
170
|
+
const raw = params.market;
|
|
171
|
+
const validationError = validateMarketId(raw);
|
|
172
|
+
if (validationError)
|
|
173
|
+
throw new Error(validationError);
|
|
174
|
+
const marketId = raw.toUpperCase();
|
|
110
175
|
const result = await handleMarketSummary({ market_id: marketId }, client, reqCache);
|
|
111
|
-
const text = result.content[0].
|
|
176
|
+
const text = result.content[0]?.text ?? JSON.stringify({ error: "No content returned" });
|
|
112
177
|
return {
|
|
113
178
|
contents: [
|
|
114
179
|
{
|
|
@@ -123,6 +188,32 @@ function createServer() {
|
|
|
123
188
|
}
|
|
124
189
|
const app = express();
|
|
125
190
|
app.use(express.json());
|
|
191
|
+
const MCP_AUTH_TOKEN = process.env.MCP_AUTH_TOKEN;
|
|
192
|
+
if (authEnabled && !MCP_AUTH_TOKEN) {
|
|
193
|
+
console.error("[buda-mcp] FATAL: BUDA_API_KEY/BUDA_API_SECRET are set but MCP_AUTH_TOKEN is not.\n" +
|
|
194
|
+
" The /mcp endpoint would be publicly accessible with full account access.\n" +
|
|
195
|
+
" Set MCP_AUTH_TOKEN to a long random secret, or run in stdio mode instead.");
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
const mcpRateLimiter = rateLimit({
|
|
199
|
+
windowMs: 60_000,
|
|
200
|
+
max: parseInt(process.env.MCP_RATE_LIMIT ?? "120", 10),
|
|
201
|
+
standardHeaders: true,
|
|
202
|
+
legacyHeaders: false,
|
|
203
|
+
message: { error: "Too many requests. Retry after 60 seconds.", code: "RATE_LIMITED" },
|
|
204
|
+
});
|
|
205
|
+
function mcpAuthMiddleware(req, res, next) {
|
|
206
|
+
if (!MCP_AUTH_TOKEN) {
|
|
207
|
+
next();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const auth = req.headers.authorization ?? "";
|
|
211
|
+
if (auth !== `Bearer ${MCP_AUTH_TOKEN}`) {
|
|
212
|
+
res.status(401).json({ error: "Unauthorized" });
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
next();
|
|
216
|
+
}
|
|
126
217
|
// Health check for Railway / uptime monitors
|
|
127
218
|
app.get("/health", (_req, res) => {
|
|
128
219
|
res.json({
|
|
@@ -148,7 +239,7 @@ app.get("/.well-known/mcp/server-card.json", (_req, res) => {
|
|
|
148
239
|
});
|
|
149
240
|
});
|
|
150
241
|
// Stateless StreamableHTTP — new server instance per request (no session state needed)
|
|
151
|
-
app.post("/mcp", async (req, res) => {
|
|
242
|
+
app.post("/mcp", mcpRateLimiter, mcpAuthMiddleware, async (req, res) => {
|
|
152
243
|
const transport = new StreamableHTTPServerTransport({
|
|
153
244
|
sessionIdGenerator: undefined,
|
|
154
245
|
});
|
|
@@ -160,7 +251,7 @@ app.post("/mcp", async (req, res) => {
|
|
|
160
251
|
await transport.handleRequest(req, res, req.body);
|
|
161
252
|
});
|
|
162
253
|
// SSE upgrade for clients that prefer streaming
|
|
163
|
-
app.get("/mcp", async (req, res) => {
|
|
254
|
+
app.get("/mcp", mcpRateLimiter, mcpAuthMiddleware, async (req, res) => {
|
|
164
255
|
const transport = new StreamableHTTPServerTransport({
|
|
165
256
|
sessionIdGenerator: undefined,
|
|
166
257
|
});
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
5
5
|
import { BudaClient } from "./client.js";
|
|
6
6
|
import { cache, CACHE_TTL } from "./cache.js";
|
|
7
7
|
import { VERSION } from "./version.js";
|
|
8
|
+
import { validateMarketId } from "./validation.js";
|
|
8
9
|
import * as markets from "./tools/markets.js";
|
|
9
10
|
import * as ticker from "./tools/ticker.js";
|
|
10
11
|
import * as orderbook from "./tools/orderbook.js";
|
|
@@ -24,6 +25,21 @@ import * as positionSize from "./tools/calculate_position_size.js";
|
|
|
24
25
|
import * as marketSentiment from "./tools/market_sentiment.js";
|
|
25
26
|
import * as technicalIndicators from "./tools/technical_indicators.js";
|
|
26
27
|
import * as deadMansSwitch from "./tools/dead_mans_switch.js";
|
|
28
|
+
import * as banks from "./tools/banks.js";
|
|
29
|
+
import * as account from "./tools/account.js";
|
|
30
|
+
import * as balance from "./tools/balance.js";
|
|
31
|
+
import * as orderLookup from "./tools/order_lookup.js";
|
|
32
|
+
import * as networkFees from "./tools/fees.js";
|
|
33
|
+
import * as deposits from "./tools/deposits.js";
|
|
34
|
+
import * as withdrawals from "./tools/withdrawals.js";
|
|
35
|
+
import * as receiveAddresses from "./tools/receive_addresses.js";
|
|
36
|
+
import * as remittances from "./tools/remittances.js";
|
|
37
|
+
import * as remittanceRecipients from "./tools/remittance_recipients.js";
|
|
38
|
+
import * as quotation from "./tools/quotation.js";
|
|
39
|
+
import * as cancelAllOrders from "./tools/cancel_all_orders.js";
|
|
40
|
+
import * as cancelOrderByClientId from "./tools/cancel_order_by_client_id.js";
|
|
41
|
+
import * as batchOrders from "./tools/batch_orders.js";
|
|
42
|
+
import * as lightning from "./tools/lightning.js";
|
|
27
43
|
import { handleMarketSummary } from "./tools/market_summary.js";
|
|
28
44
|
const client = new BudaClient(undefined, process.env.BUDA_API_KEY, process.env.BUDA_API_SECRET);
|
|
29
45
|
const server = new McpServer({
|
|
@@ -42,9 +58,11 @@ priceHistory.register(server, client, cache);
|
|
|
42
58
|
arbitrage.register(server, client, cache);
|
|
43
59
|
marketSummary.register(server, client, cache);
|
|
44
60
|
simulateOrder.register(server, client, cache);
|
|
61
|
+
quotation.register(server, client);
|
|
45
62
|
positionSize.register(server);
|
|
46
63
|
marketSentiment.register(server, client, cache);
|
|
47
64
|
technicalIndicators.register(server, client);
|
|
65
|
+
banks.register(server, client, cache);
|
|
48
66
|
// Auth-gated tools — only registered when API credentials are present
|
|
49
67
|
if (client.hasAuth()) {
|
|
50
68
|
balances.register(server, client);
|
|
@@ -52,6 +70,19 @@ if (client.hasAuth()) {
|
|
|
52
70
|
placeOrder.register(server, client);
|
|
53
71
|
cancelOrder.register(server, client);
|
|
54
72
|
deadMansSwitch.register(server, client);
|
|
73
|
+
account.register(server, client);
|
|
74
|
+
balance.register(server, client);
|
|
75
|
+
orderLookup.register(server, client);
|
|
76
|
+
networkFees.register(server, client);
|
|
77
|
+
deposits.register(server, client);
|
|
78
|
+
withdrawals.register(server, client);
|
|
79
|
+
receiveAddresses.register(server, client);
|
|
80
|
+
remittances.register(server, client);
|
|
81
|
+
remittanceRecipients.register(server, client);
|
|
82
|
+
cancelAllOrders.register(server, client);
|
|
83
|
+
cancelOrderByClientId.register(server, client);
|
|
84
|
+
batchOrders.register(server, client);
|
|
85
|
+
lightning.register(server, client);
|
|
55
86
|
}
|
|
56
87
|
// MCP Resources
|
|
57
88
|
server.resource("buda-markets", "buda://markets", async (uri) => {
|
|
@@ -67,7 +98,11 @@ server.resource("buda-markets", "buda://markets", async (uri) => {
|
|
|
67
98
|
};
|
|
68
99
|
});
|
|
69
100
|
server.resource("buda-ticker", new ResourceTemplate("buda://ticker/{market}", { list: undefined }), async (uri, params) => {
|
|
70
|
-
const
|
|
101
|
+
const raw = params.market;
|
|
102
|
+
const validationError = validateMarketId(raw);
|
|
103
|
+
if (validationError)
|
|
104
|
+
throw new Error(validationError);
|
|
105
|
+
const marketId = raw.toLowerCase();
|
|
71
106
|
const data = await cache.getOrFetch(`ticker:${marketId}`, CACHE_TTL.TICKER, () => client.get(`/markets/${marketId}/ticker`));
|
|
72
107
|
return {
|
|
73
108
|
contents: [
|
|
@@ -80,9 +115,13 @@ server.resource("buda-ticker", new ResourceTemplate("buda://ticker/{market}", {
|
|
|
80
115
|
};
|
|
81
116
|
});
|
|
82
117
|
server.resource("buda-summary", new ResourceTemplate("buda://summary/{market}", { list: undefined }), async (uri, params) => {
|
|
83
|
-
const
|
|
118
|
+
const raw = params.market;
|
|
119
|
+
const validationError = validateMarketId(raw);
|
|
120
|
+
if (validationError)
|
|
121
|
+
throw new Error(validationError);
|
|
122
|
+
const marketId = raw.toUpperCase();
|
|
84
123
|
const result = await handleMarketSummary({ market_id: marketId }, client, cache);
|
|
85
|
-
const text = result.content[0].
|
|
124
|
+
const text = result.content[0]?.text ?? JSON.stringify({ error: "No content returned" });
|
|
86
125
|
return {
|
|
87
126
|
contents: [
|
|
88
127
|
{
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { BudaClient } from "../client.js";
|
|
3
|
+
export declare const toolSchema: {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: "object";
|
|
8
|
+
properties: {};
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export declare function handleGetAccountInfo(client: BudaClient): Promise<{
|
|
12
|
+
content: Array<{
|
|
13
|
+
type: "text";
|
|
14
|
+
text: string;
|
|
15
|
+
}>;
|
|
16
|
+
isError?: boolean;
|
|
17
|
+
}>;
|
|
18
|
+
export declare function register(server: McpServer, client: BudaClient): void;
|
|
19
|
+
//# sourceMappingURL=account.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../../src/tools/account.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAIxD,eAAO,MAAM,UAAU;;;;;;;CAYtB,CAAC;AAEF,wBAAsB,oBAAoB,CACxC,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,CAmChF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAOpE"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { BudaApiError } from "../client.js";
|
|
2
|
+
import { flattenAmount } from "../utils.js";
|
|
3
|
+
export const toolSchema = {
|
|
4
|
+
name: "get_account_info",
|
|
5
|
+
description: "Returns the authenticated user's profile on Buda.com. " +
|
|
6
|
+
"Fetches account details including email, display name, pubsub key, and monthly transacted amounts. " +
|
|
7
|
+
"Read-only; no side effects. " +
|
|
8
|
+
"Requires BUDA_API_KEY and BUDA_API_SECRET. " +
|
|
9
|
+
"Example: 'What is my account email and how much have I transacted this month?'",
|
|
10
|
+
inputSchema: {
|
|
11
|
+
type: "object",
|
|
12
|
+
properties: {},
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
export async function handleGetAccountInfo(client) {
|
|
16
|
+
try {
|
|
17
|
+
const data = await client.get("/me");
|
|
18
|
+
const me = data.me;
|
|
19
|
+
const monthly = flattenAmount(me.monthly_transacted);
|
|
20
|
+
return {
|
|
21
|
+
content: [
|
|
22
|
+
{
|
|
23
|
+
type: "text",
|
|
24
|
+
text: JSON.stringify({
|
|
25
|
+
id: me.id,
|
|
26
|
+
email: me.email,
|
|
27
|
+
name: me.name ?? null,
|
|
28
|
+
monthly_transacted: monthly.value,
|
|
29
|
+
monthly_transacted_currency: monthly.currency,
|
|
30
|
+
pubsub_key: me.pubsub_key ?? null,
|
|
31
|
+
}, null, 2),
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
const msg = err instanceof BudaApiError
|
|
38
|
+
? { error: err.message, code: err.status, path: err.path }
|
|
39
|
+
: { error: String(err), code: "UNKNOWN" };
|
|
40
|
+
return {
|
|
41
|
+
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
42
|
+
isError: true,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function register(server, client) {
|
|
47
|
+
server.tool(toolSchema.name, toolSchema.description, {}, () => handleGetAccountInfo(client));
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=account.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { BudaClient } from "../client.js";
|
|
3
|
+
export declare const toolSchema: {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: "object";
|
|
8
|
+
properties: {
|
|
9
|
+
currency: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
required: string[];
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
type GetBalanceArgs = {
|
|
18
|
+
currency: string;
|
|
19
|
+
};
|
|
20
|
+
export declare function handleGetBalance(args: GetBalanceArgs, client: BudaClient): Promise<{
|
|
21
|
+
content: Array<{
|
|
22
|
+
type: "text";
|
|
23
|
+
text: string;
|
|
24
|
+
}>;
|
|
25
|
+
isError?: boolean;
|
|
26
|
+
}>;
|
|
27
|
+
export declare function register(server: McpServer, client: BudaClient): void;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=balance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"balance.d.ts","sourceRoot":"","sources":["../../src/tools/balance.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAKxD,eAAO,MAAM,UAAU;;;;;;;;;;;;;CAiBtB,CAAC;AAEF,KAAK,cAAc,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3C,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,cAAc,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,CAmDhF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CASpE"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { BudaApiError } from "../client.js";
|
|
3
|
+
import { validateCurrency } from "../validation.js";
|
|
4
|
+
import { flattenAmount } from "../utils.js";
|
|
5
|
+
export const toolSchema = {
|
|
6
|
+
name: "get_balance",
|
|
7
|
+
description: "Returns the balance for a single currency for the authenticated Buda.com account. " +
|
|
8
|
+
"Fetches total, available, frozen, and pending-withdrawal amounts as floats with separate _currency fields. " +
|
|
9
|
+
"Requires BUDA_API_KEY and BUDA_API_SECRET. " +
|
|
10
|
+
"Example: 'How much ETH do I have available?'",
|
|
11
|
+
inputSchema: {
|
|
12
|
+
type: "object",
|
|
13
|
+
properties: {
|
|
14
|
+
currency: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "Currency code (e.g. 'BTC', 'CLP', 'USDC').",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
required: ["currency"],
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
export async function handleGetBalance(args, client) {
|
|
23
|
+
const { currency } = args;
|
|
24
|
+
const validationError = validateCurrency(currency);
|
|
25
|
+
if (validationError) {
|
|
26
|
+
return {
|
|
27
|
+
content: [{ type: "text", text: JSON.stringify({ error: validationError, code: "INVALID_CURRENCY" }) }],
|
|
28
|
+
isError: true,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const data = await client.get(`/balances/${currency.toUpperCase()}`);
|
|
33
|
+
const b = data.balance;
|
|
34
|
+
const amount = flattenAmount(b.amount);
|
|
35
|
+
const available = flattenAmount(b.available_amount);
|
|
36
|
+
const frozen = flattenAmount(b.frozen_amount);
|
|
37
|
+
const pending = flattenAmount(b.pending_withdraw_amount);
|
|
38
|
+
return {
|
|
39
|
+
content: [
|
|
40
|
+
{
|
|
41
|
+
type: "text",
|
|
42
|
+
text: JSON.stringify({
|
|
43
|
+
id: b.id,
|
|
44
|
+
amount: amount.value,
|
|
45
|
+
amount_currency: amount.currency,
|
|
46
|
+
available_amount: available.value,
|
|
47
|
+
available_amount_currency: available.currency,
|
|
48
|
+
frozen_amount: frozen.value,
|
|
49
|
+
frozen_amount_currency: frozen.currency,
|
|
50
|
+
pending_withdraw_amount: pending.value,
|
|
51
|
+
pending_withdraw_amount_currency: pending.currency,
|
|
52
|
+
}, null, 2),
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
const msg = err instanceof BudaApiError
|
|
59
|
+
? { error: err.message, code: err.status, path: err.path }
|
|
60
|
+
: { error: String(err), code: "UNKNOWN" };
|
|
61
|
+
return {
|
|
62
|
+
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
63
|
+
isError: true,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function register(server, client) {
|
|
68
|
+
server.tool(toolSchema.name, toolSchema.description, {
|
|
69
|
+
currency: z.string().min(2).max(10).describe("Currency code (e.g. 'BTC', 'CLP', 'USDC')."),
|
|
70
|
+
}, (args) => handleGetBalance(args, client));
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=balance.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { BudaClient } from "../client.js";
|
|
3
|
+
import { MemoryCache } from "../cache.js";
|
|
4
|
+
export declare const toolSchema: {
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object";
|
|
9
|
+
properties: {
|
|
10
|
+
currency: {
|
|
11
|
+
type: string;
|
|
12
|
+
description: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
required: string[];
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export declare function handleGetAvailableBanks(args: {
|
|
19
|
+
currency: string;
|
|
20
|
+
}, client: BudaClient, cache: MemoryCache): Promise<{
|
|
21
|
+
content: Array<{
|
|
22
|
+
type: "text";
|
|
23
|
+
text: string;
|
|
24
|
+
}>;
|
|
25
|
+
isError?: boolean;
|
|
26
|
+
}>;
|
|
27
|
+
export declare function register(server: McpServer, client: BudaClient, cache: MemoryCache): void;
|
|
28
|
+
//# sourceMappingURL=banks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banks.d.ts","sourceRoot":"","sources":["../../src/tools/banks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,WAAW,EAAa,MAAM,aAAa,CAAC;AAIrD,eAAO,MAAM,UAAU;;;;;;;;;;;;;CAkBtB,CAAC;AAEF,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,EAC1B,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,WAAW,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,CAmDhF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CASxF"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { BudaApiError } from "../client.js";
|
|
3
|
+
import { CACHE_TTL } from "../cache.js";
|
|
4
|
+
import { validateCurrency } from "../validation.js";
|
|
5
|
+
export const toolSchema = {
|
|
6
|
+
name: "get_available_banks",
|
|
7
|
+
description: "Returns banks available for deposits and withdrawals of a fiat currency on Buda.com. " +
|
|
8
|
+
"Returns an empty banks array (not an error) if the currency has no associated banks " +
|
|
9
|
+
"(e.g. crypto currencies or unsupported fiat currencies). " +
|
|
10
|
+
"Results are cached for 60 seconds. " +
|
|
11
|
+
"Example: 'Which banks can I use for CLP deposits?'",
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: "object",
|
|
14
|
+
properties: {
|
|
15
|
+
currency: {
|
|
16
|
+
type: "string",
|
|
17
|
+
description: "Currency code (e.g. 'CLP', 'COP', 'PEN').",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
required: ["currency"],
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
export async function handleGetAvailableBanks(args, client, cache) {
|
|
24
|
+
const { currency } = args;
|
|
25
|
+
const validationError = validateCurrency(currency);
|
|
26
|
+
if (validationError) {
|
|
27
|
+
return {
|
|
28
|
+
content: [{ type: "text", text: JSON.stringify({ error: validationError, code: "INVALID_CURRENCY" }) }],
|
|
29
|
+
isError: true,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const currencyUpper = currency.toUpperCase();
|
|
33
|
+
try {
|
|
34
|
+
const data = await cache.getOrFetch(`banks:${currencyUpper}`, CACHE_TTL.BANKS, () => client.get(`/currencies/${currencyUpper}/banks`));
|
|
35
|
+
return {
|
|
36
|
+
content: [
|
|
37
|
+
{
|
|
38
|
+
type: "text",
|
|
39
|
+
text: JSON.stringify({
|
|
40
|
+
currency: currencyUpper,
|
|
41
|
+
banks: data.banks.map((b) => ({ id: b.id, name: b.name, country: b.country ?? null })),
|
|
42
|
+
}, null, 2),
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
// 404 means no banks exist for this currency — return empty list (not an error)
|
|
49
|
+
if (err instanceof BudaApiError && err.status === 404) {
|
|
50
|
+
return {
|
|
51
|
+
content: [{ type: "text", text: JSON.stringify({ currency: currencyUpper, banks: [] }, null, 2) }],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const msg = err instanceof BudaApiError
|
|
55
|
+
? { error: err.message, code: err.status, path: err.path }
|
|
56
|
+
: { error: String(err), code: "UNKNOWN" };
|
|
57
|
+
return {
|
|
58
|
+
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
59
|
+
isError: true,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export function register(server, client, cache) {
|
|
64
|
+
server.tool(toolSchema.name, toolSchema.description, {
|
|
65
|
+
currency: z.string().min(2).max(10).describe("Currency code (e.g. 'CLP', 'COP', 'PEN')."),
|
|
66
|
+
}, (args) => handleGetAvailableBanks(args, client, cache));
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=banks.js.map
|