@guiie/buda-mcp 1.5.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/marketplace-docs-sync.mdc +32 -0
- package/CHANGELOG.md +75 -0
- package/PUBLISH_CHECKLIST.md +48 -89
- package/README.md +446 -78
- package/dist/audit.d.ts +21 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +14 -0
- package/dist/client.d.ts +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -1
- package/dist/http.js +65 -7
- package/dist/index.js +12 -3
- package/dist/tools/account.js +1 -1
- package/dist/tools/arbitrage.js +1 -1
- package/dist/tools/balance.js +1 -1
- package/dist/tools/balances.js +1 -1
- package/dist/tools/banks.js +1 -1
- package/dist/tools/batch_orders.d.ts +6 -1
- package/dist/tools/batch_orders.d.ts.map +1 -1
- package/dist/tools/batch_orders.js +47 -3
- package/dist/tools/cancel_all_orders.d.ts +1 -1
- package/dist/tools/cancel_all_orders.d.ts.map +1 -1
- package/dist/tools/cancel_all_orders.js +10 -13
- package/dist/tools/cancel_order.d.ts +1 -1
- package/dist/tools/cancel_order.d.ts.map +1 -1
- package/dist/tools/cancel_order.js +10 -10
- package/dist/tools/cancel_order_by_client_id.d.ts +1 -1
- package/dist/tools/cancel_order_by_client_id.d.ts.map +1 -1
- package/dist/tools/cancel_order_by_client_id.js +9 -9
- package/dist/tools/compare_markets.d.ts +9 -0
- package/dist/tools/compare_markets.d.ts.map +1 -1
- package/dist/tools/compare_markets.js +63 -53
- package/dist/tools/dead_mans_switch.d.ts +2 -2
- package/dist/tools/dead_mans_switch.d.ts.map +1 -1
- package/dist/tools/dead_mans_switch.js +68 -6
- package/dist/tools/deposits.js +2 -2
- package/dist/tools/fees.js +1 -1
- package/dist/tools/lightning.d.ts +1 -1
- package/dist/tools/lightning.d.ts.map +1 -1
- package/dist/tools/lightning.js +25 -9
- package/dist/tools/market_sentiment.js +1 -1
- package/dist/tools/market_summary.js +1 -1
- package/dist/tools/markets.js +1 -1
- package/dist/tools/order_lookup.js +2 -2
- package/dist/tools/orderbook.js +1 -1
- package/dist/tools/orders.js +1 -1
- package/dist/tools/place_order.d.ts +1 -1
- package/dist/tools/place_order.d.ts.map +1 -1
- package/dist/tools/place_order.js +53 -4
- package/dist/tools/price_history.js +1 -1
- package/dist/tools/quotation.js +1 -1
- package/dist/tools/receive_addresses.d.ts +6 -1
- package/dist/tools/receive_addresses.d.ts.map +1 -1
- package/dist/tools/receive_addresses.js +37 -13
- package/dist/tools/remittance_recipients.js +2 -2
- package/dist/tools/remittances.d.ts +7 -2
- package/dist/tools/remittances.d.ts.map +1 -1
- package/dist/tools/remittances.js +46 -23
- package/dist/tools/simulate_order.js +1 -1
- package/dist/tools/spread.js +1 -1
- package/dist/tools/technical_indicators.d.ts.map +1 -1
- package/dist/tools/technical_indicators.js +3 -2
- package/dist/tools/ticker.js +1 -1
- package/dist/tools/trades.js +1 -1
- package/dist/tools/volume.js +1 -1
- package/dist/tools/withdrawals.d.ts +1 -1
- package/dist/tools/withdrawals.d.ts.map +1 -1
- package/dist/tools/withdrawals.js +21 -11
- package/dist/utils.d.ts +10 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +29 -1
- package/dist/validation.d.ts +6 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +26 -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 +75 -4
- package/marketplace/gemini-tools.json +325 -2
- package/marketplace/openapi.yaml +160 -1
- package/package.json +2 -1
- package/server.json +2 -2
- package/src/audit.ts +24 -0
- package/src/client.ts +3 -1
- package/src/http.ts +75 -7
- package/src/index.ts +10 -3
- package/src/tools/account.ts +1 -1
- package/src/tools/arbitrage.ts +1 -1
- package/src/tools/balance.ts +1 -1
- package/src/tools/balances.ts +1 -1
- package/src/tools/banks.ts +1 -1
- package/src/tools/batch_orders.ts +52 -2
- package/src/tools/cancel_all_orders.ts +10 -12
- package/src/tools/cancel_order.ts +10 -9
- package/src/tools/cancel_order_by_client_id.ts +9 -8
- package/src/tools/compare_markets.ts +78 -61
- package/src/tools/dead_mans_switch.ts +76 -5
- package/src/tools/deposits.ts +2 -2
- package/src/tools/fees.ts +1 -1
- package/src/tools/lightning.ts +28 -9
- package/src/tools/market_sentiment.ts +1 -1
- package/src/tools/market_summary.ts +1 -1
- package/src/tools/markets.ts +1 -1
- package/src/tools/order_lookup.ts +2 -2
- package/src/tools/orderbook.ts +1 -1
- package/src/tools/orders.ts +1 -1
- package/src/tools/place_order.ts +56 -5
- package/src/tools/price_history.ts +1 -1
- package/src/tools/quotation.ts +1 -1
- package/src/tools/receive_addresses.ts +40 -13
- package/src/tools/remittance_recipients.ts +2 -2
- package/src/tools/remittances.ts +49 -22
- package/src/tools/simulate_order.ts +1 -1
- package/src/tools/spread.ts +1 -1
- package/src/tools/technical_indicators.ts +3 -2
- package/src/tools/ticker.ts +1 -1
- package/src/tools/trades.ts +1 -1
- package/src/tools/volume.ts +1 -1
- package/src/tools/withdrawals.ts +22 -10
- package/src/utils.ts +36 -1
- package/src/validation.ts +29 -0
- package/src/version.ts +11 -3
- package/test/unit.ts +623 -22
package/dist/audit.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured audit logging for destructive MCP tool calls.
|
|
3
|
+
*
|
|
4
|
+
* Writes newline-delimited JSON to stderr so it never pollutes the stdio MCP transport
|
|
5
|
+
* and is captured by Railway / any log aggregator attached to the process.
|
|
6
|
+
*
|
|
7
|
+
* Rules for args_summary:
|
|
8
|
+
* - Include: market_id, currency, price_type, type, amount ranges
|
|
9
|
+
* - NEVER include: confirmation_token, invoice, address, bank_account_id
|
|
10
|
+
*/
|
|
11
|
+
export interface AuditEvent {
|
|
12
|
+
ts: string;
|
|
13
|
+
tool: string;
|
|
14
|
+
transport: "http" | "stdio";
|
|
15
|
+
ip?: string;
|
|
16
|
+
args_summary: Record<string, unknown>;
|
|
17
|
+
success: boolean;
|
|
18
|
+
error_code?: string | number;
|
|
19
|
+
}
|
|
20
|
+
export declare function logAudit(event: AuditEvent): void;
|
|
21
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC9B;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAEhD"}
|
package/dist/audit.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured audit logging for destructive MCP tool calls.
|
|
3
|
+
*
|
|
4
|
+
* Writes newline-delimited JSON to stderr so it never pollutes the stdio MCP transport
|
|
5
|
+
* and is captured by Railway / any log aggregator attached to the process.
|
|
6
|
+
*
|
|
7
|
+
* Rules for args_summary:
|
|
8
|
+
* - Include: market_id, currency, price_type, type, amount ranges
|
|
9
|
+
* - NEVER include: confirmation_token, invoice, address, bank_account_id
|
|
10
|
+
*/
|
|
11
|
+
export function logAudit(event) {
|
|
12
|
+
process.stderr.write(JSON.stringify({ audit: true, ...event }) + "\n");
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=audit.js.map
|
package/dist/client.d.ts
CHANGED
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;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"}
|
|
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") : "";
|
package/dist/http.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
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";
|
|
7
|
+
import { safeTokenEqual, parseEnvInt } from "./utils.js";
|
|
6
8
|
import { VERSION } from "./version.js";
|
|
9
|
+
import { validateMarketId } from "./validation.js";
|
|
7
10
|
import * as markets from "./tools/markets.js";
|
|
8
11
|
import * as ticker from "./tools/ticker.js";
|
|
9
12
|
import * as orderbook from "./tools/orderbook.js";
|
|
@@ -39,7 +42,14 @@ import * as cancelOrderByClientId from "./tools/cancel_order_by_client_id.js";
|
|
|
39
42
|
import * as batchOrders from "./tools/batch_orders.js";
|
|
40
43
|
import * as lightning from "./tools/lightning.js";
|
|
41
44
|
import { handleMarketSummary } from "./tools/market_summary.js";
|
|
42
|
-
|
|
45
|
+
let PORT;
|
|
46
|
+
try {
|
|
47
|
+
PORT = parseEnvInt(process.env.PORT, 3000, 1, 65535, "PORT");
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
43
53
|
const client = new BudaClient(undefined, process.env.BUDA_API_KEY, process.env.BUDA_API_SECRET);
|
|
44
54
|
const authEnabled = client.hasAuth();
|
|
45
55
|
// Schemas for the Smithery server-card — assembled from the same definitions used in register().
|
|
@@ -119,7 +129,7 @@ function createServer() {
|
|
|
119
129
|
orders.register(server, client);
|
|
120
130
|
placeOrder.register(server, client);
|
|
121
131
|
cancelOrder.register(server, client);
|
|
122
|
-
deadMansSwitch.register(server, client);
|
|
132
|
+
deadMansSwitch.register(server, client, "http");
|
|
123
133
|
account.register(server, client);
|
|
124
134
|
balance.register(server, client);
|
|
125
135
|
orderLookup.register(server, client);
|
|
@@ -148,7 +158,11 @@ function createServer() {
|
|
|
148
158
|
};
|
|
149
159
|
});
|
|
150
160
|
server.resource("buda-ticker", new ResourceTemplate("buda://ticker/{market}", { list: undefined }), async (uri, params) => {
|
|
151
|
-
const
|
|
161
|
+
const raw = params.market;
|
|
162
|
+
const validationError = validateMarketId(raw);
|
|
163
|
+
if (validationError)
|
|
164
|
+
throw new Error(validationError);
|
|
165
|
+
const marketId = raw.toLowerCase();
|
|
152
166
|
const data = await reqCache.getOrFetch(`ticker:${marketId}`, CACHE_TTL.TICKER, () => client.get(`/markets/${marketId}/ticker`));
|
|
153
167
|
return {
|
|
154
168
|
contents: [
|
|
@@ -161,9 +175,13 @@ function createServer() {
|
|
|
161
175
|
};
|
|
162
176
|
});
|
|
163
177
|
server.resource("buda-summary", new ResourceTemplate("buda://summary/{market}", { list: undefined }), async (uri, params) => {
|
|
164
|
-
const
|
|
178
|
+
const raw = params.market;
|
|
179
|
+
const validationError = validateMarketId(raw);
|
|
180
|
+
if (validationError)
|
|
181
|
+
throw new Error(validationError);
|
|
182
|
+
const marketId = raw.toUpperCase();
|
|
165
183
|
const result = await handleMarketSummary({ market_id: marketId }, client, reqCache);
|
|
166
|
-
const text = result.content[0].
|
|
184
|
+
const text = result.content[0]?.text ?? JSON.stringify({ error: "No content returned" });
|
|
167
185
|
return {
|
|
168
186
|
contents: [
|
|
169
187
|
{
|
|
@@ -177,7 +195,47 @@ function createServer() {
|
|
|
177
195
|
return server;
|
|
178
196
|
}
|
|
179
197
|
const app = express();
|
|
198
|
+
// Required for correct client IP detection behind Railway's reverse proxy.
|
|
199
|
+
// Without this, express-rate-limit sees the proxy IP instead of the real client.
|
|
200
|
+
app.set("trust proxy", 1);
|
|
180
201
|
app.use(express.json());
|
|
202
|
+
const MCP_AUTH_TOKEN = process.env.MCP_AUTH_TOKEN;
|
|
203
|
+
if (authEnabled && !MCP_AUTH_TOKEN) {
|
|
204
|
+
console.error("[buda-mcp] FATAL: BUDA_API_KEY/BUDA_API_SECRET are set but MCP_AUTH_TOKEN is not.\n" +
|
|
205
|
+
" The /mcp endpoint would be publicly accessible with full account access.\n" +
|
|
206
|
+
" Set MCP_AUTH_TOKEN to a long random secret, or run in stdio mode instead.");
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
if (MCP_AUTH_TOKEN && MCP_AUTH_TOKEN.length < 32) {
|
|
210
|
+
console.warn("[buda-mcp] WARNING: MCP_AUTH_TOKEN has fewer than 32 characters. Use a longer random secret.");
|
|
211
|
+
}
|
|
212
|
+
let rateLimitMax;
|
|
213
|
+
try {
|
|
214
|
+
rateLimitMax = parseEnvInt(process.env.MCP_RATE_LIMIT, 120, 1, 10_000, "MCP_RATE_LIMIT");
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
const mcpRateLimiter = rateLimit({
|
|
221
|
+
windowMs: 60_000,
|
|
222
|
+
max: rateLimitMax,
|
|
223
|
+
standardHeaders: true,
|
|
224
|
+
legacyHeaders: false,
|
|
225
|
+
message: { error: "Too many requests. Retry after 60 seconds.", code: "RATE_LIMITED" },
|
|
226
|
+
});
|
|
227
|
+
function mcpAuthMiddleware(req, res, next) {
|
|
228
|
+
if (!MCP_AUTH_TOKEN) {
|
|
229
|
+
next();
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const auth = req.headers.authorization ?? "";
|
|
233
|
+
if (!safeTokenEqual(auth, `Bearer ${MCP_AUTH_TOKEN}`)) {
|
|
234
|
+
res.status(401).json({ error: "Unauthorized" });
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
next();
|
|
238
|
+
}
|
|
181
239
|
// Health check for Railway / uptime monitors
|
|
182
240
|
app.get("/health", (_req, res) => {
|
|
183
241
|
res.json({
|
|
@@ -203,7 +261,7 @@ app.get("/.well-known/mcp/server-card.json", (_req, res) => {
|
|
|
203
261
|
});
|
|
204
262
|
});
|
|
205
263
|
// Stateless StreamableHTTP — new server instance per request (no session state needed)
|
|
206
|
-
app.post("/mcp", async (req, res) => {
|
|
264
|
+
app.post("/mcp", mcpRateLimiter, mcpAuthMiddleware, async (req, res) => {
|
|
207
265
|
const transport = new StreamableHTTPServerTransport({
|
|
208
266
|
sessionIdGenerator: undefined,
|
|
209
267
|
});
|
|
@@ -215,7 +273,7 @@ app.post("/mcp", async (req, res) => {
|
|
|
215
273
|
await transport.handleRequest(req, res, req.body);
|
|
216
274
|
});
|
|
217
275
|
// SSE upgrade for clients that prefer streaming
|
|
218
|
-
app.get("/mcp", async (req, res) => {
|
|
276
|
+
app.get("/mcp", mcpRateLimiter, mcpAuthMiddleware, async (req, res) => {
|
|
219
277
|
const transport = new StreamableHTTPServerTransport({
|
|
220
278
|
sessionIdGenerator: undefined,
|
|
221
279
|
});
|
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";
|
|
@@ -97,7 +98,11 @@ server.resource("buda-markets", "buda://markets", async (uri) => {
|
|
|
97
98
|
};
|
|
98
99
|
});
|
|
99
100
|
server.resource("buda-ticker", new ResourceTemplate("buda://ticker/{market}", { list: undefined }), async (uri, params) => {
|
|
100
|
-
const
|
|
101
|
+
const raw = params.market;
|
|
102
|
+
const validationError = validateMarketId(raw);
|
|
103
|
+
if (validationError)
|
|
104
|
+
throw new Error(validationError);
|
|
105
|
+
const marketId = raw.toLowerCase();
|
|
101
106
|
const data = await cache.getOrFetch(`ticker:${marketId}`, CACHE_TTL.TICKER, () => client.get(`/markets/${marketId}/ticker`));
|
|
102
107
|
return {
|
|
103
108
|
contents: [
|
|
@@ -110,9 +115,13 @@ server.resource("buda-ticker", new ResourceTemplate("buda://ticker/{market}", {
|
|
|
110
115
|
};
|
|
111
116
|
});
|
|
112
117
|
server.resource("buda-summary", new ResourceTemplate("buda://summary/{market}", { list: undefined }), async (uri, params) => {
|
|
113
|
-
const
|
|
118
|
+
const raw = params.market;
|
|
119
|
+
const validationError = validateMarketId(raw);
|
|
120
|
+
if (validationError)
|
|
121
|
+
throw new Error(validationError);
|
|
122
|
+
const marketId = raw.toUpperCase();
|
|
114
123
|
const result = await handleMarketSummary({ market_id: marketId }, client, cache);
|
|
115
|
-
const text = result.content[0].
|
|
124
|
+
const text = result.content[0]?.text ?? JSON.stringify({ error: "No content returned" });
|
|
116
125
|
return {
|
|
117
126
|
contents: [
|
|
118
127
|
{
|
package/dist/tools/account.js
CHANGED
|
@@ -35,7 +35,7 @@ export async function handleGetAccountInfo(client) {
|
|
|
35
35
|
}
|
|
36
36
|
catch (err) {
|
|
37
37
|
const msg = err instanceof BudaApiError
|
|
38
|
-
? { error: err.message, code: err.status
|
|
38
|
+
? { error: err.message, code: err.status }
|
|
39
39
|
: { error: String(err), code: "UNKNOWN" };
|
|
40
40
|
return {
|
|
41
41
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
package/dist/tools/arbitrage.js
CHANGED
|
@@ -118,7 +118,7 @@ export async function handleArbitrageOpportunities({ base_currency, threshold_pc
|
|
|
118
118
|
}
|
|
119
119
|
catch (err) {
|
|
120
120
|
const msg = err instanceof BudaApiError
|
|
121
|
-
? { error: err.message, code: err.status
|
|
121
|
+
? { error: err.message, code: err.status }
|
|
122
122
|
: { error: String(err), code: "UNKNOWN" };
|
|
123
123
|
return {
|
|
124
124
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
package/dist/tools/balance.js
CHANGED
|
@@ -56,7 +56,7 @@ export async function handleGetBalance(args, client) {
|
|
|
56
56
|
}
|
|
57
57
|
catch (err) {
|
|
58
58
|
const msg = err instanceof BudaApiError
|
|
59
|
-
? { error: err.message, code: err.status
|
|
59
|
+
? { error: err.message, code: err.status }
|
|
60
60
|
: { error: String(err), code: "UNKNOWN" };
|
|
61
61
|
return {
|
|
62
62
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
package/dist/tools/balances.js
CHANGED
|
@@ -39,7 +39,7 @@ export function register(server, client) {
|
|
|
39
39
|
}
|
|
40
40
|
catch (err) {
|
|
41
41
|
const msg = err instanceof BudaApiError
|
|
42
|
-
? { error: err.message, code: err.status
|
|
42
|
+
? { error: err.message, code: err.status }
|
|
43
43
|
: { error: String(err), code: "UNKNOWN" };
|
|
44
44
|
return {
|
|
45
45
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
package/dist/tools/banks.js
CHANGED
|
@@ -52,7 +52,7 @@ export async function handleGetAvailableBanks(args, client, cache) {
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
const msg = err instanceof BudaApiError
|
|
55
|
-
? { error: err.message, code: err.status
|
|
55
|
+
? { error: err.message, code: err.status }
|
|
56
56
|
: { error: String(err), code: "UNKNOWN" };
|
|
57
57
|
return {
|
|
58
58
|
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
@@ -39,6 +39,10 @@ export declare const toolSchema: {
|
|
|
39
39
|
required: string[];
|
|
40
40
|
};
|
|
41
41
|
};
|
|
42
|
+
max_notional: {
|
|
43
|
+
type: string;
|
|
44
|
+
description: string;
|
|
45
|
+
};
|
|
42
46
|
confirmation_token: {
|
|
43
47
|
type: string;
|
|
44
48
|
description: string;
|
|
@@ -63,9 +67,10 @@ declare const orderShape: z.ZodObject<{
|
|
|
63
67
|
type SingleOrderInput = z.infer<typeof orderShape>;
|
|
64
68
|
type BatchOrdersArgs = {
|
|
65
69
|
orders: SingleOrderInput[];
|
|
70
|
+
max_notional?: number;
|
|
66
71
|
confirmation_token: string;
|
|
67
72
|
};
|
|
68
|
-
export declare function handlePlaceBatchOrders(args: BatchOrdersArgs, client: BudaClient): Promise<{
|
|
73
|
+
export declare function handlePlaceBatchOrders(args: BatchOrdersArgs, client: BudaClient, transport?: "http" | "stdio"): Promise<{
|
|
69
74
|
content: Array<{
|
|
70
75
|
type: "text";
|
|
71
76
|
text: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batch_orders.d.ts","sourceRoot":"","sources":["../../src/tools/batch_orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"batch_orders.d.ts","sourceRoot":"","sources":["../../src/tools/batch_orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAKxD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CtB,CAAC;AAEF,QAAA,MAAM,UAAU;;;;;;;;;;;;iBAMd,CAAC;AAEH,KAAK,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAWnD,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,eAAe,EACrB,MAAM,EAAE,UAAU,EAClB,SAAS,GAAE,MAAM,GAAG,OAAiB,GACpC,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CA2IhF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CA2BpE"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { BudaApiError } from "../client.js";
|
|
3
3
|
import { validateMarketId } from "../validation.js";
|
|
4
|
+
import { logAudit } from "../audit.js";
|
|
4
5
|
export const toolSchema = {
|
|
5
6
|
name: "place_batch_orders",
|
|
6
7
|
description: "Place multiple orders sequentially on Buda.com (up to 20). " +
|
|
7
8
|
"All orders are pre-validated before any API call — a validation failure stops execution with zero orders placed. " +
|
|
8
9
|
"Partial API failures do NOT roll back already-placed orders. " +
|
|
10
|
+
"Use max_notional to cap total exposure (computed as sum of amount × limit_price for limit orders; market orders contribute 0). " +
|
|
9
11
|
"IMPORTANT: Pass confirmation_token='CONFIRM' to execute. " +
|
|
10
12
|
"Requires BUDA_API_KEY and BUDA_API_SECRET.",
|
|
11
13
|
inputSchema: {
|
|
@@ -26,6 +28,12 @@ export const toolSchema = {
|
|
|
26
28
|
required: ["market_id", "type", "price_type", "amount"],
|
|
27
29
|
},
|
|
28
30
|
},
|
|
31
|
+
max_notional: {
|
|
32
|
+
type: "number",
|
|
33
|
+
description: "Optional spending cap: total notional (sum of amount × limit_price for limit orders). " +
|
|
34
|
+
"Batch is rejected before any API call if the sum exceeds this value. " +
|
|
35
|
+
"Market orders contribute 0 to the notional since their execution price is unknown.",
|
|
36
|
+
},
|
|
29
37
|
confirmation_token: {
|
|
30
38
|
type: "string",
|
|
31
39
|
description: "Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to execute.",
|
|
@@ -41,8 +49,8 @@ const orderShape = z.object({
|
|
|
41
49
|
amount: z.number().positive(),
|
|
42
50
|
limit_price: z.number().positive().optional(),
|
|
43
51
|
});
|
|
44
|
-
export async function handlePlaceBatchOrders(args, client) {
|
|
45
|
-
const { orders, confirmation_token } = args;
|
|
52
|
+
export async function handlePlaceBatchOrders(args, client, transport = "stdio") {
|
|
53
|
+
const { orders, max_notional, confirmation_token } = args;
|
|
46
54
|
if (confirmation_token !== "CONFIRM") {
|
|
47
55
|
return {
|
|
48
56
|
content: [
|
|
@@ -94,6 +102,26 @@ export async function handlePlaceBatchOrders(args, client) {
|
|
|
94
102
|
};
|
|
95
103
|
}
|
|
96
104
|
}
|
|
105
|
+
// Notional cap check (limit orders only; market orders have unknown execution price)
|
|
106
|
+
if (max_notional !== undefined) {
|
|
107
|
+
const totalNotional = orders.reduce((sum, o) => {
|
|
108
|
+
return sum + (o.price_type === "limit" && o.limit_price ? o.amount * o.limit_price : 0);
|
|
109
|
+
}, 0);
|
|
110
|
+
if (totalNotional > max_notional) {
|
|
111
|
+
return {
|
|
112
|
+
content: [{
|
|
113
|
+
type: "text",
|
|
114
|
+
text: JSON.stringify({
|
|
115
|
+
error: `Total notional ${totalNotional} exceeds max_notional cap of ${max_notional}. No orders were placed.`,
|
|
116
|
+
code: "NOTIONAL_CAP_EXCEEDED",
|
|
117
|
+
total_notional: totalNotional,
|
|
118
|
+
max_notional,
|
|
119
|
+
}),
|
|
120
|
+
}],
|
|
121
|
+
isError: true,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
97
125
|
// Execute sequentially
|
|
98
126
|
const results = [];
|
|
99
127
|
for (let i = 0; i < orders.length; i++) {
|
|
@@ -134,9 +162,18 @@ export async function handlePlaceBatchOrders(args, client) {
|
|
|
134
162
|
if (failed > 0 && succeeded > 0) {
|
|
135
163
|
response.warning = "Some orders failed. Already-placed orders were NOT rolled back.";
|
|
136
164
|
}
|
|
165
|
+
const isError = failed > 0 && succeeded === 0 ? true : undefined;
|
|
166
|
+
logAudit({
|
|
167
|
+
ts: new Date().toISOString(),
|
|
168
|
+
tool: "place_batch_orders",
|
|
169
|
+
transport,
|
|
170
|
+
args_summary: { order_count: orders.length, succeeded, failed },
|
|
171
|
+
success: !isError,
|
|
172
|
+
error_code: isError ? "PARTIAL_OR_FULL_FAILURE" : undefined,
|
|
173
|
+
});
|
|
137
174
|
return {
|
|
138
175
|
content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
|
|
139
|
-
isError
|
|
176
|
+
isError,
|
|
140
177
|
};
|
|
141
178
|
}
|
|
142
179
|
export function register(server, client) {
|
|
@@ -146,6 +183,13 @@ export function register(server, client) {
|
|
|
146
183
|
.min(1)
|
|
147
184
|
.max(20)
|
|
148
185
|
.describe("Array of 1–20 orders to place."),
|
|
186
|
+
max_notional: z
|
|
187
|
+
.number()
|
|
188
|
+
.positive()
|
|
189
|
+
.optional()
|
|
190
|
+
.describe("Optional spending cap: total notional (sum of amount × limit_price for limit orders). " +
|
|
191
|
+
"Batch is rejected before any API call if the sum exceeds this value. " +
|
|
192
|
+
"Market orders contribute 0 to the notional since their execution price is unknown."),
|
|
149
193
|
confirmation_token: z
|
|
150
194
|
.string()
|
|
151
195
|
.describe("Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to execute."),
|
|
@@ -22,7 +22,7 @@ type CancelAllOrdersArgs = {
|
|
|
22
22
|
market_id: string;
|
|
23
23
|
confirmation_token: string;
|
|
24
24
|
};
|
|
25
|
-
export declare function handleCancelAllOrders(args: CancelAllOrdersArgs, client: BudaClient): Promise<{
|
|
25
|
+
export declare function handleCancelAllOrders(args: CancelAllOrdersArgs, client: BudaClient, transport?: "http" | "stdio"): Promise<{
|
|
26
26
|
content: Array<{
|
|
27
27
|
type: "text";
|
|
28
28
|
text: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cancel_all_orders.d.ts","sourceRoot":"","sources":["../../src/tools/cancel_all_orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"cancel_all_orders.d.ts","sourceRoot":"","sources":["../../src/tools/cancel_all_orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAKxD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;CAuBtB,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,UAAU,EAClB,SAAS,GAAE,MAAM,GAAG,OAAiB,GACpC,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAqDhF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAkBpE"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { BudaApiError } from "../client.js";
|
|
3
3
|
import { validateMarketId } from "../validation.js";
|
|
4
|
+
import { logAudit } from "../audit.js";
|
|
4
5
|
export const toolSchema = {
|
|
5
6
|
name: "cancel_all_orders",
|
|
6
7
|
description: "Cancel all open orders on Buda.com, optionally filtered by a specific market. " +
|
|
@@ -23,7 +24,7 @@ export const toolSchema = {
|
|
|
23
24
|
required: ["market_id", "confirmation_token"],
|
|
24
25
|
},
|
|
25
26
|
};
|
|
26
|
-
export async function handleCancelAllOrders(args, client) {
|
|
27
|
+
export async function handleCancelAllOrders(args, client, transport = "stdio") {
|
|
27
28
|
const { market_id, confirmation_token } = args;
|
|
28
29
|
if (confirmation_token !== "CONFIRM") {
|
|
29
30
|
return {
|
|
@@ -55,23 +56,19 @@ export async function handleCancelAllOrders(args, client) {
|
|
|
55
56
|
try {
|
|
56
57
|
const params = market_id !== "*" ? { market_id: market_id.toLowerCase() } : undefined;
|
|
57
58
|
const data = await client.delete(`/orders`, params);
|
|
58
|
-
|
|
59
|
-
content: [
|
|
60
|
-
{
|
|
61
|
-
type: "text",
|
|
62
|
-
text: JSON.stringify({ canceled_count: data.canceled_count, market_id }),
|
|
63
|
-
},
|
|
64
|
-
],
|
|
59
|
+
const result = {
|
|
60
|
+
content: [{ type: "text", text: JSON.stringify({ canceled_count: data.canceled_count, market_id }) }],
|
|
65
61
|
};
|
|
62
|
+
logAudit({ ts: new Date().toISOString(), tool: "cancel_all_orders", transport, args_summary: { market_id }, success: true });
|
|
63
|
+
return result;
|
|
66
64
|
}
|
|
67
65
|
catch (err) {
|
|
68
66
|
const msg = err instanceof BudaApiError
|
|
69
|
-
? { error: err.message, code: err.status
|
|
67
|
+
? { error: err.message, code: err.status }
|
|
70
68
|
: { error: String(err), code: "UNKNOWN" };
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
};
|
|
69
|
+
const result = { content: [{ type: "text", text: JSON.stringify(msg) }], isError: true };
|
|
70
|
+
logAudit({ ts: new Date().toISOString(), tool: "cancel_all_orders", transport, args_summary: { market_id }, success: false, error_code: msg.code });
|
|
71
|
+
return result;
|
|
75
72
|
}
|
|
76
73
|
}
|
|
77
74
|
export function register(server, client) {
|
|
@@ -22,7 +22,7 @@ type CancelOrderArgs = {
|
|
|
22
22
|
order_id: number;
|
|
23
23
|
confirmation_token: string;
|
|
24
24
|
};
|
|
25
|
-
export declare function handleCancelOrder(args: CancelOrderArgs, client: BudaClient): Promise<{
|
|
25
|
+
export declare function handleCancelOrder(args: CancelOrderArgs, client: BudaClient, transport?: "http" | "stdio"): Promise<{
|
|
26
26
|
content: Array<{
|
|
27
27
|
type: "text";
|
|
28
28
|
text: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cancel_order.d.ts","sourceRoot":"","sources":["../../src/tools/cancel_order.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"cancel_order.d.ts","sourceRoot":"","sources":["../../src/tools/cancel_order.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAIxD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;CAuBtB,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,eAAe,EACrB,MAAM,EAAE,UAAU,EAClB,SAAS,GAAE,MAAM,GAAG,OAAiB,GACpC,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAsChF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAmBpE"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { BudaApiError } from "../client.js";
|
|
3
|
+
import { logAudit } from "../audit.js";
|
|
3
4
|
export const toolSchema = {
|
|
4
5
|
name: "cancel_order",
|
|
5
6
|
description: "Cancel an open order by ID on Buda.com. " +
|
|
@@ -22,7 +23,7 @@ export const toolSchema = {
|
|
|
22
23
|
required: ["order_id", "confirmation_token"],
|
|
23
24
|
},
|
|
24
25
|
};
|
|
25
|
-
export async function handleCancelOrder(args, client) {
|
|
26
|
+
export async function handleCancelOrder(args, client, transport = "stdio") {
|
|
26
27
|
const { order_id, confirmation_token } = args;
|
|
27
28
|
if (confirmation_token !== "CONFIRM") {
|
|
28
29
|
return {
|
|
@@ -42,20 +43,19 @@ export async function handleCancelOrder(args, client) {
|
|
|
42
43
|
}
|
|
43
44
|
try {
|
|
44
45
|
const data = await client.put(`/orders/${order_id}`, {
|
|
45
|
-
state: "canceling",
|
|
46
|
+
order: { state: "canceling" },
|
|
46
47
|
});
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
const result = { content: [{ type: "text", text: JSON.stringify(data.order, null, 2) }] };
|
|
49
|
+
logAudit({ ts: new Date().toISOString(), tool: "cancel_order", transport, args_summary: { order_id }, success: true });
|
|
50
|
+
return result;
|
|
50
51
|
}
|
|
51
52
|
catch (err) {
|
|
52
53
|
const msg = err instanceof BudaApiError
|
|
53
|
-
? { error: err.message, code: err.status
|
|
54
|
+
? { error: err.message, code: err.status }
|
|
54
55
|
: { error: String(err), code: "UNKNOWN" };
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
};
|
|
56
|
+
const result = { content: [{ type: "text", text: JSON.stringify(msg) }], isError: true };
|
|
57
|
+
logAudit({ ts: new Date().toISOString(), tool: "cancel_order", transport, args_summary: { order_id }, success: false, error_code: msg.code });
|
|
58
|
+
return result;
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
export function register(server, client) {
|
|
@@ -22,7 +22,7 @@ type CancelOrderByClientIdArgs = {
|
|
|
22
22
|
client_id: string;
|
|
23
23
|
confirmation_token: string;
|
|
24
24
|
};
|
|
25
|
-
export declare function handleCancelOrderByClientId(args: CancelOrderByClientIdArgs, client: BudaClient): Promise<{
|
|
25
|
+
export declare function handleCancelOrderByClientId(args: CancelOrderByClientIdArgs, client: BudaClient, transport?: "http" | "stdio"): Promise<{
|
|
26
26
|
content: Array<{
|
|
27
27
|
type: "text";
|
|
28
28
|
text: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cancel_order_by_client_id.d.ts","sourceRoot":"","sources":["../../src/tools/cancel_order_by_client_id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"cancel_order_by_client_id.d.ts","sourceRoot":"","sources":["../../src/tools/cancel_order_by_client_id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAKxD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;CAsBtB,CAAC;AAEF,KAAK,yBAAyB,GAAG;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAmCF,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,yBAAyB,EAC/B,MAAM,EAAE,UAAU,EAClB,SAAS,GAAE,MAAM,GAAG,OAAiB,GACpC,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAuChF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAkBpE"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { BudaApiError } from "../client.js";
|
|
3
3
|
import { flattenAmount } from "../utils.js";
|
|
4
|
+
import { logAudit } from "../audit.js";
|
|
4
5
|
export const toolSchema = {
|
|
5
6
|
name: "cancel_order_by_client_id",
|
|
6
7
|
description: "Cancel an open order by its client-assigned ID on Buda.com. " +
|
|
@@ -53,7 +54,7 @@ function normalizeOrder(o) {
|
|
|
53
54
|
paid_fee_currency: paidFee.currency,
|
|
54
55
|
};
|
|
55
56
|
}
|
|
56
|
-
export async function handleCancelOrderByClientId(args, client) {
|
|
57
|
+
export async function handleCancelOrderByClientId(args, client, transport = "stdio") {
|
|
57
58
|
const { client_id, confirmation_token } = args;
|
|
58
59
|
if (confirmation_token !== "CONFIRM") {
|
|
59
60
|
return {
|
|
@@ -73,18 +74,17 @@ export async function handleCancelOrderByClientId(args, client) {
|
|
|
73
74
|
}
|
|
74
75
|
try {
|
|
75
76
|
const data = await client.put(`/orders/by-client-id/${encodeURIComponent(client_id)}`, { order: { state: "canceling" } });
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
const result = { content: [{ type: "text", text: JSON.stringify(normalizeOrder(data.order), null, 2) }] };
|
|
78
|
+
logAudit({ ts: new Date().toISOString(), tool: "cancel_order_by_client_id", transport, args_summary: {}, success: true });
|
|
79
|
+
return result;
|
|
79
80
|
}
|
|
80
81
|
catch (err) {
|
|
81
82
|
const msg = err instanceof BudaApiError
|
|
82
|
-
? { error: err.message, code: err.status
|
|
83
|
+
? { error: err.message, code: err.status }
|
|
83
84
|
: { error: String(err), code: "UNKNOWN" };
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
};
|
|
85
|
+
const result = { content: [{ type: "text", text: JSON.stringify(msg) }], isError: true };
|
|
86
|
+
logAudit({ ts: new Date().toISOString(), tool: "cancel_order_by_client_id", transport, args_summary: {}, success: false, error_code: msg.code });
|
|
87
|
+
return result;
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
export function register(server, client) {
|
|
@@ -15,5 +15,14 @@ export declare const toolSchema: {
|
|
|
15
15
|
required: string[];
|
|
16
16
|
};
|
|
17
17
|
};
|
|
18
|
+
export declare function handleCompareMarkets(args: {
|
|
19
|
+
base_currency: string;
|
|
20
|
+
}, client: BudaClient, cache: MemoryCache): Promise<{
|
|
21
|
+
content: Array<{
|
|
22
|
+
type: "text";
|
|
23
|
+
text: string;
|
|
24
|
+
}>;
|
|
25
|
+
isError?: boolean;
|
|
26
|
+
}>;
|
|
18
27
|
export declare function register(server: McpServer, client: BudaClient, cache: MemoryCache): void;
|
|
19
28
|
//# sourceMappingURL=compare_markets.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compare_markets.d.ts","sourceRoot":"","sources":["../../src/tools/compare_markets.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;
|
|
1
|
+
{"version":3,"file":"compare_markets.d.ts","sourceRoot":"","sources":["../../src/tools/compare_markets.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,oBAAoB,CACxC,IAAI,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,EAC/B,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,CAsEhF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAaxF"}
|