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