From 8799af1355ea4522826d885b843b8d41fa17dfd5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 30 Jun 2026 15:28:01 +0000 Subject: [PATCH 1/3] fix(mcp): guide agents to size dashboard tiles correctly (HDX-4661) Co-authored-by: Brandon Pereira --- .changeset/hdx-4661-tile-height-guidance.md | 15 ++++++++++++ .../mcp/__tests__/dashboards/prompts.test.ts | 23 +++++++++++++------ .../api/src/mcp/prompts/dashboards/content.ts | 4 +++- .../api/src/mcp/tools/dashboards/schemas.ts | 14 +++++++++-- 4 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 .changeset/hdx-4661-tile-height-guidance.md diff --git a/.changeset/hdx-4661-tile-height-guidance.md b/.changeset/hdx-4661-tile-height-guidance.md new file mode 100644 index 0000000000..55cec41070 --- /dev/null +++ b/.changeset/hdx-4661-tile-height-guidance.md @@ -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. diff --git a/packages/api/src/mcp/__tests__/dashboards/prompts.test.ts b/packages/api/src/mcp/__tests__/dashboards/prompts.test.ts index c8368712f0..757cbf768e 100644 --- a/packages/api/src/mcp/__tests__/dashboards/prompts.test.ts +++ b/packages/api/src/mcp/__tests__/dashboards/prompts.test.ts @@ -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'); @@ -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', () => { diff --git a/packages/api/src/mcp/prompts/dashboards/content.ts b/packages/api/src/mcp/prompts/dashboards/content.ts index e62837872e..9e72381d9b 100644 --- a/packages/api/src/mcp/prompts/dashboards/content.ts +++ b/packages/api/src/mcp/prompts/dashboards/content.ts @@ -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). @@ -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. diff --git a/packages/api/src/mcp/tools/dashboards/schemas.ts b/packages/api/src/mcp/tools/dashboards/schemas.ts index 4d415c0173..2f7f1d97db 100644 --- a/packages/api/src/mcp/tools/dashboards/schemas.ts +++ b/packages/api/src/mcp/tools/dashboards/schemas.ts @@ -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. ' + + '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) From 6fcd36f612a6d71be7ef9bd756b14a06af5ca8cf Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 30 Jun 2026 15:35:28 +0000 Subject: [PATCH 2/3] fix(mcp): align heatmap tile width guidance and trim changeset Co-authored-by: Brandon Pereira --- .changeset/hdx-4661-tile-height-guidance.md | 10 ---------- packages/api/src/mcp/tools/dashboards/schemas.ts | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/.changeset/hdx-4661-tile-height-guidance.md b/.changeset/hdx-4661-tile-height-guidance.md index 55cec41070..56e30afaa2 100644 --- a/.changeset/hdx-4661-tile-height-guidance.md +++ b/.changeset/hdx-4661-tile-height-guidance.md @@ -3,13 +3,3 @@ --- 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. diff --git a/packages/api/src/mcp/tools/dashboards/schemas.ts b/packages/api/src/mcp/tools/dashboards/schemas.ts index 2f7f1d97db..f9918beba8 100644 --- a/packages/api/src/mcp/tools/dashboards/schemas.ts +++ b/packages/api/src/mcp/tools/dashboards/schemas.ts @@ -433,7 +433,7 @@ const mcpTileLayoutSchema = z.object({ .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). ' + + 'line / stacked_bar / pie 8-12, heatmap 12, table / search 12-24 (often the full row). ' + 'A markdown note is usually full-width (24).', ), h: z From bba571d0fd402162e5d2afee052d92ce51f7ab67 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 30 Jun 2026 15:54:37 +0000 Subject: [PATCH 3/3] fix(mcp): reconcile search tile height guidance with rule 14 (6-10) Co-authored-by: Brandon Pereira --- packages/api/src/mcp/tools/dashboards/schemas.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/api/src/mcp/tools/dashboards/schemas.ts b/packages/api/src/mcp/tools/dashboards/schemas.ts index f9918beba8..d90e759af4 100644 --- a/packages/api/src/mcp/tools/dashboards/schemas.ts +++ b/packages/api/src/mcp/tools/dashboards/schemas.ts @@ -444,8 +444,8 @@ const mcpTileLayoutSchema = z.object({ .describe( 'Height in grid rows. Default 4. ' + '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).', + 'line / stacked_bar / pie 4-6, heatmap 5-6, table / search 6-10 (taller when more rows are expected), ' + + 'markdown 2-3 for a short note (h: 1 clips the text).', ), id: z .string()