Skip to main content
Glama

LoreKeeper MCP

by frap129
spell_lookup.py5.31 kB
"""Spell lookup tool.""" from typing import Any from lorekeeper_mcp.api_clients.open5e_v2 import Open5eV2Client # Simple in-memory cache for spell lookups (max 128 entries) _spell_cache: dict[tuple[Any, ...], list[dict[str, Any]]] = {} _spell_cache_maxsize = 128 def clear_spell_cache() -> None: """Clear the in-memory spell cache.""" _spell_cache.clear() async def lookup_spell( name: str | None = None, level: int | None = None, school: str | None = None, class_key: str | None = None, concentration: bool | None = None, ritual: bool | None = None, casting_time: str | None = None, limit: int = 20, ) -> list[dict[str, Any]]: """ Search and retrieve D&D 5e spells from the Open5e v2 API. This tool provides comprehensive spell lookup functionality with support for filtering by multiple criteria. Results include complete spell descriptions, components, damage, effects, and availability information. Automatically caches responses for improved performance. Examples: - lookup_spell(name="fireball") - Find spells by name - lookup_spell(level=3, school="evocation") - Find all 3rd level evocation spells - lookup_spell(class_key="wizard", concentration=True, limit=10) - Find concentration spells for wizards - lookup_spell(level=0) - Find all cantrips - lookup_spell(ritual=True) - Find all ritual spells Args: name: Spell name or partial name search. Matches spells containing this substring. Example: "fireball", "magic", "shield" level: Spell level ranging from 0-9, where 0 represents cantrips/0-level spells and 9 represents 9th level spells. Example: 3 for 3rd level spells school: Magic school filter. Valid values: abjuration, conjuration, divination, enchantment, evocation, illusion, necromancy, transmutation Example: "evocation" for damage-dealing spells class_key: Filter spells available to a specific class. Valid values: wizard, cleric, druid, bard, paladin, ranger, sorcerer, warlock, artificer Example: "wizard" for spells in the wizard spell list concentration: Filter for spells requiring concentration. True returns only concentration spells, False returns only non-concentration spells. Example: True ritual: Filter for ritual spells. True returns spells that can be cast as rituals. Example: True casting_time: Casting time filter for spells. Examples: "1 action", "1 bonus action", "1 reaction", "1 minute", "10 minutes" limit: Maximum number of results to return. Default 20, useful for pagination or limiting large result sets. Example: 5 Returns: List of spell dictionaries, each containing: - name: Spell name - level: Spell level (0-9) - school: Magic school - casting_time: How long the spell takes to cast - range: Spell range/area of effect - components: Required components (V/S/M) - material: Material component description (if applicable) - duration: How long the spell lasts - concentration: Whether spell requires concentration - ritual: Whether spell can be cast as a ritual - desc: Full spell description and effects - higher_level: Effect when cast at higher levels - classes: List of classes that can learn this spell - document__slug: Source document reference Raises: ApiError: If the API request fails due to network issues or server errors """ # Check in-memory cache first cache_key = (name, level, school, class_key, concentration, ritual, casting_time, limit) if cache_key in _spell_cache: return _spell_cache[cache_key] client = Open5eV2Client() # Build query parameters # Note: name/search filtering happens client-side since the API doesn't filter by search # When searching by name, fetch more results to ensure we find matches # Use multiplier of 11 to balance finding matches with performance (~2.5s for 220 results) params: dict[str, Any] = {"limit": limit * 11 if name else limit} if level is not None: params["level"] = level if school is not None: params["school"] = school if class_key is not None: params["class_key"] = class_key if concentration is not None: params["concentration"] = concentration if ritual is not None: params["ritual"] = ritual if casting_time is not None: params["casting_time"] = casting_time spells = await client.get_spells(**params) # Client-side filtering by name (the API search parameter doesn't actually filter) if name: name_lower = name.lower() spells = [spell for spell in spells if name_lower in spell.name.lower()] # Limit results to requested count spells = spells[:limit] result = [spell.model_dump() for spell in spells] # Cache the result in memory if len(_spell_cache) >= _spell_cache_maxsize: # Simple FIFO eviction _spell_cache.pop(next(iter(_spell_cache))) _spell_cache[cache_key] = result return result

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/frap129/lorekeeper-mcp'

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