MealPlanner MCP Multi-Agent
Provides image analysis capabilities via Google Gemini Vision API to identify ingredients from fridge photos, enabling meal planning workflows.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@MealPlanner MCP Multi-AgentIdentify ingredients from my fridge photo and create a weekly meal plan"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
π½οΈ MealPlanner MCP Multi-Agent
A multimodal meal planning agent built using FastMCP (Model Context Protocol). It demonstrates how to build an MCP server with image analysis (via Google Gemini Vision), tools, resources, and prompts.
ποΈ Architecture
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MCP Client (client.py) β
β Connects to server, calls tools, reads resources β
βββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββ
β MCP Protocol (stdio)
βββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββ
β MCP Server (server.py) β
β β
β π§ Tools: β
β β’ identify_ingredients(image) β Gemini Vision API β
β β’ suggest_recipes(ingredients, diet) β
β β’ get_nutrition(recipe_name) β
β β’ get_meal_plan(ingredients, meals, diet) β
β β
β π¦ Resources: β
β β’ recipes://all β Full recipe database β
β β’ recipes://{id} β Single recipe details β
β β’ dietary://profiles β Dietary restrictions β
β β
β π¬ Prompts: β
β β’ analyze_fridge β Image analysis workflow β
β β’ weekly_meal_prep β Meal prep planning β
β β
β π§ External API: β
β β’ Google Gemini 2.0 Flash (Vision/Multimodal) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββRelated MCP server: Nano Banana Pro MCP
π End-to-End Workflow
High-Level Sequence
ββββββββββ ββββββββββββββ ββββββββββββββ ββββββββββββββ
β User β β MCP Client β β MCP Server β β Gemini β
β β β (client.py)β β (server.py)β β Vision β
βββββ¬βββββ βββββββ¬βββββββ βββββββ¬βββββββ βββββββ¬βββββββ
β β β β
β 1. Run client β β β
β with --image β β β
βββββββββββββββββββ>β β β
β β β β
β β 2. Connect via β β
β β stdio transport β β
β ββββββββββββββββββββ>β β
β β β β
β β 3. list_tools() β β
β β list_resources()β β
β β list_prompts() β β
β ββββββββββββββββββββ>β β
β β<ββββββββββββββββββ-β β
β β (capabilities) β β
β β β β
β β 4. read_resource β β
β β ("recipes://all") β β
β ββββββββββββββββββββ>β β
β β<ββββββββββββββββββ-β β
β β (8 recipes) β β
β β β β
β β 5. call_tool β β
β β identify_ β β
β β ingredients(img) β β
β ββββββββββββββββββββ>β 6. Send image β
β β β + prompt β
β β βββββββββββββββββββββ>β
β β β<βββββββββββββββββββββ
β β β 7. Ingredients JSONβ
β β<ββββββββββββββββββ-β β
β β (ingredients list)β β
β β β β
β β 8. call_tool β β
β β suggest_recipes() β β
β ββββββββββββββββββββ>β β
β β<ββββββββββββββββββ-β β
β β (matched recipes) β β
β β β β
β β 9. call_tool β β
β β get_nutrition() β β
β ββββββββββββββββββββ>β β
β β<ββββββββββββββββββ-β β
β β (nutrition info) β β
β β β β
β β 10. call_tool β β
β β get_meal_plan() β β
β ββββββββββββββββββββ>β β
β β<ββββββββββββββββββ-β β
β β (daily plan) β β
β β β β
β 11. Print resultsβ β β
β<βββββββββββββββββββ β β
β (formatted output) β βStep-by-Step Data Flow
Step 1: Client Connects to Server
# client.py β The client starts the server as a subprocess via stdio
client = Client("server.py")
async with client:
# Connection is established using MCP protocol over stdio
# FastMCP auto-handles: handshake, capability negotiation, lifecycleWhat happens internally:
Client("server.py")tells FastMCP to launchserver.pyas a subprocessCommunication happens over stdin/stdout (stdio transport)
MCP protocol handshake occurs automatically
Client discovers server name:
"MealPlanner"
Step 2: Discover Server Capabilities
# client.py β Ask the server what it can do
tools = await client.list_tools() # β 4 tools
resources = await client.list_resources() # β 2 static resources
templates = await client.list_resource_templates() # β 1 template
prompts = await client.list_prompts() # β 2 promptsServer responds with:
Type | Name | Description |
π§ Tool |
| Analyze fridge photo β ingredients (multimodal) |
π§ Tool |
| Match ingredients β recipes |
π§ Tool |
| Recipe β nutritional info |
π§ Tool |
| Ingredients β daily meal plan |
π¦ Resource |
| Full recipe database (8 recipes) |
π¦ Resource |
| 4 dietary profiles |
π Template |
| Lookup single recipe by ID |
π¬ Prompt |
| Guided fridge analysis workflow |
π¬ Prompt |
| Weekly planning template |
Step 3: Read Resources (Recipe Database & Dietary Profiles)
# client.py β Read the recipe database
recipes = await client.read_resource("recipes://all")Server processing (server.py):
@mcp.resource("recipes://all")
def get_all_recipes() -> str:
# Iterates RECIPE_DATABASE dict β returns JSON summary of 8 recipes
# Each recipe has: id, name, cuisine, prep_time, difficulty, ingredientsData returned:
[
{"id": "pasta_primavera", "name": "Pasta Primavera", "cuisine": "Italian", ...},
{"id": "chicken_stir_fry", "name": "Chicken Stir Fry", "cuisine": "Asian", ...},
{"id": "vegetable_omelette", "name": "Vegetable Omelette", ...},
// ... 8 recipes total
]Step 4: Multimodal β Identify Ingredients from Fridge Photo πΈ
This is the core multimodal step where an image is analyzed.
# client.py β Send fridge image for analysis
result = await client.call_tool(
"identify_ingredients",
{"image_path": "/path/to/fridge_photo.jpg"}
)Server processing (server.py) β 4 sub-steps:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β identify_ingredients(image_path) β
β β
β Step A: Read image file from disk β
β image_bytes = Path(image_path).read_bytes() β
β β
β Step B: Encode to base64 β
β image_base64 = base64.b64encode(image_bytes) β
β Detect MIME type: .jpg β "image/jpeg" β
β β
β Step C: Send to Google Gemini Vision API β
β gemini_client.models.generate_content( β
β model="gemini-2.0-flash", β
β contents=[text_prompt + inline_image_data] β
β ) β
β β
β Step D: Parse & return structured JSON β
β Clean markdown code blocks from response β
β Return ingredients list as JSON string β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββThe prompt sent to Gemini:
Analyze this image of a fridge or pantry. Identify ALL visible food ingredients.
Return a JSON object with this structure:
{
"ingredients": [
{"name": "...", "category": "produce|dairy|protein|...", "quantity": "..."}
],
"total_count": <number>,
"freshness_notes": "..."
}Example response from Gemini:
{
"ingredients": [
{"name": "eggs", "category": "protein", "quantity": "6 eggs"},
{"name": "milk", "category": "dairy", "quantity": "1 carton"},
{"name": "tomato", "category": "produce", "quantity": "3 pieces"},
{"name": "cheese", "category": "dairy", "quantity": "1 block"},
{"name": "butter", "category": "dairy", "quantity": "1 stick"}
],
"total_count": 5,
"freshness_notes": "All items appear fresh and well-stored"
}Step 5: Suggest Recipes Based on Ingredients
# client.py β Pass identified ingredients to recipe matcher
result = await client.call_tool(
"suggest_recipes",
{"ingredients": ["eggs", "milk", "tomato", "cheese", "butter"],
"dietary_preference": "none"}
)Server processing (server.py):
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β suggest_recipes(ingredients, dietary_preference) β
β β
β 1. Normalize ingredients β lowercase β
β β
β 2. Load dietary profile β
β "none" β no excluded ingredients β
β "vegan" β excludes eggs, cheese, milk, butter, etc. β
β β
β 3. For EACH recipe in RECIPE_DATABASE (8 recipes): β
β a. Skip if recipe has excluded ingredients β
β b. Count how many recipe ingredients are available β
β c. Calculate match % = available / total Γ 100 β
β d. Include if match β₯ 40% β
β β
β 4. Sort by match_percentage (highest first) β
β β
β 5. Return: recipe name, match %, available & missing items β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββExample output:
{
"dietary_preference": "No Restrictions",
"total_matches": 4,
"recipes": [
{
"name": "Vegetable Omelette",
"match_percentage": 66.7,
"available_ingredients": ["eggs", "cheese", "butter"],
"missing_ingredients": ["bell pepper", "onion", "mushroom"]
},
{
"name": "Grilled Cheese Sandwich",
"match_percentage": 66.7,
"available_ingredients": ["cheese", "butter"],
"missing_ingredients": ["bread"]
}
]
}Step 6: Get Nutrition Info for Top Recipe
# client.py β Get nutrition for the best match
result = await client.call_tool(
"get_nutrition",
{"recipe_name": "vegetable_omelette"}
)Server processing (server.py):
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β get_nutrition(recipe_name) β
β β
β 1. Search RECIPE_DATABASE by ID or name (case-insensitive) β
β 2. If not found β fuzzy match (partial string match) β
β 3. Return full recipe details: β
β - Ingredients list β
β - Step-by-step cooking instructions β
β - Nutrition: calories, protein, carbs, fat, fiber β
β - Auto-generated health tips based on values β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββExample output:
{
"recipe": "Vegetable Omelette",
"nutrition": {
"calories": 320,
"protein": "22g",
"carbs": "8g",
"fat": "24g",
"fiber": "2g"
},
"instructions": [
"Whisk eggs with a splash of milk.",
"Melt butter in a non-stick pan over medium heat.",
"SautΓ© diced vegetables for 2-3 minutes.",
"Pour in egg mixture and cook until edges set.",
"Add cheese, fold, and serve."
],
"health_tips": [
"πͺ Good protein content β great for muscle recovery!",
"π§ Remember to stay hydrated throughout the day!"
]
}Step 7: Generate Daily Meal Plan
# client.py β Create a full day's meal plan
result = await client.call_tool(
"get_meal_plan",
{"ingredients": ingredient_names, "meals_per_day": 3, "dietary_preference": "none"}
)Server processing (server.py):
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β get_meal_plan(ingredients, meals_per_day, diet) β
β β
β 1. Internally calls suggest_recipes() to get matched recipes β
β 2. Assigns recipes to meal slots: β
β - Meal 1 β "Breakfast" (best match recipe) β
β - Meal 2 β "Lunch" (2nd best match) β
β - Meal 3 β "Dinner" (3rd best match) β
β 3. Calculates total estimated calories β
β 4. Returns structured meal plan β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββExample output:
{
"meal_plan": [
{"meal": "Breakfast", "recipe": "Vegetable Omelette", "match": "66.7%"},
{"meal": "Lunch", "recipe": "Grilled Cheese Sandwich", "match": "66.7%"},
{"meal": "Dinner", "recipe": "Classic Tomato Soup", "match": "50.0%"}
],
"estimated_total_calories": 920,
"note": "This is a suggestion based on available ingredients. Adjust portions as needed!"
}Step 8: Client Prints Formatted Results
The client formats all JSON responses into a user-friendly terminal output with emojis, colors, and clear sections.
Complete Data Flow Diagram
πΈ Fridge Photo
β
βΌ
ββββββββββββββββ base64 + prompt ββββββββββββββββ
β MCP Server β βββββββββββββββββββββββ β Google Gemini β
β Tool: β β Vision API β
β identify_ β <βββ JSON ingredients ββ β (2.0 Flash) β
β ingredients β ββββββββββββββββ
ββββββββ¬ββββββββ
β ["eggs", "tomato", "cheese", ...]
βΌ
ββββββββββββββββ
β MCP Server β Compares against
β Tool: β βββΆ RECIPE_DATABASE (8 recipes)
β suggest_ β βββΆ DIETARY_PROFILES (4 profiles)
β recipes β
ββββββββ¬ββββββββ
β [{name: "Omelette", match: 83%}, ...]
βΌ
ββββββββββββββββ
β MCP Server β Looks up recipe details
β Tool: β βββΆ Full instructions
β get_ β βββΆ Nutrition data
β nutrition β βββΆ Health tips (auto-generated)
ββββββββ¬ββββββββ
β {calories: 320, protein: "22g", ...}
βΌ
ββββββββββββββββ
β MCP Server β Combines top recipes into
β Tool: β βββΆ Breakfast / Lunch / Dinner
β get_ β βββΆ Calculates total calories
β meal_plan β
ββββββββ¬ββββββββ
β
βΌ
π Complete Meal Plan
with Nutrition InfoDemo Mode vs Image Mode
Aspect | Demo Mode ( | Image Mode ( |
Image analysis | β Skipped | β Calls Gemini Vision API |
Ingredients | Hardcoded sample list (10 items) | Extracted from image by AI |
API key needed | β No (server loads but tool not called) | β Yes (GOOGLE_API_KEY in .env) |
Recipe matching | β Works | β Works |
Nutrition info | β Works | β Works |
Meal plan | β Works | β Works |
π Project Structure
mealplanner_mcp_multi_agent/
βββ server.py # MCP Server β tools, resources, prompts
βββ client.py # MCP Client β demo script (connects via HTTP)
βββ pyproject.toml # Project config & dependencies (uv)
βββ uv.lock # Locked dependency versions
βββ .env.example # Environment variable template
βββ .python-version # Python version pin
βββ README.md # This fileπ Setup
1. Install uv (if not already installed)
curl -LsSf https://astral.sh/uv/install.sh | sh2. Install dependencies
uv syncThis creates a .venv virtual environment and installs all dependencies from pyproject.toml.
3. Get a Google Gemini API key
Visit Google AI Studio
Create a free API key
Copy
.env.exampleto.envand paste your key:
cp .env.example .env
# Edit .env and add your GOOGLE_API_KEY4. Start the MCP Server (Terminal 1)
uv run python3 server.pyThe server starts at http://127.0.0.1:8000/mcp using Streamable HTTP transport.
5. Run the Client (Terminal 2)
# Demo mode (uses sample ingredients)
uv run python3 client.py
# With a real fridge photo (multimodal!)
uv run python3 client.py --image path/to/fridge_photo.jpg
# Connect to a custom server URL
uv run python3 client.py --url http://your-server:8000/mcp6. Test with MCP Inspector
uv run fastmcp dev server.pyThis opens a web UI where you can interactively test all tools, resources, and prompts.
π§ MCP Concepts Demonstrated
Concept | What It Does | Example in This Project |
Tools | Functions the LLM can call |
|
Resources | Read-only data for the LLM |
|
Resource Templates | Dynamic resources with parameters |
|
Prompts | Reusable prompt templates |
|
πΌοΈ Multimodal Feature
The identify_ingredients tool is the multimodal component:
Accepts an image file path (JPG, PNG, WebP)
Encodes the image to base64
Sends it to Google Gemini 2.0 Flash with a structured prompt
Returns a JSON list of identified ingredients with categories
This demonstrates how MCP tools can handle non-text inputs (images) alongside text.
π Example Output
============================================================
π½οΈ Meal Planner MCP Client
============================================================
π Discovering server capabilities...
π§ Available Tools:
β’ identify_ingredients: Analyze a photo of a fridge or pantry...
β’ suggest_recipes: Suggest recipes based on available ingredients...
β’ get_nutrition: Get detailed nutritional information...
β’ get_meal_plan: Generate a simple daily meal plan...
π¦ Available Resources:
β’ recipes://all: Recipe Database
β’ dietary://profiles: Dietary Profiles
π¨βπ³ Suggesting Recipes:
π’ Vegetable Omelette (83.3% match)
π’ Caprese Salad (80.0% match)
π‘ Classic Tomato Soup (66.7% match)
π‘ Pasta Primavera (57.1% match)
π‘ Grilled Cheese Sandwich (66.7% match)π Key Learning Points
FastMCP simplifies MCP server creation β just use
@mcp.tool(),@mcp.resource(),@mcp.prompt()decoratorsMultimodal = image + text β the server handles image encoding and sends to a vision API
Tools vs Resources β Tools perform actions (with side effects); Resources provide read-only data
Prompts β Reusable templates that guide the LLM through multi-step workflows
Client connects via stdio β FastMCP handles all the MCP protocol details automatically
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
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/vijayaselvam/mealplanner_mcp_multi_agent'
If you have feedback or need assistance with the MCP directory API, please join our Discord server