getcompetitive 4.5.1 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,8 +17,8 @@ Smogon-tier and archetype surface that used to sit alongside it is gone.
17
17
 
18
18
  ## What it provides
19
19
 
20
- - **30 tools** across six domains: data, team analysis, team workflows (paste in, diagnose, prepare matchups, learn from replays, scout sets, fill slots), meta (usage-derived), battle mechanics, and official regulation sets
21
- - **Six workflow prompts** — `/team-doctor`, `/matchup-prep`, `/build-around`, `/tournament-prep`, `/learn-my-team`, `/meta-report` — server-provided templates that chain the deterministic tools, so compound workflows stay discoverable without a 40-tool surface
20
+ - **8 compound tools**: `lookup`, `calculate`, `analyze_team`, `optimize_team`, `prepare_matchup`, `analyze_battle`, `analyze_meta`, `team_io` intent-level entrypoints that dispatch on a `mode`, so the model picks an intent and the server does the orchestration
21
+ - **Six workflow prompts** — `/team-doctor`, `/matchup-prep`, `/build-around`, `/tournament-prep`, `/learn-my-team`, `/meta-report` — server-provided templates that chain the eight tools, so compound workflows stay discoverable without widening the surface
22
22
  - **Structured, agent-first definitions** — every tool declares MCP annotations and an output schema, returns `structuredContent` alongside JSON text, and documents all of its parameters; the deterministic half of the [TDQS](https://tdqs.dev) checklist is linted in CI
23
23
  - Full **Pokémon Showdown** dataset — species, alternate forms, stats, moves, items, abilities, natures, learnsets, types
24
24
  - **Battle math** from Smogon's calculator — stat calculation and full damage calculation (weather, terrain, boosts, items)
@@ -30,59 +30,18 @@ Built on [`@pkmn/dex`](https://github.com/pkmn/EPOKe) (Showdown data) and
30
30
 
31
31
  ## Tools
32
32
 
33
- ### Data
34
- | Tool | Purpose |
35
- | --- | --- |
36
- | `get_pokemon` | Types, base stats, BST, abilities, forms, weight, gender, evolutions |
37
- | `list_forms` | All forms of a species (alternate, cosmetic, battle-only) |
38
- | `get_sprites` | Sprite URLs for a whole team in one call (artwork or icon), from a committed PokéAPI id table — URLs only, the server does no network I/O |
39
- | `search_dex` | Fuzzy name search across species / moves / items / abilities / natures |
40
- | `get_move` | Type, category, power, accuracy, PP, priority, target, secondary effects |
41
- | `get_item` | Effect, flags, mega stone, Z-move, Fling, boosts |
42
- | `get_ability` | Effect description, flags |
43
- | `get_nature` | Boosts / lowers which stat |
44
- | `get_learnset` | All learnable moves grouped by method (level-up, TM, egg, tutor, event) |
45
- | `get_type` | Defensive weaknesses, resistances, immunities |
46
- | `get_type_matchup` | Matchup multiplier, offensive coverage, or defensive chart (defender can be a species) |
47
-
48
- ### Team analysis
49
- | Tool | Purpose |
50
- | --- | --- |
51
- | `analyze_team` | Team synergy: stacked defensive weaknesses, offensive coverage gaps, speed placement, a heuristic score, and — with a regulation — coverage against the meta's real sets |
52
-
53
- ### Team workflows
54
- | Tool | Purpose |
55
- | --- | --- |
56
- | `parse_team` | Turn a Showdown/Pokepaste block (or a `species @ item \| ability \| nature \| EVs \| moves` one-liner) into the canonical team every tool takes; unknown names become warnings, not errors |
57
- | `format_team` | Render a canonical team back into paste text, round-tripping through `parse_team` |
58
- | `diagnose_team` | "Fix my team": weaknesses with evidence, then concrete spread, move, item and member changes, each backed by exact math or usage data |
59
- | `prepare_matchup` | "Prepare me": their likely sets by usage, speed races with margins, key damage rolls, bring-four, leads, and win/loss conditions |
60
- | `analyze_replay` | Post-match read of a battle log: teams, KOs with causes, observed Speed order, damage percentages, and a type-coverage read |
61
- | `infer_set` | Reverse constraint solving: battle observations (who moved first, damage dealt/taken) narrow the opponent's likely set, ranked against the meta |
62
- | `optimize_team` | Fill open team slots against constraints — cover these types, answer these threats — with reasons for every recommendation |
63
-
64
- ### Meta (usage-derived)
65
- | Tool | Purpose |
66
- | --- | --- |
67
- | `list_threats` | Most-used Pokemon of a regulation, ranked by measured usage (role, tier, usage share) with the sample and sources behind it |
68
- | `compare_meta` | What is changing: last 7 days of usage vs the 7 before, per species and per two-species core, from a committed build-time aggregation |
69
- | `get_set` | Most-played set for one or several species (item, ability, nature, EVs, 4 moves), with the Mega form and ability where relevant; covers each regulation's ranked species, which `list_threats` lists |
70
-
71
- ### Regulations (Pokémon Champions / VGC)
72
- | Tool | Purpose |
73
- | --- | --- |
74
- | `list_regulations` | Official Regulation Sets with dates, status, roster size, and how many species have usage-derived sets |
75
- | `get_regulation` | Full set rules: battle rules, clauses, Mega rules, and roster sizes; the name rosters come back with `includeRoster` |
76
- | `check_legality` | Validate a team against a set: illegal species, Species/Item Clause, illegal moves, team size, Mega eligibility |
33
+ Eight compound tools; each dispatches on a `mode` field where the name alone is ambiguous.
77
34
 
78
- ### Battle mechanics
79
- | Tool | Purpose |
80
- | --- | --- |
81
- | `calculate_stats` | Final 6 stats at a level with EVs/IVs/nature (in-game formula) |
82
- | `calculate_damage` | Full damage calc (sets, item, ability, boosts, weather, terrain, hazards) |
83
- | `calculate_matchups` | Batch damage: one attacker vs many defendersbest move, damage range, KO chance, who moves first |
84
- | `check_speed` | Final Speed (nature/EV/IV/boost/Scarf) vs a regulation roster's invested/uninvested speeds |
85
- | `optimize_evs` | EV spread solver: min EVs to survive / outspeed / guarantee a KO, then maximize a stat |
35
+ | Tool | Purpose | Modes |
36
+ | --- | --- | --- |
37
+ | `lookup` | One data lookup | species, forms, search, move, item, ability, nature, learnset, type, matchup, sprites |
38
+ | `calculate` | One battle calc | stats, damage, matchups, speed, optimize_evs |
39
+ | `analyze_team` | Team analysis | synergy, diagnose |
40
+ | `optimize_team` | Fill open team slots against constraints | — |
41
+ | `prepare_matchup` | Pre-game dossier: likely sets, speed races, damage rolls, exhaustive bring-four with alternates, scored leads, win/loss conditions | — |
42
+ | `analyze_battle` | Post-game and scouting | replay, infer |
43
+ | `analyze_meta` | The meta, measured | threats, compare, set |
44
+ | `team_io` | Team import/export and validation | parse, format, legality, regulation, regulations |
86
45
 
87
46
  ## Install
88
47
 
@@ -179,14 +138,14 @@ npm test # builds and drives every tool over real MCP stdio, plus the HTTP ent
179
138
 
180
139
  ## Example queries
181
140
 
182
- - `get_pokemon` `{ "species": "Ogerpon-Wellspring" }`
183
- - `get_type_matchup` `{ "attacker": "Ice", "defender": "Garchomp" }` → 4x super effective
184
- - `calculate_stats` `{ "species": "Garchomp", "level": 50, "nature": "Jolly", "evs": { "atk": 252, "spe": 252 } }`
185
- - `calculate_damage` `{ "attacker": { "species": "Garchomp", "level": 50, "nature": "Jolly", "evs": { "atk": 252, "spe": 252 }, "item": "Choice Band" }, "defender": { "species": "Corviknight", "level": 50, "nature": "Impish", "evs": { "hp": 252, "def": 252 } }, "move": "Dragon Claw" }`
186
- - `check_legality` `{ "regulation": "m-c", "team": [ { "species": "Garchomp", "item": "Choice Band" } ] }`
187
- - `analyze_team` `{ "team": [ { "species": "Garchomp", "moves": ["Earthquake", "Dragon Claw", "Rock Slide"] } ], "regulation": "m-c" }`
188
- - `parse_team` `{ "text": "Garchomp @ Choice Scarf | Rough Skin | Jolly | 252 Atk / 252 Spe | Earthquake / Dragon Claw" }`
189
- - `diagnose_team` `{ "team": [ { "species": "Garchomp", "nature": "Jolly", "evs": { "atk": 252, "spe": 252 } } ], "goal": "improve against the current meta" }`
141
+ - `lookup` `{ "mode": "species", "species": "Ogerpon-Wellspring" }`
142
+ - `lookup` `{ "mode": "matchup", "attacker": "Ice", "defender": "Garchomp" }` → 4x super effective
143
+ - `calculate` `{ "mode": "stats", "species": "Garchomp", "level": 50, "nature": "Jolly", "evs": { "atk": 252, "spe": 252 } }`
144
+ - `calculate` `{ "mode": "damage", "attacker": { "species": "Garchomp", "level": 50, "nature": "Jolly", "evs": { "atk": 252, "spe": 252 }, "item": "Choice Band" }, "defender": { "species": "Corviknight", "level": 50, "nature": "Impish", "evs": { "hp": 252, "def": 252 } }, "move": "Dragon Claw" }`
145
+ - `team_io` `{ "mode": "legality", "regulation": "m-c", "team": [ { "species": "Garchomp", "item": "Choice Band" } ] }`
146
+ - `analyze_team` `{ "mode": "synergy", "team": [ { "species": "Garchomp", "moves": ["Earthquake", "Dragon Claw", "Rock Slide"] } ], "regulation": "m-c" }`
147
+ - `team_io` `{ "mode": "parse", "text": "Garchomp @ Choice Scarf | Rough Skin | Jolly | 252 Atk / 252 Spe | Earthquake / Dragon Claw" }`
148
+ - `analyze_team` `{ "mode": "diagnose", "team": [ { "species": "Garchomp", "nature": "Jolly", "evs": { "atk": 252, "spe": 252 } } ], "goal": "improve against the current meta" }`
190
149
  - `prepare_matchup` `{ "team": [ { "species": "Garchomp" } ], "opponent": ["Sneasler", "Salamence-Mega", "Gholdengo"] }`
191
150
 
192
151
  ## Data freshness
@@ -0,0 +1,200 @@
1
+ /**
2
+ * MatchupEvaluator: one reusable definition of what constitutes an actual
3
+ * competitive answer. "Has a super-effective move" is not an answer; this
4
+ * engine runs the real math both directions — each side's hardest hit and
5
+ * damage range, level-50 Speed with priority taken into account, and a
6
+ * conservative turn simulation — and classifies the matchup.
7
+ *
8
+ * Determinism contract: the classification never consults a random roll. Our
9
+ * side is always simulated with its minimum roll and their side with its
10
+ * maximum, so a classification of HARD_ANSWER or SOFT_ANSWER is a worst-case
11
+ * guarantee, and SPEED_DEPENDENT/TRADE mean exactly that order or rolls can
12
+ * flip the result.
13
+ */
14
+ import { getChampionsDex } from './champions.js';
15
+ import { damageResult, finalStat, typeEffectiveness } from './dex.js';
16
+ /** Better answers rank lower; shared by every consumer that picks the best member. */
17
+ export const ANSWER_CLASS_RANK = {
18
+ HARD_ANSWER: 0,
19
+ SOFT_ANSWER: 1,
20
+ REVENGE: 2,
21
+ SPEED_DEPENDENT: 3,
22
+ TRADE: 4,
23
+ UNFAVORABLE: 5,
24
+ UNKNOWN: 6,
25
+ };
26
+ /** Strongest hit one side has into the other's typing, by eff x power x STAB. */
27
+ function bestMove(dex, side, targetTypes) {
28
+ let best = null;
29
+ for (const mv of side.moves ?? []) {
30
+ const m = dex.moves.get(mv);
31
+ if (!m.exists || !(m.basePower || 0))
32
+ continue;
33
+ const eff = typeEffectiveness(m.type, targetTypes, 9);
34
+ if (eff === 0)
35
+ continue;
36
+ const stab = dex.species.get(side.species).types.includes(m.type) ? 1.5 : 1;
37
+ const score = eff * m.basePower * stab;
38
+ if (!best || score > best.score)
39
+ best = { name: m.name, score, priority: m.priority ?? 0 };
40
+ }
41
+ return best ? { name: best.name, priority: best.priority } : null;
42
+ }
43
+ /**
44
+ * Conservative two-hit race simulation: our minimum roll against their maximum,
45
+ * alternating by Speed unless priority intervenes. Returns whether we win and
46
+ * the share of our HP left in the worst case.
47
+ */
48
+ function simulateRace(ourMove, theirMove, ourRange, theirRange, ourHp, theirHp, ourPriority, theirPriority, weMoveFirst) {
49
+ let usHp = ourHp;
50
+ let themHp = theirHp;
51
+ // Conservative rolls: our worst, their best.
52
+ const ourHit = ourRange[0];
53
+ const theirHit = theirRange[1];
54
+ let turn = 0;
55
+ while (usHp > 0 && themHp > 0 && turn < 12) {
56
+ turn++;
57
+ const ourTurn = ourPriority !== theirPriority
58
+ ? ourPriority > theirPriority
59
+ : weMoveFirst
60
+ ? turn % 2 === 1
61
+ : turn % 2 === 0;
62
+ if (ourTurn) {
63
+ themHp -= ourHit;
64
+ if (themHp <= 0)
65
+ return { weWin: true, hpLeftPct: (usHp / ourHp) * 100 };
66
+ }
67
+ else {
68
+ usHp -= theirHit;
69
+ if (usHp <= 0)
70
+ return { weWin: false, hpLeftPct: (usHp / ourHp) * 100 };
71
+ }
72
+ }
73
+ // 12 turns without a faint: effectively unwinnable by damage.
74
+ return { weWin: false, hpLeftPct: (usHp / ourHp) * 100 };
75
+ }
76
+ /** True when the side has at least one damaging move that is not fully resisted or immune into the target. */
77
+ function canHit(dex, side, targetTypes) {
78
+ for (const mv of side.moves ?? []) {
79
+ const m = dex.moves.get(mv);
80
+ if (m.exists && (m.basePower || 0) && typeEffectiveness(m.type, targetTypes, 9) > 0)
81
+ return true;
82
+ }
83
+ return false;
84
+ }
85
+ /**
86
+ * Classify the matchup between two sets. Both sides must carry at least one
87
+ * usable damaging move, else the class is UNKNOWN — a type chart is not a
88
+ * battle.
89
+ */
90
+ export function evaluateMatchup(us, them) {
91
+ const dex = getChampionsDex();
92
+ const usSp = dex.species.get(us.species);
93
+ const themSp = dex.species.get(them.species);
94
+ const ourMove = bestMove(dex, us, themSp.types);
95
+ const theirMove = bestMove(dex, them, usSp.types);
96
+ const speed = (side, sp) => {
97
+ const nature = side.nature ?? 'Serious';
98
+ const evs = side.championsPoints
99
+ ? Object.fromEntries(Object.entries(side.championsPoints).map(([k, v]) => [k, v * 8]))
100
+ : (side.evs ?? {});
101
+ let s = finalStat(9, 'spe', sp.baseStats.spe, 31, evs.spe ?? 0, 50, nature);
102
+ if (side.item === 'Choice Scarf')
103
+ s = Math.floor(s * 1.5);
104
+ return s;
105
+ };
106
+ const ourSpeed = speed(us, usSp);
107
+ const theirSpeed = speed(them, themSp);
108
+ const weMoveFirst = ourSpeed >= theirSpeed;
109
+ const empty = {
110
+ ourBest: null,
111
+ theirBest: null,
112
+ speed: { ours: ourSpeed, theirs: theirSpeed, weMoveFirst },
113
+ };
114
+ if (!ourMove || !theirMove) {
115
+ const weBlocked = !ourMove && !!us.moves?.length && !canHit(dex, us, themSp.types);
116
+ const theyBlocked = !theirMove && !!them.moves?.length && !canHit(dex, them, usSp.types);
117
+ if (weBlocked || theyBlocked) {
118
+ return {
119
+ ...empty,
120
+ answerClass: 'UNFAVORABLE',
121
+ reason: weBlocked
122
+ ? `every damaging move ${us.species} knows is resisted or immune into ${them.species}\u2019s typing.`
123
+ : `every damaging move ${them.species} knows is resisted or immune into ${us.species}\u2019s typing.`,
124
+ };
125
+ }
126
+ return {
127
+ ...empty,
128
+ answerClass: 'UNKNOWN',
129
+ reason: !ourMove ? `no damaging move known for ${us.species}` : `no damaging move known for ${them.species}`,
130
+ };
131
+ }
132
+ const ourRes = damageResult(9, { species: us.species, level: 50, nature: us.nature, evs: us.evs, championsPoints: us.championsPoints, item: us.item, ability: us.ability, moves: [ourMove.name] }, { species: them.species, level: 50, nature: them.nature, evs: them.evs, championsPoints: them.championsPoints, item: them.item, ability: them.ability, moves: them.moves }, ourMove.name);
133
+ const theirRes = damageResult(9, { species: them.species, level: 50, nature: them.nature, evs: them.evs, championsPoints: them.championsPoints, item: them.item, ability: them.ability, moves: [theirMove.name] }, { species: us.species, level: 50, nature: us.nature, evs: us.evs, championsPoints: us.championsPoints, item: us.item, ability: us.ability, moves: us.moves }, theirMove.name);
134
+ const ourHp = ourRes.attacker.stats.hp;
135
+ const theirHp = theirRes.attacker.stats.hp;
136
+ const ourRange = ourRes.damageRange;
137
+ const theirRange = theirRes.damageRange;
138
+ const base = {
139
+ ourBest: { move: ourMove.name, damageRange: ourRange, koChance: ourRes.koChance },
140
+ theirBest: { move: theirMove.name, damageRange: theirRange, koChance: theirRes.koChance },
141
+ speed: { ours: ourSpeed, theirs: theirSpeed, weMoveFirst },
142
+ };
143
+ const ourReliableKO = ourRange[0] >= theirHp;
144
+ const theirReliableKO = theirRange[0] >= ourHp;
145
+ const theirPossibleKO = theirRange[1] >= ourHp;
146
+ if (ourReliableKO && theirReliableKO) {
147
+ return {
148
+ ...base,
149
+ answerClass: weMoveFirst ? 'REVENGE' : 'UNFAVORABLE',
150
+ reason: `both sides one-shot the other; ${weMoveFirst ? `we move first (${ourSpeed} vs ${theirSpeed}), so we win on initiative only and cannot switch in` : `they move first (${theirSpeed} vs ${ourSpeed})`}.`,
151
+ };
152
+ }
153
+ if (ourReliableKO) {
154
+ if (theirPossibleKO && !weMoveFirst) {
155
+ return {
156
+ ...base,
157
+ answerClass: 'TRADE',
158
+ reason: `we one-shot reliably but move second; their best roll (${theirRange[1]} of ${ourHp}) can one-shot us, so the result rides on their roll.`,
159
+ };
160
+ }
161
+ return {
162
+ ...base,
163
+ answerClass: 'HARD_ANSWER',
164
+ reason: `we one-shot (${ourRange[0]}-${ourRange[1]} of ${theirHp})${theirPossibleKO ? ' and move first' : ' and survive their best hit'}.`,
165
+ };
166
+ }
167
+ if (theirReliableKO) {
168
+ return {
169
+ ...base,
170
+ answerClass: 'UNFAVORABLE',
171
+ reason: `they one-shot us (${theirRange[0]}-${theirRange[1]} of ${ourHp}) and our best hit does not one-shot back.`,
172
+ };
173
+ }
174
+ // Neither side one-shots: the two-hit race, simulated conservatively, and
175
+ // re-simulated with the order flipped to see whether speed decides it.
176
+ const worstCase = simulateRace(ourMove.name, theirMove.name, ourRange, theirRange, ourHp, theirHp, ourMove.priority, theirMove.priority, weMoveFirst);
177
+ const flipped = simulateRace(ourMove.name, theirMove.name, ourRange, theirRange, ourHp, theirHp, ourMove.priority, theirMove.priority, !weMoveFirst);
178
+ if (worstCase.weWin && flipped.weWin) {
179
+ return {
180
+ ...base,
181
+ answerClass: worstCase.hpLeftPct >= 50 ? 'HARD_ANSWER' : 'SOFT_ANSWER',
182
+ reason: worstCase.hpLeftPct >= 50
183
+ ? `we win the exchange regardless of turn order, finishing above half HP (${worstCase.hpLeftPct.toFixed(0)}%).`
184
+ : `we win the exchange regardless of turn order, but finish at ${worstCase.hpLeftPct.toFixed(0)}% HP against their best rolls.`,
185
+ };
186
+ }
187
+ if (worstCase.weWin !== flipped.weWin) {
188
+ return {
189
+ ...base,
190
+ answerClass: 'SPEED_DEPENDENT',
191
+ reason: `the winner flips with turn order: both sides need at least two hits, and ${weMoveFirst ? `we move first (${ourSpeed} vs ${theirSpeed})` : `they move first (${theirSpeed} vs ${ourSpeed})`}.`,
192
+ };
193
+ }
194
+ return {
195
+ ...base,
196
+ answerClass: 'UNFAVORABLE',
197
+ reason: `they win the exchange on both orderings; our best is ${ourRange[0]}-${ourRange[1]} of ${theirHp}, theirs ${theirRange[0]}-${theirRange[1]} of ${ourHp}.`,
198
+ };
199
+ }
200
+ //# sourceMappingURL=evaluator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluator.js","sourceRoot":"","sources":["../src/evaluator.ts"],"names":[],"mappings":"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"}
package/dist/prompts.js CHANGED
@@ -15,7 +15,7 @@ const teamArg = z
15
15
  .string()
16
16
  .optional()
17
17
  .describe('The team as paste text (Showdown format, or plain lines); omitted, the workflow asks for it.');
18
- const goalArg = z.string().optional().describe('What the player wants out of the change, carried into diagnose_team.');
18
+ const goalArg = z.string().optional().describe('What the player wants out of the change, carried into analyze_team mode "diagnose".');
19
19
  export function registerPrompts(server) {
20
20
  server.registerPrompt('team-doctor', {
21
21
  title: 'Team Doctor',
@@ -32,10 +32,10 @@ export function registerPrompts(server) {
32
32
  ${args.team ? `Their team (paste text):\n\n${args.team}\n` : 'Ask the user to paste their team first.'}
33
33
 
34
34
  Then, in order:
35
- 1. Run parse_team on the paste (with the regulation) and surface every warning it returns — a name that did not resolve is a fixable typo, not a dead end.
36
- 2. Run diagnose_team on the parsed team, passing the goal and any members the user wants kept.
37
- 3. Walk through the problems with their evidence, then the candidate changes with their math. Say plainly which changes are exact (spread, move) and which are typing-only heuristics (member swaps) — diagnose_team labels the confidence, repeat it.
38
- 4. If you propose a changed team, run format_team on it so the user can copy the paste straight into the game.
35
+ 1. Run team_io with mode "parse" on the paste (with the regulation) and surface every warning it returns — a name that did not resolve is a fixable typo, not a dead end.
36
+ 2. Run analyze_team with mode "synergy" with mode "diagnose" on the parsed team, passing the goal and any members the user wants kept.
37
+ 3. Walk through the problems with their evidence, then the candidate changes with their math. Say plainly which changes are exact (spread, move) and which are typing-only heuristics (member swaps) — analyze_team with mode "synergy" with mode "diagnose" labels the confidence, repeat it.
38
+ 4. If you propose a changed team, run team_io with mode "format" on it so the user can copy the paste straight into the game.
39
39
 
40
40
  Every claim must come from the tools\u2019 output. You explain; getcompetitive proves.`,
41
41
  },
@@ -64,7 +64,7 @@ ${args.team ? `Their team:\n\n${args.team}\n` : 'Ask the user for their team fir
64
64
  ${args.opponent ? `What they know about the opponent:\n\n${args.opponent}\n` : 'Ask the user what they know about the opponent (species at minimum).'}
65
65
 
66
66
  Then, in order:
67
- 1. parse_team on any paste text. If the opponent is just species names, that is fine — pass them to prepare_matchup as the opponent array; it estimates what it cannot know and says so.
67
+ 1. team_io with mode "parse" on any paste text. If the opponent is just species names, that is fine — pass them to prepare_matchup as the opponent array; it estimates what it cannot know and says so.
68
68
  2. Run prepare_matchup with the team, the opponent, and ${args.regulation ?? 'the current regulation'}.
69
69
  3. Deliver the dossier in plain language: their likely sets ordered by usage, the speed races that matter, the key damage rolls (quote the description lines), the recommended bring-four and why, leads to open with and watch for, win/loss conditions, and which of the user\u2019s members to preserve.
70
70
  4. End with the matchupConfidence read and what it can and cannot see.
@@ -93,10 +93,10 @@ Every number comes from prepare_matchup\u2019s output — you explain; getcompet
93
93
  text: `Act as a Pokémon Champions teambuilder. Build a team around ${args.core ?? 'the species the user names (ask them first)'} for ${args.regulation ?? 'the current regulation set'}.
94
94
 
95
95
  Workflow:
96
- 1. get_set on the core species (batched if several) so the core is built from what the meta actually plays.
97
- 2. Run analyze_team on the partial team to see the defensive weaknesses and the types nothing hits super-effectively — those are the gaps the remaining slots must fill.
98
- 3. Run diagnose_team on the partial team and mine its candidate changes (member swaps and move changes) for gap-filling partners; prefer partners the meta has usage data for (list_threats), and say when a pick is typing-only.
99
- 4. Assemble six members, run check_legality against the regulation, then format_team the finished squad so the user can paste it.
96
+ 1. analyze_meta with mode "set" on the core species (batched if several) so the core is built from what the meta actually plays.
97
+ 2. Run analyze_team with mode "synergy" on the partial team to see the defensive weaknesses and the types nothing hits super-effectively — those are the gaps the remaining slots must fill.
98
+ 3. Run analyze_team with mode "diagnose" on the partial team and mine its candidate changes (member swaps and move changes) for gap-filling partners; prefer partners the meta has usage data for (analyze_meta with mode "threats"), and say when a pick is typing-only.
99
+ 4. Assemble six members, run team_io with mode "legality" against the regulation, then team_io with mode "format" the finished squad so the user can paste it.
100
100
 
101
101
  Explain each pick with its evidence (coverage, speed, or usage). You design; getcompetitive proves the picks.`,
102
102
  },
@@ -116,11 +116,11 @@ Explain each pick with its evidence (coverage, speed, or usage). You design; get
116
116
  text: `Act as a Pokémon Champions tournament coach. Prepare the user for an event under ${args.regulation ?? 'the current regulation set'}.
117
117
 
118
118
  Workflow:
119
- 1. get_regulation for the rules that shape the format: clauses, timers, bring 4 of 6, Mega rules. (Skip includeRoster unless a full legal list is actually wanted.)
120
- 2. list_threats for the regulation and get_set (batched) on the top threats so their real sets are in front of you.
119
+ 1. team_io with mode "regulation" for the rules that shape the format: clauses, timers, bring 4 of 6, Mega rules. (Skip includeRoster unless a full legal list is actually wanted.)
120
+ 2. analyze_meta with mode "threats" for the regulation and analyze_meta with mode "set" (batched) on the top threats so their real sets are in front of you.
121
121
  3. Produce a tournament-prep briefing: the meta snapshot (top threats with usage, tier bands), what each top threat does, and a preparation checklist — answers for the top threats, a speed plan, and a bring-four habit.
122
122
 
123
- Numbers only from the tools; the sample size and sourceAsOf from list_threats are part of the briefing, not an afterthought.`,
123
+ Numbers only from the tools; the sample size and sourceAsOf from analyze_meta with mode "threats" are part of the briefing, not an afterthought.`,
124
124
  },
125
125
  },
126
126
  ],
@@ -139,10 +139,10 @@ Numbers only from the tools; the sample size and sourceAsOf from list_threats ar
139
139
  ${args.team ? `Their team:\n\n${args.team}\n` : 'Ask the user to paste their team first.'}
140
140
 
141
141
  Workflow:
142
- 1. parse_team on the paste.
143
- 2. analyze_team for the synergy read: stacked weaknesses, coverage gaps, speed placement, and the score.
144
- 3. diagnose_team for what it would change, so the guide includes the holes.
145
- 4. For each member: get_set if the species is ranked (batched across the team), else get_pokemon plus get_learnset highlights.
142
+ 1. team_io with mode "parse" on the paste.
143
+ 2. analyze_team with mode "synergy" for the synergy read: stacked weaknesses, coverage gaps, speed placement, and the score.
144
+ 3. analyze_team with mode "synergy" with mode "diagnose" for what it would change, so the guide includes the holes.
145
+ 4. For each member: analyze_meta with mode "set" if the species is ranked (batched across the team), else lookup with mode "species" plus lookup with mode "learnset" highlights.
146
146
 
147
147
  Deliver a team guide: each member\u2019s role, the pairs and synergies between them, the holes a future change should fill, and a one-paragraph game plan. Every factual claim cites a tool\u2019s output.`,
148
148
  },
@@ -162,8 +162,8 @@ Deliver a team guide: each member\u2019s role, the pairs and synergies between t
162
162
  text: `Act as a Pokémon Champions metagame reporter. Produce the meta report for ${args.regulation ?? 'the current regulation set'}.
163
163
 
164
164
  Workflow:
165
- 1. list_threats for the usage ranking, tiers and the sample behind it.
166
- 2. get_set (batched) on the top five threats for their real sets.
165
+ 1. analyze_meta with mode "threats" for the usage ranking, tiers and the sample behind it.
166
+ 2. analyze_meta with mode "set" (batched) on the top five threats for their real sets.
167
167
  3. Write the report: the tier bands with usage shares, each top threat\u2019s standard set and what it does, and a closing read of what the meta rewards. State the list\u2019s sourceAsOf and sample size so the reader knows how current the numbers are.
168
168
 
169
169
  The report is data journalism: every figure quoted comes from the tools, and the data date travels with it.`,
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"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"}
package/dist/server.js CHANGED
@@ -5,37 +5,15 @@
5
5
  */
6
6
  import { readFileSync } from 'node:fs';
7
7
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
8
- import { registerDataTools } from './tools/data.js';
9
- import { registerCalcTools } from './tools/calc.js';
10
- import { registerRegulationTools } from './tools/regulations.js';
11
- import { registerAnalyzeTools } from './tools/analyze.js';
12
- import { registerMetaTools } from './tools/meta.js';
13
- import { registerTeamTools } from './tools/team.js';
14
- import { registerDoctorTool } from './tools/doctor.js';
15
- import { registerMatchupTool } from './tools/matchup.js';
16
- import { registerSpritesTool } from './tools/sprites.js';
17
- import { registerReplayTool } from './tools/replay.js';
18
- import { registerInferTool } from './tools/infer.js';
19
- import { registerOptimizeTeamTool } from './tools/optimize.js';
20
8
  import { registerPrompts } from './prompts.js';
9
+ import { registerCompoundTools } from './tools/compound.js';
21
10
  export const VERSION = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
22
11
  export function buildServer() {
23
12
  const server = new McpServer({
24
13
  name: 'getcompetitive',
25
14
  version: VERSION,
26
15
  });
27
- registerDataTools(server);
28
- registerCalcTools(server);
29
- registerRegulationTools(server);
30
- registerAnalyzeTools(server);
31
- registerMetaTools(server);
32
- registerTeamTools(server);
33
- registerDoctorTool(server);
34
- registerMatchupTool(server);
35
- registerSpritesTool(server);
36
- registerReplayTool(server);
37
- registerInferTool(server);
38
- registerOptimizeTeamTool(server);
16
+ registerCompoundTools(server);
39
17
  registerPrompts(server);
40
18
  return server;
41
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,CAAC,MAAM,OAAO,GAClB,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAC7E,CAAC,OAAO,CAAC;AAEV,MAAM,UAAU,WAAW;IACzB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC1B,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC1B,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAChC,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC1B,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC1B,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC3B,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC5B,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC5B,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC3B,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC1B,wBAAwB,CAAC,MAAM,CAAC,CAAC;IACjC,eAAe,CAAC,MAAM,CAAC,CAAC;IAExB,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,CAAC,MAAM,OAAO,GAClB,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAC7E,CAAC,OAAO,CAAC;AAEV,MAAM,UAAU,WAAW;IACzB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAC9B,eAAe,CAAC,MAAM,CAAC,CAAC;IAExB,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -9,6 +9,7 @@ import { getChampionsDex } from '../champions.js';
9
9
  import { getRegulationSet } from '../regulations.js';
10
10
  import { getThreatList } from '../threats.js';
11
11
  import { ok, wrap, requireExists, READ_ONLY_ANNOTATIONS } from '../result.js';
12
+ import { evaluateMatchup, ANSWER_CLASS_RANK } from '../evaluator.js';
12
13
  import { evMap, championsPointsMap } from './schemas.js';
13
14
  /** One member's score against a known opponent; shared by `picks` and `leftBehind`. */
14
15
  const bringCandidate = z.object({
@@ -192,6 +193,11 @@ export function registerAnalyzeTools(server) {
192
193
  hitBy: z.string().optional().describe('The member providing that best hit; absent when nothing hits it super-effectively.'),
193
194
  hitVia: z.string().optional().describe('The attacking type providing it; absent when nothing hits it super-effectively.'),
194
195
  answered: z.boolean().describe('True when the team has a super-effective (\u22652x) hit on it.'),
196
+ answerClass: z
197
+ .enum(['HARD_ANSWER', 'SOFT_ANSWER', 'REVENGE', 'SPEED_DEPENDENT', 'TRADE', 'UNFAVORABLE', 'UNKNOWN'])
198
+ .optional()
199
+ .describe('The shared MatchupEvaluator\u2019s verdict from real damage ranges and Speed, when at least one member supplied moves: HARD_ANSWER and SOFT_ANSWER win the exchange, REVENGE wins only on initiative, SPEED_DEPENDENT and TRADE ride on order or rolls, UNFAVORABLE loses it. Absent when no member listed moves \u2014 the type-based fields above are all there is.'),
200
+ answerBy: z.string().optional().describe('The member that earns the best `answerClass`; present with it.'),
195
201
  })).describe('One entry per threat in the regulation\u2019s list, most used first.'),
196
202
  unanswered: z.array(z.string()).describe('Threats with no super-effective hit from this team \u2014 the coverage holes to fix first.'),
197
203
  unansweredCount: z.number().int().describe('How many threats are unanswered.'),
@@ -338,6 +344,32 @@ export function registerAnalyzeTools(server) {
338
344
  }
339
345
  }
340
346
  const multiplier = best?.multiplier ?? 0;
347
+ // Where a member supplied real moves, run the shared MatchupEvaluator:
348
+ // damage ranges and Speed decide what an answer actually is, instead
349
+ // of the type multiplier alone.
350
+ let answerClass;
351
+ let answerBy;
352
+ for (const entry of args.team.filter((e) => e.moves?.length)) {
353
+ const result = evaluateMatchup({
354
+ species: entry.species,
355
+ ...(entry.item ? { item: entry.item } : {}),
356
+ ...(entry.nature ? { nature: entry.nature } : {}),
357
+ ...(entry.evs ? { evs: entry.evs } : {}),
358
+ ...(entry.championsPoints ? { championsPoints: entry.championsPoints } : {}),
359
+ moves: entry.moves,
360
+ }, {
361
+ species: form,
362
+ item: threat.item,
363
+ ability: threat.ability,
364
+ nature: threat.nature,
365
+ ...(threat.evs ? { evs: threat.evs } : {}),
366
+ moves: threat.moves,
367
+ });
368
+ if (!answerClass || ANSWER_CLASS_RANK[result.answerClass] < ANSWER_CLASS_RANK[answerClass]) {
369
+ answerClass = result.answerClass;
370
+ answerBy = entry.species;
371
+ }
372
+ }
341
373
  return {
342
374
  species: form,
343
375
  usage: threat.usage,
@@ -346,6 +378,7 @@ export function registerAnalyzeTools(server) {
346
378
  hitMultiplier: multiplier,
347
379
  ...(multiplier > 1 && best ? { hitBy: best.member, hitVia: best.via } : {}),
348
380
  answered: multiplier >= 2,
381
+ ...(answerClass ? { answerClass, answerBy } : {}),
349
382
  };
350
383
  });
351
384
  const unanswered = threatCoverage?.filter((t) => !t.answered).map((t) => t.species) ?? [];