Skip to main content
Glama
jk-mcp-mls

jk-mcp-mls

by jedi-knights

jk-mcp-mls

MCP server that gives Claude live access to Major League Soccer data — teams, matches, standings, rosters, and schedule-strength analytics — via the ESPN public API.

CI Badge Coverage Evals Release Python License: MIT


Table of Contents


Related MCP server: sports-pulse-mcp

Overview

AI assistants like Claude are knowledgeable, but they have a hard cutoff date — they cannot tell you today's MLS standings, last night's scores, or which teams are currently in a playoff position. This project fixes that.

It is an MCP server — a plugin that gives Claude direct access to live MLS data: scores, standings, rosters, and derived schedule-strength analytics. Once installed, you can ask Claude natural-language questions about Major League Soccer and get accurate, up-to-date answers. No subscription, no API key, and no programming required to use it.

This is the v1 scaffold — it wraps the ESPN public API only. Richer sources (mlssoccer.com's Opta-powered feed, official CMS award articles, Leagues Cup, U.S. Open Cup, Concacaf Champions Cup) are on the roadmap.


Features

The v1 surface is eleven read-only, idempotent tools split across two tiers.

ESPN-backed (8)

Tool

Description

get_teams

List all 30 MLS clubs with IDs and abbreviations

get_team

Details for a specific team

get_roster

Team's active roster — jersey, position, age, citizenship

get_scoreboard

Match scores for a single day, a date range, or the current matchweek

get_team_schedule

Every match for a team in the current season — past + upcoming

get_match_details

One match's full details — score, venue, attendance, goals, cards, subs

get_standings

Current standings grouped by Eastern and Western Conferences

get_news

Recent MLS news articles

Derived analytics (3)

Pure functions over live standings + team schedules, exposing schedule-strength context the raw table does not.

Tool

Description

get_strength_of_schedule

Team's average opponent points-per-game across matches already played

get_results_by_opponent_tier

Team's W-L-T split across current top / middle / bottom standings tiers

get_adjusted_points_per_game

Team's raw PPG alongside an opponent-quality-adjusted PPG

Roadmap

Not in v1; probed and shown to be viable at the ESPN API:

  • Leagues Cup (concacaf.leagues.cup), U.S. Open Cup (usa.open), Concacaf Champions Cup, Campeones Cup

  • Player leaderboards and team season aggregates once a stable MLS Opta feed is identified

  • Award articles via mlssoccer.com CMS

  • Playoff bracket for the MLS Cup Playoffs


Requirements


Installation

git clone https://github.com/jedi-knights/jk-mcp-mls.git
cd jk-mcp-mls
uv sync

Usage

Run the server in stdio mode (the default — used by Claude Code and Claude Desktop):

uv run python -m mls.server

Run in HTTP mode (for networked or deployed access):

MCP_TRANSPORT=streamable-http uv run python -m mls.server

Example prompts

Standings, scores, rosters:

  • Who is leading the MLS Eastern Conference right now?

  • Show me every MLS result from this past weekend.

  • Who is on Atlanta United's roster?

  • When does LAFC play next?

Schedule strength:

  • Which MLS team has played the toughest schedule so far?

  • Show me Atlanta United's record against the current top 5 teams.

  • Compare Inter Miami and Seattle Sounders on adjusted points-per-game.


Configuration

All configuration is via environment variables. None are required for local use.

Variable

Default

Description

MCP_TRANSPORT

stdio

Transport mode: stdio or streamable-http

HOST

0.0.0.0

Bind address (HTTP transport only)

PORT

8000

TCP port (HTTP transport only)

MCP_PATH

/mcp/mls

URL path (HTTP transport only)

API_HOST

https://site.api.espn.com

ESPN API base URL

LOG_LEVEL

INFO

DEBUG, INFO, WARNING, or ERROR

MCP_TRACING_ENABLED

unset

Bootstrap the OpenTelemetry SDK

MCP_AUTH_ENABLED

unset

Require RS256 bearer tokens on streamable-http

MCP_AUTH_ISSUER_URL

unset

Auth-server origin (required when auth is on)

MCP_AUTH_RESOURCE_URL

unset

This server's public URL for the aud claim


Claude Code

Install from your local clone globally so the server is available in every project:

claude mcp add --scope user mls -- uv run --directory /path/to/jk-mcp-mls python -m mls.server

Replace /path/to/jk-mcp-mls with the absolute path to your clone. Verify with claude mcp list.

Drop --scope user to register only for the current project, or commit a .mcp.json to the repo root for collaborators:

{
  "mcpServers": {
    "mls": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/jk-mcp-mls", "python", "-m", "mls.server"]
    }
  }
}

Claude Desktop

Add the following to your Claude Desktop configuration file.

Location:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "mls": {
      "command": "uv",
      "args": [
        "run",
        "--directory", "/path/to/jk-mcp-mls",
        "python", "-m", "mls.server"
      ]
    }
  }
}

If uv is not on Claude Desktop's PATH, use the absolute path (which uv will show it). Fully quit and relaunch Claude Desktop after saving — a window close is not enough.


Docker

Build the image:

docker build -t jk-mcp-mls:latest .

Run in stdio mode (for MCP clients that spawn a subprocess):

docker run -i --rm jk-mcp-mls:latest

Run in HTTP mode:

docker run --rm -p 8000:8000 \
  -e MCP_TRANSPORT=streamable-http \
  jk-mcp-mls:latest

Development

Install

uv sync

Invoke tasks

All common workflows are invoke tasks. Run uv run inv --list to see everything.

Task

Alias

Description

uv run inv lint

inv l

Run ruff linter and format check

uv run inv lint --fix

inv l --fix

Auto-fix lint violations and reformat

uv run inv test

inv t

Run the full test suite

uv run inv coverage

inv v

Run tests with coverage report (threshold: 90%)

uv run inv check-complexity

inv cc

Check cyclomatic complexity (max 7)

uv run inv build

inv b

Build wheel and sdist into dist/

uv run inv build-image

inv bi

Build the Docker image

uv run inv clean

inv c

Remove build and coverage artifacts

Project structure

src/mls/
├── server.py                     # entry point, transport selection, logging setup
├── adapters/
│   ├── inbound/
│   │   ├── mcp_adapter.py        # FastMCP server, health endpoints, tool registration
│   │   ├── formatters.py         # domain → LLM-readable text
│   │   ├── authorization.py      # inbound authz port implementations
│   │   └── tools/
│   │       ├── espn.py           # 8 ESPN-backed tools
│   │       └── analytics.py      # 3 schedule-strength analytics tools
│   └── outbound/
│       ├── espn_adapter.py       # ESPN HTTP client
│       ├── parsers.py            # ESPN JSON → domain models
│       ├── retry_adapter.py      # transient-failure retry decorator
│       └── caching_adapter.py    # in-process TTL cache
├── application/
│   ├── service.py                # MLSService — use cases, orchestration
│   ├── _helpers.py               # input validation
│   └── _analytics_helpers.py     # pure math for schedule-strength tools
├── domain/
│   ├── models.py                 # Team, Match, Standing (with conference), etc.
│   └── exceptions.py             # MLSNotFoundError, UpstreamAPIError
├── ports/
│   ├── inbound.py                # Authorizer protocol
│   └── outbound.py               # MLSAPIPort protocol
├── observability/                # OpenTelemetry bootstrap (opt-in)
└── security/                     # JWKS token verifier

The dependency direction flows inward: adapters → ports → domain. Nothing in domain/ imports from adapters or a framework.


Contributing

  1. Fork the repository and clone your fork

  2. Create a feature branch: git checkout -b feature/your-feature

  3. Make your changes following the existing patterns (hexagonal architecture, TDD, conventional commits)

  4. Verify the full check suite passes: uv run inv lint && uv run inv check-complexity && uv run inv coverage

  5. Open a pull request against main

All CI checks (lint, complexity, tests, coverage ≥ 90%) must pass before merge.


License

MIT — see LICENSE.

Available Tools

11 tools
get_adjusted_points_per_gameA
Read-onlyIdempotent

Get a team's raw points-per-game alongside an opponent-quality-adjusted PPG.

Adjusted PPG scales raw PPG by avg_opponent_ppg / league_average_ppg, so values above raw PPG indicate the team has earned points against a tougher schedule than league average.

Args: team_id: ESPN numeric team ID.

Inputs: team_id (required). Returns: result

get_match_detailsA
Read-onlyIdempotent

Get detailed information for a single MLS match.

Returns the score, venue, attendance, and a chronological list of key events (goals, substitutions, cards). Use the match ID returned by get_scoreboard or get_team_schedule.

Args: match_id: ESPN numeric event ID (e.g. "401853883").

Inputs: match_id (required). Returns: result

get_newsA
Read-onlyIdempotent

Get recent MLS news articles.

Returns each article's headline, publication date, summary, and link to the full ESPN story.

Args: limit: Maximum number of articles to return (default 10).

Inputs: limit. Returns: result

get_results_by_opponent_tierA
Read-onlyIdempotent

Get a team's W-L-T splits against current top-tier, middle, and bottom-tier teams.

Tiers are derived from the live league standings: top tier_size, bottom tier_size, and everyone in between. Lets you ask "how does this team do against the top of the table?" without scanning every result manually.

Args: team_id: ESPN numeric team ID. tier_size: Number of teams in each of the top and bottom tiers. Defaults to 5. Must be at least 1, and 2*tier_size must not exceed the league size.

Inputs: team_id (required), tier_size. Returns: result

get_rosterA
Read-onlyIdempotent

Get the active roster for an MLS team.

Returns each player's jersey number, name, position, citizenship, and age. Use the team ID returned by get_teams.

Args: team_id: ESPN numeric team ID (e.g. "18418" for Atlanta United FC).

Inputs: team_id (required). Returns: result

get_scoreboardA
Read-onlyIdempotent

Get MLS match scores and status for a date or date range.

With no arguments, returns matches for the current matchweek. With date only, returns matches for that single day. With both date and end_date, returns every match in the inclusive range.

Args: date: Optional start date in YYYYMMDD format (e.g. "20260418"). end_date: Optional end date in YYYYMMDD format. Requires date.

Inputs: date, end_date. Returns: result

get_standingsA
Read-onlyIdempotent

Get the current MLS standings, grouped by conference.

MLS is split into Eastern and Western Conferences (15 teams each in the current alignment). The response renders each conference as a separate numbered table ordered by points descending, with win/loss/tie record, goals for, goals against, and goal differential.

Inputs: no named parameters. Returns: result

get_strength_of_scheduleA
Read-onlyIdempotent

Get a team's strength of schedule based on opponents already faced.

Returns the average current points-per-game of every opponent the team has played in completed matches, plus a per-opponent breakdown. Useful early in the season for "who has played the tougher schedule so far?" questions.

Args: team_id: ESPN numeric team ID (e.g. "18418" for Atlanta United FC).

Inputs: team_id (required). Returns: result

get_teamA
Read-onlyIdempotent

Get details for a specific MLS team.

Returns full team information including display name, abbreviation, and location. Use the numeric ID returned by get_teams.

Args: team_id: ESPN numeric team ID (e.g. "18418" for Atlanta United FC).

Inputs: team_id (required). Returns: result

get_teamsA
Read-onlyIdempotent

Get all active MLS teams.

Returns a numbered list of teams with their ID, full name, abbreviation, and home city. Use the ID or abbreviation with get_team to retrieve detailed information about a specific team.

Inputs: no named parameters. Returns: result

get_team_scheduleA
Read-onlyIdempotent

Get all matches for a single MLS team in the current season.

Returns scheduled, in-progress, and completed matches for the team — with opponent, date, score (if played), and status.

Args: team_id: ESPN numeric team ID (e.g. "18418" for Atlanta United FC).

Inputs: team_id (required). Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.6/5.0

    Scored across 11 tools

    Disambiguation4/5

    Most tools are clearly distinct (teams, roster, scoreboard, standings, news), but the three team-performance analytics tools (strength_of_schedule, results_by_opponent_tier, adjusted_points_per_game) share a similar shape and could be confused if an agent doesn't read the descriptions carefully.

    Naming Consistency5/5

    All tool names follow a consistent verb_noun pattern: 'get_' followed by a specific resource (teams, team, scoreboard, roster, etc.). There is no mixing of conventions or vague verbs.

    Tool Count5/5

    11 tools is well within the ideal 3-15 range for a domain-specific server. Each tool covers a distinct need, and the count feels appropriately scoped without being excessive or too thin.

    Completeness5/5

    The server provides comprehensive read-only coverage for MLS data: team info, rosters, matches (by date and by team), standings, news, and advanced performance metrics. There are no obvious dead ends or missing core operations for a read-only informational server.

    Maintenance

    ActivityNo data
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      D
      maintenance
      An MCP server that connects Claude Desktop to The Odds API, giving Claude real-time access to sports odds, scores, and schedules across 80+ sports and leagues worldwide.
      -
    • F
      license
      B
      quality
      D
      maintenance
      MCP server that enables Claude Desktop to access real-time sports data including live scores, fixtures, standings, and NBA statistics using free APIs.
      10
      -
    • A
      license
      A
      quality
      C
      maintenance
      MCP server providing Claude live access to USL Super League data—teams, matches, standings, rosters, and schedule-strength analytics—via the ESPN public API.
      11
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      MCP server that gives Claude live access to Women's Super League (England) data — teams, matches, standings, rosters, and schedule-strength analytics — via the ESPN public API.
      11
      MIT