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 the NL-to-SQL agent’s tool registration, updated for v2 of the MCP Python SDK:
from mcp.server import MCPServer
mcp = MCPServer("restaurant-intelligence")
@mcp.tool()
async def query_warehouse(question: str) -> str:
"""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 format_results(results)
That is a smaller surface than what we originally shipped, and worth flagging if you are copying older MCP examples: v2 of the Python SDK reworked the server API, so pip install mcp now gives you 2.x and the v1 imports no longer apply. Pin mcp>=1.28,<2 if you are not ready to migrate. In v2 the type hints are the JSON schema and a plain return value is enough — no TextContent wrapping, no manual schema declaration.
The point of MCP is that this registration is all you need. Any MCP-compatible client can discover and call the tool; we wrote no model-specific function-calling code. The server advertises its capabilities, the client negotiates what it needs, and the protocol handles the rest.
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
Why building on MCP paid off
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 changed fast: the protocol was donated to the Linux Foundation’s Agentic AI Foundation in December 2025 with over 97 million monthly SDK downloads behind it, and OpenAI, Google, Microsoft and AWS all adopted it. It went from “interesting Anthropic project” to “the way agents call tools” in about a year.
Because we built on MCP from the start, we didn’t have to rewrite integration code when the ecosystem matured. New MCP-compatible clients connect to our restaurant intelligence tools without us touching the server, and the operators’ workflows — asking questions from an assistant, triggering forecasts from a custom dashboard — all keep working because the protocol is the contract rather than the client.
The honest asterisk is that “no rewrite” applied to the integration boundary, not to the SDK. We have migrated the server code twice since: once from stdio to remote HTTP, and once for the v2 SDK rework that accompanied the protocol’s move to a fully stateless architecture — which we have written up as its own migration. The protocol bet was right; the implementation still moved underneath us.
What we’d 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 afterwards. Starting with the operators’ own terminology from day one would have caught more definitional mismatches earlier.
Make the semantic layer explicit rather than prompt-resident. This is the one we’d change most. Our data dictionary lived in the agent’s context, which meant the definition of “revenue” was enforced by a prompt rather than by the query path. It worked, but every new metric was a prompt edit and there was nothing stopping a differently-phrased question from getting a differently-defined answer. The durable version of this is a governed semantic layer that the agent queries against, so the definitions are code with tests rather than text the model is asked to respect.
References
- MCP joins the Agentic AI Foundation — Model Context Protocol blog, 9 December 2025
- MCP Python SDK — v2 server API and migration guide, accessed August 2026
- Model Context Protocol specification — Agentic AI Foundation, accessed August 2026
- XGBoost documentation — XGBoost project, accessed August 2026
This project is part of our Data Science & Data Engineering practice. Read the full case study or talk to us about a similar challenge.