Skip to main content
Glama
resource.py10.5 kB
""" Custom quotes resource with auto-seeding from remote CSV """ from typing import Dict, Any, Optional, List from src.resources.base import BaseResource from src.resources.types import MCPResource, ResourceContent import logging logger = logging.getLogger(__name__) class QuotesResource(BaseResource): """ Inspirational quotes resource that auto-seeds from a remote CSV file Provides access to a collection of quotes with search and filtering capabilities. Auto-seeds from GitHub gist with quotes when table is empty. """ name = "myorg/quotes" description = "Collection of inspirational quotes from famous authors" uri_scheme = "quotes" # Auto-seed from remote CSV when table is empty seed_source = "https://gist.githubusercontent.com/JakubPetriska/060958fd744ca34f099e947cd080b540/raw/963b5a9355f04741239407320ac973a6096cd7b6/quotes.csv" @classmethod def get_config_schema(cls) -> Optional[Dict[str, Any]]: """Configuration schema for quotes resource""" return { "type": "object", "properties": { "max_quotes": { "type": "integer", "description": "Maximum number of quotes to return", "default": 50, "minimum": 1, "maximum": 1000 }, "allowed_authors": { "type": "array", "items": {"type": "string"}, "description": "List of authors this client can access (empty = all authors)" }, "search_enabled": { "type": "boolean", "description": "Allow searching quotes by content", "default": True } } } async def _get_model_class(self): """Return the Quote model for seeding""" from .models import Quote return Quote async def list_resources(self, config: Optional[Dict[str, Any]] = None) -> List[MCPResource]: """List available quote resources with client-specific filtering""" try: resources = [] # Get configuration max_quotes = 50 allowed_authors = [] search_enabled = True if config: max_quotes = config.get("max_quotes", 50) allowed_authors = config.get("allowed_authors", []) search_enabled = config.get("search_enabled", True) # Get quotes from database if not self._db: logger.warning("No database connection available") return [] from .models import Quote async with self._db.get_session() as session: from sqlalchemy import select # Build query with author filtering query = select(Quote) if allowed_authors: query = query.where(Quote.author.in_(allowed_authors)) # Limit results query = query.limit(max_quotes) result = await session.execute(query) quotes = result.scalars().all() # Create resource entries for quote in quotes: resources.append(MCPResource( uri=f"quotes://{quote.id}", name=f"Quote by {quote.author}", description=f"{quote.quote[:100]}{'...' if len(quote.quote) > 100 else ''}", mimeType="text/plain" )) # Add search resource if enabled if search_enabled: resources.append(MCPResource( uri="quotes://search", name="Search Quotes", description="Search quotes by content or author", mimeType="application/json" )) # Add authors list resource resources.append(MCPResource( uri="quotes://authors", name="Authors List", description="List of all available authors", mimeType="application/json" )) return resources except Exception as e: logger.error(f"Error listing quotes resources: {e}") return [] async def read_resource(self, uri: str, config: Optional[Dict[str, Any]] = None) -> Optional[ResourceContent]: """Read quote content with client-specific access control""" try: if not self.validate_uri(uri): raise ValueError(f"Invalid URI for quotes resource: {uri}") # Parse URI resource_path = uri.replace("quotes://", "") # Get configuration allowed_authors = [] search_enabled = True if config: allowed_authors = config.get("allowed_authors", []) search_enabled = config.get("search_enabled", True) if not self._db: raise ValueError("No database connection available") from .models import Quote async with self._db.get_session() as session: from sqlalchemy import select if resource_path.isdigit(): # Get specific quote by ID quote_id = int(resource_path) quote = await session.get(Quote, quote_id) if not quote: raise ValueError(f"Quote not found: {quote_id}") # Check author access if allowed_authors and quote.author not in allowed_authors: raise ValueError(f"Access denied to author: {quote.author}") content = f'"{quote.quote}" - {quote.author}' return ResourceContent( uri=uri, mimeType="text/plain", text=content ) elif resource_path == "search": # Return search instructions if not search_enabled: raise ValueError("Search is disabled for this client") search_info = { "description": "Search quotes by adding ?q=term to the URI", "examples": [ "quotes://search?q=inspiration", "quotes://search?q=Einstein", "quotes://search?q=success" ], "note": "Search looks in both quote content and author names" } return ResourceContent( uri=uri, mimeType="application/json", text=str(search_info) ) elif resource_path == "authors": # Get list of authors query = select(Quote.author).distinct() if allowed_authors: query = query.where(Quote.author.in_(allowed_authors)) result = await session.execute(query) authors = [row[0] for row in result.fetchall()] authors.sort() authors_info = { "total_authors": len(authors), "authors": authors } return ResourceContent( uri=uri, mimeType="application/json", text=str(authors_info) ) elif resource_path.startswith("search?q="): # Handle search query if not search_enabled: raise ValueError("Search is disabled for this client") # Extract search term search_term = resource_path.split("search?q=", 1)[1] search_term = search_term.replace("%20", " ") # Basic URL decoding # Search in quotes and authors query = select(Quote).where( (Quote.quote.ilike(f"%{search_term}%")) | (Quote.author.ilike(f"%{search_term}%")) ) if allowed_authors: query = query.where(Quote.author.in_(allowed_authors)) query = query.limit(20) # Limit search results result = await session.execute(query) quotes = result.scalars().all() search_results = { "search_term": search_term, "total_results": len(quotes), "quotes": [ { "id": quote.id, "author": quote.author, "quote": quote.quote, "uri": f"quotes://{quote.id}" } for quote in quotes ] } return ResourceContent( uri=uri, mimeType="application/json", text=str(search_results) ) else: raise ValueError(f"Unknown resource path: {resource_path}") except Exception as e: logger.error(f"Error reading quotes resource {uri}: {e}") raise

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/GeorgeStrakhov/mcpeasy'

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