Skip to main content
Glama

get_hero_stats

Retrieve Dota 2 hero statistics including win rates across different skill brackets. Specify a hero ID for individual data or get comprehensive stats for all heroes.

Instructions

Get statistics for heroes.

Args:
    hero_id: Optional hero ID to get stats for a specific hero

Returns:
    Hero statistics including win rates by skill bracket

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
hero_idNo

Implementation Reference

  • The primary handler function for the 'get_hero_stats' tool. It fetches hero statistics from the OpenDota API endpoint 'heroStats', optionally filters by hero_id, computes win rates across skill brackets and pro scene, and returns a formatted string summary.
    @mcp.tool()
    async def get_hero_stats(hero_id: Optional[int] = None) -> str:
        """Get statistics for heroes.
    
        Args:
            hero_id: Optional hero ID to get stats for a specific hero
    
        Returns:
            Hero statistics including win rates by skill bracket
        """
        hero_stats = await make_opendota_request("heroStats")
    
        if "error" in hero_stats:
            return f"Error retrieving hero stats: {hero_stats['error']}"
    
        if hero_id is not None:
            # Filter for specific hero
            hero_stats = [
                hero
                for hero in hero_stats
                if hero.get("id") == hero_id or hero.get("hero_id") == hero_id
            ]
    
            if not hero_stats:
                return f"No stats found for hero ID {hero_id}."
    
            hero = hero_stats[0]
            localized_name = hero.get("localized_name", f"Hero {hero_id}")
    
            # Calculate win rates by bracket
            brackets = [
                "herald",
                "guardian",
                "crusader",
                "archon",
                "legend",
                "ancient",
                "divine",
                "immortal",
            ]
            bracket_stats = []
    
            for i, bracket in enumerate(brackets, 1):
                picks = hero.get(f"{i}_pick", 0)
                wins = hero.get(f"{i}_win", 0)
                win_rate = (wins / picks * 100) if picks > 0 else 0
                bracket_stats.append(
                    f"{bracket.capitalize()}: {win_rate:.2f}% ({wins}/{picks})"
                )
    
            # Pro stats
            pro_picks = hero.get("pro_pick", 0)
            pro_wins = hero.get("pro_win", 0)
            pro_win_rate = (pro_wins / pro_picks * 100) if pro_picks > 0 else 0
            pro_ban_rate = hero.get("pro_ban", 0)
    
            # Hero attributes
            roles = hero.get("roles", [])
            primary_attr = hero.get("primary_attr", "Unknown")
            attack_type = hero.get("attack_type", "Unknown")
    
            return (
                f"Hero Stats for {localized_name} (ID: {hero_id}):\n\n"
                f"Roles: {', '.join(roles)}\n"
                f"Primary Attribute: {primary_attr}\n"
                f"Attack Type: {attack_type}\n\n"
                f"Win Rates by Bracket:\n"
                f"{', '.join(bracket_stats)}\n\n"
                f"Pro Scene:\n"
                f"Pick Rate: {pro_picks} picks\n"
                f"Win Rate: {pro_win_rate:.2f}% ({pro_wins}/{pro_picks})\n"
                f"Ban Rate: {pro_ban_rate} bans"
            )
        else:
            # Return summary of all heroes
            formatted_heroes = []
    
            for hero in sorted(hero_stats, key=lambda x: x.get("localized_name", "")):
                localized_name = hero.get("localized_name", f"Hero {hero.get('id')}")
    
                # Calculate overall win rate
                total_picks = sum(hero.get(f"{i}_pick", 0) for i in range(1, 9))
                total_wins = sum(hero.get(f"{i}_win", 0) for i in range(1, 9))
                win_rate = (total_wins / total_picks * 100) if total_picks > 0 else 0
    
                formatted_heroes.append(f"{localized_name}: {win_rate:.2f}% win rate")
    
            return "Hero Win Rates:\n\n" + "\n".join(formatted_heroes)
  • The @mcp.tool() decorator registers the get_hero_stats function as an MCP tool with FastMCP.
    @mcp.tool()
  • Function signature and docstring define the input schema (optional hero_id: int) and output (str description). Schema is inferred by FastMCP from type hints and docstring.
    async def get_hero_stats(hero_id: Optional[int] = None) -> str:
        """Get statistics for heroes.
    
        Args:
            hero_id: Optional hero ID to get stats for a specific hero
    
        Returns:
  • Core helper function used by get_hero_stats to make API requests to OpenDota 'heroStats' endpoint with caching and rate limiting.
    async def make_opendota_request(
        endpoint: str, params: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """Make a request to the OpenDota API with proper error handling and caching."""
        # Apply rate limiting
        await apply_rate_limit()
    
        url = f"{OPENDOTA_API_BASE}/{endpoint}"
        request_params = API_PARAMS.copy()
        if params:
            request_params.update(params)
    
        # Create a cache key manually
        cache_key = endpoint
        if request_params:
            param_str = "&".join(f"{k}={v}" for k, v in sorted(request_params.items()))
            cache_key = f"{endpoint}?{param_str}"
    
        # Check cache
        cache_entry = api_cache.get(cache_key)
        if cache_entry:
            timestamp, data = cache_entry
            if time.time() - timestamp < CACHE_TTL:
                logger.debug(f"Cache hit for {cache_key}")
                return data
    
        logger.info(f"Making request to {endpoint} with params {request_params}")
    
        async with httpx.AsyncClient() as client:
            try:
                response = await client.get(
                    url,
                    params=request_params,
                    headers={"User-Agent": USER_AGENT},
                    timeout=10.0,
                )
                response.raise_for_status()
                data = response.json()
    
                # Cache the response
                api_cache[cache_key] = (time.time(), data)
    
                return data
            except httpx.HTTPStatusError as e:
                if e.response.status_code == 429:
                    logger.error(f"Rate limit exceeded for {endpoint}")
                    return {
                        "error": "Rate limit exceeded. Consider using an API key for more requests."
                    }
                if e.response.status_code == 404:
                    logger.error(f"Resource not found: {endpoint}")
                    return {"error": "Not found. The requested resource doesn't exist."}
                if e.response.status_code >= 500:
                    logger.error(f"OpenDota API server error: {e.response.status_code}")
                    return {"error": "OpenDota API server error. Please try again later."}
                logger.error(
                    f"HTTP error {e.response.status_code} for {endpoint}: {e.response.text}"
                )
                return {"error": f"HTTP error {e.response.status_code}: {e.response.text}"}
            except Exception as e:
                logger.error(f"Unexpected error for {endpoint}: {str(e)}")
                return {"error": f"Unexpected error: {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/asusevski/opendota-mcp-server'

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