Order_Food_MCP
The Order_Food_MCP server lets you browse a café menu and place food orders through an MCP-compatible client and LLM, with all orders calculated in Indian Rupees (₹) and persisted to a SQLite database.
Key tools:
get_menu_tool— Retrieve the full café menu (101 items across 11 categories: Hot/Cold Beverages, Bakery, Breakfast, Sandwiches & Wraps, Salads, Snacks, Desserts, Burgers, Pizza, Pasta) with item IDs, names, descriptions, prices, and vegetarian flags. No arguments required. Triggered by any general ordering intent.place_order_tool— Place an order by submitting item IDs and quantities. Returns a structured invoice with a unique sequential order ID (formatYYYYMMDD-NNN), per-line totals, subtotal, 8% tax, ₹100 delivery fee, and grand total.
Additional capabilities:
Filter menu items by dietary preference (veg/non-veg)
Persistent order history across server restarts via SQLite
Clear error messages for invalid items, bad quantities, or empty orders
Smart LLM routing: vague ordering intent (e.g., "I'm hungry") triggers the menu display first
Click on "Deploy 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., "@Order_Food_MCPShow me the full menu"
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.
Bean & Leaf Café — Food Order MCP Server
A local MCP server for a mock cafe ordering system, built as an internship assignment. Exposes menu browsing, ordering, and order-history tools — backed by a real SQLite database — to any MCP-compatible chat client with a local LLM (developed and demoed against Claude Desktop / Ollama).
Also available as a Docker image: aghimanshu/foodordermcp.
Table of Contents
Related MCP server: Burger King MCP Server
Quick Start
uv sync
uv run pytest -v # confirm everything works
uv run server.py # run the server (stdio transport)Then point any MCP client at server.py, or see Connecting to Claude Desktop.
Tech Stack & Reasoning
Choice | Why |
Python | Fast iteration; consistent with the local FastMCP/Ollama pipeline built in Week 1. |
FastMCP | Minimal boilerplate; auto-generates strict JSON Schema from Python type hints. |
SQLite | A full SQL database in a single file — no server process, fits a local, no-infrastructure project. Accessed via Python's built-in |
pytest + pytest-cov | Standard, simple test runner; coverage reporting to verify tests actually exercise the code, not just exist. |
Docker | Packages the exact runtime environment so the server runs identically on any machine, not just this one. |
Currency: INR (₹) | Deliberate deviation from the assignment brief's USD example — documented explicitly throughout. |
Project Structure
food-order-mcp/
├── menu.csv # Source-of-truth menu data (edit this, not the DB, to change the menu)
├── db.py # SQLite connection, schema, seeding, all persistence functions
├── menu.py # Business logic: menu queries (zero MCP dependency)
├── order.py # Business logic: order calculation, validation, pricing
├── user.py # Local device identity (user_id) + display name
├── server.py # MCP wiring: the 4 tools exposed to the client
├── conftest.py # pytest fixture: isolates all tests from the real database
├── test_menu.py # Tests for menu.py
├── test_order.py # Tests for order.py
├── test_server.py # Tests for the MCP tool layer
├── inspect_db.py # Manual utility: dump full DB contents
├── reset_orders.py # Manual utility: clear orders/order_lines
├── Dockerfile
└── README.mdLayering principle, held throughout: each layer only calls the layer directly below it.
server.py → order.py / menu.py / user.py → db.py → cafe.dbMCP wiring never touches SQL directly, and business logic never imports FastMCP. This is why migrating from a hardcoded list to SQLite, and later adding order persistence and user identity, only ever required editing one or two files — never server.py's tool definitions themselves (beyond wiring in the new functions).
Architecture
Chat Client (Claude Desktop)
│ stdio
MCP Server (server.py, in a container or run directly)
│
Business logic (order.py, menu.py, user.py)
│
db.py (SQLite access layer)
│
cafe.db → menu / users / orders / order_linesThe Menu
101 items across 11 categories: Hot Beverages (14), Cold Beverages (14), Bakery & Pastries (12), Breakfast (10), Sandwiches & Wraps (10), Desserts (8), Burgers (8), Pizza (8), Snacks & Sides (6), Pasta (6), Salads (5).
Source of truth is menu.csv — columns: item_id, name, category, description, price, is_veg. This is the file to hand-edit if the menu changes; the database re-seeds from it automatically (see below).
Database Design
Why SQLite over a hardcoded Python list
The original menu was a 7-item in-memory list. Per mentor direction, this was migrated to a real database so the menu can be edited without touching code, and so orders persist across server restarts.
Why money is stored as integer paise
SQLite has no native Decimal type — only INTEGER, REAL, TEXT, BLOB. REAL reintroduces float rounding errors; TEXT prevents numeric querying. The fix used by real payment systems: store the smallest currency unit as an integer. ₹299.00 is stored as 29900. Conversion to/from Decimal happens only at the boundary, in menu.py and order.py.
Schema
CREATE TABLE menu (
item_id TEXT PRIMARY KEY,
name TEXT NOT NULL,
category TEXT NOT NULL,
description TEXT,
price_paise INTEGER NOT NULL,
is_veg TEXT NOT NULL
);
CREATE TABLE users (
user_id TEXT PRIMARY KEY,
name TEXT
);
CREATE TABLE orders (
order_id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(user_id),
order_date TEXT NOT NULL,
subtotal_paise INTEGER NOT NULL,
tax_amount_paise INTEGER NOT NULL,
delivery_fee_paise INTEGER NOT NULL,
total_paise INTEGER NOT NULL
);
CREATE TABLE order_lines (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id TEXT NOT NULL REFERENCES orders(order_id),
item_id TEXT NOT NULL,
item_name TEXT NOT NULL,
quantity INTEGER NOT NULL,
unit_price_paise INTEGER NOT NULL,
line_total_paise INTEGER NOT NULL
);orders → order_lines is one-to-many, linked by order_id, mirroring the in-memory Invoice / list[InvoiceLine] relationship. orders → users links each order to whoever placed it.
Snapshotting, not referencing: order_lines stores its own copy of item_name and unit_price_paise at the moment of purchase, rather than only storing item_id and looking the price up later. If a menu price changes after an order was placed, that order's historical invoice must still reflect what was actually charged — not the current price.
Seeding strategy: seed-once
On startup, init_db() creates the schema if missing, then seeds menu from menu.csv only if that table is currently empty. Every subsequent startup leaves existing data untouched — the database, not the CSV, is the live source of truth after first run. (Rebuilding from CSV on every restart would also risk wiping order history stored in the same database.)
Order ID generation
Format: YYYYMMDD-NNN (e.g. 20260722-001), sequential per day — generated by counting how many orders already exist for today's date in orders and incrementing. This requires real persistence to work correctly; a per-day sequence has no meaning without a durable count of what's already happened.
Order persistence
place_order() calculates the invoice, generates the order ID, and calls save_order(), which inserts one row into orders and one row per line into order_lines — wrapped in a single transaction, so an order is either fully saved or not saved at all, never half-written.
User Identity Model
MCP over stdio has no login or account concept — the server never receives any information about who is connecting. To support order history, this project uses a local device identity instead:
On first run, a random UUID is generated and saved to
user_id.txtnext to the server.Every subsequent run reads the same ID back — same "user" for every order from this installation.
A
userstable stores an optional, self-reported display name against that ID (set_name_tool).
This identifies an installation, not an authenticated person. It is not verified, not secure, and not meant to represent real multi-user identity — a genuine login system would be required for that, which is explicitly out of scope for this project.
MCP Tools
get_menu_tool()
No arguments. Returns all 101 menu items (id, name, category, description, price, is_veg). Triggers on direct menu questions and vague ordering intent ("I want to order something") — the tool description explicitly covers both.
place_order_tool(items: list[dict])
items: list of {"item_id": str, "quantity": int}. Validates, prices, and persists the order under the current local user. Returns a structured invoice (order ID, date, per-line breakdown, subtotal, tax, delivery fee, total) or {"error": "..."} for invalid input — errors are returned as normal tool output, not raised exceptions, so the LLM can read and relay them clearly.
set_name_tool(name: str)
Saves a self-reported display name against the current local user ID.
get_order_history_tool()
No arguments. Returns this installation's past orders (most recent first) and saved display name, if any.
Running Locally
uv sync
uv run pytest -v
uv run server.pySanity-check discovery/execution with MCP Inspector:
npx @modelcontextprotocol/inspector uv run server.pyRunning with Docker
docker pull aghimanshu/foodordermcp
docker run -i --rm -e DATA_DIR=/app/data -v foodorder_data:/app/data aghimanshu/foodordermcp-i— keeps stdin open; required for MCP's stdio transport (no network port is used, so-pis never needed).-v foodorder_data:/app/data— a named Docker volume, not a local file path, socafe.dbanduser_id.txtpersist across container restarts and this command works identically on any machine.
Connecting to Claude Desktop
Add to claude_desktop_config.json:
Local (Python):
{
"mcpServers": {
"food-order": {
"command": "C:\\path\\to\\food-order-mcp\\.venv\\Scripts\\python.exe",
"args": ["C:\\path\\to\\food-order-mcp\\server.py"]
}
}
}Docker:
{
"mcpServers": {
"food-order": {
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "DATA_DIR=/app/data", "-v", "foodorder_data:/app/data", "aghimanshu/foodordermcp"]
}
}
}Fully quit and relaunch Claude Desktop after editing, then check "+" → Connectors for food-order and its 4 tools.
Testing & Coverage
15 automated tests across test_menu.py, test_order.py, test_server.py — covering happy paths, edge cases (unknown item, zero/negative quantity, empty order, malformed input), and a hand-verified reference case.
uv run pytest --cov=. --cov-report=term-missingFile | Coverage |
| 100% |
| 100% |
| 100% |
| 95% |
| 91%+ |
Test isolation (conftest.py): an autouse pytest fixture redirects db.DB_PATH and user.USER_ID_PATH to a fresh, disposable location (tmp_path) for every single test. This was added after discovering that running the test suite was silently writing real rows into the production cafe.db and overwriting the real user_id.txt — every test now runs against throwaway data instead.
Reference Test Case
Hand-verified order: 2× Margherita Pizza (PZ01, ₹249) + 1× Masala Chai (HB10, ₹89)
Subtotal | ₹587.00 |
Tax (8%) | ₹46.96 |
Delivery Fee | ₹100.00 |
Total | ₹733.96 |
Automated in test_order.py::test_reference_order_case.
Known Limitations & Next Steps
user_ididentifies a device, not an authenticated person — no login system exists; a real one would be required for genuine multi-user identity.No fuzzy/typo-tolerant search — item lookup relies on exact IDs and the LLM's own reasoning over the full menu, not a dedicated search index (Typesense was explored as a possible future addition).
No public/remote hosting — the server runs locally or in a locally-run container; a publicly reachable URL would require switching transport to HTTP and deploying to a real host, both explicitly out of scope for this project without further mentor approval, given the security implications of an unauthenticated public ordering endpoint.
Docker image not published to the official MCP Catalog — only distributed directly via Docker Hub; formal catalog submission involves a review process not pursued here.
Available Tools
1 toolTool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
v0.1.0- First observed
get_menu_tool
TDQS
Scored across 1 tool
With only one tool, there is no possibility of confusion between tools. The tool's purpose is clearly defined.
The single tool name 'get_menu_tool' follows a consistent verb_noun pattern with snake_case, which is a reasonable convention.
A single tool for a food ordering service is borderline thin. While the menu retrieval is essential, the absence of any ordering tool makes the count feel insufficient.
The tool surface is severely incomplete for a food ordering server. It only provides the menu and lacks critical operations like placing, modifying, or canceling orders.
Maintenance
Related MCP Connectors
Run your restaurant from an AI client: orders, menu, reports, refunds, payouts and staff.
Order food from Cordering white-label restaurants: browse menus, confirm, then place an order.
Gateway between LLM agents and world data through eight tools and a bundled endpoint catalog.
Hosted MCP server to manage a restaurant menu from AI agents - 39 tools over the DuckHub API.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables menu browsing, recommendation requests, and shopping cart management through a Model Context Protocol interface. Users can interact with food order services to query items and handle order additions or removals via natural language.-
- AlicenseAqualityDmaintenanceEnables AI assistants to search the Burger King menu, manage a cart with item customizations, and find nearby restaurant locations. It facilitates ordering for pickup or delivery using Playwright browser automation.720MIT
- AlicenseAqualityDmaintenanceThis MCP server provides tools for interacting with Shake Shack restaurant data, including browsing menus, searching for items, and finding locations. It also enables users to access nutritional information, featured items, and ordering details through natural language.79MIT

Pizza MCP Serverofficial
AlicenseNot gradedqualityBmaintenanceEnables AI agents to browse pizza menus, place orders, and track order status via the Model Context Protocol.33MIT