WIP: feat: add timeseries visualization for range queries via MCP Apps#30
WIP: feat: add timeseries visualization for range queries via MCP Apps#30falox wants to merge 13 commits intorhobs:mainfrom
Conversation
Implement interactive HTML chart visualization for execute_range_query results using the MCP Apps extension (io.modelcontextprotocol/ui). Changes: - Upgrade mcp-go to v0.44.0-beta.3 for MCP Apps support (protocol version 2025-11-25 with _meta serialization) - Add embedded uPlot-based chart HTML (pkg/mcp/ui/chart.html) - Register ui://timeseries-chart resource in MCP server - Link execute_range_query tool to chart UI via _meta.ui.resourceUri The chart app uses the passive pattern: it receives RangeQueryOutput data via ui/notifications/tool-result and renders multi-series line charts with timestamp formatting and metric labels. The implementation is backward compatible - non-UI clients continue receiving JSON text results unchanged. Signed-off-by: Alberto Falossi <afalossi@redhat.com>
Modify cardinality checks to skip when TSDB endpoint is unavailable, allowing guardrails to work with Thanos instances. Add helper function isTSDBUnsupported() to detect 404 client errors from TSDB API calls. When MaxMetricCardinality or MaxLabelCardinality guardrails are enabled, queries to Thanos would previously fail because Thanos does not expose the /api/v1/status/tsdb endpoint. Now the cardinality checks are skipped when the endpoint returns a client error, while other TSDB errors still fail as expected. Signed-off-by: Alberto Falossi <afalossi@redhat.com>
Replace custom charting implementation with Chart.js v4 for MCP Apps
timeseries visualization. Adds responsive design features and better
theming support.
Key changes:
- Chart.js v4 with custom date adapter (no external dependencies)
- Responsive tick counts based on viewport width
- Percentage unit support with Y-axis title and 0-100 range
- Horizontal-only grid lines with X-axis tick marks
- Hidden legend, truncated tooltip labels for small screens
- Transparent background with card-only styling
- Query display with mixed font styles (label + monospace value)
New files:
- pkg/mcp/ui/{app.js,styles.css,chart.min.js,date-adapter.js}
- cmd/test-chart: standalone dev server on http://localhost:9199
- Added Unit field to RangeQueryOutput for Y-axis formatting
Signed-off-by: Alberto Falossi <afalossi@redhat.com>
Signed-off-by: Alberto Falossi <afalossi@redhat.com>
Move percent unit label from Y-axis title to dedicated element above the chart for cleaner presentation. Remove "Query:" prefix from query display, showing only the PromQL query string. Signed-off-by: Alberto Falossi <afalossi@redhat.com>
Add a 'title' input parameter to execute_range_query so the LLM can provide a human-readable chart title (e.g., "API Error Rate Over Last Hour"). The title is displayed above the chart when provided and hidden when omitted. Update the test harness with a title selector dropdown. Signed-off-by: Alberto Falossi <afalossi@redhat.com>
Add min-height to chart card (300px) and chart wrapper (200px) so the UI requests sufficient space from the host iframe. Fix ResizeObserver to report full outer dimensions (offsetWidth/Height + margins) instead of contentRect, which excluded border and margin from the reported size. Signed-off-by: Alberto Falossi <afalossi@redhat.com>
Start the chart card hidden (display: none) and report zero size to the host so the iframe occupies no space. Show the card only when tool-result delivers valid structuredContent, and collapse it again on resource-teardown. Update test harness to simulate this: remove auto-send on load, add Clear button for teardown, resize iframe dynamically based on size-changed notifications, and remove duplicate Resend button. Signed-off-by: Alberto Falossi <afalossi@redhat.com>
Add a custom HTML legend below the chart canvas that displays dataset labels with color swatches. Clicking a legend item toggles the corresponding dataset visibility. The legend is scrollable with a max height and updates on chart render, no-data, and chart destroy events. Signed-off-by: Alberto Falossi <afalossi@redhat.com>
Remove the unit label, percent-specific Y-axis formatting, and all related code from the chart UI, output schema, and test harness. Increase title font size and spacing between title and chart. Auto-load chart data on iframe initialization. Signed-off-by: Alberto Falossi <afalossi@redhat.com>
Add a copy-to-clipboard button with inline SVG icon before the PromQL query. Make the query and legend areas drag-to-scroll with grab cursor. Restructure query footer as flex container so padding is respected on both sides. Update test harness with text input for title and revised time range options. Signed-off-by: Alberto Falossi <afalossi@redhat.com>
Show date labels (deduplicated) on the X-axis when the time range exceeds 24 hours, instead of repeating ambiguous time-only labels. Rebuild chart HTML on every /chart request so UI file changes are picked up on browser refresh without restarting the server. Persist test harness selections (theme, series, range, title) in URL query parameters for refresh-safe state. Signed-off-by: Alberto Falossi <afalossi@redhat.com>
Rename cmd/test-chart to cmd/test-mcp-apps and update the Makefile target accordingly. Use a single metric name for all sample series so values share a consistent scale. Signed-off-by: Alberto Falossi <afalossi@redhat.com>
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: falox The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
@falox what's the state of you reviewing the code after the vibe-coding part? Just to avoid the maintainers being the first ones to actually be confronted with the code? |
| </div> | ||
|
|
||
| <script> | ||
| var dark = false; |
There was a problem hiding this comment.
That's a lot of javascript hidden inside a string: deserves to be pulled to a separate file and embedded here instead.
There was a problem hiding this comment.
and there already is the app.js. What is the reason part of it is there and part here? I don't feel like vibe-reviewing the PR until it's critically reviewed by the person whos name is in the commit message :)
|
Side-note: we would need an appropriate mechanism to pull js dependencies in, I don't think we can get away just embedding chart.js.min without any attempt for proper package dependency management. |
Please wait until I remove the WIP: prefix. There are many open points. The current goal is to have something to experiment with and test the limits of the approach. |
|
Ok, converted to draft. |
|
Please ignore the points below if these will become irrelevant and addressed after this PR is made ready for review. My $0.02: I'd expect the MCP clients to send minimal data, for instance, populating a structured schema, while the JS embedding is done by the server (LCS) itself. This not only reduces the payload size propagated, but also shields MCP teams from avoidable CSS and JS maintenance burden (even if the Additionally, how does this relate to https://issues.redhat.com/browse/GIE-376? Is the approach seen here the proposed solution for that ticket? |
|
PR needs rebase. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
Summary
Adds interactive timeseries chart visualization for
execute_range_queryresults via the MCP Apps protocol. When used with an MCP Apps-capable client, range queries now return an embedded Chart.js visualization instead of raw JSON, providing an intuitive graphical view of metrics data with theme support, interactive legends, and responsive design.IMPORTANT: remove this vibe-hack before to merge!
Key Features
Chart UI (
pkg/mcp/ui/)Test Harness (
cmd/test-mcp-apps/)Technical Implementation
go:embedTesting
Test Harness (no dependencies)
make test-mcp-apps # Open http://localhost:9199With MCP Client
./cmd/obs-mcp/ --listen 127.0.0.1:9100 --auth-mode kubeconfig --insecureexecute_range_querywith a valid PromQL queryBackward Compatibility
Non-MCP clients (stdio, HTTP without Apps support) continue to receive JSON text results unchanged.