A food ordering MCP App that demonstrates key MCP Apps SDK patterns.
FoodDash
A food ordering MCP App that demonstrates key MCP Apps SDK patterns. Browse restaurants, view menus, add items to cart, and place orders — all within an MCP host.
Getting Started
bun install
bun run dev
The server starts at http://localhost:3001/mcp.
Scripts
| Command | Description |
|---|---|
bun run dev |
Start dev server with hot reload (web + server watch) |
bun run build |
Production build (typecheck → web → server bundle) |
bun run start |
Run the built server in production mode |
bun run typecheck |
Type-check only |
bun run build:web |
Build web assets only |
bun run build:server |
Bundle server entry only |
bun run dev:server |
Watch and restart server |
MCP Apps Concepts Demonstrated
This app is designed as a learning reference. Each concept is documented with inline comments in the source code.
Tool Visibility
Two kinds of tools share one UI:
| Tool | Visibility | Purpose |
|---|---|---|
order-food |
Model + App | Model calls this when user says "order food". Opens the UI. |
get-order-summary |
Model + App | Model calls this when user asks "where's my food?". Same UI. |
get-menu |
App only | UI calls this when user taps a restaurant. Model never sees it. |
place-order |
App only | UI calls this when user clicks "Place Order". |
get-order-status |
App only | UI polls this every 10s for live order tracking. |
apply-promo |
App only | UI calls this when user applies a promo code. |
Why it matters: Model-visible tools give the LLM a clean, high-level interface ("order food", "check order status"). App-only tools handle granular CRUD that shouldn't clutter the model's tool list.
Lifecycle Hooks (mcp-app.tsx)
| Hook | When it fires | What FoodDash does |
|---|---|---|
ontoolinput |
Tool invoked, BEFORE server responds | Pre-sets cuisine filter from model's cuisine argument |
ontoolresult |
AFTER server returns | Loads restaurant list or order data into state |
ontoolcancelled |
Host cancels a tool call | Resets loading spinners |
onteardown |
Host is closing the app | Returns order summary or abandoned cart info to the model |
onhostcontextchanged |
Host theme/viewport changes | Updates safe area insets |
Structured Content
Every tool result carries two payloads:
content[]— text the model reads in its conversationstructuredContent— JSON the app UI parses to render
They can carry different levels of detail. See server.ts for examples.
Shared Resource URI
Both order-food and get-order-summary point to the same resourceUri. The host renders one UI instance and routes different tool results to it. The app distinguishes them by the shape of structuredContent.
App → Server Communication
The app calls server tools via app.callServerTool(). This is the reverse of the model calling tools — the app initiates calls for app-only operations like fetching menus and placing orders.
Features
- Browse 5 restaurants with ratings, hours, delivery info, and feature tags
- Search, filter by cuisine, and sort by rating/delivery time/fee
- Menu items with dietary labels, calorie counts, and popularity badges
- Dietary filter pills on the menu view
- Cart with quantity controls, special instructions, and promo codes
- Promo codes:
WELCOME10(10% off),FREEDEL(free delivery),SAVE5($5 off) - Live order tracking with auto-advancing status (confirmed → preparing → on the way → delivered)
- Light and dark mode support
Project Structure
server.ts MCP server, tools, and mock data
main.ts Express entry point (HTTP + stdio)
src/
mcp-app.tsx React entry point + lifecycle hooks
types.ts Shared TypeScript interfaces
constants.ts Dietary labels, order steps, sort options
global.css Design tokens and animations
hooks/
useAppState.ts Central state management + server tool calls
components/
Hea
Tools (6)
order-foodInitiates the food ordering process by opening the UI.get-order-summaryRetrieves the current order summary for the user.get-menuFetches the menu for a specific restaurant.place-orderSubmits the final food order.get-order-statusRetrieves the live status of an active order.apply-promoApplies a promotional code to the current cart.Configuration
{"mcpServers": {"food-dash": {"command": "node", "args": ["/path/to/food-dash/build/server.js"]}}}