Skip to main content
Glama

TradeStation MCP Server

by maven81g
cross-endpoint.js8.86 kB
// TradeStation API Endpoint Debugger import axios from 'axios'; import dotenv from 'dotenv'; dotenv.config(); // Access token will be obtained dynamically using the refresh token // Note: For testing purposes, you can temporarily set a token here after running quick-token-test.js // Or this script will obtain one automatically from the refresh token let ACCESS_TOKEN = null; // Different API base URLs to test const API_BASES = [ 'https://sim-api.tradestation.com/v3', //'https://api.tradestation.com/v3', //'https://sim-api.tradestation.com/v2', //'https://api.tradestation.com/v2' ]; // Different endpoint patterns to test const ENDPOINT_PATTERNS = [ '/marketdata/quotes?symbols=AAPL', '/marketdata/stream/quotes?symbols=AAPL', '/data/quotes?symbols=AAPL', '/market/quotes?symbols=AAPL', '/quotes?symbols=AAPL', '/marketdata/quotes/AAPL', '/brokerage/accounts', // This should work if you have account access '/accounts', // Alternative account endpoint '/data/symbol/AAPL/quotes', '/marketdata/symbols/AAPL/quotes', '/marketdata/barcharts/AAPL', '/marketdata/symbollists/cryptopairs/symbolnames', '/marketdata/symbols/MSFT,BTCUSD', 'marketdata/options/expirations/AAPL', '/marketdata/options/spreadtypes', '/marketdata/options/strikes/AAPL', '/marketdata/quotes/MSFT,BTCUSD' ]; async function testEndpoint(baseUrl, endpoint, token) { const fullUrl = `${baseUrl}${endpoint}`; try { console.log(`Testing: ${fullUrl}`); const response = await axios.get(fullUrl, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, timeout: 10000 }); console.log(`✅ SUCCESS: ${response.status} - ${fullUrl}`); console.log(`Response preview:`, JSON.stringify(response.data, null, 2).substring(0, 300)); return { success: true, url: fullUrl, status: response.status, data: response.data }; } catch (error) { const status = error.response?.status || 'TIMEOUT/ERROR'; const errorMsg = error.response?.data?.message || error.message; if (error.response?.status === 404) { console.log(`❌ 404: ${fullUrl}`); } else if (error.response?.status === 401) { console.log(`🔒 401 (Auth): ${fullUrl}`); } else if (error.response?.status === 403) { console.log(`🚫 403 (Forbidden): ${fullUrl}`); } else { console.log(`❌ ${status}: ${fullUrl} - ${errorMsg}`); } return { success: false, url: fullUrl, status, error: errorMsg }; } } // Function to get a fresh access token from refresh token async function getAccessToken() { const TS_CLIENT_ID = process.env.TRADESTATION_CLIENT_ID; const TS_CLIENT_SECRET = process.env.TRADESTATION_CLIENT_SECRET; const TS_REFRESH_TOKEN = process.env.TRADESTATION_REFRESH_TOKEN; const TS_TOKEN_URL = 'https://signin.tradestation.com/oauth/token'; if (!TS_CLIENT_ID || !TS_CLIENT_SECRET || !TS_REFRESH_TOKEN) { console.error('❌ Missing required environment variables'); console.error('Please ensure .env contains: TRADESTATION_CLIENT_ID, TRADESTATION_CLIENT_SECRET, TRADESTATION_REFRESH_TOKEN'); return null; } try { console.log('📡 Obtaining fresh access token from refresh token...'); const response = await axios.post(TS_TOKEN_URL, new URLSearchParams({ grant_type: 'refresh_token', client_id: TS_CLIENT_ID, client_secret: TS_CLIENT_SECRET, refresh_token: TS_REFRESH_TOKEN }), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); console.log('✅ Access token obtained successfully\n'); return response.data.access_token; } catch (error) { console.error('❌ Failed to obtain access token:', error.response?.data || error.message); return null; } } async function discoverWorkingEndpoints() { console.log('=== TradeStation API Endpoint Discovery ===\n'); if (!ACCESS_TOKEN) { console.error('❌ No access token available'); return; } const workingEndpoints = []; const results = []; for (const baseUrl of API_BASES) { console.log(`\n🔍 Testing base URL: ${baseUrl}`); console.log('─'.repeat(60)); for (const endpoint of ENDPOINT_PATTERNS) { const result = await testEndpoint(baseUrl, endpoint, ACCESS_TOKEN); results.push(result); if (result.success) { workingEndpoints.push(result); } // Small delay to avoid rate limiting await new Promise(resolve => setTimeout(resolve, 100)); } } console.log('\n=== SUMMARY ==='); console.log(`Total tests: ${results.length}`); console.log(`Working endpoints: ${workingEndpoints.length}`); if (workingEndpoints.length > 0) { console.log('\n✅ WORKING ENDPOINTS:'); workingEndpoints.forEach(endpoint => { console.log(` ${endpoint.status}: ${endpoint.url}`); }); console.log('\n📋 Recommended base URL and endpoints for your MCP server:'); // Group by base URL const byBase = {}; workingEndpoints.forEach(ep => { const base = ep.url.split('/v')[0] + '/v' + ep.url.split('/v')[1].split('/')[0]; if (!byBase[base]) byBase[base] = []; byBase[base].push(ep.url.replace(base, '')); }); Object.entries(byBase).forEach(([base, endpoints]) => { console.log(`\nBase URL: ${base}`); endpoints.forEach(ep => console.log(` Endpoint: ${ep}`)); }); } else { console.log('\n❌ No working endpoints found. This could mean:'); console.log(' 1. Your account doesn\'t have market data permissions'); console.log(' 2. The token is for a different environment (live vs sim)'); console.log(' 3. TradeStation API structure has changed'); console.log(' 4. Additional headers or parameters are required'); } // Test token validity with a simple request console.log('\n=== TOKEN VALIDATION ==='); try { // Try a basic request to see if token works at all const response = await axios.get('https://sim-api.tradestation.com/v3/', { headers: { 'Authorization': `Bearer ${ACCESS_TOKEN}`, 'Content-Type': 'application/json' } }); console.log('✅ Token appears valid - base API responds'); } catch (error) { if (error.response?.status === 401) { console.log('❌ Token is invalid or expired'); } else if (error.response?.status === 404) { console.log('✅ Token is valid but endpoint doesn\'t exist'); } else { console.log(`❓ Unexpected response: ${error.response?.status} - ${error.message}`); } } } // Also test with different headers and parameters async function testAlternativeRequests() { console.log('\n=== TESTING ALTERNATIVE REQUEST FORMATS ==='); const testConfigs = [ { name: 'With Accept header', headers: { 'Authorization': `Bearer ${ACCESS_TOKEN}`, 'Accept': 'application/json' } }, { name: 'With User-Agent', headers: { 'Authorization': `Bearer ${ACCESS_TOKEN}`, 'User-Agent': 'TradeStation-MCP/1.0' } }, { name: 'Minimal headers', headers: { 'Authorization': `Bearer ${ACCESS_TOKEN}` } } ]; for (const config of testConfigs) { console.log(`\nTesting: ${config.name}`); try { const response = await axios.get('https://sim-api.tradestation.com/v3/marketdata/quotes?symbols=AAPL', { headers: config.headers }); console.log(`✅ ${config.name} worked!`); } catch (error) { console.log(`❌ ${config.name}: ${error.response?.status} - ${error.response?.data?.message || error.message}`); } } } // Main execution async function main() { // Get fresh access token first ACCESS_TOKEN = await getAccessToken(); if (!ACCESS_TOKEN) { console.error('\n❌ Cannot proceed without a valid access token'); process.exit(1); } await discoverWorkingEndpoints(); await testAlternativeRequests(); } 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/maven81g/tradestation_mcp'

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