Skip to content

Reorderable ServerCards on the Servers screen (drag-and-drop + keyboard) #1369

@cliffhall

Description

@cliffhall

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:

  1. Drag and drop a card to a new position in the grid.
  2. 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

  • User can drag a ServerCard to a new position and the grid reflows in real time.
  • User can tab to a card, press Space to pick it up, use arrow keys to move it, and press Space to drop it. Screen-reader announcements describe the operation.
  • After either gesture, the new order is persisted to mcp.json and survives a reload.
  • If another process edits mcp.json while the user is mid-drag, the API call either succeeds (no conflict) or fails cleanly without corrupting the file — covered by an integration test.
  • Active-server highlighting, compact toggle, and the existing Add / Import / Export controls continue to work unchanged.
  • Tests:
    • ServerListScreen.test.tsx — keyboard reorder via the @dnd-kit test utilities (drag pointer events tend to be flaky under happy-dom; keyboard is the reliable path).
    • A storybook play for a "reorder via keyboard" interaction.
    • Integration test for the new backend route, including the conflicting-id-set rejection.

Out of scope

  • Multi-select reorder. Single-card drag/keyboard reorder only.
  • Cross-group / categorized servers (folders, tags). The grid stays flat.

Metadata

Metadata

Assignees

No one assigned

    Labels

    v2Issues and PRs for v2

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions