flash-trade-mcp 0.3.4 → 0.4.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/dist/index.js +231 -33
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -49236,7 +49236,7 @@ class FlashApiClient {
|
|
|
49236
49236
|
// src/tools/health.ts
|
|
49237
49237
|
function registerHealthTools(server, client) {
|
|
49238
49238
|
server.registerTool("health_check", {
|
|
49239
|
-
description: "
|
|
49239
|
+
description: "Verify Flash Trade API connectivity and status. Call this first before any other tool. Returns service status and account counts."
|
|
49240
49240
|
}, async () => {
|
|
49241
49241
|
const health = await client.getHealth();
|
|
49242
49242
|
const lines = [`Status: ${health.status}`];
|
|
@@ -49250,7 +49250,7 @@ function registerHealthTools(server, client) {
|
|
|
49250
49250
|
});
|
|
49251
49251
|
}
|
|
49252
49252
|
|
|
49253
|
-
// src/tools/
|
|
49253
|
+
// src/tools/shared/custody-map.ts
|
|
49254
49254
|
var VIRTUAL_CUSTODY_MAP = {
|
|
49255
49255
|
"6bthDsp8pcGBGKVKCKZjV5JfuSUNRo62RG4hQHj1u4CK": { symbol: "BNB", pool: "Crypto.1", maxLeverage: "60.00" },
|
|
49256
49256
|
A8SKWb3pwbFUtxLQhnpUTfy7CkxBpWGvTLYyJyWHCMWv: { symbol: "PYTH", pool: "Governance.1", maxLeverage: "60.00" },
|
|
@@ -49270,6 +49270,13 @@ var VIRTUAL_CUSTODY_MAP = {
|
|
|
49270
49270
|
RQNURQjDbq2Yah2udtFTNT7TjR15vsPV3oJNnwYher8: { symbol: "TSLA", pool: "Equity.1", maxLeverage: "12.00" },
|
|
49271
49271
|
ArnD1faZVVkkewX4HUSoDuht46egAtVvhDTFMJn3DkFo: { symbol: "SAMO", pool: "Community.3", maxLeverage: "50.00" }
|
|
49272
49272
|
};
|
|
49273
|
+
function formatPriceUsd(data) {
|
|
49274
|
+
const price = parseFloat(data.price);
|
|
49275
|
+
const exp = parseFloat(data.exponent);
|
|
49276
|
+
if (isNaN(price) || isNaN(exp))
|
|
49277
|
+
return "?";
|
|
49278
|
+
return (price * Math.pow(10, exp)).toFixed(2);
|
|
49279
|
+
}
|
|
49273
49280
|
function buildCustodySymbolMap(poolData) {
|
|
49274
49281
|
const map3 = new Map;
|
|
49275
49282
|
for (const pool of poolData.pools) {
|
|
@@ -49288,6 +49295,8 @@ function buildCustodySymbolMap(poolData) {
|
|
|
49288
49295
|
}
|
|
49289
49296
|
return map3;
|
|
49290
49297
|
}
|
|
49298
|
+
|
|
49299
|
+
// src/tools/markets.ts
|
|
49291
49300
|
function formatMarketsSummary(markets, custodyInfo) {
|
|
49292
49301
|
const lines = [
|
|
49293
49302
|
`${markets.length} markets available:
|
|
@@ -49317,7 +49326,7 @@ Use get_market with a pubkey for full details. Use get_prices for current oracle
|
|
|
49317
49326
|
}
|
|
49318
49327
|
function registerMarketTools(server, client) {
|
|
49319
49328
|
server.registerTool("get_markets", {
|
|
49320
|
-
description: "List all available perpetual futures markets
|
|
49329
|
+
description: "List all available perpetual futures markets. Returns a summary table with symbol, side, pool, max leverage, and pubkey. For a trading-ready view with prices, use get_trading_overview instead."
|
|
49321
49330
|
}, async () => {
|
|
49322
49331
|
const [markets, poolData] = await Promise.all([
|
|
49323
49332
|
client.getMarkets(),
|
|
@@ -49370,10 +49379,26 @@ Use get_pool with a pubkey for full details. Use get_pool_data for AUM and utili
|
|
|
49370
49379
|
// src/tools/custodies.ts
|
|
49371
49380
|
function registerCustodyTools(server, client) {
|
|
49372
49381
|
server.registerTool("get_custodies", {
|
|
49373
|
-
description: "List all custody accounts
|
|
49382
|
+
description: "List all custody accounts (token vaults) across pools. Returns a compact summary with symbol, mint, and pubkey. Use get_custody with a specific pubkey for full details (utilization, fees, limits)."
|
|
49374
49383
|
}, async () => {
|
|
49375
49384
|
const custodies = await client.getCustodies();
|
|
49376
|
-
|
|
49385
|
+
const lines = [
|
|
49386
|
+
`${custodies.length} custody accounts:
|
|
49387
|
+
`,
|
|
49388
|
+
"Symbol | Mint | Pubkey",
|
|
49389
|
+
"-----------|----------------------------------------------|----------------------------------------------"
|
|
49390
|
+
];
|
|
49391
|
+
for (const c of custodies) {
|
|
49392
|
+
const acct = c.account ?? {};
|
|
49393
|
+
const mintObj = typeof acct.mint === "object" ? acct.mint : null;
|
|
49394
|
+
const symbol2 = (acct.symbol ?? mintObj?.symbol ?? "?").toString().padEnd(10);
|
|
49395
|
+
const mint = (mintObj?.key ?? (typeof acct.mint === "string" ? acct.mint : "?")).toString().slice(0, 44).padEnd(44);
|
|
49396
|
+
lines.push(`${symbol2} | ${mint} | ${c.pubkey}`);
|
|
49397
|
+
}
|
|
49398
|
+
lines.push(`
|
|
49399
|
+
Use get_custody with a pubkey for full details (utilization, fees, limits).`);
|
|
49400
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
49401
|
+
`) }] };
|
|
49377
49402
|
});
|
|
49378
49403
|
server.registerTool("get_custody", {
|
|
49379
49404
|
description: "Get detailed custody information for a specific custody account. Includes utilization, fees, and limits.",
|
|
@@ -49434,7 +49459,7 @@ function formatEnrichedPosition(p) {
|
|
|
49434
49459
|
}
|
|
49435
49460
|
function registerPositionTools(server, client) {
|
|
49436
49461
|
server.registerTool("get_positions", {
|
|
49437
|
-
description: "List perpetual positions
|
|
49462
|
+
description: "List open perpetual positions. Without owner: returns all positions (large response). With owner: returns enriched positions with live PnL, leverage, and liquidation price. For a complete wallet overview, use get_account_summary instead.",
|
|
49438
49463
|
inputSchema: {
|
|
49439
49464
|
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.")
|
|
49440
49465
|
}
|
|
@@ -49455,7 +49480,7 @@ ${text}` }] };
|
|
|
49455
49480
|
return { content: [{ type: "text", text: JSON.stringify(positions, null, 2) }] };
|
|
49456
49481
|
});
|
|
49457
49482
|
server.registerTool("get_position", {
|
|
49458
|
-
description: "Get a single position by its on-chain
|
|
49483
|
+
description: "Get a single position by its on-chain pubkey. Returns raw position data. For enriched data with PnL and leverage, use get_positions with owner or get_account_summary.",
|
|
49459
49484
|
inputSchema: { pubkey: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Position account pubkey") }
|
|
49460
49485
|
}, async ({ pubkey }) => {
|
|
49461
49486
|
const position = await client.getPosition(pubkey);
|
|
@@ -49480,7 +49505,7 @@ function formatEnrichedOrder(o) {
|
|
|
49480
49505
|
}
|
|
49481
49506
|
function registerOrderTools(server, client) {
|
|
49482
49507
|
server.registerTool("get_orders", {
|
|
49483
|
-
description: "List
|
|
49508
|
+
description: "List pending orders (limit, take-profit, stop-loss). With owner: returns enriched orders with computed trigger prices and sizes. Needed to find order_id (0-7) for edit_trigger_order or cancel_trigger_order.",
|
|
49484
49509
|
inputSchema: {
|
|
49485
49510
|
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.")
|
|
49486
49511
|
}
|
|
@@ -49544,6 +49569,176 @@ Use get_pool_data with pool_pubkey for full custody stats.`);
|
|
|
49544
49569
|
});
|
|
49545
49570
|
}
|
|
49546
49571
|
|
|
49572
|
+
// src/sanitize.ts
|
|
49573
|
+
var zBool = exports_external.preprocess((v) => {
|
|
49574
|
+
if (typeof v === "string")
|
|
49575
|
+
return v === "true";
|
|
49576
|
+
return v;
|
|
49577
|
+
}, exports_external.boolean());
|
|
49578
|
+
function sanitizeError(e) {
|
|
49579
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
49580
|
+
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]");
|
|
49581
|
+
}
|
|
49582
|
+
|
|
49583
|
+
// src/tools/account-summary.ts
|
|
49584
|
+
function registerAccountSummaryTool(server, client) {
|
|
49585
|
+
server.registerTool("get_account_summary", {
|
|
49586
|
+
description: "Get a complete wallet overview: all open positions (with PnL), all pending orders (limit/TP/SL), and current prices for held markets. " + "Recommended first call when managing a specific wallet — replaces calling get_positions + get_orders + get_prices separately.",
|
|
49587
|
+
inputSchema: {
|
|
49588
|
+
owner: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Wallet pubkey")
|
|
49589
|
+
}
|
|
49590
|
+
}, async ({ owner }) => {
|
|
49591
|
+
const [posResult, ordResult, priceResult] = await Promise.allSettled([
|
|
49592
|
+
client.getOwnerPositions(owner),
|
|
49593
|
+
client.getOwnerOrders(owner),
|
|
49594
|
+
client.getPrices()
|
|
49595
|
+
]);
|
|
49596
|
+
const positions = posResult.status === "fulfilled" ? posResult.value : null;
|
|
49597
|
+
const orders = ordResult.status === "fulfilled" ? ordResult.value : null;
|
|
49598
|
+
const prices = priceResult.status === "fulfilled" ? priceResult.value : null;
|
|
49599
|
+
const lines = [`=== Account Summary for ${owner} ===
|
|
49600
|
+
`];
|
|
49601
|
+
const warnings = [];
|
|
49602
|
+
if (!positions)
|
|
49603
|
+
warnings.push(`Positions unavailable: ${sanitizeError(posResult.reason)}`);
|
|
49604
|
+
if (!orders)
|
|
49605
|
+
warnings.push(`Orders unavailable: ${sanitizeError(ordResult.reason)}`);
|
|
49606
|
+
if (!prices)
|
|
49607
|
+
warnings.push(`Prices unavailable: ${sanitizeError(priceResult.reason)}`);
|
|
49608
|
+
if (!positions) {
|
|
49609
|
+
lines.push(`Positions: unavailable
|
|
49610
|
+
`);
|
|
49611
|
+
} else if (positions.length === 0) {
|
|
49612
|
+
lines.push(`Positions: None
|
|
49613
|
+
`);
|
|
49614
|
+
} else {
|
|
49615
|
+
lines.push(`── ${positions.length} Position(s) ──`);
|
|
49616
|
+
for (const p of positions) {
|
|
49617
|
+
const pnl = p.pnlWithFeeUsdUi ? ` | PnL: $${p.pnlWithFeeUsdUi} (${p.pnlPercentageWithFee}%)` : "";
|
|
49618
|
+
lines.push(` ${p.sideUi} ${p.marketSymbol}: $${p.sizeUsdUi} @ $${p.entryPriceUi} (${p.leverageUi}x)${pnl}`);
|
|
49619
|
+
lines.push(` Key: ${p.key} | Liq: $${p.liquidationPriceUi}`);
|
|
49620
|
+
}
|
|
49621
|
+
lines.push("");
|
|
49622
|
+
}
|
|
49623
|
+
if (!orders) {
|
|
49624
|
+
lines.push(`Orders: unavailable
|
|
49625
|
+
`);
|
|
49626
|
+
} else if (orders.length === 0) {
|
|
49627
|
+
lines.push(`Orders: None
|
|
49628
|
+
`);
|
|
49629
|
+
} else {
|
|
49630
|
+
lines.push(`── Orders ──`);
|
|
49631
|
+
for (const o of orders) {
|
|
49632
|
+
for (const lo of o.limitOrders ?? []) {
|
|
49633
|
+
lines.push(` LIMIT ${lo.sideUi} ${lo.symbol}: $${lo.sizeUsdUi} @ $${lo.entryPriceUi} (${lo.leverageUi}x)`);
|
|
49634
|
+
}
|
|
49635
|
+
for (const tp of o.takeProfitOrders ?? []) {
|
|
49636
|
+
lines.push(` TP ${tp.sideUi} ${tp.symbol}: $${tp.sizeUsdUi} @ $${tp.triggerPriceUi}`);
|
|
49637
|
+
}
|
|
49638
|
+
for (const sl of o.stopLossOrders ?? []) {
|
|
49639
|
+
lines.push(` SL ${sl.sideUi} ${sl.symbol}: $${sl.sizeUsdUi} @ $${sl.triggerPriceUi}`);
|
|
49640
|
+
}
|
|
49641
|
+
}
|
|
49642
|
+
lines.push("");
|
|
49643
|
+
}
|
|
49644
|
+
if (positions && prices) {
|
|
49645
|
+
const marketSymbols = [...new Set(positions.map((p) => p.marketSymbol).filter((s) => !!s))];
|
|
49646
|
+
if (marketSymbols.length > 0) {
|
|
49647
|
+
lines.push("── Current Prices ──");
|
|
49648
|
+
for (const sym of marketSymbols) {
|
|
49649
|
+
const priceData = prices[sym];
|
|
49650
|
+
if (priceData) {
|
|
49651
|
+
const usd = formatPriceUsd(priceData);
|
|
49652
|
+
lines.push(` ${sym}: ${usd === "?" ? "price unavailable" : `$${usd}`}`);
|
|
49653
|
+
}
|
|
49654
|
+
}
|
|
49655
|
+
lines.push("");
|
|
49656
|
+
}
|
|
49657
|
+
}
|
|
49658
|
+
if (warnings.length > 0) {
|
|
49659
|
+
lines.push("── Warnings ──");
|
|
49660
|
+
for (const w of warnings)
|
|
49661
|
+
lines.push(` ${w}`);
|
|
49662
|
+
}
|
|
49663
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
49664
|
+
`) }] };
|
|
49665
|
+
});
|
|
49666
|
+
}
|
|
49667
|
+
|
|
49668
|
+
// src/tools/trading-overview.ts
|
|
49669
|
+
function registerTradingOverviewTool(server, client) {
|
|
49670
|
+
server.registerTool("get_trading_overview", {
|
|
49671
|
+
description: "Get a trading-ready market snapshot: all markets with current oracle prices, max leverage, and pool utilization. " + "Recommended first call when planning new trades — replaces calling get_markets + get_prices + get_pool_data separately."
|
|
49672
|
+
}, async () => {
|
|
49673
|
+
const [marketsResult, pricesResult, poolResult] = await Promise.allSettled([
|
|
49674
|
+
client.getMarkets(),
|
|
49675
|
+
client.getPrices(),
|
|
49676
|
+
client.getPoolData()
|
|
49677
|
+
]);
|
|
49678
|
+
const markets = marketsResult.status === "fulfilled" ? marketsResult.value : null;
|
|
49679
|
+
const prices = pricesResult.status === "fulfilled" ? pricesResult.value : null;
|
|
49680
|
+
const poolData = poolResult.status === "fulfilled" ? poolResult.value : null;
|
|
49681
|
+
const lines = [`=== Trading Overview ===
|
|
49682
|
+
`];
|
|
49683
|
+
const warnings = [];
|
|
49684
|
+
if (!markets)
|
|
49685
|
+
warnings.push(`Markets unavailable: ${sanitizeError(marketsResult.reason)}`);
|
|
49686
|
+
if (!prices)
|
|
49687
|
+
warnings.push(`Prices unavailable: ${sanitizeError(pricesResult.reason)}`);
|
|
49688
|
+
if (!poolData)
|
|
49689
|
+
warnings.push(`Pool data unavailable: ${sanitizeError(poolResult.reason)}`);
|
|
49690
|
+
if (markets) {
|
|
49691
|
+
const custodyInfo = poolData ? buildCustodySymbolMap(poolData) : new Map;
|
|
49692
|
+
lines.push("── Markets ──");
|
|
49693
|
+
lines.push("Symbol | Price | Side | Max Lev | Pool");
|
|
49694
|
+
lines.push("-----------|---------------|-------|---------|---------------");
|
|
49695
|
+
const enriched = markets.map((m) => {
|
|
49696
|
+
const info = custodyInfo.get(m.account.target_custody);
|
|
49697
|
+
return {
|
|
49698
|
+
symbol: info?.symbol ?? "UNKNOWN",
|
|
49699
|
+
pool: info?.pool ?? "?",
|
|
49700
|
+
maxLeverage: info?.maxLeverage ?? "?",
|
|
49701
|
+
side: m.account.side,
|
|
49702
|
+
open: m.account.permissions.allow_open_position
|
|
49703
|
+
};
|
|
49704
|
+
}).sort((a, b) => a.pool.localeCompare(b.pool) || a.symbol.localeCompare(b.symbol) || a.side.localeCompare(b.side));
|
|
49705
|
+
for (const m of enriched) {
|
|
49706
|
+
let priceStr = "?";
|
|
49707
|
+
if (prices) {
|
|
49708
|
+
const priceData = prices[m.symbol];
|
|
49709
|
+
if (priceData)
|
|
49710
|
+
priceStr = `$${formatPriceUsd(priceData)}`;
|
|
49711
|
+
}
|
|
49712
|
+
const status = m.open ? "" : " [CLOSED]";
|
|
49713
|
+
lines.push(`${m.symbol.padEnd(10)} | ${priceStr.padEnd(13)} | ${m.side.padEnd(5)} | ${m.maxLeverage.padEnd(7)} | ${m.pool}${status}`);
|
|
49714
|
+
}
|
|
49715
|
+
}
|
|
49716
|
+
if (poolData) {
|
|
49717
|
+
lines.push(`
|
|
49718
|
+
── Pool Utilization ──`);
|
|
49719
|
+
lines.push("Pool | AUM | LP Price | Stable%");
|
|
49720
|
+
lines.push("---------------|----------------|-----------|--------");
|
|
49721
|
+
for (const p of poolData.pools) {
|
|
49722
|
+
const name = (p.poolName ?? "Unknown").padEnd(14);
|
|
49723
|
+
const aum = `$${p.lpStats?.totalPoolValueUsd ?? "?"}`.padEnd(14);
|
|
49724
|
+
const lp = `$${p.lpStats?.lpPrice ?? "?"}`.padEnd(9);
|
|
49725
|
+
const stable = `${p.lpStats?.stableCoinPercentage ?? "?"}%`;
|
|
49726
|
+
lines.push(`${name} | ${aum} | ${lp} | ${stable}`);
|
|
49727
|
+
}
|
|
49728
|
+
}
|
|
49729
|
+
if (warnings.length > 0) {
|
|
49730
|
+
lines.push(`
|
|
49731
|
+
── Warnings ──`);
|
|
49732
|
+
for (const w of warnings)
|
|
49733
|
+
lines.push(` ${w}`);
|
|
49734
|
+
}
|
|
49735
|
+
lines.push(`
|
|
49736
|
+
Use open_position to trade. Use get_account_summary to check existing positions.`);
|
|
49737
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
49738
|
+
`) }] };
|
|
49739
|
+
});
|
|
49740
|
+
}
|
|
49741
|
+
|
|
49547
49742
|
// src/tools/open-position.ts
|
|
49548
49743
|
function formatOpenPreview(req, res) {
|
|
49549
49744
|
const lines = [
|
|
@@ -49588,7 +49783,7 @@ No transaction built (provide owner wallet pubkey to build transaction)`);
|
|
|
49588
49783
|
}
|
|
49589
49784
|
function registerOpenPositionTool(server, client) {
|
|
49590
49785
|
server.registerTool("open_position", {
|
|
49591
|
-
description: "Build a transaction to open a new perpetual position
|
|
49786
|
+
description: "Build a transaction to open a new perpetual futures position. Returns a preview (entry price, fees, leverage, liquidation price) AND an unsigned transaction. " + "Show the preview to the user before signing. Supports MARKET and LIMIT orders with optional TP/SL. " + "IMPORTANT: Use at least $11 input_amount if setting take_profit or stop_loss — collateral after fees must exceed $10 for TP/SL to work on-chain.",
|
|
49592
49787
|
inputSchema: {
|
|
49593
49788
|
input_token_symbol: exports_external.string().max(16).describe('Token to pay with: "USDC", "SOL", etc.'),
|
|
49594
49789
|
output_token_symbol: exports_external.string().max(16).describe('Market to trade: "SOL", "BTC", "ETH", etc.'),
|
|
@@ -49601,7 +49796,7 @@ function registerOpenPositionTool(server, client) {
|
|
|
49601
49796
|
slippage_percentage: exports_external.string().max(8).optional().describe('Default: "0.5" (0.5%)'),
|
|
49602
49797
|
take_profit: exports_external.string().max(32).optional().describe("TP trigger price in UI format"),
|
|
49603
49798
|
stop_loss: exports_external.string().max(32).optional().describe("SL trigger price in UI format"),
|
|
49604
|
-
degen_mode:
|
|
49799
|
+
degen_mode: zBool.optional().describe("Enable degen mode (higher leverage limits)")
|
|
49605
49800
|
}
|
|
49606
49801
|
}, async (params) => {
|
|
49607
49802
|
const res = await client.openPosition({
|
|
@@ -49670,12 +49865,12 @@ Transaction (base64, unsigned — sign with wallet):`);
|
|
|
49670
49865
|
}
|
|
49671
49866
|
function registerClosePositionTool(server, client) {
|
|
49672
49867
|
server.registerTool("close_position", {
|
|
49673
|
-
description: "Build a transaction to close (fully or partially) an existing
|
|
49868
|
+
description: "Build a transaction to close (fully or partially) an existing position. Returns preview with PnL, fees, and receive amount, plus unsigned transaction. " + "For full close: set input_usd to position size. For partial: use smaller amount. Requires position_key from get_positions.",
|
|
49674
49869
|
inputSchema: {
|
|
49675
49870
|
position_key: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Position account pubkey to close"),
|
|
49676
49871
|
input_usd: exports_external.string().max(32).describe('USD amount to close, e.g. "500.00" for full or "250.00" for partial'),
|
|
49677
49872
|
withdraw_token_symbol: exports_external.string().max(16).describe('Token to receive: "USDC", "SOL", etc.'),
|
|
49678
|
-
keep_leverage_same:
|
|
49873
|
+
keep_leverage_same: zBool.optional().describe("Keep leverage constant during partial close"),
|
|
49679
49874
|
slippage_percentage: exports_external.string().max(8).optional().describe('Default: "0.5" (0.5%)')
|
|
49680
49875
|
}
|
|
49681
49876
|
}, async (params) => {
|
|
@@ -49804,12 +49999,12 @@ Transaction (base64, unsigned — sign with wallet):`);
|
|
|
49804
49999
|
}
|
|
49805
50000
|
function registerReversePositionTool(server, client) {
|
|
49806
50001
|
server.registerTool("reverse_position", {
|
|
49807
|
-
description: "Build a transaction to reverse a position (close current + open opposite direction). For example, close a LONG and open a SHORT with
|
|
50002
|
+
description: "Build a transaction to reverse a position (close current + open opposite direction). For example, close a LONG and open a SHORT with same collateral. " + "Returns combined preview and a single unsigned transaction. Requires position_key from get_positions.",
|
|
49808
50003
|
inputSchema: {
|
|
49809
50004
|
position_key: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Position account pubkey to reverse"),
|
|
49810
50005
|
owner: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Wallet pubkey"),
|
|
49811
50006
|
slippage_percentage: exports_external.string().max(8).optional().describe('Default: "0.5" (0.5%)'),
|
|
49812
|
-
degen_mode:
|
|
50007
|
+
degen_mode: zBool.optional().describe("Enable degen mode for the new position")
|
|
49813
50008
|
}
|
|
49814
50009
|
}, async (params) => {
|
|
49815
50010
|
const res = await client.reversePosition({
|
|
@@ -56859,17 +57054,9 @@ var $VersionedTransaction = VersionedTransaction;
|
|
|
56859
57054
|
|
|
56860
57055
|
// src/tools/sign-and-send.ts
|
|
56861
57056
|
import fs from "node:fs";
|
|
56862
|
-
|
|
56863
|
-
// src/sanitize.ts
|
|
56864
|
-
function sanitizeError(e) {
|
|
56865
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
56866
|
-
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]");
|
|
56867
|
-
}
|
|
56868
|
-
|
|
56869
|
-
// src/tools/sign-and-send.ts
|
|
56870
57057
|
function registerSignAndSendTool(server) {
|
|
56871
57058
|
server.registerTool("sign_and_send", {
|
|
56872
|
-
description: "Sign and submit a base64
|
|
57059
|
+
description: "Sign and submit a base64 transaction to Solana mainnet using the local keypair. " + "Call ONLY after a transaction tool returns transactionBase64 AND the user has approved the preview. " + "IRREVERSIBLE — always confirm with user first. Call immediately — blockhashes expire in ~60 seconds. " + "Returns the confirmed signature and a Solscan link.",
|
|
56873
57060
|
inputSchema: {
|
|
56874
57061
|
transaction_base64: exports_external.string().max(1e4).describe("The base64-encoded unsigned transaction returned by a transaction tool")
|
|
56875
57062
|
}
|
|
@@ -56934,13 +57121,20 @@ Signature: ${signature2}` }],
|
|
|
56934
57121
|
const lines = [
|
|
56935
57122
|
"=== Transaction Confirmed ===",
|
|
56936
57123
|
`Signature: ${signature2}`,
|
|
56937
|
-
`Explorer: https://solscan.io/tx/${signature2}
|
|
57124
|
+
`Explorer: https://solscan.io/tx/${signature2}`,
|
|
57125
|
+
"",
|
|
57126
|
+
"Next: Call get_positions (with owner) to verify the position, or get_orders to check trigger orders."
|
|
56938
57127
|
];
|
|
56939
57128
|
return { content: [{ type: "text", text: lines.join(`
|
|
56940
57129
|
`) }] };
|
|
56941
57130
|
} catch (e) {
|
|
57131
|
+
const msg = sanitizeError(e);
|
|
57132
|
+
const isBlockhashExpired = msg.includes("Blockhash not found") || msg.includes("block height exceeded");
|
|
57133
|
+
const hint = isBlockhashExpired ? `
|
|
57134
|
+
|
|
57135
|
+
The blockhash has expired (~60 seconds). Re-call the original transaction tool to get a fresh transaction, then call sign_and_send immediately.` : "";
|
|
56942
57136
|
return {
|
|
56943
|
-
content: [{ type: "text", text: `Transaction send failed: ${
|
|
57137
|
+
content: [{ type: "text", text: `Transaction send failed: ${msg}${hint}` }],
|
|
56944
57138
|
isError: true
|
|
56945
57139
|
};
|
|
56946
57140
|
}
|
|
@@ -56950,13 +57144,13 @@ Signature: ${signature2}` }],
|
|
|
56950
57144
|
// src/tools/trigger-orders.ts
|
|
56951
57145
|
function registerTriggerOrderTools(server, client) {
|
|
56952
57146
|
server.registerTool("place_trigger_order", {
|
|
56953
|
-
description: "Place a take-profit (TP) or stop-loss (SL) trigger order on an existing position.
|
|
57147
|
+
description: "Place a take-profit (TP) or stop-loss (SL) trigger order on an existing position. Up to 5 per position. " + "Use preview_tp_sl first to calculate optimal trigger prices. Returns unsigned transaction.",
|
|
56954
57148
|
inputSchema: {
|
|
56955
57149
|
market_symbol: exports_external.string().max(16).describe('Market symbol, e.g. "SOL", "BTC", "ETH"'),
|
|
56956
57150
|
side: exports_external.enum(["LONG", "SHORT"]).describe("Position side"),
|
|
56957
57151
|
trigger_price: exports_external.string().max(32).describe('Trigger price in UI format, e.g. "160.00"'),
|
|
56958
57152
|
size_amount: exports_external.string().max(32).describe('Size in target token to close when triggered, e.g. "0.5"'),
|
|
56959
|
-
is_stop_loss:
|
|
57153
|
+
is_stop_loss: zBool.describe("true = stop-loss, false = take-profit"),
|
|
56960
57154
|
owner: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Wallet pubkey (must own the position)")
|
|
56961
57155
|
}
|
|
56962
57156
|
}, async (params) => {
|
|
@@ -56981,19 +57175,21 @@ WARNING: ${res.err}`);
|
|
|
56981
57175
|
lines.push(`
|
|
56982
57176
|
Transaction (base64, unsigned — sign with wallet):`);
|
|
56983
57177
|
lines.push(res.transactionBase64);
|
|
57178
|
+
lines.push(`
|
|
57179
|
+
Next: After signing, call get_orders with owner to see the order ID for editing/canceling.`);
|
|
56984
57180
|
}
|
|
56985
57181
|
return { content: [{ type: "text", text: lines.join(`
|
|
56986
57182
|
`) }] };
|
|
56987
57183
|
});
|
|
56988
57184
|
server.registerTool("edit_trigger_order", {
|
|
56989
|
-
description: "Edit an existing
|
|
57185
|
+
description: "Edit an existing TP or SL trigger order. Change trigger price, size, or type. Requires order_id (0-7) from get_orders. Returns unsigned transaction.",
|
|
56990
57186
|
inputSchema: {
|
|
56991
57187
|
market_symbol: exports_external.string().max(16).describe('Market symbol, e.g. "SOL", "BTC", "ETH"'),
|
|
56992
57188
|
side: exports_external.enum(["LONG", "SHORT"]).describe("Position side"),
|
|
56993
57189
|
order_id: exports_external.coerce.number().describe("Index of the trigger order to edit (0-7)"),
|
|
56994
57190
|
trigger_price: exports_external.string().max(32).describe("New trigger price in UI format"),
|
|
56995
57191
|
size_amount: exports_external.string().max(32).describe("New size in target token"),
|
|
56996
|
-
is_stop_loss:
|
|
57192
|
+
is_stop_loss: zBool.describe("true = stop-loss, false = take-profit"),
|
|
56997
57193
|
owner: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Wallet pubkey (must be original order owner)")
|
|
56998
57194
|
}
|
|
56999
57195
|
}, async (params) => {
|
|
@@ -57024,12 +57220,12 @@ Transaction (base64, unsigned — sign with wallet):`);
|
|
|
57024
57220
|
`) }] };
|
|
57025
57221
|
});
|
|
57026
57222
|
server.registerTool("cancel_trigger_order", {
|
|
57027
|
-
description: "Cancel a single
|
|
57223
|
+
description: "Cancel a single TP or SL trigger order. Requires order_id (0-7) from get_orders. Returns unsigned transaction.",
|
|
57028
57224
|
inputSchema: {
|
|
57029
57225
|
market_symbol: exports_external.string().max(16).describe('Market symbol, e.g. "SOL", "BTC", "ETH"'),
|
|
57030
57226
|
side: exports_external.enum(["LONG", "SHORT"]).describe("Position side"),
|
|
57031
57227
|
order_id: exports_external.coerce.number().describe("Index of the trigger order to cancel (0-7)"),
|
|
57032
|
-
is_stop_loss:
|
|
57228
|
+
is_stop_loss: zBool.describe("true = stop-loss, false = take-profit"),
|
|
57033
57229
|
owner: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Wallet pubkey (must own the order)")
|
|
57034
57230
|
}
|
|
57035
57231
|
}, async (params) => {
|
|
@@ -57056,7 +57252,7 @@ Transaction (base64, unsigned — sign with wallet):`);
|
|
|
57056
57252
|
`) }] };
|
|
57057
57253
|
});
|
|
57058
57254
|
server.registerTool("cancel_all_trigger_orders", {
|
|
57059
|
-
description: "Cancel ALL
|
|
57255
|
+
description: "Cancel ALL TP and SL trigger orders for a market+side in one transaction. Returns unsigned transaction.",
|
|
57060
57256
|
inputSchema: {
|
|
57061
57257
|
market_symbol: exports_external.string().max(16).describe('Market symbol, e.g. "SOL", "BTC", "ETH"'),
|
|
57062
57258
|
side: exports_external.enum(["LONG", "SHORT"]).describe("Position side"),
|
|
@@ -57095,6 +57291,8 @@ function registerReadTools(server, client) {
|
|
|
57095
57291
|
registerPositionTools(server, client);
|
|
57096
57292
|
registerOrderTools(server, client);
|
|
57097
57293
|
registerPoolDataTools(server, client);
|
|
57294
|
+
registerAccountSummaryTool(server, client);
|
|
57295
|
+
registerTradingOverviewTool(server, client);
|
|
57098
57296
|
}
|
|
57099
57297
|
function registerTransactionTools(server, client) {
|
|
57100
57298
|
registerOpenPositionTool(server, client);
|
|
@@ -57178,7 +57376,7 @@ try {
|
|
|
57178
57376
|
const client = new FlashApiClient(config2);
|
|
57179
57377
|
const server = new McpServer({
|
|
57180
57378
|
name: "flash-trade",
|
|
57181
|
-
version: "0.
|
|
57379
|
+
version: "0.4.0"
|
|
57182
57380
|
}, {
|
|
57183
57381
|
capabilities: {
|
|
57184
57382
|
tools: {},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flash-trade-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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",
|