Skip to main content
Glama
JoonHyun814

tft-meta-coach

by JoonHyun814
README.md
# TFT Meta Coach MCP Server

A stateless, Streamable-HTTP MCP server providing Teamfight Tactics (TFT, 전략적 팀 전투)
meta statistics and personal match analysis, built for submission to PlayMCP.

See [`abstrack.txt`](abstrack.txt) for the project pitch, [`mcp_guide.md`](mcp_guide.md) for
the PlayMCP requirements this server follows, and [`skills.md`](skills.md) for the full tool spec.

## Architecture

```
app/
  api/         # (reserved for future non-MCP HTTP endpoints)
  tools/       # MCP tool implementations + FastMCP registry
  riot/        # Riot API wrapper (account/summoner/league/match/ddragon/patch notes)
  cache/       # Redis client + key schema
  analysis/    # Deck classifier, meta stat aggregation, history/weakness analysis
  models/      # Pydantic domain models
  scheduler/   # Batch jobs (Cloud Run Job entrypoint)
  utils/       # Config, logging, errors
main.py        # FastAPI app; mounts the MCP Streamable HTTP transport
```

Realtime MCP tool calls only ever read Redis or make lightweight, per-player Riot API
calls (profile lookup, match history). Meta statistics (deck tier list, augment stats,
item builds, Challenger baseline) are **never computed on request** — they're produced
by the scheduled batch job below and served from Redis.

## Batch pipeline

```
Cloud Scheduler -> Cloud Run Job (python -m app.scheduler.run_batch)
                 -> tft-league-v1 (Challenger players)
                 -> tft-match-v1 (recent matches, deduped across shared lobbies)
                 -> deck classification + pandas aggregation
                 -> Redis (meta:decks, meta:augments, meta:items, meta:baseline, meta:lastUpdated)
```

Each key is stored both as the "latest" value and patch-scoped (e.g. `meta:decks:14.24`),
so tools can serve either the current snapshot or a specific past patch.

## Deck classification

Riot's match API never returns a deck name — `app/analysis/deck_rules.py` is a small,
declarative rule table (trait-threshold rules + carry-item rules) that `deck_classifier.py`
matches against. To update for a new TFT set, edit `deck_rules.py` only; the classifier
itself has no set-specific logic.

## Running locally

```bash
cp .env.example .env   # fill in RIOT_API_KEY at minimum
pip install -r requirements.txt
redis-server &          # or point REDIS_URL at an existing instance

# one-off: populate the meta caches
python -m app.scheduler.run_batch

# start the MCP server
uvicorn main:app --reload --port 8080
```

The Streamable HTTP endpoint is served at `http://localhost:8080/mcp` (stateless mode —
no session negotiation required). `GET /healthz` is a plain liveness check.

## Tests

```bash
pytest
```

Unit tests cover deck classification, meta stat aggregation, history/weakness analysis,
and hidden-OP deck recommendation. Integration tests mock the Riot API with `respx` and
Redis with `fakeredis`, and drive the MCP tool functions end-to-end.

## Docker / Cloud Run

```bash
docker build -t tft-mcp-server .
docker run -p 8080:8080 --env-file .env tft-mcp-server
```

The same image serves both roles on Cloud Run:

- **Service** (the MCP server): default `CMD` from the Dockerfile.
- **Job** (the batch scheduler): override the container command to
  `python -m app.scheduler.run_batch`, triggered on a schedule by Cloud Scheduler.

## Known limitations

- `tft_summarize_patch` scrapes Riot's public patch notes page with a heuristic
  buff/nerf classifier (keyword-based `<li>` parsing) — if Riot changes that page's
  markup, only this tool's parsing degrades (it returns an empty buff/nerf list with an
  explanatory `meta_impact`, rather than failing the whole server).
- `rankFilter` currently only supports `"challenger"`, matching what the batch job
  actually collects (`tft-league-v1` Challenger league).