f1-mcp-server / app.py
arre99's picture
removed intermediate function for OpenF1 Gradio tools. Now the display name for the MCP tools should be correct in the MCP host
070bf41
raw
history blame
8.46 kB
import gradio as gr
# Local modules
import fastf1_tools
import openf1_tools
from utils.constants import (
DRIVER_NAMES,
CONSTRUCTOR_NAMES,
CURRENT_YEAR,
DROPDOWN_SESSION_TYPES,
MARKDOWN_INTRODUCTION,
MARKDOWN_OPENF1_EXAMPLES,
OPENF1_TOOL_DESCRIPTION
)
iface_driver_championship_standings = gr.Interface(
fn=fastf1_tools.driver_championship_standings,
inputs=[
gr.Number(label="Calendar year", value=CURRENT_YEAR, minimum=1950, maximum=CURRENT_YEAR),
gr.Dropdown(label="Driver", choices=DRIVER_NAMES)
],
outputs="text",
title="World Driver Championship Standings",
description="Get the world driver championship standings for a specific driver"
)
iface_constructor_championship_standings = gr.Interface(
fn=fastf1_tools.constructor_championship_standings,
inputs=[
gr.Number(label="Calendar year", value=CURRENT_YEAR, minimum=1950, maximum=CURRENT_YEAR),
gr.Dropdown(label="Constructor", choices=CONSTRUCTOR_NAMES)
],
outputs="text",
title="World Constructor Championship Standings",
description="Get the world constructor championship standings for a specific constructor"
)
iface_event_info = gr.Interface(
fn=fastf1_tools.get_event_info,
inputs=[
gr.Number(label="Calendar year", value=CURRENT_YEAR, minimum=1950, maximum=CURRENT_YEAR),
gr.Textbox(label="Grand Prix", placeholder="Ex: Monaco", info="The name of the GP/country/location (Fuzzy matching supported) or round number"),
gr.Radio(["human", "LLM"], label="Display format", value="human")
],
outputs="text",
title="Event Info",
description="Get information about a specific Grand Prix event. Example: (2025,Monaco,human)"
)
iface_season_calendar = gr.Interface(
fn=fastf1_tools.get_season_calendar,
inputs=[
gr.Number(label="Calendar year", value=CURRENT_YEAR, minimum=1950, maximum=CURRENT_YEAR),
],
outputs="text",
title="Season Calendar",
description="Get the season calendar for the given year"
)
iface_track_visualization = gr.Interface(
fn=fastf1_tools.track_visualization,
inputs=[
gr.Number(label="Calendar year", value=CURRENT_YEAR, minimum=1950, maximum=CURRENT_YEAR),
gr.Textbox(label="Grand Prix", placeholder="Ex: Monaco", info="The name of the GP/country/location (Fuzzy matching supported) or round number"),
gr.Radio(["speed", "corners", "gear"], label="Visualization type", value="speed", info="What type of track visualization to generate"),
gr.Dropdown(label="Driver", choices=DRIVER_NAMES, info="Only applied for speed visualization. gear uses fastest lap during race.")
],
outputs="image",
title="Track Visualizations",
description="Get the track visualization for the given Grand Prix. Example: (2025,Monaco,speed,Leclerc)"
)
iface_session_results = gr.Interface(
fn=fastf1_tools.get_session_results,
inputs=[
gr.Number(label="Calendar year", value=CURRENT_YEAR, minimum=1950, maximum=CURRENT_YEAR),
gr.Textbox(label="Grand Prix", placeholder="Ex: Monaco", info="The name of the GP/country/location (Fuzzy matching supported) or round number"),
gr.Dropdown([session_type for session_type in DROPDOWN_SESSION_TYPES if "practice" not in session_type], label="Session type", value="race", info="The session type to get results for. Dataframe's columns vary depending on session type.")
],
outputs=gr.Dataframe(
headers=None, # Let it infer from returned DataFrame
row_count=(0, "dynamic"), # Start empty, allow it to grow
col_count=(0, "dynamic") # Let columns adjust too
),
title="Session Results",
description="Get the session results for the given Grand Prix. Example: (2025,Monaco,qualifying)"
)
iface_driver_info = gr.Interface(
fn=fastf1_tools.get_driver_info,
inputs=[
gr.Dropdown(label="Driver", choices=DRIVER_NAMES)
],
outputs="text",
title="Driver Info",
description="Get background information about a specific driver"
)
iface_constructor_info = gr.Interface(
fn=fastf1_tools.get_constructor_info,
inputs=[
gr.Dropdown(label="Constructor", choices=CONSTRUCTOR_NAMES)
],
outputs="text",
title="Constructor Info",
description="Get background information about a specific constructor"
)
# Create your markdown-only tab using Blocks
with gr.Blocks() as markdown_tab:
gr.Markdown(MARKDOWN_INTRODUCTION)
# OpenF1 tools tab
def openf1_tools_tab():
with gr.Blocks() as openf1_tools_tab:
gr.Markdown(OPENF1_TOOL_DESCRIPTION)
with gr.Accordion("get_api_endpoints()", open=False):
btn = gr.Button("Get all endpoints")
output = gr.JSON()
btn.click(openf1_tools.get_api_endpoints, outputs=output)
with gr.Accordion("get_api_endpoint(endpoint)", open=False):
endpoint_in = gr.Textbox(label="Endpoint", placeholder="e.g. sessions")
btn = gr.Button("Get endpoint info")
output = gr.JSON()
btn.click(openf1_tools.get_api_endpoint, inputs=endpoint_in, outputs=output)
with gr.Accordion("get_endpoint_info(endpoint)", open=False):
endpoint_in = gr.Textbox(label="Endpoint", placeholder="e.g. sessions")
btn = gr.Button("Get endpoint details")
output = gr.JSON()
btn.click(openf1_tools.get_endpoint_info, inputs=endpoint_in, outputs=output)
with gr.Accordion("get_filter_info(filter_name)", open=False):
filter_in = gr.Textbox(label="Filter name", placeholder="e.g. driver_number")
btn = gr.Button("Get filter info")
output = gr.JSON()
btn.click(openf1_tools.get_filter_info, inputs=filter_in, outputs=output)
with gr.Accordion("get_filter_string(filter_name, filter_value, operator)", open=False):
filter_name = gr.Textbox(label="Filter name", placeholder="e.g. driver_number")
filter_value = gr.Textbox(label="Filter value", placeholder="e.g. 16")
operator = gr.Dropdown(label="Operator", choices=["=", ">", "<", ">=", "<="], value="=")
btn = gr.Button("Get filter string")
output = gr.Textbox(label="Filter string", info="Example: driver_number=16&")
btn.click(openf1_tools.get_filter_string, inputs=[filter_name, filter_value, operator], outputs=output)
with gr.Accordion("apply_filters(api_string, *filters)", open=False):
api_string = gr.Textbox(label="Base API string", placeholder="e.g. https://api.openf1.org/v1/sessions?")
filters = gr.Textbox(label="Filters (comma-separated)", placeholder="e.g. driver_number=16&,session_key=123&")
btn = gr.Button("Apply filters")
output = gr.Textbox(label="Full API string")
btn.click(openf1_tools.apply_filters, inputs=[api_string, filters], outputs=output)
with gr.Accordion("send_request(api_string)", open=False):
with gr.Accordion("Example API requests (copy & paste into text box below)", open=False):
gr.Markdown(MARKDOWN_OPENF1_EXAMPLES)
api_string = gr.Textbox(label="Full API string", placeholder="e.g. https://api.openf1.org/v1/sessions?driver_number=16")
btn = gr.Button("Send API request")
output = gr.JSON()
btn.click(openf1_tools.send_request, inputs=api_string, outputs=output)
return openf1_tools_tab
# OpenF1 tabs
named_interfaces = {
"About": markdown_tab,
"Driver Championship Standings": iface_driver_championship_standings,
"Constructor Championship Standings": iface_constructor_championship_standings,
"Event Info": iface_event_info,
"Season Calendar": iface_season_calendar,
"Track Visualizations": iface_track_visualization,
"Session Results": iface_session_results,
"Driver Info": iface_driver_info,
"Constructor Info": iface_constructor_info,
"OpenF1 Tools": openf1_tools_tab(),
}
# Tab names and interfaces
tab_names = list(named_interfaces.keys())
interface_list = list(named_interfaces.values())
# Combine all the interfaces into a single TabbedInterface
gradio_server = gr.TabbedInterface(
interface_list,
tab_names=tab_names,
title="๐Ÿ Formula 1 MCP server ๐ŸŽ๏ธ"
)
# Launch the interface and MCP server
if __name__ == "__main__":
gradio_server.launch(mcp_server=True)