Skip to main content
Glama
gedin-eth

College Football MCP

by gedin-eth
odds_api.py14.5 kB
""" The Odds API Client This module handles interactions with The Odds API to retrieve college football game odds and scores. """ import requests import logging from typing import Optional, Dict, Any, List from datetime import datetime logger = logging.getLogger(__name__) ODDS_API_BASE_URL = "https://api.the-odds-api.com/v4" def get_ncaaf_odds( api_key: str, regions: str = "us", markets: str = "h2h,spreads,totals", odds_format: str = "american", date_format: str = "iso" ) -> Optional[List[Dict[str, Any]]]: """ Fetch NCAA football odds from The Odds API. Args: api_key: The Odds API key regions: Comma-separated list of regions (default: "us") markets: Comma-separated list of markets (default: "h2h,spreads,totals") odds_format: Format for odds (default: "american") date_format: Format for dates (default: "iso") Returns: List of game odds data or None if request fails """ if not api_key: logger.error("ODDS_API_KEY is not set") return None url = f"{ODDS_API_BASE_URL}/sports/americanfootball_ncaaf/odds" params = { "apiKey": api_key, "regions": regions, "markets": markets, "oddsFormat": odds_format, "dateFormat": date_format } try: response = requests.get(url, params=params, timeout=10) response.raise_for_status() # Check API usage limits remaining_requests = response.headers.get("x-requests-remaining") used_requests = response.headers.get("x-requests-used") if remaining_requests: logger.info(f"Odds API usage: {used_requests} used, {remaining_requests} remaining") return response.json() except requests.exceptions.RequestException as e: logger.error(f"Error fetching odds from The Odds API: {e}") return None def find_game_by_teams( games: List[Dict[str, Any]], team1: str, team2: Optional[str] = None ) -> Optional[Dict[str, Any]]: """ Find a game by team names. Uses improved matching to handle team name variations. Args: games: List of game data from The Odds API team1: First team name (home or away) team2: Optional second team name for exact match Returns: Matching game data or None if not found """ if not games: return None from src.team_normalizer import normalize_team_name team1_normalized = normalize_team_name(team1) team1_lower = team1.lower() for game in games: home_team = game.get("home_team", "") away_team = game.get("away_team", "") home_normalized = normalize_team_name(home_team) away_normalized = normalize_team_name(away_team) home_lower = home_team.lower() away_lower = away_team.lower() # Check multiple matching strategies team1_matches_home = ( team1_normalized == home_normalized or team1_normalized in home_normalized or home_normalized in team1_normalized or team1_lower in home_lower or home_lower in team1_lower ) team1_matches_away = ( team1_normalized == away_normalized or team1_normalized in away_normalized or away_normalized in team1_normalized or team1_lower in away_lower or away_lower in team1_lower ) if team1_matches_home or team1_matches_away: # If team2 is provided, check for match if team2: team2_normalized = normalize_team_name(team2) team2_lower = team2.lower() team2_matches_home = ( team2_normalized == home_normalized or team2_normalized in home_normalized or home_normalized in team2_normalized or team2_lower in home_lower or home_lower in team2_lower ) team2_matches_away = ( team2_normalized == away_normalized or team2_normalized in away_normalized or away_normalized in team2_normalized or team2_lower in away_lower or away_lower in team2_lower ) if team2_matches_home or team2_matches_away: return game else: return game return None def find_next_game_for_team( games: List[Dict[str, Any]], team_name: str ) -> Optional[Dict[str, Any]]: """ Find the next scheduled game for a team. Uses improved matching to handle team name variations. Args: games: List of game data from The Odds API team_name: Team name to search for Returns: Next upcoming game for the team or None if not found """ if not games: return None from src.team_normalizer import normalize_team_name team_normalized = normalize_team_name(team_name) team_lower = team_name.lower() now = datetime.now() next_game = None next_game_time = None for game in games: home_team = game.get("home_team", "") away_team = game.get("away_team", "") home_normalized = normalize_team_name(home_team) away_normalized = normalize_team_name(away_team) home_lower = home_team.lower() away_lower = away_team.lower() commence_time_str = game.get("commence_time", "") # Check if team matches using multiple strategies team_matches = ( team_normalized == home_normalized or team_normalized == away_normalized or team_normalized in home_normalized or team_normalized in away_normalized or home_normalized in team_normalized or away_normalized in team_normalized or team_lower in home_lower or team_lower in away_lower or home_lower in team_lower or away_lower in team_lower ) if not team_matches: continue # Parse commence time try: if commence_time_str: commence_time = datetime.fromisoformat(commence_time_str.replace('Z', '+00:00')) # Convert to naive datetime for comparison (or handle timezone properly) if commence_time.tzinfo: from datetime import timezone now_aware = now.replace(tzinfo=timezone.utc) if commence_time > now_aware: if next_game_time is None or commence_time < next_game_time: next_game = game next_game_time = commence_time except (ValueError, AttributeError): # If date parsing fails, still consider it if it's in the list # (might be a future game) if next_game is None: next_game = game return next_game def format_next_game_odds( team_name: str, game: Dict[str, Any] ) -> Dict[str, Any]: """ Format next game odds response. Args: team_name: Name of the team game: Game data from The Odds API Returns: Formatted response with next game and odds """ home_team = game.get("home_team", "") away_team = game.get("away_team", "") commence_time = game.get("commence_time", "") # Determine opponent is_home = team_name.lower() in home_team.lower() opponent = away_team if is_home else home_team # Extract odds from bookmakers bookmakers = game.get("bookmakers", []) odds_data = { "spread": None, "moneyline": {}, "over_under": None } if bookmakers: # Use the first bookmaker's odds bookmaker = bookmakers[0] markets = bookmaker.get("markets", []) for market in markets: market_key = market.get("key", "") outcomes = market.get("outcomes", []) if market_key == "spreads": # Extract spread odds for outcome in outcomes: team = outcome.get("name", "") point = outcome.get("point", 0) price = outcome.get("price", 0) if team.lower() == team_name.lower(): # Format spread (e.g., "Team A -6.5" or "Team B +6.5") spread_sign = "-" if point > 0 else "+" odds_data["spread"] = { "team": f"{team_name} {spread_sign}{abs(point)}", "point": point, "odds": price } if len(outcomes) > 1: other_outcome = outcomes[1] if outcomes[0].get("name") == team else outcomes[0] odds_data["spread"]["opponent_odds"] = other_outcome.get("price", 0) elif market_key == "h2h": # Extract moneyline odds for outcome in outcomes: team = outcome.get("name", "") price = outcome.get("price", 0) if team.lower() == team_name.lower(): odds_data["moneyline"]["team"] = price elif team.lower() == opponent.lower(): odds_data["moneyline"]["opponent"] = price elif market_key == "totals": # Extract over/under for outcome in outcomes: if outcome.get("name", "").lower() == "over": odds_data["over_under"] = { "total": outcome.get("point", 0), "over_odds": outcome.get("price", 0), "under_odds": outcomes[1].get("price", 0) if len(outcomes) > 1 else None } return { "team": team_name, "next_game": { "opponent": opponent, "date": commence_time, "location": "Home" if is_home else "Away" }, "odds": odds_data } def format_game_odds_response(game: Dict[str, Any]) -> Dict[str, Any]: """ Format game odds data into a structured response. Args: game: Raw game data from The Odds API Returns: Formatted game data with odds and scores """ home_team = game.get("home_team", "") away_team = game.get("away_team", "") commence_time = game.get("commence_time", "") sport_key = game.get("sport_key", "") # Extract scores if available (The Odds API may include scores for completed games) scores = game.get("scores", []) home_score = None away_score = None status = "scheduled" # Check if game time has passed to determine status if commence_time: try: from datetime import timezone game_time = datetime.fromisoformat(commence_time.replace('Z', '+00:00')) now = datetime.now(timezone.utc) if scores: for score in scores: if score.get("name") == home_team: home_score = score.get("score") elif score.get("name") == away_team: away_score = score.get("score") if home_score is not None and away_score is not None: status = "completed" elif game_time <= now: # Game time has passed but no scores - likely in progress or just started status = "in_progress" except (ValueError, AttributeError): # If date parsing fails, fall back to default pass # Extract odds from bookmakers bookmakers = game.get("bookmakers", []) odds_data = { "spread": None, "moneyline": {}, "over_under": None } if bookmakers: # Use the first bookmaker's odds (or could aggregate) bookmaker = bookmakers[0] markets = bookmaker.get("markets", []) for market in markets: market_key = market.get("key", "") outcomes = market.get("outcomes", []) if market_key == "spreads": # Extract spread odds for outcome in outcomes: team = outcome.get("name", "") point = outcome.get("point", 0) price = outcome.get("price", 0) if team == home_team: odds_data["spread"] = { "home_team": point, "away_team": -point, "home_odds": price, "away_odds": outcomes[1].get("price", 0) if len(outcomes) > 1 else None } elif market_key == "h2h": # Extract moneyline odds for outcome in outcomes: team = outcome.get("name", "") price = outcome.get("price", 0) if team == home_team: odds_data["moneyline"]["home_team"] = price elif team == away_team: odds_data["moneyline"]["away_team"] = price elif market_key == "totals": # Extract over/under for outcome in outcomes: if outcome.get("name", "").lower() == "over": odds_data["over_under"] = { "total": outcome.get("point", 0), "over_odds": outcome.get("price", 0), "under_odds": outcomes[1].get("price", 0) if len(outcomes) > 1 else None } return { "home_team": home_team, "away_team": away_team, "start_time": commence_time, "status": status, "score": { "home": home_score, "away": away_score } if home_score is not None and away_score is not None else None, "odds": odds_data }

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/gedin-eth/cfb-mcp'

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