Skip to main content
Glama

get_analysis

Analyze Formula 1 race data to assess driver performance metrics including pace, tire degradation, stint summaries, and consistency across specified sessions and seasons.

Instructions

Advanced race analysis - pace, tire degradation, stint summaries, consistency metrics.

Args: year: Season year (2018+) gp: Grand Prix name or round session: 'FP1', 'FP2', 'FP3', 'Q', 'S', 'R' analysis_type: 'race_pace', 'tire_degradation', 'stint_summary', 'consistency' driver: Driver code/number (optional, all drivers if None)

Returns: AnalysisResponse with pace data, degradation, stints, or consistency stats

Examples: get_analysis(2024, "Monaco", "R", "race_pace") → Pace analysis for all drivers get_analysis(2024, "Monza", "R", "tire_degradation", driver="VER") → VER's tire wear

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
yearYes
gpYes
sessionYes
analysis_typeYes
driverNo

Implementation Reference

  • The core handler function implementing get_analysis tool. Loads FastF1 session data and computes race pace (avg/median/fastest laps), tire degradation per stint, stint summaries, and lap time consistency (std dev, CV) for specified or all drivers.
    def get_analysis( year: int, gp: Union[str, int], session: str, analysis_type: Literal["race_pace", "tire_degradation", "stint_summary", "consistency"], driver: Optional[Union[str, int]] = None, ) -> AnalysisResponse: """ Advanced race analysis - pace, tire degradation, stint summaries, consistency metrics. Args: year: Season year (2018+) gp: Grand Prix name or round session: 'FP1', 'FP2', 'FP3', 'Q', 'S', 'R' analysis_type: 'race_pace', 'tire_degradation', 'stint_summary', 'consistency' driver: Driver code/number (optional, all drivers if None) Returns: AnalysisResponse with pace data, degradation, stints, or consistency stats Examples: get_analysis(2024, "Monaco", "R", "race_pace") → Pace analysis for all drivers get_analysis(2024, "Monza", "R", "tire_degradation", driver="VER") → VER's tire wear """ # Load session with lap data session_obj = fastf1_client.get_session(year, gp, session) session_obj.load(laps=True, telemetry=False, weather=False, messages=False) event = session_obj.event # Get laps based on driver filter if driver: laps = session_obj.laps.pick_drivers(driver) else: laps = session_obj.laps if analysis_type == "race_pace": # Calculate race pace (excluding pit laps and inaccurate laps) race_pace_list = [] if driver: drivers_to_analyze = [driver] else: drivers_to_analyze = laps['Driver'].unique() for drv in drivers_to_analyze: try: driver_laps = laps.pick_drivers(drv) # Filter for clean laps: no pit stops, accurate timing, not deleted clean_laps = driver_laps[ (pd.isna(driver_laps['PitInTime'])) & (pd.isna(driver_laps['PitOutTime'])) & (driver_laps['IsAccurate'] == True) & (driver_laps['Deleted'] == False) ] if len(clean_laps) > 0: # Convert lap times to seconds for calculation lap_times_seconds = clean_laps['LapTime'].dt.total_seconds() avg_time = lap_times_seconds.mean() median_time = lap_times_seconds.median() fastest_time = lap_times_seconds.min() race_pace_list.append( RacePaceData( driver=str(clean_laps.iloc[0]['Driver']), driver_number=str(clean_laps.iloc[0]['DriverNumber']), average_lap_time=str(pd.Timedelta(seconds=avg_time)), median_lap_time=str(pd.Timedelta(seconds=median_time)), fastest_lap_time=str(pd.Timedelta(seconds=fastest_time)), total_laps=len(driver_laps), clean_laps=len(clean_laps), ) ) except Exception: continue return AnalysisResponse( session_name=session_obj.name, event_name=event['EventName'], year=year, analysis_type=analysis_type, race_pace=race_pace_list, total_records=len(race_pace_list), driver_filter=str(driver) if driver else None, ) elif analysis_type == "tire_degradation": # Analyze tire degradation per stint degradation_list = [] if driver: drivers_to_analyze = [driver] else: drivers_to_analyze = laps['Driver'].unique() for drv in drivers_to_analyze: try: driver_laps = laps.pick_drivers(drv) # Group by stint stints = driver_laps['Stint'].unique() for stint in stints: if pd.notna(stint): stint_laps = driver_laps[driver_laps['Stint'] == stint] # Filter clean laps for analysis clean_stint_laps = stint_laps[ (pd.isna(stint_laps['PitInTime'])) & (pd.isna(stint_laps['PitOutTime'])) & (stint_laps['IsAccurate'] == True) & (stint_laps['Deleted'] == False) ] if len(clean_stint_laps) >= 2: first_lap = clean_stint_laps.iloc[0] last_lap = clean_stint_laps.iloc[-1] first_time = first_lap['LapTime'].total_seconds() if pd.notna(first_lap['LapTime']) else None last_time = last_lap['LapTime'].total_seconds() if pd.notna(last_lap['LapTime']) else None degradation = None if first_time and last_time: deg_seconds = last_time - first_time degradation = str(pd.Timedelta(seconds=deg_seconds)) avg_time = clean_stint_laps['LapTime'].dt.total_seconds().mean() degradation_list.append( TireDegradationData( driver=str(first_lap['Driver']), driver_number=str(first_lap['DriverNumber']), stint=int(stint), compound=str(first_lap['Compound']) if pd.notna(first_lap.get('Compound')) else None, first_lap_time=str(first_lap['LapTime']) if pd.notna(first_lap['LapTime']) else None, last_lap_time=str(last_lap['LapTime']) if pd.notna(last_lap['LapTime']) else None, average_lap_time=str(pd.Timedelta(seconds=avg_time)), degradation=degradation, stint_length=len(clean_stint_laps), ) ) except Exception: continue return AnalysisResponse( session_name=session_obj.name, event_name=event['EventName'], year=year, analysis_type=analysis_type, tire_degradation=degradation_list, total_records=len(degradation_list), driver_filter=str(driver) if driver else None, ) elif analysis_type == "stint_summary": # Summarize each stint stint_summaries_list = [] if driver: drivers_to_analyze = [driver] else: drivers_to_analyze = laps['Driver'].unique() for drv in drivers_to_analyze: try: driver_laps = laps.pick_drivers(drv) # Group by stint stints = driver_laps['Stint'].unique() for stint in stints: if pd.notna(stint): stint_laps = driver_laps[driver_laps['Stint'] == stint] # Filter clean laps clean_stint_laps = stint_laps[ (pd.isna(stint_laps['PitInTime'])) & (pd.isna(stint_laps['PitOutTime'])) & (stint_laps['IsAccurate'] == True) ] if len(clean_stint_laps) > 0: avg_time = clean_stint_laps['LapTime'].dt.total_seconds().mean() fastest_time = clean_stint_laps['LapTime'].dt.total_seconds().min() stint_summaries_list.append( StintSummary( driver=str(clean_stint_laps.iloc[0]['Driver']), driver_number=str(clean_stint_laps.iloc[0]['DriverNumber']), stint=int(stint), compound=str(clean_stint_laps.iloc[0]['Compound']) if pd.notna(clean_stint_laps.iloc[0].get('Compound')) else None, stint_length=len(clean_stint_laps), average_lap_time=str(pd.Timedelta(seconds=avg_time)), fastest_lap_time=str(pd.Timedelta(seconds=fastest_time)), ) ) except Exception: continue return AnalysisResponse( session_name=session_obj.name, event_name=event['EventName'], year=year, analysis_type=analysis_type, stint_summaries=stint_summaries_list, total_records=len(stint_summaries_list), driver_filter=str(driver) if driver else None, ) elif analysis_type == "consistency": # Analyze driver consistency consistency_list = [] if driver: drivers_to_analyze = [driver] else: drivers_to_analyze = laps['Driver'].unique() for drv in drivers_to_analyze: try: driver_laps = laps.pick_drivers(drv) # Filter clean laps clean_laps = driver_laps[ (pd.isna(driver_laps['PitInTime'])) & (pd.isna(driver_laps['PitOutTime'])) & (driver_laps['IsAccurate'] == True) & (driver_laps['Deleted'] == False) ] if len(clean_laps) >= 3: # Need at least 3 laps for meaningful stats lap_times_seconds = clean_laps['LapTime'].dt.total_seconds() avg_time = lap_times_seconds.mean() std_dev = lap_times_seconds.std() coefficient_of_variation = (std_dev / avg_time) * 100 if avg_time > 0 else None consistency_list.append( ConsistencyData( driver=str(clean_laps.iloc[0]['Driver']), driver_number=str(clean_laps.iloc[0]['DriverNumber']), average_lap_time=str(pd.Timedelta(seconds=avg_time)), std_deviation=float(std_dev), coefficient_of_variation=float(coefficient_of_variation) if coefficient_of_variation else None, total_laps=len(clean_laps), ) ) except Exception: continue # Sort by coefficient of variation (most consistent first) consistency_list.sort(key=lambda x: x.coefficient_of_variation if x.coefficient_of_variation else 999) return AnalysisResponse( session_name=session_obj.name, event_name=event['EventName'], year=year, analysis_type=analysis_type, consistency=consistency_list, total_records=len(consistency_list), driver_filter=str(driver) if driver else None, )
  • Pydantic BaseModel schemas defining the structured output for get_analysis tool responses, including main AnalysisResponse and specialized data classes for each analysis type.
    from pydantic import BaseModel, Field from typing import Optional class RacePaceData(BaseModel): """Race pace analysis data.""" driver: str = Field(..., description="Driver abbreviation") driver_number: str = Field(..., description="Driver number") average_lap_time: Optional[str] = Field(None, description="Average lap time (excluding pit laps)") median_lap_time: Optional[str] = Field(None, description="Median lap time") fastest_lap_time: Optional[str] = Field(None, description="Fastest lap time") total_laps: int = Field(..., description="Total number of laps") clean_laps: int = Field(..., description="Number of clean laps (no pit stops, accurate timing)") class TireDegradationData(BaseModel): """Tire degradation analysis data.""" driver: str = Field(..., description="Driver abbreviation") driver_number: str = Field(..., description="Driver number") stint: int = Field(..., description="Stint number") compound: Optional[str] = Field(None, description="Tire compound") first_lap_time: Optional[str] = Field(None, description="First lap time on this stint") last_lap_time: Optional[str] = Field(None, description="Last lap time on this stint") average_lap_time: Optional[str] = Field(None, description="Average lap time for stint") degradation: Optional[str] = Field(None, description="Estimated degradation (last - first lap)") stint_length: int = Field(..., description="Number of laps in stint") class StintSummary(BaseModel): """Summary of a tire stint.""" driver: str = Field(..., description="Driver abbreviation") driver_number: str = Field(..., description="Driver number") stint: int = Field(..., description="Stint number") compound: Optional[str] = Field(None, description="Tire compound") stint_length: int = Field(..., description="Number of laps in stint") average_lap_time: Optional[str] = Field(None, description="Average lap time") fastest_lap_time: Optional[str] = Field(None, description="Fastest lap in stint") class ConsistencyData(BaseModel): """Driver consistency analysis.""" driver: str = Field(..., description="Driver abbreviation") driver_number: str = Field(..., description="Driver number") average_lap_time: Optional[str] = Field(None, description="Average lap time") std_deviation: Optional[float] = Field(None, description="Standard deviation of lap times (seconds)") coefficient_of_variation: Optional[float] = Field(None, description="Consistency score (lower is better)") total_laps: int = Field(..., description="Total laps analyzed") class AnalysisResponse(BaseModel): """Response containing advanced race analysis.""" session_name: str = Field(..., description="Session name") event_name: str = Field(..., description="Event name") year: int = Field(..., description="Season year") analysis_type: str = Field(..., description="Type: 'race_pace', 'tire_degradation', 'stint_summary', 'consistency'") # Optional data based on type race_pace: Optional[list[RacePaceData]] = Field(None, description="Race pace data") tire_degradation: Optional[list[TireDegradationData]] = Field(None, description="Tire degradation data") stint_summaries: Optional[list[StintSummary]] = Field(None, description="Stint summary data") consistency: Optional[list[ConsistencyData]] = Field(None, description="Consistency data") # Metadata total_records: int = Field(..., description="Total number of records") driver_filter: Optional[str] = Field(None, description="Driver filter (if any)")
  • server.py:160-160 (registration)
    Registers the get_analysis handler as an MCP tool using the FastMCP decorator.
    mcp.tool()(get_analysis)

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/praneethravuri/pitstop'

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