get_year
Retrieve aggregate season statistics for a specific FRC year, including EPA percentiles, average scores, and foul rates. Use to analyze game performance or calibrate team comparisons across seasons.
Instructions
Look up Statbotics season summary statistics for a single FIRST Robotics Competition (FRC) year. Returns aggregate metrics for the game played that year: EPA (Expected Points Added) percentile breakpoints (mean, median, 75th/90th/95th/99th), score component averages, foul rates, RP (ranking point) rates, and the count of teams, events, and matches recorded. Use this to answer questions like "what was the average score in the 2024 FRC season?", "what EPA put a team in the top 10% in 2019?", or to calibrate per-year scoring before comparing teams across seasons. Data is available for years 2002 onward; pre-2016 seasons have less detail because the EPA model was less granular. For comparing many seasons in one call, use get_years instead.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| year | Yes | Four-digit year (2002 onwards) |
Implementation Reference
- src/handlers.ts:38-44 (handler)The handler for the 'get_year' tool: parses the input with GetYearInputSchema, makes an API request to /v3/year/{year}, and returns the JSON response.
case 'get_year': { const { year } = GetYearInputSchema.parse(args); const data = await makeApiRequest(`/v3/year/${year}`); return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }], }; } - src/schemas.ts:101-103 (schema)Zod schema for GetYearInputSchema: expects a single 'year' field using YearSchema (integer, min 2002, max current year+1).
export const GetYearInputSchema = z.object({ year: YearSchema, }); - src/tools.ts:32-48 (registration)Tool registration for 'get_year' in the tools array, including description, annotations, and inputSchema.
{ name: 'get_year', description: 'Look up Statbotics season summary statistics for a single FIRST Robotics Competition (FRC) year. ' + 'Returns aggregate metrics for the game played that year: EPA (Expected Points Added) percentile breakpoints ' + '(mean, median, 75th/90th/95th/99th), score component averages, foul rates, RP (ranking point) rates, and ' + 'the count of teams, events, and matches recorded. ' + 'Use this to answer questions like "what was the average score in the 2024 FRC season?", ' + '"what EPA put a team in the top 10% in 2019?", or to calibrate per-year scoring before comparing teams across seasons. ' + 'Data is available for years 2002 onward; pre-2016 seasons have less detail because the EPA model was less granular. ' + 'For comparing many seasons in one call, use get_years instead.', annotations: { title: 'Get FRC Season Statistics (Single Year)', ...readOnlyAnnotations, }, inputSchema: toMCPSchema(GetYearInputSchema), }, - src/utils.ts:23-50 (helper)The makeApiRequest helper function used by the handler to make HTTP GET requests to the Statbotics API.
export async function makeApiRequest(endpoint: string): Promise<unknown> { try { const url = `https://api.statbotics.io${endpoint}`; const response = await fetch(url, { headers: { Accept: 'application/json', }, }); if (!response.ok) { const errorMessage = `Statbotics API request failed: ${response.status} ${response.statusText} for endpoint ${endpoint}`; await log('error', errorMessage); throw new Error(errorMessage); } return response.json(); } catch (error) { if (error instanceof Error) { const errorMessage = `API request error for endpoint ${endpoint}: ${error.message}`; await log('error', errorMessage); throw error; } const errorMessage = `Unknown error during API request for endpoint ${endpoint}`; await log('error', `${errorMessage}: ${error}`); throw new Error(errorMessage); } } - src/index.ts:39-101 (registration)The MCP server registration that dispatches tool calls — routes to handleToolCall which handles 'get_year'.
server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; await log('debug', `Processing tool request: ${name}`, server); try { return await handleToolCall(name, args); } catch (error) { const errorMessage = `Tool execution error for '${name}': ${error instanceof Error ? error.message : String(error)}`; await log('error', errorMessage, server); return { content: [ { type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }); await log('info', 'Setting up transport connection ...', server); try { const transport = new StdioServerTransport(); await server.connect(transport); await log( 'info', `Statbotics MCP Server v${version} running on stdio`, server, ); } catch (error) { const errorMessage = 'Failed to connect to transport'; await log( 'error', `${errorMessage}: ${error instanceof Error ? error.message : error}`, server, ); throw new Error(errorMessage); } // Set up error handlers for the server process.on('uncaughtException', (error) => { console.error('Uncaught exception in MCP server:', error); process.exit(1); }); process.on('unhandledRejection', (reason, promise) => { console.error( 'Unhandled rejection in MCP server:', reason, 'at promise:', promise, ); process.exit(1); }); } catch (error) { const errorMessage = 'Fatal error during server initialization'; console.error( `${errorMessage}: ${error instanceof Error ? error.message : error}`, ); throw error; } }