Skip to main content
Glama

Optimize EVs for a goal

optimize_evs
Read-onlyIdempotent

Derive a minimal EV spread to survive a named attack, outspeed a target, and/or guarantee a KO in 1-4 hits. Leftover EVs go to a chosen stat; returns stats, totals, and verification.

Instructions

Derive a minimal EV spread for one Pokémon satisfying up to three goals: survive a named attack, outspeed a target Speed, and guarantee a KO in 1-4 hits. Use it when EVs must come from a goal — calculate_stats evaluates a spread you already have, check_speed ranks Speed without deriving EVs, and get_set returns a curated spread. Supplying none of survive/outspeed/kill errors; outspeed takes a set target or a raw speed, and leftover EVs fill maximize (default spe). Returns the spread, resulting stats, totalEVs/unusedEVs of the 508 usable, and a verification line per goal. Read-only and offline; an unreachable goal returns an isError.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
ivsNoIVs to hold fixed while searching, keyed by stat id; omitted stats default to 31 (use 0 for a Trick Room Speed IV).
itemNoItem held while solving, e.g. "Assault Vest"; it changes the bulk or Speed the goals are tested against.
killNoAdd a KO goal: minimizes Atk or SpA so that even the lowest damage roll reaches the per-hit HP threshold.
fieldNoBattlefield conditions applied while testing the survive and kill goals; omit for a neutral Singles field.
levelNoLevel 1-100; default 50 (VGC), where bulk and Speed benchmarks are tightest.
natureNoNature used for every stat calculation, e.g. "Adamant", "Calm"; default Serious. Change it to trade one stat for another.
abilityNoAbility assumed active while solving, e.g. "Intimidate", "Protosynthesis".
speciesYesSpecies or form name to optimize, e.g. "Garchomp", "Incineroar".
surviveNoAdd a "always live this hit" goal: the search minimizes HP plus the relevant Defense EVs that keep the worst roll below max HP.
maximizeNoStat that receives leftover EVs after the goals are met, capped at 252 (default spe); EVs are added in steps of 4.spe
outspeedNoAdd an outspeed goal: supply `target` or `speed` (one is required here, otherwise that goal errors).

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
evsYesThe solved spread: all six keys, each a multiple of 4 from 0 to 252, with the `maximize` stat holding the leftover EVs.
itemNoHeld item assumed while solving, as supplied; absent when the call gave none.
noteYesCaveat about how the spread was built: EVs come in steps of 4 (508 usable of 510), the maximize stat is capped at 252, and unused EVs can be reallocated by hand.
levelYesLevel every stat was computed at, 1-100.
statsYesThe six stats this exact spread reaches at this level and nature; recompute with `calculate_stats` to check a different spread.
natureYesNature the spread was solved with, e.g. "Adamant"; Serious when the call omitted one.
speciesYesCanonical species name the spread was solved for.
totalEVsYesSum of the six solved EVs, spent in multiples of 4 so 508 is the practical maximum.
unusedEVsYesEVs left over after the goals and the maximize step: 508 minus `totalEVs`, never negative.
verificationYesOne line per goal that was solved, e.g. "survive: Dragapult Dragon Darts -> 96-114 vs 175 HP (max 65%)" or "kill: 132 ATK EVs -> 187-221 vs 175 HP (min 100%)"; the evidence that the spread meets each goal.
championsPointsYesThe spread as Pokémon Champions stat points (whole numbers, at most 32 in a stat, 66 total) — what the game’s training screen takes; stats left uninvested are omitted and a maxed stat reads 32. The two systems budget differently (510 EVs against 66 points), so a spread trimmed to fit the EV cap reads back a point or two under what was asked: this is the nearest point spread for the stats actually computed, not a copy of the input.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed6 schema fields changedv3.2.0
    • removedInput schema / properties / generation
      Removed value: -{
      -  "default": 9,
      -  "description": "Generation whose data to use, 1-9 (default 9). Earlier generations omit moves, items, abilities, and forms that did not exist yet.",
      -  "maximum": 9,
      -  "minimum": 1,
      -  "type": "integer"
      -}
    • removedInput schema / properties / kill / properties / target / properties / teraType
      Removed value: -{
      -  "description": "Tera type to use when the move is Terastallized, e.g. \"Fairy\".",
      -  "type": "string"
      -}
    • removedInput schema / properties / outspeed / properties / target / properties / teraType
      Removed value: -{
      -  "description": "Tera type to use when the move is Terastallized, e.g. \"Fairy\".",
      -  "type": "string"
      -}
    • removedInput schema / properties / survive / properties / attacker / properties / teraType
      Removed value: -{
      -  "description": "Tera type to use when the move is Terastallized, e.g. \"Fairy\".",
      -  "type": "string"
      -}
    • removedOutput schema / properties / generation
      Removed value: -{
      -  "description": "Generation whose data and mechanics were used, 1-9.",
      -  "maximum": 9007199254740991,
      -  "minimum": -9007199254740991,
      -  "type": "integer"
      -}
    • changedOutput schema / required
      Previous value: -[
      -  "species",
      -  "generation",
      -  "level",
      -  "nature",
      -  "evs",
      -  "championsPoints",
      -  "stats",
      -  "totalEVs",
      -  "unusedEVs",
      -  "verification",
      -  "note"
      -]New value: +[
      +  "species",
      +  "level",
      +  "nature",
      +  "evs",
      +  "championsPoints",
      +  "stats",
      +  "totalEVs",
      +  "unusedEVs",
      +  "verification",
      +  "note"
      +]
  2. Changed5 schema fields changedv2.0.2
    • addedInput schema / properties / kill / properties / target / properties / championsPoints
      Added value: +{
      +  "additionalProperties": {
      +    "type": "number"
      +  },
      +  "description": "Pokémon Champions stat points keyed by stat id (hp, atk, def, spa, spd, spe): whole numbers, each 0-32, totalling at most 66. This is the spread the game’s training screen takes, and an alternative to `evs` — give one or the other, not both. One point is worth 8 EVs, so a converted spread is trimmed from its largest stats to fit the 510 EV cap the calculator enforces.",
      +  "propertyNames": {
      +    "type": "string"
      +  },
      +  "type": "object"
      +}
    • addedInput schema / properties / outspeed / properties / target / properties / championsPoints
      Added value: +{
      +  "additionalProperties": {
      +    "type": "number"
      +  },
      +  "description": "Pokémon Champions stat points keyed by stat id (hp, atk, def, spa, spd, spe): whole numbers, each 0-32, totalling at most 66. This is the spread the game’s training screen takes, and an alternative to `evs` — give one or the other, not both. One point is worth 8 EVs, so a converted spread is trimmed from its largest stats to fit the 510 EV cap the calculator enforces.",
      +  "propertyNames": {
      +    "type": "string"
      +  },
      +  "type": "object"
      +}
    • addedInput schema / properties / survive / properties / attacker / properties / championsPoints
      Added value: +{
      +  "additionalProperties": {
      +    "type": "number"
      +  },
      +  "description": "Pokémon Champions stat points keyed by stat id (hp, atk, def, spa, spd, spe): whole numbers, each 0-32, totalling at most 66. This is the spread the game’s training screen takes, and an alternative to `evs` — give one or the other, not both. One point is worth 8 EVs, so a converted spread is trimmed from its largest stats to fit the 510 EV cap the calculator enforces.",
      +  "propertyNames": {
      +    "type": "string"
      +  },
      +  "type": "object"
      +}
    • addedOutput schema / properties / championsPoints
      Added value: +{
      +  "additionalProperties": {
      +    "type": "number"
      +  },
      +  "description": "The spread as Pokémon Champions stat points (whole numbers, at most 32 in a stat, 66 total) — what the game’s training screen takes; stats left uninvested are omitted and a maxed stat reads 32. The two systems budget differently (510 EVs against 66 points), so a spread trimmed to fit the EV cap reads back a point or two under what was asked: this is the nearest point spread for the stats actually computed, not a copy of the input.",
      +  "propertyNames": {
      +    "type": "string"
      +  },
      +  "type": "object"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "species",
      -  "generation",
      -  "level",
      -  "nature",
      -  "evs",
      -  "stats",
      -  "totalEVs",
      -  "unusedEVs",
      -  "verification",
      -  "note"
      -]New value: +[
      +  "species",
      +  "generation",
      +  "level",
      +  "nature",
      +  "evs",
      +  "championsPoints",
      +  "stats",
      +  "totalEVs",
      +  "unusedEVs",
      +  "verification",
      +  "note"
      +]
  3. Changed3 schema fields changedv2.0.0
    • changedInput schema / properties / kill / properties / target / properties / moves / description
      Previous value: -"Moveset names, e.g. [\"Earthquake\", \"Dragon Claw\"]; used by `calc_matchups` to pick the hardest-hitting move per defender."New value: +"Moveset names, e.g. [\"Earthquake\", \"Dragon Claw\"]; used by `calculate_matchups` to pick the hardest-hitting move per defender."
    • changedInput schema / properties / outspeed / properties / target / properties / moves / description
      Previous value: -"Moveset names, e.g. [\"Earthquake\", \"Dragon Claw\"]; used by `calc_matchups` to pick the hardest-hitting move per defender."New value: +"Moveset names, e.g. [\"Earthquake\", \"Dragon Claw\"]; used by `calculate_matchups` to pick the hardest-hitting move per defender."
    • changedInput schema / properties / survive / properties / attacker / properties / moves / description
      Previous value: -"Moveset names, e.g. [\"Earthquake\", \"Dragon Claw\"]; used by `calc_matchups` to pick the hardest-hitting move per defender."New value: +"Moveset names, e.g. [\"Earthquake\", \"Dragon Claw\"]; used by `calculate_matchups` to pick the hardest-hitting move per defender."
  4. Addedv1.1.1

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false. The description adds valuable behavioral context beyond that: it is 'read-only and offline', an unreachable goal returns an isError, and the return payload includes spread, stats, totalEVs/unusedEVs of the 508 usable, and a verification line per goal. This goes beyond the annotations without contradicting them, though it could further clarify behavior around conflicting goals or edge cases.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a compact paragraph of five sentences, each serving a distinct purpose: core function, when-to-use, input constraint and outspeed modes, return values, and behavioral safety. It is front-loaded with the primary purpose and contains no filler or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (11 params, nested objects, output schema), the description covers the essential context: what the tool does, when to use it, what inputs are required, what it returns, and its read-only/offline behavior. The presence of a rich output schema means return-value details are already structured, and the description still summarizes them adequately. Nothing an agent needs to invoke correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 100% schema description coverage, the schema already documents every parameter. The description adds meaningful cross-parameter semantics: it explains the relationship among survive/outspeed/kill (at least one required), clarifies that outspeed accepts either a target set or a raw speed, and explains that leftover EVs go to maximize (default spe). This is genuinely useful beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('Derive a minimal EV spread') and precisely enumerates the three goal types (survive, outspeed, KO in 1-4 hits). It also distinguishes the tool from siblings by naming calculate_stats, check_speed, and get_set and contrasting their behavior. An agent can immediately tell what this tool does and how it differs.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use guidance ('Use it when EVs must come from a goal') and explains why each alternative is not appropriate (calculate_stats evaluates an existing spread, check_speed ranks without deriving EVs, get_set returns a curated spread). It also states an important usage constraint: supplying none of survive/outspeed/kill errors. This fully equips the agent to choose correctly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.