Skip to content

API DOCUMENTATION

github-actions[bot] edited this page Mar 14, 2026 · 1 revision

Sports-Odds-MCP - Complete API Documentation

Comprehensive API reference for MCP tools, Backend HTTP endpoints, and Frontend architecture.


Table of Contents

  1. MCP API (Claude Desktop Integration)
  2. Backend REST API (Dashboard)
  3. Frontend Architecture

MCP API (Claude Desktop Integration)

The MCP server exposes 30+ tools to Claude Desktop via stdio transport. All responses follow the pattern:

{
  "success": true,
  "data": { ... },
  "error": "error message" // only on failure
}

The Odds API Tools

get_available_sports(all_sports: bool = False)

List all available sports from The Odds API.

Parameters:

  • all_sports (bool): If False (default), returns only in-season sports

Response:

{
  "success": true,
  "data": [
    {
      "key": "americanfootball_nfl",
      "title": "NFL",
      "group": "American Football",
      "active": true,
      "has_outrights": false
    }
  ]
}

get_odds(sport, regions="us", markets=None, odds_format="american", date_format="iso")

Get current betting odds for upcoming games.

Parameters:

  • sport (str): Sport key (e.g., americanfootball_nfl, basketball_nba)
  • regions (str): Comma-separated bookmaker regions (us, us2, uk, au, eu)
  • markets (str): Comma-separated markets (h2h, spreads, totals, player_points, etc.)
  • odds_format (str): american, decimal, or fractional
  • date_format (str): iso or unix

Markets:

  • Game Markets: h2h (moneyline), spreads, totals, outrights
  • Player Props: 70+ markets including player_points, player_rebounds, player_assists, player_pass_tds, player_rush_yds, player_home_runs, player_shots_on_goal

Response:

{
  "success": true,
  "data": [
    {
      "id": "event_id",
      "sport_key": "basketball_nba",
      "commence_time": "2026-01-09T19:00:00Z",
      "home_team": "Los Angeles Lakers",
      "away_team": "Boston Celtics",
      "bookmakers": [
        {
          "key": "draftkings",
          "title": "DraftKings",
          "markets": [
            {
              "key": "h2h",
              "outcomes": [
                {"name": "Los Angeles Lakers", "price": -150},
                {"name": "Boston Celtics", "price": +130}
              ]
            }
          ]
        }
      ]
    }
  ],
  "usage": {
    "remaining": "492",
    "used": "8"
  }
}

get_scores(sport, days_from=3)

Get live and recent scores.

Parameters:

  • sport (str): Sport key
  • days_from (int): Number of days back to retrieve scores

Response:

{
  "success": true,
  "data": [
    {
      "id": "event_id",
      "sport_key": "basketball_nba",
      "commence_time": "2026-01-08T19:00:00Z",
      "completed": true,
      "home_team": "Los Angeles Lakers",
      "away_team": "Boston Celtics",
      "scores": [
        {"name": "Los Angeles Lakers", "score": "112"},
        {"name": "Boston Celtics", "score": "108"}
      ]
    }
  ]
}

get_event_odds(sport, event_id, markets=None, regions="us")

Get detailed odds for a specific event.

search_odds(query, sport=None, markets="h2h")

Natural language search for odds by team name or matchup.

Parameters:

  • query (str): Team name or matchup (e.g., "Lakers", "Lakers vs Celtics")
  • sport (str): Optional sport key to narrow search
  • markets (str): Comma-separated markets to include

ESPN API Tools

get_espn_scoreboard(sport, league, date=None, limit=10)

Get raw ESPN scoreboard data (JSON).

Parameters:

  • sport (str): Sport type (football, basketball, baseball, hockey, soccer)
  • league (str): League code (nfl, nba, mlb, nhl, etc.)
  • date (str): Optional date in YYYYMMDD format
  • limit (int): Max games (default 10, max 25)

Response:

{
  "success": true,
  "data": {
    "leagues": [...],
    "events": [
      {
        "id": "401547516",
        "name": "Los Angeles Lakers at Boston Celtics",
        "shortName": "LAL @ BOS",
        "competitions": [...]
      }
    ]
  }
}

get_formatted_scoreboard(sport, league, date=None, limit=10)

Get formatted Markdown scoreboard table.

Returns: Markdown table with:

  • Team names, scores
  • Game status (🔴 LIVE, ✅ FINAL, 🕐 Scheduled)
  • Time/broadcast info

get_visual_scoreboard(sport, league, date=None, limit=10)

Get interactive React artifact scoreboard.

Returns: Structured data for Claude to render as interactive cards with team colors, logos, expandable odds.

get_matchup_cards(sport, league, limit=10)

Get ASCII art matchup cards.

Returns: Text with box-drawing characters:

┌────────────────────────────────────────────────────────────────┐
│                                                                │
│                            MATCHUP                             │
│                                                                │
├────────────────────────────────────────────────────────────────┤
│                                                                │
│          Los Angeles Lakers  vs  Boston Celtics                │
│                                                                │
│                           112  -  108                          │
│                                                                │
│                  Tue, Jan 08 @ 07:00 PM                        │
│                                                                │
└────────────────────────────────────────────────────────────────┘

get_espn_standings(sport, league, season=None)

Get league standings.

get_espn_teams(sport, league)

List all teams in a league.

get_espn_team_details(sport, league, team_id)

Get detailed team information.

get_espn_team_schedule(sport, league, team_id, season=None)

Get team schedule and results.

get_espn_news(sport, league, limit=10)

Get latest news articles.

search_espn(query, sport=None)

Search for teams/players/content.

Combined Tools

get_comprehensive_game_info(sport, league, event_id)

Combines ESPN game data with betting odds.

get_odds_comparison(sport, event_id=None, query=None)

Compare odds across bookmakers in Markdown table.

Utility Tools

get_team_reference_table(sport)

Get quick reference table of team IDs, abbreviations, and divisions.

Parameters:

  • sport (str): nfl, nba, or nhl

Returns: Markdown table with all teams.

find_team_id_by_name(team_name, sport)

Fuzzy match team name to ESPN ID.


Backend REST API (Dashboard)

Express + TypeScript REST API for the web dashboard. Base URL: http://localhost:3001/api

Authentication

All /api/mcp/* routes require authentication via Bearer token:

Authorization: Bearer <token>

Response Format

All endpoints return:

{
  "status": "success" | "error",
  "data": { ... },  // on success
  "message": "...", // on error
  "error": "..."    // detailed error message
}

Authentication API (/api/auth)

OAuth2 authentication with Microsoft and Google providers.

GET /api/auth/status

Get authentication status and available providers.

Response:

{
  "authEnabled": true,
  "authMode": "oauth2",
  "user": {
    "id": "uuid",
    "email": "user@example.com",
    "name": "John Doe",
    "provider": "microsoft"
  },
  "providers": {
    "microsoft": true,
    "google": true
  }
}

GET /api/auth/microsoft

Initiate Microsoft/Azure AD OAuth2 flow.

Redirects to Microsoft login page with configured scopes (openid, profile, email).

GET /api/auth/microsoft/callback

OAuth2 callback endpoint (handled automatically).

GET /api/auth/google

Initiate Google OAuth2 flow.

Redirects to Google login page with configured scopes.

GET /api/auth/google/callback

Google OAuth2 callback endpoint (handled automatically).

POST /api/auth/logout

Log out current user and destroy session.

Response:

{
  "success": true
}

GET /api/auth/me

Get current authenticated user.

Response:

{
  "user": {
    "id": "uuid",
    "email": "user@example.com",
    "name": "John Doe",
    "provider": "microsoft"
  },
  "authEnabled": true
}

401 Response if not authenticated:

{
  "error": "Not authenticated"
}

API Keys API (/api/keys)

Manage API keys for programmatic access (alternative to OAuth2).

GET /api/keys

List all API keys for current user.

Response:

{
  "success": true,
  "data": {
    "keys": [
      {
        "id": "uuid",
        "name": "Production Bot",
        "keyPrefix": "btk_abc123",
        "permissions": {
          "read": true,
          "write": true,
          "bets": true,
          "stats": true
        },
        "lastUsedAt": "2026-01-13T10:30:00Z",
        "expiresAt": "2027-01-13T00:00:00Z",
        "revoked": false,
        "createdAt": "2026-01-01T00:00:00Z",
        "updatedAt": "2026-01-13T10:30:00Z"
      }
    ]
  }
}

Note: Full API key is never returned in list endpoints. keyPrefix shows first 10 characters only.

POST /api/keys

Create a new API key.

Request Body:

{
  "name": "Production Bot",
  "permissions": {
    "read": true,
    "write": true,
    "bets": true,
    "stats": true
  },
  "expiresAt": "2027-01-13T00:00:00Z"  // optional
}

Response:

{
  "success": true,
  "data": {
    "key": "btk_abc123xyz789def456ghi...",  // Full key - only shown ONCE!
    "id": "uuid",
    "name": "Production Bot",
    "keyPrefix": "btk_abc123",
    "permissions": { ... },
    "expiresAt": "2027-01-13T00:00:00Z",
    "createdAt": "2026-01-13T10:30:00Z"
  },
  "message": "API key created successfully. Save this key - it will not be shown again."
}

⚠️ CRITICAL: The full API key is only returned on creation. Save it immediately - it cannot be retrieved later.

PUT /api/keys/:id

Update API key name or permissions.

Request Body:

{
  "name": "Updated Name",
  "permissions": {
    "read": true,
    "write": false,
    "bets": true,
    "stats": true
  }
}

Response:

{
  "success": true,
  "data": {
    "id": "uuid",
    "name": "Updated Name",
    "permissions": { ... },
    "updatedAt": "2026-01-13T11:00:00Z"
  }
}

Note: Cannot update keyHash, keyPrefix, expiresAt, or revoked via this endpoint.

DELETE /api/keys/:id

Revoke (soft delete) an API key.

Response:

{
  "success": true,
  "message": "API key revoked successfully"
}

Note: Revoked keys are not deleted but marked as revoked: true. They can no longer be used for authentication.

Using API Keys

Include API key in request header:

Authorization: Bearer btk_abc123xyz789def456ghi...

Or as query parameter (not recommended for production):

GET /api/games?apiKey=btk_abc123xyz789def456ghi...

Authentication Order:

  1. Check for API key in Authorization header
  2. Check for API key in query parameter
  3. Check for session authentication (OAuth2)

Bets API (/api/bets)

GET /api/bets

List bets with filters.

Query Parameters:

  • status (string): Filter by status (pending, won, lost, push, cancelled)
  • betType (string): Filter by type (single, parlay, teaser)
  • sportKey (string): Filter by sport
  • startDate (ISO string): Filter by date range start
  • endDate (ISO string): Filter by date range end
  • limit (number): Max results (default: 50)
  • offset (number): Pagination offset

Response:

{
  "status": "success",
  "data": {
    "bets": [
      {
        "id": "uuid",
        "name": "Lakers ML",
        "betType": "single",
        "stake": 100.00,
        "status": "pending",
        "oddsAtPlacement": -150,
        "potentialPayout": 166.67,
        "createdAt": "2026-01-08T10:00:00Z",
        "legs": [
          {
            "id": "uuid",
            "gameId": "uuid",
            "selectionType": "moneyline",
            "selection": "home",
            "teamName": "Los Angeles Lakers",
            "odds": -150,
            "status": "pending",
            "game": {
              "awayTeamName": "Boston Celtics",
              "homeTeamName": "Los Angeles Lakers",
              "commenceTime": "2026-01-09T19:00:00Z"
            }
          }
        ]
      }
    ],
    "total": 42,
    "limit": 50,
    "offset": 0
  }
}

POST /api/bets

Create a new bet.

Request Body:

{
  "name": "Lakers ML + Celtics Spread",
  "betType": "parlay",
  "stake": 50.00,
  "legs": [
    {
      "gameId": "uuid",
      "selectionType": "moneyline",
      "selection": "home",
      "teamName": "Los Angeles Lakers",
      "odds": -150
    },
    {
      "gameId": "uuid",
      "selectionType": "spread",
      "selection": "away",
      "teamName": "Boston Celtics",
      "line": -5.5,
      "odds": -110
    }
  ],
  "notes": "Feeling confident about both teams"
}

Response:

{
  "status": "success",
  "data": {
    "id": "uuid",
    "name": "Lakers ML + Celtics Spread",
    "betType": "parlay",
    "stake": 50.00,
    "status": "pending",
    "oddsAtPlacement": 179.55,
    "potentialPayout": 139.77,
    "legs": [...]
  }
}

GET /api/bets/:id

Get single bet with full details.

PATCH /api/bets/:id

Update bet (name, stake, notes).

Request Body:

{
  "name": "Updated Name",
  "stake": 75.00,
  "notes": "Updated notes"
}

DELETE /api/bets/:id

Delete a bet.

POST /api/bets/:id/settle

Manually settle a bet.

Request Body:

{
  "status": "won",
  "actualPayout": 166.67
}

GET /api/bets/stats

Get betting statistics.

Query Parameters:

  • sportKey (string): Filter by sport
  • betType (string): Filter by bet type
  • startDate (ISO string): Date range start
  • endDate (ISO string): Date range end

Response:

{
  "status": "success",
  "data": {
    "totalBets": 142,
    "totalStake": 7150.00,
    "totalPayout": 8234.50,
    "netProfit": 1084.50,
    "roi": 15.17,
    "winRate": 54.23,
    "avgOdds": -114.5,
    "byStatus": {
      "pending": 12,
      "won": 77,
      "lost": 48,
      "push": 5
    },
    "byType": {
      "single": 89,
      "parlay": 53
    },
    "bySport": {
      "basketball_nba": { "bets": 45, "netProfit": 523.50 },
      "americanfootball_nfl": { "bets": 62, "netProfit": 561.00 }
    }
  }
}

Games API (/api/games)

GET /api/games

List games with filters (timezone-aware).

Query Parameters:

  • date (YYYY-MM-DD): Filter by specific date in user's local timezone
  • sport (string): Sport key (e.g., basketball_nba, americanfootball_nfl, or all)
  • status (string): Game status (scheduled, in_progress, completed)
  • timezoneOffset (number): User's timezone offset in minutes (e.g., 420 for MST/UTC-7)

Timezone Handling: The API accepts the user's timezone offset to correctly filter games by date. For example:

  • User in MST (UTC-7) requests date=2026-01-09&timezoneOffset=420
  • API converts to UTC range: 2026-01-09 07:00:00Z to 2026-01-10 06:59:59Z
  • Returns all games occurring during Jan 9 in MST

Response:

{
  "status": "success",
  "data": {
    "games": [
      {
        "id": "uuid",
        "exterKey": "basketball_nba",
        "sportName": "NBA Basketball",
        "sport": {
          "id": "uuid",
          "key": "basketball_nba",
          "name": "NBA Basketball",
          "groupName": "Basketball",
          "isActive": trueltics",
        "homeTeamName": "Los Angeles Lakers",
        "commenceTime": "2026-01-09T19:00:00Z",
        "status": "scheduled",
        "completed": false,
        "sport": {
          "key": "basketball_nba",
          "name": "NBA Basketball"
        },
        "currentOdds": [
          {
            "id": "uuid",
            "bookmaker": "draftkings",
            "marketType": "h2h",
            "selection": "home",
            "price": -150,
            "lastUpdated": "2026-01-08T15:30:00Z"
          }
        ]
      }
    ],
    "count": 15
  }
}
**Response:**
```json
{
  "status": "success",
  "data": {
    "id": "uuid",
    "externalId": "odds_api_event_id",
    "sportId": "uuid",
    "awayTeamName": "Boston Celtics",
    "homeTeamName": "Los Angeles Lakers",
    "commenceTime": "2026-01-09T19:00:00Z",
    "statgameId": "uuid",
        "bookmaker": "draftkings",
        "marketType": "spread",
        "selection": "home",
        "price": -110,
        "point": -5.5
      }
    ],
    "count": 245
  }
}

Futures API (/api/futures)

Manage futures bets (championship winners, season awards, long-term props).

GET /api/futures

Get all active futures with current odds.

Query Parameters:

  • sportKey (string): Filter by sport (e.g., basketball_nba, americanfootball_nfl)
  • status (string): Filter by status (active, completed, settled) - default: active

Response:

{
  "futures": [
    {
      "id": "uuid",
      "externalId": "odds_api_future_id",
      "sportId": "uuid",
      "sport": {
        "key": "basketball_nba",
        "name": "NBA Basketball"
      },
      "title": "NBA Championship Winner 2025-26",
      "description": "Which team will win the 2026 NBA Championship?",
      "status": "active",
      "commenceTime": "2025-10-22T00:00:00Z",
      "closeTime": "2026-06-01T00:00:00Z",
      "settleTime": null,
      "currentOdds": [
        {
          "id": "uuid",
          "outcome": "Boston Celtics",
          "bookmaker": "draftkings",
          "price": 400,
          "lastUpdated": "2026-01-13T10:00:00Z"
        },
        {
          "outcome": "Los Angeles Lakers",
          "bookmaker": "draftkings",
          "price": 650,
          "lastUpdated": "2026-01-13T10:00:00Z"
        }
      ],
      "groupedOutcomes": [
        {
          "outcome": "Boston Celtics",
          "bookmakers": [
            { "bookmaker": "draftkings", "price": 400 },
            { "bookmaker": "fanduel", "price": 380 },
            { "bookmaker": "betmgm", "price": 425 }
          ],
          "bestOdds": 425
        }
      ],
      "createdAt": "2025-10-01T00:00:00Z",
      "updatedAt": "2026-01-13T10:00:00Z"
    }
  ]
}

Note: groupedOutcomes consolidates all bookmaker odds for each outcome, making it easier to compare odds across sportsbooks.

GET /api/futures/:id

Get a specific future with all odds and historical snapshots.

Response:

{
  "id": "uuid",
  "externalId": "odds_api_future_id",
  "sportId": "uuid",
  "sport": {
    "key": "basketball_nba",
    "name": "NBA Basketball"
  },
  "title": "NBA Championship Winner 2025-26",
  "description": "Which team will win the 2026 NBA Championship?",
  "status": "active",
  "commenceTime": "2025-10-22T00:00:00Z",
  "closeTime": "2026-06-01T00:00:00Z",
  "currentOdds": [ ... ],
  "groupedOutcomes": [
    {
      "outcome": "Boston Celtics",
      "bookmakers": [
        {
          "bookmaker": "draftkings",
          "price": 400,
          "lastUpdated": "2026-01-13T10:00:00Z"
        }
      ],
      "bestOdds": 425,
      "averageOdds": 402
    }
  ],
  "oddsSnapshots": [
    {
      "id": "uuid",
      "outcome": "Boston Celtics",
      "bookmaker": "draftkings",
      "price": 380,
      "capturedAt": "2026-01-12T10:00:00Z"
    },
    {
      "outcome": "Boston Celtics",
      "bookmaker": "draftkings",
      "price": 400,
      "capturedAt": "2026-01-13T10:00:00Z"
    }
  ],
  "outcomes": [
    {
      "id": "uuid",
      "outcome": "Boston Celtics",
      "status": "pending",
      "settledAt": null
    }
  ]
}

Line Movement Data: The oddsSnapshots array contains the last 100 historical snapshots for tracking odds movement over time. Use this data to:

  • Display line movement charts
  • Identify best odds entry points
  • Track market sentiment shifts

AI Bets API (/api/ai/bets)

Simplified bet creation endpoint for AI/MCP integration using game IDs.

Authentication: Requires API key via apiKeyAuth middleware.

POST /api/ai/bets

Create a bet from AI-extracted data.

Request Body:

{
  "selections": [
    {
      "gameId": "uuid",
      "type": "moneyline",
      "selection": "home",
      "odds": -150,
      "teamName": "Los Angeles Lakers"
    },
    {
      "gameId": "uuid2",
      "type": "spread",
      "selection": "away",
      "odds": -110,
      "line": 5.5,
      "teamName": "Boston Celtics"
    }
  ],
  "betType": "parlay",
  "stake": 100.00,
  "notes": "AI recommendation from Claude",
  "source": "mcp"
}

Field Definitions:

  • selections (array, required): Array of bet selections
    • gameId (string, required): UUID of the game from database
    • type (string, required): moneyline, spread, or total
    • selection (string, required): home, away, over, or under
    • odds (number, required): American odds (e.g., -150, +200)
    • line (number, required for spread/total): The line/point value
    • teamName (string, optional): Display name for the selection
  • betType (string, required): single, parlay, or teaser
  • stake (number, required): Amount to wager
  • teaserPoints (number, optional): Points for teaser (6, 6.5, or 7)
  • notes (string, optional): Additional notes about the bet
  • source (string, optional): Source of bet (mcp, image, text, conversation) - default: mcp

Response:

{
  "success": true,
  "data": {
    "bet": {
      "id": "uuid",
      "name": "2-Leg Parlay",
      "betType": "parlay",
      "stake": 100.00,
      "status": "pending",
      "oddsAtPlacement": 264,
      "potentialPayout": 364.00,
      "notes": "AI recommendation from Claude",
      "source": "mcp",
      "createdAt": "2026-01-13T10:30:00Z",
      "legs": [
        {
          "id": "uuid1",
          "gameId": "uuid",
          "selectionType": "moneyline",
          "selection": "home",
          "teamName": "Los Angeles Lakers",
          "odds": -150,
          "status": "pending"
        },
        {
          "id": "uuid2",
          "gameId": "uuid2",
          "selectionType": "spread",
          "selection": "away",
          "teamName": "Boston Celtics",
          "line": 5.5,
          "odds": -110,
          "status": "pending"
        }
      ]
    },
    "isSameGameParlay": false,
    "parlayCalculation": {
      "decimalOdds": [1.67, 1.91],
      "combinedDecimal": 3.19,
      "americanOdds": 264,
      "potentialPayout": 364.00
    }
  }
}

Same Game Parlay (SGP) Detection: The endpoint automatically detects when multiple selections are from the same game:

  • Groups selections by gameId
  • For SGP games, multiplies all leg odds together
  • Sets isSameGameParlay: true in response
  • Provides detailed parlay calculation breakdown

Validation:

  • All gameId values must exist in database (404 if not found)
  • type must be valid: moneyline, spread, total
  • selection must be valid: home, away, over, under
  • odds is required for all selections
  • line is required for spread and total bets
  • stake must be positive number
  • betType must be single, parlay, or teaser

Error Responses:

400 - Missing or invalid fields:

{
  "success": false,
  "error": "Selection 1 missing required odds value"
}

400 - Game IDs not found:

{
  "success": false,
  "error": "Some game IDs not found",
  "details": {
    "requestedGames": 2,
    "foundGames": 1,
    "missingGameIds": ["uuid2"]
  }
}

Stats API (/api/stats)

Retrieve live game statistics, player performance, and historical team data from API-Sports integration.

GET /api/stats/game/:gameId

Get comprehensive game statistics including live scores, team stats, player stats, and season averages.

Path Parameters:

  • gameId (string): The game's unique identifier

Response:

{
  "teamStats": [
    {
      "id": "uuid",
      "gameId": "uuid",
      "teamId": "uuid",
      "isHome": true,
      "homeScore": 105,
      "awayScore": 98,
      "quarterScores": [28, 25, 27, 25],
      "stats": {
        "field_goals": 42,
        "field_goal_percentage": 48.3,
        "three_pointers": 12,
        "rebounds": 45,
        "assists": 24,
        "steals": 8,
        "blocks": 5,
        "turnovers": 12
      },
      "team": {
        "id": "uuid",
        "name": "Los Angeles Lakers",
        "abbreviation": "LAL"
      }
    }
  ],
  "playerStats": [
    {
      "id": "uuid",
      "gameId": "uuid",
      "playerId": "uuid",
      "stats": {
        "minutes": "35:24",
        "points": 28,
        "rebounds": 8,
        "assists": 6,
        "field_goals_made": 10,
        "field_goals_attempts": 18,
        "three_pointers_made": 4,
        "free_throws_made": 4
      },
      "player": {
        "id": "uuid",
        "name": "LeBron James",
        "externalId": "237"
      }
    }
  ],
  "seasonAverages": [
    {
      "teamId": "uuid",
      "totalGames": 45,
      "homeGames": 23,
      "awayGames": 22,
      "avgStats": {
        "points": 112.5,
        "field_goal_percentage": 46.8,
        "three_pointers": 13.2,
        "rebounds": 44.3,
        "assists": 26.1,
        "steals": 7.8,
        "blocks": 5.2
      }
    }
  ]
}

Features:

  • Live Updates: Real-time scores and stats during games
  • Quarter Breakdown: Period-by-period scoring (NBA: 4 quarters, NFL: 4 quarters, NHL: 3 periods, Soccer: final score)
  • Season Averages: Calculated averages across all games this season for both teams
  • Home/Away Splits: Separate statistics for home and away performances

GET /api/stats/team/:teamId

Get team season statistics with home/away filtering and historical game log.

Path Parameters:

  • teamId (string): The team's unique identifier

Query Parameters:

  • season (number, optional): Specific season year (defaults to current season)
  • location (string, optional): Filter by location - home, away, or all (default: all)

Response:

{
  "seasonStats": {
    "teamId": "uuid",
    "season": 2026,
    "gamesPlayed": 45,
    "wins": 32,
    "losses": 13,
    "avgPoints": 112.5,
    "avgOpponentPoints": 106.8
  },
  "gameHistory": [
    {
      "id": "uuid",
      "gameId": "uuid",
      "teamId": "uuid",
      "isHome": true,
      "homeScore": 105,
      "awayScore": 98,
      "stats": { "..." },
      "game": {
        "id": "uuid",
        "commenceTime": "2026-01-28T19:00:00Z",
        "homeTeam": { "name": "Lakers" },
        "awayTeam": { "name": "Celtics" }
      }
    }
  ],
  "splits": {
    "home": {
      "gamesPlayed": 23,
      "avgPoints": 115.2,
      "avgRebounds": 46.1,
      "avgAssists": 27.3,
      "field_goal_percentage": 48.5
    },
    "away": {
      "gamesPlayed": 22,
      "avgPoints": 109.7,
      "avgRebounds": 42.5,
      "avgAssists": 24.9,
      "field_goal_percentage": 45.1
    },
    "overall": {
      "gamesPlayed": 45,
      "avgPoints": 112.5,
      "avgRebounds": 44.3,
      "avgAssists": 26.1,
      "field_goal_percentage": 46.8
    }
  }
}

Features:

  • Location Filtering: View stats for home games only, away games only, or all games
  • Split Statistics: Compare home vs away performance automatically
  • Historical Averages: Season-long averages for all numeric stats
  • Game Log: Up to 20 most recent games with full stats

GET /api/stats/player/:playerId

Get individual player statistics and game log.

Path Parameters:

  • playerId (string): The player's unique identifier

Query Parameters:

  • season (number, optional): Specific season year (defaults to current season)
  • limit (number, optional): Number of recent games to return (default: 10, max: 50)

Response:

{
  "player": {
    "id": "uuid",
    "externalId": "237",
    "name": "LeBron James",
    "sport": "basketball_nba",
    "team": {
      "id": "uuid",
      "name": "Los Angeles Lakers",
      "abbreviation": "LAL"
    }
  },
  "seasonStats": {
    "gamesPlayed": 42,
    "avgPoints": 27.8,
    "avgRebounds": 7.2,
    "avgAssists": 8.5,
    "field_goal_percentage": 52.3,
    "three_point_percentage": 38.9,
    "free_throw_percentage": 73.1
  },
  "gameLog": [
    {
      "id": "uuid",
      "gameId": "uuid",
      "stats": {
        "minutes": "35:24",
        "points": 28,
        "rebounds": 8,
        "assists": 6
      },
      "game": {
        "id": "uuid",
        "commenceTime": "2026-01-28T19:00:00Z",
        "homeTeam": { "name": "Lakers" },
        "awayTeam": { "name": "Celtics" }
      }
    }
  ]
}

Supported Sports:

  • Basketball (NBA, NCAAB): Points, rebounds, assists, shooting percentages, steals, blocks, turnovers
  • Football (NFL, NCAAF): Passing yards/TDs, rushing yards/TDs, receptions, receiving yards, tackles, sacks
  • Hockey (NHL): Goals, assists, points, shots on goal, plus/minus, penalty minutes
  • Soccer (EPL, MLS, etc.): Goals, assists, shots, passes, tackles, interceptions, cards, goalkeeper saves

Notes:

  • Stats sync runs every 15 seconds during game hours (10 AM - 2 AM ET)
  • Requires API_SPORTS_KEY environment variable to be configured
  • Free tier API-Sports accounts limited to 10 requests/day
  • Rate limiting: 5 requests/second for Pro tier

Admin API (/api/admin)

Administrative endpoints for system management and data initialization.

POST /api/admin/init-sports

Initialize sports data in database.

Response:

{
  "status": "success",
  "message": "Initialized 7 sports",
  "data": [
    {
      "id": "uuid",
      "key": "americanfootball_nfl",
      "name": "NFL",
      "groupName": "American Football",
      "isActive": true
    }
  ]
}

Sports Initialized:

  • NFL (americanfootball_nfl)
  • NBA (basketball_nba)
  • NCAAB (basketball_ncaab)
  • NHL (icehockey_nhl)
  • MLB (baseball_mlb)
  • EPL (soccer_epl) - inactive by default
  • UEFA Champions League (soccer_uefa_champs_league) - inactive by default

POST /api/admin/sync-odds

Manually trigger odds synchronization from The Odds API.

Request Body:

{
  "sportKey": "basketball_nba"  // optional, omit to sync all active sports
}

Response:

{
  "status": "success",
  "message": "Odds sync started in background",
  "data": {
    "sportKey": "basketball_nba"
  }
}

Note: Sync runs asynchronously. Check logs for progress and completion.

POST /api/admin/resolve-outcomes

Manually trigger bet outcome resolution.

Checks completed games and automatically settles bets based on results.

Response:

{
  "status": "success",
  "message": "Outcome resolution started in background"
}

GET /api/admin/stats

Get database statistics and overview.

Response:

{
  "status": "success",
  "data": {
    "database": {
      "sports": 7,
      "teams": 124,
      "games": 458,
      "currentOdds": 6847,
      "oddsSnapshots": 45283,
      "bets": 142,
      "betLegs": 267
    },
    "activeSports": [
      { "key": "americanfootball_nfl", "name": "NFL" },
      { "key": "basketball_nba", "name": "NBA" }
    ],
    "recentGames": 127
  }
}

GET /api/admin/health

Detailed health check with database connectivity test.

Response:

{
  "status": "success",
  "data": {
    "database": "connected",
    "dataInitialized": true,
    "hasGames": true,
    "timestamp": "2026-01-09T15:30:00Z"
}

GET /api/games/:id/odds

Get current odds for a specific game.

Query Parameters:

  • bookmaker (string): Filter by bookmaker key (e.g., draftkings)
  • marketType (string): Filter by market type (h2h, spreads, totals)

Response:

{

#### `GET /api/games/:id/odds/history`
Get historical odds snapshots for line movement tracking.

**Query Parameters:**
- `bookmaker` (string): Filter by bookmaker key
- `marketType` (string): Filter by market type
- `hours` (number): Number of hours back to retrieve (default: 24)
#### `GET /api/games/:id`
Get single game with all odds.

#### `GET /api/games/:id/odds-history`
Get historical odds snapshots for line movement tracking.

**Response:**
```json
{
  "status": "success",
  "data": {
    "snapshots": [
      {
        "timestamp": "2026-01-08T10:00:00Z",
        "bookmaker": "draftkings",
        "marketType": "spread",
        "selection": "home",
        "price": -110,
        "point": -5.5
      }
    ]
  }
}

MCP Integration API (/api/mcp)

Authentication Required - All routes require Bearer token.

GET /api/mcp/bets/active

Get all pending bets for MCP integration.

Response:

{
  "status": "success",
  "data": {
    "activeBets": [
      {
        "id": "uuid",
        "name": "Lakers ML",
        "betType": "single",
        "stake": 100.00,
        "potentialPayout": 166.67,
        "legs": [...]
      }
    ],
    "count": 12,
    "totalExposure": 1200.00
  }
}

GET /api/mcp/bets/summary

Get quick betting summary for MCP context.

Response:

{
  "status": "success",
  "data": {
    "pending": {
      "count": 12,
      "totalStake": 1200.00,
      "potentialWin": 1650.00
    },
    "recentResults": {
      "last7Days": {
        "won": 8,
        "lost": 5,
        "netProfit": 345.50
      }
    },
    "favorites": {
      "bestSport": "basketball_nba",
      "bestBetType": "single"
    }
  }
}

GET /api/mcp/bets/advice-context

Get full context for AI betting advice.

Response: Extended summary with:

  • All active bets
  • Recent performance by sport
  • Betting patterns and tendencies
  • Risk exposure by game/sport

GET /api/mcp/games/with-exposure

Get games with user's betting positions.

Query Parameters:

  • sport (string): Filter by sport
  • onlyWithBets (boolean): Show only games with active bets

Response:

{
  "status": "success",
  "data": {
    "games": [
      {
        "game": {
          "id": "uuid",
          "awayTeamName": "Boston Celtics",
          "homeTeamName": "Los Angeles Lakers",
          "commenceTime": "2026-01-09T19:00:00Z"
        },
        "userBets": [
          {
            "betId": "uuid",
            "name": "Lakers ML",
            "stake": 100.00,
            "selection": "home",
            "selectionType": "moneyline"
          }
        ],
        "totalExposure": 100.00,
        "potentialWin": 166.67
      }
    ]
  }
}

POST /api/mcp/bets/quick-create

Simplified bet creation for MCP.

Request Body:

{
  "game_id": "uuid",
  "selection_type": "moneyline",
  "selection": "home",
  "stake": 50.00,
  "odds": -150,
  "name": "Lakers ML"
}

Admin API (/api/admin)

GET /api/admin/health

Health check endpoint.

Response:

{
  "status": "healthy",
  "timestamp": "2026-01-08T15:30:00Z",
  "database": "connected",
  "uptime": 3600
}

Frontend Architecture

React + TypeScript + Vite + Redux Toolkit + Tailwind CSS

Directory Structure

src/
├── components/          # Reusable UI components
│   ├── bets/           # Bet-related components
│   ├── odds/           # Odds display components
│   ├── stats/          # Statistics components
│   └── common/         # Shared components
├── pages/              # Route pages
├── store/              # Redux store
├── services/           # API clients
├── hooks/              # Custom React hooks
├── types/              # TypeScript types
└── utils/              # Utility functions

State Management (Redux Toolkit)

Store Configuration (store/index.ts)

import { configureStore } from '@reduxjs/toolkit';
import betSlipReducer from './betSlipSlice';

export const store = configureStore({
  reducer: {
    betSlip: betSlipReducer,
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

Bet Slip Slice (store/betSlipSlice.ts)

State:

interface BetSlipState {
  legs: BetLeg[];
  betType: 'single' | 'parlay' | 'teaser';
  stake: number;
  teaserPoints: number;
}

Actions:

  • addLeg(leg) - Add/update bet leg (auto-dedupes by gameId + selectionType)
  • removeLeg(index) - Remove bet leg
  • updateLeg(index, updates) - Update specific leg
  • setBetType(type) - Set bet type
  • setStake(amount) - Set stake amount
  • setTeaserPoints(points) - Set teaser points (6, 6.5, 7)
  • clearBetSlip() - Clear all legs
  • updateOdds(gameId, odds) - Update odds for specific game

Auto-behaviors:

  • Adding 2+ legs auto-switches to parlay
  • Removing to 1 leg auto-switches to single
  • Clearing legs resets stake to 0

API Service (services/api.ts)

import axios from 'axios';

const apiClient = axios.create({
  baseURL: 'http://localhost:3001/api',
  headers: { 'Content-Type': 'application/json' },
});

// Auto-adds Bearer token from localStorage
apiClient.interceptors.request.use((config) => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// Auto-handles 401 Unauthorized
apiClient.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      localStorage.removeItem('token');
      // Redirect to login
    }
    return Promise.reject(error);
  }
);

Key Components

components/bets/BetSlip.tsx

Interactive bet slip widget.

Features:

  • Add/remove legs dynamically
  • Adjust stake with validation
  • Calculate potential payout (parlay calculations)
  • Teaser control for NFL/NBA spreads/totals
  • Submit bet to API

Props: None (uses Redux state)

components/bets/BetCard.tsx

Display single bet with all details.

Props:

interface BetCardProps {
  bet: Bet;
  onSettle?: (betId: string, status: 'won' | 'lost' | 'push') => void;
  onDelete?: (betId: string) => void;
  onEdit?: (betId: string) => void;
}

components/odds/GameCard.tsx

Display game with odds from multiple bookmakers.

Props:

interface GameCardProps {
  game: Game;
  onAddToBetSlip: (leg: BetLeg) => void;
  showOdds?: boolean;
  collapsed?: boolean;
}

Features:

  • Expandable odds table
  • Click odds to add to bet slip
  • Line movement indicators
  • Team logos and colors

components/odds/OddsGrid.tsx

Grid of games with odds comparison.

Props:

interface OddsGridProps {
  sport?: string;
  date?: Date;
  bookmakers?: string[];
}

Features:

  • Filter by sport, date
  • Sort by commence time
  • Toggle between moneyline/spread/total views
  • Refresh odds button

components/stats/StatsDashboard.tsx

Statistics and performance charts.

Features:

  • Win rate charts (by sport, by bet type)
  • P&L timeline graph
  • ROI by bookmaker
  • Bet distribution pie charts
  • Filters: date range, sport, bet type

Custom Hooks

hooks/useBetSlip.ts

export const useBetSlip = () => {
  const dispatch = useDispatch();
  const betSlip = useSelector((state: RootState) => state.betSlip);

  const addLeg = (leg: BetLeg) => dispatch(addLegAction(leg));
  const removeLeg = (index: number) => dispatch(removeLegAction(index));
  const calculatePayout = () => { /* Calculate based on betType */ };

  return { betSlip, addLeg, removeLeg, calculatePayout };
};

hooks/useOddsPolling.ts

export const useOddsPolling = (gameId: string, interval: number = 30000) => {
  const [odds, setOdds] = useState<Odds[]>([]);
  
  useEffect(() => {
    const fetchOdds = async () => {
      const response = await apiClient.get(`/games/${gameId}`);
      setOdds(response.data.data.currentOdds);
    };
    
    fetchOdds();
    const timer = setInterval(fetchOdds, interval);
    
    return () => clearInterval(timer);
  }, [gameId, interval]);
  
  return odds;
};

Pages

pages/BetHistory.tsx

List of all bets with filters and search.

Features:

  • Tabs: Active, Won, Lost, All
  • Search by name
  • Filter by sport, date range
  • Sort by date, stake, potential win
  • Pagination

pages/Stats.tsx

Performance analytics and insights.

Features:

  • Overview cards (total bets, win rate, ROI, net profit)
  • Charts: P&L over time, win rate by sport, bet distribution
  • Best/worst performing sports/bet types
  • Recent activity feed

TypeScript Types

// types/game.types.ts
export interface Game {
  id: string;
  externalId: string;
  sportId: string;
  awayTeamName: string;
  homeTeamName: string;
  commenceTime: Date;
  status: 'scheduled' | 'in_progress' | 'completed';
  completed: boolean;
  sport: Sport;
  currentOdds: Odds[];
}

export interface Sport {
  id: string;
  key: string;
  name: string;
  active: boolean;
}

export interface Odds {
  id: string;
  gameId: string;
  bookmaker: string;
  marketType: 'h2h' | 'spreads' | 'totals';
  selection: 'home' | 'away' | 'over' | 'under';
  price: number;
  point?: number;
  lastUpdated: Date;
}

// types/bet.types.ts
export interface Bet {
  id: string;
  name: string;
  betType: 'single' | 'parlay' | 'teaser';
  stake: number;
  status: 'pending' | 'won' | 'lost' | 'push' | 'cancelled';
  oddsAtPlacement: number;
  potentialPayout: number;
  actualPayout?: number;
  teaserPoints?: number;
  notes?: string;
  createdAt: Date;
  settledAt?: Date;
  legs: BetLeg[];
}

export interface BetLeg {
  id: string;
  betId: string;
  gameId: string;
  selectionType: 'moneyline' | 'spread' | 'total';
  selection: 'home' | 'away' | 'over' | 'under';
  teamName?: string;
  line?: number;
  odds: number;
  userAdjustedLine?: number;
  userAdjustedOdds?: number;
  status: 'pending' | 'won' | 'lost' | 'push' | 'cancelled';
  result?: number;
  game: Game;
}

Environment Configuration

# .env
VITE_API_URL=http://localhost:3001/api
VITE_ENABLE_MOCK_API=false
VITE_ODDS_REFRESH_INTERVAL=30000

Quick Reference

MCP Server

cd mcp
python sports_mcp_server.py

Backend

cd dashboard/backend
npm run dev  # http://localhost:3001

Frontend

cd dashboard/frontend
npm run dev  # http://localhost:5173

Testing

# Backend
cd dashboard/backend
npm test

# MCP (when implemented)
cd mcp
pytest tests/ -v

Clone this wiki locally