Dodi MCP server
2026년 11월 7일 커밋드래프트, 라인업 최적화, 웨이버 클레임, 트레이드 협상을 자동화하여 AI 에이전트가 ESPN 판타지 풋볼 리그를 관리할 수 있도록 합니다.
다른 이름 HamCops/dodi
- 스타
- 1
- Tools
- 26
- 다운로드, 30일
- —
- 최신 버전
- —
- 마지막 커밋
- 2026년 11월 7일
- 라이선스
- MIT
Dodi MCP란?
Dodi은(는) MCP server입니다. 드래프트, 라인업 최적화, 웨이버 클레임, 트레이드 협상을 자동화하여 AI 에이전트가 ESPN 판타지 풋볼 리그를 관리할 수 있도록 합니다. 26개 도구를 제공합니다. 예: get_draft_context, next_pick, get_draft_state.
프로젝트 README에서 생성된 요약
Dodi MCP server 정보
Dodi는 AI 에이전트를 통해 ESPN 판타지 풋볼 리그를 관리할 수 있게 해줍니다. 이 MCP server는 LLM이 리그와 직접 상호작용할 수 있는 도구 계층을 제공하며, 실시간 드래프트 픽과 주간 라인업 최적화부터 웨이버 클레임 및 트레이드 협상까지 모든 과정을 처리합니다. 리그 상태를 읽고, 선수 가치에 대한 수학적 분석을 수행하며, 로스터 변경을 실행할 수 있는 자율 에이전트를 구축하거나 배포하려는 개발자를 위해 설계되었습니다.
사용자는 stdio 전송 방식을 사용하여 Claude Code 또는 Hermes와 같은 MCP 호환 하네스에 서버를 연결합니다. 비공개 리그에 접속하려면 ESPN 리그 ID, 팀 ID, 그리고 espn_s2 및 SWID를 포함한 특정 브라우저 쿠키가 필요합니다. 서버는 실시간 드래프트를 위한 get_draft_context와 주간 로스터 관리를 위한 set_lineup을 포함하여 약 25개의 도구를 제공합니다. 에이전트가 변경 사항을 즉시 적용할지, 아니면 request_approval 도구를 사용하여 모바일 알림으로 권한을 요청할지 구성하여 자율성 수준을 제어할 수 있습니다.
활용 사례
- 1자동 드래프트 관리에이전트가 실시간 픽을 추적하고 드래프트 중에 VORP 순위 기반의 선수 추천을 제공할 수 있습니다.
- 2주간 라인업 최적화에이전트가 경기 시작 전 주간 예측치를 바탕으로 선발 로스터를 자동으로 조정할 수 있습니다.
- 3웨이버 및 자유 계약AI가 로스터에 추가할 최적의 선수를 식별하고 방출할 선수를 제안할 수 있습니다.
- 4트레이드 탐색 및 협상서버를 통해 에이전트가 리그 내 잠재적 트레이드 파트너를 스캔하고 제안된 거래의 영향을 분석할 수 있습니다.
프로젝트의 README를 바탕으로 작성되었습니다. 모든 활용 사례는 해당 내용의 인용문과 대조하여 확인합니다.
Dodi MCP 도구 26
README에서 감지됨get_draft_contextnext_pickget_draft_stateget_available_playersget_value_boardget_playerrefresh_draft_orderrefresh_boardget_matchupset_lineupmove_playerget_roster
| get_draft_context | — | |
| next_pick | — | |
| get_draft_state | — | |
| get_available_players | — | |
| get_value_board | — | |
| get_player | — | |
| refresh_draft_order | — | |
| refresh_board | — | |
| get_matchup | — | |
| set_lineup | — | |
| move_player | — | |
| get_roster | — |
도구 26개 모두 보기도구 수 줄이기
notify_managerget_waiver_targetsadd_playerdrop_playerfind_trade_partnersanalyze_tradepropose_tradeget_pending_tradesrespond_to_tradeget_league_settingsget_transactionsrequest_approvalget_proposalscheck_report
| notify_manager | — | |
| get_waiver_targets | — | |
| add_player | — | |
| drop_player | — | |
| find_trade_partners | — | |
| analyze_trade | — | |
| propose_trade | — | |
| get_pending_trades | — | |
| respond_to_trade | — | |
| get_league_settings | — | |
| get_transactions | — | |
| request_approval | — | |
| get_proposals | — | |
| check_report | — |
자동 검사
2026년 11월 14일에 실행됨전체 보고서 →아직 인증되지 않았습니다. 서버는 80점 이상의 점수, 심각한 결함 없음, 그리고 검사에 충분한 데이터를 갖추어야 합니다. 검사 방식 · 점수를 높이기 위해 수정할 항목
- 소스 저장소 연결 및 접근 가능hamcops/dodi is public and reachablegithub.com/hamcops/dodi ↗통과
- 패키지가 이 저장소를 소스로 명시검사되지 않음
- 빌드 출처(Sigstore 증명)검사되지 않음
- 오픈 소스 라이선스 (OSI)MITspdx.org/licenses/MIT.html ↗통과
- 최근 1년 내 커밋 또는 릴리스Last commit or release 2026-11-07github.com/hamcops/dodi ↗통과
- OpenSSF Scorecard검사되지 않음
- 패키지 이름이 인기 패키지와 유사하지 않음검사되지 않음
- 패키지 및 해당 종속성에 중요한 보안 권고 없음검사되지 않음
- 모델에 대한 숨겨진 지침 없음26 tool descriptions and the README read; no override phrases, credential paths or hidden characters통과
- 파괴적인 도구에 라벨 표시됨검사되지 않음
- 원격 엔드포인트가 MCP 핸드셰이크에 응답검사되지 않음
README
GitHub에서 보기D.O.D.I. Draft Optimization Dynasty Intelligence
An MCP server that gives your AI agent full control of an ESPN fantasy football league — draft picks, weekly lineups, waivers, trades, and roster moves. Wire it into any MCP-compatible harness (Claude Code, Hermes, your own), give the agent a persona and a cron schedule, and let it run your season.
We call ours Dodi — Draft Optimization & Dynasty Intelligence. Dodi checks the waiver wire every afternoon, optimizes the lineup before kickoff, scouts trade partners, fires off offers, responds to incoming trades, and pings us on the phone when something happens. Dodi doesn't ask permission to set the best lineup. Dodi does ask before trading away your RB1. You decide where that line is.
What this is for
You have an AI agent (LLM + tool loop). You want it to manage an ESPN fantasy football team — not answer trivia about one, but actually read the league state, do the math, and submit roster changes. This server is the tool layer. It handles:
- The draft. Live pick tracking, VORP-ranked boards, on-the-clock recommendations, and optional browser automation to click DRAFT for you.
- Weekly lineups. Start/sit optimization against ESPN's weekly projections, with one call to preview and one to apply.
- Waivers and free agents. Every unrostered player scored by what he does to your starting lineup, with drop candidates ranked by expendability.
- Trades. League-wide partner scouting, both-sides-win analysis, proposal submission, and inbox management.
- Transactions. Full league history — who moved whom, when — so your agent can detect when opponents are active or asleep.
The server never guesses. Every writing tool previews the exact change by
default and only submits with apply=true. Where you draw the autonomy line —
auto-apply lineups, require confirmation for trades, let it YOLO everything —
is up to your agent's prompt.
Quick start
python3 -m venv .venv
./.venv/bin/pip install -e ".[dev]"
cp .env.example .env # fill in your credentials (see below)
./.venv/bin/python scripts/doctor.py # validates creds, prints league info + top-10 board
Register with your harness
Claude Code / Hermes:
claude mcp add espn-fantasy -- "$(pwd)/.venv/bin/python" -m espn_mcp.server
Generic MCP (stdio transport):
{
"command": "/path/to/espn-mcp/.venv/bin/python",
"args": ["-m", "espn_mcp.server"],
"transport": "stdio"
}
The server exposes ~25 tools. Your agent discovers them via MCP's tools/list.
No tool configuration needed on the harness side.
Credentials
ESPN_LEAGUE_ID — the number in fantasy.espn.com/football/league?leagueId=...
ESPN_TEAM_ID — the teamId= in your roster page URL.
Private leagues (most are) also need two cookies from a browser session logged
into ESPN. DevTools → Application → Cookies → fantasy.espn.com:
espn_s2— long URL-encoded string, full valueSWID— UUID with the braces:{F64EC866-...}
Put all four in .env. The file is gitignored. These are session credentials —
treat them like a password. They're scoped to .espn.com over HTTPS and never
sent elsewhere. They expire mid-season; an auth error means re-copy them.
Tools
Draft
| Tool | What it does |
|---|---|
get_draft_context |
The on-the-clock call. Board state, roster needs, VORP-ranked candidates, tier depth, position runs, picks until your next turn. One round-trip. |
next_pick |
Same decision in ~1KB — candidates that fill a hole, tier cliffs, time pressure. |
get_draft_state |
Picks made, who's on the clock, your upcoming slots. Never cached. |
get_available_players |
Undrafted pool by VORP, projection, or ADP. |
get_value_board |
Replacement levels and tier structure — the math behind the rankings. |
get_player |
One player in detail: projection, VORP, tier, ADP, injury. |
record_picks / record_pick |
Enter picks by name as they happen. Persisted to state/. |
undo_pick / reset_draft |
Fix mistakes or start fresh. |
refresh_draft_order |
Re-read after a randomized draw. |
refresh_board |
Force pool re-fetch after breaking news. |
Weekly lineup
| Tool | What it does |
|---|---|
get_matchup |
Both lineups, start/sit swaps with point values, opponent holes, win probability. week=N for lookahead. |
set_lineup |
Preview the optimal swaps; apply=true submits them. Locked players (game started) are planned around. |
move_player |
One player → one slot. Swaps out the weakest occupant if the slot is full. Under approvals it refuses a move that lowers this week's projection. |
get_roster |
Any team's full roster with projections and positional strength. |
notify_manager |
A short push saying a report is waiting, so an agent needs no shell to ping. Title 60 chars, message 200, no links. |
Waivers and free agents
| Tool | What it does |
|---|---|
get_waiver_targets |
Best pickups by lineup impact (ROS and this-week), drop candidates, waiver timing. |
add_player |
Free-agent add (instant) or waiver claim (queued), with optional drop in the same transaction. |
drop_player |
Cut a player. Preview shows what the lineup loses. |
Trades
| Tool | What it does |
|---|---|
find_trade_partners |
League-wide scan: who's weak where you're strong, mutual-fit candidates, trade blocks, records. |
analyze_trade |
Both sides before/after: starter strength, bench value, roster legality. |
propose_trade |
Preview or send (apply=true) an offer to another manager. |
get_pending_trades |
Inbox: offers waiting on you and offers you sent. |
respond_to_trade |
Accept, decline, or withdraw. |
League info
| Tool | What it does |
|---|---|
get_league_settings |
Scoring, roster slots, team count, draft type. Call once per session. |
get_transactions |
Full transaction log — lineup moves, adds, drops, claims, trades — by team and week. |
Approvals
| Tool | What it does |
|---|---|
request_approval |
Queue an add, drop, trade offer, trade answer or start/sit call for the manager. Previewed first; sent to ESPN only when approved. |
get_proposals |
What is waiting, and what was approved, rejected or expired. |
check_report |
Is every number in this report one a tool returned in the last 45 minutes? For the agent to run on its own report before delivering it. |
Running it hands-off
Everything in this section is optional and off by default. Each part is
switched on in .env; .env.example documents the settings.
Approve from your phone
With ESPN_REQUIRE_APPROVAL=1, roster moves and trades can no longer be
sent directly: add_player, drop_player, propose_trade and
respond_to_trade refuse apply=true, in the server, whatever the prompt
says. The agent queues them with request_approval, and you get an
ntfy notification with Approve, Reject and Details
buttons. Lineup changes stay direct.
espn-mcp-approve is the service behind the buttons. It listens on
loopback only. Put it behind something private (a tailnet, a VPN), never
the open internet: approving a proposal makes a real roster move. Each
proposal has its own token, a decision can be made once, and a proposal
interrupted mid-send is closed, never retried.
What the agent writes is checked
The numbers behind a move are computed in code. The sentence explaining it is written by a language model, which will now and then write a number that is close to the right one or belongs to another player. So:
request_approvalrefuses reasoning that quotes a number not in the move's own preview, and says which.check_reportdoes the same for a whole report, against every number the tools have returned lately.- The notification labels its two parts: THE MOVE, built by the server from ESPN's data, and the agent's view, in the agent's words.
espn-mcp-factsprints what is true right now (the week, who is locked, open trades, the queue) for a scheduler to put in front of the agent's prompt, so that it is copied and not recalled.
Only numbers can be checked this way. A right number on the wrong player, or sound numbers and a poor conclusion, still get through.
Decisions by the kickoff clock
Run espn-mcp-tick every five minutes. For each group of games on your
roster it counts back from the real kickoff:
| When | What |
|---|---|
| Kickoff − 65 min | Calls GAMETIME_HOOK, to start your agent |
| Kickoff − 45 min | Sets the lineup (espn-mcp-lineup, no model) and sends close calls |
| Kickoff − 5 min | Approval requests for that game expire |
Both runs come after inactives are announced, and leave
APPROVAL_LEAD_MINUTES (default 30) to answer. espn-mcp-tick status
prints the week's plan.
Once a week there is a third run. By Monday night every player on a roster
has played and is locked, so nobody can be dropped and no pickup made, until
ESPN rolls over to the new week. The scheduler watches for that and calls
the hook with roster the first time it sees it between 9 AM and 9 PM, so
claims reach you as early as they can be made.
The hook is called as <hook> <agent|roster> <weekday> <kickoff> and should
start the agent and return at once, not wait for it.
Outside data
With ESPN_EXTERNAL_SOURCES=1, player records also carry:
| Field | Source | What it is |
|---|---|---|
market |
FantasyCalc | Trade value from completed trades |
usage |
nflverse | Points scored against what carries and targets say he should score |
game |
ESPN scoreboard, Open-Meteo | Betting line, roof, forecast at kickoff |
adj_week_proj |
derived | ESPN's projection moved by the betting line; the lineup is set by it |
adds_24h, alt_week_proj, injury_alt |
Sleeper | Pickup trend, a second projection, a second injury report |
All public, no keys. Every source is cached and fails soft: one that is down leaves its fields off and the tools carry on with ESPN alone.
What is used and how much it counts was decided by testing each signal on
two past seasons, fit on one and scored on the other. Most did not survive.
research/README.md has the method, the results,
what was thrown out, and the scripts to rerun it.
ESPN's analysts
ESPN's own analysts publish weekly position ranks, and the player pool
returns them on the same request (no extra call, no outside source). Player
records carry expert: the rank at the position this week, how many
analysts it comes from, and, in a standard league, the spread across the
PPR panel. get_matchup and set_lineup add experts: where the lineup
their ranks would set differs from the one set.
Measured for 2026 week 4: eight analysts publish PPR ranks and one publishes standard ranks, and the two lists differ for 119 of 271 players. In a standard league the expert rank is therefore one analyst's. ESPN identifies analysts by number only; no mapping to names is claimed. Unpublished rows (about 40%) are dropped. The lineup is never set by the experts: they are scored against it in the weekly review.
Weekly review
espn-mcp-report prints last week's result, the points left on the bench,
what starting by ESPN's analysts' ranks would have scored,
how each projection did against what was recorded before kickoff, and how
the approved and rejected moves turned out.
Building an agent on top (the Dodi pattern)
The MCP server is the tool layer. Your agent is the brain. Here's how Dodi works as a reference architecture:
1. Write a system prompt
Give the agent a persona, decision framework, and autonomy rules. Dodi's:
- Aggressive, data-driven, no sentimentality
- Auto-applies lineup optimizations (no confirmation needed)
- Reports trade recommendations but waits for approval (or not — your call)
- Returns
[SILENT]when there's nothing to report, so the harness skips delivery
2. Schedule recurring runs
Dodi runs as four cron jobs in Hermes, each loading the same skill/prompt:
| Schedule | Purpose |
|---|---|
| Sunday 01:00 UTC | Full pre-game: matchup analysis, lineup optimization, trade scan, waiver check |
| Thursday 01:00 UTC | TNF lineup lock — make sure Thursday starters are set |
| Tue–Sat 20:00 UTC | Daily trade inbox + waiver wire scan |
| Monday 22:00 UTC | MNF late injury check |
Each run delivers a report to Discord and a short push notification to the
phone via ntfy (the notify_manager tool), so you know to check it without
staring at Discord all day.
Give the agent the MCP server and nothing else. Team names are written by
the other managers and reach the prompt; the server strips them to short,
plain labels, but an agent with a shell could still be talked into reading
.env or the approval queue. In Hermes, set the job's enabled_toolsets to
just this server (e.g. ["espn"]).
3. Let it write
The tools default to apply=false (preview only). Your prompt decides when
the agent calls with apply=true. Dodi auto-applies set_lineup but previews
trades. A more aggressive agent could auto-send trades where both sides gain.
A cautious one could preview everything. The server doesn't care — it enforces
the preview/apply split, your prompt enforces policy.
How it decides
VORP, not projected points. Raw projections say draft a QB first — the top QB outscores the top RB. That's wrong in a 1-QB league because the 12th QB also scores a lot, making the top QB's edge over replacement small. VORP measures each player against the last startable player at his position in your league: team count, starting slots, and an empirical FLEX allocation. Scoring rules (PPR, half-PPR, TE premium, custom) are read from the league — no configuration needed.
Two projections in season. week_proj is ESPN's weekly number (reflects
opponent, injury, bye) — decides start/sit. ros_pg is rest-of-season points
per remaining game — decides roster value, trade worth, waiver priority.
Everything is a change to the optimal starting lineup. A pickup who sits behind what you have gains 0 whatever his projection. A trade is judged by what each side's starters project before and after. The lineup solver runs the same way for every team, so strength is comparable league-wide.
Tiers are 1-D k-means over each position's draftable range. "Players left in this tier" adapts to the local distribution instead of panicking at every gap.
Draft day
ESPN publishes no picks until a draft ends — verified against real drafts. The read API reports zero picks and empty rosters while the room is live, then everything at once on completion. Your agent needs picks fed in as they happen:
record_picks(["Gibbs", "Chase", ...])each turn, only the new names. Persisted tostate/, survives restarts, deduplicates on re-send.- Or use the browser scripts:
live_draft.pywatches the room and records automatically,sync_from_room.pyseeds from room history if you join late,draft_player.pyclicks DRAFT, andautodraft.pyruns the whole draft on the value board. These needpip install -e ".[browser]"and Chrome with--remote-debugging-port=9222(bind to 127.0.0.1, close after). snapshot.pysaves the full board tosnapshots/as JSON and CSV before the draft, so an ESPN outage doesn't leave you blind.
On the clock, get_draft_context returns picks_between_this_and_next — the
number that decides if a player will last. ADP entries include adp_moving
("earlier" / "later") and adp_age_hours so your agent can discount stale
market data.
ESPN quirks handled
Each found against a real league, each with a regression test.
- Placeholder picks and negative IDs. Unstarted drafts pre-seed every slot
with
playerId: -1. D/ST IDs are also negative. Exact-1match, not> 0filtering. - No D/ST projections. Defenses project 0.0; they're marked
ranked_by: espn_adpwithvorp: nulland sort below real-value players. - Non-contiguous team IDs. A 10-team league can have IDs
[1,2,3,6,7,8,9,10,12,13]. Draft slot comes from position in the order. - Weekly stats require per-week requests. One
scoringPeriodIdper fetch. - Mid-week scores use
*Livefields.totalPointsstays 0 until the week is final;totalPointsLivehas the running score. - Draft time is epoch ms in UTC. Rendered in local time with countdown.
- Randomized draft order rewrites late. Read from ESPN's pick schedule
(live, uncached), not settings.
draft_slot_is_provisionalflags pre-draw.
Cookie expiry, datacenter IP blocks, and ESPN's mediocre projections are the other failure modes. The value math and ADP-vs-value gaps are the edge — ESPN's ADP reflects what your ESPN leaguemates will actually do.
Caching
Player pool: ESPN_POOL_TTL seconds (default 900). Rosters: 60s, refreshed
after writes. Draft state and transaction log: never cached.
Tests
./.venv/bin/python -m pytest tests/ -q
118 tests, no network, no credentials. ESPN client stubbed with real payloads. Covers value math, snake ordering, board assembly, lineup solving, trade/waiver arithmetic, every write's transaction shape, and cookie scoping.
Layout
src/espn_mcp/
config.py .env loading, secret masking
espn.py HTTP client: host pin, cookie scoping, reads and writes
constants.py ESPN position / slot / pro-team ID maps
scoring.py league shape parsing, league-scored projections
value.py replacement level, VORP, tiers (pure, tested)
season.py ROS, optimal lineups, lineup plans,
trade / waiver deltas, transaction log (pure, tested)
board.py caching layer, draft state, in-season rosters and matchups
server.py the MCP tools
proposals.py the approval queue (pure, tested)
notify.py ntfy pushes and the approval buttons
approve.py the service behind the buttons
gametime.py scheduling counted back from kickoff
autolineup.py set the lineup without a model
usage.py expected points from workload (pure, tested)
factcheck.py numbers in the agent's text against the tools' (pure, tested)
market.py trade arithmetic on market values (pure, tested)
tracking.py what was known before kickoff, for scoring later
experts.py ESPN analysts' weekly ranks, and where they disagree
report.py the weekly review
sources/ Sleeper, FantasyCalc, nflverse, betting lines, weather
research/ the backtests behind every outside signal
scripts/
doctor.py credential + access check
snapshot.py offline fallback board
live_draft.py watch the draft room, record picks (browser)
sync_from_room.py seed board from room history (browser)
draft_player.py click DRAFT in the room (browser)
autodraft.py draft on the value board (browser)
watch_draft.py poll read API during draft (proves it lags)
simulate_draft.py offline mock draft
simulate_autodraft.py offline autodraft scorer
grade_external.py grade rosters against outside ranking
state/ picks, browser profile, notes (gitignored)
Dodi MCP 질문
Dodi MCP는 어떤 도구를 제공하나요?
26개 도구를 제공합니다. 예: get_draft_context, next_pick, get_draft_state, get_available_players, get_value_board.
Dodi 서버가 제 리그에서 실제로 수행할 수 있는 작업은 무엇인가요?
드래프트 픽, 주간 라인업, 웨이버, 트레이드 및 로스터 변경에 대한 전체 제어 기능을 제공합니다.
선수 트레이드와 같은 민감한 작업을 서버가 어떻게 처리하나요?
주요 선수를 트레이드하는 것과 같은 특정 작업에 대해 에이전트가 사용자의 승인을 받도록 자율성 범위를 설정할 수 있습니다.
모바일 기기에서 로스터 변경을 승인할 수 있나요?
네, 특정 설정을 활성화하면 서버가 휴대폰으로 알림을 보내 제안을 승인하거나 거절할 수 있는 버튼을 제공합니다.