Skip to main content
Glama
memories_wrapped.py•3.38 kB
import logging import io import base64 from typing import Optional, Sequence, List import threading import osxphotos from osxphotos import PhotoInfo from datetime import datetime from mcp.server.fastmcp import FastMCP from mcp.types import TextContent, ImageContent from PIL import Image, ImageDraw, ImageFont from collections import Counter class PhotosDBLoader: def __init__(self): self._db: Optional[osxphotos.PhotosDB] = None self.load_db() def load_db(self): def load(): try: self._db = osxphotos.PhotosDB() logging.info("Loaded PhotosDB 📸") except Exception as e: logging.error(f"Error: Could not load PhotosDB {e}") thread = threading.Thread(target=load) thread.daemon = True thread.start() @property def db(self) -> osxphotos.PhotosDB: if self._db is None: logging.warning("PhotosDB is still loading; access attempted.") raise Exception("PhotosDB is still loading. Please try again later.") return self._db # Global PhotosDB loader instance photos_loader = PhotosDBLoader() mcp = FastMCP("year-wrapped") def generate_image(text: str) -> str: img = Image.open("./bg.png").convert("RGBA") draw = ImageDraw.Draw(img) font = ImageFont.truetype("InstrumentSans.ttf", size=70) bbox = draw.textbbox((0,0), text, font=font) text_width = bbox[2]- bbox[0] text_height = bbox[3] - bbox[1] OFFSET_X = 170 # move right OFFSET_Y = -100 # move up img_width, img_height = img.size x = (img_width - text_width) / 2 + OFFSET_X y = (img_height - text_height) / 2 + OFFSET_Y draw.text((x, y), text, font=font, fill="white") buffer = io.BytesIO() img.save(buffer, format="WEBP", lossless=True) buffer.seek(0) # Encode to base64 return base64.b64encode(buffer.read()).decode("utf-8") def all_photos_current_year() -> List[PhotoInfo]: current_year = datetime.now().year start = datetime(current_year, 1, 1) end = datetime(current_year, 12, 31, 23, 59, 59) return photos_loader.db.photos(from_date=start, to_date=end) def find_top_date() -> list[str | int]: # Date on which you took most photos dates = [p.date.date() for p in all_photos_current_year() if p.date] counts = Counter(dates) if not counts: print("No photos with a date found.") else: top_date, top_count = counts.most_common(1)[0] return [f"{top_date.isoformat()}", top_count] return ["", ""] @mcp.tool() async def get_year_wrapped() -> Sequence[TextContent | ImageContent]: """Get Year Wrapped of the photo library, This returns an image with statistics about the photos you clicked this year. """ # Total Photos in this year total_photos = len(all_photos_current_year()) top_date, top_count = find_top_date() text = f"You clicked {total_photos} photos!" if top_date != "": text += f"\n\n On {top_date} you clicked {top_count} photos! \n That was your top photos day!" img = generate_image(text) return [ ImageContent( type="image", data=img, mimeType="image/webp" )] def main(): # Initialize and run the server mcp.run(transport='stdio') if __name__ == "__main__": main()

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/niyabits/memories-wrapped-mcp-server'

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