flash-trade-mcp 0.3.5 → 0.4.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 +263 -29
- 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,30 @@ 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
|
+
const usd = price * Math.pow(10, exp);
|
|
49279
|
+
if (usd === 0)
|
|
49280
|
+
return "0.00";
|
|
49281
|
+
if (usd < 0.01)
|
|
49282
|
+
return usd.toPrecision(4);
|
|
49283
|
+
return usd.toFixed(2);
|
|
49284
|
+
}
|
|
49285
|
+
function formatCompactUsd(valueStr) {
|
|
49286
|
+
if (!valueStr)
|
|
49287
|
+
return "$?";
|
|
49288
|
+
const num = parseFloat(valueStr);
|
|
49289
|
+
if (isNaN(num))
|
|
49290
|
+
return `$${valueStr}`;
|
|
49291
|
+
if (num >= 1e6)
|
|
49292
|
+
return `$${(num / 1e6).toFixed(2)}M`;
|
|
49293
|
+
if (num >= 1000)
|
|
49294
|
+
return `$${(num / 1000).toFixed(1)}K`;
|
|
49295
|
+
return `$${num.toFixed(2)}`;
|
|
49296
|
+
}
|
|
49273
49297
|
function buildCustodySymbolMap(poolData) {
|
|
49274
49298
|
const map3 = new Map;
|
|
49275
49299
|
for (const pool of poolData.pools) {
|
|
@@ -49288,6 +49312,8 @@ function buildCustodySymbolMap(poolData) {
|
|
|
49288
49312
|
}
|
|
49289
49313
|
return map3;
|
|
49290
49314
|
}
|
|
49315
|
+
|
|
49316
|
+
// src/tools/markets.ts
|
|
49291
49317
|
function formatMarketsSummary(markets, custodyInfo) {
|
|
49292
49318
|
const lines = [
|
|
49293
49319
|
`${markets.length} markets available:
|
|
@@ -49317,7 +49343,7 @@ Use get_market with a pubkey for full details. Use get_prices for current oracle
|
|
|
49317
49343
|
}
|
|
49318
49344
|
function registerMarketTools(server, client) {
|
|
49319
49345
|
server.registerTool("get_markets", {
|
|
49320
|
-
description: "List all available perpetual futures markets
|
|
49346
|
+
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
49347
|
}, async () => {
|
|
49322
49348
|
const [markets, poolData] = await Promise.all([
|
|
49323
49349
|
client.getMarkets(),
|
|
@@ -49367,13 +49393,48 @@ Use get_pool with a pubkey for full details. Use get_pool_data for AUM and utili
|
|
|
49367
49393
|
});
|
|
49368
49394
|
}
|
|
49369
49395
|
|
|
49396
|
+
// src/sanitize.ts
|
|
49397
|
+
var zBool = exports_external.preprocess((v) => {
|
|
49398
|
+
if (typeof v === "string")
|
|
49399
|
+
return v === "true";
|
|
49400
|
+
return v;
|
|
49401
|
+
}, exports_external.boolean());
|
|
49402
|
+
function sanitizeError(e) {
|
|
49403
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
49404
|
+
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]");
|
|
49405
|
+
}
|
|
49406
|
+
|
|
49370
49407
|
// src/tools/custodies.ts
|
|
49371
49408
|
function registerCustodyTools(server, client) {
|
|
49372
49409
|
server.registerTool("get_custodies", {
|
|
49373
|
-
description: "List all custody accounts
|
|
49410
|
+
description: "List all custody accounts (token vaults) across pools. Returns a compact summary with symbol, pool, and pubkey. Use get_custody with a specific pubkey for full details (utilization, fees, limits)."
|
|
49374
49411
|
}, async () => {
|
|
49375
|
-
const
|
|
49376
|
-
|
|
49412
|
+
const [custResult, poolResult] = await Promise.allSettled([
|
|
49413
|
+
client.getCustodies(),
|
|
49414
|
+
client.getPoolData()
|
|
49415
|
+
]);
|
|
49416
|
+
const custodies = custResult.status === "fulfilled" ? custResult.value : [];
|
|
49417
|
+
const custodyMap = poolResult.status === "fulfilled" ? buildCustodySymbolMap(poolResult.value) : new Map;
|
|
49418
|
+
if (custodies.length === 0) {
|
|
49419
|
+
const err = custResult.status === "rejected" ? `: ${sanitizeError(custResult.reason)}` : "";
|
|
49420
|
+
return { content: [{ type: "text", text: `No custody accounts found${err}` }] };
|
|
49421
|
+
}
|
|
49422
|
+
const lines = [
|
|
49423
|
+
`${custodies.length} custody accounts:
|
|
49424
|
+
`,
|
|
49425
|
+
"Symbol | Pool | Pubkey",
|
|
49426
|
+
"-----------|----------------|----------------------------------------------"
|
|
49427
|
+
];
|
|
49428
|
+
for (const c of custodies) {
|
|
49429
|
+
const info = custodyMap.get(c.pubkey);
|
|
49430
|
+
const symbol2 = (info?.symbol ?? "?").padEnd(10);
|
|
49431
|
+
const pool = (info?.pool ?? "?").padEnd(14);
|
|
49432
|
+
lines.push(`${symbol2} | ${pool} | ${c.pubkey}`);
|
|
49433
|
+
}
|
|
49434
|
+
lines.push(`
|
|
49435
|
+
Use get_custody with a pubkey for full details (utilization, fees, limits).`);
|
|
49436
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
49437
|
+
`) }] };
|
|
49377
49438
|
});
|
|
49378
49439
|
server.registerTool("get_custody", {
|
|
49379
49440
|
description: "Get detailed custody information for a specific custody account. Includes utilization, fees, and limits.",
|
|
@@ -49434,7 +49495,7 @@ function formatEnrichedPosition(p) {
|
|
|
49434
49495
|
}
|
|
49435
49496
|
function registerPositionTools(server, client) {
|
|
49436
49497
|
server.registerTool("get_positions", {
|
|
49437
|
-
description: "List perpetual positions
|
|
49498
|
+
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
49499
|
inputSchema: {
|
|
49439
49500
|
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
49501
|
}
|
|
@@ -49455,7 +49516,7 @@ ${text}` }] };
|
|
|
49455
49516
|
return { content: [{ type: "text", text: JSON.stringify(positions, null, 2) }] };
|
|
49456
49517
|
});
|
|
49457
49518
|
server.registerTool("get_position", {
|
|
49458
|
-
description: "Get a single position by its on-chain
|
|
49519
|
+
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
49520
|
inputSchema: { pubkey: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Position account pubkey") }
|
|
49460
49521
|
}, async ({ pubkey }) => {
|
|
49461
49522
|
const position = await client.getPosition(pubkey);
|
|
@@ -49480,7 +49541,7 @@ function formatEnrichedOrder(o) {
|
|
|
49480
49541
|
}
|
|
49481
49542
|
function registerOrderTools(server, client) {
|
|
49482
49543
|
server.registerTool("get_orders", {
|
|
49483
|
-
description: "List
|
|
49544
|
+
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
49545
|
inputSchema: {
|
|
49485
49546
|
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
49547
|
}
|
|
@@ -49544,15 +49605,169 @@ Use get_pool_data with pool_pubkey for full custody stats.`);
|
|
|
49544
49605
|
});
|
|
49545
49606
|
}
|
|
49546
49607
|
|
|
49547
|
-
// src/
|
|
49548
|
-
|
|
49549
|
-
|
|
49550
|
-
|
|
49551
|
-
|
|
49552
|
-
|
|
49553
|
-
|
|
49554
|
-
|
|
49555
|
-
|
|
49608
|
+
// src/tools/account-summary.ts
|
|
49609
|
+
function registerAccountSummaryTool(server, client) {
|
|
49610
|
+
server.registerTool("get_account_summary", {
|
|
49611
|
+
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.",
|
|
49612
|
+
inputSchema: {
|
|
49613
|
+
owner: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Wallet pubkey")
|
|
49614
|
+
}
|
|
49615
|
+
}, async ({ owner }) => {
|
|
49616
|
+
const [posResult, ordResult, priceResult] = await Promise.allSettled([
|
|
49617
|
+
client.getOwnerPositions(owner),
|
|
49618
|
+
client.getOwnerOrders(owner),
|
|
49619
|
+
client.getPrices()
|
|
49620
|
+
]);
|
|
49621
|
+
const positions = posResult.status === "fulfilled" ? posResult.value : null;
|
|
49622
|
+
const orders = ordResult.status === "fulfilled" ? ordResult.value : null;
|
|
49623
|
+
const prices = priceResult.status === "fulfilled" ? priceResult.value : null;
|
|
49624
|
+
const lines = [`=== Account Summary for ${owner} ===
|
|
49625
|
+
`];
|
|
49626
|
+
const warnings = [];
|
|
49627
|
+
if (!positions)
|
|
49628
|
+
warnings.push(`Positions unavailable: ${sanitizeError(posResult.reason)}`);
|
|
49629
|
+
if (!orders)
|
|
49630
|
+
warnings.push(`Orders unavailable: ${sanitizeError(ordResult.reason)}`);
|
|
49631
|
+
if (!prices)
|
|
49632
|
+
warnings.push(`Prices unavailable: ${sanitizeError(priceResult.reason)}`);
|
|
49633
|
+
if (!positions) {
|
|
49634
|
+
lines.push(`Positions: unavailable
|
|
49635
|
+
`);
|
|
49636
|
+
} else if (positions.length === 0) {
|
|
49637
|
+
lines.push(`Positions: None
|
|
49638
|
+
`);
|
|
49639
|
+
} else {
|
|
49640
|
+
lines.push(`── ${positions.length} Position(s) ──`);
|
|
49641
|
+
for (const p of positions) {
|
|
49642
|
+
const pnl = p.pnlWithFeeUsdUi ? ` | PnL: $${p.pnlWithFeeUsdUi} (${p.pnlPercentageWithFee}%)` : "";
|
|
49643
|
+
lines.push(` ${p.sideUi} ${p.marketSymbol}: $${p.sizeUsdUi} @ $${p.entryPriceUi} (${p.leverageUi}x)${pnl}`);
|
|
49644
|
+
lines.push(` Key: ${p.key} | Liq: $${p.liquidationPriceUi}`);
|
|
49645
|
+
}
|
|
49646
|
+
lines.push("");
|
|
49647
|
+
}
|
|
49648
|
+
if (!orders) {
|
|
49649
|
+
lines.push(`Orders: unavailable
|
|
49650
|
+
`);
|
|
49651
|
+
} else if (orders.length === 0) {
|
|
49652
|
+
lines.push(`Orders: None
|
|
49653
|
+
`);
|
|
49654
|
+
} else {
|
|
49655
|
+
lines.push(`── Orders ──`);
|
|
49656
|
+
for (const o of orders) {
|
|
49657
|
+
for (const lo of o.limitOrders ?? []) {
|
|
49658
|
+
lines.push(` LIMIT ${lo.sideUi} ${lo.symbol}: $${lo.sizeUsdUi} @ $${lo.entryPriceUi} (${lo.leverageUi}x)`);
|
|
49659
|
+
}
|
|
49660
|
+
for (const tp of o.takeProfitOrders ?? []) {
|
|
49661
|
+
lines.push(` TP ${tp.sideUi} ${tp.symbol}: $${tp.sizeUsdUi} @ $${tp.triggerPriceUi}`);
|
|
49662
|
+
}
|
|
49663
|
+
for (const sl of o.stopLossOrders ?? []) {
|
|
49664
|
+
lines.push(` SL ${sl.sideUi} ${sl.symbol}: $${sl.sizeUsdUi} @ $${sl.triggerPriceUi}`);
|
|
49665
|
+
}
|
|
49666
|
+
}
|
|
49667
|
+
lines.push("");
|
|
49668
|
+
}
|
|
49669
|
+
if (positions && prices) {
|
|
49670
|
+
const marketSymbols = [...new Set(positions.map((p) => p.marketSymbol).filter((s) => !!s))];
|
|
49671
|
+
if (marketSymbols.length > 0) {
|
|
49672
|
+
lines.push("── Current Prices ──");
|
|
49673
|
+
for (const sym of marketSymbols) {
|
|
49674
|
+
const priceData = prices[sym];
|
|
49675
|
+
if (priceData) {
|
|
49676
|
+
const usd = formatPriceUsd(priceData);
|
|
49677
|
+
lines.push(` ${sym}: ${usd === "?" ? "price unavailable" : `$${usd}`}`);
|
|
49678
|
+
}
|
|
49679
|
+
}
|
|
49680
|
+
lines.push("");
|
|
49681
|
+
}
|
|
49682
|
+
}
|
|
49683
|
+
if (warnings.length > 0) {
|
|
49684
|
+
lines.push("── Warnings ──");
|
|
49685
|
+
for (const w of warnings)
|
|
49686
|
+
lines.push(` ${w}`);
|
|
49687
|
+
}
|
|
49688
|
+
lines.push("Note: Data is cached ~15 seconds. Recently closed positions may still appear briefly.");
|
|
49689
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
49690
|
+
`) }] };
|
|
49691
|
+
});
|
|
49692
|
+
}
|
|
49693
|
+
|
|
49694
|
+
// src/tools/trading-overview.ts
|
|
49695
|
+
function registerTradingOverviewTool(server, client) {
|
|
49696
|
+
server.registerTool("get_trading_overview", {
|
|
49697
|
+
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."
|
|
49698
|
+
}, async () => {
|
|
49699
|
+
const [marketsResult, pricesResult, poolResult] = await Promise.allSettled([
|
|
49700
|
+
client.getMarkets(),
|
|
49701
|
+
client.getPrices(),
|
|
49702
|
+
client.getPoolData()
|
|
49703
|
+
]);
|
|
49704
|
+
const markets = marketsResult.status === "fulfilled" ? marketsResult.value : null;
|
|
49705
|
+
const prices = pricesResult.status === "fulfilled" ? pricesResult.value : null;
|
|
49706
|
+
const poolData = poolResult.status === "fulfilled" ? poolResult.value : null;
|
|
49707
|
+
const lines = [`=== Trading Overview ===
|
|
49708
|
+
`];
|
|
49709
|
+
const warnings = [];
|
|
49710
|
+
if (!markets)
|
|
49711
|
+
warnings.push(`Markets unavailable: ${sanitizeError(marketsResult.reason)}`);
|
|
49712
|
+
if (!prices)
|
|
49713
|
+
warnings.push(`Prices unavailable: ${sanitizeError(pricesResult.reason)}`);
|
|
49714
|
+
if (!poolData)
|
|
49715
|
+
warnings.push(`Pool data unavailable: ${sanitizeError(poolResult.reason)}`);
|
|
49716
|
+
if (markets) {
|
|
49717
|
+
const custodyInfo = poolData ? buildCustodySymbolMap(poolData) : new Map;
|
|
49718
|
+
lines.push("── Markets ──");
|
|
49719
|
+
lines.push("Symbol | Price | Side | Max Lev | Pool");
|
|
49720
|
+
lines.push("-----------|---------------|-------|---------|---------------");
|
|
49721
|
+
const enriched = markets.map((m) => {
|
|
49722
|
+
const info = custodyInfo.get(m.account.target_custody);
|
|
49723
|
+
return {
|
|
49724
|
+
symbol: info?.symbol ?? "UNKNOWN",
|
|
49725
|
+
pool: info?.pool ?? "?",
|
|
49726
|
+
maxLeverage: info?.maxLeverage ?? "?",
|
|
49727
|
+
side: m.account.side,
|
|
49728
|
+
open: m.account.permissions.allow_open_position
|
|
49729
|
+
};
|
|
49730
|
+
}).sort((a, b) => a.pool.localeCompare(b.pool) || a.symbol.localeCompare(b.symbol) || a.side.localeCompare(b.side));
|
|
49731
|
+
const seen = new Set;
|
|
49732
|
+
for (const m of enriched) {
|
|
49733
|
+
const key = `${m.symbol}-${m.side}`;
|
|
49734
|
+
if (seen.has(key))
|
|
49735
|
+
continue;
|
|
49736
|
+
seen.add(key);
|
|
49737
|
+
let priceStr = "?";
|
|
49738
|
+
if (prices) {
|
|
49739
|
+
const priceData = prices[m.symbol];
|
|
49740
|
+
if (priceData)
|
|
49741
|
+
priceStr = `$${formatPriceUsd(priceData)}`;
|
|
49742
|
+
}
|
|
49743
|
+
const status = m.open ? "" : " [CLOSED]";
|
|
49744
|
+
lines.push(`${m.symbol.padEnd(10)} | ${priceStr.padEnd(13)} | ${m.side.padEnd(5)} | ${m.maxLeverage.padEnd(7)} | ${m.pool}${status}`);
|
|
49745
|
+
}
|
|
49746
|
+
}
|
|
49747
|
+
if (poolData) {
|
|
49748
|
+
lines.push(`
|
|
49749
|
+
── Pool Utilization ──`);
|
|
49750
|
+
lines.push("Pool | AUM | LP Price | Stable%");
|
|
49751
|
+
lines.push("---------------|----------------|-----------|--------");
|
|
49752
|
+
for (const p of poolData.pools) {
|
|
49753
|
+
const name = (p.poolName ?? "Unknown").padEnd(14);
|
|
49754
|
+
const aum = formatCompactUsd(p.lpStats?.totalPoolValueUsd).padEnd(14);
|
|
49755
|
+
const lp = `$${p.lpStats?.lpPrice ?? "?"}`.padEnd(9);
|
|
49756
|
+
const stable = `${p.lpStats?.stableCoinPercentage ?? "?"}%`;
|
|
49757
|
+
lines.push(`${name} | ${aum} | ${lp} | ${stable}`);
|
|
49758
|
+
}
|
|
49759
|
+
}
|
|
49760
|
+
if (warnings.length > 0) {
|
|
49761
|
+
lines.push(`
|
|
49762
|
+
── Warnings ──`);
|
|
49763
|
+
for (const w of warnings)
|
|
49764
|
+
lines.push(` ${w}`);
|
|
49765
|
+
}
|
|
49766
|
+
lines.push(`
|
|
49767
|
+
Use open_position to trade. Use get_account_summary to check existing positions.`);
|
|
49768
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
49769
|
+
`) }] };
|
|
49770
|
+
});
|
|
49556
49771
|
}
|
|
49557
49772
|
|
|
49558
49773
|
// src/tools/open-position.ts
|
|
@@ -49599,7 +49814,7 @@ No transaction built (provide owner wallet pubkey to build transaction)`);
|
|
|
49599
49814
|
}
|
|
49600
49815
|
function registerOpenPositionTool(server, client) {
|
|
49601
49816
|
server.registerTool("open_position", {
|
|
49602
|
-
description: "Build a transaction to open a new perpetual position
|
|
49817
|
+
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.",
|
|
49603
49818
|
inputSchema: {
|
|
49604
49819
|
input_token_symbol: exports_external.string().max(16).describe('Token to pay with: "USDC", "SOL", etc.'),
|
|
49605
49820
|
output_token_symbol: exports_external.string().max(16).describe('Market to trade: "SOL", "BTC", "ETH", etc.'),
|
|
@@ -49681,7 +49896,7 @@ Transaction (base64, unsigned — sign with wallet):`);
|
|
|
49681
49896
|
}
|
|
49682
49897
|
function registerClosePositionTool(server, client) {
|
|
49683
49898
|
server.registerTool("close_position", {
|
|
49684
|
-
description: "Build a transaction to close (fully or partially) an existing
|
|
49899
|
+
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.",
|
|
49685
49900
|
inputSchema: {
|
|
49686
49901
|
position_key: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Position account pubkey to close"),
|
|
49687
49902
|
input_usd: exports_external.string().max(32).describe('USD amount to close, e.g. "500.00" for full or "250.00" for partial'),
|
|
@@ -49815,7 +50030,7 @@ Transaction (base64, unsigned — sign with wallet):`);
|
|
|
49815
50030
|
}
|
|
49816
50031
|
function registerReversePositionTool(server, client) {
|
|
49817
50032
|
server.registerTool("reverse_position", {
|
|
49818
|
-
description: "Build a transaction to reverse a position (close current + open opposite direction). For example, close a LONG and open a SHORT with
|
|
50033
|
+
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.",
|
|
49819
50034
|
inputSchema: {
|
|
49820
50035
|
position_key: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Position account pubkey to reverse"),
|
|
49821
50036
|
owner: exports_external.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/).describe("Wallet pubkey"),
|
|
@@ -56872,7 +57087,7 @@ var $VersionedTransaction = VersionedTransaction;
|
|
|
56872
57087
|
import fs from "node:fs";
|
|
56873
57088
|
function registerSignAndSendTool(server) {
|
|
56874
57089
|
server.registerTool("sign_and_send", {
|
|
56875
|
-
description: "Sign and submit a base64
|
|
57090
|
+
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.",
|
|
56876
57091
|
inputSchema: {
|
|
56877
57092
|
transaction_base64: exports_external.string().max(1e4).describe("The base64-encoded unsigned transaction returned by a transaction tool")
|
|
56878
57093
|
}
|
|
@@ -56937,13 +57152,28 @@ Signature: ${signature2}` }],
|
|
|
56937
57152
|
const lines = [
|
|
56938
57153
|
"=== Transaction Confirmed ===",
|
|
56939
57154
|
`Signature: ${signature2}`,
|
|
56940
|
-
`Explorer: https://solscan.io/tx/${signature2}
|
|
57155
|
+
`Explorer: https://solscan.io/tx/${signature2}`,
|
|
57156
|
+
"",
|
|
57157
|
+
"Next: Call get_positions (with owner) to verify the position, or get_orders to check trigger orders."
|
|
56941
57158
|
];
|
|
56942
57159
|
return { content: [{ type: "text", text: lines.join(`
|
|
56943
57160
|
`) }] };
|
|
56944
57161
|
} catch (e) {
|
|
57162
|
+
const msg = sanitizeError(e);
|
|
57163
|
+
const isBlockhashExpired = msg.includes("Blockhash not found") || msg.includes("block height exceeded");
|
|
57164
|
+
const isSignerMismatch = msg.includes("Cannot sign with non signer key");
|
|
57165
|
+
let hint = "";
|
|
57166
|
+
if (isBlockhashExpired) {
|
|
57167
|
+
hint = `
|
|
57168
|
+
|
|
57169
|
+
The blockhash has expired (~60 seconds). Re-call the original transaction tool to get a fresh transaction, then call sign_and_send immediately.`;
|
|
57170
|
+
} else if (isSignerMismatch) {
|
|
57171
|
+
hint = `
|
|
57172
|
+
|
|
57173
|
+
The transaction was built for a different wallet than the local keypair. Re-call the transaction tool to get a fresh transaction, then sign immediately — blockhashes expire in ~60 seconds.`;
|
|
57174
|
+
}
|
|
56945
57175
|
return {
|
|
56946
|
-
content: [{ type: "text", text: `Transaction send failed: ${
|
|
57176
|
+
content: [{ type: "text", text: `Transaction send failed: ${msg}${hint}` }],
|
|
56947
57177
|
isError: true
|
|
56948
57178
|
};
|
|
56949
57179
|
}
|
|
@@ -56953,7 +57183,7 @@ Signature: ${signature2}` }],
|
|
|
56953
57183
|
// src/tools/trigger-orders.ts
|
|
56954
57184
|
function registerTriggerOrderTools(server, client) {
|
|
56955
57185
|
server.registerTool("place_trigger_order", {
|
|
56956
|
-
description: "Place a take-profit (TP) or stop-loss (SL) trigger order on an existing position.
|
|
57186
|
+
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.",
|
|
56957
57187
|
inputSchema: {
|
|
56958
57188
|
market_symbol: exports_external.string().max(16).describe('Market symbol, e.g. "SOL", "BTC", "ETH"'),
|
|
56959
57189
|
side: exports_external.enum(["LONG", "SHORT"]).describe("Position side"),
|
|
@@ -56984,12 +57214,14 @@ WARNING: ${res.err}`);
|
|
|
56984
57214
|
lines.push(`
|
|
56985
57215
|
Transaction (base64, unsigned — sign with wallet):`);
|
|
56986
57216
|
lines.push(res.transactionBase64);
|
|
57217
|
+
lines.push(`
|
|
57218
|
+
Next: After signing, call get_orders with owner to see the order ID for editing/canceling.`);
|
|
56987
57219
|
}
|
|
56988
57220
|
return { content: [{ type: "text", text: lines.join(`
|
|
56989
57221
|
`) }] };
|
|
56990
57222
|
});
|
|
56991
57223
|
server.registerTool("edit_trigger_order", {
|
|
56992
|
-
description: "Edit an existing
|
|
57224
|
+
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.",
|
|
56993
57225
|
inputSchema: {
|
|
56994
57226
|
market_symbol: exports_external.string().max(16).describe('Market symbol, e.g. "SOL", "BTC", "ETH"'),
|
|
56995
57227
|
side: exports_external.enum(["LONG", "SHORT"]).describe("Position side"),
|
|
@@ -57027,7 +57259,7 @@ Transaction (base64, unsigned — sign with wallet):`);
|
|
|
57027
57259
|
`) }] };
|
|
57028
57260
|
});
|
|
57029
57261
|
server.registerTool("cancel_trigger_order", {
|
|
57030
|
-
description: "Cancel a single
|
|
57262
|
+
description: "Cancel a single TP or SL trigger order. Requires order_id (0-7) from get_orders. Returns unsigned transaction.",
|
|
57031
57263
|
inputSchema: {
|
|
57032
57264
|
market_symbol: exports_external.string().max(16).describe('Market symbol, e.g. "SOL", "BTC", "ETH"'),
|
|
57033
57265
|
side: exports_external.enum(["LONG", "SHORT"]).describe("Position side"),
|
|
@@ -57059,7 +57291,7 @@ Transaction (base64, unsigned — sign with wallet):`);
|
|
|
57059
57291
|
`) }] };
|
|
57060
57292
|
});
|
|
57061
57293
|
server.registerTool("cancel_all_trigger_orders", {
|
|
57062
|
-
description: "Cancel ALL
|
|
57294
|
+
description: "Cancel ALL TP and SL trigger orders for a market+side in one transaction. Returns unsigned transaction.",
|
|
57063
57295
|
inputSchema: {
|
|
57064
57296
|
market_symbol: exports_external.string().max(16).describe('Market symbol, e.g. "SOL", "BTC", "ETH"'),
|
|
57065
57297
|
side: exports_external.enum(["LONG", "SHORT"]).describe("Position side"),
|
|
@@ -57098,6 +57330,8 @@ function registerReadTools(server, client) {
|
|
|
57098
57330
|
registerPositionTools(server, client);
|
|
57099
57331
|
registerOrderTools(server, client);
|
|
57100
57332
|
registerPoolDataTools(server, client);
|
|
57333
|
+
registerAccountSummaryTool(server, client);
|
|
57334
|
+
registerTradingOverviewTool(server, client);
|
|
57101
57335
|
}
|
|
57102
57336
|
function registerTransactionTools(server, client) {
|
|
57103
57337
|
registerOpenPositionTool(server, client);
|
|
@@ -57181,7 +57415,7 @@ try {
|
|
|
57181
57415
|
const client = new FlashApiClient(config2);
|
|
57182
57416
|
const server = new McpServer({
|
|
57183
57417
|
name: "flash-trade",
|
|
57184
|
-
version: "0.
|
|
57418
|
+
version: "0.4.1"
|
|
57185
57419
|
}, {
|
|
57186
57420
|
capabilities: {
|
|
57187
57421
|
tools: {},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flash-trade-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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",
|