Skip to main content
Glama
rishijatia

Fantasy Premier League MCP Server

get_league_analytics

Analyze Fantasy Premier League mini-leagues to visualize performance trends, team compositions, and decision patterns for strategic insights.

Instructions

Get rich analytics for a Fantasy Premier League mini-league

    Returns visualization-optimized data for various types of league analysis.
    
    Args:
        league_id: ID of the league to analyze
        analysis_type: Type of analysis to perform:
            - "overview": General league overview (default)
            - "historical": Historical performance analysis
            - "team_composition": Team composition analysis
            - "decisions": Captain and transfer decision analysis
            - "fixtures": Fixture difficulty comparison
        start_gw: Starting gameweek (defaults to 1 or use "current-N" format)
        end_gw: Ending gameweek (defaults to current)
        
    Returns:
        Rich analytics data structured for visualization
    

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
league_idYes
analysis_typeNooverview
start_gwNo
end_gwNo

Implementation Reference

  • Main tool handler function 'get_league_analytics' decorated with @mcp.tool(). Includes type annotations serving as input schema and comprehensive docstring describing parameters and analysis types. Delegates to core implementation.
    async def get_league_analytics(
        league_id: int,
        analysis_type: str = "overview",
        start_gw: Optional[int] = None,
        end_gw: Optional[int] = None
    ) -> Dict[str, Any]:
        """Get rich analytics for a Fantasy Premier League mini-league
        
        Returns visualization-optimized data for various types of league analysis.
        
        Args:
            league_id: ID of the league to analyze
            analysis_type: Type of analysis to perform:
                - "overview": General league overview (default)
                - "historical": Historical performance analysis
                - "team_composition": Team composition analysis
                - "decisions": Captain and transfer decision analysis
                - "fixtures": Fixture difficulty comparison
            start_gw: Starting gameweek (defaults to 1 or use "current-N" format)
            end_gw: Ending gameweek (defaults to current)
            
        Returns:
            Rich analytics data structured for visualization
        """
        return await _get_league_analytics(league_id, analysis_type, start_gw, end_gw)
  • Core helper function '_get_league_analytics' containing the primary execution logic: input validation, gameweek processing, league data fetching, and routing to specialized analysis based on analysis_type.
    async def _get_league_analytics(
        league_id: int,
        analysis_type: str = "overview",
        start_gw: Optional[int] = None,
        end_gw: Optional[int] = None
    ) -> Dict[str, Any]:
        """
        Get rich analytics for a Fantasy Premier League mini-league
        
        Returns visualization-optimized data for various types of league analysis.
        
        Args:
            league_id: ID of the league to analyze
            analysis_type: Type of analysis to perform:
                - "overview": General league overview (default)
                - "historical": Historical performance analysis
                - "team_composition": Team composition analysis
                - "decisions": Captain and transfer decision analysis
                - "fixtures": Fixture difficulty comparison
            start_gw: Starting gameweek (defaults to 1)
            end_gw: Ending gameweek (defaults to current)
            
        Returns:
            Rich analytics data structured for visualization
        """
        # Add logging for debugging
        logger.info(f"Starting league analytics: {analysis_type} for league {league_id}")
        
        # Validate analysis type
        valid_types = ["overview", "historical", "team_composition", "decisions", "fixtures"]
        if analysis_type not in valid_types:
            return {
                "error": f"Invalid analysis type: {analysis_type}",
                "valid_types": valid_types
            }
        
        # Get current gameweek
        try:
            current_gw_data = await api.get_current_gameweek()
            current_gw = current_gw_data.get("id", 1)
            logger.info(f"Current gameweek: {current_gw}")
        except Exception as e:
            logger.error(f"Error getting current gameweek: {e}")
            current_gw = 1
        
        # Use the configured limit for all analysis types
        logger.info(f"Using configured limit of {LEAGUE_RESULTS_LIMIT} teams for {analysis_type} analysis")
        
        # Process gameweek range to ensure it's not too large
        effective_start_gw = start_gw
        effective_end_gw = end_gw
        
        # Handle start gameweek - using a consistent default (last 5 gameweeks) for all analysis types
        DEFAULT_GW_LOOKBACK = 5
        
        if effective_start_gw is None:
            effective_start_gw = max(1, current_gw - DEFAULT_GW_LOOKBACK + 1)
            logger.info(f"Using default start gameweek: {effective_start_gw}")
        elif isinstance(effective_start_gw, str) and effective_start_gw.startswith("current-"):
            try:
                offset = int(effective_start_gw.split("-")[1])
                effective_start_gw = max(1, current_gw - offset)
                logger.info(f"Parsed relative start gameweek: {effective_start_gw}")
            except ValueError:
                effective_start_gw = max(1, current_gw - DEFAULT_GW_LOOKBACK + 1)
                logger.info(f"Invalid relative start gameweek, using default: {effective_start_gw}")
        
        # Handle end gameweek
        if effective_end_gw is None or effective_end_gw == "current":
            effective_end_gw = current_gw
            logger.info(f"Using current end gameweek: {effective_end_gw}")
        elif isinstance(effective_end_gw, str) and effective_end_gw.startswith("current-"):
            try:
                offset = int(effective_end_gw.split("-")[1])
                effective_end_gw = max(1, current_gw - offset)
                logger.info(f"Parsed relative end gameweek: {effective_end_gw}")
            except ValueError:
                effective_end_gw = current_gw
                logger.info(f"Invalid relative end gameweek, using current: {effective_end_gw}")
        
        # Convert to integers if necessary
        try:
            effective_start_gw = int(effective_start_gw)
            effective_end_gw = int(effective_end_gw)
        except (ValueError, TypeError):
            logger.error(f"Invalid gameweek values: start={effective_start_gw}, end={effective_end_gw}")
            return {"error": "Invalid gameweek values"}
        
        # Ensure the range is valid and not too large
        if effective_start_gw < 1:
            effective_start_gw = 1
        if effective_end_gw > current_gw:
            effective_end_gw = current_gw
        if effective_start_gw > effective_end_gw:
            effective_start_gw, effective_end_gw = effective_end_gw, effective_start_gw
        
        # Apply consistent gameweek range limit to prevent performance issues
        gw_range = effective_end_gw - effective_start_gw + 1
        # MAX_GW_RANGE = 5  # Use a consistent max range for all analysis types
        
        # if gw_range > MAX_GW_RANGE:
        #     logger.info(f"Reducing gameweek range from {gw_range} to {MAX_GW_RANGE}")
        #     effective_start_gw = max(1, effective_end_gw - MAX_GW_RANGE + 1)
        
        # logger.info(f"Final gameweek range: {effective_start_gw} to {effective_end_gw}")
        
        # Get league standings first
        logger.info(f"Fetching league standings for league {league_id}")
        try:
            # Don't check size limit, just fetch all and filter
            league_data = await _get_league_standings(league_id)
            
            # Check for errors
            if "error" in league_data:
                logger.error(f"Error getting league standings: {league_data['error']}")
                return league_data
            
            logger.info(f"Successfully fetched standings for {len(league_data['standings'])} teams")
        except Exception as e:
            logger.error(f"Exception getting league standings: {e}")
            return {"error": f"Failed to get league standings: {str(e)}"}
        
        # Route to the appropriate analysis function (with timeout protection)
        try:
            if analysis_type == "overview" or analysis_type == "historical":
                # For overview analysis, use the regular function but with reduced range
                return await _get_league_historical_performance(
                    league_id, effective_start_gw, effective_end_gw
                )
                
            elif analysis_type == "team_composition":
                # For team composition, use specified gameweek
                # Previously this only used end_gw, but we'll now pass both for consistency
                return await _get_league_team_composition(
                    league_id, effective_end_gw
                )
                
            elif analysis_type == "decisions":
                # For decisions, use our new simplified analysis function
                return await get_simplified_league_decision_analysis(
                    league_id, effective_start_gw, effective_end_gw,
                    _get_league_standings, get_teams_historical_data,
                    league_data=league_data  # Pass league data to avoid fetching again
                )
                
            elif analysis_type == "fixtures":
                # Call the league fixture analysis function
                return await _get_league_fixture_analysis(
                    league_id, effective_start_gw, effective_end_gw
                )
        except Exception as e:
            logger.error(f"Error in league analytics: {e}")
            return {
                "error": f"Analysis failed: {str(e)}",
                "league_info": league_data["league_info"],
                "standings": league_data["standings"],
                "status": "error"
            }
        
        # This shouldn't happen due to earlier validation
        return {"error": "Unknown analysis type"}
  • Module-level registration function 'register_tools(mcp)' that defines and registers the 'get_league_analytics' tool (and get_league_standings) using the @mcp.tool() decorator. This function is imported and called from tools/__init__.py.
    def register_tools(mcp):
        """Register league analytics tools with the MCP server"""
        
        @mcp.tool()
        async def get_league_standings(league_id: int) -> Dict[str, Any]:
            """Get standings for a specified FPL league
            
            Args:
                league_id: ID of the league to fetch
                
            Returns:
                League information with standings and team details
            """
            # When directly using the tool, enforce size check
            return await _get_league_standings(league_id)
        
        @mcp.tool()
        async def get_league_analytics(
            league_id: int,
            analysis_type: str = "overview",
            start_gw: Optional[int] = None,
            end_gw: Optional[int] = None
        ) -> Dict[str, Any]:
            """Get rich analytics for a Fantasy Premier League mini-league
            
            Returns visualization-optimized data for various types of league analysis.
            
            Args:
                league_id: ID of the league to analyze
                analysis_type: Type of analysis to perform:
                    - "overview": General league overview (default)
                    - "historical": Historical performance analysis
                    - "team_composition": Team composition analysis
                    - "decisions": Captain and transfer decision analysis
                    - "fixtures": Fixture difficulty comparison
                start_gw: Starting gameweek (defaults to 1 or use "current-N" format)
                end_gw: Ending gameweek (defaults to current)
                
            Returns:
                Rich analytics data structured for visualization
            """
            return await _get_league_analytics(league_id, analysis_type, start_gw, end_gw)

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/rishijatia/fantasy-pl-mcp'

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