# Sports MCP Server - ESPN MCP for Scores, Injuries & Odds (`neverempty/sports-mcp-server`) Actor

Sports MCP server for Claude, Cursor and AI agents: live ESPN scores, injury reports, betting odds with line movement, standings, team schedules and news for NFL, NBA, MLB, NHL, WNBA, college and top soccer leagues. Pay per successful tool call; failed calls and empty answers are free.

- **URL**: https://apify.com/neverempty/sports-mcp-server.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** MCP servers, Sports, AI
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $7.30 / 1,000 tool call answereds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Sports MCP Server - ESPN MCP for Scores, Injuries & Odds

**Give Claude, Cursor or your own AI agent live sports data in one connection.** This Sports MCP server answers questions like *"Who is out for the Chiefs on Monday?"*, *"How far has the Broncos-Chiefs total moved since it opened?"* or *"Show me the Premier League table"* straight from ESPN's public data, with no ESPN API key and nothing to host.

Built for:

- **Sports betting research** - bookmaker spreads, totals and moneylines with the opening line and how far each line has moved, next to the injury report that moved it.
- **Fantasy and sports analytics** - injury status with the latest analyst note, standings, team schedules and results as clean JSON.
- **AI agents and chat assistants** - seven small tools with short, token-efficient answers and plain-English error messages your agent can act on.

Leagues: **NFL, NBA, MLB, NHL, WNBA, NCAA Football, NCAA Men's Basketball, Premier League, LALIGA, Bundesliga, Serie A, Ligue 1, MLS and the UEFA Champions League.**

### What makes this ESPN MCP server different

- **Injury reports are a tool of their own.** Every NFL, NBA, MLB, NHL and WNBA injury on ESPN with status (Out, Questionable, Day-To-Day, Injured Reserve), the ESPN athlete id and the latest analyst note, most recent first.
- **Line movement, not just the line.** For each upcoming game the odds tool returns the current spread, total and moneylines *and* the opening numbers, plus `spreadMove`, `totalMove` and `favoriteFlipped`. Upcoming games are read first and finished games are skipped (ask with `includeFinished` for closing lines). In-game lines (for example "DraftKings - Live Odds") are marked `isLive: true` and carry no opening-line movement, because comparing a live line with the pre-game opener means nothing. MLB odds that are missing from ESPN's scoreboard are read from ESPN's per-game odds feed, so baseball lines are not silently dropped.
- **You pay only when you get what you asked for.** A failed call (ESPN refused or did not answer), an invalid argument, an empty answer ("no games today", "no odds posted yet", "no injuries for that team") and a scoreboard for a different day than the one you asked for are **not charged**. `list_leagues` is always free.
- **"Today" means today.** Game dates are the US Eastern game day, so a Sunday night kick-off at 00:20 UTC is still dated Sunday. When you ask for today's scoreboard and there are no games, ESPN shows its next game day instead; the answer starts with "No NBA games today (2026-09-13, US Eastern); ESPN's next games are on ..." and is not charged.
- **No silent empty results.** ESPN answers some league and feed combinations with an empty 200 response (for example injuries for any soccer league). That looks like "nobody is injured" but means "no feed". This server refuses those combinations up front and says why, instead of returning an empty list as if it were a fact.
- **Scores are `null` before kick-off, never `0`**, so an unplayed game never reads as a 0-0 draw.

### Connect

MCP endpoint (Streamable HTTP):

```
https://neverempty--sports-mcp-server.apify.actor/mcp
```

Authenticate with your Apify API token (Apify Console → Settings → API & Integrations), either as a header `Authorization: Bearer <APIFY_TOKEN>` or as `?token=<APIFY_TOKEN>` on the URL. Both forms were checked against the live server on 2026-09-14; a request with no token is rejected by Apify.

**Claude Code**

```bash
claude mcp add --transport http sports https://neverempty--sports-mcp-server.apify.actor/mcp --header "Authorization: Bearer <APIFY_TOKEN>"
```

**Claude Desktop** (`claude_desktop_config.json`, via the `mcp-remote` bridge). The token goes in `env` and the header is written `Authorization:${AUTH_HEADER}` with no space after the colon: this is the workaround the mcp-remote README gives for Claude Desktop on Windows (and Cursor), which do not escape spaces inside `args`.

```json
{
  "mcpServers": {
    "sports": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://neverempty--sports-mcp-server.apify.actor/mcp", "--header", "Authorization:${AUTH_HEADER}"],
      "env": { "AUTH_HEADER": "Bearer <APIFY_TOKEN>" }
    }
  }
}
```

**Cursor** (`.cursor/mcp.json`) and other clients that accept a URL with headers

```json
{
  "mcpServers": {
    "sports": {
      "url": "https://neverempty--sports-mcp-server.apify.actor/mcp",
      "headers": { "Authorization": "Bearer <APIFY_TOKEN>" }
    }
  }
}
```

**Any MCP client** - if your client only takes a URL, use `https://neverempty--sports-mcp-server.apify.actor/mcp?token=<APIFY_TOKEN>`. Keep that URL private: it contains your token.

The server is stateless: every request is answered on its own, so it works with clients that do not keep a session.

### Tools

Every tool returns two text parts: a one-line summary for the model to read, then compact JSON.

| Tool | Arguments | Leagues | Charged |
|---|---|---|---|
| `list_leagues` | none | all 14 | never |
| `get_scoreboard` | `league`, `date` (YYYY-MM-DD US Eastern; omit for today, or the current week for nfl / ncaaf), `limit` (1-200, default 100) | all 14 | when games are returned for the day you asked for |
| `get_injuries` | `league`, `team`, `status` (e.g. Out), `limit` (1-500, default 50) | nfl, nba, mlb, nhl, wnba | when reports are returned |
| `get_odds` | `league`, `date`, `team`, `maxGames` (1-20, default 8), `includeFinished` (default false) | nfl, nba, mlb, nhl | when at least one line is returned |
| `get_standings` | `league`, `team` (optional) | nfl, nba, mlb, nhl, wnba, ncaaf, epl, laliga, bundesliga, mls | when teams are returned |
| `get_news` | `league`, `limit` (1-50, default 10) | nfl, nba, mlb, nhl, wnba, ncaaf, epl, laliga | when articles are returned |
| `get_team_schedule` | `league`, `team`, `season` (e.g. 2025), `seasonType` (preseason / regular / postseason), `which` (upcoming / completed / all), `limit` - each game has start time, home team, away team, season type, state, score and winner | nfl, nba, mlb, nhl, wnba, epl | when games are returned |

League keys: `nfl`, `nba`, `mlb`, `nhl`, `wnba`, `ncaaf`, `ncaab`, `epl`, `laliga`, `bundesliga`, `seriea`, `ligue1`, `mls`, `ucl`. **How `team` is matched:** an exact abbreviation, ESPN team id, full name, city or nickname wins (`KC`, `Chiefs`, `Brooklyn Nets`, `Texas`). Otherwise the words you give must appear as whole words in the team name (`Kansas City`). Parts of words never match, so `Nets` never matches Charlotte Hornets and `NE` never matches Tennessee. When several teams qualify, only the exact matches are kept, so `Texas` returns Texas Longhorns, not Texas Tech or Texas A\&M (ask for those by name).

#### Example output (real calls to the live server, 2026-09-14)

`get_injuries { "league": "nfl", "team": "KC", "limit": 5 }`

```
NFL: 800 injury report(s) on ESPN, 25 for team "KC"; 5 most recent returned. Status: Active 20, Out 3, Injured Reserve 2.
{"league":"nfl","teamsInFeed":32,"reportsTotal":800,"reportsMatched":25,"injuries":[{"athlete":"Chris Jones","athleteId":"3044859","team":"Kansas City Chiefs","teamAbbreviation":"KC","position":"DT","status":"Active","reportedAt":"2026-09-12T19:46:00.000Z","comment":"Jones (calf) does not have an injury designation ahead of Monday night's game against the Broncos."}, ...]}
```

`get_odds { "league": "nfl", "team": "KC" }`

```
NFL: 1 line(s) from 1 game(s). Upcoming games first. Moneylines are American odds; *Move columns are current minus opening line.
{"odds":[{"game":"DEN @ KC","startTime":"2026-09-15T00:15:00.000Z","state":"pre","provider":"DraftKings","isLive":false,"details":"KC -2.5","spread":-2.5,"overUnder":43.5,"homeMoneyLine":-135,"awayMoneyLine":114,"openSpread":-2.5,"openOverUnder":42.5,"spreadMove":0,"totalMove":1,"favoriteFlipped":false}]}
```

`get_scoreboard { "league": "nfl" }`

```
NFL: 16 game(s) (post 10, in 4, pre 2). Scores are null before a game starts, never 0.
{"gamesTotal":16,"games":[{"gameId":"401872928","state":"in","status":"In Progress","startTimeUtc":"2026-09-13T20:25Z","homeTeam":"Las Vegas Raiders","awayTeam":"Miami Dolphins","homeScore":27,"awayScore":13,"venue":"Allegiant Stadium","period":4}, ...]}
```

`get_standings { "league": "epl" }`

```
Premier League standings 2026-27 English Premier League: 20 team(s) in 1 group(s).
{"standings":[{"teamId":"359","team":"Arsenal","abbreviation":"ARS","wins":4,"losses":0,"ties":0,"gamesPlayed":4,"points":12,"rank":1,"pointsFor":8,"pointsAgainst":1,"pointDifferential":7,"note":"Champions League"}, ...]}
```

`get_team_schedule { "league": "mlb", "team": "NYY", "which": "completed", "limit": 3 }`

```
New York Yankees 2026 Regular Season: 166 game(s) on ESPN, 3 completed returned, record 86-63.
{"games":[{"shortName":"NYM @ NYY","state":"post","status":"Final","homeScore":6,"awayScore":4,"winner":"New York Yankees","venue":"Yankee Stadium"}, ...]}
```

An error tells the model what failed and that nothing was charged, for example:

```
ESPN does not recognise team "zzz" in NFL (HTTP 400: Bad request for team with value: zzz). Use the team abbreviation (for example KC) or the numeric teamId from get_standings. This call was not charged.
```

#### Measured on the Apify platform (2026-09-14)

- Every ESPN request made from the Apify network in the checks above answered HTTP 200 without a proxy.
- The full NFL injury feed is an 8.86 MB response; the tool answered in 641 ms once the server was running.
- The other tools answered in 194-305 ms. The first request after the server has been idle waits for it to start (about 5 seconds in our check).

### Pricing

Pay per event: **$0.01 per successful tool call** on the Apify Free plan, lower on paid Apify plans (down to $0.007). Calls that return no data, scoreboards for a different day than the one asked for, calls ESPN refused or could not answer, invalid arguments and `list_leagues` are not charged. If your run has a maximum total charge, the server counts calls that are still in progress against it and refuses a call before contacting ESPN once the limit would be reached, so several simultaneous calls cannot go over the limit.

**Starting the Actor normally** (the Start button in Apify Console, the API, or a schedule) does not start a long-running server. The run asks ESPN for the NFL scoreboard once, writes a single health-check row (status, the MCP URL, the tool list and ESPN's answer) and finishes in a few seconds. That run is not charged. To use the tools, connect an MCP client to the URL above.

### Limits and what is not included

- **Not an official ESPN product.** Data comes from ESPN's public site endpoints and can change or disappear without notice.
- **Odds** cover NFL, NBA, MLB and NHL only, from the bookmaker(s) ESPN lists for each game (often a single provider). Each game is one ESPN request, so `maxGames` is capped at 20; pass `team` for a single game. No historical line archive.
- **Injuries** cover NFL, NBA, MLB, NHL and WNBA. ESPN publishes no injury feed for soccer or college basketball, and its college football feed held only three reports dated 2020-2022 when we checked on 2026-09-14, so college football injuries are not offered.
- **Soccer team schedules** need ESPN's numeric team id (Arsenal is `359`); take it from `get_standings`. Abbreviations are rejected by ESPN.
- **News** returns headlines, summaries, tags and links, not full article text. ESPN serves at most 50 articles.
- Not included: play-by-play, box scores, player season stats and game logs, rankings, search, and leagues not listed above.
- A league and tool combination that is not listed is refused rather than guessed.

### Support

Found a wrong number or a league you need? Open an issue in the **Issues** tab of this Actor with the tool name, the arguments and what you expected.

# Actor input Schema

## Actor input object example

```json
{}
```

# Actor output Schema

## `results` (type: `string`):

One health-check row per non-Standby run: status (ok or espn-unreachable), the MCP URL, the tool names, a one-line ESPN scoreboard check and the number of ESPN requests made. Not charged.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/sports-mcp-server").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/sports-mcp-server").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call neverempty/sports-mcp-server --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/sports-mcp-server"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/WleK6kPGEDPEvDhG0/builds/H5vDXczIZdNQ1nNCw/openapi.json
