{"url_pattern":"^https?://finance\\.yahoo\\.com(/.*)?$","site_name":"yahoo-finance","allowed_domains":["finance.yahoo.com","query1.finance.yahoo.com"],"tools":[{"name":"yahoo_finance_quote","description":"Yahoo Finance 股票行情","inputSchema":{"type":"object","properties":{"symbol":{"type":"string","description":"Stock ticker symbol, e.g. AAPL, MSFT, TSLA"}},"required":["symbol"]},"handler":"(params) => {\n  const run = async function(args) {\n\n      if (!args.symbol) return {error: 'Missing argument: symbol', hint: 'Please provide a stock ticker symbol, e.g. AAPL'};\n\n      var symbol = args.symbol.toUpperCase().trim();\n\n      // Try v7 quote API first\n      var url = 'https://query1.finance.yahoo.com/v7/finance/quote?symbols=' + encodeURIComponent(symbol);\n      var resp;\n      var data;\n\n      try {\n        resp = await fetch(url, {\n          headers: {\n            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',\n            'Accept': 'application/json'\n          }\n        });\n\n        if (resp.ok) {\n          data = await resp.json();\n          var results = data && data.quoteResponse && data.quoteResponse.result;\n          if (results && results.length > 0) {\n            var q = results[0];\n            return formatQuote(q);\n          }\n        }\n      } catch(e) {\n        // v7 failed (likely CORS), fall through to alternatives\n      }\n\n      // Fallback: try v8 chart API\n      try {\n        var chartUrl = 'https://query1.finance.yahoo.com/v8/finance/chart/' + encodeURIComponent(symbol) + '?interval=1d&range=1d';\n        resp = await fetch(chartUrl, {\n          headers: {\n            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',\n            'Accept': 'application/json'\n          }\n        });\n\n        if (resp.ok) {\n          data = await resp.json();\n          var chart = data && data.chart && data.chart.result && data.chart.result[0];\n          if (chart) {\n            return formatChart(chart, symbol);\n          }\n        }\n      } catch(e) {\n        // v8 also failed, fall through\n      }\n\n      // Fallback: scrape from Yahoo Finance page via page context\n      try {\n        var pageUrl = 'https://finance.yahoo.com/quote/' + encodeURIComponent(symbol) + '/';\n        resp = await fetch(pageUrl, {\n          headers: {\n            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',\n            'Accept': 'text/html'\n          }\n        });\n\n        if (resp.ok) {\n          var html = await resp.text();\n\n          // Try to extract JSON data embedded in the page\n          var jsonMatch = html.match(/root\\.App\\.main\\s*=\\s*(\\{.*?\\});\\s*\\n/);\n          if (jsonMatch) {\n            var appData = JSON.parse(jsonMatch[1]);\n            var stores = appData && appData.context && appData.context.dispatcher && appData.context.dispatcher.stores;\n            if (stores && stores.QuoteSummaryStore && stores.QuoteSummaryStore.price) {\n              var price = stores.QuoteSummaryStore.price;\n              return {\n                symbol: symbol,\n                name: price.shortName || price.longName || symbol,\n                price: price.regularMarketPrice && price.regularMarketPrice.raw,\n                change: price.regularMarketChange && price.regularMarketChange.raw,\n                changePercent: price.regularMarketChangePercent && price.regularMarketChangePercent.raw\n                  ? (price.regularMarketChangePercent.raw * 100).toFixed(2) + '%' : null,\n                currency: price.currency,\n                exchange: price.exchangeName,\n                marketState: price.marketState,\n                url: pageUrl\n              };\n            }\n          }\n\n          // Try to extract from newer page format (FinanceConfigStore or similar)\n          var titleMatch = html.match(/<title>([^<]+)<\\/title>/);\n          var priceMatch = html.match(/data-testid=\"qsp-price\"[^>]*>([^<]+)</);\n          var changeMatch = html.match(/data-testid=\"qsp-price-change\"[^>]*>([^<]+)</);\n          var changePctMatch = html.match(/data-testid=\"qsp-price-change-percent\"[^>]*>([^<]+)</);\n\n          if (priceMatch) {\n            return {\n              symbol: symbol,\n              name: titleMatch ? titleMatch[1].split('(')[0].trim() : symbol,\n              price: priceMatch[1].replace(/,/g, ''),\n              change: changeMatch ? changeMatch[1] : null,\n              changePercent: changePctMatch ? changePctMatch[1] : null,\n              source: 'page-scrape',\n              url: pageUrl\n            };\n          }\n\n          return {error: 'Could not parse quote data from page', symbol: symbol, url: pageUrl};\n        }\n      } catch(e) {\n        // All methods failed\n      }\n\n      return {error: 'Failed to fetch quote for ' + symbol, hint: 'All API methods failed. The symbol may be invalid or Yahoo Finance may be blocking requests.'};\n\n      function formatQuote(q) {\n        return {\n          symbol: q.symbol,\n          name: q.shortName || q.longName || q.symbol,\n          price: q.regularMarketPrice,\n          change: q.regularMarketChange != null ? q.regularMarketChange.toFixed(2) : null,\n          changePercent: q.regularMarketChangePercent != null ? q.regularMarketChangePercent.toFixed(2) + '%' : null,\n          open: q.regularMarketOpen,\n          high: q.regularMarketDayHigh,\n          low: q.regularMarketDayLow,\n          prevClose: q.regularMarketPreviousClose,\n          volume: q.regularMarketVolume,\n          marketCap: formatLargeNumber(q.marketCap),\n          pe: q.trailingPE != null ? q.trailingPE.toFixed(2) : null,\n          eps: q.epsTrailingTwelveMonths != null ? q.epsTrailingTwelveMonths.toFixed(2) : null,\n          week52High: q.fiftyTwoWeekHigh,\n          week52Low: q.fiftyTwoWeekLow,\n          avgVolume: q.averageDailyVolume3Month,\n          currency: q.currency,\n          exchange: q.fullExchangeName || q.exchange,\n          marketState: q.marketState,\n          quoteType: q.quoteType,\n          url: 'https://finance.yahoo.com/quote/' + q.symbol + '/'\n        };\n      }\n\n      function formatChart(chart, sym) {\n        var meta = chart.meta || {};\n        var indicators = chart.indicators;\n        var quoteData = indicators && indicators.quote && indicators.quote[0];\n        var timestamps = chart.timestamp || [];\n\n        var lastIdx = timestamps.length - 1;\n        var currentPrice = meta.regularMarketPrice || (quoteData && quoteData.close && quoteData.close[lastIdx]);\n        var prevClose = meta.previousClose || meta.chartPreviousClose;\n        var change = (currentPrice != null && prevClose != null) ? (currentPrice - prevClose) : null;\n        var changePct = (change != null && prevClose) ? ((change / prevClose) * 100) : null;\n\n        return {\n          symbol: meta.symbol || sym,\n          name: meta.shortName || meta.longName || sym,\n          price: currentPrice != null ? Number(currentPrice.toFixed(2)) : null,\n          change: change != null ? change.toFixed(2) : null,\n          changePercent: changePct != null ? changePct.toFixed(2) + '%' : null,\n          open: quoteData && quoteData.open ? quoteData.open[0] : null,\n          high: quoteData && quoteData.high ? Math.max.apply(null, quoteData.high.filter(function(v) { return v != null; })) : null,\n          low: quoteData && quoteData.low ? Math.min.apply(null, quoteData.low.filter(function(v) { return v != null; })) : null,\n          prevClose: prevClose,\n          volume: meta.regularMarketVolume || (quoteData && quoteData.volume ? quoteData.volume[lastIdx] : null),\n          currency: meta.currency,\n          exchange: meta.exchangeName,\n          marketState: meta.marketState,\n          source: 'chart-api',\n          url: 'https://finance.yahoo.com/quote/' + (meta.symbol || sym) + '/'\n        };\n      }\n\n      function formatLargeNumber(v) {\n        if (v == null) return null;\n        if (v >= 1e12) return (v / 1e12).toFixed(2) + 'T';\n        if (v >= 1e9) return (v / 1e9).toFixed(2) + 'B';\n        if (v >= 1e6) return (v / 1e6).toFixed(2) + 'M';\n        if (v >= 1e3) return (v / 1e3).toFixed(2) + 'K';\n        return v.toString();\n      }\n  };\n  return run(params || {});\n}"}]}