Skip to main content
Glama
berlinbra

AlphaVantage-MCP

get-etf-profile

Retrieve detailed ETF profile data including holdings, sector allocation, and key metrics to analyze investment characteristics and make informed decisions.

Instructions

Get comprehensive ETF profile information including holdings, sector allocation, and key metrics

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
symbolYesETF symbol (e.g., QQQ, SPY, VTI)

Implementation Reference

  • Handler logic for executing the get-etf-profile tool: extracts symbol, makes API request to ETF_PROFILE endpoint, formats response using format_etf_profile, and returns formatted text.
    elif name == "get-etf-profile": symbol = arguments.get("symbol") if not symbol: return [types.TextContent(type="text", text="Missing symbol parameter")] symbol = symbol.upper() async with httpx.AsyncClient() as client: etf_data = await make_alpha_request( client, "ETF_PROFILE", symbol ) if isinstance(etf_data, str): return [types.TextContent(type="text", text=f"Error: {etf_data}")] formatted_etf = format_etf_profile(etf_data) etf_text = f"ETF profile for {symbol}:\n\n{formatted_etf}" return [types.TextContent(type="text", text=etf_text)]
  • JSON Schema defining the input parameters for the get-etf-profile tool: requires a 'symbol' string (e.g., QQQ).
    types.Tool( name="get-etf-profile", description="Get comprehensive ETF profile information including holdings, sector allocation, and key metrics", inputSchema={ "type": "object", "properties": { "symbol": { "type": "string", "description": "ETF symbol (e.g., QQQ, SPY, VTI)", } }, "required": ["symbol"], }, )
  • Helper function that formats the raw ETF profile data from Alpha Vantage API into a human-readable string, including basic info, sector allocation, and top holdings.
    def format_etf_profile(etf_data: Dict[str, Any]) -> str: """Format ETF profile data into a concise string. Args: etf_data: The response data from the Alpha Vantage ETF_PROFILE endpoint Returns: A formatted string containing the ETF profile information """ try: if "Error Message" in etf_data: return f"Error: {etf_data['Error Message']}" if not etf_data: return "No ETF profile data available in the response" # Extract basic ETF information net_assets = etf_data.get("net_assets", "N/A") net_expense_ratio = etf_data.get("net_expense_ratio", "N/A") portfolio_turnover = etf_data.get("portfolio_turnover", "N/A") dividend_yield = etf_data.get("dividend_yield", "N/A") inception_date = etf_data.get("inception_date", "N/A") leveraged = etf_data.get("leveraged", "N/A") formatted = [ f"ETF Profile\n\n", f"Basic Information:\n" ] # Format net assets if net_assets != "N/A" and net_assets.replace('.', '').isdigit(): formatted.append(f"Net Assets: ${float(net_assets):,.0f}\n") else: formatted.append(f"Net Assets: {net_assets}\n") # Format net expense ratio if net_expense_ratio != "N/A" and net_expense_ratio.replace('.', '').replace('-', '').isdigit(): formatted.append(f"Net Expense Ratio: {float(net_expense_ratio):.3%}\n") else: formatted.append(f"Net Expense Ratio: {net_expense_ratio}\n") # Format portfolio turnover if portfolio_turnover != "N/A" and portfolio_turnover.replace('.', '').replace('-', '').isdigit(): formatted.append(f"Portfolio Turnover: {float(portfolio_turnover):.1%}\n") else: formatted.append(f"Portfolio Turnover: {portfolio_turnover}\n") # Format dividend yield if dividend_yield != "N/A" and dividend_yield.replace('.', '').replace('-', '').isdigit(): formatted.append(f"Dividend Yield: {float(dividend_yield):.2%}\n") else: formatted.append(f"Dividend Yield: {dividend_yield}\n") formatted.extend([ f"Inception Date: {inception_date}\n", f"Leveraged: {leveraged}\n\n" ]) # Format sectors if available sectors = etf_data.get("sectors", []) if sectors: formatted.append("Sector Allocation:\n") for sector in sectors: sector_name = sector.get("sector", "Unknown") weight = sector.get("weight", "0") try: weight_pct = float(weight) * 100 formatted.append(f"{sector_name}: {weight_pct:.1f}%\n") except (ValueError, TypeError): formatted.append(f"{sector_name}: {weight}\n") formatted.append("\n") # Format top holdings if available holdings = etf_data.get("holdings", []) if holdings: formatted.append("Top Holdings:\n") # Show top 10 holdings for i, holding in enumerate(holdings[:10]): symbol = holding.get("symbol", "N/A") description = holding.get("description", "N/A") weight = holding.get("weight", "0") try: weight_pct = float(weight) * 100 formatted.append(f"{i+1:2d}. {symbol} - {description}: {weight_pct:.2f}%\n") except (ValueError, TypeError): formatted.append(f"{i+1:2d}. {symbol} - {description}: {weight}\n") if len(holdings) > 10: formatted.append(f"\n... and {len(holdings) - 10} more holdings\n") formatted.append(f"\nTotal Holdings: {len(holdings)}\n") return "".join(formatted) except Exception as e: return f"Error formatting ETF profile data: {str(e)}"
  • General helper function used by all tools to make authenticated HTTP requests to Alpha Vantage API, handles errors, rate limits, and parses responses (used with 'ETF_PROFILE' function for this tool).
    async def make_alpha_request(client: httpx.AsyncClient, function: str, symbol: Optional[str], additional_params: Optional[Dict[str, Any]] = None) -> Dict[str, Any] | str: """Make a request to the Alpha Vantage API with proper error handling. Args: client: An httpx AsyncClient instance function: The Alpha Vantage API function to call symbol: The stock/crypto symbol (can be None for some endpoints) additional_params: Additional parameters to include in the request Returns: Either a dictionary containing the API response, or a string with an error message """ params = { "function": function, "apikey": API_KEY } if symbol: params["symbol"] = symbol if additional_params: params.update(additional_params) try: response = await client.get( ALPHA_VANTAGE_BASE, params=params, timeout=30.0 ) # Check for specific error responses if response.status_code == 429: return f"Rate limit exceeded. Error details: {response.text}" elif response.status_code == 403: return f"API key invalid or expired. Error details: {response.text}" response.raise_for_status() # Check if response is empty if not response.text.strip(): return "Empty response received from Alpha Vantage API" # Special handling for EARNINGS_CALENDAR which returns CSV by default if function == "EARNINGS_CALENDAR": try: # Parse CSV response csv_reader = csv.DictReader(io.StringIO(response.text)) earnings_list = list(csv_reader) return earnings_list except Exception as e: return f"Error parsing CSV response: {str(e)}" # For other functions, expect JSON try: data = response.json() except ValueError as e: return f"Invalid JSON response from Alpha Vantage API: {response.text[:200]}" # Check for Alpha Vantage specific error messages if "Error Message" in data: return f"Alpha Vantage API error: {data['Error Message']}" if "Note" in data and "API call frequency" in data["Note"]: return f"Rate limit warning: {data['Note']}" return data except httpx.TimeoutException: return "Request timed out after 30 seconds. The Alpha Vantage API may be experiencing delays." except httpx.ConnectError: return "Failed to connect to Alpha Vantage API. Please check your internet connection." except httpx.HTTPStatusError as e: return f"HTTP error occurred: {str(e)} - Response: {e.response.text}" except Exception as e: return f"Unexpected error occurred: {str(e)}"

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/berlinbra/alpha-vantage-mcp'

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