# 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.