Skip to main content
Glama

get_qualifying_sessions

Retrieve Formula 1 qualifying session data by splitting Q1, Q2, and Q3 segments with lap details for specific Grand Prix events and seasons.

Instructions

Split qualifying into Q1, Q2, Q3 segments with lap data for each.

Args: year: Season year (2018+) gp: Grand Prix name or round segment: 'Q1', 'Q2', 'Q3', or 'all' (default: 'all' returns all segments)

Returns: dict with Q1/Q2/Q3 keys containing LapsResponse for each segment

Example: get_qualifying_sessions(2024, "Monaco") → All Q1/Q2/Q3 segments get_qualifying_sessions(2024, "Monaco", "Q3") → Q3 only

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
yearYes
gpYes
segmentNoall

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The core handler function for the 'get_qualifying_sessions' tool. Fetches qualifying session using FastF1Client, loads laps data, splits into Q1/Q2/Q3 using laps.split_qualifying_sessions(), converts to LapData models via helper, and packages into LapsResponse objects grouped by segment.
    def get_qualifying_sessions(
        year: int,
        gp: Union[str, int],
        segment: str = "all"
    ) -> dict[str, LapsResponse]:
        """
        Split qualifying into Q1, Q2, Q3 segments with lap data for each.
    
        Args:
            year: Season year (2018+)
            gp: Grand Prix name or round
            segment: 'Q1', 'Q2', 'Q3', or 'all' (default: 'all' returns all segments)
    
        Returns:
            dict with Q1/Q2/Q3 keys containing LapsResponse for each segment
    
        Example:
            get_qualifying_sessions(2024, "Monaco") → All Q1/Q2/Q3 segments
            get_qualifying_sessions(2024, "Monaco", "Q3") → Q3 only
        """
        session_obj = fastf1_client.get_session(year, gp, 'Q')
        session_obj.load(laps=True, telemetry=False, weather=False, messages=False)
    
        event = session_obj.event
        laps = session_obj.laps
    
        # Split qualifying into segments
        q1_laps, q2_laps, q3_laps = laps.split_qualifying_sessions()
    
        results = {}
    
        # Convert each segment to LapsResponse
        if segment in ["all", "Q1"] and q1_laps is not None and len(q1_laps) > 0:
            q1_list = [_row_to_lap_data(row) for idx, row in q1_laps.iterrows()]
            results["Q1"] = LapsResponse(
                session_name="Q1",
                event_name=event['EventName'],
                driver=None,
                lap_type="all",
                laps=q1_list,
                total_laps=len(q1_list)
            )
    
        if segment in ["all", "Q2"] and q2_laps is not None and len(q2_laps) > 0:
            q2_list = [_row_to_lap_data(row) for idx, row in q2_laps.iterrows()]
            results["Q2"] = LapsResponse(
                session_name="Q2",
                event_name=event['EventName'],
                driver=None,
                lap_type="all",
                laps=q2_list,
                total_laps=len(q2_list)
            )
    
        if segment in ["all", "Q3"] and q3_laps is not None and len(q3_laps) > 0:
            q3_list = [_row_to_lap_data(row) for idx, row in q3_laps.iterrows()]
            results["Q3"] = LapsResponse(
                session_name="Q3",
                event_name=event['EventName'],
                driver=None,
                lap_type="all",
                laps=q3_list,
                total_laps=len(q3_list)
            )
    
        return results
  • Supporting helper that maps pandas DataFrame row (from FastF1 laps) to the LapData Pydantic model used in the response.
    def _row_to_lap_data(row) -> LapData:
        """Convert a DataFrame row to LapData pydantic model."""
        return LapData(
            time=str(row['Time']) if pd.notna(row.get('Time')) else None,
            driver=str(row['Driver']) if pd.notna(row.get('Driver')) else "",
            driver_number=str(row['DriverNumber']) if pd.notna(row.get('DriverNumber')) else "",
            lap_time=str(row['LapTime']) if pd.notna(row.get('LapTime')) else None,
            lap_number=int(row['LapNumber']) if pd.notna(row.get('LapNumber')) else 0,
            stint=int(row['Stint']) if pd.notna(row.get('Stint')) else None,
            pit_out_time=str(row['PitOutTime']) if pd.notna(row.get('PitOutTime')) else None,
            pit_in_time=str(row['PitInTime']) if pd.notna(row.get('PitInTime')) else None,
            sector_1_time=str(row['Sector1Time']) if pd.notna(row.get('Sector1Time')) else None,
            sector_2_time=str(row['Sector2Time']) if pd.notna(row.get('Sector2Time')) else None,
            sector_3_time=str(row['Sector3Time']) if pd.notna(row.get('Sector3Time')) else None,
            sector_1_session_time=str(row['Sector1SessionTime']) if pd.notna(row.get('Sector1SessionTime')) else None,
            sector_2_session_time=str(row['Sector2SessionTime']) if pd.notna(row.get('Sector2SessionTime')) else None,
            sector_3_session_time=str(row['Sector3SessionTime']) if pd.notna(row.get('Sector3SessionTime')) else None,
            speed_i1=float(row['SpeedI1']) if pd.notna(row.get('SpeedI1')) else None,
            speed_i2=float(row['SpeedI2']) if pd.notna(row.get('SpeedI2')) else None,
            speed_fl=float(row['SpeedFL']) if pd.notna(row.get('SpeedFL')) else None,
            speed_st=float(row['SpeedST']) if pd.notna(row.get('SpeedST')) else None,
            is_personal_best=bool(row['IsPersonalBest']) if pd.notna(row.get('IsPersonalBest')) else None,
            compound=str(row['Compound']) if pd.notna(row.get('Compound')) else None,
            tyre_life=float(row['TyreLife']) if pd.notna(row.get('TyreLife')) else None,
            fresh_tyre=bool(row['FreshTyre']) if pd.notna(row.get('FreshTyre')) else None,
            team=str(row['Team']) if pd.notna(row.get('Team')) else None,
            lap_start_time=str(row['LapStartTime']) if pd.notna(row.get('LapStartTime')) else None,
            lap_start_date=str(row['LapStartDate']) if pd.notna(row.get('LapStartDate')) else None,
            track_status=str(row['TrackStatus']) if pd.notna(row.get('TrackStatus')) else None,
            position=float(row['Position']) if pd.notna(row.get('Position')) else None,
            deleted=bool(row['Deleted']) if pd.notna(row.get('Deleted')) else None,
            deleted_reason=str(row['DeletedReason']) if pd.notna(row.get('DeletedReason')) else None,
            fast_f1_generated=bool(row['FastF1Generated']) if pd.notna(row.get('FastF1Generated')) else None,
            is_accurate=bool(row['IsAccurate']) if pd.notna(row.get('IsAccurate')) else None,
        )
  • server.py:154-154 (registration)
    MCP tool registration decorator applied to the get_qualifying_sessions handler function.
    mcp.tool()(get_qualifying_sessions)
  • tools/__init__.py:8-8 (registration)
    Import of get_qualifying_sessions into the main tools namespace, enabling server.py import.
    get_qualifying_sessions,
  • Import of the handler from its implementation file into the session tools submodule.
    from .qualifying import get_qualifying_sessions
Behavior3/5

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

With no annotations provided, the description carries the full burden. It discloses that the tool returns segmented lap data and specifies default behavior ('all' returns all segments), but lacks details on error handling, data freshness, rate limits, or authentication needs. It adequately describes core behavior but misses operational traits.

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 appropriately sized and front-loaded: the first sentence states the purpose, followed by structured sections for Args, Returns, and Examples. Every sentence earns its place with no wasted words, making it easy to scan and understand.

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

Completeness4/5

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

Given the tool's moderate complexity, no annotations, and an output schema (implied by 'Returns' section), the description is mostly complete. It covers purpose, parameters, returns, and examples, but could benefit from mentioning limitations (e.g., year range enforcement) or error cases to be fully comprehensive.

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?

Schema description coverage is 0%, so the description must compensate fully. It adds significant meaning beyond the schema: explains 'year' as 'Season year (2018+)', 'gp' as 'Grand Prix name or round', and 'segment' with allowed values and default behavior. This provides complete parameter semantics not in the schema.

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 tool's purpose: 'Split qualifying into Q1, Q2, Q3 segments with lap data for each.' It specifies the verb ('split'), resource ('qualifying'), and output structure ('segments with lap data'), distinguishing it from siblings like get_laps or get_session_results which handle different data types.

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

Usage Guidelines4/5

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

The description provides clear context for usage through examples and parameter explanations, showing when to use specific segment values. However, it does not explicitly state when to use this tool versus alternatives like get_laps or get_session_results, which might overlap in functionality for qualifying data.

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

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