145 lines
4.7 KiB
Python
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}
|
|
|
|

|
|
<!--
|
|
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"""
|
|

|
|
<!--
|
|
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()
|