Skip to main content
Glama

LoreKeeper MCP

by frap129
architecture.md12.3 kB
# Architecture Documentation This document describes the architecture and design decisions behind the LoreKeeper MCP project. ## Table of Contents - [Overview](#overview) - [Architecture Principles](#architecture-principles) - [System Architecture](#system-architecture) - [Component Design](#component-design) - [Data Flow](#data-flow) - [Design Decisions](#design-decisions) - [Performance Considerations](#performance-considerations) - [Scalability Considerations](#scalability-considerations) - [Security Considerations](#security-considerations) ## Overview LoreKeeper is a Model Context Protocol (MCP) server that provides AI assistants with access to comprehensive Dungeons & Dragons 5th Edition data. The system is designed around performance, reliability, and maintainability principles. ### Key Requirements - **Fast Response Times**: AI assistants need quick access to game data - **High Reliability**: System must be dependable for interactive sessions - **Data Freshness**: Game data should be reasonably up-to-date - **Extensibility**: Easy to add new data sources and tools - **Type Safety**: Catch errors at development time rather than runtime ## Architecture Principles ### 1. Async-First Design All I/O operations use async/await patterns to ensure non-blocking behavior: ```python async def lookup_spell(name: str) -> dict[str, Any]: # Check cache first cached = await get_cached(cache_key) if cached: return cached # Fetch from API response = await http_client.get(url) data = response.json() # Cache for future use await set_cached(cache_key, data, "spell", ttl) return data ``` **Benefits**: - Non-blocking I/O operations - Better resource utilization - Improved responsiveness - Natural fit for FastMCP server model ### 2. Layered Architecture The system follows a strict layered approach: ``` ┌─────────────────────────────────────┐ │ FastMCP Server │ ← MCP Protocol Layer ├─────────────────────────────────────┤ │ MCP Tools Layer │ ← Business Logic Layer ├─────────────────────────────────────┤ │ API Client Layer │ ← External Integration Layer ├─────────────────────────────────────┤ │ Cache Layer │ ← Data Persistence Layer ├─────────────────────────────────────┤ │ Configuration Layer │ ← Infrastructure Layer └─────────────────────────────────────┘ ``` **Benefits**: - Clear separation of concerns - Testability in isolation - Easy to modify individual layers - Consistent interfaces between layers ### 3. Cache-First Strategy The system prioritizes cached data over fresh API calls: ```python async def get_data(key: str) -> dict[str, Any]: # Always check cache first cached = await cache.get(key) if cached: return cached # Only fetch from API if cache miss fresh_data = await fetch_from_api(key) await cache.set(key, fresh_data, ttl) return fresh_data ``` **Benefits**: - Dramatically reduced API latency - Lower API rate limiting impact - Better offline capability - Reduced external dependency risk ### 4. Type Safety with Pydantic All configuration and data structures use Pydantic models: ```python class Settings(BaseSettings): db_path: Path = Field(default=Path("./data/cache.db")) cache_ttl_days: int = Field(default=7, ge=1, le=365) class Spell(BaseModel): name: str level: int = Field(ge=0, le=9) school: Literal["abjuration", "conjuration", ...] ``` **Benefits**: - Compile-time error detection - Automatic validation - IDE support and autocomplete - Self-documenting code ## System Architecture ### Component Interaction ```mermaid graph TD A[AI Assistant] --> B[FastMCP Server] B --> C[MCP Tools] C --> D[Cache Layer] C --> E[API Clients] D --> F[SQLite Database] E --> G[Open5e API] E --> H[D&D 5e API] I[Configuration] --> B I --> D I --> E ``` ### Data Flow Patterns #### 1. Cache Hit Flow ``` AI Request → MCP Tool → Cache Check → Return Cached Data → AI Response ``` #### 2. Cache Miss Flow ``` AI Request → MCP Tool → Cache Check → API Call → Cache Storage → Return Data → AI Response ``` #### 3. Error Flow ``` AI Request → MCP Tool → Cache Check → API Error → Cache Error → Return Error Message → AI Response ``` ## Component Design ### FastMCP Server Layer **Responsibilities**: - MCP protocol handling - Tool registration and discovery - Request routing - Lifecycle management **Key Design Decisions**: - Use FastMCP framework for MCP compliance - Async context manager for resource lifecycle - Centralized error handling ```python @asynccontextmanager async def lifespan(app: Any) -> AsyncGenerator[None]: await init_db() # Initialize database yield # Cleanup resources if needed mcp = FastMCP( name="lorekeeper-mcp", version="0.1.0", lifespan=lifespan, ) ``` ### MCP Tools Layer **Responsibilities**: - Implement specific lookup functionality - Parameter validation - Response formatting - Cache integration **Tool Categories**: - `lookup_spell`: Spell information - `lookup_creature`: Monster/creature stat blocks - `lookup_character_option`: Classes, races, backgrounds, feats - `lookup_equipment`: Weapons, armor, magic items - `lookup_rule`: Rules, conditions, references **Design Pattern**: ```python @mcp.tool() async def lookup_spell( name: str | None = None, level: int | None = None, school: str | None = None, # ... other parameters ) -> str: """Search and retrieve spell information.""" # Build cache key cache_key = build_cache_key("spell", name, level, school, ...) # Check cache cached = await get_cached(cache_key) if cached: return format_spell_response(cached) # Fetch from API api_data = await fetch_from_open5e("spells", params) # Cache results await set_cached(cache_key, api_data, "spell", ttl) return format_spell_response(api_data) ``` ### API Client Layer **Responsibilities**: - HTTP communication with external APIs - Request/response handling - Error handling and retry logic - Response normalization **Design Considerations**: - Separate clients for each API (Open5e, D&D 5e) - Consistent interface across different APIs - Automatic retry with exponential backoff - Rate limiting awareness ```python class Open5eClient: def __init__(self, base_url: str, http_client: httpx.AsyncClient): self.base_url = base_url self.client = http_client async def get_spells(self, **params) -> dict[str, Any]: """Fetch spells from Open5e API.""" url = f"{self.base_url}/v2/spells/" response = await self.client.get(url, params=params) response.raise_for_status() return response.json() ``` ### Cache Layer **Responsibilities**: - Data persistence and retrieval - TTL management - Cache invalidation - Performance optimization **Database Schema**: ```sql CREATE TABLE api_cache ( cache_key TEXT PRIMARY KEY, response_data TEXT NOT NULL, created_at REAL NOT NULL, expires_at REAL NOT NULL, content_type TEXT NOT NULL, source_api TEXT NOT NULL ); CREATE INDEX idx_expires_at ON api_cache(expires_at); CREATE INDEX idx_content_type ON api_cache(content_type); ``` **Performance Optimizations**: - WAL mode for concurrent access - Indexes on frequently queried columns - Connection pooling - Batch operations for cleanup ### Configuration Layer **Responsibilities**: - Environment variable handling - Default value management - Validation - Type conversion **Design Features**: - Pydantic Settings for automatic validation - Environment file support - Type-safe configuration - Runtime configuration validation ## Design Decisions ### 1. SQLite vs. External Cache **Decision**: Use SQLite instead of Redis or other external caches **Rationale**: - No external dependencies - Persistent across restarts - ACID compliance for data integrity - Excellent Python support with aiosqlite - Sufficient performance for use case - Easy backup and migration ### 2. API Selection Strategy **Decision**: Prefer Open5e API, use D&D 5e API as fallback **Rationale**: - Open5e has more comprehensive data - Better structured responses - Includes community content - D&D 5e API needed for rules (not available in Open5e) - Reduces complexity by using single primary API per category ### 3. TTL Values **Decision**: 7 days for normal data, 5 minutes for errors **Rationale**: - Game data changes infrequently (new books, errata) - 7 days balances freshness with performance - Errors should be cached briefly to allow quick recovery - Manual cache invalidation available if needed ### 4. Async vs. Sync **Decision**: Full async implementation **Rationale**: - Natural fit for I/O-bound operations - Better resource utilization - FastMCP server is async-based - Future scalability for concurrent requests - Modern Python best practices ### 5. Type Safety Level **Decision**: Strict type checking with MyPy **Rationale**: - Catch errors at development time - Better IDE support - Self-documenting code - Easier refactoring - Reduced runtime errors ## Performance Considerations ### Cache Performance **Optimizations**: - WAL mode for concurrent reads/writes - Indexes on expiration and content type - Connection pooling - Efficient cache key generation **Metrics to Monitor**: - Cache hit ratio (target: >80%) - Average cache retrieval time - Database size growth - Cleanup operation frequency ### API Performance **Optimizations**: - HTTP/2 support with httpx - Connection pooling - Request timeout configuration - Retry logic with exponential backoff **Metrics to Monitor**: - API response times - Error rates - Rate limiting encounters - Timeout frequency ### Memory Usage **Considerations**: - Streaming large responses - Efficient JSON parsing - Connection pool sizing - Cache size management ## Scalability Considerations ### Horizontal Scaling **Current Limitations**: - SQLite file-based storage (not suitable for multi-instance) - In-process cache (no distributed caching) **Future Enhancements**: - PostgreSQL for multi-instance deployments - Redis for distributed caching - Load balancer support - Container orchestration ### Vertical Scaling **Current Capabilities**: - Async I/O for concurrent requests - Efficient caching reduces load - Lightweight Python runtime **Optimization Opportunities**: - CPU profiling for bottlenecks - Memory usage optimization - Database query optimization ## Security Considerations ### Current Security Measures **Input Validation**: - Pydantic model validation - Type checking - Parameter sanitization **Data Protection**: - No sensitive data in cache - Environment variable configuration - No hardcoded credentials ### Future Security Enhancements **API Security**: - API key management - Rate limiting - Request signing **Data Security**: - Encryption at rest - Secure cache key generation - Audit logging **Network Security**: - HTTPS enforcement - Certificate validation - Proxy support ## Architecture Evolution ### Phase 1: Current Implementation - Single-instance SQLite cache - Basic MCP tools - Open5e + D&D 5e API integration ### Phase 2: Enhanced Features - Additional data sources - Advanced search capabilities - Real-time data synchronization ### Phase 3: Production Scaling - Multi-instance support - Distributed caching - Load balancing - Monitoring and observability ### Phase 4: Advanced Features - Machine learning integration - Predictive caching - Advanced analytics - Custom data sources This architecture provides a solid foundation for the current requirements while allowing for future growth and enhancement.

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