import { StockQuote } from '../types.js';
const API_BASE = 'https://www.alphavantage.co/query';
const RATE_LIMIT_DELAY = 13000; // 13 seconds between calls
interface AlphaVantageQuote {
'Global Quote': {
'01. symbol': string;
'02. open': string;
'03. high': string;
'04. low': string;
'05. price': string;
'06. volume': string;
'07. latest trading day': string;
'08. previous close': string;
'09. change': string;
'10. change percent': string;
};
}
interface AlphaVantageError {
Note?: string;
'Error Message'?: string;
Information?: string;
}
export function isConfigured(): boolean {
return !!process.env.ALPHA_VANTAGE_API_KEY;
}
export function getApiKeyInstructions(): {
error: string;
message: string;
instructions: string[];
} {
return {
error: 'API key not configured',
message: 'Alpha Vantage API key is required for stock data.',
instructions: [
'1. Get a free API key at: https://www.alphavantage.co/support/#api-key',
'2. Set the ALPHA_VANTAGE_API_KEY environment variable',
'3. Restart the MCP server'
]
};
}
export async function getQuote(symbol: string): Promise<StockQuote> {
if (!isConfigured()) {
throw new Error('Alpha Vantage API key is not configured');
}
const apiKey = process.env.ALPHA_VANTAGE_API_KEY;
const symbolUpper = symbol.toUpperCase();
const url = `${API_BASE}?function=GLOBAL_QUOTE&symbol=${symbolUpper}&apikey=${apiKey}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch stock data: ${response.statusText}`);
}
const data: AlphaVantageQuote & AlphaVantageError = await response.json();
// Check for API errors
if (data.Note) {
throw new Error('API rate limit reached. Please try again in a minute.');
}
if (data['Error Message']) {
throw new Error(`Invalid stock symbol: ${symbolUpper}`);
}
if (data.Information) {
throw new Error(data.Information);
}
const quote = data['Global Quote'];
if (!quote || !quote['05. price']) {
throw new Error(`No data available for symbol: ${symbolUpper}`);
}
const changePercent = parseFloat(quote['10. change percent'].replace('%', ''));
return {
symbol: quote['01. symbol'],
price: parseFloat(quote['05. price']),
change: parseFloat(quote['09. change']),
changePercent,
timestamp: quote['07. latest trading day']
};
}
function delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
export async function compareStocks(
symbols: string[]
): Promise<{
comparison: Array<StockQuote & { summary: string }>;
note?: string;
}> {
if (!isConfigured()) {
throw new Error('Alpha Vantage API key is not configured');
}
const results: Array<StockQuote & { summary: string }> = [];
const errors: string[] = [];
for (let i = 0; i < symbols.length; i++) {
const symbol = symbols[i];
try {
const quote = await getQuote(symbol);
const sign = quote.change >= 0 ? '+' : '';
results.push({
...quote,
summary: `${quote.symbol}: $${quote.price.toFixed(2)} (${sign}${quote.changePercent.toFixed(2)}%)`
});
} catch (error) {
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
errors.push(`${symbol}: ${errorMsg}`);
}
// Add delay between calls to respect rate limits (except for last call)
if (i < symbols.length - 1) {
await delay(RATE_LIMIT_DELAY);
}
}
const response: {
comparison: Array<StockQuote & { summary: string }>;
note?: string;
} = { comparison: results };
if (errors.length > 0) {
response.note = 'Some symbols failed to load due to API limits or invalid symbols';
}
return response;
}