import httpx
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("poke")
POKEAPI_BASE = "https://pokeapi.co/api/v2"
# --- Helper to fetch Pokémon data ---
async def fetch_pokemon_data(name: str) -> dict:
async with httpx.AsyncClient() as client:
try:
response = await client.get(f"{POKEAPI_BASE}/pokemon/{name.lower()}")
if response.status_code == 200:
return response.json()
except httpx.HTTPError:
pass
return {}
# --- Tool: Get info about a Pokémon ---
@mcp.tool()
async def get_pokemon_info(name: str) -> str:
"""Get detailed info about a Pokémon by name."""
data = await fetch_pokemon_data(name)
if not data:
return f"No data found for Pokémon: {name}"
stats = {stat['stat']['name']: stat['base_stat'] for stat in data['stats']}
types_ = [t['type']['name'] for t in data['types']]
abilities = [a['ability']['name'] for a in data['abilities']]
return f"""
Name: {data['name'].capitalize()}
Types: {', '.join(types_)}
Abilities: {', '.join(abilities)}
Stats: {', '.join(f"{k}: {v}" for k, v in stats.items())}
"""
# --- Tool: Create a tournament squad ---
@mcp.tool()
async def create_tournament_squad() -> str:
"""Create a powerful squad of Pokémon for a tournament."""
top_pokemon = ["charizard", "garchomp", "lucario", "dragonite", "metagross", "gardevoir"]
squad = []
for name in top_pokemon:
data = await fetch_pokemon_data(name)
if data:
squad.append(data["name"].capitalize())
return "Tournament Squad:\n" + "\n".join(squad)
# --- Tool: List popular Pokémon ---
@mcp.tool()
async def list_popular_pokemon() -> str:
"""List popular tournament-ready Pokémon."""
return "\n".join([
"Charizard", "Garchomp", "Lucario",
"Dragonite", "Metagross", "Gardevoir"
])
# --- Tool: Find Pokémon by type ---
@mcp.tool()
async def find_pokemon_by_type(type_name: str) -> str:
"""Find Pokémon of a specific type (e.g. fire, water, psychic). Returns top 5 with stats."""
async with httpx.AsyncClient() as client:
try:
response = await client.get(f"{POKEAPI_BASE}/type/{type_name.lower()}")
if response.status_code != 200:
return f"Unknown type: {type_name}"
type_data = response.json()
except httpx.HTTPError:
return "Failed to fetch type data."
# Get first 5 pokemon of that type
pokemon_list = type_data["pokemon"][:5]
results = []
for entry in pokemon_list:
name = entry["pokemon"]["name"]
data = await fetch_pokemon_data(name)
if data:
stats = {s['stat']['name']: s['base_stat'] for s in data['stats']}
total = sum(stats.values())
results.append(f"{data['name'].capitalize()} — Total stats: {total} | HP: {stats.get('hp')} | Atk: {stats.get('attack')} | Spd: {stats.get('speed')}")
return f"Top {type_name.capitalize()} type Pokémon:\n" + "\n".join(results)
# --- Tool: Battle Simulator ---
@mcp.tool()
async def battle_simulator(pokemon1: str, pokemon2: str) -> str:
"""Simulate a 1v1 battle between two Pokémon using stats. Considers speed, attack, defense, and HP."""
data1 = await fetch_pokemon_data(pokemon1)
data2 = await fetch_pokemon_data(pokemon2)
if not data1:
return f"Could not find Pokémon: {pokemon1}"
if not data2:
return f"Could not find Pokémon: {pokemon2}"
def get_stats(data):
return {s['stat']['name']: s['base_stat'] for s in data['stats']}
s1 = get_stats(data1)
s2 = get_stats(data2)
name1 = data1['name'].capitalize()
name2 = data2['name'].capitalize()
# Who attacks first (based on speed)
if s1['speed'] >= s2['speed']:
first_name, second_name = name1, name2
first_stats, second_stats = s1, s2
else:
first_name, second_name = name2, name1
first_stats, second_stats = s2, s1
# Damage estimation: attacker's attack vs defender's defense
# Higher attack relative to opponent's defense = more damage
hit1 = (s1['attack'] + s1['special-attack']) / 2
defense2 = (s2['defense'] + s2['special-defense']) / 2
hit2 = (s2['attack'] + s2['special-attack']) / 2
defense1 = (s1['defense'] + s1['special-defense']) / 2
# Score = how many hits to knock out opponent (lower = better)
hits_to_ko_2 = s2['hp'] / max((hit1 - defense2 * 0.5), 1)
hits_to_ko_1 = s1['hp'] / max((hit2 - defense1 * 0.5), 1)
# Speed tiebreaker: faster pokemon gets a 10% advantage
score1 = hits_to_ko_2 * (0.9 if s1['speed'] >= s2['speed'] else 1.0)
score2 = hits_to_ko_1 * (0.9 if s2['speed'] > s1['speed'] else 1.0)
winner = name1 if score1 <= score2 else name2
loser = name2 if winner == name1 else name1
return f"""
⚔️ Battle: {name1} vs {name2}
{name1} Stats — HP: {s1['hp']} | Atk: {s1['attack']} | Def: {s1['defense']} | Sp.Atk: {s1['special-attack']} | Sp.Def: {s1['special-defense']} | Spd: {s1['speed']}
{name2} Stats — HP: {s2['hp']} | Atk: {s2['attack']} | Def: {s2['defense']} | Sp.Atk: {s2['special-attack']} | Sp.Def: {s2['special-defense']} | Spd: {s2['speed']}
⚡ {first_name} goes first (higher speed)!
🏆 Winner: {winner} defeats {loser}!
Reasoning: {winner} has a better balance of offensive power relative to {loser}'s defenses{"and strike advantage from higher speed" if (winner == name1 and s1['speed'] >= s2['speed']) or (winner == name2 and s2['speed'] > s1['speed']) else ""}.
"""
# Pokémon → real animal mapping
POKEMON_ANIMALS = {
"pikachu": ("mouse", "Mouse"),
"charizard": ("dragon", "Komodo dragon"),
"bulbasaur": ("frog", "Frog"),
"squirtle": ("turtle", "Sea turtle"),
"caterpie": ("caterpillar", "Caterpillar"),
"pidgey": ("pigeon", "Pigeon"),
"ekans": ("king cobra", "King cobra"),
"arcanine": ("dog", "Dog"),
"ponyta": ("horse", "Horse"),
"slowpoke": ("salamander", "Axolotl"),
"dewgong": ("dugong", "Dugong"),
"gengar": ("cat", "Cat"),
"kangaskhan": ("kangaroo", "Kangaroo"),
"scyther": ("mantis", "Praying mantis"),
"magikarp": ("carp", "Carp"),
"gyarados": ("oarfish", "Oarfish"),
"eevee": ("fox", "Fox"),
"snorlax": ("bear", "Bear"),
"dragonite": ("dragon", "Chinese dragon"),
"mewtwo": ("cat", "Cat"),
"hoothoot": ("owl", "Owl"),
"mareep": ("sheep", "Sheep"),
"sudowoodo": ("cactus", "Cactus"),
"aipom": ("monkey", "Squirrel monkey"),
"wooper": ("axolotl", "Axolotl"),
"heracross": ("hercules beetle", "Hercules beetle"),
"ursaring": ("brown bear", "Brown bear"),
"remoraid": ("remora", "Remora"),
"mantine": ("manta ray", "Manta ray"),
"larvitar": ("lizard", "Lizard"),
"zigzagoon": ("raccoon", "Raccoon"),
"taillow": ("swallow", "Barn swallow"),
"shroomish": ("mushroom", "Mushroom"),
"slakoth": ("sloth", "Three-toed sloth"),
"nincada": ("cicada", "Cicada"),
"trapinch": ("antlion", "Antlion"),
"zangoose": ("mongoose", "Mongoose"),
"seviper": ("viper", "Viper"),
"absol": ("wolf", "Wolf"),
"garchomp": ("hammerhead shark", "Hammerhead shark"),
"lucario": ("anubis jackal", "Jackal"),
"hippopotas": ("hippopotamus", "Hippopotamus"),
"skorupi": ("scorpion", "Scorpion"),
"finneon": ("neon tetra", "Neon tetra"),
"leafeon": ("fox", "Fox"),
"glaceon": ("fox", "Fox"),
"emolga": ("flying squirrel", "Japanese dwarf flying squirrel"),
"axew": ("dinosaur", "Ankylosaur"),
"beartic": ("polar bear", "Polar bear"),
"braviary": ("bald eagle", "Bald eagle"),
"chespin": ("hedgehog", "Hedgehog"),
"pancham": ("giant panda", "Giant panda"),
"gogoat": ("goat", "Mountain goat"),
"skiddo": ("goat", "Goat"),
"hawlucha": ("hawk", "Hawk"),
"inkay": ("firefly squid", "Firefly squid"),
"malamar": ("squid", "Squid"),
"sylveon": ("fox", "Fox"),
}
@mcp.tool()
async def pokemon_real_animal(name: str) -> str:
"""Find which real animal a Pokémon resembles and get fun facts about that animal from Wikipedia."""
name_lower = name.lower()
if name_lower not in POKEMON_ANIMALS:
return f"Sorry, I don't have an animal mapping for {name}. Try: {', '.join(list(POKEMON_ANIMALS.keys())[:10])}..."
wiki_search, display_name = POKEMON_ANIMALS[name_lower]
# Fetch Pokémon data
poke_data = await fetch_pokemon_data(name_lower)
if not poke_data:
return f"Could not fetch Pokémon data for {name}"
types_ = [t['type']['name'] for t in poke_data['types']]
stats = {s['stat']['name']: s['base_stat'] for s in poke_data['stats']}
# Fetch Wikipedia summary
async with httpx.AsyncClient() as client:
try:
response = await client.get(
f"https://en.wikipedia.org/api/rest_v1/page/summary/{wiki_search.replace(' ', '_')}",
headers={"User-Agent": "PokemonMCPServer/1.0"}
)
if response.status_code == 200:
wiki = response.json()
animal_summary = wiki.get("extract", "No summary available.")[:300] + "..."
else:
animal_summary = "Could not fetch Wikipedia data."
except httpx.HTTPError:
animal_summary = "Could not fetch Wikipedia data."
return f"""
🐾 {name.capitalize()} → Real Animal: {display_name}
📖 About the {display_name} (via Wikipedia):
{animal_summary}
⚡ Pokémon Stats Connection:
- Type: {', '.join(types_)}
- Speed: {stats.get('speed')} | Attack: {stats.get('attack')} | HP: {stats.get('hp')}
🔍 How they compare:
{name.capitalize()} is inspired by the {display_name}, sharing similar physical traits and characteristics that influenced its design and typing.
"""
# --- Entry point ---
if __name__ == "__main__":
mcp.run(transport="stdio")