Skip to main content
Glama
grovesjosephn

Pokemon MCP Server

api-client.ts4.03 kB
import { IngestionConfig } from './config.js'; import { RateLimiter } from './rate-limiter.js'; export interface PokemonListResponse { count: number; next: string | null; previous: string | null; results: Array<{ name: string; url: string; }>; } export interface TypeResponse { count: number; next: string | null; previous: string | null; results: Array<{ name: string; url: string; }>; } export interface SpeciesResponse { generation: { name: string; url: string; }; } export interface Pokemon { id: number; name: string; height: number; weight: number; base_experience: number; generation: number; species?: { name: string; url: string }; sprites: { front_default: string; back_default: string; front_shiny: string; back_shiny: string; }; stats: Array<{ stat: { name: string; url: string }; base_stat: number; effort: number; }>; types: Array<{ slot: number; type: { name: string; url: string }; }>; abilities: Array<{ ability: { name: string; url: string }; is_hidden: boolean; slot: number; }>; } export class PokeApiClient { private baseUrl = 'https://pokeapi.co/api/v2'; private config: IngestionConfig; private rateLimiter: RateLimiter; constructor(config: IngestionConfig) { this.config = config; this.rateLimiter = new RateLimiter(60); // 60 requests per minute max } private async delay(ms: number): Promise<void> { return new Promise((resolve) => setTimeout(resolve, ms)); } async fetchWithRetry(url: string, retries?: number): Promise<any> { if (!url) { throw new Error('URL is required'); } const maxRetries = retries ?? this.config.maxRetries; // Apply rate limiting await this.rateLimiter.waitForSlot(); // Add request delay if configured if (this.config.requestDelayMs > 0) { await this.delay(this.config.requestDelayMs); } for (let i = 0; i < maxRetries; i++) { try { const controller = new AbortController(); const timeoutId = setTimeout( () => controller.abort(), this.config.timeoutMs ); const response = await fetch(url, { signal: controller.signal, headers: { 'User-Agent': 'Pokemon-MCP-Server/1.0.0', }, }); clearTimeout(timeoutId); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); // Basic content validation if (!data || typeof data !== 'object') { throw new Error('Invalid response: not a JSON object'); } return data; } catch (error) { const errorMsg = error instanceof Error ? error.message : String(error); console.log(`⚠️ Retry ${i + 1}/${maxRetries} for ${url}: ${errorMsg}`); if (i === maxRetries - 1) { throw new Error(`Failed after ${maxRetries} retries: ${errorMsg}`); } // Exponential backoff with jitter const backoffMs = Math.min(1000 * Math.pow(2, i), 10000); const jitter = Math.random() * 1000; await this.delay(backoffMs + jitter); } } } async fetchPokemonList(limit: number): Promise<PokemonListResponse> { const url = `${this.baseUrl}/pokemon?limit=${limit}`; return (await this.fetchWithRetry(url)) as PokemonListResponse; } async fetchTypes(): Promise<TypeResponse> { const url = `${this.baseUrl}/type`; return (await this.fetchWithRetry(url)) as TypeResponse; } async fetchPokemonDetails(pokemonId: number): Promise<Pokemon> { const url = `${this.baseUrl}/pokemon/${pokemonId}`; return (await this.fetchWithRetry(url)) as Pokemon; } async fetchSpeciesDetails(speciesUrl: string): Promise<SpeciesResponse> { return (await this.fetchWithRetry(speciesUrl)) as SpeciesResponse; } getRateLimiterStats() { return this.rateLimiter.getStats(); } }

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/grovesjosephn/pokemcp'

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