feat(mcp): add patch_dashboard, get_dashboard_tile, search_dashboards tools (HDX-4139)#2343
feat(mcp): add patch_dashboard, get_dashboard_tile, search_dashboards tools (HDX-4139)#2343brandon-pereira wants to merge 13 commits into
Conversation
… tools (HDX-4139) Add three new MCP dashboard tools for more granular operations: - hyperdx_get_dashboard_tile: retrieve a single tile by tileId without loading the full dashboard - hyperdx_patch_dashboard: update dashboard name/tags and/or replace a single tile by tileId in one call, preserving all other tiles and falling back to existing layout when omitted - hyperdx_search_dashboards: search dashboards by name (case-insensitive) and/or tags Additional improvements: - Fix empty parameter schema on patch_dashboard and search_dashboards caused by Zod .refine() wrapping (moved cross-field validation to handler body so inputSchema stays a plain z.object) - Add 'prefer patch_dashboard' hint to save_dashboard description - Document Lucene substring matching limitation prominently in tool descriptions and query guide prompt (field:value is ilike not equality, field:val* is prefix-within-substring not true prefix) - Split monolithic dashboards.test.ts (3040 lines) into 8 focused test files under __tests__/dashboards/
🦋 Changeset detectedLatest commit: 317ecbb The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
E2E Test Results✅ All tests passed • 192 passed • 3 skipped • 1282s
Tests ran across 4 shards in parallel. |
🔴 Tier 4 — CriticalTouches auth, data models, config, tasks, OTel pipeline, ClickHouse, or CI/CD. Why this tier:
Review process: Deep review from a domain expert. Synchronous walkthrough may be required. Stats
|
Deep Review🟡 P2 -- recommended
🔵 P3 nitpicks (16)
Reviewers (9): correctness, testing, maintainability, project-standards, security, adversarial, api-contract, kieran-typescript, reliability. Testing gaps:
|
searchDashboards: - Escape regex metacharacters in query before passing to MongoDB $regex to prevent injection (e.g. '[' throwing BadValue, '.foo' matching unintended substrings, catastrophic backtracking on patterns like '(a+)+$') - Cap query length at 200 chars via Zod schema - Wrap Dashboard.find in try/catch returning isError on failure patchDashboard: - Work directly with persisted internal tiles array instead of round-tripping all tiles through convertToExternalDashboard (which strips orphaned container refs from unrelated tiles via self-heal logic at utils/dashboards.ts:407-424) - Convert only the single patched tile to internal format via convertToInternalTileConfig - Use positional $set (tiles.<index>) instead of full-array $set so concurrent patches on different tiles don't clobber each other
searchDashboards: - Regex metacharacters treated as literals (parentheses, brackets, dots) - Invalid regex chars like '[' don't throw patchDashboard: - Positional update verified via raw DB read: untouched tiles are byte-identical before and after patching a sibling tile
P0 — patchDashboard concurrent write race:
- Replace `tiles.${tileIndex}` positional set with `tiles.id` in the
query filter + `tiles.$` positional operator. A concurrent
save_dashboard that replaces the tiles array can no longer cause us
to overwrite an unrelated tile at a stale numeric index.
- null result from findOneAndUpdate now returns a specific error
message indicating the tile may have been removed concurrently.
P2 — patchDashboard missing alert cleanup:
- Restore cleanupDashboardAlerts call after tile update, scoped to
the single patched tile. Catches displayType changes to configs
that don't support alerts.
P2 — patch tile name required when it should be optional:
- Make `name` optional on mcpPatchTileLayoutSchema so the LLM can
send a config-only patch without re-specifying the tile title.
The merge fallback (incoming.name ?? existing name) is now reachable.
- Add .min(1) to both mcpTileLayoutSchema.name and
mcpPatchTileLayoutSchema.name to reject blank tile titles at the
schema boundary.
P2 — searchDashboards empty query/tags bypass:
- Replace `query === undefined` guard with length checks so
`query: ''` and `tags: []` are rejected instead of silently
returning all dashboards.
|
Note for reviewer: most of the diff is refactoring the tests to be multiple files. Would suggest focusing on the src |
searchDashboards: - Use lodash escapeRegExp instead of reimplemented helper - Inline type narrowing to eliminate query! non-null assertion - Tighten tags schema to z.string().min(1) to reject tags: [''] - Add .limit(100) with truncation hint when results exceed cap - Log errors before returning user-facing message patchDashboard: - Coerce legacy empty-string containerId/tabId to undefined in merge (mirrors convertTileToExternalChart self-heal for legacy docs) - Expand concurrent-miss error to state name/tags were also discarded schemas: - Drop unused id field from mcpPatchTileLayoutSchema (tileId is the authoritative selector; inner id was silently overridden) tests: - Switch JSON.stringify byte-equality to toEqual (order-insensitive) - Add tests: config-only patch preserves name, concurrent tile removal detected, empty query/tags/both rejected changeset: - Note .min(1) contract tightening on tile name
Extract shared validateDashboardTiles() helper in utils/dashboards.ts that consolidates the ~95-line tile validation block previously duplicated across 5 call sites (REST v2 POST/PUT, MCP save create/ update, MCP patch). Net -355 lines. The helper runs all 7 checks in order: container refs, missing sources, missing connections, source/connection mismatches (was REST-only, now shared with MCP), heatmap source-kind gate, onClick dashboard targets, onClick search source validation. Also move getSourceConnectionMismatches from a private function in the REST router to a shared export in utils — MCP callers now get the same source/connection mismatch validation that was previously REST-only. Fix cleanupDashboardAlerts to also catch builder tiles with alert-incompatible display types (Pie, Table, Heatmap, Search, Markdown). Previously only raw SQL tiles with incompatible types were cleaned up, leaving stale alert documents on builder tiles that changed to an unsupported displayType.
…hboard tools Move inline inputSchema definitions from patchDashboard.ts and searchDashboards.ts into schemas.ts as mcpPatchDashboardSchema and mcpSearchDashboardsSchema. Also replace unicode escape \u2014 with literal em dash in patch description.
What
Add three new MCP dashboard tools for more granular, efficient operations alongside the existing full create/update path:
hyperdx_get_dashboard_tile— Retrieve a single tile bytileIdwithout loading the full dashboardhyperdx_patch_dashboard— Update dashboard name/tags and/or replace a single tile bytileIdin one call. Unmentioned tiles and fields are preserved. Layout fields fall back to existing values when omitted.hyperdx_search_dashboards— Search dashboards by name (case-insensitive partial match) and/or tagsWhy
The current MCP dashboard tooling requires passing the full dashboard object on every update — making it slow and token-heavy for targeted edits like changing one tile's query or renaming a dashboard. These tools let the LLM do targeted edits without resubmitting everything.
Closes HDX-4139
Additional improvements
save_dashboarddescription now mentionspatch_dashboardas the preferred tool for single-tile updates.field:valueisilike(substring match, not equality) andfield:val*is prefix-within-substring (not true prefix). Added to tool-levelWHERE_DESCRIPTION, query guide prompt, and common mistakes section.dashboards.test.ts(3040 lines) into 8 focused files under__tests__/dashboards/with a shared setup helper.Testing