Skip to main content
Glama

finance-search

Search for real-time financial data on stocks, indices, mutual funds, currencies, and futures. View price trends, market news, and summaries with customizable time ranges and response formats.

Instructions

Search for stocks, indices, mutual funds, currencies, and futures using Google Finance

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
asyncNoSubmit search asynchronously
include_discoverNoInclude discover more section
include_marketsNoInclude market overview data
include_newsNoInclude top news
max_futuresNoMaximum number of futures chain items to return
no_cacheNoForce fresh results instead of cached
outputNoResponse format
qYesStock symbol, index, mutual fund, currency, or futures (e.g., 'GOOGL:NASDAQ', 'WMT:NYSE')
summary_onlyNoReturn only essential information (price, movement, news)
windowNoTime range for graph data

Implementation Reference

  • Main handler function that performs the finance search using SerpAPI's Google Finance API, optionally fetches additional news from Brave Search, filters and formats the response into Markdown.
    export async function searchFinance( params: z.infer<typeof financeSearchSchema> ): Promise<string> { const apiKey = process.env.SERP_API_KEY; if (!apiKey) { throw new Error("SERP_API_KEY environment variable is required"); } const { include_markets, include_discover, include_news, max_futures, summary_only, max_news, ...apiParams } = params; const searchParams = new URLSearchParams({ engine: "google_finance", api_key: apiKey, q: apiParams.q, hl: "en", ...(apiParams.window && { window: apiParams.window }), ...(apiParams.async !== undefined && { async: apiParams.async.toString() }), }); try { const response = await axios.get<SerpApiFinanceResponse>( `${SERPAPI_BASE_URL}?${searchParams.toString()}` ); // Check for API errors first if (response.data.error) { throw new Error(`SerpAPI Error: ${response.data.error}`); } const filteredData = filterFinanceResponse(response.data, params); // Fetch additional news if requested let braveNews: BraveNewsResult[] = []; if (max_news && max_news > 1 && include_news !== false) { braveNews = await fetchAdditionalNews(params.q, max_news); } return formatFinanceToMarkdown(filteredData, params, braveNews); } catch (error) { if (axios.isAxiosError(error)) { throw new Error( `Finance API request failed: ${ error.response?.data?.error || error.message }` ); } throw error; } }
  • Zod schema defining the input parameters for the finance-search tool, including query, options for news, markets, etc.
    const financeSearchSchema = z.object({ q: z .string() .describe( "Stock symbol with exchange in format 'SYMBOL:EXCHANGE'. Examples: 'AAPL:NASDAQ', 'TSLA:NASDAQ', 'MSFT:NASDAQ', 'GOOGL:NASDAQ', 'NVDA:NASDAQ' for stocks. For ETFs/Index funds: 'SPY:NYSEARCA', 'VTI:NYSEARCA', 'QQQ:NASDAQ', 'VOO:NYSEARCA'. For crypto: 'BTC-USD', 'ETH-USD'. Note: Just 'SPY' returns market overview with related ETFs in futures_chain." ), window: z .enum(["1D", "5D", "1M", "6M", "YTD", "1Y", "5Y", "MAX"]) .optional() .describe("Time range for graph data"), async: z.boolean().optional().describe("Submit search asynchronously"), include_markets: z .boolean() .optional() .default(false) .describe("Include market overview data"), include_discover: z .boolean() .optional() .default(false) .describe("Include discover more section"), include_news: z .boolean() .optional() .default(true) .describe("Include top news"), max_futures: z .number() .optional() .default(3) .describe("Maximum number of futures chain items to return"), summary_only: z .boolean() .optional() .default(true) .describe("Return only essential information (price, movement, news)"), max_news: z .number() .optional() .default(1) .describe( "Maximum number of news articles to return (1 = only top news from Google Finance, >1 = additional news from Brave Search)" ), });
  • src/index.ts:120-148 (registration)
    Registration of the 'finance-search' tool on the MCP server, importing and using the schema and handler from finance.ts, with error handling wrapper.
    server.tool( "finance-search", "Search for stocks, indices, mutual funds, currencies, and futures using Google Finance. Use format 'SYMBOL:EXCHANGE'. Stocks: 'AAPL:NASDAQ', 'TSLA:NASDAQ'. ETFs/Index funds: 'SPY:NYSEARCA', 'VTI:NYSEARCA', 'QQQ:NASDAQ', 'VOO:NYSEARCA'. Crypto: 'BTC-USD', 'ETH-USD'. Note: Query 'SPY' (no exchange) returns market overview with S&P 500 related ETFs.", financeSearchSchema.shape, async (params) => { try { const result = await searchFinance(params); return { content: [ { type: "text", text: result, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error searching finance data: ${ error instanceof Error ? error.message : String(error) }`, }, ], }; } } );
  • Key helper function that formats the raw API data into a structured Markdown response for the tool output.
    function formatFinanceToMarkdown( data: any, params: z.infer<typeof financeSearchSchema>, braveNews: BraveNewsResult[] = [] ): string { if (!data) return "No financial data available."; let markdown = `# ${params.q}\n\n`; // Main stock/security info if (data.summary) { const summary = data.summary; const price = summary.price !== undefined ? summary.price : "N/A"; markdown += `Current Price: ${summary.currency || "$"}${price} \n`; if (summary.price_movement) { const movement = summary.price_movement; const arrow = movement.movement === "up" ? "📈" : movement.movement === "down" ? "📉" : "➡️"; markdown += `Change: ${arrow} ${movement.percentage}% (${ movement.value >= 0 ? "+" : "" }${movement.value}) \n`; } if (summary.name && summary.name !== summary.symbol) { markdown += `Name: ${summary.name} \n`; } markdown += `\n`; } // Price insights if (data.price_insights) { const insights = data.price_insights; markdown += `## Price Analysis\n\n`; if (insights.previous_close) markdown += `Previous Close: $${insights.previous_close} \n`; if (insights.day_range) markdown += `Day Range: $${insights.day_range} \n`; if (insights.year_range) markdown += `52-Week Range: $${insights.year_range} \n`; if (insights.market_cap) markdown += `Market Cap: ${insights.market_cap} \n`; if (insights.pe_ratio) markdown += `P/E Ratio: ${insights.pe_ratio} \n`; markdown += `\n`; } // Futures chain (for summary mode) if ( data.futures_chain && params.summary_only && data.futures_chain.length > 0 ) { const futures = data.futures_chain.slice(0, params.max_futures || 3); if (futures.length > 1) { markdown += `## Futures Contracts\n\n`; futures.forEach((future: any) => { markdown += `${future.date || future.stock}: $${ future.price || future.extracted_price }`; if (future.change) markdown += ` (${future.change})`; markdown += ` \n`; }); markdown += `\n`; } } // Top news - safely handle different data structures if (data.top_news && params.include_news !== false) { try { let newsItems: any[] = []; if (Array.isArray(data.top_news)) { newsItems = data.top_news; } else if ( data.top_news.results && Array.isArray(data.top_news.results) ) { newsItems = data.top_news.results; } else if (typeof data.top_news === "object") { // If it's a single news object, put it in an array if ( data.top_news.title || data.top_news.headline || data.top_news.snippet ) { newsItems = [data.top_news]; } else { // Try to convert object values to array newsItems = Object.values(data.top_news).filter( (item: any) => item && typeof item === "object" && (item.title || item.headline || item.snippet) ); } } if (newsItems.length > 0) { markdown += `## Latest News\n\n`; newsItems.slice(0, 5).forEach((news: any, index: number) => { if (news && (news.title || news.headline || news.snippet)) { markdown += `### ${index + 1}. ${ news.title || news.headline || "News Update" }\n`; if (news.source) markdown += `Source: ${news.source} \n`; if (news.date || news.published_date) markdown += `Date: ${news.date || news.published_date} \n`; if (news.snippet || news.description) markdown += `${news.snippet || news.description} \n`; if (news.link || news.url) markdown += `[Read More](${news.link || news.url}) \n`; markdown += `\n`; } }); } } catch (error) { // Silently skip news section if there's an error } } // Additional news from Brave Search if (braveNews.length > 0 && params.include_news !== false) { if (!data.top_news || Object.keys(data.top_news || {}).length === 0) { markdown += `## Latest News\n\n`; } braveNews.forEach((news, index) => { const newsIndex = data.top_news ? index + 2 : index + 1; // Start after Google Finance news markdown += `### ${newsIndex}. ${news.title}\n`; if (news.source?.name) markdown += `Source: ${news.source.name} \n`; if (news.published_datetime) markdown += `Date: ${news.published_datetime} \n`; if (news.description) markdown += `${news.description} \n`; if (news.url) markdown += `[Read More](${news.url}) \n`; markdown += `\n`; }); } // Market overview (if included) if (data.markets && params.include_markets) { markdown += `## Market Overview\n\n`; if (data.markets.top_news) { markdown += `Market News Available: ${data.markets.top_news.length} articles \n`; } markdown += `\n`; } // Discover more (if included) if (data.discover_more && params.include_discover) { markdown += `## Related\n\n`; if (data.discover_more.similar_stocks) { markdown += `Similar Stocks: ${data.discover_more.similar_stocks .map((s: any) => s.stock || s) .join(", ")} \n`; } markdown += `\n`; } return markdown; }

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/CaptainCrouton89/maps-mcp'

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