Skip to content

feat(tools): Add MCP Apps visualization support for search_events - #744

Draft
dcramer wants to merge 1 commit into
mainfrom
feat/mcp-apps-visualization
Draft

feat(tools): Add MCP Apps visualization support for search_events#744
dcramer wants to merge 1 commit into
mainfrom
feat/mcp-apps-visualization

Conversation

@dcramer

@dcramer dcramer commented Jan 27, 2026

Copy link
Copy Markdown
Member

Add interactive chart visualizations for aggregate query results using the MCP Apps protocol. When search_events returns aggregate data, it now includes structured chart data that MCP Apps-compatible clients can render as bar, pie, line charts, tables, or single numbers.

What this adds

  • New mcp-apps-ui package - Contains Chart.js-based visualization app bundled as a single HTML file using Vite
  • UI resource registration - Server registers ui://sentry/search-events-chart.html resource that clients can fetch
  • Tool metadata - search_events now includes _meta.ui.resourceUri in its tool definition
  • Chart data in responses - Aggregate queries return both text (for backward compatibility) and an embedded resource with structured chart data
  • AI-suggested chart types - The search_events agent can suggest appropriate chart types (bar, pie, line, table, number) based on query intent

How it works

  1. MCP Apps-compatible clients see _meta.ui.resourceUri in tool definition
  2. Client fetches the UI resource and renders it in a sandboxed iframe
  3. When search_events returns aggregate results, the response includes:
    • Text content (works everywhere)
    • Embedded resource with mimeType: "application/json;chart" containing structured data
  4. The UI app receives the tool result via app.ontoolresult and renders the chart

Backward compatibility

Clients without MCP Apps support continue to receive text responses exactly as before. The chart data is an additional content block that non-Apps clients simply ignore.

Chart type inference

The system infers chart type based on data shape:

  • Single value → number display
  • Non-numeric labels → pie chart
  • Time-based labels → line chart
  • Default → bar chart

The AI agent can also explicitly suggest a chart type based on query intent.

Comment thread packages/mcp-core/src/tools/search-events/formatters.ts Outdated
Comment thread packages/mcp-apps-ui/src/apps/search-events-chart/app.ts
Add interactive chart visualizations for aggregate query results using
the MCP Apps protocol. When search_events returns aggregate data, it now
includes structured chart data that MCP Apps-compatible clients can
render as bar, pie, line charts, tables, or single numbers.

Key changes:
- Create new mcp-apps-ui package with Chart.js-based visualization
- Extend ToolConfig with optional UI metadata for resource URIs
- Register UI resources in server for client fetching
- Enhance formatters to return chart data alongside text for aggregates
- Add chartType field to search_events agent for visualization hints

The implementation maintains backward compatibility - clients without
MCP Apps support continue to receive text responses.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: OpenAI Codex <noreply@openai.com>
@dcramer
dcramer force-pushed the feat/mcp-apps-visualization branch from b6847a9 to 6388d9c Compare July 28, 2026 22:32

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6388d9c. Configure here.

if (!existsSync(htmlPath)) {
console.error(`Warning: ${htmlPath} not found. Skipping.`);
continue;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Windows path breaks app bundling

Medium Severity

bundle-apps.ts resolves paths with new URL(import.meta.url).pathname instead of fileURLToPath. On Windows that yields an invalid path (leading slash / encoded characters), so the bundled HTML is not found. The script only warns and continues, so the build can succeed without exporting searchEventsChartHtml, leaving the UI resource empty.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6388d9c. Configure here.

),
allColumns,
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chart instance not destroyed on switch

Low Severity

renderChart destroys currentChart before creating a new chart, but renderNumberDisplay and renderTable replace #content via innerHTML without destroying an existing Chart.js instance. A later number/table result after a chart leaves a live chart bound to a detached canvas.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6388d9c. Configure here.

line: "line",
};
const chartJsType = chartJsTypeMap[chartType] ?? "bar";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chart labels show "Unknown" for falsy values like 0 and false

In renderChart, labels with numeric value 0, boolean false, or empty string are incorrectly replaced with "Unknown" instead of being displayed correctly.

Evidence
  • Line 123 of app.ts evaluates String(d[labelField] || "Unknown"), which treats any falsy label value as missing.
  • Aggregate group-by fields can legitimately be 0 (e.g., a numeric status code), false (a boolean flag), or "" (an empty tag); these should appear on the axis/pie slice, not as "Unknown".
  • String(0 || "Unknown") yields "Unknown"; String(false || "Unknown") yields "Unknown".
  • The fix is String(d[labelField] ?? "Unknown") so only null/undefined fall back to "Unknown".
  • No tests in the PR cover chart label rendering with falsy field values.

Identified by Warden · code-review · N2L-S6C

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant