Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/hdx-4661-tile-height-guidance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@hyperdx/api': patch
---

fix(mcp): guide agents to size dashboard tiles correctly (HDX-4661)

Teach the MCP dashboard tools to pick deliberate tile sizes instead of
leaving everything at the 12x4 default.

- Add per-displayType width/height guidance to the tile layout `w` and `h`
descriptions in the save/patch schemas (number tiles stay small, tables
and search lists take the full row, etc.).
- Replace the cramped `h: 1` markdown advice with sizing that fits the text
and add design-checklist rule 14 "SIZE TILES TO FIT THEIR CONTENT" to the
create-dashboard prompt.
23 changes: 16 additions & 7 deletions packages/api/src/mcp/__tests__/dashboards/prompts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,13 +324,13 @@ describe('MCP Dashboard Prompts', () => {
const checklistIdx = prompt.indexOf('== DESIGN CHECKLIST ==');
const adaptIdx = prompt.indexOf('== ADAPT, DO NOT COPY ==');
const checklistBody = prompt.slice(checklistIdx, adaptIdx);
// Thirteen rules: the original ten plus GROUP BY HAS NO ALIAS HOOK
// (rule 3), VALIDATE EVERY TILE AFTER SAVE (rule 12), and NO
// TITLE-RECAP MARKDOWN TILE (rule 13). Each came out of a live
// verification pass after watching Claude reliably ignore the soft
// "should" formulations or hit a schema gap the earlier checklist
// did not call out.
for (let i = 1; i <= 13; i++) {
// Fourteen rules: the original ten plus GROUP BY HAS NO ALIAS HOOK
// (rule 3), VALIDATE EVERY TILE AFTER SAVE (rule 12), NO
// TITLE-RECAP MARKDOWN TILE (rule 13), and SIZE TILES TO FIT THEIR
// CONTENT (rule 14). Each came out of a live verification pass after
// watching Claude reliably ignore the soft "should" formulations or
// hit a schema gap the earlier checklist did not call out.
for (let i = 1; i <= 14; i++) {
expect(checklistBody).toMatch(new RegExp(`^${i}\\. `, 'm'));
}
expect(prompt).toContain('ADAPT, DO NOT COPY');
Expand Down Expand Up @@ -373,6 +373,15 @@ describe('MCP Dashboard Prompts', () => {
expect(checklistBody).toMatch(/id: "kpis"/);
expect(checklistBody).toMatch(/id: "trends"/);
expect(checklistBody).toMatch(/id: "errors"/);
// Rule 14 teaches per-displayType tile sizing. Claude reliably left
// every tile at the 12x4 default, clipping tables and search lists
// and leaving number tiles oversized; the rule has to name concrete
// per-type w/h ranges so the model picks deliberate sizes.
expect(checklistBody).toMatch(/SIZE TILES TO FIT THEIR CONTENT/);
expect(checklistBody).toMatch(/number tiles stay small \(w 6-8, h 3-4\)/);
expect(checklistBody).toMatch(
/tables and search lists want the full row/,
);
});

it('walks the workflow through six steps including read-existing and group-into-containers', () => {
Expand Down
4 changes: 3 additions & 1 deletion packages/api/src/mcp/prompts/dashboards/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Use BUILDER tiles (with sourceId) for most cases:
pie Proportional breakdowns (traffic share by service, errors by type). Keep slice count under 8.
heatmap Distribution of a numeric value over time (latency buckets, payload size). Trace sources only. Requires non-empty valueExpression.
search Browse raw log/event rows (error logs, recent traces).
markdown Use sparingly. The dashboard already shows its name in the title bar at the top; do NOT add a "About this dashboard" tile that repeats it. Markdown bodies render h1/h2/h3 headings at title-bar scale, so a single \`## Service Catalog\` line eats most of the tile and pushes real KPIs below the fold. Skip markdown tiles for starter dashboards. If you must add one, use h: 1, plain prose, no \`#\`/\`##\`/\`###\` headings. Use containers/tabs for section grouping instead.
markdown Use sparingly. The dashboard already shows its name in the title bar at the top; do NOT add a "About this dashboard" tile that repeats it. Markdown bodies render h1/h2/h3 headings at title-bar scale, so a single \`## Service Catalog\` line eats most of the tile and pushes real KPIs below the fold. Skip markdown tiles for starter dashboards. If you must add one, size it to fit the text (h: 2-3 for a line or two; h: 1 clips it), use plain prose, no \`#\`/\`##\`/\`###\` headings. Use containers/tabs for section grouping instead.

Use RAW SQL tiles (with connectionId) only for queries the builder cannot express:
Requires configType: "sql" plus a displayType (line, stacked_bar, table, number, pie).
Expand Down Expand Up @@ -128,6 +128,8 @@ Apply these before calling clickstack_save_dashboard. Each rule is enforced by t

13. NO TITLE-RECAP MARKDOWN TILE. The dashboard's name shows in the title bar. Adding a markdown tile with the dashboard name (or a "About this dashboard" header) doubles the title and eats a row of vertical space because markdown heading styles render at title-bar scale. Skip the markdown tile entirely on starter dashboards.

14. SIZE TILES TO FIT THEIR CONTENT. The layout w/h are not one-size-fits-all; a tile that is too short clips its content (a table loses rows below the fold, a number tile crops its label) and one that is too wide wastes the row. Match the size to the displayType: number tiles stay small (w 6-8, h 3-4) so three or four KPIs share a row; line / stacked_bar / pie want w 8-12 and h 4-6; tables and search lists want the full row (w 24) and h 6-10 so rows are not cut off; heatmaps want w 12 and h 5-6; a markdown note wants h 2-3 (never h 1, which clips the text). The per-field w/h descriptions on the tile schema carry the same per-displayType ranges; reach for them instead of leaving every tile at the 12x4 default.

== ADAPT, DO NOT COPY ==

The dashboard_examples prompt returns concrete example shapes. Read them as patterns to adapt to the user's actual request and schema, not as literal templates to substitute names into. Specifically: the literal "ServiceName", "StatusCode:STATUS_CODE_ERROR", "Duration" come from the standard HyperDX schema. Real source schemas may use different column names and status values; always verify with clickstack_list_sources before reusing literals.
Expand Down
14 changes: 12 additions & 2 deletions packages/api/src/mcp/tools/dashboards/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,13 +430,23 @@ const mcpTileLayoutSchema = z.object({
.max(24)
.optional()
.default(12)
.describe('Width in grid columns (1–24). Default 12'),
.describe(
'Width in grid columns (1-24; a full row is 24). Default 12. ' +
'Match the width to the displayType: number 6-8 (three or four KPIs per row), ' +
'line / stacked_bar / pie 8-12, table / search / heatmap 12-24 (often the full row). ' +
'A markdown note is usually full-width (24).',
),
h: z
.number()
.min(1)
.optional()
.default(4)
.describe('Height in grid rows. Default 4'),
.describe(
'Height in grid rows. Default 4. ' +
Comment thread
greptile-apps[bot] marked this conversation as resolved.
'Match the height to the displayType so content is not clipped: number 3-4, ' +
'line / stacked_bar / pie 4-6, heatmap 5-6, table 6-10 (taller when more rows are expected), ' +
'search 6-8, markdown 2-3 for a short note (h: 1 clips the text).',
),
id: z
.string()
.max(36)
Expand Down
Loading