search_all.py•4.34 kB
"""Unified search tool for D&D content across multiple types.
This module provides a search tool that exposes the Open5e unified search
endpoint with fuzzy and semantic matching capabilities for cross-entity searches.
Semantic search is always enabled for conceptual matching.
Architecture:
- Uses Open5eV2Client.unified_search() for fuzzy and semantic search
- Supports searching across multiple content types
- Handles limit distribution when searching multiple types
- Semantic search is always enabled for better matching
Examples:
Basic search:
results = await search_all(query="fireball")
Semantic search for related content:
results = await search_all(query="healing magic")
Filter by content type:
spells = await search_all(query="fire", content_types=["Spell"])
Filter by document:
results = await search_all(query="fireball", documents=["srd-5e"])
"""
from typing import Any
from lorekeeper_mcp.api_clients.open5e_v2 import Open5eV2Client
def _get_open5e_client() -> Open5eV2Client:
"""Get Open5eV2Client instance.
Returns:
Open5eV2Client for unified search
"""
return Open5eV2Client()
async def search_all(
query: str,
content_types: list[str] | None = None,
documents: list[str] | None = None,
limit: int = 20,
) -> list[dict[str, Any]]:
"""
Search across all D&D content with semantic matching.
This tool uses Open5e's unified search endpoint to find content across
multiple types (spells, creatures, items, etc.) with fuzzy typo tolerance
and semantic conceptual matching. Perfect for exploratory searches like
"find anything related to fire" or when you're not sure of exact spelling.
Semantic search is always enabled to provide the best conceptual matching.
Examples:
# Cross-entity search
search_all(query="dragon") # Finds dragons, dragon spells, etc.
# Typo-tolerant search
search_all(query="firbal") # Finds "Fireball" despite typo
# Concept-based search
search_all(query="healing magic") # Finds healing spells
# Type-filtered search
search_all(query="fire", content_types=["Spell"]) # Only spells
# Document-filtered search
search_all(query="fireball", documents=["srd-5e"])
search_all(query="spell", documents=["srd-5e", "tce"])
Args:
query: Search term (handles typos and concepts automatically)
content_types: Limit to specific types: ["Spell", "Creature", "Item",
"Background", "Feat"]. Default None searches all content types.
documents: Filter results to specific documents. Provide list of
document names from list_documents() tool. Post-filters search
results by document field. Examples: ["srd-5e"], ["srd-5e", "tce"].
limit: Maximum number of results to return (default 20)
Returns:
List of content dictionaries with varied structure based on content
type. Each result includes a 'type' or 'model' field indicating
content type.
Raises:
ApiError: If the API request fails due to network issues or errors
"""
if documents is not None and len(documents) == 0:
return []
client = _get_open5e_client()
if content_types:
all_results: list[dict[str, Any]] = []
per_type_limit = limit // len(content_types)
for content_type in content_types:
results = await client.unified_search(
query=query,
fuzzy=True,
vector=True,
object_model=content_type,
limit=per_type_limit,
)
all_results.extend(results)
if documents:
all_results = [
r
for r in all_results
if r.get("document") in documents or r.get("document__slug") in documents
]
return all_results[:limit]
results = await client.unified_search(
query=query,
fuzzy=True,
vector=True,
limit=limit,
)
if documents:
results = [
r
for r in results
if r.get("document") in documents or r.get("document__slug") in documents
]
return results[:limit]