Appearance
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:
| Tab | Content |
|---|---|
| Overview | KPI cards, engagement funnel, detailed metrics, mini impressions chart |
| Timeline | Impressions + interaction rate over time, viewability chart (if available) |
| Flow | Conversation flow tree visualization with indent levels |
| Glossary | Metric 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().
Print / PDF
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
| File | Purpose |
|---|---|
src/tools/reports/html-report.ts | Main report generator (~1500 lines) |
src/tools/reports/make-report.ts | MCP tool handler (CSV/Excel/HTML dispatch) |
src/tools/reports/bubbles-asset.ts | Base64 Bubbles winking mascot (legacy fallback) |
src/tools/reports/bubbles-normal.ts | Base64 Bubbles normal mascot (default) |
src/tools/reports/bubbles-christmas.ts | Base64 Bubbles with Santa hat |
src/tools/reports/bubbles-halloween.ts | Base64 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