Skip to main content
Glama

Trading Simulator MCP Server

by recallnet
#!/usr/bin/env node import { logger } from './env.js'; import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, Tool } from "@modelcontextprotocol/sdk/types.js"; import { tradingClient } from "./api-client.js"; import { BlockchainType, SpecificChain, PriceHistoryParams, TradeHistoryParams, TradeParams, TeamMetadata, } from "./types.js"; // Create an MCP server instance using the Server class const server = new Server( { name: "trading-simulator-mcp", version: "0.1.0" }, { capabilities: { tools: {}, // We support tools resources: {}, // We support resources (even if we just return empty arrays) prompts: {} // We support prompts (even if we just return empty arrays) } } ); // Define the MCP tools const TRADING_SIM_TOOLS: Tool[] = [ // Account Tools { name: "get_profile", description: "Get your team's profile information", inputSchema: { type: "object", properties: {}, additionalProperties: false, $schema: "http://json-schema.org/draft-07/schema#" } }, { name: "update_profile", description: "Update your team's profile information", inputSchema: { type: "object", properties: { contactPerson: { type: "string", description: "New contact person name" }, metadata: { type: "object", description: "Agent metadata with ref, description, and social information", properties: { ref: { type: "object", properties: { name: { type: "string", description: "Agent name" }, version: { type: "string", description: "Agent version" }, url: { type: "string", description: "Link to agent documentation or repository" } } }, description: { type: "string", description: "Brief description of the agent" }, social: { type: "object", properties: { name: { type: "string", description: "Agent social name" }, email: { type: "string", description: "Contact email for the agent" }, twitter: { type: "string", description: "Twitter handle" } } } } } }, additionalProperties: false, $schema: "http://json-schema.org/draft-07/schema#" } }, { name: "get_balances", description: "Get token balances for your team", inputSchema: { type: "object", properties: {}, additionalProperties: false, $schema: "http://json-schema.org/draft-07/schema#" } }, { name: "get_portfolio", description: "Get portfolio information for your team", inputSchema: { type: "object", properties: {}, additionalProperties: false, $schema: "http://json-schema.org/draft-07/schema#" } }, { name: "get_trades", description: "Get trade history for your team", inputSchema: { type: "object", properties: { limit: { type: "number", description: "Maximum number of trades to retrieve (default: 20)" }, offset: { type: "number", description: "Offset for pagination" }, token: { type: "string", description: "Filter by token address" }, chain: { type: "string", enum: ["svm", "evm"], description: "Filter by blockchain type" } }, additionalProperties: false, $schema: "http://json-schema.org/draft-07/schema#" } }, // Price Tools { name: "get_price", description: "Get the current price for a token", inputSchema: { type: "object", properties: { token: { type: "string", description: "Token address" }, chain: { type: "string", enum: ["svm", "evm"], description: "Optional blockchain type" }, specificChain: { type: "string", enum: ["eth", "polygon", "bsc", "arbitrum", "base", "optimism", "avalanche", "linea", "svm"], description: "Optional specific chain for EVM tokens" } }, required: ["token"], additionalProperties: false, $schema: "http://json-schema.org/draft-07/schema#" } }, { name: "get_token_info", description: "Get detailed information about a token", inputSchema: { type: "object", properties: { token: { type: "string", description: "Token address" }, chain: { type: "string", enum: ["svm", "evm"], description: "Optional blockchain type" }, specificChain: { type: "string", enum: ["eth", "polygon", "bsc", "arbitrum", "base", "optimism", "avalanche", "linea", "svm"], description: "Optional specific chain for EVM tokens" } }, required: ["token"], additionalProperties: false, $schema: "http://json-schema.org/draft-07/schema#" } }, { name: "get_price_history", description: "Get historical price data for a token", inputSchema: { type: "object", properties: { token: { type: "string", description: "Token address" }, startTime: { type: "string", description: "Start time as ISO timestamp" }, endTime: { type: "string", description: "End time as ISO timestamp" }, interval: { type: "string", enum: ["1m", "5m", "15m", "1h", "4h", "1d"], description: "Time interval for price points" }, chain: { type: "string", enum: ["svm", "evm"], description: "Optional blockchain type" }, specificChain: { type: "string", enum: ["eth", "polygon", "bsc", "arbitrum", "base", "optimism", "avalanche", "linea", "svm"], description: "Optional specific chain for EVM tokens" } }, required: ["token"], additionalProperties: false, $schema: "http://json-schema.org/draft-07/schema#" } }, // Trading Tools { name: "execute_trade", description: "Execute a trade between tokens", inputSchema: { type: "object", properties: { fromToken: { type: "string", description: "Source token address" }, toToken: { type: "string", description: "Destination token address" }, amount: { type: "string", description: "Amount of fromToken to trade" }, reason: { type: "string", description: "Reason for executing this trade" }, slippageTolerance: { type: "string", description: "Optional slippage tolerance percentage (e.g., '0.5' for 0.5%)" }, fromChain: { type: "string", enum: ["svm", "evm"], description: "Optional blockchain type for source token" }, toChain: { type: "string", enum: ["svm", "evm"], description: "Optional blockchain type for destination token" }, fromSpecificChain: { type: "string", enum: ["eth", "polygon", "bsc", "arbitrum", "base", "optimism", "avalanche", "linea", "svm"], description: "Optional specific chain for source token" }, toSpecificChain: { type: "string", enum: ["eth", "polygon", "bsc", "arbitrum", "base", "optimism", "avalanche", "linea", "svm"], description: "Optional specific chain for destination token" } }, required: ["fromToken", "toToken", "amount", "reason"], additionalProperties: false, $schema: "http://json-schema.org/draft-07/schema#" } }, { name: "get_quote", description: "Get a quote for a potential trade", inputSchema: { type: "object", properties: { fromToken: { type: "string", description: "Source token address" }, toToken: { type: "string", description: "Destination token address" }, amount: { type: "string", description: "Amount of fromToken to potentially trade" }, fromChain: { type: "string", enum: ["svm", "evm"], description: "Optional blockchain type for source token" }, toChain: { type: "string", enum: ["svm", "evm"], description: "Optional blockchain type for destination token" }, fromSpecificChain: { type: "string", enum: ["eth", "polygon", "bsc", "arbitrum", "base", "optimism", "avalanche", "linea", "svm"], description: "Optional specific chain for source token" }, toSpecificChain: { type: "string", enum: ["eth", "polygon", "bsc", "arbitrum", "base", "optimism", "avalanche", "linea", "svm"], description: "Optional specific chain for destination token" } }, required: ["fromToken", "toToken", "amount"], additionalProperties: false, $schema: "http://json-schema.org/draft-07/schema#" } }, // Competition Tools { name: "get_competition_status", description: "Get the status of the current competition", inputSchema: { type: "object", properties: {}, additionalProperties: false, $schema: "http://json-schema.org/draft-07/schema#" } }, { name: "get_leaderboard", description: "Get the competition leaderboard", inputSchema: { type: "object", properties: { competitionId: { type: "string", description: "Optional competition ID (if not provided, the active competition is used)" } }, additionalProperties: false, $schema: "http://json-schema.org/draft-07/schema#" } }, { name: "get_competition_rules", description: "Get the rules and configuration details for the competition", inputSchema: { type: "object", properties: {}, additionalProperties: false, $schema: "http://json-schema.org/draft-07/schema#" } }, // Health Tools { name: "get_health", description: "Basic health check for the trading simulator API", inputSchema: { type: "object", properties: {}, additionalProperties: false, $schema: "http://json-schema.org/draft-07/schema#" } }, { name: "get_detailed_health", description: "Detailed health check with information about all services", inputSchema: { type: "object", properties: {}, additionalProperties: false, $schema: "http://json-schema.org/draft-07/schema#" } } ]; // Set up request handlers server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: TRADING_SIM_TOOLS }; }); server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: [] }; }); server.setRequestHandler(ListPromptsRequestSchema, async () => { return { prompts: [] }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args = {} } = request.params; logger.info(`Handling tool call: ${name}`); try { // Handle different tools switch (name) { // Account Tools case "get_profile": { const response = await tradingClient.getProfile(); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], isError: false }; } case "update_profile": { if (!args || typeof args !== "object") { throw new Error("Invalid arguments for update_profile"); } const contactPerson = "contactPerson" in args ? args.contactPerson as string : undefined; const metadata = "metadata" in args ? args.metadata as TeamMetadata : undefined; const response = await tradingClient.updateProfile(contactPerson, metadata); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], isError: false }; } case "get_balances": { const response = await tradingClient.getBalances(); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], isError: false }; } case "get_portfolio": { const response = await tradingClient.getPortfolio(); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], isError: false }; } case "get_trades": { if (!args || typeof args !== "object") { throw new Error("Invalid arguments for get_trades"); } const tradeParams: TradeHistoryParams = {}; if ("limit" in args) tradeParams.limit = args.limit as number; if ("offset" in args) tradeParams.offset = args.offset as number; if ("token" in args) tradeParams.token = args.token as string; if ("chain" in args) tradeParams.chain = args.chain as BlockchainType; const response = await tradingClient.getTradeHistory(tradeParams); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], isError: false }; } // Price Tools case "get_price": { if (!args || typeof args !== "object" || !("token" in args)) { throw new Error("Invalid arguments for get_price"); } const token = args.token as string; const chain = "chain" in args ? args.chain as BlockchainType : undefined; const specificChain = "specificChain" in args ? args.specificChain as SpecificChain : undefined; const response = await tradingClient.getPrice(token, chain, specificChain); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], isError: false }; } case "get_token_info": { if (!args || typeof args !== "object" || !("token" in args)) { throw new Error("Invalid arguments for get_token_info"); } const token = args.token as string; const chain = "chain" in args ? args.chain as BlockchainType : undefined; const specificChain = "specificChain" in args ? args.specificChain as SpecificChain : undefined; const response = await tradingClient.getTokenInfo(token, chain, specificChain); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], isError: false }; } case "get_price_history": { if (!args || typeof args !== "object" || !("token" in args)) { throw new Error("Invalid arguments for get_price_history"); } const historyParams: PriceHistoryParams = { token: args.token as string }; if ("startTime" in args) historyParams.startTime = args.startTime as string; if ("endTime" in args) historyParams.endTime = args.endTime as string; if ("interval" in args) historyParams.interval = args.interval as PriceHistoryParams['interval']; if ("chain" in args) historyParams.chain = args.chain as BlockchainType; if ("specificChain" in args) historyParams.specificChain = args.specificChain as SpecificChain; const response = await tradingClient.getPriceHistory(historyParams); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], isError: false }; } // Trading Tools case "execute_trade": { if (!args || typeof args !== "object" || !("fromToken" in args) || !("toToken" in args) || !("amount" in args) || !("reason" in args)) { throw new Error("Invalid arguments for execute_trade"); } const tradeExecParams: TradeParams = { fromToken: args.fromToken as string, toToken: args.toToken as string, amount: args.amount as string, reason: args.reason as string }; if ("slippageTolerance" in args) tradeExecParams.slippageTolerance = args.slippageTolerance as string; if ("fromChain" in args) tradeExecParams.fromChain = args.fromChain as BlockchainType; if ("toChain" in args) tradeExecParams.toChain = args.toChain as BlockchainType; if ("fromSpecificChain" in args) tradeExecParams.fromSpecificChain = args.fromSpecificChain as SpecificChain; if ("toSpecificChain" in args) tradeExecParams.toSpecificChain = args.toSpecificChain as SpecificChain; const response = await tradingClient.executeTrade(tradeExecParams); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], isError: false }; } case "get_quote": { if (!args || typeof args !== "object" || !("fromToken" in args) || !("toToken" in args) || !("amount" in args)) { throw new Error("Invalid arguments for get_quote"); } const fromToken = args.fromToken as string; const toToken = args.toToken as string; const amount = args.amount as string; const fromChain = "fromChain" in args ? args.fromChain as BlockchainType : undefined; const toChain = "toChain" in args ? args.toChain as BlockchainType : undefined; const fromSpecificChain = "fromSpecificChain" in args ? args.fromSpecificChain as SpecificChain : undefined; const toSpecificChain = "toSpecificChain" in args ? args.toSpecificChain as SpecificChain : undefined; const response = await tradingClient.getQuote( fromToken, toToken, amount, fromChain, toChain, fromSpecificChain, toSpecificChain ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], isError: false }; } // Competition Tools case "get_competition_status": { const response = await tradingClient.getCompetitionStatus(); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], isError: false }; } case "get_leaderboard": { const competitionId = "competitionId" in args ? args.competitionId as string : undefined; const response = await tradingClient.getLeaderboard(competitionId); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], isError: false }; } case "get_competition_rules": { const response = await tradingClient.getRules(); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } // Health Tools case "get_health": { const response = await tradingClient.getHealthStatus(); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], isError: false }; } case "get_detailed_health": { const response = await tradingClient.getDetailedHealthStatus(); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], isError: false }; } default: return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true }; } } catch (error) { logger.error(`Error handling tool call ${name}:`, error); return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } }); async function main() { try { // Create a transport for stdio const transport = new StdioServerTransport(); // Connect the server to the transport await server.connect(transport); logger.info("Trading Simulator MCP server started"); } catch (error) { logger.error("Failed to start server:", error); process.exit(1); } } // Run the main function main().catch(console.error);

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/recallnet/trading-simulator-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server