Skip to main content
Glama
by frap129
search_equipment.py13.3 kB
"""Equipment search tool using the repository pattern for caching. This module provides comprehensive equipment search for weapons, armor, and magic items with automatic database caching through the repository pattern. The repository abstracts away cache management and allows filtering across multiple equipment types. Architecture: - Uses EquipmentRepository for cache-aside pattern with item-type routing - Repository manages Milvus cache automatically - Supports test context-based repository injection - Handles weapon, armor, and magic item filtering Examples: Default usage (automatically creates repository): weapons = await search_equipment(type="weapon", damage_dice="1d8") items = await search_equipment(type="magic-item", rarity="rare") With context-based injection (testing): from lorekeeper_mcp.tools.search_equipment import _repository_context from lorekeeper_mcp.repositories.equipment import EquipmentRepository repository = EquipmentRepository(cache=my_cache) _repository_context["repository"] = repository armor = await search_equipment(type="armor") Item type filtering: all_items = await search_equipment(type="all", name="chain") simple_weapons = await search_equipment(type="weapon", is_simple=True)""" from typing import Any, Literal, cast from lorekeeper_mcp.repositories.equipment import EquipmentRepository from lorekeeper_mcp.repositories.factory import RepositoryFactory _repository_context: dict[str, Any] = {} EquipmentType = Literal["weapon", "armor", "magic-item", "all"] def _get_repository() -> EquipmentRepository: """Get equipment repository, respecting test context. Returns the repository from _repository_context if set, otherwise creates a default EquipmentRepository using RepositoryFactory. Returns: EquipmentRepository instance for equipment lookups. """ if "repository" in _repository_context: return cast(EquipmentRepository, _repository_context["repository"]) return RepositoryFactory.create_equipment_repository() async def search_equipment( type: EquipmentType = "all", # noqa: A002 rarity: str | None = None, damage_dice: str | None = None, is_simple: bool | None = None, requires_attunement: str | None = None, cost_min: int | float | None = None, cost_max: int | float | None = None, weight_max: float | None = None, is_finesse: bool | None = None, is_light: bool | None = None, is_magic: bool | None = None, documents: list[str] | None = None, # Replaces document and document_keys search: str | None = None, limit: int = 20, ) -> list[dict[str, Any]]: """ Search and retrieve D&D 5e weapons, armor, and magic items using the repository pattern. This tool provides comprehensive equipment lookup across weapons, armor, and magical items. Filter by rarity, damage potential, complexity, or attunement requirements. Automatically uses the database cache through the repository for improved performance. Examples: Basic equipment lookup: rare_items = await search_equipment(type="magic-item", rarity="rare") light_armor = await search_equipment(type="armor", is_simple=True) Using cost ranges (NEW in Phase 3): affordable_weapons = await search_equipment( type="weapon", cost_max=25 ) expensive_items = await search_equipment( type="weapon", cost_min=50, cost_max=100 ) Using weight and properties (NEW in Phase 3): lightweight_weapons = await search_equipment( type="weapon", weight_max=3 ) finesse_weapons = await search_equipment( type="weapon", is_finesse=True ) light_dual_wield_weapons = await search_equipment( type="weapon", is_light=True ) magical_weapons = await search_equipment( type="weapon", is_magic=True ) Complex equipment queries: affordable_simple_weapons = await search_equipment( type="weapon", is_simple=True, cost_max=10 ) light_finesse_weapons = await search_equipment( type="weapon", is_light=True, is_finesse=True, limit=10 ) expensive_magical_weapons = await search_equipment( type="weapon", is_magic=True, cost_min=100 ) Searching all types: all_chain_items = await search_equipment( type="all", search="chain" ) Semantic search (natural language queries): melee_weapons = await search_equipment( type="weapon", search="slashing blade for close combat" ) protective_gear = await search_equipment( type="armor", search="heavy protective plate" ) magical_storage = await search_equipment( type="magic-item", search="bag that holds items" ) Hybrid search (search + filters): finesse_slashing = await search_equipment( type="weapon", search="elegant blade", is_finesse=True ) rare_magical = await search_equipment( type="magic-item", search="fire wand", rarity="rare" ) Args: type: Equipment type to search. Default "all" searches all types. Options: - "weapon": Melee weapons (longsword, dagger, etc.) and ranged weapons (bow, crossbow) - "armor": Protective gear (leather armor, chain mail, plate, etc.) - "magic-item": Magical items (Bag of Holding, Wand of Fireballs, etc.) - "all": Search all equipment types simultaneously (may return many results) rarity: Magic item rarity filter (weapon/armor types don't use this). Valid values: common, uncommon, rare, very rare, legendary, artifact Example: "rare" for high-value magical items damage_dice: Weapon damage dice filter to find weapons dealing specific damage. Examples: "1d4" (dagger), "1d8" (longsword), "2d6" (greataxe), "1d12" (greatsword) is_simple: Filter for simple weapons (True) or martial weapons (False). Simple weapons: club, dagger, greatclub, handaxe, javelin, light hammer, mace, quarterstaff, sickle, spear Martial weapons: all other melee and ranged weapons Example: True for low-complexity options requires_attunement: Magic item attunement filter. Some powerful items require attunement to a character. Examples: "yes", "no", or specific requirements cost_min: Minimum cost in gold pieces (weapons and armor). Filters items costing at least this amount. Example: 10 for items costing 10+ gp cost_max: Maximum cost in gold pieces (weapons and armor). Filters items costing at most this amount. Example: 25 for items costing 25 gp or less weight_max: Maximum weight in pounds (weapons). Filters weapons weighing at most this amount. Example: 3 for lightweight weapons is_finesse: Finesse property filter (weapons). When True, returns only weapons with the finesse property (can use STR or DEX modifier). Example: True is_light: Light property filter (weapons). When True, returns only light weapons suitable for dual-wielding. Example: True is_magic: Magic property filter (weapons). When True, returns only magical weapons. Example: True documents: Filter to specific source documents. Provide a list of document names/identifiers from list_documents() tool. Examples: ["srd-5e"] for SRD only, ["srd-5e", "tce"] for SRD and Tasha's. Use list_documents() to see available documents. search: Natural language search query for semantic/vector search. When provided, uses vector similarity to find equipment matching the conceptual meaning rather than exact text matches. Can be combined with other filters for hybrid search. Examples: "slashing weapon for melee combat", "protective heavy armor", "magical wand for spells" limit: Maximum number of results to return. Default 20. For type="all" with many matches, limit applies to total results. Examples: 5, 20, 100 Returns: List of equipment dictionaries. Structure varies by type: For type="weapon": - name: Weapon name - damage_dice: Damage expression (e.g., "1d8") - damage_type: Type of damage (slashing, piercing, bludgeoning) - weight: Weight in pounds - is_simple: Whether this is a simple weapon - range: Range for ranged weapons (e.g., "20/60 feet") - properties: Weapon properties (finesse, heavy, reach, two-handed, etc.) - rarity: Equipment rarity For type="armor": - name: Armor name - armor_class: AC provided by this armor - armor_class_dex: Whether DEX bonus applies (light/medium) - armor_class_strength: Whether STR requirement applies (heavy) - weight: Weight in pounds - armor_category: Light/Medium/Heavy classification - rarity: Equipment rarity For type="magic-item": - name: Item name - description: What the item does and its powers - rarity: Rarity level (common through artifact) - requires_attunement: Attunement requirements - wondrous: Whether item is wondrous (non-weapon/armor) - weight: Weight if applicable - armor_class: AC bonus if armor - damage: Damage if weapon Raises: ApiError: If the API request fails due to network issues or server errors """ repository = _get_repository() results: list[dict[str, Any]] = [] if type in ("weapon", "all"): weapon_filters: dict[str, Any] = {"item_type": "weapon"} if damage_dice is not None: weapon_filters["damage_dice"] = damage_dice if is_simple is not None: weapon_filters["is_simple"] = is_simple if cost_min is not None: weapon_filters["cost_min"] = cost_min if cost_max is not None: weapon_filters["cost_max"] = cost_max if weight_max is not None: weapon_filters["weight_max"] = weight_max if is_finesse is not None: weapon_filters["is_finesse"] = is_finesse if is_light is not None: weapon_filters["is_light"] = is_light if is_magic is not None: weapon_filters["is_magic"] = is_magic if documents is not None: weapon_filters["document"] = documents if search is not None: weapon_filters["search"] = search weapons = await repository.search(limit=limit, **weapon_filters) weapon_dicts = [w.model_dump() for w in weapons] results.extend(weapon_dicts) if type in ("armor", "all"): armor_filters: dict[str, Any] = {"item_type": "armor"} if cost_min is not None: armor_filters["cost_min"] = cost_min if cost_max is not None: armor_filters["cost_max"] = cost_max if documents is not None: armor_filters["document"] = documents if search is not None: armor_filters["search"] = search armors = await repository.search(limit=limit, **armor_filters) armor_dicts = [a.model_dump() for a in armors] results.extend(armor_dicts) if type in ("magic-item", "all"): magic_item_filters: dict[str, Any] = {"item_type": "magic-item"} if rarity is not None: magic_item_filters["rarity"] = rarity if requires_attunement is not None: if requires_attunement.lower() in ("yes", "true", "1"): magic_item_filters["requires_attunement"] = True else: magic_item_filters["requires_attunement"] = False if documents is not None: magic_item_filters["document"] = documents if search is not None: magic_item_filters["search"] = search magic_items = await repository.search(limit=limit, **magic_item_filters) magic_item_dicts = [m.model_dump() for m in magic_items] results.extend(magic_item_dicts) if type == "all" and len(results) > limit: results = results[:limit] return results

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

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