• v0.52.0 4028c6a349

    joeyr released this 2026-09-10 20:04:34 -05:00 | 62 commits to main since this release

    Tenant #6: espnff — ESPN fantasy football

    One league, read-only, ten tools: get_league_info, list_teams, get_team_roster,
    get_team_info, get_standings, get_matchups, get_box_score, search_players,
    get_free_agents, get_league_activity.

    In the code and not deployed. It needs a Forgejo OAuth app, a stack service and a
    proxy host, none of which live in this repo.

    A library that answers the wrong question rather than refusing

    The HTTP, cookie auth and parsing are espn_api's (cwendt94). Every other provider in
    this project fails by refusing — a revoked password, an expired cookie, a 404 — and those
    failures announce themselves. This one has three paths that return a complete, confident,
    well-formed answer to a question nobody asked. All three are now guarded:

    • box_scores(week) ignores a future week and returns the current week's numbers. No
      error, no flag. Refused now, and refused before the request so ESPN never sees it.
    • free_agents(position=...) drops a position code it does not recognise and returns
      the unfiltered pool
      — position="rb" would read as a list of running backs and be a
      list of everybody. Validated against espn_api's own map, derived rather than retyped.
      Lower case is still normalised; a guess at what "RunningBack" meant would be inventing
      a filter.
    • A stale ESPN_SEASON_YEAR is served perfectly by ESPN. Every tool answers last
      season's question correctly and nothing goes red, because nothing is wrong with the
      request. No network failure could detect it, so preflight checks the calendar instead
      and warns — advisory, since reading a finished season on purpose is legitimate.

    Every espn_api request had no timeout

    The library issues bare requests.get(...) calls throughout, so a stalled connection to
    ESPN would have held an MCP tool call open indefinitely — nothing else in this stack gives
    up first. Fixed by replacing the one module attribute those calls go through, which is a
    monkeypatch on an internal, so it comes with a ceiling on espn-api and a test pinning
    the patch point. An upstream change there would not crash; it would quietly take the
    timeout back off.

    search_players returns every match

    The obvious implementation takes the first name containing the query, and it is wrong in
    exactly the case somebody searches — a surname shared by two players — with nothing in the
    response saying so. Every match comes back with its position, its NFL team and whoever in
    this league already holds it.

    Nothing is cached

    Every call rebuilds the whole league: three to five requests including the full
    professional player index. A waiver run rewrites half the rosters at once, so the failure
    mode of a cache here is a confident answer about a roster that changed twenty minutes ago.
    Two consequences: deploy/nginx/espnff.conf carries a 180s proxy_read_timeout where the
    other four carry 120s, and the healthcheck does not touch ESPN.

    Read-only by construction

    ESPN has a full write API — lineups, waiver claims, trades — and no code path here calls
    any of it. The boot summary prints writes=(none, no write tool exists on this tenant) as
    a constant, and a test fails on any tool name starting with a write verb.

    Also

    • _check_secrets carries values rather than names. It chose the credential value with a
      conditional on tenant name, which worked while every identity was an Apple ID or a mail
      address; this tenant authenticates with two cookies and has no address at all.
    • espn-api>=0.46,<0.47 added, bringing requests and urllib3 into an image that had
      neither. Accepted deliberately — the four existing tenants use niquests/urllib3-future
      and are untouched.
    • Both locks regenerated with the documented --upgrade procedure (~35 other pins moved).
      That surfaced anyio 4.15 deprecating anyio.abc.BlockingPortal, which starlette's
      TestClient still imports; a narrowly scoped filterwarnings ignore names whose it is.

    1611 tests green.

    Downloads