# mordn chat-widget — agent reference
> Complete agent-readable reference for @mordn/chat-widget, the secure-by-default AI agent widget for React and Next.js. Fetch this one file instead of crawling the docs. MIT licensed; self-host or use the hosted backend.
## What it is
A React/Next.js chat surface backed by a single mounted server handler. It provides conversation ownership, idempotent persistence, history, private attachments, streaming, memory, knowledge (RAG), and tools — while keeping identity and policy on your server. One component, one route, one versioned agent config.
- npm: `@mordn/chat-widget`
- GitHub: https://github.com/arnavv-guptaa/chat-widget
- Docs: https://www.mordn.com/docs
- Agents page: https://www.mordn.com/agents
- Skill file: https://www.mordn.com/SKILL.md
## Install
```bash
npm install @mordn/chat-widget ai @ai-sdk/react
```
Styles are prebuilt and scoped to `.chat-widget-container`. No Tailwind needed on the host.
## The one rule (identity boundary — read this first)
`getUserId` (aliased as `getChatUserId`) is the authorization boundary. Derive it from a verified server session; return `null` when unauthenticated. **Never** read identity from a header, query param, or body. The browser never tells the server who it is.
Store and storage factories are constructed per request with the server-verified id, so cross-user access is unrepresentable rather than merely checked. The client sends no `userId`, `agentId`, model, prompt, or config headers: the widget calls `GET /api/chat/bootstrap` on mount and gets back only what the browser may see.
## Hosted handler (recommended starting point)
```ts
// app/api/chat/[[...chat]]/route.ts
import { createMordnHandler } from '@mordn/chat-widget/server';
import { auth } from '@clerk/nextjs/server';
export const { GET, POST, DELETE, OPTIONS } = createMordnHandler({
apiKey: process.env.MORDN_CHAT_KEY!,
getUserId: async () => (await auth()).userId,
});
```
```tsx
// components/assistant.tsx
'use client';
import { ChatWidget } from '@mordn/chat-widget';
import '@mordn/chat-widget/styles.css';
export default function Assistant() {
return ; // must match the route path
}
```
```env
AI_GATEWAY_API_KEY="..." # runs runtime.model gateway strings
MORDN_CHAT_KEY="..." # published config + hosted persistence
```
Models execute in your route, so the route needs its own credential. Without the AI SDK gateway, install a provider package, set its key, and pass `model` in code.
## Self-hosted handler (your own DB + storage)
Swap `createMordnHandler` for `createChatHandler` with a `store`, optional `storage`, and a code `model`:
```ts
import { createChatHandler } from '@mordn/chat-widget/server';
export const { GET, POST, DELETE, OPTIONS } = createChatHandler({
getUserId,
model, // a Vercel AI SDK model
store: storeFactory, // ChatStoreFactory(verifiedUserId)
storage: storageFactory, // optional StorageAdapterFactory(verifiedUserId)
});
```
## Config (AgentConfig)
`AgentConfig` is versioned, JSON-serializable, and shared by control plane, handler, and preview transport. Data only: no React nodes, functions, credentials, or endpoint URLs. Production handlers ignore request config; owner previews opt in via `resolvePreviewConfig`.
```ts
import type { AgentConfig } from '@mordn/chat-widget';
const config: AgentConfig = {
schemaVersion: 1,
runtime: { model: 'anthropic/claude-sonnet-4-5', temperature: 0.3 },
client: { greeting: 'How can I help?', display: { layout: 'popup' } },
};
```
## CLI
`npx @mordn/chat-widget` — scaffolding, ingestion, inspection, and retrieval evaluation. Detects the framework and wires the route. Docs: https://www.mordn.com/docs/api/cli
## Script-tag embed (no build step)
```html
```
Same bootstrap flow: identity and agent selection stay server-side.
## Tools / actions (server-side, identity-scoped)
Your agent can perform actions in your product — create records, update orders, search data. Tools are server-side, defined in `buildTools` inside `createChatHandler`. Each tool has a Zod `inputSchema` and an `execute` that runs with the verified user's identity.
```ts
import { tool } from 'ai';
import { z } from 'zod';
buildTools: async (ctx) => ({
tools: {
myAction: tool({
description: 'What this tool does (the model reads this to decide when to call it)',
inputSchema: z.object({ param: z.string().describe('What this param means') }),
execute: async ({ param }) => {
// Runs on your server with ctx.userId (verified identity)
// Scope all db queries to ctx.userId. Do not trust client input for identity.
return { ok: true, result: '...' };
},
}),
},
cleanup: () => { /* optional: release resources */ },
}),
```
Key rules:
- Use `inputSchema` (not `parameters`) — AI SDK v6.
- `ctx.userId` is server-verified via `getUserId` — never trust client input for identity.
- Return `null` from a `toolRenderer` to use the default rendering.
- `toolRenderers`: custom JSX per tool. `actionRenderers`: declarative status card (success/error/pending/partial).
- Built-ins: `EntityCarousel` (swipeable card list), `SummaryCard` (key-value summary), `StatusTracker` (timeline).
## Request flow
```text
Browser ChatWidget
→ createChatHandler
→ getUserId(request)
→ ChatStoreFactory(verifiedUserId)
→ optional StorageAdapterFactory(verifiedUserId)
→ model + optional tools/retrieval/memory
```
`createChatHandler` returns `GET`, `POST`, `DELETE`, and `OPTIONS`. Export `OPTIONS` when using its cross-origin CORS support.
## Backends
- Hosted: mordn runs persistence, storage, config, feedback. Set `MORDN_CHAT_KEY`.
- Drizzle / Postgres: the current handler schema + shared client.
- Supabase: private attachment storage with signed URLs.
- Custom: implement the user-bound `ChatStore` and `StorageAdapter` contracts yourself.
## Decision summary for an agent installing this
1. Detect the framework (Next.js App Router is the happy path; the script-tag embed covers non-React hosts).
2. Detect the existing auth provider and derive `getUserId` from its verified server session (Clerk → `auth().userId`; NextAuth → `getServerSession`; Supabase → `getUser`; custom → your session). Return `null` when unauthenticated.
3. Decide hosted vs self-hosted. Hosted = `createMordnHandler` + `MORDN_CHAT_KEY`. Self-hosted = `createChatHandler` + your `store`/`model`.
4. Scaffold the route at `app/api/chat/[[...chat]]/route.ts`. Mount ``. Import the prebuilt styles.
5. Set the required env vars. Run the build. Verify `/api/chat/bootstrap` returns 200 for an authenticated request and 401 for an unauthenticated one.
Do not invent identity, do not widen the host app's public-route allowlist, and do not pass client-supplied ids into the store.
## Deeper docs
- Quickstart: https://www.mordn.com/docs/quickstart
- Security: https://www.mordn.com/docs/security
- Build actions: https://www.mordn.com/docs/guides/build-actions
- Models & tools: https://www.mordn.com/docs/guides/models-and-tools
- Knowledge / RAG: https://www.mordn.com/docs/guides/knowledge
- Memory: https://www.mordn.com/docs/guides/memory
- Theming: https://www.mordn.com/docs/guides/theming
- Production readiness: https://www.mordn.com/docs/guides/production-readiness
- Full API reference: https://www.mordn.com/docs/api/create-chat-handler
## License
MIT.