Mirror of https://github.com/alexanimal/tradovate-mcp-server
This MCP server provides an interface to the Tradovate futures trading API, allowing users to manage contracts, positions, orders, and accounts. It ex...
258server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
259 const uri = request.params.uri;
260
261 // Parse the URI to get the resource type and ID
262 // Handle both tradovate:// protocol scheme and simple tradovate/ format
263 const match = uri.match(/^(?:tradovate:\/\/|tradovate\/)([^\/]+)(?:\/(.*))?$/);
264
265 if (!match) {
266 throw new Error(`Invalid resource URI: ${uri}`);
267 }
268
269 const resourceType = match[1];
270 const resourceId = match[2] || '';
271
272 if (!resourceType) {
273 throw new Error(`Invalid resource URI: ${uri}`);
274 }
275
276 // Handle different resource types
277 switch (resourceType) {
278 case "contract": {
279 if (resourceId) {
280 // Return specific contract
281 const contract = contractsCache[resourceId];
282 if (!contract) {
283 throw new Error(`Resource not found: ${uri}`);
284 }
285
286 return {
287 contents: [
288 {
289 type: "application/json",
290 text: JSON.stringify(contract),
291 uri: `tradovate://contract/${resourceId}`
292 },
293 ],
294 };
295 } else {
296 // Return list of contracts
297 const contracts = Object.values(contractsCache);
298
299 return {
300 contents: [
301 {
302 type: "application/json",
303 text: JSON.stringify(contracts),
304 uri: "tradovate://contract/"
305 },
306 ],
307 };
308 }
309 }
310
311 case "position": {
312 if (resourceId) {
313 // Return specific position - fetch directly from API
314 try {
315 const position = await tradovateRequest('GET', `position/find?id=${resourceId}`);// Local-only MCP, requires compromised LLM to exploit
632export async function handleGetMarketData(request: any) {
633 const symbol = String(request.params.arguments?.symbol);
634 const dataType = String(request.params.arguments?.dataType);
635 const chartTimeframe = String(request.params.arguments?.chartTimeframe || "1min");
636
637 if (!symbol || !dataType) {
638 throw new Error("Symbol and dataType are required");
639 }
640
641 try {
642 // Find contract by symbol
643 const contract = await tradovateRequest('GET', `contract/find?name=${symbol}`);// Local-only MCP, requires compromised LLM to exploit
501export async function handleGetAccountSummary(request: any) {
502 const accountId = String(request.params.arguments?.accountId || "");
503
504 try {
505 // Get accounts
506 let accounts;
507 if (accountId) {
508 accounts = [await tradovateRequest('GET', `account/find?id=${accountId}`)];// Local-only MCP, requires compromised LLM to exploit
810export async function handleListOrders(request: any) {
811 const accountId = String(request.params.arguments?.accountId || "");
812
813 try {
814 // Get orders from API
815 let endpoint = 'order/list';
816 if (accountId) {
817 endpoint += `?accountId=${accountId}`;
818 }// Local-only MCP, requires compromised LLM to exploit
8export async function handleGetContractDetails(request: any) {
9 const symbol = String(request.params.arguments?.symbol);
10 if (!symbol) {
11 throw new Error("Symbol is required");
12 }
13
14 try {
15 // Find contract by symbol
16 const contract = await tradovateRequest('GET', `contract/find?name=${symbol}`);// Local-only MCP, requires compromised LLM to exploit
60export async function handleListPositions(request: any) {
61 const accountId = String(request.params.arguments?.accountId || "");
62
63 try {
64 // Get positions from API
65 let endpoint = 'position/list';
66 if (accountId) {
67 endpoint += `?accountId=${accountId}`;
68 }// Local-only MCP, requires compromised LLM to exploit
263export async function handleModifyOrder(request: any) {
264 const orderId = String(request.params.arguments?.orderId);
265 const price = request.params.arguments?.price !== undefined ? Number(request.params.arguments.price) : undefined;
266 const stopPrice = request.params.arguments?.stopPrice !== undefined ? Number(request.params.arguments.stopPrice) : undefined;
267 const quantity = request.params.arguments?.quantity !== undefined ? Number(request.params.arguments.quantity) : undefined;
268
269 if (!orderId) {
270 throw new Error("Order ID is required");
271 }
272
273 try {
274 // Find order by ID
275 const order = await tradovateRequest('GET', `order/find?id=${orderId}`);// Local-only MCP, requires compromised LLM to exploit
131export async function handlePlaceOrder(request: any) {
132 const symbol = String(request.params.arguments?.symbol);
133 const action = String(request.params.arguments?.action);
134 const orderType = String(request.params.arguments?.orderType);
135 const quantity = Number(request.params.arguments?.quantity);
136 const price = request.params.arguments?.price ? Number(request.params.arguments.price) : undefined;
137 const stopPrice = request.params.arguments?.stopPrice ? Number(request.params.arguments.stopPrice) : undefined;
138
139 if (!symbol || !action || !orderType || !quantity) {
140 throw new Error("Symbol, action, orderType, and quantity are required");
141 }
142
143 // Validate order type and required parameters - moved up before any API calls
144 if ((orderType === "Limit" || orderType === "StopLimit") && price === undefined) {
145 throw new Error("Price is required for Limit and StopLimit orders");
146 }
147
148 if ((orderType === "Stop" || orderType === "StopLimit") && stopPrice === undefined) {
149 throw new Error("Stop price is required for Stop and StopLimit orders");
150 }
151
152 try {
153 // Find contract by symbol
154 const contract = await tradovateRequest('GET', `contract/find?name=${symbol}`);// Local-only MCP, requires compromised LLM to exploit
398export async function handleLiquidatePosition(request: any) {
399 const symbol = String(request.params.arguments?.symbol);
400
401 if (!symbol) {
402 throw new Error("Symbol is required");
403 }
404
405 try {
406 // Find contract by symbol
407 const contract = await tradovateRequest('GET', `contract/find?name=${symbol}`);// Local-only MCP, requires compromised LLM to exploit
60 // Debug log for credentials
61 logger.debug('DEBUG: Credentials retrieved from environment:');
62 logger.debug('name present:', !!credentials.name);
63 logger.debug('password present:', !!credentials.password);
64 logger.debug('appId present:', !!credentials.appId);
65 logger.debug('deviceId present:', !!credentials.deviceId);
66 logger.debug('cid present:', !!credentials.cid);
67 logger.debug('sec present:', !!credentials.sec);// Local-only MCP, requires compromised LLM to exploit