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
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 describes what the tool 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'), which is crucial since there's no output schema. However, it doesn't mention potential limitations like data availability for all races or pagination for large result sets, leaving some behavioral aspects unspecified.

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 core purpose, followed by elaboration, usage note, examples, and return format. Every sentence adds value—none are redundant or wasteful—making it efficient and well-structured for quick understanding.

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 (historical data retrieval), no annotations, no output schema, and low schema coverage, the description does a good job of covering key aspects: purpose, usage guidelines, parameter context, and return format. However, it lacks details on error handling or data completeness (e.g., what happens if no victory data exists), which would enhance completeness for this context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 0%, so the description must compensate. It adds meaningful context for the single parameter (race_id) by explaining it's needed to identify the race and providing examples (Tour de France ID: 17, Paris-Roubaix ID: 30), which clarifies the parameter's purpose beyond the schema's basic type definition. However, it doesn't specify where to find these IDs or if they're standardized, leaving some ambiguity.

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 specific action ('Get the all-time victory table') and resource ('for a cycling race'), distinguishing it from sibling tools like get_race_details or get_race_edition_results by focusing on historical rider victories rather than race details or specific edition results. It explicitly mentions what the 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.'

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 includes explicit guidance on when to use this tool versus alternatives: 'If you don't know the race's ID, use the search_race tool first to find it by name.' This directly addresses a common prerequisite and names the specific sibling tool to use as an alternative, providing clear context for proper tool selection.

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

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

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