Skip to main content
Glama
index.ts13.2 kB
#!/usr/bin/env node import process from 'node:process'; import { BalldontlieAPI } from '@balldontlie/sdk'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import z from 'zod'; import { getSchedulePrompt } from './prompts.js'; import { formatMLBGame, formatNBAGame, formatNFLGame } from './utils.js'; const server = new McpServer( { name: `Balldontlie MCP Server`, version: `0.5.0`, }, { capabilities: { logging: {}, tools: {}, }, }, ); const leagueEnum = z.enum(['NBA', 'MLB', 'NFL']); export type LeagueEnum = z.infer<typeof leagueEnum>; // #region API Key Check and setup const BALLDONTLIE_API_KEY = process.env.BALLDONTLIE_API_KEY; if (!BALLDONTLIE_API_KEY) { console.error('Error: BALLDONTLIE_API_KEY environment variable is not set.'); log('Error: BALLDONTLIE_API_KEY environment variable is not set.', 'error'); process.exit(1); } const api = new BalldontlieAPI({ apiKey: BALLDONTLIE_API_KEY }); // #endregion API Key Check and setup // #region Tool Definitions server.tool( 'get_teams', 'Gets the list of team from one of the following leagues NBA (National Basketball Association), MLB (Major League Baseball), NFL (National Football League)', { league: leagueEnum, }, async ({ league }) => { switch (league) { case 'NBA': { const nbaTeams = await api.nba.getTeams(); const text = nbaTeams.data.map((team) => { return `Team ID: ${team.id}\n` + `Full Name: ${team.full_name}\n` + `Name: ${team.name}\n` + `Abbreviation: ${team.abbreviation}\n` + `City: ${team.city}\n` + `Conference: ${team.conference}\n` + `Division: ${team.division}\n`; }).join('\n-----\n'); return { content: [{ type: 'text', text }] }; } case 'MLB': { const mlbTeams = await api.mlb.getTeams(); const text = mlbTeams.data.map((team) => { return `Team ID: ${team.id}\n` + `Display Name: ${team.display_name}\n` + `Name: ${team.name}\n` + `Abbreviation: ${team.abbreviation}\n` + `Location: ${team.location}\n` + `League: ${team.league}\n` + `Division: ${team.division}\n` + `Slug: ${team.slug}\n`; }).join('\n-----\n'); ; return { content: [{ type: 'text', text }] }; } case 'NFL': { const nlfTeams = await api.nfl.getTeams(); const text = nlfTeams.data.map((team) => { return `Team ID: ${team.id}\n` + `Name: ${team.name}\n` + `Abbreviation: ${team.abbreviation}\n` + `Full Name: ${team.full_name}\n` + `Location: ${team.location}\n` + `Conference: ${team.conference}\n` + `Division: ${team.division}\n`; }).join('\n-----\n'); return { content: [{ type: 'text', text }] }; } default: { return { content: [{ type: 'text', text: `Unknown league: ${league}` }], isError: true, }; } } }, ); server.tool( `get_players`, `Gets the list of players from one of the following leagues NBA (National Basketball Association), MLB (Major League Baseball), NFL (National Football League)`, { league: leagueEnum, firstName: z.string().optional().describe('First name of the player to search for, optional'), lastName: z.string().optional().describe('Last name of the player to search for, optional'), cursor: z.number().optional().describe('Cursor for pagination, the value should be next_cursor from previous call of get_players tool, optional'), }, async ({ league, firstName = undefined, lastName = undefined, cursor = undefined }) => { switch (league) { case 'NBA': { const nbaPlayers = await api.nba.getPlayers({ first_name: firstName, last_name: lastName, cursor, }); const text = nbaPlayers.data.map((player) => { return `Player ID: ${player.id}\n` + `First Name: ${player.first_name}\n` + `Last Name: ${player.last_name}\n` + `Position: ${player.position}\n` + `Height: ${player.height}\n` + `Weight: ${player.weight}\n` + `Jersey Number: ${player.jersey_number}\n` + `College: ${player.college}\n` + `Country: ${player.country}\n` + `Draft Year: ${player.draft_year}\n` + `Draft Round: ${player.draft_round}\n` + `Draft Number: ${player.draft_number}\n` + `Team ID: ${player.team.id}\n` + `Team Name: ${player.team.name}\n`; }).join('\n-----\n'); let finalText = text; if (nbaPlayers.meta?.next_cursor) { finalText += `\n\nPagination Information:\nnext_cursor: ${nbaPlayers.meta.next_cursor}`; } return { content: [{ type: 'text', text: finalText }] }; } case 'MLB': { const mlbPlayers = await api.mlb.getPlayers({ first_name: firstName, last_name: lastName, cursor, }); const text = mlbPlayers.data.map((player) => { return `Player ID: ${player.id}\n` + `First Name: ${player.first_name}\n` + `Last Name: ${player.last_name}\n` + `Full Name: ${player.full_name}\n` + `Debut Year: ${player.debut_year}\n` + `Jersey: ${player.jersey}\n` + `College: ${player.college}\n` + `Position: ${player.position}\n` + `Active: ${player.active}\n` + `Birth Place: ${player.birth_place}\n` + `Date of Birth: ${player.dob}\n` + `Age: ${player.age}\n` + `Height: ${player.height}\n` + `Weight: ${player.weight}\n` + `Draft: ${player.draft}\n` + `Bats/Throws: ${player.bats_throws}\n` + `Team ID: ${player.team.id}\n` + `Team Name: ${player.team.name}\n`; }).join('\n-----\n'); let finalText = text; if (mlbPlayers.meta?.next_cursor) { finalText += `\n\nPagination Information:\nnext_cursor: ${mlbPlayers.meta.next_cursor}`; } return { content: [{ type: 'text', text: finalText }] }; } case 'NFL': { const nflPlayers = await api.nfl.getPlayers({ first_name: firstName, last_name: lastName, cursor, }); const text = nflPlayers.data.map((player) => { return `ID: ${player.id}\n` + `First Name: ${player.first_name}\n` + `Last Name: ${player.last_name}\n` + `Position: ${player.position}\n` + `Position Abbreviation: ${player.position_abbreviation}\n` + `Height: ${player.height}\n` + `Weight: ${player.weight}\n` + `Jersey Number: ${player.jersey_number}\n` + `College: ${player.college}\n` + `Experience: ${player.experience}\n` + `Age: ${player.age}\n` + `Team ID: ${player.team.id}\n` + `Team Name: ${player.team.name}\n` + `Team Location: ${player.team.location}\n` + `Team Abbreviation: ${player.team.abbreviation}\n` + `Team Conference: ${player.team.conference}\n` + `Team Division: ${player.team.division}\n`; }).join('\n-----\n'); let finalText = text; if (nflPlayers.meta?.next_cursor) { finalText += `\n\nPagination Information:\nnext_cursor: ${nflPlayers.meta.next_cursor}`; } return { content: [{ type: 'text', text: finalText }] }; } default: { return { content: [{ type: 'text', text: `Unknown league: ${league}` }], isError: true, }; } } }, ); server.tool( 'get_games', 'Gets the list of games from one of the following leagues NBA (National Basketball Association), MLB (Major League Baseball), NFL (National Football League)', { league: leagueEnum, dates: z.array(z.string()).optional().describe('Get games for a range of dates, format: YYYY-MM-DD, optional'), seasons: z.array(z.number()).optional().describe('Get games for a specific season, format: YYYY, optional'), teamIds: z.array(z.number()).optional().describe('Get games for specific team IDs, optional'), cursor: z.number().optional().describe('Cursor for pagination, the value should be next_cursor from previous call of get_games tool, optional'), }, async ({ league, dates = undefined, seasons = undefined, teamIds = undefined, cursor = undefined }) => { switch (league) { case 'NBA': { const nbaGames = await api.nba.getGames({ dates, seasons, team_ids: teamIds, cursor, }); const text = nbaGames.data.map((game) => { return formatNBAGame(game); }).join('\n-----\n'); let finalText = text; if (nbaGames.meta?.next_cursor) { finalText += `\n\nPagination Information:\nnext_cursor: ${nbaGames.meta.next_cursor}`; } return { content: [{ type: 'text', text: finalText }] }; } case 'MLB': { const mlbGames = await api.mlb.getGames({ dates, seasons, team_ids: teamIds, cursor, }); const text = mlbGames.data.map((game) => { return formatMLBGame(game); }).join('\n\n-----\n\n'); let finalText = text; if (mlbGames.meta?.next_cursor) { finalText += `\n\nPagination Information:\nnext_cursor: ${mlbGames.meta.next_cursor}`; } return { content: [{ type: 'text', text: finalText }] }; } case 'NFL': { const nflGames = await api.nfl.getGames({ dates, seasons, team_ids: teamIds, cursor, }); const text = nflGames.data.map((game) => { return formatNFLGame(game); }).join('\n\n-----\n\n'); let finalText = text; if (nflGames.meta?.next_cursor) { finalText += `\n\nPagination Information:\nnext_cursor: ${nflGames.meta.next_cursor}`; } return { content: [{ type: 'text', text: finalText }] }; } default: { return { content: [{ type: 'text', text: `Unknown league: ${league}` }], isError: true, }; } } }, ); server.tool( 'get_game', 'Get a specific game from one of the following leagues NBA (National Basketball Association), MLB (Major League Baseball), NFL (National Football League)', { league: leagueEnum, gameId: z.number().describe('Game ID to get the game for, the value should be Game ID from previous call of get_games tool'), }, async ({ league, gameId }) => { switch (league) { case 'NBA': { const nbaGame = await api.nba.getGame(gameId); if (nbaGame.data) { const text = formatNBAGame(nbaGame.data); return { content: [{ type: 'text', text }] }; } return { content: [{ type: 'text', text: `Game ID ${gameId} not found` }], isError: true }; } case 'MLB': { const mlbGame = await api.mlb.getGame(gameId); if (mlbGame.data) { const text = formatMLBGame(mlbGame.data); return { content: [{ type: 'text', text }] }; } return { content: [{ type: 'text', text: `Game ID ${gameId} not found` }], isError: true }; } case 'NFL': { const nflGame = await api.nfl.getGame(gameId); if (nflGame.data) { const text = formatNFLGame(nflGame.data); return { content: [{ type: 'text', text }] }; } return { content: [{ type: 'text', text: `Game ID ${gameId} not found` }], isError: true }; } default: { return { content: [{ type: 'text', text: `Unknown league: ${league}` }], isError: true, }; } } }, ); // #endregion Tool Definitions server.prompt( 'schedule_generator', 'Generates a schedule for a league from a given start date to end date', { league: leagueEnum, startDate: z.string().describe('Start date for the schedule, format: YYYY-MM-DD'), endDate: z.string().describe('End date for the schedule, format: YYYY-MM-DD'), }, async ({ league, startDate, endDate }) => { return { messages: [ { role: 'user', content: { type: 'text', text: getSchedulePrompt(league, startDate, endDate), }, }, ] }; }, ); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); log('Balldontlie MCP Server is running with Stdio transport'); } function log( message: string, level: 'error' | 'debug' | 'info' | 'notice' | 'warning' | 'critical' | 'alert' | 'emergency' = 'info', ) { server.server.sendLoggingMessage({ level, message, }); } main().catch((error) => { console.error('Error starting server:', error); process.exit(1); });

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/mikechao/balldontlie-mcp'

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