Skip to main content
Glama

get_match_data

Retrieve detailed Dota 2 match information including player stats, scores, and game metrics for analysis or reference.

Instructions

Get detailed data for a specific match.

Args:
    match_id: ID of the match to retrieve

Returns:
    Detailed match information including players, scores, and stats

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
match_idYes

Implementation Reference

  • The core handler function for the 'get_match_data' tool. Decorated with @mcp.tool() for automatic registration in the FastMCP server. It fetches match data from the OpenDota API using make_opendota_request and formats it using format_match_data.
    @mcp.tool()
    async def get_match_data(match_id: int) -> str:
        """Get detailed data for a specific match.
    
        Args:
            match_id: ID of the match to retrieve
    
        Returns:
            Detailed match information including players, scores, and stats
        """
        match_data = await make_opendota_request(f"matches/{match_id}")
    
        if "error" in match_data:
            return f"Error retrieving match data: {match_data['error']}"
    
        return format_match_data(match_data)
  • Helper function that formats the raw match dictionary from the API into a detailed, human-readable string including match details, scores, teams, and per-player stats.
    def format_match_data(match: Dict[str, Any]) -> str:
        """Format match data into a readable string."""
        if not match or "match_id" not in match:
            return "Match data not found."
    
        # Basic match info
        match_id = match.get("match_id", "Unknown")
        duration = match.get("duration", 0)
        duration_formatted = format_duration(duration)
        start_time = format_timestamp(match.get("start_time", 0))
    
        game_mode = match.get("game_mode", "Unknown")
        radiant_win = match.get("radiant_win", False)
        winner = "Radiant" if radiant_win else "Dire"
    
        # Scores
        radiant_score = match.get("radiant_score", 0)
        dire_score = match.get("dire_score", 0)
    
        # Teams
        radiant_team_data = match.get("radiant_team", {})
        dire_team_data = match.get("dire_team", {})
    
        # Handle the case where these might be strings instead of dicts
        if isinstance(radiant_team_data, dict):
            radiant_team = radiant_team_data.get("name", "Radiant")
        else:
            radiant_team = "Radiant"
    
        if isinstance(dire_team_data, dict):
            dire_team = dire_team_data.get("name", "Dire")
        else:
            dire_team = "Dire"
    
        # Format players data
        player_data = []
        players = match.get("players", [])
    
        for player in players:
            account_id = player.get("account_id", "Anonymous")
            hero_id = player.get("hero_id", "Unknown")
            hero_name = player.get("hero_name", "Unknown Hero")
            kills = player.get("kills", 0)
            deaths = player.get("deaths", 0)
            assists = player.get("assists", 0)
            gpm = player.get("gold_per_min", 0)
            xpm = player.get("xp_per_min", 0)
            team = "Radiant" if player.get("player_slot", 0) < 128 else "Dire"
    
            player_data.append(
                f"Player ID: {account_id}\n"
                f"- Team: {team}\n"
                f"- Hero: {hero_name} (ID: {hero_id})\n"
                f"- K/D/A: {kills}/{deaths}/{assists}\n"
                f"- GPM/XPM: {gpm}/{xpm}"
            )
        joined_player_data = "\n\n".join(player_data)
        formatted_output = (
            f"Match ID: {match_id}\n"
            f"Date: {start_time}\n"
            f"Duration: {duration_formatted}\n"
            f"Game Mode: {game_mode}\n"
            f"Teams: {radiant_team} vs {dire_team}\n"
            f"Score: {radiant_score} - {dire_score}\n"
            f"Winner: {winner}\n\n"
            f"Player Details:\n"
            f"{'-' * 40}\n"
            f"{joined_player_data}"
        )
    
        return formatted_output
  • Core utility function used by get_match_data to perform API requests to OpenDota. Includes rate limiting, caching (5min TTL), retries, and comprehensive error handling.
    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)}"}
  • The @mcp.tool() decorator registers the get_match_data function as an MCP tool in the FastMCP server.
    @mcp.tool()

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