Models & tools
A model is required in code unless hosted configuration supplies one. A store is also required by the current handler.
Model selection
createChatHandler({
getUserId: getChatUserId,
model: anthropic('claude-sonnet-4-5'),
store: createDrizzleChatStore(),
});For per-user selection, pass a function of the verified request context:
createChatHandler({
getUserId: getChatUserId,
store: createDrizzleChatStore(),
model: async (ctx) => {
const plan = await getUserPlan(ctx.userId);
return plan === 'pro'
? anthropic('claude-sonnet-4-5')
: openai('gpt-4o');
},
});@mordn/chat-widget/models exports MODELS, the package's hosted gateway-string
catalog. Provider SDK calls use provider-native ids; hosted config uses gateway
strings such as anthropic/claude-sonnet-4-5.
Request-scoped tools
import { tool } from 'ai';
import { z } from 'zod';
createChatHandler({
getUserId: getChatUserId,
model,
store,
buildTools: async (ctx) => {
const client = await connectMcp(ctx.userId);
return {
tools: {
searchDocs: tool({
description: 'Search the documentation.',
inputSchema: z.object({ query: z.string() }),
execute: async ({ query }) => client.search(query),
}),
},
cleanup: () => client.close(),
};
},
});AI SDK v6 tool definitions use inputSchema. Do not copy v5 examples that use
parameters.
cleanup runs exactly once after success, error, or abort. Use it for MCP
clients, transactions, or other per-request resources. Omit it for stateless
tools.
Client tool rendering
toolRenderers receives ToolPartLike and may render custom JSX. Return null
to fall through to the default row. Use actionRenderers when a declarative
status card is enough, and derive status from actual tool state/output.
See MCP tools and AI UX extras.