Building an MCP-Powered Restaurant Intelligence Platform from Scratch
The business context: six systems, one guess
Restaurant operators already sit on mountains of useful data — POS transactions, reservation logs, weather feeds, local event calendars, delivery platform metrics, inventory counts. The catch is that it lives in six different systems with six different schemas, so operators end up trusting their gut and a shared spreadsheet instead.
Our client, a restaurant analytics startup, wanted to give operators a single conversational interface where they could ask plain-English questions like “What should I prep for Saturday given there’s a football game nearby?” and get answers grounded in their actual data.
Step 1: Data Unification
Before building any agents, we needed a single trustworthy data layer. The raw inputs were:
| Source | Format | Frequency |
|---|---|---|
| POS transactions | CSV exports, REST API | Daily batch |
| Reservation system | Webhook events | Real-time |
| Local events | Scraped calendar feeds | Weekly |
| Weather | Third-party API | Hourly |
| Delivery platforms | CSV reports | Daily batch |
| Inventory | Manual spreadsheet uploads | Ad hoc |
We built an ingestion layer in Python that normalised all sources into a PostgreSQL warehouse with a consistent schema: location, timestamp, metric type, and value. Data quality checks ran on every load — null rates, range violations, duplicate detection, and freshness alerts.
The hardest part was not the engineering but the semantics. “Revenue” in the POS system included tips; “revenue” in the delivery platform did not. We documented every definitional choice in a data dictionary that the agents reference at query time.
Step 2: MCP Agent Design
We structured the platform around three MCP (Model Context Protocol) tool-calling agents, each with a well-defined responsibility:
Agent 1: NL-to-SQL Query Engine
This agent translates natural-language questions into SQL queries against the warehouse. It uses the data dictionary and table schemas as context, generates a candidate query, executes it in a read-only sandbox, and returns the results formatted as a table or summary.
The critical design choice was query validation before execution. The agent generates SQL, then a lightweight validator checks for common pitfalls: missing WHERE clauses that would scan full tables, joins on non-indexed columns, and aggregations that mix incompatible time granularities. Queries that fail validation get revised automatically.
Agent 2: Demand Forecaster
This agent wraps an XGBoost model trained on 18 months of historical demand data, enriched with weather and event features. When an operator asks about future demand, the forecaster:
- Retrieves the relevant historical window from the warehouse
- Fetches upcoming weather and event data
- Runs the trained model to generate a point forecast with confidence intervals
- Formats the output as a human-readable recommendation (“Expect 15-20% higher covers than a typical Saturday; prep accordingly”)
We retrain the model weekly on a rolling window. Feature importance analysis showed that local events (sports, concerts, festivals) were the single strongest predictor of demand spikes — more predictive than day-of-week or weather.
Agent 3: Event-Aware Recommender
This agent monitors upcoming local events within a configurable radius and proactively generates preparation recommendations. Unlike the forecaster (which responds to questions), the recommender pushes alerts: “There’s a 40,000-seat concert 2 miles from your downtown location next Friday. Based on similar past events, expect a 35% demand spike between 5-8 PM.”
MCP Tool Registration
Here’s what the NL-to-SQL agent’s tool registration actually looks like:
from mcp.server import Server
from mcp.types import Tool, TextContent
server = Server("restaurant-intelligence")
@server.tool()
async def query_warehouse(question: str) -> list[TextContent]:
"""Translate a natural-language question into SQL and execute it
against the restaurant data warehouse."""
sql = await nl_to_sql(question, schema=DATA_DICTIONARY)
validated = validate_query(sql) # guard against full-table scans
results = await execute_readonly(validated)
return [TextContent(type="text", text=format_results(results))]
The beauty of MCP is that this tool registration is all you need. Any MCP-compatible client — Claude Desktop, a custom LangGraph agent, or a third-party integration — can discover and call this tool. We didn’t write model-specific function-calling code. The server advertises its capabilities, the client negotiates what it needs, and the protocol handles the rest. This is a meaningful shift from the old pattern of building bespoke API wrappers for every model provider.
Step 3: Evaluation Against Real Scenarios
We tested the system against 200 real planning scenarios from the previous quarter — situations where the operator had to decide how much to prep, how many staff to schedule, or whether to run a promotion.
For each scenario, we compared:
- The operator’s actual decision (what they did in reality)
- The agent’s recommendation (what the system would have suggested)
- The actual outcome (what demand/revenue materialised)
This gave us a concrete accuracy metric: how often would the agent’s recommendation have led to a better outcome than the operator’s gut decision?
Results
- 56% improvement in forecast accuracy compared to the operators’ previous manual estimates
- NL-to-SQL structured decision support that reduced the time from “I have a question” to “I have an answer” from hours (waiting for an analyst) to seconds
- 3 MCP agents in production handling query, forecast, and recommendation workloads independently
- Weekly model retraining with automated data quality gates
MCP in 2026: Why This Matters Now
When we built this platform, MCP was still early. Anthropic had released the spec, a handful of teams were experimenting, and the tooling was rough around the edges. That’s changed dramatically. MCP has crossed 97 million SDK downloads, governance moved to the Linux Foundation, and OpenAI, Google, and Microsoft have all adopted it in their agent frameworks. The protocol went from “interesting Anthropic project” to “the way agents call tools” in about a year.
This project was one of the earlier MCP deployments in the hospitality space, and the bet paid off. Because we built on MCP from the start, we didn’t have to rewrite any integration code when the ecosystem matured. New MCP-compatible clients can connect to our restaurant intelligence tools without us touching the server. The operators’ existing workflows — asking questions in Claude Desktop, triggering forecasts from custom dashboards — all work because the protocol is the contract, not the client.
What We Would Do Differently
Start with fewer data sources. We integrated all six sources before building any agents. In hindsight, we could have launched with just POS and events data (the two highest-signal sources) and added others incrementally. This would have shortened the first-usable-system delivery from 8 weeks to 4.
Invest more in query caching. Popular questions (“How was last Saturday?” or “What’s the forecast for this weekend?”) get asked repeatedly. We added a semantic cache late in the project; doing it earlier would have cut agent API costs by an estimated 40%.
Build the data dictionary collaboratively. We built it ourselves and validated with the client afterward. Starting with the operators’ own terminology from day one would have caught more definitional mismatches earlier.
This project is part of our Data Science & Data Engineering practice. Read the full case study or talk to us about a similar challenge.