Skip to content

MCP HTML Reports

Interactive, self-contained HTML reports generated from Cavai aggregation data. Built as part of the cavai-mcp server (src/tools/reports/html-report.ts).

Architecture

convertToHtml() is a pure function:

convertToHtml(bucketsData, resourceNames, flowData?, dailyFlowData?, metadata?)
  → { html: string, rowCount: number }

It takes raw aggregation data and produces a single HTML file with no external dependencies (except Google Fonts and Chart.js CDN). Everything — styles, scripts, data, assets — is embedded inline.

Data flow

MCP tool call (cavai_make_report with format=html)
  → fetches buckets, flow, daily-flow from Aggregation API
  → passes to convertToHtml()
  → returns self-contained HTML string
  → served as download link (24h expiry)

Report structure

Four tabs:

TabContent
OverviewKPI cards, engagement funnel, detailed metrics, mini impressions chart
TimelineImpressions + interaction rate over time, viewability chart (if available)
FlowConversation flow tree visualization with indent levels
GlossaryMetric definitions (auto-filtered to only show metrics present in report)

Key features

Active campaign day filtering

Days with < 500 impressions are automatically excluded. This removes pre-launch test traffic and post-campaign tail. The date range in the header reflects the actual active period, not the raw query dates.

Threshold is set as ACTIVE_DAY_THRESHOLD = 500 in html-report.ts.

Viewability smoothing

Days with < 100 impressions get null viewability (hidden from chart) to avoid misleading spikes. Charts use spanGaps: true to bridge gaps.

Dynamic glossary

Only metrics with non-zero values appear in the glossary. No video metrics shown for non-video creatives, no "Avg. Rounds" unless replay behavior is detected, etc.

Inline tooltips

All KPI labels and glossary-referenced terms have hover tooltips. Implemented as a single shared <div class="tip-popup"> appended to <body> and positioned via JS on mouseenter. This avoids all stacking context / z-index issues.

Replay insight box

When reached_end > continued (indicating users replay the experience), a contextual insight box appears below the flow tree explaining the pattern and showing the avg. rounds.

Tab animations

Sliding pill indicator (.tab-indicator) moves between tabs using getBoundingClientRect and cubic-bezier transition. Tab content slides in from the right with a CSS animation.

CSV download

"Download CSV" button embeds raw bucket data as a JS object (RAW_BUCKETS) and generates a CSV file client-side via downloadCSV().

beforeprint event handler shows all tab views and calls chart.resize() on all Chart.js instances so hidden charts render correctly. afterprint restores the original view.

Seasonal theming

See seasonal-assets.md.

Responsive / mobile

Layout uses clamp() for font sizes, flex-wrap for KPI cards, and avoids horizontal scroll on mobile.

Files

FilePurpose
src/tools/reports/html-report.tsMain report generator (~1500 lines)
src/tools/reports/make-report.tsMCP tool handler (CSV/Excel/HTML dispatch)
src/tools/reports/bubbles-asset.tsBase64 Bubbles winking mascot (legacy fallback)
src/tools/reports/bubbles-normal.tsBase64 Bubbles normal mascot (default)
src/tools/reports/bubbles-christmas.tsBase64 Bubbles with Santa hat
src/tools/reports/bubbles-halloween.tsBase64 Bubbles halloween variant

Test data

Test xlsx files are stored in /Users/nicolay/Cavai/MCPReports/. Extracted JSON test data: /tmp/bmw_report_data.json and /tmp/puma_report_data.json.

To regenerate a report locally:

bash
cd cavai-mcp
npm run build
node -e "
const { convertToHtml } = require('./build/tools/reports/html-report.js');
const fs = require('fs');
const data = JSON.parse(fs.readFileSync('/tmp/puma_report_data.json', 'utf8'));
const result = convertToHtml(data.bucketsData, data.resourceNames, data.flowData, data.dailyFlowData, data.metadata);
fs.writeFileSync('report.html', result.html);
"

The input JSON must match the BucketsResponse / FlowResponse types from src/api/types.ts. Key field: buckets use time (not time_from).

Console utilities

In the browser console:

  • setSeason('christmas') — swap mascot, particles, and glow to any theme
  • Available themes: winter, spring, summer, autumn, new-year, valentines, easter, may17, midsummer, halloween, christmas, normal

Internal documentation