Skip to main content
Glama
ntinosgkiou-code

football-odds-agent

README.md
# football-odds-agent

An MCP (Model Context Protocol) server that gives any MCP client (Claude Desktop,
Claude Code, etc.) tools to pull live football odds, check a team's recent form,
and flag matches where the bookmaker's implied probability disagrees a lot with
a naive form-based estimate.

## Why this project

I'm an economics student — market efficiency in prediction markets (of which
sports betting odds are a textbook example) is a real topic I already understand,
not a borrowed one. This project applies that lens to a live dataset through an
MCP agent, instead of just describing the theory.

**Important honesty note:** `find_value_matches` computes a *disagreement score*
between the market price and a very simple recent-form heuristic. A high score
means "the market and this naive model disagree" — it is **not** a validated
betting edge, and the tool says so in its own output. Real market-efficiency
research would need a much better model, backtesting, and transaction-cost
accounting before any of this could inform a real decision. This project is a
demonstration of MCP tool-calling and data synthesis, not a trading signal.

## Architecture

```
MCP client (e.g. Claude Desktop)
        │  calls tools over MCP
        ▼
server.py  (MCPServer, 3 tools)
        │
        ├─ get_upcoming_odds()  ──▶ clients/odds_client.py   ──▶ the-odds-api.com
        ├─ get_team_form()      ──▶ clients/stats_client.py  ──▶ football-data.org
        └─ find_value_matches() ──▶ analysis.py (pure functions, fully unit-tested)
                                     combines the two above + flags divergence
```

`analysis.py` has zero I/O — it's pure math (implied probability, overround
removal, a naive form-based estimate, a divergence score) so it can be unit
tested without hitting either API. See `tests/test_analysis.py`.

## Tools exposed

| Tool | What it does |
|---|---|
| `get_upcoming_odds(sport_key)` | Live 1X2 odds for an upcoming league's matches |
| `get_team_form(team_id, last_n)` | A team's win/draw/loss record over its last N matches |
| `find_value_matches(sport_key, team_ids, threshold)` | Cross-references odds + form, flags large disagreements |

## Setup

```bash
python -m venv venv
source venv/bin/activate   # Windows: venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env       # fill in your two free API keys
```

Get free keys (no credit card required for either):
- **ODDS_API_KEY** — [the-odds-api.com](https://the-odds-api.com) (500 credits/month free)
- **FOOTBALL_DATA_API_KEY** — [football-data.org](https://www.football-data.org/client/register) (10 requests/min free)

## Run

```bash
python server.py
```

To use it from Claude Desktop, add it to your MCP server config pointing at
this `server.py`. See the [MCP quickstart](https://modelcontextprotocol.io/quickstart)
for the exact config file location for your OS.

## Test

```bash
pytest tests/ -v
```

10 tests, all pure-logic (no API calls, no network, no keys needed).

## Known limitations

- The form-based probability estimate is deliberately naive (win rate only —
  no home/away split, no strength-of-opponent adjustment, no injuries/lineups).
  It exists to give the divergence score *something* independent to compare
  the market against, not to be predictive on its own.
- `find_value_matches` requires you to manually map match labels to
  football-data.org team ids (`team_ids` parameter) — there's no automatic
  team-name matching between the two APIs yet.
- Free-tier rate limits (500/day odds, 10/min stats) are enough for exploring
  a few leagues, not for scanning every match in every league continuously.

## What I'd do next

- Automatic team-name → team-id resolution between the two APIs.
- A better form model (home/away split, opponent strength, recency weighting).
- Backtest the divergence score against historical results before claiming
  any predictive value.