flash-trade-mcp 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +100 -16
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -49229,15 +49229,84 @@ function registerHealthTools(server, client) {
49229
49229
  }
49230
49230
 
49231
49231
  // src/tools/markets.ts
49232
+ var VIRTUAL_CUSTODY_MAP = {
49233
+ "6bthDsp8pcGBGKVKCKZjV5JfuSUNRo62RG4hQHj1u4CK": { symbol: "BNB", pool: "Crypto.1", maxLeverage: "60.00" },
49234
+ A8SKWb3pwbFUtxLQhnpUTfy7CkxBpWGvTLYyJyWHCMWv: { symbol: "PYTH", pool: "Governance.1", maxLeverage: "60.00" },
49235
+ "5JtPiHFmkb1nv1Qvs3sryLgXmjs8p5iQexAseC2Ljjzg": { symbol: "KMNO", pool: "Governance.1", maxLeverage: "60.00" },
49236
+ "9GeU2eX2B8nLCJr7FKhXeR73fM2ULBwUXZqHov9iipxz": { symbol: "MET", pool: "Governance.1", maxLeverage: "15.00" },
49237
+ "7WWSRZSgFmp7UDfD1KHWYJ2CqXVbpCdQ5cQfgBxjpFeL": { symbol: "EUR", pool: "Virtual.1", maxLeverage: "550.00" },
49238
+ Ah1Kd146CtAexGvbVNWRMQ8aXJTJDf4AopNLQZKGfYck: { symbol: "GBP", pool: "Virtual.1", maxLeverage: "550.00" },
49239
+ ERiMNq88WEByvDUKsPsvkJRnvsDrPPhbWedn59cDfvXY: { symbol: "USDJPY", pool: "Virtual.1", maxLeverage: "550.00" },
49240
+ "2zB3Uv3SoFGe17UiGjPGrwBRA7edH3YRwtHWQES7KkqP": { symbol: "USDCNH", pool: "Virtual.1", maxLeverage: "550.00" },
49241
+ "3j1xiP6GckKCzsTm6sni5iy6zrpZX5BWZGbKCq5buk4d": { symbol: "XAU", pool: "Virtual.1", maxLeverage: "120.00" },
49242
+ GMqeFJ8LG5BcrRtVgvfQuA7giBETcY76ikC8h5hPh59h: { symbol: "XAG", pool: "Virtual.1", maxLeverage: "130.00" },
49243
+ "5mggznCHoC98t2xXNYPVR8cqNhRhYdhV7qGWqMoY6YSJ": { symbol: "CRUDEOIL", pool: "Virtual.1", maxLeverage: "7.00" },
49244
+ A2C8A9QMEQ1XAWjLSe7zUNRXSjDqQA4cpYzLYGvDZS1u: { symbol: "AMZN", pool: "Equity.1", maxLeverage: "12.00" },
49245
+ CK6ByFWy3fMbymx55SGhWi4yEv4HebdtdbLMwfPTZDwK: { symbol: "AAPL", pool: "Equity.1", maxLeverage: "12.00" },
49246
+ GDudQbq15yQuhvZ2N63qiYdQBiMipooeArcUgcwizd5b: { symbol: "AMD", pool: "Equity.1", maxLeverage: "12.00" },
49247
+ HbwAAHzRwNqrZMD9WzMJBYGnKqUrDLcodD9rvaEkPYXK: { symbol: "NVDA", pool: "Equity.1", maxLeverage: "12.00" },
49248
+ RQNURQjDbq2Yah2udtFTNT7TjR15vsPV3oJNnwYher8: { symbol: "TSLA", pool: "Equity.1", maxLeverage: "12.00" },
49249
+ ArnD1faZVVkkewX4HUSoDuht46egAtVvhDTFMJn3DkFo: { symbol: "SAMO", pool: "Community.3", maxLeverage: "50.00" }
49250
+ };
49251
+ function buildCustodySymbolMap(poolData) {
49252
+ const map3 = new Map;
49253
+ for (const pool of poolData.pools) {
49254
+ for (const c of pool.custodyStats) {
49255
+ map3.set(c.custodyAccount, {
49256
+ symbol: c.symbol,
49257
+ maxLeverage: c.maxLeverage,
49258
+ pool: pool.poolName
49259
+ });
49260
+ }
49261
+ }
49262
+ for (const [pubkey, info] of Object.entries(VIRTUAL_CUSTODY_MAP)) {
49263
+ if (!map3.has(pubkey)) {
49264
+ map3.set(pubkey, info);
49265
+ }
49266
+ }
49267
+ return map3;
49268
+ }
49269
+ function formatMarketsSummary(markets, custodyInfo) {
49270
+ const lines = [
49271
+ `${markets.length} markets available:
49272
+ `,
49273
+ "Symbol | Side | Pool | Max Lev | Pubkey",
49274
+ "-----------|-------|----------------|---------|----------------------------------------------"
49275
+ ];
49276
+ const enriched = markets.map((m) => {
49277
+ const info = custodyInfo.get(m.account.target_custody);
49278
+ return {
49279
+ symbol: info?.symbol ?? "UNKNOWN",
49280
+ pool: info?.pool ?? "?",
49281
+ maxLeverage: info?.maxLeverage ?? "?",
49282
+ side: m.account.side,
49283
+ pubkey: m.pubkey,
49284
+ open: m.account.permissions.allow_open_position
49285
+ };
49286
+ }).sort((a, b) => a.pool.localeCompare(b.pool) || a.symbol.localeCompare(b.symbol) || a.side.localeCompare(b.side));
49287
+ for (const m of enriched) {
49288
+ const status = m.open ? "" : " [CLOSED]";
49289
+ lines.push(`${m.symbol.padEnd(10)} | ${m.side.padEnd(5)} | ${m.pool.padEnd(14)} | ${m.maxLeverage.padEnd(7)} | ${m.pubkey}${status}`);
49290
+ }
49291
+ lines.push(`
49292
+ Use get_market with a pubkey for full details. Use get_prices for current oracle prices.`);
49293
+ return lines.join(`
49294
+ `);
49295
+ }
49232
49296
  function registerMarketTools(server, client) {
49233
49297
  server.registerTool("get_markets", {
49234
- description: "List all available perpetual futures markets on Flash Trade. Returns market symbols (SOL, BTC, ETH, etc.), their account pubkeys, and configuration. Use this to discover which markets can be traded."
49298
+ description: "List all available perpetual futures markets on Flash Trade. Returns a compact summary table with symbol, side (Long/Short), pool, max leverage, and market pubkey. For full details on a specific market, use get_market with the pubkey."
49235
49299
  }, async () => {
49236
- const markets = await client.getMarkets();
49237
- return { content: [{ type: "text", text: JSON.stringify(markets, null, 2) }] };
49300
+ const [markets, poolData] = await Promise.all([
49301
+ client.getMarkets(),
49302
+ client.getPoolData()
49303
+ ]);
49304
+ const custodyInfo = buildCustodySymbolMap(poolData);
49305
+ const text = formatMarketsSummary(markets, custodyInfo);
49306
+ return { content: [{ type: "text", text }] };
49238
49307
  });
49239
49308
  server.registerTool("get_market", {
49240
- description: "Get detailed information about a specific market by its on-chain account pubkey. Returns full market configuration including max leverage, fees, and status.",
49309
+ description: "Get detailed information about a specific market by its on-chain account pubkey. Returns full market configuration including permissions and collective position data.",
49241
49310
  inputSchema: { pubkey: exports_external.string().describe("Solana pubkey of the market account") }
49242
49311
  }, async ({ pubkey }) => {
49243
49312
  const market = await client.getMarket(pubkey);
@@ -49470,7 +49539,7 @@ function registerOpenPositionTool(server, client) {
49470
49539
  input_token_symbol: exports_external.string().describe('Token to pay with: "USDC", "SOL", etc.'),
49471
49540
  output_token_symbol: exports_external.string().describe('Market to trade: "SOL", "BTC", "ETH", etc.'),
49472
49541
  input_amount: exports_external.string().describe('Amount of input token, e.g. "100.0"'),
49473
- leverage: exports_external.number().describe("Leverage multiplier, e.g. 5.0"),
49542
+ leverage: exports_external.string().describe('Leverage multiplier, e.g. "5.0"'),
49474
49543
  trade_type: exports_external.enum(["LONG", "SHORT"]).describe("Trade direction"),
49475
49544
  owner: exports_external.string().describe("Wallet pubkey (required to build the transaction)"),
49476
49545
  order_type: exports_external.enum(["MARKET", "LIMIT"]).optional().describe("Default: MARKET"),
@@ -49485,7 +49554,7 @@ function registerOpenPositionTool(server, client) {
49485
49554
  inputTokenSymbol: params.input_token_symbol,
49486
49555
  outputTokenSymbol: params.output_token_symbol,
49487
49556
  inputAmountUi: params.input_amount,
49488
- leverage: params.leverage,
49557
+ leverage: parseFloat(params.leverage),
49489
49558
  tradeType: params.trade_type,
49490
49559
  owner: params.owner,
49491
49560
  orderType: params.order_type,
@@ -56727,9 +56796,13 @@ var $VersionedTransaction = VersionedTransaction;
56727
56796
 
56728
56797
  // src/tools/sign-and-send.ts
56729
56798
  import fs from "node:fs";
56799
+ function sanitizeError(e) {
56800
+ 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]");
56802
+ }
56730
56803
  function registerSignAndSendTool(server) {
56731
56804
  server.registerTool("sign_and_send", {
56732
- 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.",
56805
+ 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.",
56733
56806
  inputSchema: {
56734
56807
  transaction_base64: exports_external.string().describe("The base64-encoded unsigned transaction returned by a transaction tool")
56735
56808
  }
@@ -56738,12 +56811,26 @@ function registerSignAndSendTool(server) {
56738
56811
  const keypairPath = process.env.KEYPAIR_PATH ?? `${process.env.HOME}/.config/solana/id.json`;
56739
56812
  let keypair;
56740
56813
  try {
56741
- const keypairData = JSON.parse(fs.readFileSync(keypairPath, "utf-8"));
56814
+ const raw = fs.readFileSync(keypairPath, "utf-8");
56815
+ let keypairData;
56816
+ try {
56817
+ keypairData = JSON.parse(raw);
56818
+ } catch {
56819
+ return {
56820
+ content: [{ type: "text", text: `Keypair file at ${keypairPath} is not valid JSON.` }],
56821
+ isError: true
56822
+ };
56823
+ }
56824
+ if (!Array.isArray(keypairData) || keypairData.length !== 64) {
56825
+ return {
56826
+ content: [{ type: "text", text: `Keypair file at ${keypairPath} does not contain a valid 64-byte Solana keypair.` }],
56827
+ isError: true
56828
+ };
56829
+ }
56742
56830
  keypair = $Keypair.fromSecretKey(Uint8Array.from(keypairData));
56743
56831
  } catch (e) {
56744
- const msg = e instanceof Error ? e.message : String(e);
56745
56832
  return {
56746
- content: [{ type: "text", text: `Failed to load keypair from ${keypairPath}: ${msg}` }],
56833
+ content: [{ type: "text", text: `Failed to load keypair from ${keypairPath}: ${sanitizeError(e)}` }],
56747
56834
  isError: true
56748
56835
  };
56749
56836
  }
@@ -56752,9 +56839,8 @@ function registerSignAndSendTool(server) {
56752
56839
  const txBytes = Buffer.from(params.transaction_base64, "base64");
56753
56840
  tx = $VersionedTransaction.deserialize(txBytes);
56754
56841
  } catch (e) {
56755
- const msg = e instanceof Error ? e.message : String(e);
56756
56842
  return {
56757
- content: [{ type: "text", text: `Failed to decode transaction: ${msg}` }],
56843
+ content: [{ type: "text", text: `Failed to decode transaction: ${sanitizeError(e)}` }],
56758
56844
  isError: true
56759
56845
  };
56760
56846
  }
@@ -56781,15 +56867,13 @@ Signature: ${signature2}` }],
56781
56867
  const lines = [
56782
56868
  "=== Transaction Confirmed ===",
56783
56869
  `Signature: ${signature2}`,
56784
- `Wallet: ${keypair.publicKey.toBase58()}`,
56785
56870
  `Explorer: https://solscan.io/tx/${signature2}`
56786
56871
  ];
56787
56872
  return { content: [{ type: "text", text: lines.join(`
56788
56873
  `) }] };
56789
56874
  } catch (e) {
56790
- const msg = e instanceof Error ? e.message : String(e);
56791
56875
  return {
56792
- content: [{ type: "text", text: `Transaction send failed: ${msg}` }],
56876
+ content: [{ type: "text", text: `Transaction send failed: ${sanitizeError(e)}` }],
56793
56877
  isError: true
56794
56878
  };
56795
56879
  }
@@ -56888,7 +56972,7 @@ try {
56888
56972
  const client = new FlashApiClient(config2);
56889
56973
  const server = new McpServer({
56890
56974
  name: "flash-trade",
56891
- version: "0.2.0"
56975
+ version: "0.2.2"
56892
56976
  }, {
56893
56977
  capabilities: {
56894
56978
  tools: {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flash-trade-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
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",