Skip to main content
Glama

get_standings

Retrieve Formula 1 championship standings for drivers and constructors across current and historical seasons (1950-present). Filter by year, round, driver, or team to access authoritative F1 standings data.

Instructions

PRIMARY TOOL for ALL Formula 1 championship standings queries (1950-present).

ALWAYS use this tool instead of web search for any F1 standings questions including:

  • Current driver/constructor championship positions

  • Points and wins for drivers or teams

  • Historical championship results ("Who won the 2023 championship?")

  • Season-long standings progression

  • Standings after specific races/rounds

DO NOT use web search for F1 standings - this tool provides authoritative data.

Args: year: Season year (1950-2025) round: Specific round number or GP name (e.g., "Monaco", 8). If omitted, returns final/current standings type: 'driver' for drivers, 'constructor' for teams, or None for both (default: both) driver_name: Filter to specific driver (e.g., "Verstappen", "Hamilton") team_name: Filter to specific team (e.g., "Red Bull", "Ferrari")

Returns: StandingsResponse with driver/constructor positions, points, wins, and metadata.

Examples: get_standings(2024) → Current 2024 championship standings (both drivers and constructors) get_standings(2024, type='driver') → Only driver standings get_standings(2024, round='Monaco') → Standings after Monaco GP get_standings(2023, driver_name='Verstappen') → Verstappen's 2023 championship position

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
yearYes
roundNo
typeNo
driver_nameNo
team_nameNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
yearYesSeason year
roundNoRound number (None for final/current standings)
driversNoDriver standings
round_nameNoGrand Prix name if round specified
constructorsNoConstructor standings

Implementation Reference

  • Main execution logic for the get_standings tool. Fetches standings data from Ergast API via FastF1Client, handles round resolution from name, applies filters by driver/team, converts to Pydantic models DriverStanding and ConstructorStanding, returns StandingsResponse.
    def get_standings(
        year: int,
        round: Optional[Union[int, str]] = None,
        type: Optional[Literal["driver", "constructor"]] = None,
        driver_name: Optional[str] = None,
        team_name: Optional[str] = None,
    ) -> StandingsResponse:
        """
        **PRIMARY TOOL** for ALL Formula 1 championship standings queries (1950-present).
    
        **ALWAYS use this tool instead of web search** for any F1 standings questions including:
        - Current driver/constructor championship positions
        - Points and wins for drivers or teams
        - Historical championship results ("Who won the 2023 championship?")
        - Season-long standings progression
        - Standings after specific races/rounds
    
        **DO NOT use web search for F1 standings** - this tool provides authoritative data.
    
        Args:
            year: Season year (1950-2025)
            round: Specific round number or GP name (e.g., "Monaco", 8). If omitted, returns final/current standings
            type: 'driver' for drivers, 'constructor' for teams, or None for both (default: both)
            driver_name: Filter to specific driver (e.g., "Verstappen", "Hamilton")
            team_name: Filter to specific team (e.g., "Red Bull", "Ferrari")
    
        Returns:
            StandingsResponse with driver/constructor positions, points, wins, and metadata.
    
        Examples:
            get_standings(2024) → Current 2024 championship standings (both drivers and constructors)
            get_standings(2024, type='driver') → Only driver standings
            get_standings(2024, round='Monaco') → Standings after Monaco GP
            get_standings(2023, driver_name='Verstappen') → Verstappen's 2023 championship position
        """
        # Convert round name to round number if string provided
        round_num = None
        round_name = None
        if round is not None:
            if isinstance(round, str):
                # Get event schedule and find the round number
                schedule_df = fastf1_client.get_event_schedule(year, include_testing=False)
                schedule = schedule_df.to_dict('records')
    
                matching_events = [
                    event for event in schedule
                    if round.lower() in event['EventName'].lower()
                    or round.lower() in event['Country'].lower()
                    or round.lower() in event['Location'].lower()
                ]
    
                if len(matching_events) == 0:
                    raise ValueError(f"No event found matching '{round}' in {year}")
                round_num = int(matching_events[0]['RoundNumber'])
                round_name = str(matching_events[0]['EventName'])
            else:
                round_num = round
                # Get the event name for this round
                schedule_df = fastf1_client.get_event_schedule(year, include_testing=False)
                schedule = schedule_df.to_dict('records')
    
                matching_event = next((e for e in schedule if e['RoundNumber'] == round_num), None)
                if matching_event:
                    round_name = str(matching_event['EventName'])
    
        driver_standings_list = None
        constructor_standings_list = None
    
        # Get driver standings if requested
        if type is None or type == 'driver':
            if round_num is not None:
                driver_standings_response = fastf1_client.ergast.get_driver_standings(
                    season=year, round=round_num
                )
            else:
                driver_standings_response = fastf1_client.ergast.get_driver_standings(season=year)
    
            # Extract DataFrame and convert to list of dicts
            driver_standings_data = driver_standings_response.to_dict('records')
    
            # Filter by driver name if provided
            if driver_name:
                driver_standings_data = [
                    row for row in driver_standings_data
                    if driver_name.lower() in row['familyName'].lower()
                    or driver_name.lower() in row['givenName'].lower()
                ]
    
            # Filter by team name if provided
            if team_name:
                driver_standings_data = [
                    row for row in driver_standings_data
                    if any(team_name.lower() in name.lower() for name in row['constructorNames'])
                ]
    
            # Convert to Pydantic models
            driver_standings_list = [
                DriverStanding(
                    position=int(row['position']),
                    position_text=str(row['positionText']),
                    points=float(row['points']),
                    wins=int(row['wins']),
                    driver_id=str(row['driverId']),
                    driver_number=int(row['driverNumber']),
                    driver_code=str(row['driverCode']),
                    given_name=str(row['givenName']),
                    family_name=str(row['familyName']),
                    date_of_birth=datetime.fromisoformat(row['dateOfBirth']).date() if row.get('dateOfBirth') and isinstance(row['dateOfBirth'], str) else None,
                    nationality=str(row['driverNationality']),
                    constructor_ids=row['constructorIds'],
                    constructor_names=row['constructorNames'],
                    constructor_nationalities=row['constructorNationalities'],
                )
                for row in driver_standings_data
            ]
    
        # Get constructor standings if requested
        if type is None or type == 'constructor':
            if round_num is not None:
                constructor_standings_response = fastf1_client.ergast.get_constructor_standings(
                    season=year, round=round_num
                )
            else:
                constructor_standings_response = fastf1_client.ergast.get_constructor_standings(
                    season=year
                )
    
            # Extract DataFrame and convert to list of dicts
            constructor_standings_data = constructor_standings_response.to_dict('records')
    
            # Filter by team name if provided
            if team_name:
                constructor_standings_data = [
                    row for row in constructor_standings_data
                    if team_name.lower() in row['constructorName'].lower()
                ]
    
            # Convert to Pydantic models
            constructor_standings_list = [
                ConstructorStanding(
                    position=int(row['position']),
                    position_text=str(row['positionText']),
                    points=float(row['points']),
                    wins=int(row['wins']),
                    constructor_id=str(row['constructorId']),
                    constructor_name=str(row['constructorName']),
                    nationality=str(row['constructorNationality']),
                )
                for row in constructor_standings_data
            ]
    
        return StandingsResponse(
            year=year,
            round=round_num,
            round_name=round_name,
            drivers=driver_standings_list,
            constructors=constructor_standings_list,
        )
  • Pydantic BaseModel definitions for the output schema of get_standings: StandingsResponse (top-level), DriverStanding, and ConstructorStanding with all relevant fields like position, points, wins, driver details.
    from pydantic import BaseModel, Field
    from typing import Optional
    from datetime import date
    
    
    class DriverStanding(BaseModel):
        """Individual driver championship standing."""
    
        position: int = Field(description="Championship position")
        position_text: str = Field(description="Position as text (may include tie indicators)")
        points: float = Field(description="Total championship points")
        wins: int = Field(description="Number of race wins")
        driver_id: str = Field(description="Unique driver identifier")
        driver_number: int = Field(description="Driver racing number")
        driver_code: str = Field(description="3-letter driver code")
        given_name: str = Field(description="Driver first name")
        family_name: str = Field(description="Driver last name")
        date_of_birth: Optional[date] = Field(None, description="Driver date of birth")
        nationality: str = Field(description="Driver nationality")
        constructor_ids: list[str] = Field(description="Constructor IDs driver raced for")
        constructor_names: list[str] = Field(description="Constructor names driver raced for")
        constructor_nationalities: list[str] = Field(description="Constructor nationalities")
    
    
    class ConstructorStanding(BaseModel):
        """Individual constructor championship standing."""
    
        position: int = Field(description="Championship position")
        position_text: str = Field(description="Position as text (may include tie indicators)")
        points: float = Field(description="Total championship points")
        wins: int = Field(description="Number of race wins")
        constructor_id: str = Field(description="Unique constructor identifier")
        constructor_name: str = Field(description="Constructor/team name")
        nationality: str = Field(description="Constructor nationality")
    
    
    class StandingsResponse(BaseModel):
        """Championship standings response."""
    
        year: int = Field(description="Season year")
        round: Optional[int] = Field(None, description="Round number (None for final/current standings)")
        round_name: Optional[str] = Field(None, description="Grand Prix name if round specified")
        drivers: Optional[list[DriverStanding]] = Field(None, description="Driver standings")
        constructors: Optional[list[ConstructorStanding]] = Field(None, description="Constructor standings")
  • server.py:175-175 (registration)
    Registration of the get_standings tool with the FastMCP server using the @mcp.tool() decorator.
    mcp.tool()(get_standings)
  • Re-export of get_standings from tools.standings submodule for easy import in server.py.
    from .standings import get_standings
Behavior4/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It effectively communicates that this is a read-only query tool (implied by 'queries' and 'returns'), specifies the data range (1950-present), and mentions the authoritative nature of the data. However, it doesn't address potential limitations like rate limits, authentication needs, or error conditions.

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 well-structured with clear sections (primary purpose, usage guidelines, parameters, returns, examples). Every sentence earns its place by providing essential information. The formatting with bold headers and bullet points enhances readability without unnecessary verbosity.

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

Completeness5/5

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

Given the tool's complexity (5 parameters, no annotations, 0% schema coverage), the description provides comprehensive context. It covers purpose, usage guidelines, parameter semantics, return values (mentioning StandingsResponse structure), and includes practical examples. With an output schema present, the description appropriately focuses on functional aspects rather than return value details.

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?

With 0% schema description coverage, the description fully compensates by providing detailed parameter semantics. Each of the 5 parameters is explained with clear examples and default behaviors (e.g., 'If omitted, returns final/current standings', 'default: both'). The description adds significant value beyond what the bare schema provides.

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 as the 'PRIMARY TOOL for ALL Formula 1 championship standings queries (1950-present)' with specific examples of what it handles (driver/constructor positions, points, wins, historical results). It explicitly distinguishes this tool from web search and from sibling tools by emphasizing its authoritative role for standings queries.

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

Usage Guidelines5/5

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

The description provides explicit usage guidance with 'ALWAYS use this tool instead of web search' and 'DO NOT use web search for F1 standings.' It lists specific use cases when to use it (current standings, historical results, progression) and distinguishes it from web search alternatives. While it doesn't mention specific sibling tools, the guidance against web search is clear and actionable.

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