recipeMakerMCP/server.py

145 lines
4.7 KiB
Python

from mcp.server.fastmcp import FastMCP
from PIL import Image, ImageDraw, ImageFont
import os
import textwrap
# Initialize the FastMCP server
mcp = FastMCP("recipe-maker")
# Constants
RECIPE_DIR = "recipes"
IMAGE_DIR = "recipes/images"
# Ensure directories exist
os.makedirs(RECIPE_DIR, exist_ok=True)
os.makedirs(IMAGE_DIR, exist_ok=True)
def create_placeholder(text: str, file_path: str, color: str = "lightgray"):
"""Creates a simple placeholder image with text."""
width, height = 1024, 1024
img = Image.new('RGB', (width, height), color=color)
d = ImageDraw.Draw(img)
# Simple text centering logic
try:
# Try to load a font, otherwise fallback to default
font = ImageFont.load_default()
# Scale up text if possible (rudimentary)
# In a real app we'd load a TTF
except:
font = None
text_lines = textwrap.wrap(text, width=40)
y_text = height // 2 - (len(text_lines) * 15)
for line in text_lines:
d.text((width // 2, y_text), line, fill="black", anchor="mm", font=font)
y_text += 30
img.save(file_path)
def generate_nanobanana_hero_prompt(title: str, description: str) -> str:
return (
f"Photorealistic hero shot of {title}, {description}. "
"Professional food photography, high detailed, appetizing, 4k resolution, soft natural lighting."
)
def generate_nanobanana_prep_prompt(title: str, ingredients: list[str]) -> str:
ing_list = ", ".join(ingredients[:5]) # Top 5 ingredients
return (
f"Photorealistic overhead shot of ingredients for {title}, including {ing_list}. "
"Kitchen workspace with bowls, mixers, and utensils. Clean, organized, bright lighting."
)
@mcp.tool()
def create_recipe(title: str, description: str, ingredients: list[str], steps: list[str], servings: str) -> str:
"""
Creates a recipe as a Markdown file with placeholder images.
Args:
title: The title of the recipe.
description: A brief description.
ingredients: List of ingredient strings (e.g. "1 cup flour").
steps: List of cooking steps.
servings: Serving size information.
"""
safe_title = "".join(c for c in title if c.isalnum() or c in (' ', '_')).replace(' ', '_').lower()
recipe_filename = os.path.join(RECIPE_DIR, f"{safe_title}.md")
hero_img_name = f"{safe_title}_hero.png"
prep_img_name = f"{safe_title}_prep.png"
hero_img_path = os.path.join(IMAGE_DIR, hero_img_name)
prep_img_path = os.path.join(IMAGE_DIR, prep_img_name)
# Generate prompts
hero_prompt = generate_nanobanana_hero_prompt(title, description)
prep_prompt = generate_nanobanana_prep_prompt(title, ingredients)
# Create placeholders
create_placeholder(f"HERO: {title}\nPrompt: {hero_prompt}", hero_img_path, color="lightblue")
create_placeholder(f"PREP: {title}\nPrompt: {prep_prompt}", prep_img_path, color="lightgreen")
# Create Markdown content
md_content = f"""# {title}
**Servings:** {servings}
## Description
{description}
![Hero Image of {title}](images/{hero_img_name})
<!--
NANOBANANA HERO PROMPT:
{hero_prompt}
-->
## Ingredients
"""
for ing in ingredients:
md_content += f"- {ing}\n"
md_content += "\n## Steps\n"
for i, step in enumerate(steps, 1):
md_content += f"{i}. {step}\n"
md_content += f"""
![Ingredients and Prep for {title}](images/{prep_img_name})
<!--
NANOBANANA PREP PROMPT:
{prep_prompt}
-->
"""
with open(recipe_filename, "w") as f:
f.write(md_content)
return f"Recipe created at {recipe_filename}"
@mcp.tool()
def create_hero_image(title: str, description: str) -> str:
"""Generates a hero image placeholder and prompt."""
safe_title = "".join(c for c in title if c.isalnum() or c in (' ', '_')).replace(' ', '_').lower()
img_name = f"{safe_title}_hero_custom.png"
img_path = os.path.join(IMAGE_DIR, img_name)
prompt = generate_nanobanana_hero_prompt(title, description)
create_placeholder(f"HERO: {title}\nPrompt: {prompt}", img_path, color="lightblue")
return f"Created placeholder at {img_path}. Prompt: {prompt}"
@mcp.tool()
def create_prep_image(title: str, ingredients: list[str]) -> str:
"""Generates a prep image placeholder and prompt."""
safe_title = "".join(c for c in title if c.isalnum() or c in (' ', '_')).replace(' ', '_').lower()
img_name = f"{safe_title}_prep_custom.png"
img_path = os.path.join(IMAGE_DIR, img_name)
prompt = generate_nanobanana_prep_prompt(title, ingredients)
create_placeholder(f"PREP: {title}\nPrompt: {prompt}", img_path, color="lightgreen")
return f"Created placeholder at {img_path}. Prompt: {prompt}"
if __name__ == "__main__":
mcp.run()