Skip to main content
Glama
ferdousbhai

WSB Analyst MCP Server

get_top_trending_tickers

Fetch trending stock tickers from ApeWisdom, filtered by valid NASDAQ symbols and WallStreetBets categories to identify market-relevant securities.

Instructions

Fetch top trending stock tickers from ApeWisdom, filtered by valid NASDAQ symbols.

Args:
    num_stocks: Number of top stocks to consider based on upvotes and mentions. Defaults to 20.
    filter: ApeWisdom filter category (e.g., 'wallstreetbets', 'all'). Defaults to 'wallstreetbets'.

Returns:
    A list of valid, trending tickers.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
num_stocksNo
filterNowallstreetbets

Implementation Reference

  • The primary handler function decorated with @mcp.tool() for automatic registration. It fetches top trending tickers from the ApeWisdom API based on upvotes and mentions, collects potential tickers, and validates them against NASDAQ listed symbols using the filter_valid_tickers helper. Returns a list of unique valid tickers.
    @mcp.tool()
    async def get_top_trending_tickers(num_stocks: int = 20, filter: str = "wallstreetbets") -> list[str]:
        """
        Fetch top trending stock tickers from ApeWisdom, filtered by valid NASDAQ symbols.
    
        Args:
            num_stocks: Number of top stocks to consider based on upvotes and mentions. Defaults to 20.
            filter: ApeWisdom filter category (e.g., 'wallstreetbets', 'all'). Defaults to 'wallstreetbets'.
    
        Returns:
            A list of valid, trending tickers.
        """
        api_url = f"https://apewisdom.io/api/v1.0/filter/{filter}"
        logger.info(f"Fetching trending tickers from {api_url}")
    
        async with httpx.AsyncClient() as client:
            try:
                response = await client.get(api_url)
                response.raise_for_status() # Raise exception for bad status codes
                data = response.json()
                logger.info(f"Successfully fetched data from ApeWisdom for filter '{filter}'")
            except httpx.RequestError as e:
                logger.error(f"Error fetching from ApeWisdom: {e}")
                return []
            except httpx.HTTPStatusError as e:
                 logger.error(f"ApeWisdom API returned error {e.response.status_code}: {e}")
                 return []
            except Exception as e: # Catch potential JSON decoding errors etc.
                logger.error(f"An unexpected error occurred fetching/parsing ApeWisdom data: {e}")
                return []
    
        if "results" not in data or not isinstance(data["results"], list):
             logger.error("Invalid data structure received from ApeWisdom API.")
             return []
    
        potential_tickers = set()
        metrics = ["upvotes", "mentions"]
        for metric in metrics:
            # Sort by the current metric and take top N
            # Add try-except for robustness against missing keys or non-numeric values
            try:
                sorted_stocks = sorted(
                    [stock for stock in data["results"] if isinstance(stock.get(metric), (int, float))],
                    key=lambda item: item.get(metric, 0),
                    reverse=True
                )
                for stock in sorted_stocks[:num_stocks]:
                     if "ticker" in stock:
                         potential_tickers.add(stock["ticker"])
            except Exception as e:
                 logger.warning(f"Could not process metric '{metric}' due to error: {e}")
    
        if not potential_tickers:
            logger.info("No potential tickers found after processing ApeWisdom results.")
            return []
    
        logger.info(f"Found {len(potential_tickers)} potential tickers, filtering...")
    
        # Filter valid tickers
        valid_tickers = await filter_valid_tickers(list(potential_tickers))
        logger.info(f"Filtered down to {len(valid_tickers)} valid trending tickers.")
    
        return valid_tickers
  • Helper function used by get_top_trending_tickers to validate potential tickers against official NASDAQ listed symbols by fetching and parsing nasdaqlisted.txt and otherlisted.txt files, returning only valid uppercase tickers.
    async def filter_valid_tickers(tickers: list[str]) -> list[str]:
        """Filter out invalid tickers using NASDAQ's symbol list."""
        async with httpx.AsyncClient() as client:
            try:
                nasdaq_response = await client.get("https://www.nasdaqtrader.com/dynamic/SymDir/nasdaqlisted.txt")
                other_response = await client.get("https://www.nasdaqtrader.com/dynamic/SymDir/otherlisted.txt")
                nasdaq_response.raise_for_status()
                other_response.raise_for_status()
            except httpx.RequestError as e:
                logger.error(f"Error fetching NASDAQ ticker lists: {e}")
                return list(set(ticker.upper() for ticker in tickers))
            except httpx.HTTPStatusError as e:
                 logger.error(f"Error fetching NASDAQ ticker lists (status {e.response.status_code}): {e}")
                 return list(set(ticker.upper() for ticker in tickers))
    
            valid_symbols = set()
            try:
                for line in nasdaq_response.text.splitlines()[1:-1]:  # Skip header and footer
                    parts = line.split('|')
                    if len(parts) > 0:
                        symbol = parts[0]
                        valid_symbols.add(symbol)
    
                for line in other_response.text.splitlines()[1:-1]:  # Skip header and footer
                     parts = line.split('|')
                     if len(parts) > 0:
                        symbol = parts[0]
                        valid_symbols.add(symbol)
            except Exception as e:
                logger.error(f"Error parsing NASDAQ ticker lists: {e}")
                return list(set(ticker.upper() for ticker in tickers))
    
        unique_tickers = set(ticker.upper() for ticker in tickers)
        return [ticker for ticker in unique_tickers if ticker in valid_symbols]
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions fetching and filtering but lacks details on rate limits, authentication needs, error handling, or data freshness. For a tool interacting with external data sources, this omission leaves significant behavioral aspects unclear to the agent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficiently structured with a clear purpose statement followed by labeled sections for Args and Returns. Each sentence adds value without redundancy, making it easy to parse and front-loaded with the core functionality, demonstrating excellent conciseness and organization.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 2 parameters, no annotations, and no output schema, the description covers purpose and parameters well but lacks behavioral context and usage guidelines. It's adequate for basic understanding but incomplete given the external data source complexity, as it doesn't address reliability or integration aspects that an agent might need.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Given 0% schema description coverage, the description fully compensates by explaining both parameters clearly. It defines 'num_stocks' as 'Number of top stocks to consider based on upvotes and mentions' and 'filter' with examples like 'wallstreetbets' and 'all', adding essential context beyond the bare schema, which only lists types and defaults.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Fetch top trending stock tickers'), source ('from ApeWisdom'), and key constraint ('filtered by valid NASDAQ symbols'). It distinguishes itself from sibling tools by focusing on ticker retrieval rather than post details or external links, making the purpose immediately understandable and distinct.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. While it mentions filtering by NASDAQ symbols, it doesn't specify scenarios where this tool is preferred over sibling tools like 'find_top_posts' or 'fetch_detailed_wsb_posts', leaving the agent without context for selection among available options.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/ferdousbhai/wsb-analyst-mcp'

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