[Feat] UI - Allow sorting MCPs by created_at, Display name date (#22825)

* Add column sorting to MCP servers table

- Added sorting state management to DataTable component
- Enabled getSortedRowModel for tanstack/react-table
- Made column headers clickable with sort indicators (↑↓⇅)
- Added enableSorting: true to sortable columns in mcp_server_columns
- Columns now support ascending/descending sort by clicking headers
- Updated package-lock.json and tsconfig.json from build process

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

* Make table sorting opt-in to avoid conflicts with existing consumers

Address Greptile feedback (score 2/5):
- Added enableSorting prop to DataTable (defaults to false)
- Only enable sorting features when explicitly requested
- Pass enableSorting=true from MCP servers component
- This prevents unintended sorting on other DataTable consumers:
  * view_logs (has server-side sorting)
  * pass_through_settings
  * UsagePage
- Sorting UI (indicators, click handlers) only shown when enabled

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>
This commit is contained in:
Ishaan Jaff
2026-03-04 17:47:01 -08:00
committed by GitHub
co-authored by Cursor Agent Ishaan Jaff
parent a33d3e035d
commit dd183a7fcb
4 changed files with 40 additions and 19 deletions
-15
View File
@@ -12984,21 +12984,6 @@
"type": "github", "type": "github",
"url": "https://github.com/sponsors/wooorm" "url": "https://github.com/sponsors/wooorm"
} }
},
"node_modules/@next/swc-win32-ia32-msvc": {
"version": "14.2.33",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.33.tgz",
"integrity": "sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
} }
} }
} }
@@ -15,6 +15,7 @@ export const mcpServerColumns = (
{ {
accessorKey: "server_id", accessorKey: "server_id",
header: "Server ID", header: "Server ID",
enableSorting: true,
cell: ({ row }) => ( cell: ({ row }) => (
<button <button
onClick={() => onView(row.original.server_id)} onClick={() => onView(row.original.server_id)}
@@ -27,10 +28,12 @@ export const mcpServerColumns = (
{ {
accessorKey: "server_name", accessorKey: "server_name",
header: "Name", header: "Name",
enableSorting: true,
}, },
{ {
accessorKey: "alias", accessorKey: "alias",
header: "Alias", header: "Alias",
enableSorting: true,
}, },
{ {
id: "url", id: "url",
@@ -47,6 +50,7 @@ export const mcpServerColumns = (
{ {
accessorKey: "transport", accessorKey: "transport",
header: "Transport", header: "Transport",
enableSorting: true,
cell: ({ row }) => { cell: ({ row }) => {
const transport = row.original.transport || "http"; const transport = row.original.transport || "http";
const specPath = row.original.spec_path; const specPath = row.original.spec_path;
@@ -58,6 +62,7 @@ export const mcpServerColumns = (
{ {
accessorKey: "auth_type", accessorKey: "auth_type",
header: "Auth Type", header: "Auth Type",
enableSorting: true,
cell: ({ getValue }) => <span>{(getValue() as string) || "none"}</span>, cell: ({ getValue }) => <span>{(getValue() as string) || "none"}</span>,
}, },
{ {
@@ -166,6 +171,7 @@ export const mcpServerColumns = (
{ {
header: "Created At", header: "Created At",
accessorKey: "created_at", accessorKey: "created_at",
enableSorting: true,
sortingFn: "datetime", sortingFn: "datetime",
cell: ({ row }) => { cell: ({ row }) => {
const server = row.original; const server = row.original;
@@ -177,6 +183,7 @@ export const mcpServerColumns = (
{ {
header: "Updated At", header: "Updated At",
accessorKey: "updated_at", accessorKey: "updated_at",
enableSorting: true,
sortingFn: "datetime", sortingFn: "datetime",
cell: ({ row }) => { cell: ({ row }) => {
const server = row.original; const server = row.original;
@@ -410,6 +410,7 @@ const MCPServers: React.FC<MCPServerProps> = ({ accessToken, userRole, userID })
isLoading={isLoadingServers} isLoading={isLoadingServers}
noDataMessage="No MCP servers configured" noDataMessage="No MCP servers configured"
loadingMessage="🚅 Loading MCP servers..." loadingMessage="🚅 Loading MCP servers..."
enableSorting={true}
/> />
</div> </div>
</div> </div>
@@ -1,5 +1,5 @@
import { Fragment } from "react"; import { Fragment, useState } from "react";
import { ColumnDef, flexRender, getCoreRowModel, getExpandedRowModel, Row, useReactTable } from "@tanstack/react-table"; import { ColumnDef, flexRender, getCoreRowModel, getExpandedRowModel, Row, useReactTable, getSortedRowModel, SortingState } from "@tanstack/react-table";
import { Table, TableHead, TableHeaderCell, TableBody, TableRow, TableCell } from "@tremor/react"; import { Table, TableHead, TableHeaderCell, TableBody, TableRow, TableCell } from "@tremor/react";
@@ -15,6 +15,8 @@ interface DataTableProps<TData, TValue> {
isLoading?: boolean; isLoading?: boolean;
loadingMessage?: string; loadingMessage?: string;
noDataMessage?: string; noDataMessage?: string;
/** Enable client-side column sorting (defaults to false to avoid conflicts with server-side sorting) */
enableSorting?: boolean;
} }
export function DataTable<TData, TValue>({ export function DataTable<TData, TValue>({
@@ -27,18 +29,28 @@ export function DataTable<TData, TValue>({
isLoading = false, isLoading = false,
loadingMessage = "🚅 Loading logs...", loadingMessage = "🚅 Loading logs...",
noDataMessage = "No logs found", noDataMessage = "No logs found",
enableSorting = false,
}: DataTableProps<TData, TValue>) { }: DataTableProps<TData, TValue>) {
const supportsExpansion = !!(renderSubComponent || renderChildRows) && !!getRowCanExpand; const supportsExpansion = !!(renderSubComponent || renderChildRows) && !!getRowCanExpand;
const [sorting, setSorting] = useState<SortingState>([]);
const table = useReactTable<TData>({ const table = useReactTable<TData>({
data, data,
columns, columns,
...(enableSorting && {
state: {
sorting,
},
onSortingChange: setSorting,
enableSortingRemoval: false,
}),
...(supportsExpansion && { getRowCanExpand }), ...(supportsExpansion && { getRowCanExpand }),
getRowId: (row: TData, index: number) => { getRowId: (row: TData, index: number) => {
const _row: any = row as any; const _row: any = row as any;
return _row?.request_id ?? String(index); return _row?.request_id ?? String(index);
}, },
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
...(enableSorting && { getSortedRowModel: getSortedRowModel() }),
...(supportsExpansion && { getExpandedRowModel: getExpandedRowModel() }), ...(supportsExpansion && { getExpandedRowModel: getExpandedRowModel() }),
}); });
@@ -49,9 +61,25 @@ export function DataTable<TData, TValue>({
{table.getHeaderGroups().map((headerGroup) => ( {table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}> <TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => { {headerGroup.headers.map((header) => {
const canSort = enableSorting && header.column.getCanSort();
const isSorted = header.column.getIsSorted();
return ( return (
<TableHeaderCell key={header.id} className="py-1 h-8"> <TableHeaderCell
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} key={header.id}
className={`py-1 h-8 ${canSort ? 'cursor-pointer select-none hover:bg-gray-50' : ''}`}
onClick={canSort ? header.column.getToggleSortingHandler() : undefined}
>
{header.isPlaceholder ? null : (
<div className="flex items-center gap-1">
{flexRender(header.column.columnDef.header, header.getContext())}
{canSort && (
<span className="text-gray-400">
{isSorted === 'asc' ? '↑' : isSorted === 'desc' ? '↓' : '⇅'}
</span>
)}
</div>
)}
</TableHeaderCell> </TableHeaderCell>
); );
})} })}