open-leagues

Overview

An open-source MCP server. It reads any Sleeper league by id, NFL stats, open projections, and the closing line for every game since 1999.

What this is

Three families of verbs. Your league reads any Sleeper league by id with no account; on a box you own it also moves the league you host. The game is the NFL with no league attached. The lab is every closing line since 1999.

SurfaceReachEntry point
MCP server88 of 99 verbs; 23 with no account/api/mcp · scripts/mcp.mjs
The public boxDefault mode: public MCP plus the open-data files; no accounts, no leaguesOPENLEAGUES_MODE unset · league for the whole product
The labReal NFL lines since 1999, cohorts, grading, staking/api/lines/:season.json · two skills
Open dataThree JSON files, anonymous, CORS on/api/players.json · /api/wire · /api/lines
ReceiptsA worked example: any Sleeper league, anonymous/r/:leagueId
Browser appEvery verb, in the browsercreateServerFn
Ledger CLIThree reads, one gated writescripts/ledger.mjs
The CLI dispatches a slice of the catalog; see CLI.

Install

Docker gives a durable box: PGLite on a volume, the league clock in-process, no managed Postgres.

docker · durable box, in-process clock
git clone https://github.com/ryanwaits/open-leagues.git
cd open-leagues
docker compose up -d

# http://localhost:8080

The shape of a league

One league row, ten rosters by default, a schedule, a wire, and a book. Three invariants hold on every surface:

  • FAAB is one purse. Waiver bids and wager stakes draw from the same faab_remaining. A spend is checked against spendable (remaining minus unsettled stakes), never against remaining.
  • You cannot fade your own roster. You may back it. The engine throws: “You can back yourself, but you cannot bet against yourself.”
  • Stakes are whole dollars. A stake is floored to an integer with a $1 minimum before any other check runs.
exportLeague returns a JSON snapshot. Nothing reads one back in, and nothing syncs to Sleeper or ESPN.