Skip to main content
Glama

get_year

Read-onlyIdempotent

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

TableJSON Schema
NameRequiredDescriptionDefault
yearYesFour-digit year (2002 onwards)

Implementation Reference

  • 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) }],
      };
    }
  • 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),
    },
  • 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;
      }
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate readOnlyHint, so no contradiction. Description adds useful behavioral context: results are aggregate metrics, and pre-2016 seasons have less detail due to EPA model granularity. This goes beyond annotations.

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?

Three sentences efficiently cover purpose, examples, and caveats. No fluff; every sentence adds value. Front-loaded with the verb phrase 'Look up...'.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter tool with robust annotations, the description fully explains what the tool returns, when to use it, and limitations. No gaps remain.

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?

With 100% schema coverage, the schema already describes the year parameter. The description adds value by clarifying the valid range (2002 onward) and noting that pre-2016 years return less detailed data, which is not in the schema description.

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?

Clearly states it looks up season summary statistics for a single FRC year, listing specific metrics like EPA breakpoints and score averages. Distinguishes from sibling get_years by noting its single-year scope.

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?

Provides explicit example questions and directly states when to use get_years instead for multi-season comparison. Also mentions data availability from 2002 onward and the caveat for pre-2016 detail.

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

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/withinfocus/statbotics-mcp-server'

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