The problem

The client runs an online marketplace with a four-person analytics team and several hundred employees who want numbers every day: sales by category, seller performance, refund rates, campaign results. The team estimated that more than half of its week went to ad-hoc requests, many of them variations of questions answered the week before. Leadership wanted people to use AI assistants to answer their own data questions, but the data team wouldn't give an LLM raw SQL access to the warehouse, for good reasons: wrong joins, inconsistent metric definitions, exposure of customer data, and runaway query costs.

What we actually did

A semantic layer as the only way in

We started with the analytics team's own metric definitions and encoded 42 of the most requested metrics (gross merchandise value, net revenue, refund rate, active sellers, and so on) as governed definitions with approved dimensions and filters. The MCP server exposes these definitions, not tables. An assistant asks for "refund rate by category for last quarter"; the server compiles that into SQL from the approved definition, so the number always matches the official dashboard.

Six narrow tools

The server exposes six tools over the Model Context Protocol: list available metrics, describe a metric and its allowed dimensions, query a metric, compare two periods, list saved reports, and explain how a number is calculated. Every query tool enforces row-level security based on the calling user's identity from the company's single sign-on, so a regional manager sees only their region, and customer-level data is never returned at all.

Cost and safety limits

Each query is estimated before it runs. Queries that would scan beyond a set threshold are rejected with a suggestion to narrow the date range. Results are cached for common questions, and every call is logged with the user, the metric, and the generated SQL, so the data team can see exactly what's being asked.

A design decision worth calling out
We deliberately didn't build a "text to SQL" tool. Letting a model write arbitrary SQL against a warehouse produces plausible numbers that disagree with the official dashboards, which destroys trust in both. Routing every question through governed metric definitions means the assistant is only as flexible as the semantic layer, and that tradeoff is what made the data team comfortable.

Challenges and tradeoffs

Results

Within two months of rollout, ad-hoc data requests to the analytics team dropped by 80%, measured from their request queue. The team moved that time to forecasting and experimentation work that had been sitting in their backlog. Because every answer comes from the same definitions as the dashboards, there have been no "the AI says a different number" escalations, which was the main risk leadership worried about.

What we'd do differently

We would publish the miss log to business teams, not just the data team. Seeing which questions couldn't be answered helped teams phrase requests better and made the backlog of new metric definitions a shared priority rather than a data-team chore.