Architecture

@sinups/ai-kit is a UI construction kit for agent products built on Mantine. It covers the chat itself and everything around it: multi-step wizards, settings screens, MCP servers, agents, skills, permissions, hooks, sessions, background tasks and diff review. Every piece works in a narrow widget (about 360px) and in a full-page app (900px and wider).

Layers
styles/            --ae-* tokens, keyframes
utils/, hooks/     pure functions and generic hooks, no JSX
primitives/        domain-neutral building blocks (Wizard, SettingsLayout, EntityList, ...)
<chat modules>     AgentChat, MessageList, input/, tools/, question/, elicitation/, ...
<domain modules>   mcp/, agents/, skills/, permissions/, hooks-config/, sessions/, tasks/,
                   message-actions/, diff/, model-settings/

Dependencies point down only. A domain module may use primitives, utilities and chat modules; a primitive never imports a domain module; domain modules do not import each other except through exported types. Everything public is exported from the package root.

Component contract
  • Presentational and controlled. Components receive data through props and report intent through callbacks (onSelect, onSave, onReconnect). No fetching, no global stores, no routing: your app owns data and side effects.
  • Async actions return promises. While the promise is pending the button shows a loader; a rejection message is shown in an alert.
  • Every data view handles four states: loading (skeleton), error (alert with optional retry), empty (Mantine EmptyState) and data.
  • Logic lives outside JSX. Parsing, validation, filtering and formatting are pure, tested functions exported next to the components; reusable stateful logic is a hook (useWizard, useFuzzySearch).
  • Domain types are exported and stay compatible with the source protocol: the MCP specification for MCP components, the AI SDK UIMessage for chat.
  • Labels are English defaults that can be overridden through props, usually labels.
  • Width.Layout adapts to the component's own width, not the viewport: side by side when wide (list and detail, navigation and content), stacked with a back action when narrow.
<McpServerList
  servers={servers}                          // data comes from your app
  loading={isLoading}
  error={loadError}
  onRetry={reload}
  onReconnect={(server) => api.reconnect(server.id)}  // a promise: the action shows pending state
  onRemove={(server) => api.remove(server.id)}        // a rejection is shown in an alert
  labels={{ addServer: "Connect server" }}            // English defaults, override any label
/>
Mantine only

Components are built from Mantine components and the Styles API, so they follow your theme, color scheme and fonts. Peer dependencies are only @mantine/core, @mantine/hooks and @tabler/icons-react. Styles use --mantine-* and --ae-* variables, which you can override on any ancestor.

Primitives

Domain modules such as MCP, Permissions and Hooks are assembled from these domain-neutral building blocks. Use them directly for your own screens.

Primitive
Purpose
Multi-step flows: per-step validation, conditional steps, review step, non-linear editing
Settings screens: section navigation, titled groups, label/description/control rows
List + detail: two panes when wide, stacked with a back action when narrow
Searchable, filterable, groupable lists with the four data states and per-item actions
Mod+K palette: fuzzy search, groups, shortcuts, recent items
Confirmation for destructive actions with pending and error states
One status vocabulary: idle, pending, running, success, warning, error, disabled, needs-auth
JSON Schema as a parameter table: name, type, required, default, enum, nested objects
Editable key/value pairs (env vars, headers) with secret masking and validation
Keyboard shortcut rendered with Kbd, mod key resolved per platform
Files per component
<module>/<Name>/<Name>.tsx          memo component, JSDoc on every prop
<module>/<Name>/<Name>.module.css   optional
<module>/<Name>/<Name>.story.tsx    Usage + Narrow + Wide (+ states: Loading, Error, Empty)
<module>/<Name>/<Name>.test.tsx     behavior tests with @mantine-tests/core + user-event
<module>/<logic>.ts + .test.ts      pure logic

Small modules keep components flat in the module folder, for example tools/ and input/. Every component has stories for a narrow and a wide container.

Checks

In the repository, yarn test runs dependency checks, formatting, type checking, lint and unit tests; yarn build builds the package and yarn storybook serves the stories.