🎨 feat: ClickHouse MCP Tool Call UI with Click UI#12663
🎨 feat: ClickHouse MCP Tool Call UI with Click UI#12663dustinhealy wants to merge 33 commits intodevfrom
Conversation
- Add @clickhouse/click-ui and styled-components dependencies - Bump react/react-dom to ^18.3.1 to satisfy click-ui peer deps - Add lazy-loaded ClickHouseToolCall component with SQL syntax highlighting via CodeBlock, tabbed Input/Query/Result views, and status Badge — wrapped in its own ClickUIProvider - Delegate ClickHouse MCP tool calls from ToolCallInfo when domain matches /clickhouse/i - Add styled-components Vite chunk for code splitting
…d output - Tabbed layout: Query (SQL), Result (table/key-value), Details (raw JSON) - Details tab right-aligned, separated from Query/Result - Metrics bar (elapsed, rows read, bytes) in bottom right with status badge - Sticky table headers for scrollable query results - Key-value display for service details, backup config, etc. - Max height with scroll on Details input/output CodeBlocks - SQL CodeBlock with wrapLines and showWrapButton for long queries
…arators, pill rendering
- New ClickHouseCostView for get_organization_cost with By Entity / By Date toggle, Daily / Weekly grouping, metric breakdowns, grand total display, and entity type pills - Collapsible service rows for get_services_list with expand/collapse all, endpoint pills, ipAccessList pills, and full-width separators - Pass functionName through ToolCall → ToolCallInfo → ClickHouseToolCall for per-tool rendering decisions - Use ClickHouse design tokens for container backgrounds
…uto-tab, cost polish
🚨 Unused NPM Packages DetectedThe following unused dependencies were found: 📂 Client
|
There was a problem hiding this comment.
Pull request overview
This PR adds a ClickHouse-specific rich UI renderer for MCP tool calls in the chat UI by delegating ClickHouse-domain tool calls to a new ClickHouseToolCall component backed by @clickhouse/click-ui, along with dependency upgrades required to support it.
Changes:
- Add
@clickhouse/click-ui(+styled-components) and bumpreact/react-domto^18.3.1. - Delegate ClickHouse-domain tool calls to a lazily loaded
ClickHouseToolCallrenderer (tabs, SQL, results grid, details, metrics, cost views). - Add a Vite manual chunk rule intended to isolate Click UI-related code.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/client/package.json | Bumps React/ReactDOM versions used by the client package workspace. |
| client/package.json | Adds Click UI + styled-components and bumps React/ReactDOM to support new UI. |
| package-lock.json | Locks the new dependency graph (Click UI + transitive deps) and React bumps. |
| client/vite.config.ts | Adds a manualChunks rule to group styled-components into a click-ui chunk. |
| client/src/components/Chat/Messages/Content/ToolCallInfo.tsx | Adds ClickHouse-domain delegation via React.lazy + Suspense. |
| client/src/components/Chat/Messages/Content/ToolCall.tsx | Passes domain and function_name through to ToolCallInfo. |
| client/src/components/Chat/Messages/Content/ClickHouseToolCall.tsx | New rich renderer for ClickHouse tool calls (query/result/details/cost/metrics). |
| client/src/components/Chat/Messages/Content/ClickHouseCostView.tsx | New cost breakdown UI (by entity/date with day/week grouping). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "homepage": "https://librechat.ai", | ||
| "dependencies": { | ||
| "@ariakit/react": "^0.4.15", | ||
| "@clickhouse/click-ui": "0.2.0-rc.4", |
| {metrics.totalRows !== undefined && !metrics.rowsRead && ( | ||
| <Text color="muted" size="xs"> | ||
| {metrics.totalRows.toLocaleString()} {metrics.totalRows === 1 ? 'row' : 'rows'} | ||
| </Text> | ||
| )} |
| function getDateRange(costs: CostEntry[]): { days: number; start: Date; end: Date } { | ||
| const dates = costs.map((c) => new Date(c.date)).sort((a, b) => a.getTime() - b.getTime()); | ||
| const start = dates[0]; | ||
| const end = dates[dates.length - 1]; | ||
| const days = Math.round((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)) + 1; | ||
| return { days, start, end }; |
| function getTimeLevels(days: number): TimeLevel[] { | ||
| if (days <= 7) { | ||
| return ['day']; | ||
| } | ||
| return ['week', 'day']; | ||
| } | ||
|
|
| if (domain && /clickhouse/i.test(domain)) { | ||
| return ( | ||
| <Suspense fallback={<div className="p-3 text-xs text-text-secondary">Loading...</div>}> | ||
| <ClickHouseToolCall input={input} output={output} functionName={functionName} /> | ||
| </Suspense> | ||
| ); | ||
| } |
| functionName?: string; | ||
| } | ||
|
|
||
| interface ParsedInput { |
There was a problem hiding this comment.
create a clickhouse folder, types in one module, helpers in another, custom hooks in another (if any), leaving the main modules with as little boilerplate as possible. add tests for your helpers.
| "remark-math": "^6.0.0", | ||
| "remark-supersub": "^1.0.0", | ||
| "sse.js": "^2.5.0", | ||
| "styled-components": "^6.1.11", |
There was a problem hiding this comment.
if absolutely required when using click-ui, make an exception in .github/workflows/unused-packages.yml
- Move ClickHouse components into ClickHouse/ folder per review - Extract types into types.ts, helpers into helpers.ts - Add 42 unit tests for all helper functions - Fix rowsRead falsy check (use === undefined) - Guard getDateRange against empty costs array - Remove unused getTimeLevels function - Add styled-components exception to unused-packages workflow
- Reuse existing keys: com_ui_result, com_ui_details, com_ui_input, com_endpoint_output - Add new keys for ClickHouse-specific strings: com_ch_tab_query, com_ch_label_elapsed, com_ch_label_read, com_ch_expand_all, etc. - Fix useMemo deps warning in CostView DateView - Remove autoFocus prop (jsx-a11y/no-autofocus) - Run prettier on all files
🚨 Unused i18next Keys DetectedThe following translation keys are defined in
|
…auses CJS interop error
click-ui's transitive deps (styled-components etc.) in the vendor chunk disrupt Rollup's CJS interop wrapping for React, causing forwardRef and useSyncExternalStore to be undefined at runtime. Giving click-ui its own manual chunk keeps vendor identical to the dev branch. Also replace CodeBlock with a lightweight CodeDisplay component since CodeBlock pulls in react-syntax-highlighter (CJS lowlight@1.x) which conflicts with rehype-highlight's ESM lowlight@2.x.
Use lowlight (already loaded by rehype-highlight) for SQL/JSON highlighting, with Click UI's exact color palette scoped via .ch-code class. Supports dark/light mode.
There was a problem hiding this comment.
Do you need this in the repo?
| "com_ui_zoom_level": "Zoom level", | ||
| "com_ui_zoom_out": "Zoom out", | ||
| "com_user_message": "You" | ||
| "com_user_message": "You", |
There was a problem hiding this comment.
make sure these are in alphabetical order along with the other i18n keys
| ], | ||
| globIgnores: ['images/**/*', '**/*.map', 'index.html'], | ||
| maximumFileSizeToCacheInBytes: 4 * 1024 * 1024, | ||
| maximumFileSizeToCacheInBytes: 5 * 1024 * 1024, |
There was a problem hiding this comment.
What is the current byte size of the build? Can you avoid making this change?
|
@codex review |
|
/gitnexus index |
| ); | ||
| } | ||
|
|
||
| function groupEntriesByWeek( |
There was a problem hiding this comment.
Remove helpers from the module, do not group into generic single module helpers.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 82a4273553
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const groups = new Map<string, CostEntry[]>(); | ||
|
|
||
| for (const cost of costs) { | ||
| const key = getGroupKey(new Date(cost.date), currentLevel); |
There was a problem hiding this comment.
Group cost dates using UTC-safe calendar parsing
This groups entries with new Date(cost.date) and then derives day/week buckets from local-date methods, which misclassifies date-only strings (YYYY-MM-DD) in non-UTC timezones. For example, in US timezones 2026-01-01 is interpreted as the prior local day, so "By Date" and weekly rollups can be shown under the wrong date/week and produce incorrect financial breakdowns for users.
Useful? React with 👍 / 👎.
| const rawColumns = Object.keys(rows[0]).filter((col) => { | ||
| const sample = rows[0][col]; | ||
| return sample === null || typeof sample !== 'object'; | ||
| }); |
There was a problem hiding this comment.
Build table columns from all rows, not just first row
The table schema is inferred only from rows[0], so any scalar field that is absent in the first row but present in later rows is never rendered as a column. This silently hides valid result data for sparse/heterogeneous responses, which makes the tool output incomplete and hard to trust.
Useful? React with 👍 / 👎.
Sort com_ch_* i18n keys alphabetically in translation.json. Revert PWA precache limit to 4MB (vendor is 3.4MB with chunk isolation). Move formatCHC into CostView to keep cost helpers colocated. Fix UTC date parsing in CostView with parseLocalDate helper. Build FlatTable columns from all rows, not just first row. Fix grid bottom padding gap.
Fix UTC timezone bug in CostView getGroupKey (toISOString → localISODate).
Fix parseOutput false positive on {error: false} responses.
Fix formatCHC handling of negative values.
Remove broad regex fallback in parseOutput catch block.
Eliminate double JSON.parse in parseOutput.
Memoize FlatTable column discovery and sync selectedColumns on row changes.
Stabilize Grid Cell with useCallback.
Add try/catch to clipboard copy with timeout cleanup on unmount.
Inject CodeDisplay styles once via singleton instead of per-instance.
Localize Badge text and Suspense fallback.
Type CostData.costs as CostEntry[] directly.
Add parseLocalDate input validation.
Extract grid height magic numbers to named constants.
Fix i18n key ordering (com_ch before com_citation). Fix HMR style leak by reusing existing DOM element. Fix parseLocalDate to return null for invalid dates, filter in callers. Remove groupEntriesByWeek, use buildTimeHierarchy instead. Memoize getDateRange in DateView and EntityView. Fix allOpen check for empty rows. Remove redundant dark dep from style injection useEffect.
Use Inconsolata 14px/1.7 (weight 500), theme background/text colors, 1rem padding, and 0.25rem border-radius to match Click UI's codeblock design tokens exactly.
Show idle/waking services with clock badge and console link. Add serviceId/database context pills for list_tables and list_databases. Route get_organizations to collapsible rows, get_organization_details to static rows. Detect "Failed to" prefixed output as errors with friendly message.
Summary
Adds
@clickhouse/click-uito the client and renders ClickHouse MCP tool calls with rich, structured UI instead of raw JSON. When a tool call's domain matchesclickhouse, it delegates to a customClickHouseToolCallcomponent.Also bumps
react/react-domto^18.3.1and addsstyled-components(Click UI peer dep).Change Type
Testing
clickhouse-cloudorclickhouse-dev)run_select_query— SQL in Query tab, virtualized table in Resultget_organizations/get_organization_details— flat table / single-row tableget_services_list— collapsible service cards with endpointsget_service_details— static key-value with endpoints and ipAccessList pillslist_tables— collapsible rows with column definitions and SQL CodeBlocksget_organization_cost— cost view with By Entity/Date toggle, Daily/Weeklylist_service_backups/get_service_backup_details— flat table / single rowChecklist