Skip to main content
Glama
ross-jill-ws

Alpha Vantage MCP Server

by ross-jill-ws
run-stocks.ts6.16 kB
#!/usr/bin/env bun import yargs from "yargs"; import { hideBin } from "yargs/helpers"; import { connect, disconnect, findDocuments } from "./mongo"; import { pullStock, pullNews } from "./business"; import type { StockData, NewsItem } from "./business"; // ============================================================================ // Date Format Conversion Helpers // ============================================================================ /** * Converts YYYYMMDD to YYYY-MM-DD (MongoDB stock date format) */ function formatStockDate(dateStr: string): string { if (!/^\d{8}$/.test(dateStr)) { throw new Error(`Invalid date format: ${dateStr}. Expected YYYYMMDD`); } return `${dateStr.slice(0, 4)}-${dateStr.slice(4, 6)}-${dateStr.slice(6, 8)}`; } /** * Converts YYYYMMDD-YYYYMMDD to API time format */ function parseDateRange(rangeStr: string): { from_time: string; to_time: string } { if (!/^\d{8}-\d{8}$/.test(rangeStr)) { throw new Error(`Invalid date range format: ${rangeStr}. Expected YYYYMMDD-YYYYMMDD`); } const [from, to] = rangeStr.split("-"); return { from_time: `${from}T0000`, to_time: `${to}T2359` }; } /** * Converts YYYYMMDD to news time_published format for queries */ function formatNewsTime(dateStr: string, isEnd: boolean = false): string { if (!/^\d{8}$/.test(dateStr)) { throw new Error(`Invalid date format: ${dateStr}. Expected YYYYMMDD`); } return isEnd ? `${dateStr}T2359` : `${dateStr}T0000`; } // ============================================================================ // CLI Configuration // ============================================================================ const argv = yargs(hideBin(process.argv)) .usage("Usage: bun src/run-stocks.ts [options]") .option("pull-stock", { type: "string", description: "Pull stock data for a symbol from Alpha Vantage API", alias: "ps" }) .option("pull-news", { type: "string", description: "Pull news data for a date range (YYYYMMDD-YYYYMMDD)", alias: "pn" }) .option("symbol", { type: "string", description: "Query stock data for a symbol from MongoDB", alias: "s" }) .option("date", { type: "string", description: "Specific date for stock query (YYYYMMDD)", alias: "d" }) .option("news", { type: "boolean", description: "Query news data from MongoDB", alias: "n" }) .option("from", { type: "string", description: "Start date for news query (YYYYMMDD)", alias: "f" }) .option("to", { type: "string", description: "End date for news query (YYYYMMDD)", alias: "t" }) .example([ ["bun src/run-stocks.ts --pull-stock AAPL", "Pull AAPL stock data from Alpha Vantage API"], ["bun src/run-stocks.ts --pull-news 20251201-20251202", "Pull news from Dec 1-2, 2025"], ["bun src/run-stocks.ts --symbol IBM", "Get latest 100 IBM stock prices"], ["bun src/run-stocks.ts --symbol IBM --date 20251223", "Get IBM price for Dec 23, 2025"], ["bun src/run-stocks.ts --news --from 20251221 --to 20251222", "Query news from Dec 21-22"] ]) .help() .alias("help", "h") .parseSync(); // ============================================================================ // Main CLI Logic // ============================================================================ async function main() { const client = await connect(); try { if (argv["pull-stock"]) { // Handle --pull-stock <symbol> const symbol = argv["pull-stock"]; console.log(`Pulling stock data for ${symbol}...`); const count = await pullStock(symbol); console.log(`Successfully pulled ${count} stock records for ${symbol}`); } else if (argv["pull-news"]) { // Handle --pull-news <date-range> const { from_time, to_time } = parseDateRange(argv["pull-news"]); console.log(`Pulling news from ${from_time} to ${to_time}...`); const count = await pullNews(from_time, to_time); console.log(`Successfully pulled ${count} news articles`); } else if (argv.symbol) { // Handle --symbol <symbol> [--date <date>] const symbol = argv.symbol; const collectionName = `stock-${symbol}`; if (argv.date) { // Query specific date const dateFormatted = formatStockDate(argv.date); const docs = await findDocuments<StockData>("finance", collectionName, { date: dateFormatted }); if (docs.length === 0) { console.log(`No stock data found for ${symbol} on ${dateFormatted}`); } else { console.log(JSON.stringify(docs[0], null, 2)); } } else { // Query latest 100 prices const docs = await findDocuments<StockData>("finance", collectionName, {}, { sort: { date: -1 }, limit: 100 }); if (docs.length === 0) { console.log(`No stock data found for ${symbol}`); } else { console.log(JSON.stringify(docs, null, 2)); } } } else if (argv.news) { // Handle --news --from <date1> --to <date2> if (!argv.from || !argv.to) { console.error("Error: --news requires both --from and --to options"); console.error("Example: bun src/run-stocks.ts --news --from 20251221 --to 20251222"); process.exit(1); } const fromTime = formatNewsTime(argv.from, false); const toTime = formatNewsTime(argv.to, true); console.log(`Querying news from ${fromTime} to ${toTime}...`); const docs = await findDocuments<NewsItem>("finance", "news", { time_published: { $gte: fromTime, $lte: toTime } }); if (docs.length === 0) { console.log(`No news found between ${argv.from} and ${argv.to}`); } else { console.log(`Found ${docs.length} news articles:`); console.log(JSON.stringify(docs, null, 2)); } } else { // No valid command provided, show help yargs(hideBin(process.argv)).showHelp(); } } finally { await disconnect(client); } } main().catch((error) => { console.error("Error:", error.message); process.exit(1); });

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/ross-jill-ws/alphavantage'

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