We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/oculairmedia/Tandoor.mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
# Tandoor MCP Server v2 - Product Requirements Document
## Overview
Extend the existing Tandoor MCP Server with cooking history, inventory tracking, and recipe suggestion capabilities while maintaining the consolidated 4-tool architecture.
## Current State
### Existing Tools (4 tools, 15 operations)
| Tool | Operations |
|------|------------|
| `tandoor_recipes` | list, get, create, import |
| `tandoor_meal_plan` | types, list, create, create_type |
| `tandoor_shopping_list` | list, add, update, remove |
| `tandoor_lookup` | keywords, foods, units |
## Proposed Changes
### Tool Extensions (4 tools, 22 operations)
| Tool | Existing | New | Total |
|------|----------|-----|-------|
| `tandoor_recipes` | 4 | 3 | 7 |
| `tandoor_meal_plan` | 4 | 1 | 5 |
| `tandoor_shopping_list` | 4 | 2 | 6 |
| `tandoor_lookup` | 3 | 1 | 4 |
---
## Feature Specifications
### 1. tandoor_recipes - New Operations
#### 1.1 `history` - Get Cooking History
**Purpose:** Query the cook log to answer questions like "What did I eat last week?" or "When did I last make pasta?"
**Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `operation` | string | yes | "history" |
| `recipe_id` | int | no | Filter to specific recipe (API-level filter) |
| `from_date` | string | no | Start date YYYY-MM-DD (client-side filter) |
| `to_date` | string | no | End date YYYY-MM-DD (client-side filter) |
| `limit` | int | no | Max results (default: 50) |
> **Note:** Tandoor's cook-log API only supports `?recipe=` filter natively. Date filtering is done client-side after fetching. For large histories, consider pagination.
**Response:**
```json
{
"total": 15,
"returned": 10,
"cook_logs": [
{
"id": 123,
"recipe_id": 45,
"recipe_name": "Spaghetti Carbonara",
"created_at": "2025-01-20T18:30:00Z",
"servings": 4,
"rating": 5,
"comment": "Perfect consistency"
}
]
}
```
**Use Cases:**
- "What have I cooked this month?"
- "When did I last make chicken curry?"
- "Show me my highest-rated meals from January"
---
#### 1.2 `log` - Log a Cooked Recipe
**Purpose:** Record that a recipe was cooked, with optional rating and notes.
**Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `operation` | string | yes | "log" |
| `recipe_id` | string | yes | Recipe ID or name |
| `servings` | int | no | Servings made (default: recipe default) |
| `rating` | int | no | Rating 1-5 |
| `comment` | string | no | Notes about this cook |
**Response:**
```json
{
"success": true,
"id": 124,
"recipe_name": "Beef Tacos",
"logged_at": "2025-01-25T19:00:00Z",
"message": "Logged cooking of 'Beef Tacos'"
}
```
**Use Cases:**
- Tracking what was actually eaten (vs. just planned)
- Building cooking history for analytics
- Rating recipes after cooking them
---
#### 1.3 `suggest` - Suggest Recipes Based on Inventory
**Purpose:** Get recipe recommendations based on what ingredients are currently marked as on-hand.
> **Algorithm:** Since Tandoor uses boolean on-hand status (not quantities), suggestions are based on ingredient match percentage. Recipes where more ingredients are on-hand rank higher.
**Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `operation` | string | yes | "suggest" |
| `mode` | string | no | "full-match" (only recipes where ALL ingredients on-hand) or "best-match" (ranked by % match). Default: "best-match" |
| `limit` | int | no | Max suggestions (default: 10) |
| `min_match` | float | no | Minimum match percentage 0.0-1.0 (default: 0.5 for best-match) |
| `keywords` | string | no | Comma-separated keyword IDs to filter |
**Response:**
```json
{
"suggestions": [
{
"recipe_id": 78,
"recipe_name": "Garlic Butter Pasta",
"match_percent": 0.80,
"on_hand_count": 4,
"total_ingredients": 5,
"on_hand": ["pasta", "garlic", "butter", "parmesan"],
"missing": ["parsley"]
}
]
}
```
**Algorithm Details:**
1. Query all foods where `on_hand=true` for current user
2. For each recipe (optionally filtered by keywords):
- Fetch ingredients list
- Count how many ingredients are on-hand
- Calculate `match_percent = on_hand_count / total_ingredients`
3. Filter by `min_match` threshold
4. Sort by `match_percent` descending
5. Return top `limit` results
**Use Cases:**
- "What can I make with what I have?"
- "Show recipes I'm only missing 1-2 ingredients for"
- Reduce food waste by using available ingredients
---
### 2. tandoor_meal_plan - New Operations
#### 2.1 `delete` - Delete a Meal Plan Entry
**Purpose:** Remove a meal plan entry.
**Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `operation` | string | yes | "delete" |
| `meal_plan_id` | int | yes | ID of the meal plan entry to delete |
**Response:**
```json
{
"success": true,
"message": "Meal plan entry 456 deleted"
}
```
**Use Cases:**
- Cancel a planned meal
- Clean up old/past meal plans
- Correct mistakes in meal planning
---
### 3. tandoor_shopping_list - New Operations
#### 3.1 `check` - Bulk Check/Uncheck Items
**Purpose:** Mark multiple shopping list items as checked (purchased) or unchecked.
**Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `operation` | string | yes | "check" |
| `item_ids` | list[int] | no | List of item IDs to check |
| `items` | list[string] | no | List of food names to check |
| `checked` | bool | no | True to check, False to uncheck (default: True) |
**Response:**
```json
{
"success": true,
"checked_count": 5,
"items": [
{"id": 101, "food": "Milk", "checked": true},
{"id": 102, "food": "Eggs", "checked": true}
]
}
```
**Use Cases:**
- Quickly check off multiple items while shopping
- Uncheck items that weren't available
- Batch operations for efficiency
---
#### 3.2 `clear` - Clear Checked Items
**Purpose:** Remove all checked items from the shopping list, optionally updating pantry inventory.
**Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `operation` | string | yes | "clear" |
| `update_pantry` | bool | no | If true, mark cleared items as "on hand" in pantry (default: True) |
**Response:**
```json
{
"success": true,
"cleared_count": 8,
"pantry_updated": true,
"foods_marked_on_hand": ["Milk", "Eggs", "Butter"],
"message": "Cleared 8 checked items and updated pantry"
}
```
**Implementation:**
1. Query shopping list entries where `checked=true`
2. For each entry, extract the `food_id`
3. If `update_pantry=true`, PATCH each food to set `food_onhand=true`
4. DELETE each shopping list entry
5. Return summary
**Use Cases:**
- Post-shopping cleanup
- Sync shopping list completion with inventory
- Keep shopping list tidy
---
### 4. tandoor_lookup - New Operations
#### 4.1 `pantry` - Manage Pantry Inventory
**Purpose:** View and update what ingredients are currently on hand.
> **Note:** Tandoor v2 only supports boolean on-hand status (not quantities). Quantity tracking was in v1 and is on the roadmap for v2. This implementation uses boolean-only to match the current API.
**Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `type` | string | yes | "pantry" |
| `action` | string | no | "list" (default) or "update" |
| `query` | string | no | Search term for listing |
| `limit` | int | no | Max results (default: 50) |
| `food_id` | int | no | Food ID for update |
| `food_name` | string | no | Food name for update (resolved to ID) |
| `on_hand` | bool | no | Set on-hand status (for update) |
**Response (list):**
```json
{
"total": 45,
"returned": 20,
"pantry_items": [
{
"food_id": 23,
"food_name": "Chicken Breast",
"on_hand": true
}
]
}
```
**Response (update):**
```json
{
"success": true,
"food_id": 23,
"food_name": "Chicken Breast",
"on_hand": true,
"message": "Marked 'Chicken Breast' as on hand"
}
```
**Use Cases:**
- Track what's in the fridge/pantry (boolean: have it or don't)
- Update inventory after cooking or shopping
- Enable smart recipe suggestions
- Mark staples as always on-hand
---
## API Endpoints Reference
Based on Tandoor API documentation:
| Feature | Endpoint | Method | Notes |
|---------|----------|--------|-------|
| Cook Log List | `/api/cook-log/` | GET | Supports date filtering |
| Cook Log Create | `/api/cook-log/` | POST | Links to recipe |
| Cook Log Detail | `/api/cook-log/{id}/` | GET | |
| Meal Plan Delete | `/api/meal-plan/{id}/` | DELETE | |
| Shopping List Entry | `/api/shopping-list-entry/{id}/` | PATCH/DELETE | |
| Food On-Hand Status | `/api/food/{id}/` | PATCH | Set `food_onhand: true/false` |
| Foods List (on-hand) | `/api/food/?food_onhand=true` | GET | Filter by on-hand status |
| Recipe by Foods | `/api/recipe/?foods_or=1,2,3` | GET | Match recipes containing foods |
---
## Implementation Notes
### Pantry/Inventory Tracking
Tandoor tracks inventory via the `Food` model's `onhand_users` ManyToMany field - a boolean per user.
**API Details:**
- List on-hand foods: `GET /api/food/?food_onhand=true`
- Update status: `PATCH /api/food/{id}/` with `{"food_onhand": true/false}`
- The `food_onhand` field is a custom serializer field that checks if current user is in `onhand_users`
**Limitations:**
- No quantity tracking (boolean only)
- No expiry dates
- Per-user (supports shared households via `shopping_share`)
### Recipe Suggestions
Tandoor doesn't have a native "suggest from inventory" endpoint. Implementation:
```python
def suggest_recipes(mode="best-match", min_match=0.5, limit=10):
# 1. Get all on-hand food IDs
on_hand_foods = get_foods(on_hand=True)
on_hand_ids = {f["id"] for f in on_hand_foods}
# 2. Get candidate recipes (optionally filtered by keywords)
recipes = get_recipes(limit=100) # May need pagination
# 3. Score each recipe
suggestions = []
for recipe in recipes:
ingredients = get_recipe_ingredients(recipe["id"])
total = len(ingredients)
on_hand = [i for i in ingredients if i["food_id"] in on_hand_ids]
match_pct = len(on_hand) / total if total > 0 else 0
if mode == "full-match" and match_pct < 1.0:
continue
if match_pct >= min_match:
suggestions.append({
"recipe": recipe,
"match_percent": match_pct,
"on_hand": on_hand,
"missing": [i for i in ingredients if i["food_id"] not in on_hand_ids]
})
# 4. Sort and return
return sorted(suggestions, key=lambda x: -x["match_percent"])[:limit]
```
**Performance Considerations:**
- Cache on-hand food IDs (changes infrequently)
- Limit candidate recipes or use keyword filtering
- Consider background indexing for large collections
### Cook Log Integration
The cook log (`/api/cook-log/`) tracks:
- `recipe` - Which recipe was cooked (required)
- `created_at` - When it was cooked (auto-set)
- `servings` - Servings made
- `rating` - Rating 1-5 (optional)
- `comment` - Notes (optional)
This is separate from meal plans - meal plans are intentions, cook logs are actuals.
**Key Insight:** Recipes have computed fields from cook logs:
- `rating` - Average of all cook log ratings
- `last_cooked` - Most recent cook log date
### Shopping List Clear Workflow
The `clear` operation connects shopping and pantry:
```
Shopping List (checked=true)
│
├──► DELETE /api/shopping-list-entry/{id}/
│
└──► (if update_pantry=true)
PATCH /api/food/{food_id}/ {"food_onhand": true}
```
This creates a natural workflow:
1. Add items to shopping list (from recipes or manually)
2. Go shopping, check off items as purchased
3. Clear checked items → automatically updates pantry
4. Pantry now reflects what you have → better recipe suggestions
---
## Success Criteria
1. **History queries work** - Can answer "what did I eat last week?"
2. **Logging works** - Can record meals as cooked
3. **Delete works** - Can remove meal plan entries
4. **Shopping workflow complete** - Check and clear operations function
5. **Pantry tracking works** - Can view and update inventory
6. **Suggestions work** - Can get recipe ideas based on pantry
---
## Testing Plan
### Unit Tests
- Each new operation has dedicated test coverage
- Mock API responses for predictable testing
### Integration Tests
- Full workflow: Plan meal → Log cooking → Update pantry
- Shopping workflow: Add items → Check → Clear → Verify pantry
### Manual Testing
- Verify MCP tools appear correctly in Claude
- Test natural language queries about meal history
- Validate suggestion quality
---
## Timeline Estimate
| Phase | Scope |
|-------|-------|
| Phase 1 | `history` and `log` operations |
| Phase 2 | `delete` meal plan |
| Phase 3 | `check` and `clear` shopping operations |
| Phase 4 | `pantry` lookup operation |
| Phase 5 | `suggest` recipes operation |
| Phase 6 | Testing and documentation |
---
## Open Questions
1. ~~**Pantry amounts** - Does Tandoor support tracking quantities?~~ **RESOLVED:** No, v2 is boolean-only. Quantities were in v1, on roadmap for v2.
2. **Suggestion performance** - For large recipe collections, client-side scoring may be slow. Consider caching on-hand food IDs.
3. **Cook log from meal plan** - Should logging a cook auto-reference the meal plan entry if one exists for that date?
4. ~~**Expiry tracking**~~ **RESOLVED:** Not supported in Tandoor. Out of scope for this version.
---
## Appendix: Competitive Analysis
### Features from starbuck93/tandoor-mcp-server
- ✅ Hierarchical keywords/foods (root/tree params) - *nice to have*
- ✅ Multiple recipes per meal plan entry - *nice to have*
### Features from ChristopherJMiller/tandoor-mcp
- ✅ Cook log (`get_cook_log`, `log_cooked_recipe`) - *included in this PRD*
- ✅ Pantry tracking (`update_pantry`) - *included in this PRD*
- ✅ Recipe suggestions (`suggest_from_inventory`) - *included in this PRD*
- ✅ Delete meal plan - *included in this PRD*
- ✅ Bulk check shopping items - *included in this PRD*
- ✅ Clear shopping list - *included in this PRD*
- ⏭️ Scaled ingredients in recipe details - *future enhancement*
- ⏭️ Shopping list organized by store section - *future enhancement*