Skip to main content
Glama
pokeapi.ts5.51 kB
import { normName, unique } from './utils.js'; type AnyObj = Record<string, any>; const API = 'https://pokeapi.co/api/v2'; class Cache<T=any> extends Map<string, Promise<T>> {} export class PokeAPI { private pokemonCache = new Cache<AnyObj>(); private speciesCache = new Cache<AnyObj>(); private moveCache = new Cache<AnyObj>(); private typeCache = new Cache<AnyObj>(); private listCache = new Cache<{results:{name:string,url:string}[]}>(); private async getJSON<T=any>(url: string): Promise<T> { const res = await fetch(url, { headers: { 'user-agent': 'pokemon-mcp/1.0' } }); if (!res.ok) throw new Error(`HTTP ${res.status} for ${url}`); return await res.json() as T; } async list(resource: 'pokemon'|'move'): Promise<{name:string,url:string}[]> { const key = `list:${resource}`; if (!this.listCache.has(key)) { this.listCache.set(key, this.getJSON(`${API}/${resource}?limit=20000`)); } const data = await this.listCache.get(key)!; return data.results; } async listNames(resource: 'pokemon'|'move'): Promise<string[]> { const list = await this.list(resource); return list.map(x => x.name); } async getPokemon(name: string): Promise<AnyObj> { const n = normName(name); if (!this.pokemonCache.has(n)) this.pokemonCache.set(n, this.getJSON(`${API}/pokemon/${n}`)); return this.pokemonCache.get(n)!; } async getSpecies(name: string): Promise<AnyObj> { const n = normName(name); if (!this.speciesCache.has(n)) this.speciesCache.set(n, this.getJSON(`${API}/pokemon-species/${n}`)); return this.speciesCache.get(n)!; } async getEvolutionChainBySpeciesName(name: string): Promise<AnyObj> { const sp = await this.getSpecies(name); const url = sp.evolution_chain.url as string; return this.getJSON(url); } async getMove(name: string): Promise<AnyObj> { const n = normName(name); if (!this.moveCache.has(n)) this.moveCache.set(n, this.getJSON(`${API}/move/${n}`)); return this.moveCache.get(n)!; } async getType(name: string): Promise<AnyObj> { const n = normName(name); if (!this.typeCache.has(n)) this.typeCache.set(n, this.getJSON(`${API}/type/${n}`)); return this.typeCache.get(n)!; } // Compute offensive multiplier using PokeAPI damage relations async typeMultiplier(attacking: string, defending: string[]): Promise<number> { const atk = await this.getType(attacking); const rel = atk.damage_relations; const dbl = rel.double_damage_to.map((t:AnyObj)=>t.name); const half = rel.half_damage_to.map((t:AnyObj)=>t.name); const zero = rel.no_damage_to.map((t:AnyObj)=>t.name); let mult = 1; for (const d of defending) { if (zero.includes(normName(d))) { mult *= 0; continue; } if (dbl.includes(normName(d))) mult *= 2; else if (half.includes(normName(d))) mult *= 0.5; } return mult; } // Learnset helpers methodAlias(m: string): string { const a = m.toLowerCase(); if (a.includes('level')) return 'level'; if (a.includes('machine')) return 'machine'; if (a.includes('tutor')) return 'tutor'; if (a.includes('egg')) return 'egg'; return a; } validVersionGroup(g?: string): string|undefined { if (!g) return undefined; const ok = ['red-blue','yellow','gold-silver','crystal','ruby-sapphire','firered-leafgreen','emerald']; const x = normName(g); return ok.includes(x) ? x : undefined; } async learnset(name: string, version_group?: string) { const p = await this.getPokemon(name); const vg = this.validVersionGroup(version_group); const rows = p.moves as AnyObj[]; const out: { move: string, methods: {version_group:string, method:string, level:number}[] }[] = []; for (const r of rows) { const methods = (r.version_group_details as AnyObj[]) .filter(d => !vg || d.version_group.name === vg) .map(d => ({ version_group: d.version_group.name, method: this.methodAlias(d.move_learn_method.name), level: d.level_learned_at })); if (methods.length) out.push({ move: r.move.name, methods }); } return out; } async pokemonCore(name: string) { const p = await this.getPokemon(name); return { name: p.name, types: (p.types as AnyObj[]).sort((a,b)=>a.slot-b.slot).map(t => t.type.name), base_stats: Object.fromEntries((p.stats as AnyObj[]).map(s => [s.stat.name.replace('special-attack','spa').replace('special-defense','spd').replace('speed','spe').replace('attack','atk').replace('defense','def').replace('hp','hp'), s.base_stat])), abilities: (p.abilities as AnyObj[]).map(a => a.ability.name) }; } parseEvolutionChain(chain: AnyObj) { const edges: {from:string,to:string,conditions:AnyObj}[] = []; function walk(node: AnyObj) { const from = node.species.name; for (const evo of node.evolves_to as AnyObj[]) { const to = evo.species.name; const conds = (evo.evolution_details?.[0]) || {}; edges.push({ from, to, conditions: conds }); walk(evo); } } walk(chain.chain); return edges; } async searchNames(query: string) { const q = normName(query); const poke = await this.listNames('pokemon'); const moves = await this.listNames('move'); const filt = (arr:string[]) => unique(arr.filter(n => n.includes(q))).slice(0, 50); return { pokemon: filt(poke), moves: filt(moves) }; } } export const pokeapi = new PokeAPI();

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/EscasanN/MCP_Pokemon'

If you have feedback or need assistance with the MCP directory API, please join our Discord server