cross-endpoint.js•8.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);