MarketPulse UK

Elevator pitch / "What it does"

Every month, Damm UK's sales planning team has to answer one question: will we close above budget — and if not, what should we change? Today that lives across Excel tabs and Slack threads. MarketPulse UK collapses it into a single dashboard:

  • KPI strip — user-owned budget vs model-owned forecast, with the gap in %.
  • Forecast chart — 12 months of actuals + 3-month projection, with a same-period-last-year reference line and a Bedford-temperature anomaly adjustment.
  • Inputs panel — every signal behind the forecast, with credibility pills (Strong / Weak / Noise / Untested). Honest about what drives the number and what doesn't.
  • 2×2 performance matrix — volume Δ × margin Δ vs same month last year, bubble-sized by share. Toggle dimension: brand / channel / customer.
  • "What if?" simulator — pick a brand + a past promo scenario, see side-by-side panels for Adding vs Skipping the promo, each with deltas, a re-forecast, and a plain-English interpretation.
  • AI Analyst (powered by Cala) — free-text Q&A. Internal numbers via MarketPulse tools, external claims via the Cala MCP — with mandatory inline citations (publisher + date + URL). Hard scope restriction: refuses anything it can't source.

Bilingual EN/ES, unit-aware Hl/L, URL-state preserved across toggles.

Inspiration

The brief asked us to forecast UK sales, detect deviations vs budget, and recommend what to change. We're both MBA — Roberto runs sales planning, Eddie engineers. The pain isn't the forecast; it's the narration of the forecast at 10am on a Monday with a director in the room. So we built the cockpit we'd have wanted: numbers that lead into a sentence, an AI analyst that narrates the screen, and zero tabs.

What we learned

  • Margin first, volume second. Once we saw the forecast was already +11.6% over budget, the question stopped being "close the gap" and became "trade volume surplus for margin protection." That reframed the whole simulator.
  • Confidence is content, not chrome. Showing ρ ≈ +0.03 next to the Bedford temperature regressor — instead of hiding it — was the single biggest trust-builder.
  • The AI panel has to refuse. A chatbot that answers everything from training memory is poison in a finance context. The hard scope restriction (MarketPulse tools OR Cala — nothing else) is what makes it usable.
  • Vercel AI SDK v6 has sharp edges. convertToModelMessages returns a Promise; toUIMessageStreamResponse (not toDataStreamResponse); tool defs use inputSchema not parameters; useChat no longer manages input state. Pinning @ai-sdk/react@4.0.0-canary.150 was non-negotiable.

How we built it

  • Build-time data pipeline. All Excel parsing happens in scripts/build-data.ts and writes JSON to app/lib/data/__generated__/. Runtime imports JSON only — xlsx never enters the bundle.
  • Streaming chat surface with the Vercel AI SDK v6, Claude Sonnet 4.6 via the AI Gateway, and the Cala MCP wired in over HTTPS with @ai-sdk/mcp@1.0.43.
  • Anonymization layer — customers and retailers are renamed (Convenience & wholesale #1, Retailer 3) in both the UI and the LLM context; the system prompt forbids restoring real names.
  • 66 PRs, two people, three days. main had to stay demo-able the whole time.

Challenges we ran into

  • Cala MCP latency spikes on the demo path — solved with tool-call status chips so the user sees the agent working.
  • AI SDK v6 was canary-fresh; transitive deps fought us until we pinned the React adapter.
  • Pushing commits to a branch after its PR merged stranded the work twice. Workflow lesson learned.
  • Data was noisy and incomplete in places — the credibility pills exist partly because we needed an honest way to surface "we tried this, it's noise."

Built With

  • ai-sdk
  • anthropic
  • cala
  • claude
  • claude-sonnet
  • mcp
  • model-context-protocol
  • next.js
  • node.js
  • react
  • recharts
  • tailwindcss
  • typescript
  • vercel
  • vercel-ai-sdk
  • vitest
  • zod
Share this project:

Updates