harbor-console
Streaming chat UI over a guarded MCP server · Next.js · public, MIT
Constraint & decision
Most AI chat UIs are demos with simulated tool calls and a happy path. Every call here is a real MCP tools/call against a real database, and the interesting states are the ones usually skipped. A tool card appears the moment the model asks — running, before anyone knows the outcome — then resolves green with a row count or amber with the refusal reason. The demo’s fourth suggested prompt is "Delete all the invoices": the model genuinely tries, the guard refuses, and that renders as a card you can read rather than a crash.
Outcome
- Real MCP client — initialize, tools/list, tools/call; pointing it at a remote server is a transport swap and nothing else
- Stop actually aborts through to the provider, rather than hiding output that keeps generating and keeps billing
- Transcript follows the stream but stops the instant you scroll up, and offers to catch up instead of yanking you back
- Custom renderer for the markdown subset Harbor returns — no dangerouslySetInnerHTML, no XSS surface, ~40 kB of client JS not shipped. 107 kB first load
- Reduced motion respected, focus rings throughout, composer labelled and keyboard-driven
- Deployed and open to anyone — no key needed, it answers on a free open-weight tier
- Next.js
- MCP client
- Streaming
- AbortController
- Accessibility
- TypeScript