Skip to main content
Glama
oliver-howard

pogo-mcp

README.md
# pogo-mcp

An MCP server that gives an LLM reliable, current Pokémon GO mechanics data and
(eventually) PvE/raid analysis tools.

> This project is not affiliated with Niantic, The Pokémon Company, Nintendo, or
> Pokémon GO. Game data is obtained from
> [pokemon-go-api](https://pokemon-go-api.github.io/pokemon-go-api), a
> community-maintained public source, and is **not** an official Niantic API.
> Its Pokémon/move data is derived primarily from PokeMiners' Game Master
> dumps, with raid/quest data scraped from Leek Duck, Pokebattler, and
> Snacknap.

## Status

Data integration is done - species, moves, movesets, forms, and the type
chart, all backed by a validated local snapshot. Core mechanics are in too:
CP/HP calculation, power-up economics, and PvE (raid/gym) move damage -
STAB, type effectiveness, weather, Shadow, Mega/Primal boost, and
friendship, with a full multiplier breakdown returned alongside the final
damage. None of this data comes from pokemon-go-api (it has no CP, economy,
damage-formula, or Shadow/Mega-boost data at all) - every constant is
sourced directly from the game's own datamined Game Master file with exact
citations; see `docs/mechanics-constants.md`.

### Supported now

- Current Pokémon GO species data: base stats, types, legal fast/charged/elite
  movesets, generation, Pokédex class.
- Every alternate battle form as its own first-class entity - Mega/Primal
  evolutions, and forms like Giratina Altered/Origin, Deoxys
  Normal/Attack/Defense/Speed, Zamazenta Hero/Crowned, Kyurem/Black
  Kyurem/White Kyurem - never merged just because they share a Pokédex number.
- Move data with PvE fields (power/energy/duration) kept strictly separate
  from PvP fields (different power curve, buff/debuff chances).
- Pokémon GO's actual 2-tier type effectiveness chart and the weather→type
  boost mapping (sourced from the provider, not hand-maintained).
- Fuzzy-but-safe name resolution (`"Ho-Oh"` / `"ho oh"`, `"Mr. Mime"`,
  `"Farfetch'd"` / `"farfetchd"`) that returns a structured
  `ambiguous_pokemon`/`ambiguous_move` error with every candidate instead of
  silently guessing which battle form or move you meant.
- A locally-owned, validated, versioned snapshot with atomic promotion - tool
  calls never make a live network request; refreshes are explicit
  (auto on staleness, or via `refresh_data`) and never discard the last known
  good snapshot until a new one has been fetched, normalized, *and* validated.
- CP/HP/actual-Attack/actual-Defense calculation for any exact IV spread and
  level (1.0-50.0 in 0.5 increments), including the Shadow attack/defense
  modifiers the real game applies to displayed CP.
- Power-up Stardust/Candy/Candy XL cost between any two levels, including
  Shadow surcharge, Purified discount, and Lucky's Stardust-only discount
  (Lucky + Purified can combine; Shadow can't combine with either).
- PvE (raid/gym) move damage for a single hit - STAB, type effectiveness,
  weather boost, Shadow attack/defense, same/different-type Mega/Primal raid
  boost, friendship attack bonus (Not Friends through Best Friend), and
  dodge damage reduction - returning both the final damage and every
  intermediate multiplier for transparency.

### Not yet implemented (see `docs/provider-gap-analysis.md`)

DPS/TDO/ER moveset evaluation, raid counter ranking, fast-move breakpoints,
and a full raid battle timeline simulator are all still to come. **PvP is
explicitly out of scope for this project.**

## Architecture

```
pokemon-go-api  →  PokemonGoApiProvider  →  Raw* models  →  normalizer
                                                                  │
                                                                  ▼
                                          validator  →  local snapshot (.cache/)
                                                                  │
                                                                  ▼
                                                      in-memory repository
                                                                  │
                                                                  ▼
                                                            MCP tools
```

Nothing outside `src/pogo_mcp/providers/` ever makes a network request or
knows pokemon-go-api's JSON shape. Everything else - normalization, the
repository, and every tool - depends only on the normalized Pydantic models in
`src/pogo_mcp/models/`, so the data source can be replaced later without
touching the rest of the codebase.

## Caching and freshness

On startup, the server loads `.cache/current/` immediately if it exists (no
network call). If it doesn't (first run), it fetches synchronously before
serving any tool call. If the loaded snapshot is older than
`POGO_MCP_REFRESH_INTERVAL_HOURS` (default 6), a refresh is triggered
automatically; otherwise use the `refresh_data` tool to force one.

A refresh always fetches, normalizes, and validates a *new* snapshot into a
staging directory before touching anything - `.cache/current/` is only
replaced (via an atomic directory rename, moving the old `current/` to
`previous/`) once the new snapshot is fully written and passes validation. If
the upstream API is unreachable or the new data fails validation, the
previously-served snapshot keeps being used and the failure is surfaced
through `get_data_status` as `last_refresh_error` - tool calls never silently
fall back to an empty dataset.

## Installation

Requires Python 3.12+ and [`uv`](https://docs.astral.sh/uv/).

```bash
uv sync
cp .env.example .env   # optional; defaults work out of the box
```

Run the server directly (stdio transport):

```bash
uv run pogo-mcp
```

### MCP client setup

For Claude Code:

```bash
claude mcp add pogo-mcp -- uv run --directory /path/to/pogo-mcp pogo-mcp
```

Or add manually to your MCP client config (e.g. `claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "pogo-mcp": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/pogo-mcp", "pogo-mcp"]
    }
  }
}
```

## Tools

| Tool | Description |
|---|---|
| `get_pokemon` | Look up one Pokémon battle form by name or id. Returns a structured `ambiguous_pokemon` error with every candidate if the name matches more than one form. |
| `search_pokemon` | Substring search across Pokémon display names, for browsing. |
| `get_move` | Look up one move by name or id. Returns PvE and PvP fields separately. |
| `search_moves` | Substring search across move display names. |
| `get_moveset` | Every legal fast/charged/elite move for one Pokémon, resolved to full move records. |
| `get_data_status` | Snapshot freshness, validation status, record counts, and whether cached data is currently being served after a failed refresh. |
| `refresh_data` | Force an immediate fetch/normalize/validate/promote cycle. |
| `calculate_cp` | CP and actual Attack/Defense/HP for a Pokémon at an exact level + IV spread, with optional `shadow`. |
| `calculate_powerup_cost` | Total Stardust/Candy/Candy XL to power up between two levels, with optional `shadow`/`purified`/`lucky`. |
| `calculate_move_damage` | Damage for one hit of a move (attacker vs. defender), with the full STAB/effectiveness/weather/Mega/friendship/dodge multiplier breakdown. Not a battle simulator - one deterministic hit. |

All tools return structured JSON - errors use a consistent
`{"error": "...", ...}` shape (`pokemon_not_found`, `ambiguous_pokemon`,
`move_not_found`, `ambiguous_move`, `invalid_input`) rather than guessing or
returning empty results.

## Validation strategy

Every snapshot is validated before promotion (`src/pogo_mcp/data/validator.py`):
non-empty datasets, positive base stats, every referenced move id actually
exists, all types are recognized against the fetched type chart, no duplicate
ids, sane move power/duration ranges, and sentinel lookups for well-known
Pokémon (Pikachu, Mewtwo, Machamp, Groudon, Kyogre, Rayquaza, Zamazenta) and
moves (Counter, Psycho Cut, Psystrike, Hydro Cannon, Precipice Blades) by
name - not by pinning exact stat values, since balance changes are legitimate
and shouldn't fail validation.

## Mechanics constants

CP multipliers, power-up costs, Shadow attack/defense modifiers, Mega/Primal
raid-boost multipliers, and PvE damage formula constants (STAB, weather
boost, friendship bonus, dodge reduction) aren't in pokemon-go-api. They're
sourced directly from the game's own datamined Game Master file
(`PokeMiners/game_masters`), with the exact commit, template, and field cited
in the module that defines each one (`src/pogo_mcp/mechanics/levels.py`,
`powerups.py`, `shadows.py`, `megas.py`, `types.py`, `damage.py`). See
`docs/mechanics-constants.md` for the consolidated index, including the two
places confidence is lower than the rest (half-level CPM extrapolation past
level 38.5, and Candy XL cost step granularity) and why.

## Development

```bash
uv sync --all-groups
uv run pytest                    # unit tests only (no network)
uv run pytest -m integration     # + live pokemon-go-api tests
uv run ruff check .
uv run ruff format .
uv run pyright
```

Tests under `tests/fixtures/` are real recorded pokemon-go-api payloads
(trimmed to a handful of species chosen to exercise every tricky case: Mega
and Primal forms, gendered species with colliding names, and multi-form
legendaries) so normalization tests don't depend on network access or on
upstream data staying byte-for-byte identical forever.

## Scripts

```bash
uv run python scripts/refresh_data.py       # force a fetch/normalize/validate/promote cycle
uv run python scripts/validate_snapshot.py  # re-validate the current local snapshot offline
```

TDQS

A4/5.0

Scored across 10 tools

Disambiguation5/5

Every tool has a clear, unique purpose: search vs exact lookup for both Pokemon and moves, separate calculation tools for CP, power-up cost, and damage, plus distinct data-management tools for status and refresh. There is no apparent overlap or ambiguity between any pair of tools.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern using snake_case: get_pokemon, search_pokemon, get_move, search_moves, get_moveset, get_data_status, refresh_data, calculate_cp, calculate_powerup_cost, calculate_move_damage. Grouping by verb (get/search/calculate/refresh) creates a predictable and readable structure.

Tool Count5/5

10 tools is well-scoped for a Pokemon GO data and calculation server. The set covers lookup, discovery, data management, and simulation without being bloated or thin, and each tool earns its place.

Completeness4/5

The domain is well covered with Pokemon/move lookup and search, moveset resolution, CP and damage calculations, and data-refresh management. Minor gaps exist, such as no PvP moveset endpoint and no standalone type-effectiveness lookup, but these are non-critical and the core workflows are fully supported.

Maintenance

ActivityMaintained
ResponsivenessNo issues