Skip to main content
Glama
maven81g

TradeStation MCP Server

by maven81g

marketData

Retrieve real-time market quotes for trading symbols to access current price data through TradeStation's API.

Instructions

Get quotes for symbols

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
symbolsYesComma-separated list of symbols

Implementation Reference

  • Zod input schema for the marketData tool, defining the 'symbols' parameter as a comma-separated string of trading symbols.
    const marketDataSchema = {
      symbols: z.string().describe('Comma-separated list of symbols')
    };
  • Full handler implementation for the 'marketData' tool: registers the tool with MCP server, parses 'symbols' argument, calls TradeStation /marketdata/quotes/{symbols} endpoint via authenticated request, returns JSON-formatted quotes or error message.
    server.tool(
      "marketData",
      "Get quotes for symbols",
      marketDataSchema,
      async (args) => {
        try {
          const { symbols } = args;
    
          // Fixed: symbols should be in the path, not query parameter
          const quotes = await makeAuthenticatedRequest(
            `/marketdata/quotes/${encodeURIComponent(symbols)}`
          );
    
          return {
            content: [
              {
                type: "text",
                text: JSON.stringify(quotes, null, 2)
              }
            ]
          };
        } catch (error: unknown) {
          return {
            content: [
              {
                type: "text",
                text: `Failed to fetch market data: ${error instanceof Error ? error.message : 'Unknown error'}`
              }
            ],
            isError: true
          };
        }
      }
    );
  • Core helper function used by marketData handler for making authenticated HTTP requests to TradeStation API, including automatic token refresh and comprehensive error handling.
    async function makeAuthenticatedRequest(
      endpoint: string,
      method: AxiosRequestConfig['method'] = 'GET',
      data: any = null
    ): Promise<any> {
      const userTokens = tokenStore.get(DEFAULT_USER);
    
      if (!userTokens) {
        throw new Error('User not authenticated. Please set TRADESTATION_REFRESH_TOKEN in .env file.');
      }
    
      // Check if token is expired or about to expire (within 60 seconds)
      if (userTokens.expiresAt < Date.now() + 60000) {
        // Refresh the token
        const newTokens = await refreshToken(userTokens.refreshToken);
        tokenStore.set(DEFAULT_USER, newTokens);
      }
    
      try {
        const options: AxiosRequestConfig = {
          method,
          url: `${TS_API_BASE}${endpoint}`,
          headers: {
            'Authorization': `Bearer ${tokenStore.get(DEFAULT_USER)?.accessToken}`,
            'Content-Type': 'application/json',
            'Accept': 'application/json'
          },
          timeout: 60000
        };
    
        if (data && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
          options.data = data;
        }
    
        const response = await axios(options);
        return response.data;
      } catch (error: unknown) {
        if (error instanceof AxiosError) {
          const errorMessage = error.response?.data?.Message || error.response?.data?.message || error.message;
          const statusCode = error.response?.status;
          console.error(`API request error [${statusCode}]: ${errorMessage}`);
          console.error('Endpoint:', endpoint);
          throw new Error(`API Error (${statusCode}): ${errorMessage}`);
        } else if (error instanceof Error) {
          console.error('API request error:', error.message);
          throw error;
        } else {
          console.error('Unknown API request error:', error);
          throw new Error('Unknown API request error');
        }
      }
    }
  • Helper function for refreshing OAuth access tokens, called by makeAuthenticatedRequest when token is expired.
    async function refreshToken(refreshToken: string): Promise<TokenData> {
      try {
        const response = await axios.post<RefreshTokenResponse>(TS_TOKEN_URL,
          new URLSearchParams({
            grant_type: 'refresh_token',
            client_id: TS_CLIENT_ID!,
            client_secret: TS_CLIENT_SECRET!,
            refresh_token: refreshToken
          }), 
          {
            headers: {
              'Content-Type': 'application/x-www-form-urlencoded'
            }
          }
        );
        
        return {
          accessToken: response.data.access_token,
          refreshToken: refreshToken,
          expiresAt: Date.now() + 3600000 // 1 hour from now
        };
      } catch (error: unknown) {
        if (error instanceof AxiosError) {
          console.error('Error refreshing token:', error.response?.data || error.message);
        } else if (error instanceof Error) {
          console.error('Error refreshing token:', error.message);
        } else {
          console.error('Unknown error refreshing token:', error);
        }
        throw new Error('Failed to refresh token');
      }
    }

Latest Blog Posts

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/maven81g/tradestation_mcp'

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