Models & tools

A model is required in code unless hosted configuration supplies one. A store is also required by the current handler.

Model selection

code
createChatHandler({
  getUserId: getChatUserId,
  model: anthropic('claude-sonnet-4-5'),
  store: createDrizzleChatStore(),
});

For per-user selection, pass a function of the verified request context:

code
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

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