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
| Name | Required | Description | Default |
|---|---|---|---|
| year | Yes | ||
| round | No | ||
| type | No | ||
| driver_name | No | ||
| team_name | No |
Implementation Reference
- tools/standings/standings.py:9-166 (handler)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, )
- models/sessions/standings.py:1-45 (schema)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)
- tools/__init__.py:25-25 (registration)Re-export of get_standings from tools.standings submodule for easy import in server.py.from .standings import get_standings