@guiie/buda-mcp 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +87 -0
- package/PUBLISH_CHECKLIST.md +192 -0
- package/README.md +308 -70
- package/dist/cache.d.ts +13 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +25 -0
- package/dist/client.d.ts +9 -8
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +95 -12
- package/dist/http.d.ts +2 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +262 -0
- package/dist/index.js +53 -9
- package/dist/tools/balances.d.ts +4 -0
- package/dist/tools/balances.d.ts.map +1 -0
- package/dist/tools/balances.js +23 -0
- package/dist/tools/cancel_order.d.ts +4 -0
- package/dist/tools/cancel_order.d.ts.map +1 -0
- package/dist/tools/cancel_order.js +53 -0
- package/dist/tools/compare_markets.d.ts +5 -0
- package/dist/tools/compare_markets.d.ts.map +1 -0
- package/dist/tools/compare_markets.js +65 -0
- package/dist/tools/markets.d.ts +2 -1
- package/dist/tools/markets.d.ts.map +1 -1
- package/dist/tools/markets.js +22 -8
- package/dist/tools/orderbook.d.ts +2 -1
- package/dist/tools/orderbook.d.ts.map +1 -1
- package/dist/tools/orderbook.js +27 -13
- package/dist/tools/orders.d.ts +4 -0
- package/dist/tools/orders.d.ts.map +1 -0
- package/dist/tools/orders.js +57 -0
- package/dist/tools/place_order.d.ts +4 -0
- package/dist/tools/place_order.d.ts.map +1 -0
- package/dist/tools/place_order.js +88 -0
- package/dist/tools/price_history.d.ts +5 -0
- package/dist/tools/price_history.d.ts.map +1 -0
- package/dist/tools/price_history.js +97 -0
- package/dist/tools/spread.d.ts +5 -0
- package/dist/tools/spread.d.ts.map +1 -0
- package/dist/tools/spread.js +55 -0
- package/dist/tools/ticker.d.ts +2 -1
- package/dist/tools/ticker.d.ts.map +1 -1
- package/dist/tools/ticker.js +19 -5
- package/dist/tools/trades.d.ts +2 -1
- package/dist/tools/trades.d.ts.map +1 -1
- package/dist/tools/trades.js +22 -10
- package/dist/tools/volume.d.ts +2 -1
- package/dist/tools/volume.d.ts.map +1 -1
- package/dist/tools/volume.js +17 -5
- package/dist/types.d.ts +42 -1
- package/dist/types.d.ts.map +1 -1
- package/marketplace/claude-listing.md +76 -8
- package/marketplace/cursor-mcp.json +2 -2
- package/marketplace/gemini-tools.json +51 -1
- package/marketplace/openapi.yaml +204 -4
- package/package.json +6 -2
- package/railway.json +12 -0
- package/server.json +1 -1
- package/src/cache.ts +34 -0
- package/src/client.ts +107 -12
- package/src/http.ts +305 -0
- package/src/index.ts +77 -9
- package/src/tools/balances.ts +30 -0
- package/src/tools/cancel_order.ts +65 -0
- package/src/tools/compare_markets.ts +82 -0
- package/src/tools/markets.ts +30 -11
- package/src/tools/orderbook.ts +31 -16
- package/src/tools/orders.ts +68 -0
- package/src/tools/place_order.ts +107 -0
- package/src/tools/price_history.ts +124 -0
- package/src/tools/spread.ts +71 -0
- package/src/tools/ticker.ts +23 -8
- package/src/tools/trades.ts +24 -12
- package/src/tools/volume.ts +20 -8
- package/src/types.ts +52 -1
- package/test/run-all.ts +122 -1
- package/tsconfig.json +1 -1
package/dist/http.js
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4
|
+
import { BudaClient } from "./client.js";
|
|
5
|
+
import { MemoryCache, CACHE_TTL } from "./cache.js";
|
|
6
|
+
import * as markets from "./tools/markets.js";
|
|
7
|
+
import * as ticker from "./tools/ticker.js";
|
|
8
|
+
import * as orderbook from "./tools/orderbook.js";
|
|
9
|
+
import * as trades from "./tools/trades.js";
|
|
10
|
+
import * as volume from "./tools/volume.js";
|
|
11
|
+
import * as spread from "./tools/spread.js";
|
|
12
|
+
import * as compareMarkets from "./tools/compare_markets.js";
|
|
13
|
+
import * as priceHistory from "./tools/price_history.js";
|
|
14
|
+
import * as balances from "./tools/balances.js";
|
|
15
|
+
import * as orders from "./tools/orders.js";
|
|
16
|
+
import * as placeOrder from "./tools/place_order.js";
|
|
17
|
+
import * as cancelOrder from "./tools/cancel_order.js";
|
|
18
|
+
const PORT = parseInt(process.env.PORT ?? "3000", 10);
|
|
19
|
+
const client = new BudaClient(undefined, process.env.BUDA_API_KEY, process.env.BUDA_API_SECRET);
|
|
20
|
+
const authEnabled = client.hasAuth();
|
|
21
|
+
function createServer() {
|
|
22
|
+
const server = new McpServer({ name: "buda-mcp", version: "1.1.0" });
|
|
23
|
+
// Per-request cache so caching works correctly for stateless HTTP
|
|
24
|
+
const reqCache = new MemoryCache();
|
|
25
|
+
markets.register(server, client, reqCache);
|
|
26
|
+
ticker.register(server, client, reqCache);
|
|
27
|
+
orderbook.register(server, client, reqCache);
|
|
28
|
+
trades.register(server, client, reqCache);
|
|
29
|
+
volume.register(server, client, reqCache);
|
|
30
|
+
spread.register(server, client, reqCache);
|
|
31
|
+
compareMarkets.register(server, client, reqCache);
|
|
32
|
+
priceHistory.register(server, client, reqCache);
|
|
33
|
+
if (authEnabled) {
|
|
34
|
+
balances.register(server, client);
|
|
35
|
+
orders.register(server, client);
|
|
36
|
+
placeOrder.register(server, client);
|
|
37
|
+
cancelOrder.register(server, client);
|
|
38
|
+
}
|
|
39
|
+
// MCP Resources
|
|
40
|
+
server.resource("buda-markets", "buda://markets", async (uri) => {
|
|
41
|
+
const data = await reqCache.getOrFetch("markets", CACHE_TTL.MARKETS, () => client.get("/markets"));
|
|
42
|
+
return {
|
|
43
|
+
contents: [
|
|
44
|
+
{
|
|
45
|
+
uri: uri.href,
|
|
46
|
+
mimeType: "application/json",
|
|
47
|
+
text: JSON.stringify(data.markets, null, 2),
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
server.resource("buda-ticker", new ResourceTemplate("buda://ticker/{market}", { list: undefined }), async (uri, params) => {
|
|
53
|
+
const marketId = params.market.toLowerCase();
|
|
54
|
+
const data = await reqCache.getOrFetch(`ticker:${marketId}`, CACHE_TTL.TICKER, () => client.get(`/markets/${marketId}/ticker`));
|
|
55
|
+
return {
|
|
56
|
+
contents: [
|
|
57
|
+
{
|
|
58
|
+
uri: uri.href,
|
|
59
|
+
mimeType: "application/json",
|
|
60
|
+
text: JSON.stringify(data.ticker, null, 2),
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
return server;
|
|
66
|
+
}
|
|
67
|
+
const app = express();
|
|
68
|
+
app.use(express.json());
|
|
69
|
+
// Health check for Railway / uptime monitors
|
|
70
|
+
app.get("/health", (_req, res) => {
|
|
71
|
+
res.json({ status: "ok", server: "buda-mcp", version: "1.1.0", auth_mode: authEnabled ? "authenticated" : "public" });
|
|
72
|
+
});
|
|
73
|
+
// Smithery static server card — lets Smithery scan tools without running the server
|
|
74
|
+
app.get("/.well-known/mcp/server-card.json", (_req, res) => {
|
|
75
|
+
const publicTools = [
|
|
76
|
+
{
|
|
77
|
+
name: "get_markets",
|
|
78
|
+
description: "List all available trading pairs on Buda.com, or get details for a specific market.",
|
|
79
|
+
inputSchema: {
|
|
80
|
+
type: "object",
|
|
81
|
+
properties: {
|
|
82
|
+
market_id: { type: "string", description: "Optional market ID (e.g. BTC-CLP)" },
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: "get_ticker",
|
|
88
|
+
description: "Get current price, bid/ask, volume, and price change for a Buda.com market.",
|
|
89
|
+
inputSchema: {
|
|
90
|
+
type: "object",
|
|
91
|
+
properties: {
|
|
92
|
+
market_id: { type: "string", description: "Market ID (e.g. BTC-CLP)" },
|
|
93
|
+
},
|
|
94
|
+
required: ["market_id"],
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "get_orderbook",
|
|
99
|
+
description: "Get the full order book (bids and asks) for a Buda.com market.",
|
|
100
|
+
inputSchema: {
|
|
101
|
+
type: "object",
|
|
102
|
+
properties: {
|
|
103
|
+
market_id: { type: "string", description: "Market ID (e.g. BTC-CLP)" },
|
|
104
|
+
limit: { type: "number", description: "Max levels per side" },
|
|
105
|
+
},
|
|
106
|
+
required: ["market_id"],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: "get_trades",
|
|
111
|
+
description: "Get recent trade history for a Buda.com market.",
|
|
112
|
+
inputSchema: {
|
|
113
|
+
type: "object",
|
|
114
|
+
properties: {
|
|
115
|
+
market_id: { type: "string", description: "Market ID (e.g. BTC-CLP)" },
|
|
116
|
+
limit: { type: "number", description: "Number of trades (max 100)" },
|
|
117
|
+
timestamp: { type: "number", description: "Unix timestamp for pagination" },
|
|
118
|
+
},
|
|
119
|
+
required: ["market_id"],
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "get_market_volume",
|
|
124
|
+
description: "Get 24h and 7-day transacted volume for a Buda.com market.",
|
|
125
|
+
inputSchema: {
|
|
126
|
+
type: "object",
|
|
127
|
+
properties: {
|
|
128
|
+
market_id: { type: "string", description: "Market ID (e.g. BTC-CLP)" },
|
|
129
|
+
},
|
|
130
|
+
required: ["market_id"],
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: "get_spread",
|
|
135
|
+
description: "Calculate bid/ask spread (absolute and percentage) for a Buda.com market.",
|
|
136
|
+
inputSchema: {
|
|
137
|
+
type: "object",
|
|
138
|
+
properties: {
|
|
139
|
+
market_id: { type: "string", description: "Market ID (e.g. BTC-CLP)" },
|
|
140
|
+
},
|
|
141
|
+
required: ["market_id"],
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: "compare_markets",
|
|
146
|
+
description: "Compare ticker data for all trading pairs of a given base currency side by side.",
|
|
147
|
+
inputSchema: {
|
|
148
|
+
type: "object",
|
|
149
|
+
properties: {
|
|
150
|
+
base_currency: { type: "string", description: "Base currency (e.g. BTC, ETH)" },
|
|
151
|
+
},
|
|
152
|
+
required: ["base_currency"],
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: "get_price_history",
|
|
157
|
+
description: "Get OHLCV price history for a market, derived from recent trade history.",
|
|
158
|
+
inputSchema: {
|
|
159
|
+
type: "object",
|
|
160
|
+
properties: {
|
|
161
|
+
market_id: { type: "string", description: "Market ID (e.g. BTC-CLP)" },
|
|
162
|
+
period: { type: "string", enum: ["1h", "4h", "1d"], description: "Candle period" },
|
|
163
|
+
limit: { type: "number", description: "Raw trades to fetch (max 100)" },
|
|
164
|
+
},
|
|
165
|
+
required: ["market_id"],
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
];
|
|
169
|
+
const authTools = authEnabled
|
|
170
|
+
? [
|
|
171
|
+
{
|
|
172
|
+
name: "get_balances",
|
|
173
|
+
description: "Get all currency balances for the authenticated account.",
|
|
174
|
+
inputSchema: { type: "object", properties: {} },
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
name: "get_orders",
|
|
178
|
+
description: "Get orders for a given market.",
|
|
179
|
+
inputSchema: {
|
|
180
|
+
type: "object",
|
|
181
|
+
properties: {
|
|
182
|
+
market_id: { type: "string" },
|
|
183
|
+
state: { type: "string" },
|
|
184
|
+
},
|
|
185
|
+
required: ["market_id"],
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: "place_order",
|
|
190
|
+
description: "Place a limit or market order. Requires confirmation_token='CONFIRM'.",
|
|
191
|
+
inputSchema: {
|
|
192
|
+
type: "object",
|
|
193
|
+
properties: {
|
|
194
|
+
market_id: { type: "string" },
|
|
195
|
+
type: { type: "string", enum: ["Bid", "Ask"] },
|
|
196
|
+
price_type: { type: "string", enum: ["limit", "market"] },
|
|
197
|
+
amount: { type: "number" },
|
|
198
|
+
limit_price: { type: "number" },
|
|
199
|
+
confirmation_token: { type: "string" },
|
|
200
|
+
},
|
|
201
|
+
required: ["market_id", "type", "price_type", "amount", "confirmation_token"],
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: "cancel_order",
|
|
206
|
+
description: "Cancel an order by ID. Requires confirmation_token='CONFIRM'.",
|
|
207
|
+
inputSchema: {
|
|
208
|
+
type: "object",
|
|
209
|
+
properties: {
|
|
210
|
+
order_id: { type: "number" },
|
|
211
|
+
confirmation_token: { type: "string" },
|
|
212
|
+
},
|
|
213
|
+
required: ["order_id", "confirmation_token"],
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
]
|
|
217
|
+
: [];
|
|
218
|
+
res.json({
|
|
219
|
+
serverInfo: { name: "buda-mcp", version: "1.1.0" },
|
|
220
|
+
authentication: { required: authEnabled },
|
|
221
|
+
tools: [...publicTools, ...authTools],
|
|
222
|
+
resources: [
|
|
223
|
+
{ uri: "buda://markets", name: "All Buda.com markets", mimeType: "application/json" },
|
|
224
|
+
{ uri: "buda://ticker/{market}", name: "Ticker for a specific market", mimeType: "application/json" },
|
|
225
|
+
],
|
|
226
|
+
prompts: [],
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
// Stateless StreamableHTTP — new server instance per request (no session state needed)
|
|
230
|
+
app.post("/mcp", async (req, res) => {
|
|
231
|
+
const transport = new StreamableHTTPServerTransport({
|
|
232
|
+
sessionIdGenerator: undefined,
|
|
233
|
+
});
|
|
234
|
+
res.on("close", () => {
|
|
235
|
+
transport.close();
|
|
236
|
+
});
|
|
237
|
+
const server = createServer();
|
|
238
|
+
await server.connect(transport);
|
|
239
|
+
await transport.handleRequest(req, res, req.body);
|
|
240
|
+
});
|
|
241
|
+
// SSE upgrade for clients that prefer streaming
|
|
242
|
+
app.get("/mcp", async (req, res) => {
|
|
243
|
+
const transport = new StreamableHTTPServerTransport({
|
|
244
|
+
sessionIdGenerator: undefined,
|
|
245
|
+
});
|
|
246
|
+
res.on("close", () => {
|
|
247
|
+
transport.close();
|
|
248
|
+
});
|
|
249
|
+
const server = createServer();
|
|
250
|
+
await server.connect(transport);
|
|
251
|
+
await transport.handleRequest(req, res);
|
|
252
|
+
});
|
|
253
|
+
app.delete("/mcp", async (_req, res) => {
|
|
254
|
+
res.status(405).json({ error: "Sessions not supported (stateless server)" });
|
|
255
|
+
});
|
|
256
|
+
app.listen(PORT, () => {
|
|
257
|
+
console.log(`buda-mcp HTTP server listening on port ${PORT}`);
|
|
258
|
+
console.log(` MCP endpoint: http://localhost:${PORT}/mcp`);
|
|
259
|
+
console.log(` Health check: http://localhost:${PORT}/health`);
|
|
260
|
+
console.log(` Auth mode: ${authEnabled ? "authenticated" : "public (no credentials)"}`);
|
|
261
|
+
});
|
|
262
|
+
//# sourceMappingURL=http.js.map
|
package/dist/index.js
CHANGED
|
@@ -1,23 +1,67 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
4
|
import { BudaClient } from "./client.js";
|
|
5
|
+
import { cache, CACHE_TTL } from "./cache.js";
|
|
4
6
|
import * as markets from "./tools/markets.js";
|
|
5
7
|
import * as ticker from "./tools/ticker.js";
|
|
6
8
|
import * as orderbook from "./tools/orderbook.js";
|
|
7
9
|
import * as trades from "./tools/trades.js";
|
|
8
10
|
import * as volume from "./tools/volume.js";
|
|
11
|
+
import * as spread from "./tools/spread.js";
|
|
12
|
+
import * as compareMarkets from "./tools/compare_markets.js";
|
|
13
|
+
import * as priceHistory from "./tools/price_history.js";
|
|
14
|
+
import * as balances from "./tools/balances.js";
|
|
15
|
+
import * as orders from "./tools/orders.js";
|
|
16
|
+
import * as placeOrder from "./tools/place_order.js";
|
|
17
|
+
import * as cancelOrder from "./tools/cancel_order.js";
|
|
18
|
+
const client = new BudaClient(undefined, process.env.BUDA_API_KEY, process.env.BUDA_API_SECRET);
|
|
9
19
|
const server = new McpServer({
|
|
10
20
|
name: "buda-mcp",
|
|
11
|
-
version: "1.
|
|
21
|
+
version: "1.1.0",
|
|
22
|
+
});
|
|
23
|
+
// Public tools
|
|
24
|
+
markets.register(server, client, cache);
|
|
25
|
+
ticker.register(server, client, cache);
|
|
26
|
+
orderbook.register(server, client, cache);
|
|
27
|
+
trades.register(server, client, cache);
|
|
28
|
+
volume.register(server, client, cache);
|
|
29
|
+
spread.register(server, client, cache);
|
|
30
|
+
compareMarkets.register(server, client, cache);
|
|
31
|
+
priceHistory.register(server, client, cache);
|
|
32
|
+
// Auth-gated tools — only registered when API credentials are present
|
|
33
|
+
if (client.hasAuth()) {
|
|
34
|
+
balances.register(server, client);
|
|
35
|
+
orders.register(server, client);
|
|
36
|
+
placeOrder.register(server, client);
|
|
37
|
+
cancelOrder.register(server, client);
|
|
38
|
+
}
|
|
39
|
+
// MCP Resources
|
|
40
|
+
server.resource("buda-markets", "buda://markets", async (uri) => {
|
|
41
|
+
const data = await cache.getOrFetch("markets", CACHE_TTL.MARKETS, () => client.get("/markets"));
|
|
42
|
+
return {
|
|
43
|
+
contents: [
|
|
44
|
+
{
|
|
45
|
+
uri: uri.href,
|
|
46
|
+
mimeType: "application/json",
|
|
47
|
+
text: JSON.stringify(data.markets, null, 2),
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
server.resource("buda-ticker", new ResourceTemplate("buda://ticker/{market}", { list: undefined }), async (uri, params) => {
|
|
53
|
+
const marketId = params.market.toLowerCase();
|
|
54
|
+
const data = await cache.getOrFetch(`ticker:${marketId}`, CACHE_TTL.TICKER, () => client.get(`/markets/${marketId}/ticker`));
|
|
55
|
+
return {
|
|
56
|
+
contents: [
|
|
57
|
+
{
|
|
58
|
+
uri: uri.href,
|
|
59
|
+
mimeType: "application/json",
|
|
60
|
+
text: JSON.stringify(data.ticker, null, 2),
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
12
64
|
});
|
|
13
|
-
const client = new BudaClient();
|
|
14
|
-
// Register all public-endpoint tools
|
|
15
|
-
markets.register(server, client);
|
|
16
|
-
ticker.register(server, client);
|
|
17
|
-
orderbook.register(server, client);
|
|
18
|
-
trades.register(server, client);
|
|
19
|
-
volume.register(server, client);
|
|
20
|
-
// Start the server over stdio (standard MCP transport)
|
|
21
65
|
const transport = new StdioServerTransport();
|
|
22
66
|
await server.connect(transport);
|
|
23
67
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"balances.d.ts","sourceRoot":"","sources":["../../src/tools/balances.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAGxD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAyBpE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { BudaApiError } from "../client.js";
|
|
2
|
+
export function register(server, client) {
|
|
3
|
+
server.tool("get_balances", "Get all currency balances for the authenticated Buda.com account. " +
|
|
4
|
+
"Returns total, available, frozen, and pending withdrawal amounts per currency. " +
|
|
5
|
+
"Requires BUDA_API_KEY and BUDA_API_SECRET environment variables.", {}, async () => {
|
|
6
|
+
try {
|
|
7
|
+
const data = await client.get("/balances");
|
|
8
|
+
return {
|
|
9
|
+
content: [{ type: "text", text: JSON.stringify(data.balances, null, 2) }],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
const msg = err instanceof BudaApiError
|
|
14
|
+
? { error: err.message, code: err.status, path: err.path }
|
|
15
|
+
: { error: String(err), code: "UNKNOWN" };
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
18
|
+
isError: true,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=balances.js.map
|
|
@@ -0,0 +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;AAGxD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CA2DpE"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { BudaApiError } from "../client.js";
|
|
3
|
+
export function register(server, client) {
|
|
4
|
+
server.tool("cancel_order", "Cancel an open order by ID on Buda.com. " +
|
|
5
|
+
"IMPORTANT: To prevent accidental cancellation from ambiguous prompts, you must pass " +
|
|
6
|
+
"confirmation_token='CONFIRM' to execute. " +
|
|
7
|
+
"Requires BUDA_API_KEY and BUDA_API_SECRET environment variables.", {
|
|
8
|
+
order_id: z
|
|
9
|
+
.number()
|
|
10
|
+
.int()
|
|
11
|
+
.positive()
|
|
12
|
+
.describe("The numeric ID of the order to cancel."),
|
|
13
|
+
confirmation_token: z
|
|
14
|
+
.string()
|
|
15
|
+
.describe("Safety confirmation. Must equal exactly 'CONFIRM' (case-sensitive) to cancel the order. " +
|
|
16
|
+
"Any other value will reject the request without canceling."),
|
|
17
|
+
}, async ({ order_id, confirmation_token }) => {
|
|
18
|
+
if (confirmation_token !== "CONFIRM") {
|
|
19
|
+
return {
|
|
20
|
+
content: [
|
|
21
|
+
{
|
|
22
|
+
type: "text",
|
|
23
|
+
text: JSON.stringify({
|
|
24
|
+
error: "Order not canceled. confirmation_token must equal 'CONFIRM' to execute. " +
|
|
25
|
+
"Verify the order ID and set confirmation_token='CONFIRM' to proceed.",
|
|
26
|
+
code: "CONFIRMATION_REQUIRED",
|
|
27
|
+
order_id,
|
|
28
|
+
}),
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
isError: true,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const data = await client.put(`/orders/${order_id}`, {
|
|
36
|
+
state: "canceling",
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
content: [{ type: "text", text: JSON.stringify(data.order, null, 2) }],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
const msg = err instanceof BudaApiError
|
|
44
|
+
? { error: err.message, code: err.status, path: err.path }
|
|
45
|
+
: { error: String(err), code: "UNKNOWN" };
|
|
46
|
+
return {
|
|
47
|
+
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
48
|
+
isError: true,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=cancel_order.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { BudaClient } from "../client.js";
|
|
3
|
+
import { MemoryCache } from "../cache.js";
|
|
4
|
+
export declare function register(server: McpServer, client: BudaClient, cache: MemoryCache): void;
|
|
5
|
+
//# sourceMappingURL=compare_markets.d.ts.map
|
|
@@ -0,0 +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;AAGrD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CA2ExF"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { BudaApiError } from "../client.js";
|
|
3
|
+
import { CACHE_TTL } from "../cache.js";
|
|
4
|
+
export function register(server, client, cache) {
|
|
5
|
+
server.tool("compare_markets", "Compare ticker data for all trading pairs of a given base currency across Buda.com's " +
|
|
6
|
+
"supported quote currencies (CLP, COP, PEN, BTC, USDC, ETH). " +
|
|
7
|
+
"For example, passing 'BTC' returns side-by-side data for BTC-CLP, BTC-COP, BTC-PEN, etc.", {
|
|
8
|
+
base_currency: z
|
|
9
|
+
.string()
|
|
10
|
+
.describe("Base currency to compare across all available markets (e.g. 'BTC', 'ETH', 'XRP')."),
|
|
11
|
+
}, async ({ base_currency }) => {
|
|
12
|
+
try {
|
|
13
|
+
const base = base_currency.toUpperCase();
|
|
14
|
+
const data = await cache.getOrFetch("tickers:all", CACHE_TTL.TICKER, () => client.get("/tickers"));
|
|
15
|
+
const matching = data.tickers.filter((t) => {
|
|
16
|
+
const [tickerBase] = t.market_id.split("-");
|
|
17
|
+
return tickerBase === base;
|
|
18
|
+
});
|
|
19
|
+
if (matching.length === 0) {
|
|
20
|
+
return {
|
|
21
|
+
content: [
|
|
22
|
+
{
|
|
23
|
+
type: "text",
|
|
24
|
+
text: JSON.stringify({
|
|
25
|
+
error: `No markets found for base currency '${base}'.`,
|
|
26
|
+
code: "NOT_FOUND",
|
|
27
|
+
}),
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
isError: true,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const result = {
|
|
34
|
+
base_currency: base,
|
|
35
|
+
markets: matching.map((t) => ({
|
|
36
|
+
market_id: t.market_id,
|
|
37
|
+
last_price: t.last_price[0],
|
|
38
|
+
currency: t.last_price[1],
|
|
39
|
+
best_bid: t.max_bid ? t.max_bid[0] : null,
|
|
40
|
+
best_ask: t.min_ask ? t.min_ask[0] : null,
|
|
41
|
+
volume_24h: t.volume ? t.volume[0] : null,
|
|
42
|
+
price_change_24h: t.price_variation_24h
|
|
43
|
+
? (parseFloat(t.price_variation_24h) * 100).toFixed(2) + "%"
|
|
44
|
+
: null,
|
|
45
|
+
price_change_7d: t.price_variation_7d
|
|
46
|
+
? (parseFloat(t.price_variation_7d) * 100).toFixed(2) + "%"
|
|
47
|
+
: null,
|
|
48
|
+
})),
|
|
49
|
+
};
|
|
50
|
+
return {
|
|
51
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
const msg = err instanceof BudaApiError
|
|
56
|
+
? { error: err.message, code: err.status, path: err.path }
|
|
57
|
+
: { error: String(err), code: "UNKNOWN" };
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
60
|
+
isError: true,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=compare_markets.js.map
|
package/dist/tools/markets.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { BudaClient } from "../client.js";
|
|
3
|
-
|
|
3
|
+
import { MemoryCache } from "../cache.js";
|
|
4
|
+
export declare function register(server: McpServer, client: BudaClient, cache: MemoryCache): void;
|
|
4
5
|
//# sourceMappingURL=markets.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markets.d.ts","sourceRoot":"","sources":["../../src/tools/markets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"markets.d.ts","sourceRoot":"","sources":["../../src/tools/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;AAGrD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CA+CxF"}
|
package/dist/tools/markets.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
|
|
2
|
+
import { BudaApiError } from "../client.js";
|
|
3
|
+
import { CACHE_TTL } from "../cache.js";
|
|
4
|
+
export function register(server, client, cache) {
|
|
3
5
|
server.tool("get_markets", "List all available trading pairs on Buda.com, or get details for a specific market. " +
|
|
4
6
|
"Returns base/quote currencies, fees, and minimum order sizes.", {
|
|
5
7
|
market_id: z
|
|
@@ -7,16 +9,28 @@ export function register(server, client) {
|
|
|
7
9
|
.optional()
|
|
8
10
|
.describe("Optional market ID (e.g. 'BTC-CLP', 'ETH-BTC'). Omit to list all markets."),
|
|
9
11
|
}, async ({ market_id }) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
try {
|
|
13
|
+
if (market_id) {
|
|
14
|
+
const id = market_id.toLowerCase();
|
|
15
|
+
const data = await cache.getOrFetch(`market:${id}`, CACHE_TTL.MARKETS, () => client.get(`/markets/${id}`));
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: "text", text: JSON.stringify(data.market, null, 2) }],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const data = await cache.getOrFetch("markets", CACHE_TTL.MARKETS, () => client.get("/markets"));
|
|
12
21
|
return {
|
|
13
|
-
content: [{ type: "text", text: JSON.stringify(data.
|
|
22
|
+
content: [{ type: "text", text: JSON.stringify(data.markets, null, 2) }],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
const msg = err instanceof BudaApiError
|
|
27
|
+
? { error: err.message, code: err.status, path: err.path }
|
|
28
|
+
: { error: String(err), code: "UNKNOWN" };
|
|
29
|
+
return {
|
|
30
|
+
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
31
|
+
isError: true,
|
|
14
32
|
};
|
|
15
33
|
}
|
|
16
|
-
const data = await client.get("/markets");
|
|
17
|
-
return {
|
|
18
|
-
content: [{ type: "text", text: JSON.stringify(data.markets, null, 2) }],
|
|
19
|
-
};
|
|
20
34
|
});
|
|
21
35
|
}
|
|
22
36
|
//# sourceMappingURL=markets.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { BudaClient } from "../client.js";
|
|
3
|
-
|
|
3
|
+
import { MemoryCache } from "../cache.js";
|
|
4
|
+
export declare function register(server: McpServer, client: BudaClient, cache: MemoryCache): void;
|
|
4
5
|
//# sourceMappingURL=orderbook.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orderbook.d.ts","sourceRoot":"","sources":["../../src/tools/orderbook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"orderbook.d.ts","sourceRoot":"","sources":["../../src/tools/orderbook.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;AAGrD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAgDxF"}
|
package/dist/tools/orderbook.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { BudaApiError } from "../client.js";
|
|
3
|
+
import { CACHE_TTL } from "../cache.js";
|
|
4
|
+
export function register(server, client, cache) {
|
|
5
|
+
server.tool("get_orderbook", "Get the current order book (bids and asks) for a Buda.com market. Returns sorted arrays of " +
|
|
4
6
|
"bids (buy orders) and asks (sell orders), each as [price, amount] pairs.", {
|
|
5
7
|
market_id: z
|
|
6
8
|
.string()
|
|
@@ -12,17 +14,29 @@ export function register(server, client) {
|
|
|
12
14
|
.optional()
|
|
13
15
|
.describe("Maximum number of levels to return per side (default: all)."),
|
|
14
16
|
}, async ({ market_id, limit }) => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
try {
|
|
18
|
+
const id = market_id.toLowerCase();
|
|
19
|
+
const data = await cache.getOrFetch(`orderbook:${id}`, CACHE_TTL.ORDERBOOK, () => client.get(`/markets/${id}/order_book`));
|
|
20
|
+
const book = data.order_book;
|
|
21
|
+
const result = {
|
|
22
|
+
bids: limit ? book.bids.slice(0, limit) : book.bids,
|
|
23
|
+
asks: limit ? book.asks.slice(0, limit) : book.asks,
|
|
24
|
+
bid_count: book.bids.length,
|
|
25
|
+
ask_count: book.asks.length,
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
const msg = err instanceof BudaApiError
|
|
33
|
+
? { error: err.message, code: err.status, path: err.path }
|
|
34
|
+
: { error: String(err), code: "UNKNOWN" };
|
|
35
|
+
return {
|
|
36
|
+
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
37
|
+
isError: true,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
26
40
|
});
|
|
27
41
|
}
|
|
28
42
|
//# sourceMappingURL=orderbook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orders.d.ts","sourceRoot":"","sources":["../../src/tools/orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAGxD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CA8DpE"}
|