Summary
Users have no way to control the order of ServerCards on the Servers screen. The list is rendered in mcp.json insertion order today, which means the only way to reorder is to hand-edit the config file. Users should be able to:
- Drag and drop a card to a new position in the grid.
- Keyboard reorder — focus a card, press a modifier+arrow (e.g. Space to "pick up", arrow keys to move, Space again to drop) and have the position update accordingly. This is the standard accessible pattern (
@dnd-kit calls it the keyboard sensor) and is required for parity with mouse users.
The new ordering must persist to mcp.json so it survives reloads and is visible to other Inspector incarnations (TUI/CLI) that read the same file.
Current state
clients/web/src/components/screens/ServerListScreen/ServerListScreen.tsx renders the cards via a Mantine SimpleGrid and maps over servers (ServerListScreen.tsx:72-91). No reorder affordance.
- Persistence:
useServers (core/react/useServers.ts) exposes addServer / updateServer / removeServer only — no reorder mutator.
- Backend:
core/mcp/remote/node/server.ts exposes GET/POST/PUT/DELETE /api/servers[/:id] (lines 1318, 1377, 1441, 1602). There's no endpoint to rewrite the ordering, and the in-memory list returned by GET /api/servers reflects mcp.json insertion order.
Proposed change
UI layer (ServerListScreen)
- Wrap the
SimpleGrid in a drag-and-drop context. Recommend @dnd-kit/core + @dnd-kit/sortable — it ships a SortableContext that works inside grid layouts, supports both pointer and keyboard sensors out of the box, and is already a common React ecosystem choice. Each ServerCard becomes a useSortable item keyed by server.id.
- Add a visible drag handle area on each card (existing card body can serve as the handle, but an icon affordance — e.g. a
IconGripVertical in the card header — makes the gesture discoverable).
- Keyboard sensor: Space (or Enter) toggles "pick up / drop", Arrow keys move the picked-up card by one grid cell. The
@dnd-kit KeyboardSensor with the sortableKeyboardCoordinates strategy gives this for free in a grid.
- Live region announcements (
announcements prop on DndContext) so screen readers narrate "Picked up server X. Moved to position 3 of 7." etc.
Persistence
- Extend
useServers with reorderServers(orderedIds: string[]) => Promise<void>. On call, optimistically reorder the local servers array and POST/PUT the new order to the backend.
- Add a new backend route
PUT /api/servers/order (or similar) in core/mcp/remote/node/server.ts that accepts an array of server ids and rewrites mcp.json so the servers map iterates in the supplied order. The rewrite must reuse the existing file-write path (atomic write, events channel notify) so external watchers see exactly one change. Reject the call if the supplied id set doesn't match the current set on disk (race against an external edit) so we don't silently drop or duplicate entries.
- Make sure the SSE
/api/servers/events channel emits a single update when ordering changes, so other tabs / clients re-render.
Compact mode
ServerListScreen already supports a compact toggle. The reorder gesture must work in both compact and non-compact layouts — @dnd-kit/sortable handles this as long as both layouts share the same SortableContext.
Acceptance criteria
Out of scope
- Multi-select reorder. Single-card drag/keyboard reorder only.
- Cross-group / categorized servers (folders, tags). The grid stays flat.
Summary
Users have no way to control the order of
ServerCards on the Servers screen. The list is rendered inmcp.jsoninsertion order today, which means the only way to reorder is to hand-edit the config file. Users should be able to:@dnd-kitcalls it the keyboard sensor) and is required for parity with mouse users.The new ordering must persist to
mcp.jsonso it survives reloads and is visible to other Inspector incarnations (TUI/CLI) that read the same file.Current state
clients/web/src/components/screens/ServerListScreen/ServerListScreen.tsxrenders the cards via a MantineSimpleGridand maps overservers(ServerListScreen.tsx:72-91). No reorder affordance.useServers(core/react/useServers.ts) exposesaddServer/updateServer/removeServeronly — no reorder mutator.core/mcp/remote/node/server.tsexposesGET/POST/PUT/DELETE /api/servers[/:id](lines 1318, 1377, 1441, 1602). There's no endpoint to rewrite the ordering, and the in-memory list returned byGET /api/serversreflectsmcp.jsoninsertion order.Proposed change
UI layer (
ServerListScreen)SimpleGridin a drag-and-drop context. Recommend@dnd-kit/core+@dnd-kit/sortable— it ships aSortableContextthat works inside grid layouts, supports both pointer and keyboard sensors out of the box, and is already a common React ecosystem choice. EachServerCardbecomes auseSortableitem keyed byserver.id.IconGripVerticalin the card header — makes the gesture discoverable).@dnd-kitKeyboardSensorwith thesortableKeyboardCoordinatesstrategy gives this for free in a grid.announcementsprop onDndContext) so screen readers narrate "Picked up server X. Moved to position 3 of 7." etc.Persistence
useServerswithreorderServers(orderedIds: string[]) => Promise<void>. On call, optimistically reorder the localserversarray and POST/PUT the new order to the backend.PUT /api/servers/order(or similar) incore/mcp/remote/node/server.tsthat accepts an array of server ids and rewritesmcp.jsonso theserversmap iterates in the supplied order. The rewrite must reuse the existing file-write path (atomic write, events channel notify) so external watchers see exactly one change. Reject the call if the supplied id set doesn't match the current set on disk (race against an external edit) so we don't silently drop or duplicate entries./api/servers/eventschannel emits a single update when ordering changes, so other tabs / clients re-render.Compact mode
ServerListScreenalready supports acompacttoggle. The reorder gesture must work in both compact and non-compact layouts —@dnd-kit/sortablehandles this as long as both layouts share the sameSortableContext.Acceptance criteria
ServerCardto a new position and the grid reflows in real time.mcp.jsonand survives a reload.mcp.jsonwhile the user is mid-drag, the API call either succeeds (no conflict) or fails cleanly without corrupting the file — covered by an integration test.ServerListScreen.test.tsx— keyboard reorder via the@dnd-kittest utilities (drag pointer events tend to be flaky under happy-dom; keyboard is the reliable path).Out of scope