flash-trade-mcp 0.3.0 → 0.3.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/dist/index.js +83 -70
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21063,7 +21063,7 @@ var require_fallback = __commonJS((exports, module) => {
|
|
|
21063
21063
|
|
|
21064
21064
|
// node_modules/bufferutil/index.js
|
|
21065
21065
|
var require_bufferutil = __commonJS((exports, module) => {
|
|
21066
|
-
var __dirname = "/
|
|
21066
|
+
var __dirname = "/home/runner/work/flash-trade-MCP/flash-trade-MCP/mcp/node_modules/bufferutil";
|
|
21067
21067
|
try {
|
|
21068
21068
|
module.exports = require_node_gyp_build2()(__dirname);
|
|
21069
21069
|
} catch (e) {
|
|
@@ -21479,7 +21479,7 @@ var require_fallback2 = __commonJS((exports, module) => {
|
|
|
21479
21479
|
|
|
21480
21480
|
// node_modules/utf-8-validate/index.js
|
|
21481
21481
|
var require_utf_8_validate = __commonJS((exports, module) => {
|
|
21482
|
-
var __dirname = "/
|
|
21482
|
+
var __dirname = "/home/runner/work/flash-trade-MCP/flash-trade-MCP/mcp/node_modules/utf-8-validate";
|
|
21483
21483
|
try {
|
|
21484
21484
|
module.exports = require_node_gyp_build2()(__dirname);
|
|
21485
21485
|
} catch (e) {
|
|
@@ -49053,6 +49053,15 @@ function loadConfig() {
|
|
|
49053
49053
|
if (!apiBaseUrl) {
|
|
49054
49054
|
throw new Error("FLASH_API_URL environment variable is required");
|
|
49055
49055
|
}
|
|
49056
|
+
let parsed;
|
|
49057
|
+
try {
|
|
49058
|
+
parsed = new URL(apiBaseUrl);
|
|
49059
|
+
} catch {
|
|
49060
|
+
throw new Error(`FLASH_API_URL is not a valid URL: ${apiBaseUrl}`);
|
|
49061
|
+
}
|
|
49062
|
+
if (parsed.protocol !== "https:") {
|
|
49063
|
+
console.error(`[flash-trade-mcp] WARNING: FLASH_API_URL uses ${parsed.protocol} — HTTPS is strongly recommended for production`);
|
|
49064
|
+
}
|
|
49056
49065
|
return {
|
|
49057
49066
|
apiBaseUrl: apiBaseUrl.replace(/\/$/, ""),
|
|
49058
49067
|
timeoutMs: parseInt(process.env.FLASH_API_TIMEOUT ?? "30000", 10),
|
|
@@ -49069,8 +49078,8 @@ class FlashApiError extends Error {
|
|
|
49069
49078
|
super(`Flash API error [${statusCode}] ${endpoint}: ${message}`);
|
|
49070
49079
|
this.statusCode = statusCode;
|
|
49071
49080
|
this.endpoint = endpoint;
|
|
49072
|
-
this.responseBody = responseBody;
|
|
49073
49081
|
this.name = "FlashApiError";
|
|
49082
|
+
this.responseBody = responseBody?.slice(0, 500);
|
|
49074
49083
|
}
|
|
49075
49084
|
}
|
|
49076
49085
|
|
|
@@ -49137,51 +49146,51 @@ class FlashApiClient {
|
|
|
49137
49146
|
return this.get("/markets");
|
|
49138
49147
|
}
|
|
49139
49148
|
async getMarket(pubkey) {
|
|
49140
|
-
return this.get(`/markets/${pubkey}`);
|
|
49149
|
+
return this.get(`/markets/${encodeURIComponent(pubkey)}`);
|
|
49141
49150
|
}
|
|
49142
49151
|
async getPools() {
|
|
49143
49152
|
return this.get("/pools");
|
|
49144
49153
|
}
|
|
49145
49154
|
async getPool(pubkey) {
|
|
49146
|
-
return this.get(`/pools/${pubkey}`);
|
|
49155
|
+
return this.get(`/pools/${encodeURIComponent(pubkey)}`);
|
|
49147
49156
|
}
|
|
49148
49157
|
async getCustodies() {
|
|
49149
49158
|
return this.get("/custodies");
|
|
49150
49159
|
}
|
|
49151
49160
|
async getCustody(pubkey) {
|
|
49152
|
-
return this.get(`/custodies/${pubkey}`);
|
|
49161
|
+
return this.get(`/custodies/${encodeURIComponent(pubkey)}`);
|
|
49153
49162
|
}
|
|
49154
49163
|
async getPrices() {
|
|
49155
49164
|
return this.get("/prices");
|
|
49156
49165
|
}
|
|
49157
49166
|
async getPrice(symbol2) {
|
|
49158
|
-
return this.get(`/prices/${symbol2}`);
|
|
49167
|
+
return this.get(`/prices/${encodeURIComponent(symbol2)}`);
|
|
49159
49168
|
}
|
|
49160
49169
|
async getPositions(owner) {
|
|
49161
|
-
const query = owner ? `?owner=${owner}` : "";
|
|
49170
|
+
const query = owner ? `?owner=${encodeURIComponent(owner)}` : "";
|
|
49162
49171
|
return this.get(`/positions${query}`);
|
|
49163
49172
|
}
|
|
49164
49173
|
async getPosition(pubkey) {
|
|
49165
|
-
return this.get(`/positions/${pubkey}`);
|
|
49174
|
+
return this.get(`/positions/${encodeURIComponent(pubkey)}`);
|
|
49166
49175
|
}
|
|
49167
49176
|
async getOwnerPositions(owner, includePnlInLeverage = false) {
|
|
49168
|
-
return this.get(`/positions/owner/${owner}?includePnlInLeverageDisplay=${includePnlInLeverage}`);
|
|
49177
|
+
return this.get(`/positions/owner/${encodeURIComponent(owner)}?includePnlInLeverageDisplay=${includePnlInLeverage}`);
|
|
49169
49178
|
}
|
|
49170
49179
|
async getOrders(owner) {
|
|
49171
|
-
const query = owner ? `?owner=${owner}` : "";
|
|
49180
|
+
const query = owner ? `?owner=${encodeURIComponent(owner)}` : "";
|
|
49172
49181
|
return this.get(`/orders${query}`);
|
|
49173
49182
|
}
|
|
49174
49183
|
async getOrder(pubkey) {
|
|
49175
|
-
return this.get(`/orders/${pubkey}`);
|
|
49184
|
+
return this.get(`/orders/${encodeURIComponent(pubkey)}`);
|
|
49176
49185
|
}
|
|
49177
49186
|
async getOwnerOrders(owner) {
|
|
49178
|
-
return this.get(`/orders/owner/${owner}`);
|
|
49187
|
+
return this.get(`/orders/owner/${encodeURIComponent(owner)}`);
|
|
49179
49188
|
}
|
|
49180
49189
|
async getPoolData() {
|
|
49181
49190
|
return this.get("/pool-data");
|
|
49182
49191
|
}
|
|
49183
49192
|
async getPoolSnapshot(poolPubkey) {
|
|
49184
|
-
return this.get(`/pool-data/${poolPubkey}`);
|
|
49193
|
+
return this.get(`/pool-data/${encodeURIComponent(poolPubkey)}`);
|
|
49185
49194
|
}
|
|
49186
49195
|
async openPosition(req) {
|
|
49187
49196
|
return this.post("/transaction-builder/open-position", req);
|
|
@@ -49307,7 +49316,7 @@ function registerMarketTools(server, client) {
|
|
|
49307
49316
|
});
|
|
49308
49317
|
server.registerTool("get_market", {
|
|
49309
49318
|
description: "Get detailed information about a specific market by its on-chain account pubkey. Returns full market configuration including permissions and collective position data.",
|
|
49310
|
-
inputSchema: { pubkey: exports_external.string().describe("Solana pubkey of the market account") }
|
|
49319
|
+
inputSchema: { pubkey: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Solana pubkey of the market account") }
|
|
49311
49320
|
}, async ({ pubkey }) => {
|
|
49312
49321
|
const market = await client.getMarket(pubkey);
|
|
49313
49322
|
return { content: [{ type: "text", text: JSON.stringify(market, null, 2) }] };
|
|
@@ -49324,7 +49333,7 @@ function registerPoolTools(server, client) {
|
|
|
49324
49333
|
});
|
|
49325
49334
|
server.registerTool("get_pool", {
|
|
49326
49335
|
description: "Get detailed information about a specific pool by its on-chain account pubkey.",
|
|
49327
|
-
inputSchema: { pubkey: exports_external.string().describe("Solana pubkey of the pool account") }
|
|
49336
|
+
inputSchema: { pubkey: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Solana pubkey of the pool account") }
|
|
49328
49337
|
}, async ({ pubkey }) => {
|
|
49329
49338
|
const pool = await client.getPool(pubkey);
|
|
49330
49339
|
return { content: [{ type: "text", text: JSON.stringify(pool, null, 2) }] };
|
|
@@ -49341,7 +49350,7 @@ function registerCustodyTools(server, client) {
|
|
|
49341
49350
|
});
|
|
49342
49351
|
server.registerTool("get_custody", {
|
|
49343
49352
|
description: "Get detailed custody information for a specific custody account. Includes utilization, fees, and limits.",
|
|
49344
|
-
inputSchema: { pubkey: exports_external.string().describe("Solana pubkey of the custody account") }
|
|
49353
|
+
inputSchema: { pubkey: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Solana pubkey of the custody account") }
|
|
49345
49354
|
}, async ({ pubkey }) => {
|
|
49346
49355
|
const custody = await client.getCustody(pubkey);
|
|
49347
49356
|
return { content: [{ type: "text", text: JSON.stringify(custody, null, 2) }] };
|
|
@@ -49366,7 +49375,7 @@ function registerPriceTools(server, client) {
|
|
|
49366
49375
|
});
|
|
49367
49376
|
server.registerTool("get_price", {
|
|
49368
49377
|
description: 'Get the current oracle price for a specific asset symbol (e.g., "SOL", "BTC", "ETH"). Case-insensitive. Returns price in USD with timestamp.',
|
|
49369
|
-
inputSchema: { symbol: exports_external.string().describe('Asset symbol, e.g. "SOL", "BTC", "ETH"') }
|
|
49378
|
+
inputSchema: { symbol: exports_external.string().max(16).describe('Asset symbol, e.g. "SOL", "BTC", "ETH"') }
|
|
49370
49379
|
}, async ({ symbol: symbol2 }) => {
|
|
49371
49380
|
const data = await client.getPrice(symbol2);
|
|
49372
49381
|
const usd = formatPrice(data.price, data.exponent);
|
|
@@ -49400,7 +49409,7 @@ function registerPositionTools(server, client) {
|
|
|
49400
49409
|
server.registerTool("get_positions", {
|
|
49401
49410
|
description: "List perpetual positions, optionally filtered by wallet owner. Without an owner filter, returns ALL open positions (may be large). With owner, returns enriched positions with computed PnL, leverage, and liquidation price.",
|
|
49402
49411
|
inputSchema: {
|
|
49403
|
-
owner: exports_external.string().optional().describe("Wallet pubkey to filter by. When provided, returns enriched positions with PnL, leverage, and liquidation price.")
|
|
49412
|
+
owner: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).optional().describe("Wallet pubkey to filter by. When provided, returns enriched positions with PnL, leverage, and liquidation price.")
|
|
49404
49413
|
}
|
|
49405
49414
|
}, async ({ owner }) => {
|
|
49406
49415
|
if (owner) {
|
|
@@ -49420,7 +49429,7 @@ ${text}` }] };
|
|
|
49420
49429
|
});
|
|
49421
49430
|
server.registerTool("get_position", {
|
|
49422
49431
|
description: "Get a single position by its on-chain account pubkey. Returns raw position data. For enriched data (PnL, leverage, liq price), use get_positions with the owner filter instead.",
|
|
49423
|
-
inputSchema: { pubkey: exports_external.string().describe("Position account pubkey") }
|
|
49432
|
+
inputSchema: { pubkey: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Position account pubkey") }
|
|
49424
49433
|
}, async ({ pubkey }) => {
|
|
49425
49434
|
const position = await client.getPosition(pubkey);
|
|
49426
49435
|
return { content: [{ type: "text", text: JSON.stringify(position, null, 2) }] };
|
|
@@ -49446,7 +49455,7 @@ function registerOrderTools(server, client) {
|
|
|
49446
49455
|
server.registerTool("get_orders", {
|
|
49447
49456
|
description: "List open orders (limit orders, take-profit, stop-loss), optionally filtered by wallet owner. When owner is provided, returns enriched order data with computed trigger prices and sizes.",
|
|
49448
49457
|
inputSchema: {
|
|
49449
|
-
owner: exports_external.string().optional().describe("Wallet pubkey to filter by. When provided, returns enriched orders.")
|
|
49458
|
+
owner: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).optional().describe("Wallet pubkey to filter by. When provided, returns enriched orders.")
|
|
49450
49459
|
}
|
|
49451
49460
|
}, async ({ owner }) => {
|
|
49452
49461
|
if (owner) {
|
|
@@ -49466,7 +49475,7 @@ ${text}` }] };
|
|
|
49466
49475
|
});
|
|
49467
49476
|
server.registerTool("get_order", {
|
|
49468
49477
|
description: "Get a single order account by its on-chain pubkey.",
|
|
49469
|
-
inputSchema: { pubkey: exports_external.string().describe("Order account pubkey") }
|
|
49478
|
+
inputSchema: { pubkey: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Order account pubkey") }
|
|
49470
49479
|
}, async ({ pubkey }) => {
|
|
49471
49480
|
const order = await client.getOrder(pubkey);
|
|
49472
49481
|
return { content: [{ type: "text", text: JSON.stringify(order, null, 2) }] };
|
|
@@ -49478,7 +49487,7 @@ function registerPoolDataTools(server, client) {
|
|
|
49478
49487
|
server.registerTool("get_pool_data", {
|
|
49479
49488
|
description: "Get computed pool metrics including AUM (assets under management), LP token stats, custody ratios, and utilization. Data is cached and refreshed every 15 seconds. Provide a pool_pubkey for a specific pool, or omit for all pools.",
|
|
49480
49489
|
inputSchema: {
|
|
49481
|
-
pool_pubkey: exports_external.string().optional().describe("Specific pool pubkey. If omitted, returns all pool snapshots.")
|
|
49490
|
+
pool_pubkey: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).optional().describe("Specific pool pubkey. If omitted, returns all pool snapshots.")
|
|
49482
49491
|
}
|
|
49483
49492
|
}, async ({ pool_pubkey }) => {
|
|
49484
49493
|
if (pool_pubkey) {
|
|
@@ -49536,17 +49545,17 @@ function registerOpenPositionTool(server, client) {
|
|
|
49536
49545
|
server.registerTool("open_position", {
|
|
49537
49546
|
description: "Build a transaction to open a new perpetual position on Flash Trade. Returns a preview (entry price, fees, leverage, liquidation price) AND an unsigned transaction. The transaction must be signed by the user's wallet and submitted to Solana separately. IMPORTANT: Always present the preview to the user before they sign. This tool does NOT execute the trade. Supports both MARKET and LIMIT orders, with optional take-profit and stop-loss. COLLATERAL WARNING: Limit orders, take-profit, and stop-loss require >$10 collateral AFTER entry fees. A $10 position will have fees deducted, dropping collateral below $10 and preventing TP/SL/limit orders. Use at least $11-12 input_amount when planning to set TP/SL.",
|
|
49538
49547
|
inputSchema: {
|
|
49539
|
-
input_token_symbol: exports_external.string().describe('Token to pay with: "USDC", "SOL", etc.'),
|
|
49540
|
-
output_token_symbol: exports_external.string().describe('Market to trade: "SOL", "BTC", "ETH", etc.'),
|
|
49541
|
-
input_amount: exports_external.string().describe('Amount of input token, e.g. "100.0"'),
|
|
49542
|
-
leverage: exports_external.string().describe('Leverage multiplier, e.g. "5.0"'),
|
|
49548
|
+
input_token_symbol: exports_external.string().max(16).describe('Token to pay with: "USDC", "SOL", etc.'),
|
|
49549
|
+
output_token_symbol: exports_external.string().max(16).describe('Market to trade: "SOL", "BTC", "ETH", etc.'),
|
|
49550
|
+
input_amount: exports_external.string().max(32).describe('Amount of input token, e.g. "100.0"'),
|
|
49551
|
+
leverage: exports_external.string().max(8).describe('Leverage multiplier, e.g. "5.0"'),
|
|
49543
49552
|
trade_type: exports_external.enum(["LONG", "SHORT"]).describe("Trade direction"),
|
|
49544
|
-
owner: exports_external.string().describe("Wallet pubkey (required to build the transaction)"),
|
|
49553
|
+
owner: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Wallet pubkey (required to build the transaction)"),
|
|
49545
49554
|
order_type: exports_external.enum(["MARKET", "LIMIT"]).optional().describe("Default: MARKET"),
|
|
49546
|
-
limit_price: exports_external.string().optional().describe("Required for LIMIT orders, UI format price"),
|
|
49547
|
-
slippage_percentage: exports_external.string().optional().describe('Default: "0.5" (0.5%)'),
|
|
49548
|
-
take_profit: exports_external.string().optional().describe("TP trigger price in UI format"),
|
|
49549
|
-
stop_loss: exports_external.string().optional().describe("SL trigger price in UI format"),
|
|
49555
|
+
limit_price: exports_external.string().max(32).optional().describe("Required for LIMIT orders, UI format price"),
|
|
49556
|
+
slippage_percentage: exports_external.string().max(8).optional().describe('Default: "0.5" (0.5%)'),
|
|
49557
|
+
take_profit: exports_external.string().max(32).optional().describe("TP trigger price in UI format"),
|
|
49558
|
+
stop_loss: exports_external.string().max(32).optional().describe("SL trigger price in UI format"),
|
|
49550
49559
|
degen_mode: exports_external.boolean().optional().describe("Enable degen mode (higher leverage limits)")
|
|
49551
49560
|
}
|
|
49552
49561
|
}, async (params) => {
|
|
@@ -49609,11 +49618,11 @@ function registerClosePositionTool(server, client) {
|
|
|
49609
49618
|
server.registerTool("close_position", {
|
|
49610
49619
|
description: "Build a transaction to close (fully or partially) an existing perpetual position. Returns a preview with PnL, fees, and receive amount, plus an unsigned transaction. For a full close, set input_usd to the position's full size. For a partial close, use a smaller amount. The transaction must be signed and submitted separately.",
|
|
49611
49620
|
inputSchema: {
|
|
49612
|
-
position_key: exports_external.string().describe("Position account pubkey to close"),
|
|
49613
|
-
input_usd: exports_external.string().describe('USD amount to close, e.g. "500.00" for full or "250.00" for partial'),
|
|
49614
|
-
withdraw_token_symbol: exports_external.string().describe('Token to receive: "USDC", "SOL", etc.'),
|
|
49621
|
+
position_key: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Position account pubkey to close"),
|
|
49622
|
+
input_usd: exports_external.string().max(32).describe('USD amount to close, e.g. "500.00" for full or "250.00" for partial'),
|
|
49623
|
+
withdraw_token_symbol: exports_external.string().max(16).describe('Token to receive: "USDC", "SOL", etc.'),
|
|
49615
49624
|
keep_leverage_same: exports_external.boolean().optional().describe("Keep leverage constant during partial close"),
|
|
49616
|
-
slippage_percentage: exports_external.string().optional().describe('Default: "0.5" (0.5%)')
|
|
49625
|
+
slippage_percentage: exports_external.string().max(8).optional().describe('Default: "0.5" (0.5%)')
|
|
49617
49626
|
}
|
|
49618
49627
|
}, async (params) => {
|
|
49619
49628
|
const res = await client.closePosition({
|
|
@@ -49672,11 +49681,11 @@ function registerCollateralTools(server, client) {
|
|
|
49672
49681
|
server.registerTool("add_collateral", {
|
|
49673
49682
|
description: "Build a transaction to add collateral to an existing position. This reduces leverage and moves the liquidation price further from the current price (safer). Returns a preview and unsigned transaction.",
|
|
49674
49683
|
inputSchema: {
|
|
49675
|
-
position_key: exports_external.string().describe("Position account pubkey"),
|
|
49676
|
-
deposit_amount: exports_external.string().describe("Amount to deposit in UI format"),
|
|
49677
|
-
deposit_token_symbol: exports_external.string().describe('Token to deposit: "USDC", "SOL", etc.'),
|
|
49678
|
-
owner: exports_external.string().describe("Wallet pubkey"),
|
|
49679
|
-
slippage_percentage: exports_external.string().optional().describe('Default: "0.5" (0.5%)')
|
|
49684
|
+
position_key: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Position account pubkey"),
|
|
49685
|
+
deposit_amount: exports_external.string().max(32).describe("Amount to deposit in UI format"),
|
|
49686
|
+
deposit_token_symbol: exports_external.string().max(16).describe('Token to deposit: "USDC", "SOL", etc.'),
|
|
49687
|
+
owner: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Wallet pubkey"),
|
|
49688
|
+
slippage_percentage: exports_external.string().max(8).optional().describe('Default: "0.5" (0.5%)')
|
|
49680
49689
|
}
|
|
49681
49690
|
}, async (params) => {
|
|
49682
49691
|
const res = await client.addCollateral({
|
|
@@ -49691,11 +49700,11 @@ function registerCollateralTools(server, client) {
|
|
|
49691
49700
|
server.registerTool("remove_collateral", {
|
|
49692
49701
|
description: "Build a transaction to remove collateral from an existing position. This increases leverage and moves the liquidation price closer (riskier). WARNING: Removing too much collateral can lead to liquidation. Returns a preview and unsigned transaction.",
|
|
49693
49702
|
inputSchema: {
|
|
49694
|
-
position_key: exports_external.string().describe("Position account pubkey"),
|
|
49695
|
-
withdraw_amount_usd: exports_external.string().describe("USD amount to withdraw"),
|
|
49696
|
-
withdraw_token_symbol: exports_external.string().describe('Token to receive: "USDC", "SOL", etc.'),
|
|
49697
|
-
owner: exports_external.string().describe("Wallet pubkey"),
|
|
49698
|
-
slippage_percentage: exports_external.string().optional().describe('Default: "0.5" (0.5%)')
|
|
49703
|
+
position_key: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Position account pubkey"),
|
|
49704
|
+
withdraw_amount_usd: exports_external.string().max(32).describe("USD amount to withdraw"),
|
|
49705
|
+
withdraw_token_symbol: exports_external.string().max(16).describe('Token to receive: "USDC", "SOL", etc.'),
|
|
49706
|
+
owner: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Wallet pubkey"),
|
|
49707
|
+
slippage_percentage: exports_external.string().max(8).optional().describe('Default: "0.5" (0.5%)')
|
|
49699
49708
|
}
|
|
49700
49709
|
}, async (params) => {
|
|
49701
49710
|
const res = await client.removeCollateral({
|
|
@@ -49743,9 +49752,9 @@ function registerReversePositionTool(server, client) {
|
|
|
49743
49752
|
server.registerTool("reverse_position", {
|
|
49744
49753
|
description: "Build a transaction to reverse a position (close current + open opposite direction). For example, close a LONG and open a SHORT with the same collateral. Returns combined close+open preview and a single unsigned transaction.",
|
|
49745
49754
|
inputSchema: {
|
|
49746
|
-
position_key: exports_external.string().describe("Position account pubkey to reverse"),
|
|
49747
|
-
owner: exports_external.string().describe("Wallet pubkey"),
|
|
49748
|
-
slippage_percentage: exports_external.string().optional().describe('Default: "0.5" (0.5%)'),
|
|
49755
|
+
position_key: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Position account pubkey to reverse"),
|
|
49756
|
+
owner: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Wallet pubkey"),
|
|
49757
|
+
slippage_percentage: exports_external.string().max(8).optional().describe('Default: "0.5" (0.5%)'),
|
|
49749
49758
|
degen_mode: exports_external.boolean().optional().describe("Enable degen mode for the new position")
|
|
49750
49759
|
}
|
|
49751
49760
|
}, async (params) => {
|
|
@@ -49764,11 +49773,11 @@ function registerPreviewTools(server, client) {
|
|
|
49764
49773
|
server.registerTool("preview_limit_order_fees", {
|
|
49765
49774
|
description: "Preview the entry price, fees, liquidation price, and borrow rate for a limit order BEFORE placing it. Use this to evaluate a trade before committing. No transaction is built.",
|
|
49766
49775
|
inputSchema: {
|
|
49767
|
-
market_symbol: exports_external.string().describe('Market symbol, e.g. "SOL", "BTC", "ETH"'),
|
|
49768
|
-
input_amount: exports_external.string().describe("Collateral amount in UI format"),
|
|
49769
|
-
output_amount: exports_external.string().describe("Position size in target token"),
|
|
49776
|
+
market_symbol: exports_external.string().max(16).describe('Market symbol, e.g. "SOL", "BTC", "ETH"'),
|
|
49777
|
+
input_amount: exports_external.string().max(32).describe("Collateral amount in UI format"),
|
|
49778
|
+
output_amount: exports_external.string().max(32).describe("Position size in target token"),
|
|
49770
49779
|
side: exports_external.enum(["LONG", "SHORT"]).describe("Trade direction"),
|
|
49771
|
-
limit_price: exports_external.string().optional().describe("Limit price; uses live price if omitted"),
|
|
49780
|
+
limit_price: exports_external.string().max(32).optional().describe("Limit price; uses live price if omitted"),
|
|
49772
49781
|
trading_fee_discount_percent: exports_external.number().optional().describe("Fee discount from FAF staking (0-100)")
|
|
49773
49782
|
}
|
|
49774
49783
|
}, async (params) => {
|
|
@@ -49796,8 +49805,8 @@ WARNING: ${res.err}`);
|
|
|
49796
49805
|
server.registerTool("preview_exit_fee", {
|
|
49797
49806
|
description: "Preview the exit fee and exit price for closing a specific amount of a position. Use this to estimate close costs before calling close_position. No transaction is built.",
|
|
49798
49807
|
inputSchema: {
|
|
49799
|
-
position_key: exports_external.string().describe("Position account pubkey"),
|
|
49800
|
-
close_amount_usd: exports_external.string().describe("USD amount to close")
|
|
49808
|
+
position_key: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Position account pubkey"),
|
|
49809
|
+
close_amount_usd: exports_external.string().max(32).describe("USD amount to close")
|
|
49801
49810
|
}
|
|
49802
49811
|
}, async (params) => {
|
|
49803
49812
|
const res = await client.previewExitFee({
|
|
@@ -49819,14 +49828,14 @@ WARNING: ${res.err}`);
|
|
|
49819
49828
|
description: 'Calculate take-profit or stop-loss prices and projected PnL. Three modes: "forward" (trigger price → PnL), "reverse_pnl" (target PnL → trigger price), "reverse_roi" (target ROI% → trigger price). Works for existing positions (by pubkey) or hypothetical orders (provide market_symbol, entry_price, size, collateral, side).',
|
|
49820
49829
|
inputSchema: {
|
|
49821
49830
|
mode: exports_external.enum(["forward", "reverse_pnl", "reverse_roi"]).describe("Calculation mode"),
|
|
49822
|
-
position_key: exports_external.string().optional().describe("Position pubkey (for existing positions)"),
|
|
49823
|
-
market_symbol: exports_external.string().optional().describe("Market symbol (for hypothetical orders)"),
|
|
49824
|
-
entry_price: exports_external.string().optional().describe("Entry price (for hypothetical orders)"),
|
|
49825
|
-
size_usd: exports_external.string().optional().describe("Position size USD (for hypothetical orders)"),
|
|
49826
|
-
collateral_usd: exports_external.string().optional().describe("Collateral USD (for hypothetical orders)"),
|
|
49831
|
+
position_key: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).optional().describe("Position pubkey (for existing positions)"),
|
|
49832
|
+
market_symbol: exports_external.string().max(16).optional().describe("Market symbol (for hypothetical orders)"),
|
|
49833
|
+
entry_price: exports_external.string().max(32).optional().describe("Entry price (for hypothetical orders)"),
|
|
49834
|
+
size_usd: exports_external.string().max(32).optional().describe("Position size USD (for hypothetical orders)"),
|
|
49835
|
+
collateral_usd: exports_external.string().max(32).optional().describe("Collateral USD (for hypothetical orders)"),
|
|
49827
49836
|
side: exports_external.enum(["LONG", "SHORT"]).optional().describe("Side (for hypothetical orders)"),
|
|
49828
|
-
trigger_price: exports_external.string().optional().describe('Trigger price (required for "forward" mode)'),
|
|
49829
|
-
target_pnl_usd: exports_external.string().optional().describe('Target PnL USD (required for "reverse_pnl" mode)'),
|
|
49837
|
+
trigger_price: exports_external.string().max(32).optional().describe('Trigger price (required for "forward" mode)'),
|
|
49838
|
+
target_pnl_usd: exports_external.string().max(32).optional().describe('Target PnL USD (required for "reverse_pnl" mode)'),
|
|
49830
49839
|
target_roi_percent: exports_external.number().optional().describe('Target ROI% (required for "reverse_roi" mode)')
|
|
49831
49840
|
}
|
|
49832
49841
|
}, async (params) => {
|
|
@@ -49858,8 +49867,8 @@ WARNING: ${res.err}`);
|
|
|
49858
49867
|
server.registerTool("preview_margin", {
|
|
49859
49868
|
description: "Preview the effect of adding or removing margin (collateral) on a position. Shows new leverage, new liquidation price, and max adjustable amount. Use this before calling add_collateral or remove_collateral. No transaction is built.",
|
|
49860
49869
|
inputSchema: {
|
|
49861
|
-
position_key: exports_external.string().describe("Position account pubkey"),
|
|
49862
|
-
margin_delta_usd: exports_external.string().describe("Amount in USD to add or remove"),
|
|
49870
|
+
position_key: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Position account pubkey"),
|
|
49871
|
+
margin_delta_usd: exports_external.string().max(32).describe("Amount in USD to add or remove"),
|
|
49863
49872
|
action: exports_external.enum(["ADD", "REMOVE"]).describe("ADD to reduce leverage, REMOVE to increase leverage")
|
|
49864
49873
|
}
|
|
49865
49874
|
}, async (params) => {
|
|
@@ -56796,15 +56805,19 @@ var $VersionedTransaction = VersionedTransaction;
|
|
|
56796
56805
|
|
|
56797
56806
|
// src/tools/sign-and-send.ts
|
|
56798
56807
|
import fs from "node:fs";
|
|
56808
|
+
|
|
56809
|
+
// src/sanitize.ts
|
|
56799
56810
|
function sanitizeError(e) {
|
|
56800
56811
|
const msg = e instanceof Error ? e.message : String(e);
|
|
56801
|
-
return msg.replace(/\[[\d,\s]{20,}\]/g, "[REDACTED]").replace(/[0-9a-fA-F]{40,}/g, "[REDACTED]").replace(/[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{40,}/g, "[REDACTED]");
|
|
56812
|
+
return msg.replace(/\[[\d,\s]{20,}\]/g, "[REDACTED]").replace(/[0-9a-fA-F]{40,}/g, "[REDACTED]").replace(/[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{40,}/g, "[REDACTED]").replace(/[A-Za-z0-9+/]{40,}={0,2}/g, "[REDACTED]");
|
|
56802
56813
|
}
|
|
56814
|
+
|
|
56815
|
+
// src/tools/sign-and-send.ts
|
|
56803
56816
|
function registerSignAndSendTool(server) {
|
|
56804
56817
|
server.registerTool("sign_and_send", {
|
|
56805
56818
|
description: "Sign and submit a base64-encoded unsigned Solana transaction using the locally configured keypair. " + "Call this AFTER a transaction tool (open_position, close_position, add_collateral, remove_collateral, reverse_position) " + "returns a transactionBase64 string AND the user has reviewed and approved the preview. " + "The keypair is read from KEYPAIR_PATH (default: ~/.config/solana/id.json). " + "Returns the confirmed transaction signature and a Solscan link. " + "IMPORTANT: Always show the transaction preview to the user and get their approval BEFORE calling this tool. " + "This tool signs with the local keypair and submits to Solana mainnet — the action is IRREVERSIBLE. " + "NOTE: This tool never exposes private key material in its output.",
|
|
56806
56819
|
inputSchema: {
|
|
56807
|
-
transaction_base64: exports_external.string().describe("The base64-encoded unsigned transaction returned by a transaction tool")
|
|
56820
|
+
transaction_base64: exports_external.string().max(1e4).describe("The base64-encoded unsigned transaction returned by a transaction tool")
|
|
56808
56821
|
}
|
|
56809
56822
|
}, async (params) => {
|
|
56810
56823
|
const rpcUrl = process.env.SOLANA_RPC_URL ?? "https://api.mainnet-beta.solana.com";
|
|
@@ -56960,11 +56973,11 @@ function registerResources(server, client) {
|
|
|
56960
56973
|
|
|
56961
56974
|
// src/index.ts
|
|
56962
56975
|
process.on("uncaughtException", (err) => {
|
|
56963
|
-
console.error("[flash-trade-mcp] Uncaught exception:", err);
|
|
56976
|
+
console.error("[flash-trade-mcp] Uncaught exception:", sanitizeError(err));
|
|
56964
56977
|
process.exit(1);
|
|
56965
56978
|
});
|
|
56966
56979
|
process.on("unhandledRejection", (reason) => {
|
|
56967
|
-
console.error("[flash-trade-mcp] Unhandled rejection:", reason);
|
|
56980
|
+
console.error("[flash-trade-mcp] Unhandled rejection:", sanitizeError(reason));
|
|
56968
56981
|
process.exit(1);
|
|
56969
56982
|
});
|
|
56970
56983
|
try {
|
|
@@ -56972,7 +56985,7 @@ try {
|
|
|
56972
56985
|
const client = new FlashApiClient(config2);
|
|
56973
56986
|
const server = new McpServer({
|
|
56974
56987
|
name: "flash-trade",
|
|
56975
|
-
version: "0.
|
|
56988
|
+
version: "0.3.1"
|
|
56976
56989
|
}, {
|
|
56977
56990
|
capabilities: {
|
|
56978
56991
|
tools: {},
|
|
@@ -56986,6 +56999,6 @@ try {
|
|
|
56986
56999
|
const transport = new StdioServerTransport;
|
|
56987
57000
|
await server.connect(transport);
|
|
56988
57001
|
} catch (err) {
|
|
56989
|
-
console.error("[flash-trade-mcp] Fatal startup error:", err);
|
|
57002
|
+
console.error("[flash-trade-mcp] Fatal startup error:", sanitizeError(err));
|
|
56990
57003
|
process.exit(1);
|
|
56991
57004
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flash-trade-mcp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "MCP server for Flash Trade — perpetual futures DEX on Solana. Provides AI agents with typed tools for trading, position management, and market data.",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"main": "dist/index.js",
|