Skip to main content
Glama
r-huijts

FirstCycling MCP Server

by r-huijts

get_race_victory_table

Retrieve the all-time victory table for a cycling race, displaying the most successful riders, their number of victories, and the years they won. Use the race ID to access historical achievements.

Instructions

Get the all-time victory table for a cycling race. This tool provides a historical summary of the most successful riders in a specific race, showing the number of victories for each rider throughout the race's history.

Note: If you don't know the race's ID, use the search_race tool first to find it by name.

Example usage:
- Get victory table for Tour de France (ID: 17)
- Get victory table for Paris-Roubaix (ID: 30)

Returns a formatted string with:
- Race name
- List of riders with the most victories
- Number of victories for each rider
- Years of victories where available

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
race_idYes

Implementation Reference

  • The primary handler method that retrieves the RaceVictoryTable endpoint for the race's all-time victory table.
    def victory_table(self):
        """
        Get race all-time victory table.
    
        Returns
        -------
        RaceVictoryTable
        """
        return self._get_endpoint(endpoint=RaceVictoryTable, k='W')
  • The endpoint handler class responsible for parsing the HTML response into a victory table DataFrame.
    class RaceVictoryTable(RaceEndpoint):
    	"""
    	Race victory table response. Extends RaceEndpoint.
    
    	Attributes
    	----------
    	table : pd.DataFrame
    		Victory table for race.
    	"""
    
    	def _parse_soup(self):
    		super()._parse_soup()
    		self._get_victory_table()
    		
    
    	def _get_victory_table(self):
    		victory_table = self.soup.find('table', {'class': 'tablesorter'})
    		self.table = parse_table(victory_table)
  • Helper method within RaceVictoryTable that locates the victory table in the HTML soup and parses it.
    def _get_victory_table(self):
    	victory_table = self.soup.find('table', {'class': 'tablesorter'})
    	self.table = parse_table(victory_table)
  • Core helper function that converts the raw HTML table element into a structured pandas DataFrame, extracting additional metadata from links and images.
    def parse_table(table):
    	""" Convert HTML table from bs4 to pandas DataFrame. Return None if no data. """
    	# TODO for rider results, format dates nicely with hidden column we are throwing away
    	import pandas as pd
    	import io
    
    	# Check early if table contains "No data" text
    	if table and "No data" in table.get_text():
    		return None
    
    	# Load pandas DataFrame from raw text only
    	html = str(table)
    	out_df = pd.read_html(io.StringIO(html), decimal=',')[0]
    
    	if out_df.iat[0, 0] == 'No data': # No data
    		return None
    
    	# Convert decimal points to thousands separator
    	# NOTE: Cannot use thousands='.' in pd.read_html because will ruin other columns (e.g. CAT for races)
    	thousands_cols = ['Points']
    	for col in thousands_cols:
    		if col in out_df:
    			out_df[col] = out_df[col].astype(str).str.replace('.', '', regex=False).astype(int)
    
    	# Parse soup to add information hidden in tags/links
    	headers = [th.text for th in table.find_all('th')]
    	trs = [tr for tr in table.find_all('tr') if tr.th is None]
    
    	if 'Race.1' in out_df:
    		out_df = out_df.rename(columns={'Race': 'Race_Country', 'Race.1': 'Race'})
    		headers.insert(headers.index('Race'), 'Race_Country')
    
    	soup_df = pd.DataFrame([tr.find_all('td') for tr in trs], columns=headers)
    
    	# Add information hidden in tags
    	for col, series in soup_df.items():
    		if col in ('Rider', 'Winner', 'Second', 'Third'):
    			out_df[col + '_ID'] = series.apply(lambda td: rider_link_to_id(td.a))
    			try:
    				out_df[col + '_Country'] = series.apply(lambda td: img_to_country_code(td.img))
    			except TypeError:
    				pass
    
    		elif col == 'Team':
    			out_df['Team_ID'] = series.apply(lambda td: team_link_to_id(td.a) if td.a else None)
    			out_df['Team_Country'] = series.apply(lambda td: img_to_country_code(td.img) if td.img else None)
    
    		elif col == 'Race':
    			out_df['Race_ID'] = series.apply(lambda td: get_url_parameters(td.a['href'])['r'] if td.a else None)
    			
    		elif col == 'Race_Country':
    			out_df['Race_Country'] = series.apply(lambda td: img_to_country_code(td.img) if td.img else None)
    
    		elif col == '':
    			try:
    				out_df['Icon'] = series.apply(lambda td: get_img_name(td.img) if td.img else None)
    			except AttributeError:
    				pass
    
    	out_df = out_df.replace({'-': None}).dropna(how='all', axis=1)
    
    	# TODO Remove Unnamed columns
    	
    	return out_df

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/r-huijts/firstcycling-mcp'

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