Skip to main content
Glama
DynamicEndpoints

ESPN MCP Server

tool-handlers.ts19.6 kB
/** * Shared tool handlers to eliminate code duplication * Provides a single source of truth for all ESPN MCP tool implementations */ import type { GetScoresParams, GetTeamParams, GetNewsParams, ESPNScoreboard, ESPNNewsResponse, ESPNTeamsResponse, ESPNStandingsResponse, ESPNRankingsResponse, ESPNGameSummary, ESPNAthletesResponse, } from '../types/espn-api.js'; import { ValidationError } from '../utils/errors.js'; import { logToolCall, logToolResult, PerformanceTimer } from '../utils/logger.js'; import type { ModernESPNClient } from '../client/espn-client.js'; // ============================================================================ // Tool Handler Class // ============================================================================ /** * Centralized tool handlers for ESPN MCP Server * Eliminates duplication between STDIO and HTTP handlers */ export class ESPNToolHandlers { constructor(private client: ModernESPNClient) {} /** * Handle get_live_scores tool */ async handleGetLiveScores(args: GetScoresParams): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: boolean; }> { const timer = new PerformanceTimer('get_live_scores'); logToolCall('get_live_scores', args); try { // Validate arguments this.validateScoresArgs(args); const { sport, league, dates, week, seasontype } = args; let data: ESPNScoreboard; // Route to appropriate client method based on sport/league switch (sport) { case 'football': if (league === 'nfl') { data = await this.client.getNFLScores({ dates, week, seasontype }); } else if (league === 'college-football') { data = await this.client.getCollegeFootballScores({ dates }); } else { data = await this.client.getScoreboard(sport, league); } break; case 'basketball': if (league === 'nba') { data = await this.client.getNBAScores(); } else if (league === 'wnba') { data = await this.client.getWNBAScores(); } else if (league === 'mens-college-basketball') { data = await this.client.getMensCollegeBasketballScores(); } else if (league === 'womens-college-basketball') { data = await this.client.getWomensCollegeBasketballScores(); } else { data = await this.client.getScoreboard(sport, league); } break; case 'baseball': if (league === 'mlb') { data = await this.client.getMLBScores(); } else if (league === 'college-baseball') { data = await this.client.getCollegeBaseballScores(); } else { data = await this.client.getScoreboard(sport, league); } break; case 'hockey': if (league === 'nhl') { data = await this.client.getNHLScores(); } else { data = await this.client.getScoreboard(sport, league); } break; case 'soccer': if (league === 'mls') { data = await this.client.getMLSScores(); } else if (league === 'premier-league') { data = await this.client.getPremierLeagueScores(); } else if (league === 'champions-league') { data = await this.client.getChampionsLeagueScores(); } else if (league) { data = await this.client.getSoccerScores(league); } else { data = await this.client.getScoreboard(sport, league); } break; default: data = await this.client.getScoreboard(sport, league); } const duration = timer.end(true); logToolResult('get_live_scores', true, duration); return { content: [{ type: 'text', text: `Live scores for ${sport}${league ? ` (${league})` : ''}:\n\n${JSON.stringify(data, null, 2)}`, }], }; } catch (error) { const duration = timer.end(false); logToolResult('get_live_scores', false, duration); return { content: [{ type: 'text', text: `Error getting live scores: ${error instanceof Error ? error.message : String(error)}`, }], isError: true, }; } } /** * Handle get_team_information tool */ async handleGetTeamInformation(args: GetTeamParams): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: boolean; }> { const timer = new PerformanceTimer('get_team_information'); logToolCall('get_team_information', args); try { const { sport, league } = args; let data: ESPNTeamsResponse; // Route to appropriate client method switch (sport) { case 'football': if (league === 'nfl') { data = await this.client.getNFLTeams(); } else if (league === 'college-football') { data = await this.client.getCollegeFootballTeams(); } else { data = await this.client.getTeams(sport, league); } break; case 'basketball': if (league === 'nba') { data = await this.client.getNBATeams(); } else if (league === 'wnba') { data = await this.client.getWNBATeams(); } else if (league === 'mens-college-basketball') { data = await this.client.getMensCollegeBasketballTeams(); } else if (league === 'womens-college-basketball') { data = await this.client.getWomensCollegeBasketballTeams(); } else { data = await this.client.getTeams(sport, league); } break; case 'baseball': if (league === 'mlb') { data = await this.client.getMLBTeams(); } else if (league === 'college-baseball') { data = await this.client.getCollegeBaseballTeams(); } else { data = await this.client.getTeams(sport, league); } break; case 'hockey': if (league === 'nhl') { data = await this.client.getNHLTeams(); } else { data = await this.client.getTeams(sport, league); } break; case 'soccer': if (league) { data = await this.client.getSoccerTeams(league); } else { data = await this.client.getTeams(sport, league); } break; default: data = await this.client.getTeams(sport, league); } const duration = timer.end(true); logToolResult('get_team_information', true, duration); return { content: [{ type: 'text', text: `Team information for ${sport}${league ? ` (${league})` : ''}:\n\n${JSON.stringify(data, null, 2)}`, }], }; } catch (error) { const duration = timer.end(false); logToolResult('get_team_information', false, duration); return { content: [{ type: 'text', text: `Error getting team information: ${error instanceof Error ? error.message : String(error)}`, }], isError: true, }; } } /** * Handle get_specific_team tool */ async handleGetSpecificTeam(args: GetTeamParams & { team: string }): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: boolean; }> { const timer = new PerformanceTimer('get_specific_team'); logToolCall('get_specific_team', args); try { const { sport, league, team } = args; if (!team) { throw new ValidationError('Team abbreviation is required', 'team', team); } let data: any; // Route to appropriate client method switch (sport) { case 'football': if (league === 'college-football') { data = await this.client.getCollegeFootballTeam(team); } else if (league === 'nfl') { data = await this.client.getNFLTeam(team); } else { data = await this.client.getTeamDetails(sport, league!, team); } break; case 'basketball': if (league === 'nba') { data = await this.client.getNBATeam(team); } else if (league === 'wnba') { data = await this.client.getWNBATeam(team); } else if (league === 'mens-college-basketball') { data = await this.client.getMensCollegeBasketballTeam(team); } else if (league === 'womens-college-basketball') { data = await this.client.getWomensCollegeBasketballTeam(team); } else { data = await this.client.getTeamDetails(sport, league!, team); } break; case 'baseball': if (league === 'mlb') { data = await this.client.getMLBTeam(team); } else if (league === 'college-baseball') { data = await this.client.getCollegeBaseballTeam(team); } else { data = await this.client.getTeamDetails(sport, league!, team); } break; case 'hockey': if (league === 'nhl') { data = await this.client.getNHLTeam(team); } else { data = await this.client.getTeamDetails(sport, league!, team); } break; case 'soccer': if (league) { data = await this.client.getSoccerTeam(league, team); } else { data = await this.client.getTeamDetails(sport, league!, team); } break; default: data = await this.client.getTeamDetails(sport, league!, team); } const duration = timer.end(true); logToolResult('get_specific_team', true, duration); return { content: [{ type: 'text', text: `Team information for ${team} in ${sport} (${league}):\n\n${JSON.stringify(data, null, 2)}`, }], }; } catch (error) { const duration = timer.end(false); logToolResult('get_specific_team', false, duration); return { content: [{ type: 'text', text: `Error getting specific team: ${error instanceof Error ? error.message : String(error)}`, }], isError: true, }; } } /** * Handle get_league_standings tool */ async handleGetLeagueStandings(args: { sport: string; league?: string }): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: boolean; }> { const timer = new PerformanceTimer('get_league_standings'); logToolCall('get_league_standings', args); try { const { sport, league } = args; const data: ESPNStandingsResponse = await this.client.getStandings(sport, league); const duration = timer.end(true); logToolResult('get_league_standings', true, duration); return { content: [{ type: 'text', text: `Standings for ${sport}${league ? ` (${league})` : ''}:\n\n${JSON.stringify(data, null, 2)}`, }], }; } catch (error) { const duration = timer.end(false); logToolResult('get_league_standings', false, duration); return { content: [{ type: 'text', text: `Error getting standings: ${error instanceof Error ? error.message : String(error)}`, }], isError: true, }; } } /** * Handle get_sports_news tool */ async handleGetSportsNews(args: GetNewsParams): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: boolean; }> { const timer = new PerformanceTimer('get_sports_news'); logToolCall('get_sports_news', args); try { const { sport, limit = 10 } = args; let data: any; if (!sport) { // General news data = await this.client.getNews(); } else { // Sport-specific news switch (sport) { case 'football': { const [nflNews, cfbNews] = await Promise.all([ this.client.getNFLNews().catch(() => null), this.client.getCollegeFootballNews().catch(() => null), ]); data = { nfl: nflNews, collegefootball: cfbNews }; break; } case 'basketball': { const [nbaNews, wnbaNews, mensCbbNews, womensCbbNews] = await Promise.all([ this.client.getNBANews().catch(() => null), this.client.getWNBANews().catch(() => null), this.client.getMensCollegeBasketballNews().catch(() => null), this.client.getWomensCollegeBasketballNews().catch(() => null), ]); data = { nba: nbaNews, wnba: wnbaNews, menscollege: mensCbbNews, womenscollege: womensCbbNews, }; break; } case 'baseball': data = await this.client.getMLBNews(); break; case 'hockey': data = await this.client.getNHLNews(); break; case 'soccer': { const [premierLeagueNews, mlsNews, championsLeagueNews, laLigaNews] = await Promise.all([ this.client.getSoccerNews('eng.1').catch(() => null), this.client.getSoccerNews('usa.1').catch(() => null), this.client.getSoccerNews('uefa.champions').catch(() => null), this.client.getSoccerNews('esp.1').catch(() => null), ]); data = { premierLeague: premierLeagueNews, mls: mlsNews, championsLeague: championsLeagueNews, laLiga: laLigaNews, }; break; } default: data = await this.client.getNews(sport); } } const duration = timer.end(true); logToolResult('get_sports_news', true, duration); return { content: [{ type: 'text', text: `Sports news${sport ? ` for ${sport}` : ''}:\n\n${JSON.stringify(data, null, 2)}`, }], }; } catch (error) { const duration = timer.end(false); logToolResult('get_sports_news', false, duration); return { content: [{ type: 'text', text: `Error getting sports news: ${error instanceof Error ? error.message : String(error)}`, }], isError: true, }; } } /** * Handle search_athletes tool */ async handleSearchAthletes(args: { sport: string; league?: string }): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: boolean; }> { const timer = new PerformanceTimer('search_athletes'); logToolCall('search_athletes', args); try { const { sport, league } = args; const data: ESPNAthletesResponse = await this.client.getAthletes(sport, league); const duration = timer.end(true); logToolResult('search_athletes', true, duration); return { content: [{ type: 'text', text: `Athletes for ${sport}${league ? ` (${league})` : ''}:\n\n${JSON.stringify(data, null, 2)}`, }], }; } catch (error) { const duration = timer.end(false); logToolResult('search_athletes', false, duration); return { content: [{ type: 'text', text: `Error searching athletes: ${error instanceof Error ? error.message : String(error)}`, }], isError: true, }; } } /** * Handle get_college_football_rankings tool */ async handleGetCollegeFootballRankings(): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: boolean; }> { const timer = new PerformanceTimer('get_college_football_rankings'); logToolCall('get_college_football_rankings', {}); try { const data: ESPNRankingsResponse = await this.client.getCollegeFootballRankings(); const duration = timer.end(true); logToolResult('get_college_football_rankings', true, duration); return { content: [{ type: 'text', text: `College Football Rankings:\n\n${JSON.stringify(data, null, 2)}`, }], }; } catch (error) { const duration = timer.end(false); logToolResult('get_college_football_rankings', false, duration); return { content: [{ type: 'text', text: `Error getting rankings: ${error instanceof Error ? error.message : String(error)}`, }], isError: true, }; } } /** * Handle get_game_summary tool */ async handleGetGameSummary(args: { sport: string; league: string; gameId: string; }): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: boolean; }> { const timer = new PerformanceTimer('get_game_summary'); logToolCall('get_game_summary', args); try { const { sport, league, gameId } = args; if (sport === 'football' && league === 'college-football') { const data: ESPNGameSummary = await this.client.getCollegeFootballGameSummary(gameId); const duration = timer.end(true); logToolResult('get_game_summary', true, duration); return { content: [{ type: 'text', text: `Game Summary for ${gameId}:\n\n${JSON.stringify(data, null, 2)}`, }], }; } throw new ValidationError('Game summary only supported for college football currently'); } catch (error) { const duration = timer.end(false); logToolResult('get_game_summary', false, duration); return { content: [{ type: 'text', text: `Error getting game summary: ${error instanceof Error ? error.message : String(error)}`, }], isError: true, }; } } // ============================================================================ // Validation Helpers // ============================================================================ private validateScoresArgs(args: GetScoresParams): void { if (!args.sport) { throw new ValidationError('Sport is required', 'sport', args.sport); } // Validate date format if provided if (args.dates && !/^\d{8}$/.test(args.dates)) { throw new ValidationError( 'Date must be in YYYYMMDD format', 'dates', args.dates ); } // Validate week number for NFL if (args.week) { const week = parseInt(args.week); if (isNaN(week) || week < 1 || week > 18) { throw new ValidationError( 'Week must be a number between 1 and 18', 'week', args.week ); } } // Validate season type if (args.seasontype && !['1', '2', '3'].includes(args.seasontype)) { throw new ValidationError( 'Season type must be 1 (preseason), 2 (regular), or 3 (postseason)', 'seasontype', args.seasontype ); } } }

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/DynamicEndpoints/espn-mcp'

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